Evaluation
To evaluate the parse tree we are going to write a recursive function. But before we get started, let us try and see what observations we can make about the structure of the tree we get as input. Try printing out some expressions using your program from the previous chapter. What do you notice?
lispy> * 10 (+ 1 51)
>
regex
operator|char:1:1 '*'
expr|number|regex:1:3 '10'
expr|>
char:1:6 '('
operator|char:1:7 '+'
expr|number|regex:1:9 '1'
expr|number|regex:1:11 '51'
char:1:13 ')'
regex
One observation is that if a node is tagged with number
it is always a number, has no children, and we can just convert the contents to an integer. This will act as the base case in our recursion.
If a node is tagged with expr
, and is not a number
, we need to look at its second child (the first child is always '('
) and see which operator it is. Then we need to apply this operator to the evaluation of the remaining children, excluding the final child which is always ')'
. This is our recursive case. This also needs to be done for the root node.
When we evaluate our tree, just like when counting the nodes, we’ll need to accumulate the result. To represent this result we’ll use the C type long
which means a long integer.
To detect the tag of a node, or to get a number from a node, we will need to make use of the tag
and contents
fields. These are string fields, so we are going to have to learn a couple of string functions first.
atoi | Converts a char to a int . |
strcmp | Takes as input two char and if they are equal it returns 0 . |
strstr | Takes as input two char* and returns a pointer to the location of the second in the first, or 0 if the second is not a sub-string of the first. |
We can use strcmp
to check which operator to use, and strstr
to check if a tag contains some substring. Altogether our recursive evaluation function looks like this.
long eval(mpc_ast_t* t) {
/* If tagged as number return it directly. */
if (strstr(t->tag, "number")) {
return atoi(t->contents);
}
/* The operator is always second child. */
char* op = t->children[1]->contents;
/* We store the third child in `x` */
long x = eval(t->children[2]);
/* Iterate the remaining children and combining. */
int i = 3;
while (strstr(t->children[i]->tag, "expr")) {
x = eval_op(x, op, eval(t->children[i]));
i++;
}
return x;
}
We can define the eval_op
function as follows. It takes in a number, an operator string, and another number. It tests for which operator is passed in, and performs the corresponding C operation on the inputs.
/* Use operator string to see which operation to perform */
long eval_op(long x, char* op, long y) {
if (strcmp(op, "+") == 0) { return x + y; }
if (strcmp(op, "-") == 0) { return x - y; }
if (strcmp(op, "*") == 0) { return x * y; }
if (strcmp(op, "/") == 0) { return x / y; }
return 0;
}