ANTLR:使用Grun

时间:2016-04-12 09:59:33

标签: java antlr4 operator-precedence

我有这样的语法:

/* entry point */
parse: expr EOF;


expr
    : value                                 # argumentArithmeticExpr
    | l=expr operator=(MULT|DIV) r=expr     # multdivArithmeticExpr
    | l=expr operator=(PLUS|MINUS) r=expr   # addsubtArithmeticExpr
    | operator=('-'|'+') r=expr             # minusPlusArithmeticExpr
    | IDENTIFIER '(' (expr ( COMMA  expr )* ) ? ')'# functionExpr
    | LPAREN expr RPAREN                    # parensArithmeticExpr
    ;

value
    : number
    | variable
    | string // contains date
    | bool
    | null_value
    ;


/* Atomes */

bool
    : BOOL
    ;

variable
    : VARIABLE
    ;

string
    : STRING_LITERAL
    ;


number
    : ('+'|'-')? NUMERIC_LITERAL
    ;

null_value
    : NULL // TODO: test this
    ;

IDENTIFIER
    : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
    ;

NUMERIC_LITERAL
    : DIGIT+ ( '.' DIGIT* )? ( E [-+]? DIGIT+ )? // ex: 0.05e3
    | '.' DIGIT+ ( E [-+]? DIGIT+ )? // ex: .05e3
    ;

INT: DIGIT+;

STRING_LITERAL
    :  '\'' ( ~'\'' | '\'\'' )* '\''
    |  '"' ( ~'"' | '""' )* '"'
    ;

VARIABLE
    : LBRACKET ( ~']' | ' ')* RBRACKET
    ;

现在,我想解析一下:

-1.3 * 5 + -2 * 7

有了Grun,我明白了:

 antlr4 formula.g4 && javac *.java && time  grun formula parse -gui
 -1.3*5 + -2*7
 ^D

enter image description here

看起来不错,我会很高兴。

但是在我的Java代码中,我使用访问者模式进行调用:

visitMinusPlusArithmeticExpr -1.3*5+-2*7 // ugh ?? sees "- (1.3 * 5 + - 2 * 7 )" instead of "(-1.3*5) + (-2*7)"
visitAddsubtArithmeticExpr 1.3*5+-2*7
visitMultdivArithmeticExpr 1.3*5
visitArgumentArithmeticExpr 1.3
visitNumber 1.3
visitArgumentArithmeticExpr 5
visitValue 5
visitNumber 5
visitMinusPlusArithmeticExpr -2*7 // UHG? should see a MultDiv with -2 and 7
visitMultdivArithmeticExpr 2*7
visitArgumentArithmeticExpr 2
visitValue 2
visitNumber 2
visitArgumentArithmeticExpr 7
visitValue 7
visitNumber 7

这意味着我没有得到我的负数(-1.3),而是我不应该得到的减去表达式。

为什么我的Java结果与Grun不同?我已经验证语法是重新编译的,我使用我的解析器:

    formulaLexer   lexer = new formulaLexer(new ANTLRInputStream(s));
    formulaParser parser = new formulaParser(new CommonTokenStream(lexer));

    parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
    parser.setErrorHandler(new BailErrorStrategy()); // will throw exceptions on failure

    formula = tryParse(parser);
    if( formula == null && errors.isEmpty() ){
        // the parsing failed, retry in LL mode
        parser.getInterpreter().setPredictionMode(PredictionMode.LL);
        parser.reset();
        tryParse(parser);
    }

我已禁用SLL模式以验证这不是问题,结果是一样的。 我认为这可能是一个优先问题,但在expr我已经指定首先匹配value,然后只匹配minusPlusArithmeticExpr

我无法理解我将如何发现这个'减去'表达而不是我的负值'。你能检查一下吗?

另外,为什么Grun会显示正确的行为而不是我的Java代码?

修改

根据评论建议,我将语法修改为如下所示:

expr
    : value                                 # argumentArithmeticExpr
    | (PLUS|MINUS) expr                     # plusMinusExpr
    | l=expr operator=(MULT|DIV) r=expr     # multdivArithmeticExpr
    | l=expr operator=(PLUS|MINUS) r=expr   # addsubtArithmeticExpr
    | function=IDENTIFIER '(' (expr ( COMMA  expr )* ) ? ')'# functionExpr
    | '(' expr ')'           # parensArithmeticExpr
    ;

但是现在,我想优化我有一个" -1.3"某处。 我不知道如何正确地做到这一点,因为当我登陆visitMinusPlusAritmeticExpr时,我必须检查终端节点是否是数字。

以下是我在调试时得到的内容:

ctx = {formulaParser$PlusMinusExprContext@929} "[16]"
children = {ArrayList@955}  size = 2
    0 = {TerminalNodeImpl@962} "-"
    1 = {formulaParser$ArgumentArithmeticExprContext@963} "[21 16]" 
        children = {ArrayList@967}  size = 1
        0 = {formulaParser$ValueContext@990} "[22 21 16]"
            children = {ArrayList@992}  size = 1
            0 = {formulaParser$NumberContext@997} "[53 22 21 16]"
                children = {ArrayList@999}  size = 1
                0 = {TerminalNodeImpl@1004} "1.3"

我怀疑我应该走在树下,告诉终端节点是否是一个数字,但这看起来很麻烦。您是否知道如何在不影响我的代码易读性的情况下做到这一点?

1 个答案:

答案 0 :(得分:0)

好的,对于那些感兴趣的人,Lucas和Bart得到了答案,我的实现是这样的:

expr
    : value                                 # argumentArithmeticExpr
    | (PLUS|MINUS) expr                     # plusMinusExpr
    | l=expr operator=(MULT|DIV) r=expr     # multdivArithmeticExpr
    | l=expr operator=(PLUS|MINUS) r=expr   # addsubtArithmeticExpr
    | function=IDENTIFIER '(' (expr ( COMMA  expr )* ) ? ')'# functionExpr
    | '(' expr ')'           # parensArithmeticExpr
    ;

在plusMinusExpr的访客中:

@Override
public Formula visitPlusMinusExpr(formulaParser.PlusMinusExprContext ctx) {
    if( debug ) LOG.log(Level.INFO, "visitPlusMinusExpr " + ctx.getText());
    Formula formulaExpr = visit(ctx.expr());
    if( ctx.MINUS() == null ) return formulaExpr;
    else {
        if(formulaExpr instanceof DoubleFormula){
            // optimization for numeric values: we don't return "(0.0 MINUS THEVALUE)" but directly "-THEVALUE"
            Double v = - ((DoubleFormula) formulaExpr).getValue();
            return new DoubleFormula( v );
        } else {
            return ArithmeticOperator.MINUS( 0, formulaExpr);
        }
    }
}