如何为两个相似的ANTLR4语法实现共享Visitor以避免重复代码
解决方案
以下是几种可落地的复用方案,你可以根据项目约束选择:
方案1:抽取公共逻辑到静态工具类(改造成本最低)
将数字、加法的核心转换逻辑抽为无状态的静态工具方法,两个Converter各自从自己的Context中取出参数调用工具类即可,仅保留极薄的适配层代码:
// 公共工具类,核心逻辑仅写一次 public class ExpressionConvertUtils { // 数字节点转换逻辑 public static Expression buildNumberExpr(String numberStr) { return new NumberExpression(Integer.parseInt(numberStr)); } // 加法节点转换逻辑 public static Expression buildAddExpr(Expression left, Expression right) { return new AddExpression(left, right); } }
两个Converter的对应方法直接调用:
// Grammar1Converter 中的实现 @Override public Expression visitNumber(Grammar1Parser.NumberContext ctx) { return ExpressionConvertUtils.buildNumberExpr(ctx.NUMBER().getText()); } @Override public Expression visitAddition(Grammar1Parser.AdditionContext ctx) { return ExpressionConvertUtils.buildAddExpr(visit(ctx.expr(0)), visit(ctx.expr(1))); } // Grammar2Converter 中的实现 @Override public Expression visitNumber(Grammar2Parser.NumberContext ctx) { return ExpressionConvertUtils.buildNumberExpr(ctx.NUMBER().getText()); } @Override public Expression visitAddition(Grammar2Parser.AdditionContext ctx) { return ExpressionConvertUtils.buildAddExpr(visit(ctx.expr(0)), visit(ctx.expr(1))); }
方案2:接口默认方法适配(适配层代码也可复用)
如果希望进一步减少重复的适配代码,可以通过泛型接口+默认方法实现公共逻辑封装:
public interface CommonConvertCapability<NumberCtx, AddCtx> { // 子类实现各自Context的取值逻辑 String getNumberText(NumberCtx ctx); Expression visitLeftExpr(AddCtx ctx); Expression visitRightExpr(AddCtx ctx); // 默认实现公共转换逻辑 default Expression convertNumber(NumberCtx ctx) { return new NumberExpression(Integer.parseInt(getNumberText(ctx))); } default Expression convertAdd(AddCtx ctx) { return new AddExpression(visitLeftExpr(ctx), visitRightExpr(ctx)); } }
Converter实现接口即可直接使用公共逻辑:
public class Grammar1Converter extends Grammar1BaseVisitor<Expression> implements CommonConvertCapability<Grammar1Parser.NumberContext, Grammar1Parser.AdditionContext> { @Override public String getNumberText(Grammar1Parser.NumberContext ctx) { return ctx.NUMBER().getText(); } @Override public Expression visitLeftExpr(Grammar1Parser.AdditionContext ctx) { return visit(ctx.expr(0)); } @Override public Expression visitRightExpr(Grammar1Parser.AdditionContext ctx) { return visit(ctx.expr(1)); } @Override public Expression visitNumber(Grammar1Parser.NumberContext ctx) { return convertNumber(ctx); } @Override public Expression visitAddition(Grammar1Parser.AdditionContext ctx) { return convertAdd(ctx); } // 其他独有规则的实现... }
方案3:ANTLR语法层抽公共规则(复用程度最高)
如果允许调整原始语法定义,可以直接用ANTLR的import语法复用公共规则,从根源上避免重复逻辑:
- 先定义公共语法文件
CommonExpr.g4:
grammar CommonExpr; NUMBER : [0-9]+ ; WS : [ \r\n\t]+ -> skip ; expr : expr '+' expr # Addition | NUMBER # Number ;
- 两个业务语法导入公共语法,扩展独有规则:
// Grammar1.g4 grammar Grammar1; import CommonExpr; root : expr EOF; expr : '*' expr expr # Multiplication; // 扩展乘法规则
// Grammar2.g4 grammar Grammar2; import CommonExpr; root : expr EOF; expr : NUMBER '!' # Factorial | NUMBER '^' NUMBER # Exponentiation; // 扩展阶乘、幂运算规则
这种方式生成的公共规则Context会继承自统一父类,你可以直接编写公共Visitor基类处理公共规则,完全不需要重复代码。
内容的提问来源于stack exchange,提问作者tomashauser
相关产品推荐
相关产品推荐

