您现在的位置是:亿华云 > 应用开发
用 Antlr 重构脚本解释器
亿华云2025-10-02 19:04:24【应用开发】1人已围观
简介前言实现的脚本解释器GScript中实现了基本的四则运算以及AST的生成。当我准备再新增一个%取模的运算符时,会发现工作很繁琐而且几乎都是重复的;主要是两步:需要在词法解析器中新增对%符号的支持。在
实现的重构脚本解释器 GScript 中实现了基本的四则运算以及 AST 的生成。
当我准备再新增一个 % 取模的脚本解释运算符时,会发现工作很繁琐而且几乎都是重构重复的;主要是两步:
需要在词法解析器中新增对% 符号的支持。在语法解析器遍历 AST 时对% token 实现具体逻辑。脚本解释其中的重构词法解析和遍历 AST 完全是重复工作,所以我们可否能够简化这两步呢?脚本解释
AntlrAntlr 就是做帮我们解决这些问题的常用工具,利用它我们只需要编写词法文件,重构然后就可以自动生成词法、脚本解释语法解析器,重构并且可以生成不同语言的脚本解释代码。
下面以 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") 8Antlr 会自动将我们的表达式解析为 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]?;完整规则:https://github.com/crossoverJie/gscript/blob/main/GScript.g4
运行:
antlr -Dlanguage=Go -o parser -visitor -no-listener GScript.g4就可以帮我们生成 Go 的代码(默认是 Java),关于 Antlr 的词法、文法规则以及安装步骤请参考官网。
而我们要实现具体的语法逻辑时只需要实现相关的接口,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 语法树,可以帮我们更好的调试代码。
借助 GScript 提供的 statement,xjson 也提供了有些有意思的写法:
因为 xjson 的四则运算语法没有使用 Antlr 生成,所以为了能支持 GScript 提供的 statement 需要手写许多词法代码。
这也体现了 Antlr 这类前端工具的重要性,效率提升是非常明显的。
很赞哦!(42234)
相关文章
- 荣获2022金融科技应用创新奖,超聚变助力金融业数字化转型
- 网站页面结构改版,仅是页面样式发生变化,不会对排名、收录有影响;只有涉及到页面URL改变,才会对网站排名、收录有影响。
- g) 散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的;
- 尽量不要在域名中出现特殊字符,这样的域名很容易导致访问者输入错误,同时给人留下不专业的印象,降低网站的可信度,并流失大量潜在客户。
- 数据中心环境控制是管理员的重中之重
- 四、长串数字域名
- 并非一个好米任何人都会给你一个好的价格。那你该如何用以有的好米卖出最理想的价格呢?
- 公司和个人选域名方法一样吗?有什么不同?
- 关于数据中心中断需要知道的十件事
- 为啥修改dns服务器?dns服务器与域名有何联系?
热门文章
站长推荐
HVAC 解决方案帮助数据中心实现可持续发展
cm域名有什么独特之处?新人要了解cm域名哪些?
主流搜索引擎显示的相关搜索项越多,越能积极反映该域名的市场价值。同时,被评估域名的搜索引擎显示结果不佳可能是由于以下两个原因:
只要我们做的是从目前的市场情况选择域名,从简单易记,从个性特征上,我们就可以找到一个好域名进行注册。域名注册进行域名记录和解析以及绑定网站后,客户可以通过URL登录您的网站。
冯丹教授:近数据处理新型盘框等技术创新,加速数据中心向Diskless架构演进
并非一个好米任何人都会给你一个好的价格。那你该如何用以有的好米卖出最理想的价格呢?
4、域名传输时,取决于域名原始用户的邮箱是否有效,以及他是否将密码发送到此邮箱。
3、不明先知,根据相关征兆预测可能发生的事件,以便提前做好准备,赶紧注册相关域名。;不差钱域名;buchaqian抢先注册,就是这种敏感类型。预言是最敏感的状态。其次,你应该有眼力。所谓眼力,就是善于从社会上时不时出现的各种热点事件中获取与事件相关的域名资源。眼力的前提是对域名领域的熟悉和丰富的知识。