当返回不同类型的对象时,请使用ANTLR4中的Visitor或Listener

7

我使用ANTLR4将一种语言翻译成另一种语言。例如,当我读取数字时,我可以返回一个整数双精度浮点数

@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);
    }
    ...
}

虽然我不介意这种“动态性”,但我想知道这是否是一种可维护的方法,或者是否应该使用其他技术,例如创建目标语言结构的适当层次结构。


我会选择自定义后处理步骤。这样你就可以拥有与每个规则所需匹配的返回类型。这并不需要太多额外的工作,但它值得花费时间。正如你已经提到的那样,这样做更清晰,因为你不必进行类型转换等操作。只需确保你不会错过规则中的任何一个情况(目前我发现最好的方法是使用一个触发每个规则情况的示例输入文件和断言来检查是否触发了每个子规则)。 - Onur
你能给我一个示例链接吗?这是我第一次使用ANTLR进行项目开发,我真的很想做好它。谢谢! - vasily
我手头没有真正的代码,但是发布了一个样本伪代码片段,以便让您了解它可能看起来像什么。方法中的内容几乎相同,只是没有共同的签名(例如“结果值”),需要自己处理子调用。 - Onur
3个回答

4

有一个建议是每种返回类型都有一个访问者:

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());
  }
}

当你需要访问非常不同的东西时,这种做法就更有意义了(例如,如果你正在使用Java语法进行解析,你可能会有一个MethodVisitor和一个ClassVisitor等)。请点击此处查看示例:在此处查看示例


2
为了让您对自定义后处理的效果有所了解。
一些ANTLR代码。
topMostRule : childRule+ EOL;
childRule : variantOne | variantTwo;
variantOne : 'A';
variantTwo : '1';
...

自定义后处理的伪代码(更像C#而不是Java / 不是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
我建议创建一个值类来包装不同类型的对象,然后将这个值类用作访问者的通用类型。
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
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接