Skip to content

期中報告

Soober9260 edited this page Jun 17, 2022 · 2 revisions

非自創

Minilisp

由C語言寫出的編譯器,包含以下功能

  • 整數,代號,以及虛數。
  • 全域變數(C語言)
  • 局部變數
  • 匿名函式
  • if 條件句
  • 原始函數
  • 自訂函數
  • 巨集系統
  • 垃圾回收

Compile

$ make

Minilisp 已經在 Linux x86/x86-64 和 64 位 Mac OS 上進行了測試。它並不是建構於體系結構上,因此可以在大部分類Unix操作系統上進行編譯和運行。

Test

MiniLisp 帶有一個全面的測試套件。為了運行測試,給 make 提供“test”參數。

$ make test

語言特徵

MiniLisp 是一個傳統的 Lisp 解釋器。它一次從標準輸入中讀取一個表達式,對其求值,然後打印出表達式的返回值。這是有效輸入的示例。

(+ 1 2)  

結果為3

字元

MiniLisp 支援整數定數, (), t, 符號, 串列定數

  • Integer literals are positive or negative integers.
  • () 是唯一假值. 也代表空值.
  • t是對自身求值的預定變義量。這是表示真值的首選方式,而任何非()值都被認為是真值。
  • 符號是有獨立名字的物件. 他們通常代表識別字,由於 MiniLisp 沒有字串類別,所以符號有時候被當字串使用
  • 串列定數是構造函數. 它的cdr元素會是ˋ()ˋ,或是一個點狀列表沒有任何ˋ()ˋ值,一個點狀列表會這樣表示 (a . b)

運算子表

cons 接受兩個參數並返回一個新的 cons 單元格,使第一個參數為car,第二個參數為 cdr

(cons 'a '(b)) ; -> (a b)

carcdrcons 單元的訪問器。car歸還carcdr歸還cdr

(car '(a . b)) ; -> a
(cdr '(a . b)) ; -> b

setcar突變一個 cons 單元格。setcar接受兩個參數,假設第一個參數是一個 cons 單元格。它將第二個參數的值設置為 cons 單元的car。

(define cell (cons 'a 'b))
cell  ; -> (a . b)
(setcar cell 'x)
cell  ; -> (x . b)

運算子

+操作域裡的總和 (+ 1) ; -> 1 (+ 1 2) ; -> 3 (+ 1 2 3) ; -> 6

- 如果操作域裡只有一個參數則否定操作域裡的參數 (- 3) ; -> -3 (- -5) ; -> 5

如果操作域裡不只一個參數,則由第一個參數減去後面的參數

(- 5 2)    ; -> 3
(- 5 2 7)  ; -> -4 

= 接受兩個參數,如果兩者是相同的整數則輸出 t ,不是則輸出()。

(= 11 11)  ; -> t
(= 11 6)   ; -> ()

< 接受兩個參數,如果第一個參數小於第二個參數,則輸出 t

(< 2 3)    ; -> t
(< 3 3)    ; -> ()
(< 4 3)    ; -> ()

條件句

(if cond then else) 是語言中唯一的條件。首先執行cond。如果結果為真值,執行then不然就是執行else

迴圈

(while cond expr ...) 執行 expr ... 直到 cond 得出為() 這是 MiniLisp 唯一支持的循環

如果您熟悉 Scheme,您可能想知道是否可以在 MiniLisp 中通過尾呼叫編寫循環。答案是不。尾呼叫會消耗 MiniLisp 中的堆疊空間,因此將會因為內存耗盡錯誤而失敗。

Equivalence test operators

eq 執行兩格參數如果兩個參數一樣則輸出 t 。 而 eq比較的是地址,用它對symbols(符號)快速地比較,測試兩個cons cells是否物理上是同一個對象

Output operators

println 將指定對象輸出

(println 3)               ; prints "3"
(println '(hello world))  ; prints "(hello world)"

Definitions

MiniLisps支持變量跟函數 可以用 define來定義

(define a (+ 1 2))
(+ a a)   ; -> 6

定義函數有兩種方式。一種方法是使用特殊形式 lambda。(lambda (args ...) expr ... 輸出得函數可以使用ˊdefineˊ指定變數

(define double (lambda (x) (+ x x)))
(double 6)                ; -> 12
((lambda (x) (+ x x)) 6)  ; 沒有指定也是同樣的結果

另一個則是 defun (defun fn (args ...) expr ...) 比起(define fn (lambda (args ...) expr ...)比較短

;; Define "double" using defun
(defun double (x) (+ x x))

(defun fn (expr . rest) rest)
(fn 1)     ; -> ()
(fn 1 2 3) ; -> (2 3)

變量是詞法範圍的並且具有不確定的範圍。即使在創建變量的函數返回後,對“外部”變量的引用仍然有效。

;; A countup function. We use lambda to introduce local variables because we
;; do not have "let" and the like.
(define counter
  ((lambda (count)
     (lambda ()
       (setq count (+ count 1))
       count))
   0))

(counter)  ; -> 1
(counter)  ; -> 2

;; This will not return 12345 but 3. Variable "count" in counter function
;; is resolved based on its lexical context rather than dynamic context.
((lambda (count) (counter)) 12345)  ; -> 3

setq為現有變量設置新值。如果未定義變量,則為錯誤。

(define val (+ 3 5))
(setq val (+ val 1))  ; increment "val"

巨集

巨集看起來類似於函數,但它們的不同之處在於Macro將表達式作為輸入並返回一個新的表達式作為輸出。(defmacro macro-name (args ...) body ...)定義一個Macro。

ex

(defmacro unless (condition expr)
  (list 'if condition () expr))

上面defmacro定義了一個新的巨集。除非unless是一個新的條件,它評估expr,除非條件是一個真值。您不能對函數做同樣的事情,因為所有參數都將在控件傳遞給函數之前進行評估。

(define x 0)
(unless (= x 0) '(x is not 0))  ; -> ()
(unless (= x 1) '(x is not 1))  ; -> (x is not 1)

macroexpand是一種方便的特殊形式,可以查看巨集的擴展形式。

(macroexpand (unless (= x 1) '(x is not 1)))
;; -> (if (= x 1) () (quote (x is not 1)))

gensym創建一個新符號,它永遠不會出現eq在除自身之外的任何其他符號上。對於編寫引入新標識符的巨集很有用。

(gensym) ; -> a new symbol

注釋

與傳統的 Lisp 語法一樣, ; (分號)開始單行註釋。持續到行尾。

Clone this wiki locally