網站首頁 編程語言 正文
前言
在上一個版本實現的腳本解釋器 GScript 中實現了基本的四則運算以及 AST
的生成。
當我準備再新增一個 %
取模的運算符時,會發現工作很繁瑣而且幾乎都是重復的;主要是兩步:
- 需要在詞法解析器中新增對
%
符號的支持。 - 在語法解析器遍歷 AST 時對
%
token 實現具體邏輯。
其中的詞法解析和遍歷 AST 完全是重復工作,所以我們可否能夠簡化這兩步呢?
Antlr
Antlr
就是做幫我們解決這些問題的常用工具,利用它我們只需要編寫詞法文件,然后就可以自動生成詞法、語法解析器,并且可以生成不同語言的代碼。
下面以 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
會自動將我們的表達式解析為 token
,遍歷 token
時還能拿到該 token
所在的代碼行數、位置等信息,在編譯期間做語法檢查非常有用。
要實現這些我們只需要編寫詞法、語法規則文件即可。
剛才的示例所對應的詞法、語法規則如下:
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]?;
完整規則:github.com/crossoverJi…
運行:
antlr -Dlanguage=Go -o parser -visitor -no-listener GScript.g4
而我們要實現具體的語法邏輯時只需要實現相關的接口,Antlr
會自動遍歷 AST
(當然也可以手動控制),同時在訪問不同的 AST
節點時會回調我們自己實現的接口,這樣我們就能編寫自己的語法規則了。
以這里的新增的取模運算為例:
func (v *GScriptVisitor) VisitModExpr(ctx *parser.ModExprContext) interface{} { lhs := v.Visit(ctx.GetLhs()) rhs := v.Visit(ctx.GetRhs()) return lhs.(int) % rhs.(int) }
當 Antlr
回調 VisitModExpr
方法時,便能獲取到 % 符號左右兩側的數據,這時只需要做相關運算即可。
基于這個模式這次新增了一個 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 還有其他各種優勢,比如可以解決:
- 左遞歸。
- 二義性。
- 優先級。
等問題。
這里也推薦在 IDE 中安裝 Antlr 的插件,這樣就可以直觀的查看 AST 語法樹,可以幫我們更好的調試代碼。
升級 xjson
借助 GScript
提供的 statement
,xjson
也提供了有些有意思的寫法:
因為 xjson
的四則運算語法沒有使用 Antlr
生成,所以為了能支持 GScript
提供的 statement
需要手寫許多詞法代碼。
這也體現了 Antlr
這類前端工具的重要性,效率提升是非常明顯的。
總結
借助于 Antlr
后續 GScript
會繼續支持函數調用、更完善的類型系統、面向對象等特性;感興趣的朋友請持續關注。
源碼地址:
github.com/crossoverJi…
github.com/crossoverJi…
原文鏈接:https://juejin.cn/post/7129357242159071240
相關推薦
- 2022-08-29 如何用python將單引號替換為雙引號_python
- 2022-03-28 go實現一個分布式限流器的方法步驟_Golang
- 2022-04-01 關于使用pyqt彈出消息提示框的問題_python
- 2022-11-13 C語言快速掌握位段使用_C 語言
- 2022-07-24 Python使用apscheduler模塊設置定時任務的實現_python
- 2022-06-17 Go基礎教程系列之WaitGroup用法實例詳解_Golang
- 2022-04-01 C#對Xamarin框架進行數據綁定_C#教程
- 2022-10-18 react電商商品列表的實現流程詳解_React
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支