【问题标题】:ANTLR AST rules fail with RewriteEmptyStreamExceptionANTLR AST 规则因 RewriteEmptyStreamException 而失败
【发布时间】:2010-04-26 00:26:48
【问题描述】:

我有一个简单的语法:

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)*
    ;

这种行为差异是错误吗?

【问题讨论】:

    标签: parsing antlr


    【解决方案1】:

    不,不是错误,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 章:树构造。尤其是 Rewrite Rules in Subrules(第 173 页)和 Rewrite Rules 中引用以前的 Rule AST(第 174/175 页)段落。

    【讨论】:

      【解决方案2】:

      如果您想为 '*' 运算符生成一个所有子级都处于同一级别的 N-ary 树,您可以这样做:

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

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

      Tokens: AST
      factor: factor
      factor '*' factor: ^('*' factor factor)
      factor '*' factor '*' factor: ^('*' factor factor factor)
      

      上面 Bart 的第三个示例将生成一个嵌套树,因为 $expr 每次连续迭代的结果是一个具有两个子节点的节点,如下所示:

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

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

      【讨论】:

      • 非常感谢@JoelPM。这正是我一直在寻找的。我们在评估时遇到了深度嵌套树和堆栈溢出的问题。这使我们有机会生成 N 叉树并大大降低树的深度
      猜你喜欢
      • 2019-10-05
      • 2015-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多