如何在没有回溯的情况下同时进行函数调用和括号分组

时间:2012-05-23 00:48:00

标签: antlr antlr3

有没有办法指定允许以下语法的语法:

f(x)(g, (1-(-2))*3, 1+2*3)[0]

转换为(在伪lisp中显示顺序):

(index 
  ((f x)
    g 
    (* (- 1 -2) 3)
    (+ (* 2 3) 1)
  ) 
  0
)

以及有限的运营商优先等等。


以下语法适用于backtrack = true,但我想避免这种情况:

grammar T;

options {
 output=AST;
 backtrack=true;
 memoize=true;
}

tokens {
  CALL;
  INDEX;
  LOOKUP;
}

prog: (expr '\n')* ;

expr : boolExpr;

boolExpr
 : relExpr (boolop^ relExpr)?
 ;

relExpr
 : addExpr (relop^ addExpr)?
 | a=addExpr oa=relop b=addExpr ob=relop c=addExpr
   -> ^(LAND ^($oa $a $b) ^($ob $b $c))
 ;


addExpr
 : mulExpr (addop^ mulExpr)?
 ;

mulExpr
 : atomExpr (mulop^ atomExpr)?
 ;

atomExpr
 : INT
 | ID
 | OPAREN expr CPAREN -> expr
 | call
 ;

call
 : callable ( OPAREN (expr (COMMA expr)*)? CPAREN -> ^(CALL callable expr*)
            | OBRACK expr CBRACK -> ^(INDEX callable expr)
            | DOT ID -> ^(INDEX callable ID)
            )
 ;

fragment
callable
 : ID
 | OPAREN expr CPAREN
 ;

fragment
boolop
 : LAND | LOR
 ;

fragment
relop
 : (EQ|GT|LT|GTE|LTE)
 ;

fragment
addop
 : (PLUS|MINUS)
 ;

fragment
mulop
 : (TIMES|DIVIDE)
 ;

EQ : '==' ;
GT : '>' ;
LT : '<' ;
GTE : '>=' ;
LTE : '<=' ;

LAND : '&&' ;
LOR : '||' ;

PLUS : '+' ;
MINUS : '-' ;
TIMES : '*' ;
DIVIDE : '/' ;

ID : ('a'..'z')+ ;
INT : '0'..'9' ;

OPAREN : '(' ;
CPAREN : ')' ;
OBRACK : '[' ;
CBRACK : ']' ;
DOT : '.' ;

COMMA : ',' ;

1 个答案:

答案 0 :(得分:2)

你的语法有几个问题:

1

只有词法分析器规则可以是fragment,而不是解析器规则。一些ANTLR目标只是忽略解析器规则前面的fragment关键字(比如Java目标),但最好只从语法中删除它们:如果你决定为不同的目标语言创建一个解析器,你可能会遇到问题,因为它

2

如果没有backtrack=true,则无法混合树重写运算符(^!重写规则(->),因为您需要在relExpr内创建一个替代方案而不是现在的两个替代方案(这是为了消除歧义)。

在你的情况下,你不能只用^(在一个替代方案中)创建所需的AST,所以你需要这样做:

relExpr
 : (a=addExpr -> $a) ( (oa=relOp b=addExpr    -> ^($oa $a $b))
                         ( ob=relOp c=addExpr -> ^(LAND ^($oa $a $b) ^($ob $b $c))
                         )?
                     )?
 ;

(是的,我知道,这不是特别漂亮,但是AFAIK无法帮助)

此外,如果在LAND块中定义了tokens { ... }令牌,则只能将tokens { // literal tokens LAND='&&'; ... // imaginary tokens CALL; ... } 令牌放入重写规则中:

mulExpr
 : unaryExpr ((TIMES | DIVIDE)^ unaryExpr)*
 ;

unaryExpr
 : MINUS atomExpr -> ^(UNARY_MINUS atomExpr)
 | atomExpr
 ;

否则,如果它们确实出现在解析器规则本身内,则只能在重写规则中使用标记(和其他解析器规则)。

3

你没有考虑你的语法中的一元减号,实现它如下:

backtrack=true

现在,要创建不需要ID的语法,请从'(' expr ')'规则中删除atomExpratomExpr : INT | call ;

callable

并在call规则中将所有内容call : (callable -> callable) ( OPAREN params CPAREN -> ^(CALL $call params) | OBRACK expr CBRACK -> ^(INDEX $call expr) | DOT ID -> ^(INDEX $call ID) )* ; 设为可选:

ID

这样,'(' expr ')'call已经与grammar T; options { output=AST; } tokens { // literal tokens EQ = '==' ; GT = '>' ; LT = '<' ; GTE = '>=' ; LTE = '<=' ; LAND = '&&' ; LOR = '||' ; PLUS = '+' ; MINUS = '-' ; TIMES = '*' ; DIVIDE = '/' ; OPAREN = '(' ; CPAREN = ')' ; OBRACK = '[' ; CBRACK = ']' ; DOT = '.' ; COMMA = ',' ; // imaginary tokens CALL; INDEX; LOOKUP; UNARY_MINUS; PARAMS; } prog : expr EOF -> expr ; expr : boolExpr ; boolExpr : relExpr ((LAND | LOR)^ relExpr)? ; relExpr : (a=addExpr -> $a) ( (oa=relOp b=addExpr -> ^($oa $a $b)) ( ob=relOp c=addExpr -> ^(LAND ^($oa $a $b) ^($ob $b $c)) )? )? ; addExpr : mulExpr ((PLUS | MINUS)^ mulExpr)* ; mulExpr : unaryExpr ((TIMES | DIVIDE)^ unaryExpr)* ; unaryExpr : MINUS atomExpr -> ^(UNARY_MINUS atomExpr) | atomExpr ; atomExpr : INT | call ; call : (callable -> callable) ( OPAREN params CPAREN -> ^(CALL $call params) | OBRACK expr CBRACK -> ^(INDEX $call expr) | DOT ID -> ^(INDEX $call ID) )* ; callable : ID | OPAREN expr CPAREN -> expr ; params : (expr (COMMA expr)*)? -> ^(PARAMS expr*) ; relOp : EQ | GT | LT | GTE | LTE ; ID : 'a'..'z'+ ; INT : '0'..'9'+ ; SPACE : (' ' | '\t') {skip();}; 匹配(并且没有歧义)。


考虑到上述所有评论,您可以获得以下语法:

"a >= b < c"

将输入"f(x)(g, (1-(-2))*3, 1+2*3)[0]"解析为以下AST:

enter image description here

和输入{{1}}如下:

enter image description here