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