使用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樹。

三、程式程式碼
四、程式碼對應的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的規則如下:

五、Bbcode.g4文件

生成命令:

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

輸出的程式碼目錄結構如下:

圖六、antlr生成目錄結構
圖7、解析出的規則
圖八、token詞法文件

三、使用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包,這兩個包都可以拷貝到自己的工程下,

圖九、拷貝runtime庫和頭文件到自己的工程路徑下

3.2 antlr的訪問模式

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

兩者的區別是啥:

圖十、使用Listener模式和Visitor模式訪問

3.2.1 使用listener模式

圖十一、listener模式

3.2.2 使用visitor模式

圖十二、visitor模式

四、有什麼用

可以模擬解析,了解學習某種程式語言特性。

也可以自定義自己的語法規則,拿來自動化生成程式碼。