2

我有一个带有如下子树的 antlr 语法:

 ^(type ID)

我想转换为:

 ^(type DUMMY ID)

其中类型是“a”|“b”。

注意:我真正想做的是通过生成虚拟名称将匿名实例转换为显式实例。

我已将其范围缩小到以下语法,但我得到了这个:

(a bar) (b bar)
got td
got bu
Exception in thread "main" org.antlr.runtime.tree.RewriteEmptyStreamException: rule type
        at org.antlr.runtime.tree.RewriteRuleElementStream._next(RewriteRuleElementStream.java:157)
        at org.antlr.runtime.tree.RewriteRuleSubtreeStream.nextNode(RewriteRuleSubtreeStream.java:77)
        at Pattern.bu(Pattern.java:382)

错误消息继续。到目前为止我的调试:

  1. 输入通过初始语法生成两棵树。一个酒吧和一个酒吧。
  2. 第二个语法确实匹配树。它正在打印 td 和 bu。
  3. 重写崩溃,但我不知道为什么?RewriteEmptyStreamException 是什么意思。

进行这种重写的正确方法是什么?

我的主要语法 Rewrite.g:

grammar Rewrite;

options {
  output=AST;
}

@members{
  public static void main(String[] args) throws Exception {
      RewriteLexer lexer = new RewriteLexer(new ANTLRStringStream("a foo\nb bar"));
      RewriteParser parser = new RewriteParser(new CommonTokenStream(lexer));
      CommonTree tree = (CommonTree)parser.test().getTree();
      System.out.println(tree.toStringTree());

      CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);        
      Pattern p = new Pattern(nodes);

      CommonTree newtree = (CommonTree) p.downup(tree);
  }
}

type 
    : 'a'
    | 'b'
    ;

test : id+;
id   : type ID  -> ^(type ID["bar"]);

DUMMY : 'dummy';
ID   : ('a'..'z')+;
WS : (' '|'\n'|'r')+ {$channel=HIDDEN;};

和 Pattern.g

tree grammar Pattern;

options {
    tokenVocab = Rewrite;
    ASTLabelType=CommonTree;
    output=AST;
    filter=true;             // tree pattern matching mode
}

topdown
    : td
    ;

bottomup
    : bu
    ;

type 
    : 'a'
    | 'b'
    ;

td
    : ^(type ID) { System.out.println("got td"); }
    ;

bu
    : ^(type ID) { System.out.println("got bu"); }
        -> ^(type DUMMY ID)
    ;

编译:

java -cp ../jar/antlr-3.4-complete-no-antlrv2.jar org.antlr.Tool Rewrite.g 
java -cp ../jar/antlr-3.4-complete-no-antlrv2.jar org.antlr.Tool Pattern.g 
javac -cp ../jar/antlr-3.4-complete-no-antlrv2.jar *.java 
java -classpath .:../jar/antlr-3.4-complete-no-antlrv2.jar RewriteParser

编辑 1:我也尝试过使用 antlr4,但我遇到了同样的崩溃。

4

2 回答 2

2

有两个小问题需要解决才能使重写工作,一个问题RewritePattern.

Rewrite语法^(type ID)在输出 AST 中作为根元素生成,如输出所示(a bar) (b bar)。不能转换根元素,因为转换实际上是子交换的一种形式:元素的父元素丢弃该元素并用新的“转换”版本替换它。没有父母,你会得到错误Can't set single child to a list。添加根是创建一个虚构的令牌ROOT或任何您喜欢的名称并在您的入门级规则的 AST 生成中引用它的问题,如下所示test : id+ -> ^(ROOT id+);

产生错误的Pattern语法被type规则混淆:type : 'a' | 'b' ;作为重写的一部分。我不知道这里的低级细节,但显然树解析器不会像编写转换时那样维护已访问根规则的状态type^(type ID)或者它不能或不应该,或者它可能是一些其他限制)。解决此问题的最简单方法是进行以下两个更改:

  • 通过将 rule in from更改为 just ,让文本“a”和“b”匹配ID词法分析器中的规则。typeRewritetype: 'a' | 'b';type: ID;
  • 让规则匹配并转换bu为。Pattern^(ID ID)^(ID DUMMY ID)

现在对Rewrite's进行一些小的调试更改main,输入"a foo\nb bar"会产生以下输出:

(ROOT (a foo) (b bar))
got td
got bu
(a foo) -> (a DUMMY foo)
got td
got bu
(b bar) -> (b DUMMY bar)
(ROOT (a DUMMY foo) (b DUMMY bar))

以下是我更改后的文件:

重写.g

grammar Rewrite;

options {
  output=AST;
}

tokens { 
 ROOT;
}

@members{
  public static void main(String[] args) throws Exception {
      RewriteLexer lexer = new RewriteLexer(new ANTLRStringStream("a foo\nb bar"));
      RewriteParser parser = new RewriteParser(new CommonTokenStream(lexer));
      CommonTree tree = (CommonTree)parser.test().getTree();
      System.out.println(tree.toStringTree());

      CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);        
      Pattern p = new Pattern(nodes);

      CommonTree newtree = (CommonTree) p.downup(tree, true); //print the transitions to help debugging
      System.out.println(newtree.toStringTree()); //print the final result
  }
}

type : ID;
test : id+ -> ^(ROOT id+);
id   : type ID  -> ^(type ID);

DUMMY : 'dummy';
ID   : ('a'..'z')+;
WS : (' '|'\n'|'r')+ {$channel=HIDDEN;};

模式.g

tree grammar Pattern;

options {
    tokenVocab = Rewrite;
    ASTLabelType=CommonTree;
    output=AST;
    filter=true;             // tree pattern matching mode
}

topdown
    : td
    ;

bottomup
    : bu
    ;

td
    : ^(ID ID) { System.out.println("got td"); }
    ;

bu
    : ^(ID ID) { System.out.println("got bu"); }
        -> ^(ID DUMMY ID)
    ;
于 2012-12-03T21:48:38.863 回答
1

我对树模式没有太多经验,不管有没有重写。但是当在其中使用重写规则时,我相信您的选项也应该包括rewrite=true;. Definitive ANTLR Reference 不处理它们,所以我不完全确定(查看ANTLR wiki了解更多信息)。

但是,对于这种(相对)简单的重写,您实际上并不需要单独的语法。您可以制作DUMMY一个虚构的标记并将其注入其他一些解析器规则,如下所示:

grammar T;

options {
  output=AST;
}

tokens {
  DUMMY;
}

test : id+;
id   : type ID  -> ^(type DUMMY["dummy"] ID);

type 
 : 'a'
 | 'b'
 ;

ID : ('a'..'z')+;
WS : (' '|'\n'|'r')+ {$channel=HIDDEN;};

这将解析输入:

a bar 
b foo

进入以下AST:

在此处输入图像描述

请注意,如果您的词法分析器还打算将输入标记"dummy"为标记,请将块DUMMY更改为:tokens { ... }

tokens {
  DUMMY='dummy';
}

而且您仍然可以在其他规则中注入a 。DUMMY

于 2012-12-03T20:27:22.150 回答