中卫市网站建设_网站建设公司_云服务器_seo优化
2026/1/15 10:58:57 网站建设 项目流程

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。这样,当执行到特定节点出错时,就可以报出详细的行列号信息。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询