[圖片上傳失敗...(image-636c17-1659876521176)]
前言
在上一個(gè)版本實(shí)現(xiàn)的腳本解釋器 GScript 中實(shí)現(xiàn)了基本的四則運(yùn)算以及 AST
的生成棒拂。
[圖片上傳失敗...(image-2b6f3b-1659876521176)]
當(dāng)我準(zhǔn)備再新增一個(gè) %
取模的運(yùn)算符時(shí),會(huì)發(fā)現(xiàn)工作很繁瑣而且?guī)缀醵际侵貜?fù)的玫氢;主要是兩步:
- 需要在詞法解析器中新增對
%
符號(hào)的支持。 - 在語法解析器遍歷 AST 時(shí)對
%
token 實(shí)現(xiàn)具體邏輯谜诫。
其中的詞法解析和遍歷 AST 完全是重復(fù)工作漾峡,所以我們可否能夠簡化這兩步呢?
Antlr
Antlr
就是做幫我們解決這些問題的常用工具喻旷,利用它我們只需要編寫詞法文件生逸,然后就可以自動(dòng)生成詞法、語法解析器,并且可以生成不同語言的代碼槽袄。
下面以 GScript
的示例來看看 antlr 是如何幫我們生成詞法分析器的烙无。
func TestGScriptVisitor_Visit_Lexer(t *testing.T) {
expression := "(2+3) * 2"
input := antlr.NewInputStream(expression)
lexer := parser.NewGScriptLexer(input)
for {
t := lexer.NextToken()
if t.GetTokenType() == antlr.TokenEOF {
break
}
fmt.Printf("%s (%q) %d\n",
lexer.SymbolicNames[t.GetTokenType()], t.GetText(),t.GetColumn())
}
}
//output:
("(") 0
DECIMAL_LITERAL ("2") 1
PLUS ("+") 2
DECIMAL_LITERAL ("3") 3
(")") 4
MULT ("*") 6
DECIMAL_LITERAL ("2") 8
Antlr
會(huì)自動(dòng)將我們的表達(dá)式解析為 token
,遍歷 token
時(shí)還能拿到該 token
所在的代碼行數(shù)遍尺、位置等信息截酷,在編譯期間做語法檢查非常有用。
要實(shí)現(xiàn)這些我們只需要編寫詞法乾戏、語法規(guī)則文件即可迂苛。
剛才的示例所對應(yīng)的詞法、語法規(guī)則如下:
expr
: '(' expr ')' #NestedExpr
| liter=literal #Liter
| lhs=expr bop=( MULT | DIV ) rhs=expr #MultDivExpr
| lhs=expr bop=MOD rhs=expr #ModExpr
| lhs=expr bop=( PLUS | SUB ) rhs=expr #PlusSubExpr
| expr bop=(LE | GE | GT | LT ) expr # GLe
| expr bop=(EQUAL | NOTEQUAL) expr # EqualOrNot
;
DECIMAL_LITERAL: ('0' | [1-9] (Digits? | '_'+ Digits)) [lL]?;
完整規(guī)則:https://github.com/crossoverJie/gscript/blob/main/GScript.g4
運(yùn)行:
antlr -Dlanguage=Go -o parser -visitor -no-listener GScript.g4
就可以幫我們生成 Go
的代碼(默認(rèn)是 Java
)鼓择,關(guān)于 Antlr
的詞法三幻、文法規(guī)則以及安裝步驟請參考官網(wǎng)。
而我們要實(shí)現(xiàn)具體的語法邏輯時(shí)只需要實(shí)現(xiàn)相關(guān)的接口呐能,Antlr
會(huì)自動(dòng)遍歷 AST
(當(dāng)然也可以手動(dòng)控制)念搬,同時(shí)在訪問不同的 AST
節(jié)點(diǎn)時(shí)會(huì)回調(diào)我們自己實(shí)現(xiàn)的接口,這樣我們就能編寫自己的語法規(guī)則了摆出。
以這里的新增的取模運(yùn)算為例:
func (v *GScriptVisitor) VisitModExpr(ctx *parser.ModExprContext) interface{} {
lhs := v.Visit(ctx.GetLhs())
rhs := v.Visit(ctx.GetRhs())
return lhs.(int) % rhs.(int)
}
當(dāng) Antlr
回調(diào) VisitModExpr
方法時(shí)朗徊,便能獲取到 % 符號(hào)左右兩側(cè)的數(shù)據(jù),這時(shí)只需要做相關(guān)運(yùn)算即可懊蒸。
基于這個(gè)模式這次新增了一個(gè) statement
荣倾,具體語法如下:
func TestGScriptVisitor_VisitIfElse8(t *testing.T) {
expression := `
if(3!=(1+2)){
return 1+3
} else {
return false
}`
input := antlr.NewInputStream(expression)
lexer := parser.NewGScriptLexer(input)
stream := antlr.NewCommonTokenStream(lexer, 0)
parser := parser.NewGScriptParser(stream)
parser.BuildParseTrees = true
tree := parser.Prog()
visitor := GScriptVisitor{}
var result = visitor.Visit(tree)
fmt.Println(expression, " result:", result)
assert.Equal(t, result, false)
}
Antlr 還有其他各種優(yōu)勢,比如可以解決:
- 左遞歸骑丸。
- 二義性舌仍。
- 優(yōu)先級。
等問題通危。
這里也推薦在 IDE 中安裝 Antlr 的插件铸豁,這樣就可以直觀的查看 AST 語法樹,可以幫我們更好的調(diào)試代碼菊碟。
[圖片上傳失敗...(image-f05c05-1659876521176)]
[圖片上傳失敗...(image-1adeb1-1659876521176)]
升級 xjson
借助 GScript
提供的 statement
节芥,xjson
也提供了有些有意思的寫法:
[圖片上傳失敗...(image-4a7b95-1659876521176)]
因?yàn)?xjson
的四則運(yùn)算語法沒有使用 Antlr
生成,所以為了能支持 GScript
提供的 statement
需要手寫許多詞法代碼逆害。
[圖片上傳失敗...(image-66f2e8-1659876521176)]
這也體現(xiàn)了 Antlr
這類前端工具的重要性头镊,效率提升是非常明顯的。
總結(jié)
借助于 Antlr
后續(xù) GScript
會(huì)繼續(xù)支持函數(shù)調(diào)用魄幕、更完善的類型系統(tǒng)相艇、面向?qū)ο蟮忍匦裕桓信d趣的朋友請持續(xù)關(guān)注纯陨。