Skip to content

midwork.md

Vialbum edited this page Jun 17, 2022 · 2 revisions

期中

研究專案minilisp且有使用到此專案

Minilisp

是一個由一千行的C語言所寫出的解譯器。他可有以下功能:

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

編譯

$ make

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

測試

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

$ make test

語言特點

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

(+ 1 2)

上面執行結果為"3"

字元

MiniLisp 支持整數文字、 ()t 、符號和列表文字。

  • 整數文字是正整數或負整數。
  • () 是唯一的假值。它也代表空列表。
  • t 是對自身求值的預定義變量。這是表示真值的首選方式,而任何非()值都被認為是真值。
  • 符號是具有唯一名稱的對象。它們用於表示標識符。因為 MiniLisp 沒有字符串類型,所以符號有時也被用作字符串的替代品。
  • 列表文字是缺點單元格。它要么是最後一個元素的 cdr 為的常規列表,要么是以任何非值()結尾的點列表。()一個虛線列表寫為(a . b)。

運算子表

cons 接受兩個參數並得出新的串列定數,使第一個參數為car,第二個參數為 cdr。

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

carcdr 是 cons 單元的訪問器。 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。如果結果為true(1),則進行評估。否則 評估else(0) 。

while迴圈

(while cond expr ...)執行expr ...直到cond被評估為 ()。這是 MiniLisp 支持的唯一循環。

等價測試運算符

eq接受兩個參數,t如果對象相同則返回。真正做eq的是指針比較,因此兩個對象碰巧具有相同的內容但實際上不同的對像被認為是不相同的eq。

輸出運算符

println 將指定對象輸出

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

定義(define)

MiniLisp 支持變量和函數。它們可以使用來定義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)  ; do the same thing without assignment

另一種方式是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"

巨集

巨集與函數的功能相同,但在編譯時,編譯時會以巨集取代原來的敘述,而函數則是一個跳躍敘述;在程式執行期間,由於巨集已經展開為它所代表的敘述,程式會一行一行執行下去,而碰到函數則是跳到函數定義的副程式去執行。 (defmacro macro-name (args ...) body ...) 下面是定義巨集的示範

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

上述的 defmacro 定義一個新巨集 unless. 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在除自身之外的任何其他符號上。對於編寫引入新標識符的Macro很有用。

(gensym)   ; -> a new symbol

詮釋

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