使用antlr4構造我的語法樹
- 2020 年 1 月 13 日
- 筆記
一、編譯原理
編譯器的前端和後端。前端指的是編譯器對程序代碼的分析和理解。前端階段只與語言的語法有關,而和目標機器無關。後端則是生成目標機器的目標代碼有關。第一節說說編譯器的前端技術。

編譯器將一般會將詞法和語法解析器分開實現。
1.1、詞法(Lexer)
英語一般用空格和標點將單詞隔開,但是在計算機,僅僅用空格和標點分割是不夠的。比如「a!=5「。
詞法規則玩玩是用類似於正則語法的表達式生成「有限狀態機」算法,並根據這些算法切割出token。
詞法規則負責從輸入讀取,並解析成一個個token符號。為了方便,antlr一般將這些token編號用數字表示。
詞法規則 antlr語法表示規則,查看以下example:
INTERGER: DIGIT+ |'0'[Xx] HEX_DIGIT+ ;
- 一個分號";"表示結束
- 一個標識符必須是全大寫:
- 一個冒號表示開始
- 一個"|"表示可選
可用的選項還有,類似於正則文法。
詞法 |
意義 |
---|---|
A |
匹配A |
A B |
匹配A緊接着匹配B |
(A|B) |
匹配A或者B |
『text』 |
匹配text字符串 |
A? |
A出現0次或者1次 |
A+ |
A出現1次或者多次 |
A* |
A出現0次或者多次 |
[A-Z0-9] |
在範圍內的字符或數字 |
『a』..'z' |
類似於[a-z],另一種表達 |
-[A-Z] |
不匹配[A-Z]的字符 |
. |
任意字符 |
1.2.1顯式詞法
以大寫字母開頭。或者是有名的詞法規則。
比如說PROJECT: "antlr4_code_gen"
1.2.2 匿名隱式詞法
他的位置在於parser之後,但是在顯式詞法之前。一般都是以「T__數字」表示。比如說這個匿名的詞法代表着一個冒號的token。

1.2.3 fragment:分片表示
INTERGER: DIGIT+ | '0' [Xx] HEX_DIGIT+ ; fragment DIGIT: [0-9]; fragment HEX_DIGIT: [0-9A-Fa-f];
1.2.4 詞法的優先級
匹配遵循以下的優先級準則:
- 匹配輸入的最多字符串的那個詞法
- 如果是特殊字符比如「{」,「」:」,那麼使用隱式語法匹配
- 如果匹配多個詞法,則選按先後順序找最先匹配到的那個
1.2.5 詞法的命令
詞法命令用於操作解析到的token,比如常用到的skip:放棄解析到的token
WHITESPACE: { rn}->skip
其他的還有channel(n),type(n),mode(n), pushMode(n),popMode(n)
1.1.4 詞法的執行動作
比如說以下的兩個例子:
ID: [A-Z]+ {log{"matched rule"}} ID: {A-Z}+ {isIdValid()}
1.2、語法(Rule)
詞法分析是識別一個個token,而語法分析是識別出程序的語法樹狀結構。這棵樹也叫做AST(Abstrace Syntax Tree)抽象語法樹。參考這個網址給出的演示,https://resources.jointjs.com/demos/javascript-ast,如下的表達式將被解析出一顆AST樹。


1.3、語義分析
語義分析的目的是消除語義中模稜兩可的「二義性」。比如一個變量同時定義在花括號外部和內部,那麼到底該用哪一個。
二、antlr使用
2.1Antlr是什麼
antlr是java實現的編譯工程,歷經20多年發展,目前是4.7版本。雖然是java實現的編譯工具,但是antlr支持生成cpp、java、python、c#等的解析運行庫,可以當做多種語言的解析工具用。
2.2安裝antlr
2.2.1 安裝依賴java環境
在安裝好的java環境,需要把官網下載的包antlr-4.7.2-complete.jar放在某個文件路徑下,並把這個路徑加到CLASS_PATH。
CLASSPATH=.:/usr/local/lib/antlr-4.7.2-complete.jar
2.3使用antlr
設置antlr4的快捷命令:
antlr4='java -jar /usr/local/lib/antlr-4.7.2-complete.jar'
根據語法文件生成相對應語言版本的解析工具代碼。
Bbcode.g4的規則如下:

生成命令:
antlr4 -Dlanguage=Cpp -visitor ./Bbcode.g4 -o antlr4-bbcode
- -Dlangguage是生成Cpp,如果不指定,默認是java
- -visitor額外生成vistor模式訪問的工具代碼,沒有指定默認是listener模式。
- *g4代表着你的g4文法文件
- -o輸出代碼文件到哪個文件夾下
輸出的代碼目錄結構如下:



三、使用antlr-runtime構建自己的代碼工程
3.1 antlr結構
提取出相對應語言版本的antlr-runtime目錄到自己的工程。這裡以cpp舉例。https://github.com/antlr/antlr4/tree/master/runtime/Cpp。把git工程拉到本地。然後
cp Cpp /data/mariolu/antlr4-runtime -rf
在build目錄執行編譯,這裡注意cmake設置宏把自己的antlr的jar路徑替換上去
mkdir build && mkdir run && cd build cmake .. -DANTLR_JAR_LOCATION=/usr/local/lib/antlr-4.7.2-complete.jar -DWITH_DEMO=True make
設置安裝路徑,注意吧DESTDIR設置成自己的路徑
DESTDIR=/runtime/Cpp/run make install
然後可以在/runtime/Cpp/run找到lib包和include包,這兩個包都可以拷貝到自己的工程下,

3.2 antlr的訪問模式
listener模式是antlr解析AST樹的各個節點,並調用相應的hook函數,而visitor需要實現遍歷訪問,如果沒有主動visit,則不會進行處理。監聽者模式有點類似於XML的解析語法,在這顆AST語法樹(類似於DOM樹),當解析到node,則調用listener的hook函數接口。
兩者的區別是啥:

3.2.1 使用listener模式

3.2.2 使用visitor模式

四、有什麼用
可以模擬解析,了解學習某種編程語言特性。
也可以自定義自己的語法規則,拿來自動化生成代碼。