当前位置: 首页 > 科技观察

使用Antlr重构脚本解释器_0

时间:2023-03-13 07:23:45 科技观察

前言前言中实现的脚本解释器GScript,实现了基本的四种算术运算和AST的生成。当我准备再添加一个%模运算符时,我会发现这项工作非常繁琐,几乎是重复的;主要有两个步骤:我需要在词法分析器中添加对%符号的支持。当解析器遍历AST时,为%token实现特定的逻辑。AST的词法分析和遍历是完全重复的工作,那我们能不能简化这两个步骤呢?AntlrAntlr就是帮助我们解决这些问题的常用工具。有了它,我们只需要编写词法文件,然后自动生成词法和解析器,就可以生成不同语言的代码。下面以GScript为例,看看antlr是如何帮助我们生成词法分析器的。funcTestGScriptVisitor_Visit_Lexer(t*testing.T){expression:="(2+3)*2"input:=antlr.NewInputStream(expression)lexer:=parser.NewGScriptLexer(input)for{t:=lexer.NextToken()如果t.GetTokenType()==antlr.TokenEOF{break}fmt.Printf("%s(%q)%d\n",lexer.SymbolicNames[t.GetTokenType()],t.GetText(),t.GetColumn())}}//输出:("(")0DECIMAL_LITERAL("2")1PLUS("+")2DECIMAL_LITERAL("3")3(")")4MULT("*")6DECIMAL_LITERAL("2")8Antlr会自动将我们的表达式解析成token,在遍历token的时候,还可以获得代码行数、token所在位置等信息。在编译期间进行语法检查非常有用。为此,我们只需要编写词法和语法规则文件即可。刚才例子对应的词法和语法规则如下:expr:'('expr')'#NestedExpr|升=文字#Liter|lhs=exprbop=(MULT|DIV)rhs=expr#MultDivExpr|lhs=exprbop=MODrhs=expr#ModExpr|lhs=exprbop=(PLUS|SUB)rhs=expr#PlusSubExpr|exprbop=(LE|GE|GT|LT)expr#GLe|exprbop=(EQUAL|NOTEQUAL)expr#EqualOrNot;DECIMAL_LITERAL:('0'|[1-9](Digits?|'_'+Digits))[lL]?;完整规则:https://github.com/crossoverJie/gscript/blob/main/GScript.g4运行:antlr-Dlanguage=Go-oparser-visitor-no-listenerGScript.g4可以帮我们生成Go代码(默认是Java),Antlr的词法、语法规则和安装步骤请参考官网。当我们要实现具体的语法逻辑时,只需要实现相关的接口即可。Antlr会自动遍历AST(当然也可以手动控制),同时在访问不同的AST节点时会回调我们自己实现的接口,这样我们就可以编写自己的语法规则了。以这里新增的取模操作为例:.GetRhs())returnlhs.(int)%rhs.(int)}Antlr回调VisitModExpr方法时,可以得到%符号左右两边的数据,只需要做相关计算即可.基于此模式,本次新增一条语句。具体语法如下:funcTestGScriptVisitor_VisitIfElse8(t*testing.T){expression:=`if(3!=(1+2)){return1+3}else{returnfalse}`input:=antlr.NewInputStream(表达式)词法分析器:=解析器.NewGScriptLexer(输入)流:=antlr.NewCommonTokenStream(词法分析器,0)解析器:=解析器.NewGScriptParser(流)解析器.BuildParseTrees=真树:=解析器。Prog()visitor:=GScriptVisitor{}varresult=visitor.Visit(tree)fmt.Println(expression,"result:",result)assert.Equal(t,result,false)}Antlr有许多其他优点,例如,可以解决:左递归。歧义。优先事项。等问题。另外推荐在IDE中安装Antlr插件,这样可以直观的查看AST语法树,可以帮助我们更好的调试代码。升级xjson借助GScript提供的语句,xjson还提供了一些有趣的写法:因为xjson的四种算术文法不是使用Antlr生成的,所以为了支持GScript提供的语句,大量的词法代码需要手写。这也体现了Antlr等前端工具的重要性,效率提升非常明显。