【jjcc系列第三篇】如何编译progn

Liutos

progn是什么玩意

progn是Common Lisp里面的一个special operator,见这份文档的说明:http://clhs.lisp.se/Body/s_pr...

为嘛要编译这东西

现在已经支持了二元四则运算了,但现在这里有一个大问题,就是这四个运算没办法嵌套着组合使用。比如,遇到下面这样的代码,jjcc2函数就懵逼了

(+ 1 (+ 2 3))

那要编译这种代码的话怎么办呢?一个比较直观的做法,是引入临时变量,来保存嵌套在其中的表达式的求值结果,然后再用变量来代替原本嵌套的表达式。修改后的代码可能长这个样子

(setq a (+ 2 3))
(+ 1 a)

显然,这是有先后的时间依赖关系的两条语句,因此应当使用progn将它们包裹起来,结果如下

(progn
  (setq a (+ 2 3))
  (+ 1 a))

这样整个表达式的求值结果,或者说它被编译之后的运行结果,应当就是在寄存器EAX中放入整数6了。所以,本篇将来解决对progn的编译问题。

如何编译progn

其实很简单,progn可能有不定数量的form在其中,那么只需要按照顺序对它们一个个进行编译,输出汇编代码就可以了,因此最终jjcc2被修改为如下的样子

(defun jjcc2 (expr)
  "支持两个数的四则运算的编译器"
  (cond ((eq (first expr) '+)
         `((movl ,(second expr) %eax)
           (movl ,(third expr) %ebx)
           (addl %ebx %eax)))
        ((eq (first expr) '-)
         `((movl ,(second expr) %eax)
           (movl ,(third expr) %ebx)
           (subl %ebx %eax)))
        ((eq (first expr) '*)
         ;; 将两个数字相乘的结果放到第二个操作数所在的寄存器中
         ;; 因为约定了用EAX寄存器作为存放最终结果给continuation用的寄存器,所以第二个操作数应当为EAX
         `((movl ,(second expr) %eax)
           (movl ,(third expr) %ebx)
           (imull %ebx %eax)))
        ((eq (first expr) '/)
         `((movl ,(second expr) %eax)
           (cltd)
           (movl ,(third expr) %ebx)
           (idivl %ebx)))
        ((eq (first expr) 'progn)
         (let ((result '()))
           (dolist (expr (rest expr))
             (setf result (append result (jjcc2 expr))))
           result))))

就酱就足够了。下一篇,是时候讲一下如何编译setq了。

全文完。

阅读原文

阅读 1.1k

留坨诗
一些与编程有关的有趣的东西
169 声望
3.7k 粉丝
0 条评论
169 声望
3.7k 粉丝
文章目录
宣传栏