progn
是Common Lisp裏面的一個special operator,見這份文檔的說明:http://clhs.lisp.se/Body/s_pr...git
如今已經支持了二元四則運算了,但如今這裏有一個大問題,就是這四個運算沒辦法嵌套着組合使用。好比,遇到下面這樣的代碼,jjcc2
函數就懵逼了github
(+ 1 (+ 2 3))
那要編譯這種代碼的話怎麼辦呢?一個比較直觀的作法,是引入臨時變量,來保存嵌套在其中的表達式的求值結果,而後再用變量來代替本來嵌套的表達式。修改後的代碼可能長這個樣子app
(setq a (+ 2 3)) (+ 1 a)
顯然,這是有前後的時間依賴關係的兩條語句,所以應當使用progn
將它們包裹起來,結果以下函數
(progn (setq a (+ 2 3)) (+ 1 a))
這樣整個表達式的求值結果,或者說它被編譯以後的運行結果,應當就是在寄存器EAX
中放入整數6了。因此,本篇未來解決對progn
的編譯問題。rest
其實很簡單,progn
可能有不定數量的form在其中,那麼只須要按照順序對它們一個個進行編譯,輸出彙編代碼就能夠了,所以最終jjcc2
被修改成以下的樣子code
(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
了。orm
全文完。htm
閱讀原文ci