打印表达式

距离验证本章做的所有改变只有一步之遥了!我们还需要修改打印函数使其能够打印出 S-表达式。通过将 S-表达式打印出来和输入对比可以进一步检查读入模块是否正确工作。

为了打印 S-表达式,我们创建一个新函数,遍历所有的子表达式,并将它们单独打印出来,以空格隔开,正如输入的时候一样。

  1. void lval_expr_print(lval* v, char open, char close) {
  2. putchar(open);
  3. for (int i = 0; i < v->count; i++) {
  4. /* Print Value contained within */
  5. lval_print(v->cell[i]);
  6. /* Don't print trailing space if last element */
  7. if (i != (v->count-1)) {
  8. putchar(' ');
  9. }
  10. }
  11. putchar(close);
  12. }
  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. }
  8. }
  9. void lval_println(lval* v) { lval_print(v); putchar('\n'); }

我没法声明这些函数,因为它们互相调用了对方。

lval_expr_print 函数内部调用了 lval_print 函数,lval_print 内部又调用了 lval_expr_print。似乎是没有办法解决依赖性的。C 语言提供了前置声明来解决这个问题。前置声明只定义了函数的形式,而没有函数体(译者注:前置声明就是告诉编译器:“我保证有这个函数,你放心调用就是了”)。它允许其他函数调用它,而具体的函数定义则在后面。函数声明只要将函数定义的函数体换成 ; 即可。在我们的程序中,应该将 void lval_print(lval* v); 语句放在一个比 lval_expr_print 函数靠前的地方。在以后的编程过程中,你一定会再次遇到这个问题,所以请记住这个解决方案!

在主循环中,我们可以将求值部分移除了,替换为新写就的读取和打印函数。

  1. lval* x = lval_read(r.output);
  2. lval_println(x);
  3. lval_del(x);

正常情况下,你应该可以看到下面这样的结果。

  1. lispy> + 2 2
  2. (+ 2 2)
  3. lispy> + 2 (* 7 6) (* 2 5)
  4. (+ 2 (* 7 6) (* 2 5))
  5. lispy> * 55 101 (+ 0 0 0)
  6. (* 55 101 (+ 0 0 0))
  7. lispy>