ANTLR AST规则因RewriteEmptyStreamException而失败

时间:2010-04-26 00:26:48

标签: parsing antlr

我有一个简单的语法:

grammar sample;
options { output = AST; }
assignment
    : IDENT ':=' expr ';'
    ;
expr    
    : factor ('*' factor)*
    ;
factor
    : primary ('+' primary)*
    ;
primary
    : NUM
    | '(' expr ')'
    ;
IDENT : ('a'..'z')+ ;
NUM   : ('0'..'9')+ ;
WS    : (' '|'\n'|'\t'|'\r')+ {$channel=HIDDEN;} ;

现在我想添加一些重写规则来生成AST。根据我在网上和“语言模式”一书中的内容,我应该能够像这样修改语法:

assignment
    : IDENT ':=' expr ';'   -> ^(':=' IDENT expr)
    ;
expr    
    : factor ('*' factor)* -> ^('*' factor+)
    ;
factor  
    : primary ('+' primary)* -> ^('+' primary+)
    ;
primary
    : NUM
    | '(' expr ')' -> ^(expr)
    ;

但它不起作用。虽然它编译得很好,但是当我运行解析器时,我得到一个RewriteEmptyStreamException错误。这就是事情变得奇怪的地方。

如果我定义伪令牌ADD和MULT并使用它们而不是树节点文字,它可以正常工作。

tokens { ADD; MULT; }

expr    
    : factor ('*' factor)* -> ^(MULT factor+)
    ;
factor  
    : primary ('+' primary)* -> ^(ADD primary+)
    ;

或者,如果我使用节点后缀表示法,它似乎也可以正常工作:

expr    
    : factor ('*'^ factor)*
    ;
factor  
    : primary ('+'^ primary)*
    ;

这种行为差异是一个错误吗?

2 个答案:

答案 0 :(得分:10)

不,不是错误,AFAIK。以您的expr规则为例:

expr    
    : factor ('*' factor)* -> ^('*' factor+)
    ;

由于*可能不存在,因此它也不应该在您的AST重写规则中。所以,以上是不正确的,ANTLR抱怨它正确。

现在,如果您插入像MULT这样的虚构标记:

expr    
    : factor ('*' factor)* -> ^(MULT factor+)
    ;

一切都没关系,因为你的规则总会生成一个或多个factor

你可能想要做的是这样的事情:

expr    
    :  (factor -> factor) ('*' f=factor -> ^('*' $expr $f))*
    ;

另请参阅The Definitive ANTLR Reference中的第7章:树构造。特别是段落重写规则(第173页)和在重写规则中引用先前规则AST (第174/175页)。

答案 1 :(得分:7)

如果要为'*'运算符生成一个N元树,所有子级都在同一级别,您可以这样做:

expr
    : (s=factor -> factor) (('*' factor)+ -> ^('*' $s factor+))?
    ;

以下是一些将返回的示例:

Tokens: AST
factor: factor
factor '*' factor: ^('*' factor factor)
factor '*' factor '*' factor: ^('*' factor factor factor)
巴顿上面的第三个例子将产生一个嵌套树,因为每个连续迭代的$ expr结果是一个有两个子节点的节点,如下所示:

factor * factor * factor: ^('*' factor ^('*' factor factor))

你可能不需要,因为乘法是可交换的。