0

I have a grammar like this:

/* 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
    ;

Now, I want to parse this:

-1.3 * 5 + -2 * 7

With Grun, I get this:

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

enter image description here

Which looks OK and I would be happy with that.

But in my Java code, I get called like this using the Visitor pattern:

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

Which means that I don't get my negative number (-1.3), but rather the 'minus expression', which I should not get.

Why is my Java result different from Grun ? I have verified that the grammar is recompiled and I use my parser like this:

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

I have disabled the SLL mode to verify if this was not the problem, and the result was the same. I thought this could be a problem of precedence, but in my expr I have specified to match a value first, and then only a minusPlusArithmeticExpr.

I can't understand how I will detect this 'minus' expression instead of my 'negative value'. Can you check this?

Also, why does Grun show the correct behavior and not my Java code?

EDIT

Following the comments advice, I modified the grammar to look like this:

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
    ;

But now, I want to optimize the case where I have a single "-1.3" somewhere. I don't know how to do it correctly, since when I land in the visitMinusPlusAritmeticExpr, I have to check if the terminal node is a number.

Here is what I get while debugging:

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"

I suspect I should walk down the tree and tell if the terminal node is a number, but it seems cumbersome. Do you have any idea on how to do that without compromising legibility of my code?

Gui13
  • 12,993
  • 17
  • 57
  • 104
  • I cant tell you exact answer as i didn't play antlr for ages, but i think problem is because this statement your grammar `expr : ('-'|'+') expr` this together with statement `number : ('+'|'-')? NUMERIC_LITERAL` make statement `-1` ambiguous. – user902383 Apr 12 '16 at 10:11
  • Yeah I get what you say, but how should I do then? Because I should be able to identify "-1.3" and "-(5*6)" as two different things: the first is a negative number, the other is the operation or negating the expression in parenthesis. – Gui13 Apr 12 '16 at 10:28
  • yes "-1.3" and "-(5*6)" are different, because have brackets, maybe that you should add, and make statement `expr : ('-'|'+') '(' expr ')'` in this case `-1` wont be ambiguous and -5*6 wont be interpret as -(5*6) – user902383 Apr 12 '16 at 10:34
  • Yes, typing my answer, I thought about that. But now my problem is that there are expressions that don't need parenthesis, like `-myvar`. – Gui13 Apr 12 '16 at 11:00
  • Also, why does the TestRig gets it correctly and my code does not?? – Gui13 Apr 12 '16 at 11:07
  • you could do same with variable what you did with number `variable : ('+'|'-') VARIABLE` then -myvar will be evaluated properly. I don't have any clue why Grun gives you different result, maybe one of them is using bottom-up parsing while other top-down parsing – user902383 Apr 12 '16 at 11:34
  • Get rid of the `('+'|'-')?` in the `number` rule. Negative number = negated positive number expression. I have a minimalist example grammar [here](http://stackoverflow.com/a/29996191/3764814), along with a working calculator. – Lucas Trzesniewski Apr 12 '16 at 11:54
  • No that is not what I want. I should be able to detect whether the number is a negative one, so that I can optimize the case.. – Gui13 Apr 12 '16 at 13:59
  • Yes, you should do it like Lucas suggested. If you want to detect if `expr` in `minusPlusArithmeticExpr` is a number, you simply do so in a visitor (check if `expr` is a `TerminalNode`) – Bart Kiers Apr 12 '16 at 19:27
  • Hi bart, I tried but in my debugger, I get a hierarchy like this in expr(): argumentArithmeticExpr -> value -> number -> terminalNode. So if I want to be sure of the type of the expr() I have to recursively check that I only have 1 child down to the bottom, and that the terminalNode is a number? This seems overly complicated.. Do you have a code example for that? Thanks for all the help here! – Gui13 Apr 13 '16 at 06:32

1 Answers1

0

Ok, for those interested, Lucas and Bart got the answer, and my implementation is like this:

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
    ;

And in the visitor of 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);
        }
    }
}
Gui13
  • 12,993
  • 17
  • 57
  • 104