Cirru 解析縮進的方案

關於編譯器 parsing 的理論知識我沒有完整學, 就是補過一些片斷
因此這篇文章裏可能有用理論知識很容易解釋的一些問題, 我並無看到
並且 Cirru 的語法堅持要用縮進, 現有的方案是難以讓我知足的
這些天用 Go 重寫了 Cirru 的 parser, 後面會對思路作一些解釋git

舊的方案

解析縮進部分

以前一個版本的 Cirru parser 解析縮進的方案比較原始, 就是解析文本塊
好比說下面這樣一個文本塊, 我想要按照縮進解析到到一個嵌套的關係:github

a
  b
      c
    d
e
  f
(a (b ((c)) (d)))
(e (f))

我從前的作法是對文本進行分割, 先獲得 ae 開頭的兩塊文本
而後取出 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 如今出於很是原始的狀態, 我並不清楚能作些什麼

相關文章
相關標籤/搜索