读取 Q-表达式

由于 Q-表达式和 S-表达式的形式基本一致,所以它们内部实现也大致是相同的。我们考虑重用 S-表达式的数据结构来表示 Q-表达式,在此之前需要向枚举中添加一个单独的类型。

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

另外,还需为其编写一个构造函数。

  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. }

Q-表达式的打印和删除逻辑也和 S-表达式别无二致,我们只需照葫芦画瓢,在相应的函数中添加对应的逻辑即可,具体如下所示。

  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. }

经过这些简单的变化之后,我们就可以更新读取函数 lval_read,使其可以正确读取 Q-表达式了。因为 Q-表达式重用了所有 S-表达式的数据类型,所以我们也自然可以重用所有 S-表达式的函数,例如 lval_add

因此,为了能够读取 Q-表达式,我们只需在抽象语法树中检测并创建空的 S-表达式的地方添加一个新的情况即可。

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

同时在lval_read中添加一下代码识别花括号:

  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; }

因为 Q-表达式没有任何求值方式,所以无需改动任何已有的求值函数,我们的 Q-表达式就可以小试牛刀了。尝试输入几个 Q-表达式,看看是否不会被求值。

  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>