如何在ANTLR中检测非法表达式?基于JavaParser的技术问询
关于ANTLR Java语法规则检测非法表达式的疑问
我使用ANTLR的JavaParser语法规则,其中表达式定义如下:
expression : primary | expression bop='.' ( identifier | methodCall | THIS | NEW nonWildcardTypeArguments? innerCreator | SUPER superSuffix | explicitGenericInvocation ) | expression '[' expression ']' | methodCall | NEW creator | '(' annotation* typeType ('&' typeType)* ')' expression | expression postfix=('++' | '--') | prefix=('+'|'-'|'++'|'--') expression | prefix=('~'|'!') expression | expression bop=('*'|'/'|'%') expression | expression bop=('+'|'-') expression | expression ('<' '<' | '>' '>' '>' | '>' '>') expression | expression bop=('<=' | '>=' | '>' | '<') expression | expression bop=INSTANCEOF (typeType | pattern) | expression bop=('==' | '!=') expression | expression bop='&' expression | expression bop='^' expression | expression bop='|' expression | expression bop='&&' expression | expression bop='||' expression | <assoc=right> expression bop='?' expression ':' expression | <assoc=right> expression bop=('=' | '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^=' | '>>=' | '>>>=' | '<<=' | '%=') expression
但发现部分非法表达式无法被直接检测,例如3 > 3 ? 3 : 45 : 45、2 > 3 > 4。我想了解:
- Java语法是如何检测这类非法表达式的?
- 这是ANTLR本身的能力还是需要额外编码实现?
- 若需额外编码,具体该如何操作?
解答
为什么ANTLR默认没检测到这些非法表达式?
ANTLR生成的解析器仅负责语法层面的分析,也就是检查输入是否符合语法规则的结构。但你提到的两个例子,从语法规则上看并没有违反结构定义:
- 对于
2 > 3 > 4,语法规则允许expression bop='>' expression,而第二个>的左侧可以是前一个2>3的结果(属于expression),所以解析器认为结构合法。 - 对于
3 > 3 ? 3 : 45 : 45,因为三元表达式标注了<assoc=right>(右结合),解析器会把它解析成(3>3 ? 3 : 45) : 45,而右侧的45 : 45又被尝试匹配三元表达式的结构,但实际上三元表达式的冒号后必须是完整的expression,这种嵌套错误属于语义层面问题,语法规则无法直接禁止。
Java语言的合法性检测分为语法分析和语义分析两个阶段:ANTLR只完成了前者,后者需要额外实现。
这类检测需要额外编码实现
这类非法表达式的问题属于语义错误,而非语法错误,因此ANTLR本身不会自动检测,必须通过额外的语义分析代码来实现。
具体实现方法
可以通过ANTLR的**监听器(Listener)或访问者(Visitor)**模式,遍历解析树并添加检查逻辑:
1. 检测a > b > c这类链式比较
Java中不允许这种链式比较(因为a>b返回boolean,不能再和c比较数值),所以需要在访问比较表达式节点时,检查左右操作数的类型:
- 遍历到
expression bop=('>' | '<' | '>=' | '<=') expression节点时,获取左右子表达式的类型。 - 如果左子表达式的类型是
boolean(比如是另一个比较表达式的结果),则抛出语义错误。
2. 检测三元表达式的多余冒号
三元表达式的结构是条件 ? 表达式1 : 表达式2,不允许在表达式2后再加冒号。可以通过以下方式检测:
- 访问三元表达式节点时,检查其节点内的冒号数量是否超过1个。
- 或者递归检查冒号后的子表达式,确保其本身不是未闭合的三元结构。
示例代码思路(基于Visitor模式)
@Override public Boolean visitConditionalExpression(JavaParser.ConditionalExpressionContext ctx) { // 检查三元表达式是否存在多余冒号 long colonCount = ctx.children.stream().filter(child -> child.getText().equals(":")).count(); if (colonCount > 1) { throw new SemanticErrorException("三元表达式只能包含一个冒号分支"); } // 递归检查冒号后的表达式合法性 visit(ctx.expression(2)); // 检查是否为链式比较 if (ctx.parent instanceof JavaParser.RelationalExpressionContext) { JavaParser.RelationalExpressionContext parentCtx = (JavaParser.RelationalExpressionContext) ctx.parent; if (parentCtx.expression(0) instanceof JavaParser.RelationalExpressionContext) { throw new SemanticErrorException("Java不允许链式比较表达式"); } } return true; }
总结
ANTLR仅负责语法结构的解析,Java语言中的这类非法表达式属于语义错误,需要通过额外的语义分析代码(利用Listener/Visitor遍历解析树)来检测和报告错误。
内容的提问来源于stack exchange,提问作者Zheng Xiaodong
相关产品推荐
相关产品推荐

