【jjcc系列第三篇】如何編譯progn

progn是什麼玩意

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

其實很簡單,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

相關文章
相關標籤/搜索