我有一个简单的语法规则:
expr : expr (EQUALS | NOT_EQUALS) expr
| literal;
literal : ...; // omitted here
词法分析器可以识别EQUALS
和NOT_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语法时,很少这样做。所以我不确定这是否是前进的方式。
答案 0 :(得分:1)
永远不要只看代码就能猜测性能。衡量吧!
子列表很短,这意味着getToken
仅在发现EQUALS
或NOT_EQUALS
之前执行1-2个循环。代码的其他部分可能需要比查找更多的时间。
但是,如果您想发挥所有性能,请避免使用便捷方法并手动执行操作,因为您对语法的了解可以优化访问。在这种特殊情况下,您的expr规则只能有一个或三个孩子。查看第一个变体,它可以获得单个literal
规则子节点或expr
规则子节点,内部alt的令牌子节点和另一个expr
规则子节点。您要做的只是检查:
if (ctx.getChildCount() > 1 && ((TerminalNode)ctx.getChild(1)).getSymbol().getType() == YourParser.EQUALS)