返回不同类型的对象时,使用 ANTLR4 的 Visitor 或 Listener
Use Visitor or Listener with ANTLR4 when returning objects of different types
我用 ANTLR4 将一种语言翻译成另一种语言。例如,当我阅读数字文字时,我可以 return 一个 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);
}
...
}
虽然我不介意这种 'dynamism',但我想知道这是否是一种可维护的方法,或者是否有其他我应该使用的技术,比如创建适当的目标层次结构语言结构。
了解自定义 post 处理的外观。
一些 ANTLR 代码
topMostRule : childRule+ EOL;
childRule : variantOne | variantTwo;
variantOne : 'A';
variantTwo : '1';
...
自定义 post 处理的伪代码(比 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());
}
}
我建议创建一个值 class 来包装不同种类的对象,然后使用这个值 class 作为访问者的通用类型。
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
}
}
一个建议是让每个 return 类型的访客:
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
等。请在此处查看示例:See an example here
我用 ANTLR4 将一种语言翻译成另一种语言。例如,当我阅读数字文字时,我可以 return 一个 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);
}
...
}
虽然我不介意这种 'dynamism',但我想知道这是否是一种可维护的方法,或者是否有其他我应该使用的技术,比如创建适当的目标层次结构语言结构。
了解自定义 post 处理的外观。
一些 ANTLR 代码
topMostRule : childRule+ EOL;
childRule : variantOne | variantTwo;
variantOne : 'A';
variantTwo : '1';
...
自定义 post 处理的伪代码(比 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());
}
}
我建议创建一个值 class 来包装不同种类的对象,然后使用这个值 class 作为访问者的通用类型。
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
}
}
一个建议是让每个 return 类型的访客:
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
等。请在此处查看示例:See an example here