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
添加到调用
规则的右侧。如果遇到问题,请创建一个新问题:这些注释框不适合对于广泛的问答。祝你好运!不太明白你在说什么,所以请帮助我进一步。