我们有两个语法:
grammar Grammar1;
NUMBER : [0-9]+ ;
WS : [ rnt]+ -> skip ;
root : expr EOF
;
expr : '*' expr expr # Multiplication
| expr '+' expr # Addition
| NUMBER # Number
;
grammar Grammar2;
NUMBER : [0-9]+ ;
WS : [ rnt]+ -> skip ;
root : expr EOF
;
expr : NUMBER '!' # Factorial
| NUMBER '^' NUMBER # Exponentiation
| expr '+' expr # Addition
| NUMBER # Number
;
我们可以看到它们共享一个数字和加法,而其他的都不同。
两个转换器到一个Expression
AST将是这样的:
public class Grammar1Converter extends Grammar1BaseVisitor<Expression> {
public Expression visitRoot(Grammar1Parser.RootContext ctx);
public Expression visitMultiplication(Grammar1Parser.MultiplicationContext ctx);
public Expression visitNumber(Grammar2Parser.NumberContext ctx);
public Expression visitAddition(Grammar1Parser.AdditionContext ctx);
}
public class Grammar2Converter extends Grammar2BaseVisitor<Expression> {
public Expression visitRoot(Grammar2Parser.RootContext ctx);
public Expression visitFactorial(Grammar2Parser.FactorialContext ctx);
public Expression visitExponentiation(Grammar2Parser.ExponentiationContext ctx);
public Expression visitNumber(Grammar2Parser.NumberContext ctx);
public Expression visitAddition(Grammar2Parser.AdditionContext ctx);
}
两种语法共享它们被转换成的结构。如何实现visitNumber
和visitAddition
的共享转换器?两个转换器都已经扩展了抽象类,因此基本继承是没有问题的。两个转换器在参数中都有不同的上下文类型,因此我们不能仅为共享方法提供通用访问者,因为我们必须在上下文之间进行转换。有没有避免重复代码的方法?
正如在评论中指出的那样,我们可以利用antlr提供的层次结构和接口。
我们将创建一个新的实用程序类来处理共享功能(如果你愿意,你可以使用泛型类型)。
public final class SharedGrammerConverter() { ... }
对于叶节点,我们不需要传递访问者:
// SharedGrammarConverter
public static Expression visitNumber(ParserRuleContext ctx) {
return new Number(Integer.parseInt(ctx.getChild(0).getText()));
}
// Grammar1Converter
public Expression visitNumber(Grammar1Parser.NumberContext ctx) {
return SharedGrammarConverter.visitNumber(ctx);
}
// Grammar2Converter
public Expression visitNumber(Grammar2Parser.NumberContext ctx) {
return SharedGrammarConverter.visitNumber(ctx);
}
如果我们需要访问子树,我们也会传递访问者:
// SharedGrammarConverter
public static Expression visitAddition(ParserRuleContext ctx,
AbstractParseTreeVisitor<Expression> visitor) {
Expression left = visitor.visit(ctx.getChild(0));
Expression right = visitor.visit(ctx.getChild(2));
return new Addition(left, right);
}
// Grammar1Converter
public Expression visitAddition(Grammar1Parser.AdditionContext ctx) {
return SharedGrammarConverter.visitAddition(ctx, this);
}
// Grammar2Converter
public Expression visitAddition(Grammar2Parser.AdditionContext ctx) {
return SharedGrammarConverter.visit(ctx, this);
}