如何在ANTLR4的规则中有效区分不同的令牌?

时间:2018-08-05 08:57:38

标签: java antlr 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()会调用getToken(),这实际上会遍历所有子级。这是多次完成的操作,所以我不确定这是否明智。另外,我两次致电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语法时,很少这样做。所以我不确定这是否是前进的方式。

1 个答案:

答案 0 :(得分:1)

永远不要只看代码就能猜测性能。衡量吧!

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

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

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