grammar Calc;

options
{
        output = AST;
        // language = Javascript;
}

tokens
{
        NEGATE;                 // used to disambiguate from subtract
}

parse
        : (stat ('\n'|EOF))+
        -> stat+
        ;

stat
        : REGISTER '='^ expr   // assignment
        | expr                 // expression
        ;

expr                            // lowest level of precedence
        : additionExpr
        ;

additionExpr                    // 5th level of precedence
        : multiplyExpr (('+'|'-')^ multiplyExpr)*
        ;

multiplyExpr                    // 4th level of precedence
        : indicyExpr (('*'|'/')^ indicyExpr)*
        ;

indicyExpr                      // 3rd level of precedence
        : prefixExpr ('**'^ prefixExpr)*
        ;

prefixExpr                      // 2nd level of precedence
        : prefix^ prefixExpr   // prefix rules are right associative
        | primaryExpr
        ;

primaryExpr                     // highest level of precedence
        : '('! expr^ ')'!
        | atom^
        ;

fragment
prefix                          // a list of allowable prefixes
        : '-'  -> NEGATE["-"]   // rewrite to differentiate from subtract
        | 'sqrt'
        | 'abs'
        ;

fragment
atom                            // unrecursable elements
        : ANS
        | NUMBER
        | REGISTER
        ;

ANS
        : 'ans'
        ;

REGISTER                        // a register variable
        : CHAR (CHAR | DIGIT | '_')*
        ;

NUMBER                          // any double precision number
        : DIGIT+ ('.' DIGIT*)? (('e'|'E') '-'? DIGIT+)?
        ;

fragment
CHAR
        : 'A' .. 'Z'
        | 'a' .. 'z'
        ;

fragment
DIGIT
        : '0' .. '9'
        ;

WS                               // whitespace
        : (' '|'\t')+ { $channel = HIDDEN; }
        ;