首页 > 解决方案 > 如何有效区分 ANTLR4 规则中的不同标记?

问题描述

我有一个简单的语法规则:

expr    : expr (EQUALS | NOT_EQUALS) expr
        | literal;
literal : ...; // omitted here

词法分析器识别EQUALSNOT_EQUALS

EQUALS     : '=';
NOT_EQUALS : '!=';

在我的代码中,我想区分等于不等于的情况。我想知道如何有效地做到这一点。目前,我将访问者实现如下:

public Expression visitExpr(ExprContext ctx) {
    if (ctx.EQUALS() != null) {
        return EqualsExpression.construct(ctx.expr());
    } else if (ctx.NOT_EQUALS() != null) {
        return NotEqualsExpression.construct(ctx.expr());
    } else if (ctx.literal() != null) {
        return LiteralExpression.construct(ctx.literal());
    }
    throw new IllegalStateException();
}

我不确定这是否非常有效,因为EQUALS()/ NOT_EQUALS()callsgetToken()基本上循环遍历所有子项。这是多次完成,所以我不确定这是否聪明。另外,我打literal()了两次电话。关于后者,我知道我可以缓存在一个局部变量中,但是如果要考虑多个子规则,这将很快变成非常丑陋的代码。

有没有办法更有效地做到这一点?基于某种令牌标识符或分支标识符的 switch/case 语句会更理想吗?

边注

我可以将expr规则拆分为多个规则,如下所示:

expr         : expr_eq | expr_not_eq | expr_literal
expr_eq      : expr EQUALS expr
expr_not_eq  : expr NOT_EQUALS expr
expr_literal : literal

现在,访问者将分别访问每个可能的分支:

public Expression visitExprEx(ExprEqContext ctx) {
    return EqualsExpression.construct(ctx.expr());
}

public Expression visitExprNotEq(ExprNotEqContext ctx) {
    return NotEqualsExpression.construct(ctx.expr());
}

public Expression visitExprLiteral(ExprLiteralContext ctx) {
    return LiteralExpression.construct(ctx.literal());
}

但是看看 Github ( https://github.com/antlr/grammars-v4 ) 上的 G4 语法,很少有人这样做。所以我不确定这是否是前进的道路。

标签: javaantlrantlr4

解决方案


永远不要仅仅通过查看代码来猜测性能。测量它!

子列表非常短,这意味着在找到orgetToken之前只执行 1-2 个循环。代码的其他部分可能需要比查找更多的时间。EQUALSNOT_EQUALS

但是,如果您想获得每一项性能,请避免使用方便的方法并手动执行操作,因为您可以根据您对语法的了解来优化访问。在这种特殊情况下,您的 expr 规则只能有一个或 3 个孩子。查看第一个变体,它可以获得单个literal规则子节点或expr规则子节点、内部 alt 的标记子节点和另一个expr规则子节点。您所要做的就是检查:

if (ctx.getChildCount() > 1 && ((TerminalNode)ctx.getChild(1)).getSymbol().getType() == YourParser.EQUALS)

推荐阅读