We can also add a constructor for this variation.
lval* lval_qexpr(void) {
lval* v = malloc(sizeof(lval));
v->type = LVAL_QEXPR;
v->count = 0;
v->cell = NULL;
return v;
}
void lval_del(lval* v) {
case LVAL_NUM: break;
case LVAL_SYM: free(v->sym); break;
/* If Qexpr or Sexpr then delete all elements inside */
case LVAL_QEXPR:
case LVAL_SEXPR:
for (int i = 0; i < v->count; i++) {
lval_del(v->cell[i]);
}
free(v->cell);
break;
free(v);
}
Using these simple changes we can update our reading function lval_read
to be able to read in Q-Expressions. Because we reused all the S-Expression data fields for our Q-Expression type, we can also reuse all of the functions for S-Expressions such as lval_add
. Therefore to read in Q-Expressions we just need to add a special case for constructing an empty Q-Expression to lval_read
just below where we detect and create empty S-Expressions from the abstract syntax tree.
if (strcmp(t->children[i]->contents, "(") == 0) { continue; }
if (strcmp(t->children[i]->contents, ")") == 0) { continue; }
if (strcmp(t->children[i]->contents, "}") == 0) { continue; }
if (strcmp(t->children[i]->contents, "{") == 0) { continue; }
Because there is no special method of evaluating Q-Expressions, we don’t need to edit any of the evaluation functions. Our Q-Expressions should be ready to try. Compile and run the program. Try using them as a new data type and ensure they are not evaluated.