Reading Expressions


First we are going to read in the program and construct an lval* that represents it all. Then we are going to evaluate this lval* to get the result of our program. This first stage should convert the abstract syntax tree into an S-Expression, and the second stage should evaluate this S-Expression using our normal Lisp rules.

To complete the first stage we can recursively look at each node of the tree, and construct different lval* types depending on the tag and contents fields of the node.

If the given node is tagged as a number or symbol, then we use our constructors to return an lval* directly for those types. If the given node is the root, or an sexpr, then we create an empty S-Expression lval and slowly add each valid sub-expression contained in the tree.

To add an element to an S-Expression we can create a function lval_add. This function increases the count of the Expression list by one, and then uses realloc to reallocate the amount of space required by v->cell. This new space can be used to store the extra lval* required. Using this new space it sets the final value of the list with v->cell[v->count-1] to the value lval* x passed in.

Don’t Lisps use Cons cells?

Other Lisps have a slightly different definition of what an S-Expression is. In most other Lisps S-Expressions are defined inductively as either an atom such as a symbol of number, or two other S-Expressions joined, or cons, together.

This naturally leads to an implementation using linked lists, a different data structure to the one we are using. I choose to represent S-Expressions as a variable sized array in this book for the purposes of simplicity, but it is important to be aware that the official definition, and typical implementation are both subtly different.

  1. lval* lval_read_num(mpc_ast_t* t) {
  2. errno = 0;
  3. long x = strtol(t->contents, NULL, 10);
  4. return errno != ERANGE ?
  5. lval_num(x) : lval_err("invalid number");
  6. }
  1. lval* lval_read(mpc_ast_t* t) {
  2. /* If Symbol or Number return conversion to that type */
  3. if (strstr(t->tag, "number")) { return lval_read_num(t); }
  4. if (strstr(t->tag, "symbol")) { return lval_sym(t->contents); }
  5. /* If root (>) or sexpr then create empty list */
  6. lval* x = NULL;
  7. if (strcmp(t->tag, ">") == 0) { x = lval_sexpr(); }
  8. if (strstr(t->tag, "sexpr")) { x = lval_sexpr(); }
  9. /* Fill this list with any valid expression contained within */
  10. for (int i = 0; i < t->children_num; i++) {
  11. if (strcmp(t->children[i]->contents, "(") == 0) { continue; }
  12. if (strcmp(t->children[i]->contents, ")") == 0) { continue; }
  13. if (strcmp(t->children[i]->tag, "regex") == 0) { continue; }
  14. x = lval_add(x, lval_read(t->children[i]));
  15. }
  16. return x;
  17. }
  1. lval* lval_add(lval* v, lval* x) {
  2. v->count++;
  3. v->cell = realloc(v->cell, sizeof(lval*) * v->count);
  4. v->cell[v->count-1] = x;
  5. return v;
  6. }