text: 
  /**
   * @file expr_table.c
   *
   * @brief The expressions are stored in the expr_table_t data structure. They
   * are accessed by index. The index is what is stored in the instruction
   * stream. The actual expression is stored in RPN format in the expression
   * data structure at the index. The stack that is used to solve it is
   * integrated into the data structure. The first time the expression is solved
   * the solution stack is allocated. The size of the stack can never grow
   * beyond the size of the original expression. When the expression is solved,
   * the original expression buffer is read non-destructively and the expression
   * stack is used. When the expression has been completely evaluated, the
   * result is on the top of the stack.
   *
   */
  #include "stdinc.h"
  #include "memory.h"
  #include "expr_table.h"
  
  /**
   * @brief Pop a value from the stack. The value is assumed to be a literal value.
   *
   * @param ent
   * @param val
   */
  void pop_value(expr_entry_t * ent, expr_value_t * val) {
  
      expr_value_t vt;
  
      // The parser should prevent this from being entered, but check anyway.
      if(ent->stk_len == 0) {
          fprintf(stderr, "RUNTIME: expression stack underflow\n");
          exit(1);
      }
      ent->stk_len--;
  
      vt.val.literal = ent->vstack[ent->stk_len].val.literal;
      vt.type = ent->vstack[ent->stk_len].type;   // errors caught by runtime
      memcpy(val, &vt, sizeof(expr_value_t));
  }
  
  /**
   * @brief Push the value on the top of stack.
   *
   * @param ent
   * @param val
   */
  void push_value(expr_entry_t * ent, expr_value_t * val) {
  
      // grow the stack is required
      if(ent->stk_len + 1 > ent->stk_cap) {
          ent->stk_cap <<= 1;
          ent->vstack = REALLOC(ent->vstack, ent->stk_cap, expr_value_t);
      }
  
      // save the data in the stack
      expr_value_t *vt = &ent->vstack[ent->stk_len];
  
      vt->type = val->type;
      vt->val.literal = val->val.literal;
      ent->stk_len++;
  }
  
  /**
   * @brief Peek at the type of var on the top of the stack.
   *
   * @param ent
   * @return int
   */
  int peek_value_type(expr_entry_t * ent) {
  
      return ent->vstack[ent->stk_len - 1].type;
  }
  
  /**
   * @brief Get the expr entry ptr object
   *
   * @param tab
   * @param idx
   * @return expr_entry_t*
   */
  expr_entry_t *get_expr_entry_ptr(expr_table_t * tab, int idx) {
  
      return &tab->table[idx];
  }
  
  /**
   * @brief Create a expression table object
   *
   * @return expr_table_t*
   */
  expr_table_t *create_expr_table() {
  
      expr_table_t *tab = ALLOC_DS(expr_table_t);
  
      tab->cap = 0x01 << 3;
      tab->len = 0;
      tab->table = ALLOC_ARRAY(tab->cap, expr_entry_t);
  
      return tab;
  }
  
  /**
   * @brief Destroy an expression table.
   *
   * @param tab
   */
  void destroy_expr_table(expr_table_t * tab) {
  
      if(tab != NULL) {
          if(tab->table != NULL) {
              for(size_t i = 0; i < tab->len; i++)
                  destroy_expr(&tab->table[i]);
              FREE(tab->table);
          }
          FREE(tab);
      }
  }
  
  /**
   * @brief Create a expression object
   *
   * @return expr_entry_t*
   */
  int create_expr(expr_table_t * tab) {
  
      if(tab->len + 1 > tab->cap) {
          tab->cap <<= 1;
          tab->table = REALLOC(tab->table, tab->cap, expr_entry_t);
      }
  
      expr_entry_t *entry = &tab->table[tab->len];
  
      entry->stk_cap = 0x01 << 3;
      entry->stk_len = 0;
      entry->vstack = ALLOC_ARRAY(entry->stk_cap, expr_value_t);
      entry->tab_cap = 0x01 << 3;
      entry->tab_len = 0;
      entry->tab = ALLOC_ARRAY(entry->tab_cap, expr_value_t);
  
      int idx = tab->len;
  
      tab->len++;
      return idx;
  }
  
  /**
   * @brief Destroy an expression object.
   *
   * @param ent
   */
  void destroy_expr(expr_entry_t * ent) {
  
      if(ent != NULL) {
          if(ent->tab != NULL)
              FREE(ent->tab);
          if(ent->vstack != NULL) {
              FREE(ent->vstack);
          }
          // ent is not an allocated pointer.
      }
  }
  
  /**
   * @brief Add an expression term to the expression. This can be a number,
   * a variable, a register, or an operator.
   *
   * @param ent
   * @param val
   */
  void add_expr_term(expr_entry_t * ent, expr_value_t * val) {
  
      if(ent->tab_len + 1 > ent->tab_cap) {
          ent->tab_cap = ent->tab_cap << 1;
          ent->tab = REALLOC(ent->tab, ent->tab_cap, expr_value_t);
      }
  
      memcpy(&ent->tab[ent->tab_len], val, sizeof(expr_value_t));
      ent->tab_len++;
  }
  
  /**
   * @brief Iterate the stored expression, returning NULL when it's completed.
   *
   * @param ent
   * @return expr_value_t*
   */
  expr_value_t *iterate_expr_terms(expr_entry_t * ent, bool reset) {
  
      if(reset)
          ent->crnt = 0;
  
      // unsigned >= 0 is always true
      if(ent->crnt < ent->tab_len) {
          expr_value_t *val = &ent->tab[ent->crnt];
  
          ent->crnt++;
          return val;
      }
  
      return NULL;                // no more list
  }
  
  /**
   * @brief Load the expression table from disk. If there is an error, then
   * return NULL. Otherwise return a pointer to a fully functional expression
   * table.
   *
   * @param fp
   * @return expr_table_t*
   */
  expr_table_t *load_expr_table(FILE * fp) {
      (void)fp;
      return NULL;
  }
  
  /**
   * @brief Save the expression table to disk using the file pointer.
   *
   * @param tab
   * @param fp
   */
  void save_expr_table(expr_table_t * tab, FILE * fp) {
      (void)fp;
      (void)tab;
  
  }
  