Antlr 在避免回溯的同时添加(…){…}函数文本

Antlr 在避免回溯的同时添加(…){…}函数文本,antlr,Antlr,在中找到答案的基础上,我想添加非LL(*)形式的函数文本,其实现方式如下 ... tokens { ... FN; ID_LIST; } stmt : expr SEMI // SEMI=';' ; callable : ... | fn ; fn : OPAREN opt_id_list CPAREN compound_stmt -> ^(FN opt_id_list compound_stmt) ; compound_stmt : OBRACE s

在中找到答案的基础上,我想添加非LL(*)形式的函数文本,其实现方式如下

...

tokens {
 ...
 FN;
 ID_LIST;
}

stmt
 : expr SEMI // SEMI=';'
 ;

callable
 : ...
 | fn
 ;

fn
 : OPAREN opt_id_list CPAREN compound_stmt
   -> ^(FN opt_id_list compound_stmt)
 ;

compound_stmt
 : OBRACE stmt* CBRACE

opt_id_list
 : (ID (COMMA ID)*)? -> ^(ID_LIST ID*)
 ;


我想做的是允许匿名函数文本具有一个参数列表(例如,
()
(a)
(a,b,c)
),后跟一个
复合字符串。所以
(a,b,c){…}
是好的。但是,
(x)(y){}
没有那么多。(当然,
(x)*(y){}
在解析器方面是“有效的”,就像
((y){})([1].x
一样。)

解析器需要一些额外的前瞻性。我想没有它也可以做到,但是它肯定会导致一些看起来很糟糕的解析器规则,这些规则很难维护,而且解析器会接受
(a,2,3){…}
(一个带有表达式列表而不是id列表的函数文本),例如。这将导致您在创建AST之后进行大量语义检查

(IMO)解决这个问题的最佳方法是在
callable
中添加函数literal规则,并在其前面添加一个语法谓词,这将告诉解析器在实际匹配它之前确保确实存在这样的替代方法

callable
 : (fn_literal)=> fn_literal
 | OPAREN expr CPAREN -> expr
 | ID
 ;
演示:

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  = ',' ;
 OBRACE = '{' ;
 CBRACE = '}' ;
 SEMI   = ';' ;

 // imaginary tokens
 CALL;
 INDEX;
 LOOKUP;
 UNARY_MINUS;
 PARAMS;
 FN;
 ID_LIST;
 STATS;
}

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
 : (fn_literal)=> fn_literal
 | OPAREN expr CPAREN -> expr
 | ID
 ;

fn_literal
 : OPAREN id_list CPAREN compound_stmt -> ^(FN id_list compound_stmt)
 ;

id_list
 : (ID (COMMA ID)*)? -> ^(ID_LIST ID*)
 ;

params
 : (expr (COMMA expr)*)? -> ^(PARAMS expr*)
 ;

compound_stmt
 : OBRACE stmt* CBRACE -> ^(STATS stmt*)
 ;

stmt
 : expr SEMI
 ;

relOp
 : EQ | GT | LT | GTE | LTE
 ;

ID     : 'a'..'z'+ ;
INT    : '0'..'9'+ ;
SPACE  : (' ' | '\t') {skip();};
语法T;
选择权{
输出=AST;
}
代币{
//文字标记
等式='='=';
GT='>';
LT='=';

LTE='它不是来自现有的语言。如果上面的编辑没有意义,我将发布更广泛的示例以及我目前拥有的代码。