使用golang+antlr4構建一個自己的語言解析器(完結篇)

ThirteenAnimation發表於2023-03-28

Goland 中Antlr4外掛

在goland中安裝Antlr4外掛,用於識別輸入的字元在在語法檔案中生成的語法樹的樣子,大概就是如下的摸樣

下載步驟:
1.點選檔案中的設定選項
image
2.在外掛目錄下輸入Antlr4搜尋外掛
image
3.點選安裝即可

編寫自己的語言語法檔案

編寫語法之前,我們首先要構思一下自己的DSL都有什麼關鍵字,這是個重要的步驟,就像我們學習java或者Golang一樣,首先知道這個語法裡面都有那些關鍵字。
我定義的DSL中有四則運算,有比較運算,還有邏輯運算,變數可以有數字、字串、時間格式

關鍵字

四則運算

作用 符號
乘法 *
除法 /
加法 +
減法 -

比較運算

作用 符號
等於 =
不等於 <>
大於 >
大於等於 >=
小於 <
小於等於 <=

邏輯運算

作用 符號
&&
||
如果 if
否則 else

變數

作用 符號
數字 ('0' | [1-9] ('_'? [0-9])*)
文字 [\p{Nd}]
字串 LETTER (LETTER
日期 ([0-9]{4}/[0-1]{0,1}[0-9]/[0-3]{0,1}[0-9])

常量輔助符號

作用 符號
.
逗號 ,
左括號 (
右括號 )
分號 ;
多行註釋 '/*' .*? '*/'

上述就是我們的Token列表,可以建立一個文法檔案單獨寫Token,文法檔案申明:lexer grammar Lexer;

編寫語法

編寫語法之前,我們需要構思一下,我們的DSL可以支援那些語法操作,例如四則運算可以支援字串運算嗎?日期支援四則運算嗎?我們可以從基礎開始編寫,例如我們把變數使用算則模式編寫成一個語法規則,

simpleStmt:NUMBER|TEXT|STRING|DATE

我的DSL中支援任何資料的四則運算,那麼我就可以使用simpleStmt和四則運算子號組成四則運算

expression:
simpleStmt #SimpleExpression
|expression op = (MUL|DIV) expression #MulDiv
|expression op = (ADD|SUB) expression #AddSub
;

這時候我們就定義了支援四則運算的語法規則,我們來試一下語法定義的對不對。
image
發現我們輸入的加法運算和乘法運算都可以被解析,說明我們的語法定義正確。接下來我們新增比較運算

我定義的DLS支援所有資料做比較運算,那麼我直接在上面的expression中新增比較運算就可以了,這裡需要注意的是比較運算如果希望有優先順序,需要先定義優先順序高的比較符號,我這裡沒有優先順序操作,所以都是平級的。

新增比較運算子號

expression:
simpleStmt #SimpleExpression
|expression op = (MUL|DIV) expression #MulDiv
|expression op = (ADD|SUB) expression #AddSub
|expression op = (EQ|NE|LT|LE|GT|GE) expression #Compare
;

驗證一下比較運算語法定義的是否正確。
image
發現沒有錯誤,正確解析出樹就代表語法定義的正確。接下來大家可以自己構思一下剩下的語法規則,或者新增自己的語法規則了。大概思路就是先想一個語法希望是什麼樣,然後編寫語法規則,然後輸入希望的格式驗證語法規則。

編寫Listener

還是老樣子,編寫號語法檔案,我們執行Antlr4生成執行時語言為Go的命令:
java -jar 'C:\Program Files\Java\antlr\antlr-4.12.0-complete.jar' -Dlanguage=Go -no-visitor -package parser *.g4
編寫監聽器類

type CalcListener struct{
	*parser.BaseCalcListener //繼承Listener基類
	*antlr.DefaultErrorListener //繼承錯誤基類
}

//發生錯誤時,處理錯誤
func (l *CalcLister) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) {

}

//退出MulDiv語法時
func (l *CalcLister) ExitMulDiv(c *parser.MulDivContext) {

}
//退出AddSub語法時
func (l *CalcLister) ExitAddSub(c *parser.AddSubContext) {

}
//退出數字語法時
func (l *CalcLister) ExitNumber(c *parser.NumberContext) {

}

這裡細心的小夥伴已經發現,在語法檔案中使用“#”指定的節點名稱在監聽器中回生成一個節點方法,在Antlr4中,“#”號代表手動指定語法規則名稱,需要注意的是,不要跟Token和規則名稱重複。

遍歷語法樹

image
Antlr4遍歷語法樹時,使用DFS方式遍歷樹

監聽模式和訪問模式

Antlr4提供了兩種遍歷語法樹的方式,監聽模式和訪問模式,預設是監聽模式,如果希望使用訪問模式的話,需要修改命令:
java -jar 'C:\Program Files\Java\antlr\antlr-4.12.0-complete.jar' -Dlanguage=Go -visitor -package parser *.g4
這樣會生成訪問者模式和監聽者模式

calc_base_listener.go //監聽者模式基類檔案
calc_base_visitor.go //訪問者模式基類檔案

訪問者模式:先遍歷父節點,然後遍歷子節點
監聽者模式:Enter先進入父節點,Exit最後退出父節點
個人建議還是使用監聽者模式,在Enter控制子節點訪問,Exit做父節點子樹執行邏輯。訪問者模式控制能力更強,監聽者模式需要遍歷整個樹。

至此,使用golang+Antlr4就可以定義一個屬於自己的語法規則的解析器了,如果有哪裡不同的可以給小編留言,我們共同學習!!!

相關文章