我用ANTLR4将一种语言翻译成另一种语言。例如,当我阅读数字文字时,我可以返回Integer
或Double
。
@Override
public Integer visitIntegerValue(Parser.IntegerValueContext ctx) {
return Integer.valueOf(ctx.getText());
}
@Override
public Double visitDoubleValue(Parser.DoubleValueContext ctx) {
return Double.valueOf(ctx.getText());
}
最终,如果您进一步扩展此方法并引入其他结构(如字符串和条件),则访问者唯一合理的类型是class Visitor extends BaseVisitor<Object>
,但会导致代码严重加上instanceof
。例如
@Override
public CollectionQuery visitCondition(Parser.ConditionContext ctx) {
Property property = (Property) visit(ctx.property());
String operator = (String) visit(ctx.operator());
Object value = visit(ctx.amount());
Object condition;
if (value instanceof String && operator.equals("$regex")) {
condition = Pattern.compile((String) value, Pattern.CASE_INSENSITIVE);
}
...
}
虽然我不介意这种动态,但我想知道这是否是一种可维护的方式,或者是否有其他技术可以改为使用,比如创造适当的目标语言结构的层次结构。
答案 0 :(得分:1)
了解自定义后期处理的效果。
一些ANTLR代码
topMostRule : childRule+ EOL;
childRule : variantOne | variantTwo;
variantOne : 'A';
variantTwo : '1';
...
自定义后处理的伪代码(比Java更像C#,而不是ANTLR使用的真实方法名称):
public class MyCustomPostprocessor
{
private IntermediateResults lookupTable; // use private fields for lookup tables etc.
public ResultType process(Parser.TopMostRuleContext ctx)
{
// inspect the children
var children = new List<object>();
foreach (var rule in ctx.ChildRules)
{
switch (rule.Type)
{
case typeof (Parser.ChildRuleContext):
var result = process(rule);
children.Add(result);
else
throw new NotImplementedException("Don't know what to do with " + rule.Type.ToString());
}
// use the information gathered so far to form the result
return new ResultType (children);
}
}
public object process (Parser.ChildRuleContext)
{
foreach (var rule in ctx.ChildRules)
{
switch (rule.Type)
{
case typeof (Parser.VariantOneContext):
var result = process(rule);
return result;
case typeof (Parser.VariantTwoContext):
var result = process(rule);
return result;
else
throw new NotImplementedException("Don't know what to do with " + rule.Type.ToString());
}
}
}
public string process (Parser.VariantOneContext ctx)
{
return ctx.GetText();
}
public int process (Parser.VariantTwoContext ctx)
{
return Int.Parse(ctx.GetText());
}
}
答案 1 :(得分:1)
我建议创建一个Value类来包装不同类型的对象,然后将此值类用作访问者的泛型类型。
Visitor<Value> visitor;
public class Value {
private Object value;
public Value(Object object) {
this.value = object
if (!(isDouble() || isInteger))
throw new IllegalArgumentException();
}
public boolean isDouble() {
return value instanceof Double;
}
public Double asDouble() {
return (Double) value;
}
public boolean isInteger() {
return value instanceof Integer;
}
public Integer asInteger() {
return (Integer) value;
}
@Override
public int hashCode() {
// generate hascode
}
@Override
public boolean equals(Object object) {
// equals
}
}
答案 2 :(得分:1)
一个建议是按返回类型指定访客:
awk 'ENDFILE{print FILENAME, $0}' *.asc
当您访问截然不同的事物时,这更有意义(例如,如果您使用Java语法进行解析,则可能会有public class IntegerVisitor extends BaseListener<Integer> {
@Override
public Integer visitIntegerValue(Parser.IntegerValueContext ctx) {
return Integer.valueOf(ctx.getText());
}
}
public class DoubleVisitor extends BaseListener<Double> {
@Override
public Double visitDoubleValue(Parser.DoubleValueContext ctx) {
return Integer.valueOf(ctx.getText());
}
}
和MethodVisitor
等。请参见此处的示例:{ {3}}