Antlr 如何在没有回溯的情况下同时进行函数调用和插入式分组

Antlr 如何在没有回溯的情况下同时进行函数调用和插入式分组,antlr,antlr3,Antlr,Antlr3,有没有办法指定允许以下语法的语法: f(x)(g, (1-(-2))*3, 1+2*3)[0] 转换为(在伪lisp中显示顺序): 以及有限运算符优先级等 以下语法适用于backtrack=true,但我希望避免这种情况: grammar T; options { output=AST; backtrack=true; memoize=true; } tokens { CALL; INDEX; LOOKUP; } prog: (expr '\n')* ; expr

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

f(x)(g, (1-(-2))*3, 1+2*3)[0]
转换为(在伪lisp中显示顺序):

以及有限运算符优先级等


以下语法适用于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 : ',' ;
语法T;
选择权{
输出=AST;
回溯=真;
memoize=true;
}
代币{
呼叫
指数
查找;
}
程序:(expr'\n')*;
表达式:boolExpr;
博莱克斯普
:relExpr(boolop^relExpr)?
;
重新解释
:addExpr(relop^addExpr)?
|a=加法运算oa=加法运算b=加法运算ob=加法运算c=加法运算
->^(土地^($oa$a$b)^($ob$b$c))
;
加法器
:mulExpr(addop^mulExpr)?
;
mulExpr
:atomExpr(mulop^atomExpr)?
;
atomExpr
:INT
|身份证
|OPAREN expr CPAREN->expr
|召唤
;
呼叫
:可调用(OPAREN(expr(逗号expr)*)?CPAREN->^(调用可调用expr*)
|OBRACK expr CBRACK->^(索引可调用expr)
|点ID->^(索引可调用ID)
)
;
片段
可调用
:ID
|OPAREN expr CPAREN
;
片段
博洛普
:土地|土地
;
片段
重播
:(EQ | GT | LT | GTE | LTE)
;
片段
阿多普
:(加|减)
;
片段
穆洛普
:(倍|分)
;
等式:'=';
GT:“>”;
LT:'=';

LTE:“你的语法有几处错误:

1. 只有lexer规则可以是
fragment
s,而不是解析器规则。一些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))
                         )?
                     )?
 ;
(是的,我知道,它不是特别漂亮,但这是没办法的)

此外,如果
LAND
标记在
tokens{…}
块中定义,则只能将其放入重写规则中:

tokens {
  // literal tokens
  LAND='&&';
  ...

  // imaginary tokens
  CALL;
  ...
}
否则,您只能在重写规则中使用令牌(和其他解析器规则),如果它们确实发生在解析器规则本身内部

3. 你没有在语法中解释一元负号,你可以这样实现它:

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

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

现在,要创建不需要
backtrack=true
的语法,请从
atomExpr
规则中删除
ID
'('expr')

atomExpr
 : INT
 | call
 ;
call
 : (callable -> callable) ( OPAREN params CPAREN -> ^(CALL $call params)
                          | OBRACK expr CBRACK   -> ^(INDEX $call expr)
                          | DOT ID               -> ^(INDEX $call ID)
                          )*
 ;
并在您的
调用
规则中使所有传递的内容都可调用
可选:

atomExpr
 : INT
 | 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();};
语法T;
选择权{
输出=AST;
}
代币{
//文字标记
等式='='=';
GT='>';
LT='=';

LTE='有什么理由人们通常不把所有的令牌放在tokens块中吗?@luxun,你只能把文字令牌放在里面,所以像
Bs:'B'+;
这样的规则不能放在
tokens{…}
块中。但是把文字令牌放在哪里并不重要:按照lexer规则还是放在
tokens{…}
(当然,除非你在重写规则中“注入”外来标记)。@Bart_Kiers:我如何在这个方案中实现一个类似于
OPAREN(ID(逗号ID)+)的函数文本?CPAREN OBRACE stmt*CBRACE
?@luxun,更改
callable
,使其在
OPAREN expr CPAREN
中不接受一个
expr
,而是接受更多。并将
OBRACE stmt*CBRACE
添加到
调用
规则的右侧。如果遇到问题,请创建一个新问题:这些注释框不适合对于广泛的问答。祝你好运!不太明白你在说什么,所以请帮助我进一步。