關於編譯器 parsing 的理論知識我沒有完整學, 就是補過一些片斷
因此這篇文章裏可能有用理論知識很容易解釋的一些問題, 我並無看到
並且 Cirru 的語法堅持要用縮進, 現有的方案是難以讓我知足的
這些天用 Go 重寫了 Cirru 的 parser, 後面會對思路作一些解釋git
以前一個版本的 Cirru parser 解析縮進的方案比較原始, 就是解析文本塊
好比說下面這樣一個文本塊, 我想要按照縮進解析到到一個嵌套的關係:github
a b c d e f
(a (b ((c)) (d))) (e (f))
我從前的作法是對文本進行分割, 先獲得 a
和 e
開頭的兩塊文本
而後取出 a
的文本塊, 按照縮進進行處理, 獲得 a
, 以及縮進之後的文本:數組
a b c d
b c d
而後按照縮進一步步解析, 到縮進消失也就解析完了網絡
而文本部分, 我記錄了一個狀態機, 每次讀取一個字符時都有一個狀態用於判斷
好比說, 對於包含字符串的這樣一段代碼, 每一個字符解析都有狀態:函數
a "b \c d"
token 'a' token ' ' token '"' -> string string 'b' string ' ' string '\' -> escape escape 'c' -> string string ' ' string 'd' string '"' -> token
我還在上面標記了狀態轉移的步驟, 特定的符號會觸發狀態的轉移
對於括號的縮進, 我原先採起的是 Lispy 教程裏的方案分開處理的:指針
= a (str 1)
具體的作法用的是高階函數, 這裏不展開了code
後來漸漸想到這個方法有很差的地方, 當初也是爲了技能不夠而折衷的
就是, 我解析一段文本, 混用了好幾種方式來處理, 更難定位 bug
另外一個, 就是我必定要獲取整個文件, 先掃描好縮進, 而後才能開始解析
而一般的解析器能夠接收流的數據進行解析, 好比接收網絡傳送文件的同時進行解析教程
Cirru 的語法規則就幾條, 引號和轉義, 括號, 縮進, 另外兩個特殊規則(, $
)
特殊規則是在語法樹解析完成後作的, 構建樹的過程先不考慮
引號和轉義已經能用狀態機解決, 那麼我想到的方案是把縮進也用狀態機表示
至於括號, 括號對應的是一個嵌套關係的改變, 實際上沒有狀態改變token
那麼怎麼把縮進用狀態表示呢, 前面的幾個字段可不足以表達縮進啊
所以我引入了一個字段 level
, 配合每一行的縮進數量 buffer
來判斷狀態
好比上邊的縮進語法, 我作一些簡化, 而後用狀態表示出來ci
a b c d e f
其中 level
對應前一個縮進的長度, len(buffer)
對應當前的縮進:
level len(buffer) -> 0 0 -> 0 2 -> push 2 6 -> push push 6 4 -> pop 4 0 -> pop pop 0 2 -> pop
這裏的 pop push
對應跳出當前數組和建立數組的操做, 對應表達式的嵌套
而括號的行爲也和這裏的 pop push
對應:
= a (str 1)
token '=' token ' ' token 'a' token ' ' token '(' -> push token 's' token 't' token 'r' token ' ' token '1' token ')' -> pop
當有一個嵌套的數組的結構, 有個指針用於跳到上級和建立新數組
就能夠讀取文件, 生成一棵 Cirru 的語法樹, 包含嵌套的表達式
因爲 Cirru 的語法簡單, 這樣一個簡單的狀態機就完成了:
https://github.com/Cirru/parser
考慮假如之後會增長其餘的語法的話, 我猜測是直接添加狀態來擴展了
好比轉義的內容, 要支持 "\u02aa"
之類稍微複雜的語法 固然也可能 Cirru 太不實用, 我徹底沒有添加語法的需求 Cirru 如今出於很是原始的狀態, 我並不清楚能作些什麼