在上一篇文章中,新增了两个函数:inside-out
以及inside-out/aux
——曾经想过将inside-out/aux
放到前者的函数中用labels
来定义,但担忧很差调试,因此剥离了出来成为一个独立的函数——inside-out
基本上只是驱动了后者,真正地将嵌套表达式拆解开来的仍是inside-out/aux
。所以,为了让让这个编译器最终能够处理以下形式的代码git
(_exit (+ (+ 1 2) 3))
就须要先对inside-out/aux
进行一番改造,使其能够处理上述代码。github
在此以前,先处理一下inside-out/aux
目前的一些问题。在以前的实现中,因为使用了setf
对输入参数expr
进行了修改,所以在example3
中的列表实际上在第二次运行的时候已经不是代码中看到的那样子了。因此,先将inside-out/aux
改写为更pure的形式segmentfault
(defun inside-out/aux (expr result) "将嵌套的表达式EXPR由内而外地翻出来" (check-type expr list) ;; 出于简单起见,暂时只处理加法运算 (cond ((member (first expr) '(+ - * /)) (let ((operands '())) (if (listp (second expr)) ;; 第一个操做数也是须要翻出来的 ;; 翻出来后,result中的第一个元素就是一个没有嵌套表达式的叶子表达式了,能够做为setq的第二个操做数 (let ((var (gensym))) (setf result (inside-out/aux (second expr) result)) (let ((val (pop result))) (push `(setq ,var ,val) result) (push var operands))) (push (second expr) operands)) (if (listp (third expr)) (let ((var (gensym))) (setf result (inside-out/aux (third expr) result)) (let ((val (pop result))) (push `(setq ,var ,val) result) (push var operands))) (push (third expr) operands)) (push (cons (first expr) (nreverse operands)) result) result)) (t (push expr result) result)))
其实改动很简单,就是使用一个新的列表operands
来承载被修改后的符号或本来的表达式而已。接下来能够开始支持_exit
函数了。ide
其实要支持_exit
也是很简单的,直接模仿对加减乘除的处理便可。将处理第一个操做数部分的代码抄过来,基本上就搞定了。不过这样子不利于之后支持更泛用的函数调用的表达式,所以这里尝试将其改写为稍微通用一点的实现方式。函数
通用的地方就在于,不是只考虑两个参数或者一个参数的状况。其实在上一篇文章中应该就能够感觉到,对加减乘除的两个参数的处理也是至关有规律的,只须要将调用second
和third
分别提取输入表达式的第一和第二个参数的代码替换为处理一个来自于循环的变量便可。最终的代码以下调试
(defun inside-out/aux (expr result) "将嵌套的表达式EXPR由内而外地翻出来" (check-type expr list) ;; 出于简单起见,暂时只处理加法运算 (cond ((member (first expr) '(+ - * / _exit)) (let ((operands '())) ;; 对参数列表中的全部表达式都递归地进行【外翻】处理 (dolist (arg (rest expr)) (if (listp arg) (let ((var (gensym))) (setf result (inside-out/aux arg result)) (let ((val (pop result))) (push `(setq ,var ,val) result) (push var operands))) (push arg operands))) (push (cons (first expr) (nreverse operands)) result) result)) (t (push expr result) result)))
哈,通用的版本反而是最短的一个XD如今,inside-out
函数能够处理刚才的代码了。在REPL中运行以下代码rest
(inside-out '(_exit (+ (+ 1 2) 3)))
即可以获取翻转后的“线性”的代码code
(PROGN (SETQ #:G717 (+ 1 2)) (SETQ #:G716 (+ #:G717 3)) (_EXIT #:G716))
如此一来,也没有必要在stringify
函数中内置调用_exit
函数的固定代码了,stringify
改成以下的样子orm
(defun stringify (asm globals) "根据jjcc2产生的S表达式生成汇编代码字符串" (check-type globals hash-table) ;; 输出globals中的全部变量 ;; FIXME: 暂时只支持输出数字 (format t " .data~%") (maphash (lambda (k v) (format t "~A: .long ~D~%" k v)) globals) (format t " .section __TEXT,__text,regular,pure_instructions~%") (format t " .globl _main~%") (format t "_main:~%") (dolist (ins asm) (cond ((= (length ins) 3) (format t " ~A ~A, ~A~%" (first ins) (if (numberp (second ins)) (format nil "$~A" (second ins)) (second ins)) (if (numberp (third ins)) (format nil "$~A" (third ins)) (third ins)))) ((= (length ins) 2) (format t " ~A ~A~%" (first ins) (if (numberp (second ins)) (format nil "$~A" (second ins)) (second ins)))) ((= (length ins) 1) (format t " ~A~%" (first ins))))))
若是但愿调用_exit
来验证四则运算的计算结果的话,就显式地调用_exit
函数吧,代码以下递归
(defun example4 () "处理含有嵌套表达式的_exit函数调用" (let ((expr '(_exit (+ (- (* (/ 1 2) 3) 4) 5))) (ht (make-hash-table))) (let* ((expr2 (inside-out expr)) (asm (jjcc2 expr2 ht))) (stringify asm ht))))
全文完。