Question

I have started playing with ANTL3, I found it very cool, it's support for multiple languages is awesome.

Right now I am experimenting with Javascript. I've extended the grammar found @ antlr3 - Generating a Parse Tree

I would allow my user to call the functions defined by him in javascript such as:

function sum(args){
    var s=0;
    for(var i=0;i<args.length;i++)
        s+=args[i];
    return s;
}

function avg(args){
    var s=sum(args);
    return s/args.length;
}

Please find my grammar:

grammar Excel;
options {
  output=AST;
  language=JavaScript;
}
tokens {
    // define pseudo-operations
    FUNC;
    CALL;
}

parse
  :  exp EOF -> exp
  ;

exp
  :  orExp
  ;

orExp
  :  andExp (OR^ andExp)*
  ;

andExp
  :  eqExp (AND^ eqExp)*
  ;

eqExp
    : relExp (( EQUALS | NOTEQUALS)^ relExp)*
    ;

relExp
: addExp ( (LT^|LTEQ^|GT^|GTEQ^) addExp)*
;

addExp
: multExp ( (PLUS^| MINUS^) multExp)*
;
multExp 
: unaryExp (( MULT^ | DIV^ | MOD^ |POW^| IS^) unaryExp)*
;

unaryExp
  :  NOT atom -> ^(NOT atom)
  |  atom
  ;

atom
  :  TRUE
  |  FALSE
  |  INT
  |  FLOAT
  |  function
  |  '(' exp ')' -> exp
  ;
POW : '^';
DIV : '/';
MOD : '%';
MULT : '*';
PLUS : '+';
MINUS : '-';
LT   : '<';
LTEQ : '<=';
GT   : '>';
GTEQ : '>=';
EQUALS : '==';
NOTEQUALS : '<>';
INT    : '0'..'9'+;
FLOAT   :   ('0'..'9')* '.' ('0'..'9')+;
OR     : 'or' ;
AND    : 'and' ;
IS     : 'is' ;
NOT    : 'not' ;
TRUE   : 'true' ;
FALSE  : 'false' ;
function
    :   IDENT '(' ( exp (',' exp)* )? ')' -> ^(FUNC IDENT exp*)
;
IDENT
:   ('a'..'z' | 'A'..'Z') ('a'..'z' | 'A'..'Z' |'0'..'9')*
;   
SPACE  : (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;} ;

Tree Grammar:

tree grammar ExcelWalker;

options {
  tokenVocab=Excel;
  ASTLabelType=CommonTree;
  language=JavaScript;
}

// `walk` returns a string
walk returns [expr]
  :  exp {expr = $exp.expr;} //($exp.expr == 1) ? 'True' : 'False';}
  //|  ^(FUNC IDENT a=exp*)
 ;

// `exp` returns either 1 (true) or 0 (false)
exp returns [expr]
  :  ^(OR  a=exp b=exp) {expr = ($a.expr == 1 || $b.expr == 1) ? true : false;}
  |  ^(AND a=exp b=exp) {expr = ($a.expr == 1 && $b.expr == 1) ? true : false;}
  |  ^(IS  a=exp b=exp) {expr = ($a.expr == $b.expr) ? true : false;}
  |  ^(NOT a=exp)       {expr = ($a.expr == 1) ? false : true;}
  |  ^(LT a=exp b=exp) { expr = a<b;}
  |  ^(LTEQ a=exp b=exp) { expr = a<=b;}
  |  ^(GT a=exp b=exp) { expr = a>b;}
  |  ^(GTEQ a=exp b=exp) { expr = a>=b;}  
  |  ^(PLUS a=exp b=exp) { expr = a+b;}
  |  ^(MINUS a=exp b=exp) { expr = a-b;}
  |  ^(MULT a=exp b=exp){ expr = a*b;}
  |  ^(DIV a=exp b=exp) { expr = a/b;}
  |  ^(MOD a=exp b=exp) { expr = a \% b;}
  |  ^(POW a=exp b=exp) { expr = Math.pow(a,b); }  
  |  call               { expr=$call.value;}
  |  TRUE               {expr = true;}
  |  FALSE              {expr = false;}
  |  INT                { expr = $INT.text-0;}
  |  FLOAT              { expr = $FLOAT.text-0;}
  ;

call returns [value]
: ^(FUNC IDENT a=exp*) { 
    console.info($FUNC.toStringTree());
    var fn=$IDENT.text;
    var params=[];
    for(var i=1;i<$FUNC.getChildCount();i++)
        params.push($FUNC.getChild(i));
    var method=fn+'(['+params+'])';
    alert(method);
    var evalResult=eval(method);
    value=evalResult;
    alert(value);
    //alert('function '+fn+' with params '+params.length+' will be called');
    //alert($FUNC.getChildCount());alert($IDENT.text);alert(a.toString());
}
;

My Test Rig:

sum(2,3) produces 5
avg(1,2,3) produces 2

however when I am trying to evaluate:

sum(2+3,4+7,sum(4,5,6),avg(4,4,4,4))

It's getting failed.

Please help me to write proper AST walker to evaluate mutli-value functions.

Thanks in Advance.

Was it helpful?

Solution

Inside the call rule of the tree grammar, you should push the evaluated values of exp*. At the moment, you're pushing the actual AST's onto your params array.

Change your call rule to:

call returns [value]
  :  {
       var params = [];
     }
     ^(FUNC IDENT (exp {params.push($exp.expr);})*) 
     { 
       var fn = $IDENT.text;
       var method = fn + '([' + params + '])';
       value = eval(method);
     }
  ;

Now, the expression:

sum(2+3,4+7,sum(4,5,6),avg(4,4,4,4))

will be evaluated to:

35
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top