简体   繁体   English

ANTLR4 调用访问者的下一步不起作用

[英]ANTLR4 calling next step in visitor not working

I have my grammar complete and want to implement now functionality.我的语法已经完成并想实现现在的功能。 But I get it not working, I do not understand how to evaluate in the functionExpr which command should be called.但是我发现它不起作用,我不明白如何在 functionExpr 中评估应该调用哪个命令。 As a test expression I am using作为我使用的测试表达式

  isSet(value, test) and isSet(value, foo)

Currently I do not know how to evaluate the single CommandIsSet Function and also evaluate there the variable type.目前我不知道如何评估单个 CommandIsSet 函数以及如何评估变量类型。 To solve this the order should be要解决这个问题,订单应该是

  1. visitProg访问程序
  2. visitAndExpr访问和Expr
  3. visitFunctionExpr (for first isSet(value, test) visitFunctionExpr(对于第一个 isSet(value, test)
  4. visitCommandIsSet访问命令设置
  5. visitVariableParamter访问变量参数
  6. visitValueType访问值类型
  7. visitFunctionExpr (for second isSet(value, foo) visitFunctionExpr(对于第二个 isSet(value, foo)
  8. visitCommandIsSet访问命令设置
  9. visitVariableParamter访问变量参数
  10. visitValueType访问值类型

Currently I stuck in step 3, how can I determine that its a commandIsSet do I need to use the ctx.getText() and check if it starts with isSet?目前我停留在第 3 步,我如何确定它是一个 commandIsSet 我是否需要使用 ctx.getText() 并检查它是否以 isSet 开头?

I have already found a great answer from @BartKiers in SO to such a topic Using Visitors in AntLR4 in a Simple Integer List Grammar from there I used the Value Object.我已经发现从@BartKiers一个伟大的答案SO这样的话题在一个简单的整数列表语法在AntLR4使用参观者从那里我用值对象。

Here is my grammar这是我的语法

grammar FEL;

prog: expr+ EOF;
expr:
             statement                          #StatementExpr
             |NOT expr                          #NotExpr
             | expr AND expr                 #AndExpr
             | expr (OR | XOR) expr        #OrExpr
             | function                           #FunctionExpr
             | LPAREN expr RPAREN       #ParenExpr
             | writeCommand                 #WriteExpr
 ;

writeCommand: setCommand | setIfCommand;
statement: ID '=' getCommand  NEWLINE         #Assign;
setCommand: 'set' LPAREN variableType '|' parameter RPAREN SEMI;
setIfCommand: 'setIf' LPAREN variableType '|' expr '?' parameter ':' parameter RPAREN SEMI;

getCommand:         getFieldValue                   #FieldValue
                            | getInstanceAttribValue     #InstanceAttribValue
                            | getFormAttribValue          #FormAttributeValue
                            | getMandatorAttribValue   #MandatorAttributeValue
                            ;

getFieldValue: 'getFieldValue' LPAREN instanceID=ID COMMA fieldname=ID RPAREN;
getInstanceAttribValue: 'getInstanceAttrib' LPAREN instanceId=ID COMMA moduleId=ID COMMA attribname=ID RPAREN;
getFormAttribValue: 'getFormAttrib' LPAREN formId=ID COMMA moduleId=ID COMMA attribname=ID RPAREN;
getMandatorAttribValue: 'getMandatorAttrib' LPAREN mandator=ID COMMA moduleId=ID COMMA attribname=ID RPAREN;

twoParameterList: parameter '|' parameter;
parameter:
            variableType #VariableParameter
            | constType  #ConstParameter
            ;
pdixFuncton:ID;
constType:
                ID #ID_Without
                | '"'  ID '"' #ID_WITH
                ;
variableType:
                    valueType                      #ValueTyp
                    |instanceType                #InstanceTyp
                    |formType                      #FormTyp
                    |bufferType                    #BufferTyp
                    |instanceAttribType       #InstanceAttribTyp
                    |formAttribType             #FormAttribTyp
                    |mandatorAttribType     #ManatorTyp
                    ;
valueType:'value' COMMA parameter (COMMA functionParameter)?;
instanceType: 'instance' COMMA instanceParameter;
formType: 'form' COMMA formParameter;
bufferType: 'buffer' COMMA parameter;
instanceParameter: 'instanceId'
                                | 'instanceKey'
                                | 'firstpenId'
                                | 'lastpenId'
                                | 'lastUpdate'
                                | 'started'
                                ;
formParameter: 'formId'
                            |'formKey'
                            |'lastUpdate'
                            ;
functionParameter: 'lastPen'
                                | 'fieldGroup'
                                | ' fieldType'
                                | 'fieldSource'
                                | 'updateId'
                                | 'sessionId'
                                | 'icrConfidence'
                                | 'icrRecognition'
                                |  'lastUpdate';

instanceAttribType:('instattrib' | 'instanceattrib') COMMA attributeType;
formAttribType:'formattrib' COMMA attributeType;
mandatorAttribType: 'mandatorattrib' COMMA attributeType;
attributeType:ID '#' ID;

 function:
                commandIsSet                                    #IsSet
                | commandIsEmpty                            #IsEmpty
                | commandIsEqual                               #IsEqual
                |commandIsNumLessEqual                  #IsLessEqual
                |commandIsNumLess                           #IsNumLess
                |commandIsNumGreaterEqual            #IsNumGreaterEqual
                |commandIsNumGreater                    #IsNumGreater
                |commandIsNumEqual                       #IsNumEqual
                |commandIsLess                               #IsLess
                |commandIsLessEqual                       #IsLessEqual
                |commandIsGreater                           #IsGreater
                |commandIsGreaterEqual                  #IsGreaterEqual
                |commandMatches                             #Matches
                |commandContains                            #Contains
                |commandEndsWith                            #EndsWith
                |commandStartsWith                          #StartsWith
                ;
commandIsSet: IS_SET LPAREN parameter RPAREN;
commandIsEmpty: IS_EMPTY LPAREN parameter RPAREN;
commandIsEqual: IS_EQUAL LPAREN twoParameterList RPAREN;
commandStartsWith: 'startsWith' LPAREN twoParameterList RPAREN;
commandEndsWith: 'endsWith' LPAREN twoParameterList RPAREN;
commandContains: 'contains' LPAREN twoParameterList RPAREN;
commandMatches: 'maches' LPAREN twoParameterList RPAREN;
commandIsLess: 'isLess' LPAREN twoParameterList RPAREN;
commandIsLessEqual: 'isLessEqual' LPAREN twoParameterList RPAREN;
commandIsGreater: 'isGreater' LPAREN twoParameterList RPAREN;
commandIsGreaterEqual: 'isGreaterEqual' LPAREN twoParameterList RPAREN;
commandIsNumEqual: 'isNumEqual' LPAREN twoParameterList RPAREN;
commandIsNumGreater: 'isNumGreater' LPAREN twoParameterList RPAREN;
commandIsNumGreaterEqual: 'isNumGreaterEqual' LPAREN twoParameterList RPAREN;
commandIsNumLess: 'isNumLess' LPAREN twoParameterList RPAREN;
commandIsNumLessEqual: 'isNumLessEqual' LPAREN twoParameterList RPAREN;

/*
stringFunctionType:
    a=substringStrFunction
    |   a=cutStrFunction
    |   a=replaceStrFunction
    |   a=reformatDateStrFunction
    |   a=translateStrFunction
    |   a=fillStrFunction
    |   a=concatStrFunction
    |   a=justifyStrFunction
    |   a=ifElseStrFunction
    |   a=tokenStrFunction
    |   a=toLowerFunction
    |   a=toUpperFunction
    |   a=trimFunction
;
*/

LPAREN : '(';
RPAREN : ')';
LBRACE : '{';
RBRACE : '}';
LBRACK : '[';
RBRACK : ']';
SEMI : ';';
COMMA : ',';
DOT : '.';
ASSIGN : '=';
GT : '>';
LT : '<';
BANG : '!';
TILDE : '~';
QUESTION : '?';
COLON : ':';
EQUAL : '==';
LE : '<=';
GE : '>=';
NOTEQUAL : '!=';
AND : 'and';
OR : 'or';
XOR :'xor';
NOT :'not'  ;
INC : '++';
DEC : '--';
ADD : '+';
SUB : '-';
MUL : '*';
DIV : '/';

INT: [0-9]+;
NEWLINE: '\r'? '\n';
IS_SET:'isSet';
IS_EMPTY:'isEmpty';
IS_EQUAL:'isEqual';
WS: (' '|'\t' | '\n' | '\r' )+ -> skip;

ID
:   JavaLetter JavaLetterOrDigit*
;

fragment
JavaLetter
:   [a-zA-Z$_] // these are the "java letters" below 0xFF
|   // covers all characters above 0xFF which are not a surrogate
    ~[\u0000-\u00FF\uD800-\uDBFF]
    {Character.isJavaIdentifierStart(_input.LA(-1))}?
|   // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF
    [\uD800-\uDBFF] [\uDC00-\uDFFF]
    {Character.isJavaIdentifierStart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}?
;

fragment
JavaLetterOrDigit
:   [a-zA-Z0-9$_] // these are the "java letters or digits" below 0xFF
|   // covers all characters above 0xFF which are not a surrogate
    ~[\u0000-\u00FF\uD800-\uDBFF]
    {Character.isJavaIdentifierPart(_input.LA(-1))}?
|   // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF
    [\uD800-\uDBFF] [\uDC00-\uDFFF]
    {Character.isJavaIdentifierPart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}?
;
fragment DoubleQuote: '"' ;   // Hard to read otherwise.

This is my visitor implementation with a test expression这是我的带有测试表达式的访问者实现

public class FELCustomVisitor extends FELBaseVisitor<Value> {

// used to compare floating point numbers
public static final double SMALL_VALUE = 0.00000000001;

// store variables (there's only one global scope!)
private Map<String, Value> memory = new HashMap<String, Value>();

public static void main(String[] args) {
    String expression = "isSet(value, test) and isSet(value, foo)";
    FELLexer lexer = new FELLexer(new ANTLRInputStream(expression));
    FELParser parser = new FELParser(new CommonTokenStream(lexer));
    ParseTree tree = parser.prog();
    Value answer = new FELCustomVisitor().visit(tree);
    System.out.printf("answer is %s", answer.asBoolean());
}


@Override
public Value visitProg(FELParser.ProgContext ctx) {
    System.out.println("called prog " + ctx.getText());
    return this.visitChildren(ctx);
}

@Override
public Value visitAndExpr(FELParser.AndExprContext ctx) {
    Value left = this.visit(ctx.expr(0));
    Value right = this.visit(ctx.expr(2));
    System.out.println("working on and expression: left == " + left.asBoolean() + " right is " + right.asBoolean());
    return new Value(left.asBoolean() && right.asBoolean());
}

@Override
public Value visitFunctionExpr(FELParser.FunctionExprContext ctx) {
    System.out.println("called function expresssion " + ctx.getText());
    FELParser.FunctionContext function = ctx.function();

    return null;
}


@Override
public Value visitCommandIsSet(FELParser.CommandIsSetContext ctx) {
    //here I need to extract the inner variable type with this i can evaluate the return value

    return super.visitCommandIsSet(ctx);
}

@Override
public Value visitVariableParameter(FELParser.VariableParameterContext ctx) {
    return super.visitVariableParameter(ctx);
}
@Override
public Value visitValueTyp(FELParser.ValueTypContext ctx) {
    return super.visitValueTyp(ctx);
}

The part this.visit(ctx.expr(2)) in this method is wrong:这个方法中的this.visit(ctx.expr(2))部分是错误的:

@Override
public Value visitAndExpr(FELParser.AndExprContext ctx) {
    Value left = this.visit(ctx.expr(0));
    Value right = this.visit(ctx.expr(2));
    System.out.println("working on and expression: left == " + left.asBoolean() + " right is " + right.asBoolean());
    return new Value(left.asBoolean() && right.asBoolean());
}

There are 2 child nodes that have index 0 and 1.有 2 个子节点的索引为 0 和 1。

You're making it a bit too complicated in your visitor.你在访问者中让它有点太复杂了。 Start with the most simple expressions you'd like to evaluate, like isSet(value, test) , and work your way from the bottom of the tree to the root of it and implement these visit...() methods.从您想要计算的最简单的表达式开始,例如isSet(value, test) ,然后从树的底部到树的根部执行这些visit...()方法。 In case of isSet(value, test) that would be the rules:isSet(value, test)情况下,这将是规则:

@Override
public Value visitID_Without(FELParser.ID_WithoutContext ctx) {
    // ID #ID_Without
}

@Override
public Value visitCommandIsSet(FELParser.CommandIsSetContext ctx) {
    // commandIsSet: IS_SET LPAREN parameter RPAREN;
}

@Override
public Value visitProg(FELParser.ProgContext ctx) {
    // expr+ EOF
}

when that works, add the and expression to it to support isSet(value, test) and isSet(value, foo) :当它起作用时,向它添加and表达式以支持isSet(value, test) and isSet(value, foo)

@Override
public Value visitID_Without(FELParser.ID_WithoutContext ctx) {
    // ID #ID_Without
}

@Override
public Value visitCommandIsSet(FELParser.CommandIsSetContext ctx) {
    // commandIsSet: IS_SET LPAREN parameter RPAREN;
}

@Override
public Value visitAndExpr(FELParser.AndExprContext ctx) {
    // expr AND expr
}

@Override
public Value visitProg(FELParser.ProgContext ctx) {
    // expr+ EOF
}

etc.等等。

A small working example would look like this:一个小的工作示例如下所示:

public class FELCustomVisitor extends FELBaseVisitor<Value> {

    private final Map<String, Value> memory;

    public FELCustomVisitor(Map<String, Value> memory) {
        this.memory = memory;
    }

    @Override
    public Value visitID_Without(FELParser.ID_WithoutContext ctx) {
        // ID #ID_Without
        return this.memory.getOrDefault(ctx.ID().getText(), Value.NULL);
    }

    @Override
    public Value visitCommandIsSet(FELParser.CommandIsSetContext ctx) {
        // commandIsSet: IS_SET LPAREN parameter RPAREN;
        Value param = this.visit(ctx.parameter());
        return param == Value.NULL ? Value.FALSE : Value.TRUE;
    }

    @Override
    public Value visitAndExpr(FELParser.AndExprContext ctx) {
        // expr AND expr
        Value lhs = this.visit(ctx.expr(0));
        Value rhs = this.visit(ctx.expr(1));
        return lhs.asBoolean() && rhs.asBoolean() ? Value.TRUE : Value.FALSE;
    }

    @Override
    public Value visitProg(FELParser.ProgContext ctx) {
        // expr+ EOF
        Value result = Value.NULL;
        for (FELParser.ExprContext expr : ctx.expr()) {
            result = this.visit(expr);
        }
        return result;
    }

    public static void main(String[] args) {
        String[] expressions = {
                "isSet(value, test)",
                "isSet(value, foo)",
                "isSet(value, fooooooo)",
                "isSet(value, test) and isSet(value, foo)",
                "isSet(value, nope) and isSet(value, test) and isSet(value, foo)"
        };

        for (String expression  : expressions) {
            FELLexer lexer = new FELLexer(CharStreams.fromString(expression));
            FELParser parser = new FELParser(new CommonTokenStream(lexer));
            ParseTree tree = parser.prog();
            Map<String, Value> memory = new HashMap<>();
            memory.put("test", new Value(42));
            memory.put("foo", new Value("bar"));
            Value answer = new FELCustomVisitor(memory).visit(tree);
            System.out.printf("%s = %s%n", expression, answer);
        }
    }
}

class Value {

    public static final Value TRUE = new Value(true);
    public static final Value FALSE = new Value(false);
    public static final Value NULL = new Value(null);

    private final Object value;

    public Value(Object value) {
        this.value = value;
    }

    public Boolean asBoolean() {
        return (Boolean) this.value;
    }

    @Override
    public String toString() {
        return String.format("Value{type: %s, value: %s}",
                this.value == null ? "null" : this.value.getClass().getSimpleName(), this.value);
    }
}

when running it, you'll see the following printed to your console:运行它时,您会看到以下打印到您的控制台:

isSet(value, test) = Value{type: Boolean, value: true}
isSet(value, foo) = Value{type: Boolean, value: true}
isSet(value, fooooooo) = Value{type: Boolean, value: false}
isSet(value, test) and isSet(value, foo) = Value{type: Boolean, value: true}
isSet(value, nope) and isSet(value, test) and isSet(value, foo) = Value{type: Boolean, value: false}

EDIT编辑

[...] So visitVariableParameter will be called, how can I distinguish there between the different options and call the correct visit method [...] [...] 因此将调用visitVariableParameter,我如何区分不同的选项并调用正确的访问方法[...]

You don't need to override that method.您不需要覆盖该方法。 Let's say you want to implement isSet(instance, instanceId) , then all you have to do is override visitInstanceType(...) in your visitor.假设您想实现isSet(instance, instanceId) ,那么您所要做的就是在您的访问者中覆盖visitInstanceType(...) Whenever somewhere in your code this.visit(ctx.parameter());每当你的代码中的某个地方this.visit(ctx.parameter()); is called, it will automatically makes sure the children of parameter are called.被调用,它会自动确保parameter的子代被调用。

You only add this method to the visitor:您只需将此方法添加到访问者:

@Override
public Value visitInstanceType(FELParser.InstanceTypeContext ctx) {
    // 'instance' COMMA instanceParameter
    Value value = new Value(String.format("TODO: instance, `%s`", ctx.instanceParameter().getText()));
    System.out.println("We're in visitInstanceType(...), returning: " + value);
    return value;
}

And now when evaluating "isSet(instance, instanceId)" , you will see that the above method is invoked properly.现在在评估"isSet(instance, instanceId)" ,您将看到上述方法被正确调用。

Thanks for the great answer, this helps a lot.感谢您的出色回答,这很有帮助。 Actually I am testing this, but how to handle the rule其实我正在测试这个,但是如何处理规则

variableType:
                    valueType                      #ValueTyp
                    |instanceType                #InstanceTyp
                    |formType                      #FormTyp
                    |bufferType                    #BufferTyp
                    |instanceAttribType       #InstanceAttribTyp
                    |formAttribType             #FormAttribTyp
                    |mandatorAttribType     #ManatorTyp
                    ;

This will called in commandIsSet because there is a parameter.这将在 commandIsSet 中调用,因为有一个参数。 So visitVariableParameter will be called, how can I distinguish there between the different options and call the correct visit method.所以将调用visitVariableParameter,我如何区分不同的选项并调用正确的访问方法。 Actually I am trying this with ctx.start.toString().equals("value") but this seem not a good approach实际上我正在尝试使用 ctx.start.toString().equals("value") 但这似乎不是一个好方法

@Override
public Value visitVariableParameter(FELParser.VariableParameterContext ctx) {
    System.out.println("called variable parameter");
    
    if (ctx.start.toString().startsWith("value")){
        //compile error wrong ctx
        return this.visitValueTyp(ctx);
    }
    
    return null;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM