Reading Q-Expressions


Because Q-Expressions are so similar S-Expressions much of their internal behaviour is going to be the same. We’re going to reuse our S-Expression data fields to represent Q-Expressions, but we still need to add a separate type to the enumeration.

  1. enum { LVAL_ERR, LVAL_NUM, LVAL_SYM, LVAL_SEXPR, LVAL_QEXPR };

We can also add a constructor for this variation.

  1. /* A pointer to a new empty Qexpr lval */
  2. lval* lval_qexpr(void) {
  3. lval* v = malloc(sizeof(lval));
  4. v->type = LVAL_QEXPR;
  5. v->count = 0;
  6. v->cell = NULL;
  7. return v;
  8. }

To print and delete Q-Expressions we do essentially the same thing as with S-Expressions. We can add the relevant lines to our functions for printing and deletion as follows.

  1. void lval_print(lval* v) {
  2. switch (v->type) {
  3. case LVAL_NUM: printf("%li", v->num); break;
  4. case LVAL_ERR: printf("Error: %s", v->err); break;
  5. case LVAL_SYM: printf("%s", v->sym); break;
  6. case LVAL_SEXPR: lval_expr_print(v, '(', ')'); break;
  7. case LVAL_QEXPR: lval_expr_print(v, '{', '}'); break;
  8. }
  9. }
  1. void lval_del(lval* v) {
  2. switch (v->type) {
  3. case LVAL_NUM: break;
  4. case LVAL_ERR: free(v->err); break;
  5. case LVAL_SYM: free(v->sym); break;
  6. /* If Qexpr or Sexpr then delete all elements inside */
  7. case LVAL_QEXPR:
  8. case LVAL_SEXPR:
  9. for (int i = 0; i < v->count; i++) {
  10. lval_del(v->cell[i]);
  11. }
  12. /* Also free the memory allocated to contain the pointers */
  13. free(v->cell);
  14. break;
  15. }
  16. free(v);
  17. }

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.

  1. if (strstr(t->tag, "qexpr")) { x = lval_qexpr(); }

We also need to update lval_read to recognize the curly bracket characters when they appear.

  1. if (strcmp(t->children[i]->contents, "(") == 0) { continue; }
  2. if (strcmp(t->children[i]->contents, ")") == 0) { continue; }
  3. if (strcmp(t->children[i]->contents, "}") == 0) { continue; }
  4. 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.

  1. lispy> {1 2 3 4}
  2. {1 2 3 4}
  3. lispy> {1 2 (+ 5 6) 4}
  4. {1 2 (+ 5 6) 4}
  5. lispy> {{2 3 4} {1}}
  6. {{2 3 4} {1}}
  7. lispy>