簡體   English   中英

ANTLR4 調用訪問者的下一步不起作用

[英]ANTLR4 calling next step in visitor not working

我的語法已經完成並想實現現在的功能。 但是我發現它不起作用,我不明白如何在 functionExpr 中評估應該調用哪個命令。 作為我使用的測試表達式

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

目前我不知道如何評估單個 CommandIsSet 函數以及如何評估變量類型。 要解決這個問題,訂單應該是

  1. 訪問程序
  2. 訪問和Expr
  3. visitFunctionExpr(對於第一個 isSet(value, test)
  4. 訪問命令設置
  5. 訪問變量參數
  6. 訪問值類型
  7. visitFunctionExpr(對於第二個 isSet(value, foo)
  8. 訪問命令設置
  9. 訪問變量參數
  10. 訪問值類型

目前我停留在第 3 步,我如何確定它是一個 commandIsSet 我是否需要使用 ctx.getText() 並檢查它是否以 isSet 開頭?

我已經發現從@BartKiers一個偉大的答案SO這樣的話題在一個簡單的整數列表語法在AntLR4使用參觀者從那里我用值對象。

這是我的語法

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.

這是我的帶有測試表達式的訪問者實現

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

這個方法中的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());
}

有 2 個子節點的索引為 0 和 1。

你在訪問者中讓它有點太復雜了。 從您想要計算的最簡單的表達式開始,例如isSet(value, test) ,然后從樹的底部到樹的根部執行這些visit...()方法。 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
}

當它起作用時,向它添加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
}

等等。

一個小的工作示例如下所示:

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

運行它時,您會看到以下打印到您的控制台:

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}

編輯

[...] 因此將調用visitVariableParameter,我如何區分不同的選項並調用正確的訪問方法[...]

您不需要覆蓋該方法。 假設您想實現isSet(instance, instanceId) ,那么您所要做的就是在您的訪問者中覆蓋visitInstanceType(...) 每當你的代碼中的某個地方this.visit(ctx.parameter()); 被調用,它會自動確保parameter的子代被調用。

您只需將此方法添加到訪問者:

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

現在在評估"isSet(instance, instanceId)" ,您將看到上述方法被正確調用。

感謝您的出色回答,這很有幫助。 其實我正在測試這個,但是如何處理規則

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

這將在 commandIsSet 中調用,因為有一個參數。 所以將調用visitVariableParameter,我如何區分不同的選項並調用正確的訪問方法。 實際上我正在嘗試使用 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