go Antlr重構(gòu)腳本解釋器實(shí)現(xiàn)示例
前言
在上一個(gè)版本實(shí)現(xiàn)的腳本解釋器 GScript 中實(shí)現(xiàn)了基本的四則運(yùn)算以及 AST
的生成。
當(dāng)我準(zhǔn)備再新增一個(gè) %
取模的運(yùn)算符時(shí),會(huì)發(fā)現(xiàn)工作很繁瑣而且?guī)缀醵际侵貜?fù)的;主要是兩步:
- 需要在詞法解析器中新增對(duì)
%
符號(hào)的支持。 - 在語(yǔ)法解析器遍歷 AST 時(shí)對(duì)
%
token 實(shí)現(xiàn)具體邏輯。
其中的詞法解析和遍歷 AST 完全是重復(fù)工作,所以我們可否能夠簡(jiǎn)化這兩步呢?
Antlr
Antlr
就是做幫我們解決這些問(wèn)題的常用工具,利用它我們只需要編寫詞法文件,然后就可以自動(dòng)生成詞法、語(yǔ)法解析器,并且可以生成不同語(yǔ)言的代碼。
下面以 GScript
的示例來(lái)看看 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ù)、位置等信息,在編譯期間做語(yǔ)法檢查非常有用。
要實(shí)現(xiàn)這些我們只需要編寫詞法、語(yǔ)法規(guī)則文件即可。
剛才的示例所對(duì)應(yīng)的詞法、語(yǔ)法規(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ī)則:github.com/crossoverJi…
運(yùn)行:
antlr -Dlanguage=Go -o parser -visitor -no-listener GScript.g4
而我們要實(shí)現(xiàn)具體的語(yǔ)法邏輯時(shí)只需要實(shí)現(xiàn)相關(guān)的接口,Antlr
會(huì)自動(dòng)遍歷 AST
(當(dāng)然也可以手動(dòng)控制),同時(shí)在訪問(wèn)不同的 AST
節(jié)點(diǎn)時(shí)會(huì)回調(diào)我們自己實(shí)現(xiàn)的接口,這樣我們就能編寫自己的語(yǔ)法規(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
,具體語(yǔ)法如下:
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)勢(shì),比如可以解決:
- 左遞歸。
- 二義性。
- 優(yōu)先級(jí)。
等問(wèn)題。
這里也推薦在 IDE 中安裝 Antlr 的插件,這樣就可以直觀的查看 AST 語(yǔ)法樹,可以幫我們更好的調(diào)試代碼。
升級(jí) xjson
借助 GScript
提供的 statement
,xjson
也提供了有些有意思的寫法:
因?yàn)?xjson
的四則運(yùn)算語(yǔ)法沒有使用 Antlr
生成,所以為了能支持 GScript
提供的 statement
需要手寫許多詞法代碼。
這也體現(xiàn)了 Antlr
這類前端工具的重要性,效率提升是非常明顯的。
總結(jié)
借助于 Antlr
后續(xù) GScript
會(huì)繼續(xù)支持函數(shù)調(diào)用、更完善的類型系統(tǒng)、面向?qū)ο蟮忍匦?;感興趣的朋友請(qǐng)持續(xù)關(guān)注。
源碼地址:
以上就是go Antlr重構(gòu)腳本解釋器實(shí)現(xiàn)示例的詳細(xì)內(nèi)容,更多關(guān)于go Antlr腳本解釋器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Golang使用JWT進(jìn)行認(rèn)證和加密的示例詳解
JWT是一個(gè)簽名的JSON對(duì)象,通常用作Oauth2的Bearer?token,JWT包括三個(gè)用.分割的部分。本文將利用JWT進(jìn)行認(rèn)證和加密,感興趣的可以了解一下2023-02-02Go語(yǔ)言配置數(shù)據(jù)庫(kù)連接池的實(shí)現(xiàn)
本文內(nèi)容我們將解釋連接池背后是如何工作的,并探索如何配置數(shù)據(jù)庫(kù)能改變或優(yōu)化其性能。文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12go語(yǔ)言中sort包的實(shí)現(xiàn)方法與應(yīng)用詳解
golang中也實(shí)現(xiàn)了排序算法的包sort包,所以下面這篇文章主要給大家介紹了關(guān)于go語(yǔ)言中sort包的實(shí)現(xiàn)方法與應(yīng)用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11golang如何用type-switch判斷interface變量的實(shí)際存儲(chǔ)類型
這篇文章主要介紹了golang如何用type-switch判斷interface變量的實(shí)際存儲(chǔ)類型,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04深入了解Go語(yǔ)言的基本語(yǔ)法與常用函數(shù)
這篇文章主要為大家詳細(xì)介紹一下Go語(yǔ)言中的基本語(yǔ)法與常用函數(shù),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語(yǔ)言有一定的幫助,需要的可以參考一下2022-07-07golang基礎(chǔ)之Gocurrency并發(fā)
這篇文章主要介紹了golang基礎(chǔ)之Gocurrency并發(fā),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07