news 2026/4/18 8:47:24

antlr4使用心得

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
antlr4使用心得

antlr4总结

算符优先级问题

同一条规则内,运算符分支在前的,优先级越高,比如:

expr : expr op=('+'|'-') expr #AddSub | expr op=('*'|'/') expr #MulDiv

会认为加减运算符优先于乘除运算符,这样在计算1+2*3时,就会得到错误的结果9。正确的写法是乘除分支在前:

expr : expr op=('*'|'/') expr #MulDiv | expr op=('+'|'-') expr #AddSub

算符右结合问题

antlr默认是算符左结合,像幂运算这样的算符一般要求右结合,可以这么写:

expr : <assoc=right> expr '**' expr #power | expr op=('*'|'/'|'%') expr #MulDiv | expr op=('+'|'-') expr #AddSub | '(' expr ')' #Parens

注意:<assoc=right>要写在分支的最左边,写在其它位置会有warning。

多分支下的标签不可省略

多分支规则里,要么所有分支给标签(否则无法为每个分支自动生成visit函数),要么所有分支都不给标签。

上例中,#AddSub就是一个分支标签,这条规则里的每个分支都要有一个标签,这样antlr会自动为每个分支生成一个visit函数,方便使用者来处理。比如:

@OverridepublicTvisitMulDiv(LumenParser.MulDivContextctx)...@OverridepublicTvisitAddSub(LumenParser.AddSubContextctx)

单分支规则自然无需手写标签,antlr会自动生成visit函数。

字面量

字面量可以是一个字符,也可以是字符串。必须用单引号括起来。下面是例子:

STRING : '"' (~["\r\n])* '"' ; BOOL : 'true' | 'false' ;

上例中,true和false都是字符串字面量。

错误处理

两种方式,一个是定制自己的BaseErrorListener:

publicclassSyntaxErrorListenerextendsBaseErrorListener{privateSourcesource;publicSyntaxErrorListener(Sourcesource){this.source=source;}@OverridepublicvoidsyntaxError(Recognizer<?,?>recognizer,ObjectoffendingSymbol,intline,intcharPositionInLine,Stringmsg,RecognitionExceptione){// line 和 charPositionInLine 就是错误的行列号StringsourceName=source.getName();if(!sourceName.isEmpty()){sourceName=String.format("%s:%d:%d: ",sourceName,line,charPositionInLine);}System.err.println(sourceName+msg);}}

然后设置这个Listener:

parser.addErrorListener(newSyntaxErrorListener(request.getSource()));

parser就是antlr自动为我们生成的语法解析器。

antlr框架会在出现语法错误时调用BaseErrorListener,然后恢复错误,继续解析。

若有运行时错误,可在每个grammar node里塞入行列号信息,从antlr的ParserRuleContext里即可获得,比如:

publicNodesupplySourceSection(Sourcesource,ParserRuleContextctx){Tokenstart=ctx.getStart();Tokenstop=ctx.getStop();// 可能为 null(EOF)intstartLine=start.getLine();intstartCol=start.getCharPositionInLine()+1;intendLine=(stop!=null?stop.getLine():startLine);intendCol=(stop!=null?stop.getCharPositionInLine()+1:startCol);this.sourceSection=source.createSection(startLine,startCol,endLine,endCol);returnthis;}

我这里使用的是graal vm的truffle框架,所以把行列号信息设置为每个语法节点的SourceSection。这样,当执行到特定节点出错时,就可以报出详细的行列号信息。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/13 0:10:47

【Java毕设全套源码+文档】基于springboot的郑州旅游景点智能推荐系统设计与实现(丰富项目+远程调试+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/18 3:26:02

【Java毕设源码分享】基于springboot+JavaWeb的毕业季旅游一站式定制服务平台的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/18 3:33:58

Java IO 与 NIO:从 BIO 阻塞陷阱到 NIO 万级并发

文章目录&#x1f3af;&#x1f525; Java IO 与 NIO&#xff1a;从 BIO 阻塞陷阱到 NIO 万级并发&#xff08;实测 10 万 QPS 性能对比&#xff09;&#x1f31f;&#x1f30d; 引言&#xff1a;数字时代的“脉搏”与 IO 性能天花板&#x1f4ca;&#x1f4cb; 第一章&#xf…

作者头像 李华
网站建设 2026/4/18 3:33:47

基于Simulink的储能SOC均衡控制策略仿真

目录 手把手教你学Simulink 一、引言&#xff1a;为什么储能系统需要“SOC均衡”&#xff1f; 二、系统整体架构 控制层级&#xff1a; 三、理论基础&#xff1a;SOC 均衡策略 1. 被动均衡&#xff08;Passive&#xff09; 2. 主动均衡&#xff08;Active&#xff09; 四…

作者头像 李华