解釋器是一種複雜有用的工具,用到了不少技能。程序員
因爲 Lisp 簡單優雅的設計,讓 Lisp 解釋器的設計成爲一種每一個程序員都有能力嘗試的挑戰。正則表達式
沒有複雜的形式化語法。解釋器的每個部分都向程序員開放,代碼和數據結構的統一,讀取期,編譯期和運行期的分離,讓這門語言的擴展能力無以倫比,達到了理論上能想象的任何程度。算法
只有幾個字符的讀取會產生不一樣的狀態。括號,空格,單引號,雙引號,反引號,井號,分號,反斜槓,逗號,冒號。數組
( 左括號標誌一個列表的開始,) 右括號標誌一個列表的結束。數據結構
(setq var 10)
空格分離兩個不一樣的語言單位,能夠是字符串,也能夠數字,符號,keyword 等函數
(if nil 1 0)
單引號是 (quote xx) 的縮寫,算是一個語法上的簡寫。工具
'(1 2 3) == (quote 1 2 3) == (list 1 2 3)
雙引號開始一個字符串,也結束一個字符串。設計
(print "hello world!")
反引號是另一種 (quote xx) 的縮寫,其中的一些符號會有特別的意義,例如逗號表明一個列表模板中的變量插入,,@(逗號加小老鼠)表明對一個內插列表的展開。code
`(print ,var ,@list 10)
井號(或問號)一般開始一個字符,或一個特別的數據結構,例如多維數組或一個函數的解引用。orm
#\a #2(1 2 3)
冒號開始一個關鍵字,這個關鍵字相似 Ruby 的符號,是一種自身求值的字符串表示方法而已,用於作函數參數的鍵指示,是一種在數據結構上快速的字符串實現。
(defun (:from x :end y) (print '(x y))
反斜槓一般對後面的字符串進行轉義,表明一些特別的字符,
分號是註釋的開始標記,到每行的結束,除了 Scheme,沒有多行註釋的起始標記,須要每行都進行註釋標記。
;; This is comments
還有一些符號,是在進入一個狀態後纔會有效。
最多的就是字符串,進入字符串模式後,有格式化標記,%(emacs lisp), ~ (common lisp). 還有轉義標記 \ 來表示不能打印或顯示的字符。在 Common Lisp 中,井號以後的反斜槓表明一個字符的開始。還有關於正則表達式一些特別字符的標記,和字符串是不一樣的。在不一樣的上下文中。正則表達式和字符串是不一樣的類型,雖然語法相同,都是用雙引號界定的。
(format t "%times" time)
進入符號定義後,容許的字符是不少的,一般用一些字符作後綴來表示一些特定的命名規則,例如歎號結尾表示有反作用,問號結尾表示返回一個真假值。
(symbol? var) => t (set! var 10)
讀取到的數據結構能夠是嵌套的。全部的數據類型都是動態的,用一個符號表示。
只有一種或兩種數據結構,就是列表或向量,也能夠抽象成一個序列。
同時創建了好幾個數據結構,來維護生成的表明代碼的樹形數據結構。
數字表,全部的數字,都用一個數字 ID 來表示一個數字。
關鍵字表,這個表是全局共享的,鍵和值內容相同。
符號表,每一個符號都用 name 映射一個字符串名字,用 value 映射一個值,這個值多是一個數據結構, plist 映射一個屬性表,是一個嵌套的查詢表,也是用字符串作鍵,用一個對象或原子類型作值。函數也多是一個單獨的鍵來定義,也可能定義在 value 中。
保存局部變量的一個符號表列表,不一樣深度的列表,有不一樣的局部符號表。
掃描數據結構中每一個列表的第一個符號,判斷是不是一個用 defmacro 定義的宏,若是是,就按照定義的規則,將這個列表進行轉換,並替換掉原來的數據結構。這個掃描是反覆進行的,直到找不到符合條件的宏的定義。
遞歸的,深度優先的對列表或符號進行解釋,自身求值的返回自身,列表結構就把第一個符號當成函數名稱,後面的當成參數,進行運算。返回計算的值或是一個數據結構。
在讀取期,一些實現容許對讀取的規則進行動態定義,改變一些字符的含義,是讀取期的讀取期。
在編譯期和運行期,宏或函數均可能會返回值或返回一個數據結構,而這個數據結構,就會替換掉原來的表達式,成爲新的將要計算的表達式。尤爲編譯期的這種擴展,能夠任意改變數據結構,在文法上對代碼進行擴展,實現必定範圍甚至是全局的代碼變動和重寫,將抽象實現到代碼的形式上,實現文法和算法的多重轉換。
許多自稱是 Lisp 方言的語言,在這點上,因爲太多分化的語法,很難將代碼表現成一種和數據統一的數據結構,從而實現對代碼的重寫成爲泡影。因此,在語法上追求的各類不一樣的形式,將會讓解析後的數據結構喪失重寫代碼的能力,算是一個平衡吧。
Lisp 雖然在不少表現形式上笨拙,難看,但他的這種擴展性讓他有能力擔當任何形式的抽象語言的進化和表示。成爲永不過期的語言。