2011-11-12 46 views
1

我已經開始玩ANTL3了,我發現它非常酷,它支持多種語言非常棒。ANTL3 targetting中的表達式解析器Javascript

現在我正在試驗Javascript。我發現擴展@ antlr3 - Generating a Parse Tree

語法我會讓我的用戶調用由他在JavaScript中定義的功能,如:

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

請找我的語法:

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

我的測試鑽機:

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

然而,當我試圖評價:

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

它變得失敗。

請幫我寫適當的AST步行者來評估多值函數。

在此先感謝。

回答

2

在樹語法的call規則中,您應該推送exp*的評估值。目前,您正在將實際的AST推到您的params陣列上。

call規則更改爲:現在

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

,表達:

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

將被計算到:

35 
+0

巴特你搖滾!非常感謝... – humblecoder

+0

謝謝,不用客氣@humblecoder。 :) –