I'm trying to write a grammar to evaluate expressions.
I've started with the given example on the ANTLR website (it manage +,- and *). I added the division. But I would like to notify user if he tries to divide by 0. Moreover, I would like to add the pow in my evaluator (with an higher precedence than multiply and divide. (for example 2^3=8).
Hope it's understandable.
Here's my grammar Expr.g :

grammar Expr;

@header {  
import java.util.HashMap;  
}

@members {  
/** Map variable name to Integer object holding value */  
HashMap memory = new HashMap();  
}

prog:   stat+ ;

stat:   expr NEWLINE {System.out.println($expr.value);}  
    |   ID '=' expr NEWLINE  
        {memory.put($ID.text, new Integer($expr.value));}  
    |   NEWLINE  
    ;

expr returns [int value]
    :   e=multExpr {$value = $e.value;}
        ((   '+' e=multExpr {$value += $e.value;}
        |   '-' e=multExpr {$value -= $e.value;}
        ))*
    ;

multExpr returns [int value]
    :   e=atom {$value = $e.value;} 
        ('*' e=atom {$value *= $e.value;}
        |'/' e=atom {if (e != 0) $value /= $e.value; 
                else System.err.println("Division par 0 !");}
        )*
    ; 

atom returns [int value]
    :   INT {$value = Integer.parseInt($INT.text);}
    |   ID
        {
        Integer v = (Integer)memory.get($ID.text);
        if ( v!=null ) $value = v.intValue();
        else System.err.println("Variable indéfinie "+$ID.text);
        }
    |   '(' expr ')' {$value = $expr.value;}
    ;

ID  :   ('a'..'z'|'A'..'Z')+ ;
INT :   '0'..'9'+ ;
NEWLINE:'\r'? '\n' ;
WS  :   (' '|'\t')+ {skip();} ;

Thanks in advance. Ed.

有帮助吗?

解决方案

eouti wrote:

I added the division. But I would like to notify user if he tries to divide by 0.

Inside your multExpr rule, you shouldn't do if (e != 0) ..., but you should access e's value attribute instead. Also, the left-hand-side of your expression is called e while the right-hand-side is also called e. You'd better give them unique names:

multExpr returns [int value]
    :   e1=atom {$value = $e1.value;} 
        ( '*' e2=atom {$value *= $e2.value;}
        | '/' e2=atom {if ($e2.value != 0) $value /= $e2.value; 
                       else System.err.println("Division par 0 !");}
        )*
    ; 

But, do you really want to warn the user? After this warning, the calculation will just continue at the moment. IMO, you should just let the exception be thrown.

eouti wrote:

I would like to add the pow in my evaluator (with an higher precedence than multiply and divide.

Then add a powExpr rule in between multExpr and atom and let multExpr use this powExpr rule instead of the atom rule:

multExpr returns [int value]
    :   e1=powExpr       {...} 
        ( '*' e2=powExpr {...}
        | '/' e2=powExpr {...}
        )*
    ;  

powExpr returns [int value]
    :   atom      {...} 
        ('^' atom {...} 
        )*
    ;

atom returns [int value]
    :   INT          {...}
    |   ID           {...}
    |   '(' expr ')' {...}
    ;

(powExpr doesn't literally need to be in between these rules of course...)

Also, you might want to change returns [int value] into returns [double value], especially since you're using division.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top