最近幾年的項目技術難點都和編譯原理,抽象語法樹,代碼編輯器 有關系。現在時間有點空,先從基礎了解起來,讓有些交互和提示能夠更智能些。
編譯原理-Parser
編譯原理 其實就是 讓計算機懂的 “437+734” 這樣的字符串編程 sum 437, 734 計算機懂得的機器碼。實際場景中可能是從一種高級語言編譯成一種低級語言。
編譯過程份成2個步驟:
1.詞法分析
詞法分析就是 通過定義的正則表達式,把輸入的字符串變成一個個標記(token)。
“437+734” => NUM PLUS NUM
2.語法分析
根據定於的語法,把上邊的token通過移進/歸約自動機,一步步的移入,規約,變成抽象語法樹,最終產生結構。
文法 E => NUM PLUS NUM => sum(437, 734);
這個編譯代碼的程序我們叫做解析器,分析器,Parser。
解析器生成器 parser generator
在實際情況中,有專門用來生成Parser的程序,解析器生成器。
前端的js語法分析器生成器,還是瞞多的 https://tomassetti.me/parsing-in-javascript/,不同的語法生成器支持的文法也有差別。
JISON介紹
項目中用到的是jison庫,這個庫支持(lr0, slr, lr1, ll, lalr)文法。一般我們程序中使用的是lalr文法。
可以通過debugger的方式去理解移入規約的過,和jison的使用。http://nolanlawson.github.io/jison-debugger/。
使用起來很簡單,官網的文檔也比較詳細, http://zaa.ch/jison/docs/。定義jison文件,通過 node 命令 "jison 文件"生成Parser的js文件;
jison是bison在js端的實現,但是也不是完全實現了相關的功能。
從http://nolanlawson.github.io/jison-debugger/上就能看到下面的一些文法格式
/* 詞法文法 */ %lex /* 選項設置 flex 最長匹配原則 case-insensitive 忽略大小寫 */ %options flex case-insensitive %% \s+ /* skip whitespace */ [0-9]+("."[0-9]+)?\b return 'NUMBER' <<EOF>> return 'EOF' /lex /*操作符優先級設置*/ %left '+' '-' %left '*' '/' /*語法開始的非終結符號*/ %start expressions %% /* 詞法 */ expressions : e EOF {return $1;} ;
試了下http://dinosaur.compilertools.net/bison/bison_10.html#SEC84,bison文檔上面的這個文法不支持,所以使用的時候自己要看着處理
2.jison的輸出
jison生成的Parser的輸出其實是文法自己定義的,可以是一個結果值,可以是ast樹,也可以是用戶自己在文法上面定義的結果
3.Parser.js
其實這個可以直接研究下源代碼,源代碼看起來雖然有些難度,主要是語法分析的邏輯,移入規則邏輯,標示符,終結符,非終結符號等等相關的定義,其他詞法的邏輯。
和實際使用中的有的一些變量還是比較容易看懂,或者說debug一下大概就知道有哪些參數了。
文法定義中的參數
yy: 這個屬性是用戶自己定義的,在編譯期間共享,這樣也是代碼更加靈活。
4.誤區
在使用中,會有一些誤區存在,特別是移入規約的順序,可能和我們平時看代碼存在差異。比如說上面的例子。
我們需要在RULE{expr}中的expr內容 有另外一種含義,
但是實際情況的執行順序其實是先 expr規則規約,才是 RULE{expr}規則規約,所以在進行expr規約的時候並不知道它是在RULE{}里面的,所以我們通過Parser生成ast,再通過ast遍歷來解決這個問題可能會更好一些。