2010-02-09 190 views
5

我正在尝试学习ANTLR,并同时将它用于当前项目。如何使用ANTLR修改CommonTokenStream中令牌的文本?

我已经到了可以在一段代码上运行词法分析器并将其输出到CommonTokenStream的地步。这工作正常,我已验证源文本正在被分解为相应的标记。

现在,我希望能够修改此流中某些标记的文本,并显示现在修改的源代码。

例如,我已经试过:

import org.antlr.runtime.*; 
import java.util.*; 

public class LexerTest 
{ 
    public static final int IDENTIFIER_TYPE = 4; 

    public static void main(String[] args) 
    { 
    String input = "public static void main(String[] args) { int myVar = 0; }"; 
    CharStream cs = new ANTLRStringStream(input); 


     JavaLexer lexer = new JavaLexer(cs); 
     CommonTokenStream tokens = new CommonTokenStream(); 
     tokens.setTokenSource(lexer); 

     int size = tokens.size(); 
     for(int i = 0; i < size; i++) 
     { 
      Token token = (Token) tokens.get(i); 
      if(token.getType() == IDENTIFIER_TYPE) 
      { 
       token.setText("V"); 
      } 
     } 
     System.out.println(tokens.toString()); 
    } 
} 

我试图将所有标识符标记的文本字符串“V”。

  1. 为什么我在调用tokens.toString()时未反映对标记文本的更改?

  2. 我如何知道各种令牌类型ID?我用调试器走过去,发现IDENTIFIER标记的ID是“4”(因此我的常数在顶端)。但我怎么知道,否则呢?有没有其他方式将令牌类型ID映射到令牌名称?


编辑:

有一件事对我来说重要的是我希望的标记有原来的开始和结束字符位置。也就是说,我不希望他们反映他们的新职位,变量名称更改为“V”。这就是我知道令牌在原始源文本中的位置。

+0

只是想知道 - 是您使用ANTLR的要求为了这? – cowboydan 2013-12-18 22:11:23

回答

5

ANTLR有办法在语法文件中做到这一点。

假设您正在解析由逗号分隔的由数字和字符串组成的字符串。语法看起来是这样的:

grammar Foo; 

parse 
    : value (',' value)* EOF 
    ; 

value 
    : Number 
    | String 
    ; 

String 
    : '"' (~('"' | '\\') | '\\\\' | '\\"')* '"' 
    ; 

Number 
    : '0'..'9'+ 
    ; 

Space 
    : (' ' | '\t') {skip();} 
    ; 

这应该对你来说都很熟悉。假设您想要围绕所有整数值包装方括号。以下是如何做到这一点:

grammar Foo; 

options {output=template; rewrite=true;} 

parse 
    : value (',' value)* EOF 
    ; 

value 
    : n=Number -> template(num={$n.text}) "[<num>]" 
    | String 
    ; 

String 
    : '"' (~('"' | '\\') | '\\\\' | '\\"')* '"' 
    ; 

Number 
    : '0'..'9'+ 
    ; 

Space 
    : (' ' | '\t') {skip();} 
    ; 

正如你看到的,我已经添加了一些options在顶部,并在value解析器规则Number后添加(在->后一切)重写规则。

现在来测试这一切,编译并运行这个类:

import org.antlr.runtime.*; 

public class FooTest { 
    public static void main(String[] args) throws Exception { 
    String text = "12, \"34\", 56, \"a\\\"b\", 78"; 
    System.out.println("parsing: "+text); 
    ANTLRStringStream in = new ANTLRStringStream(text); 
    FooLexer lexer = new FooLexer(in); 
    CommonTokenStream tokens = new TokenRewriteStream(lexer); // Note: a TokenRewriteStream! 
    FooParser parser = new FooParser(tokens); 
    parser.parse(); 
    System.out.println("tokens: "+tokens.toString()); 
    } 
} 

主要生产:

parsing: 12, "34", 56, "a\"b", 78 
tokens: [12],"34",[56],"a\"b",[78] 
2

在词法分析器改变文本的其他给定的例子效果很好,如果你想全局替换所有情况下的文本,但是您经常只想在特定情况下替换令牌的文本。

使用TokenRewriteStream可让您灵活地仅在某些情况下更改文本。

这可以使用您正在使用的令牌流类的子类来完成。您可以使用TokenRewriteStream而不是使用CommonTokenStream类。

所以你会有TokenRewriteStream使用词法分析器,然后运行解析器。

在你的语法通常你会做更换这样的:

/** Convert "int foo() {...}" into "float foo();" */ 
function 
: 
{ 
    RefTokenWithIndex t(LT(1)); // copy the location of the token you want to replace 
    engine.replace(t, "float"); 
} 
type id:ID LPAREN (formalParameter (COMMA formalParameter)*)? RPAREN 
    block[true] 
; 

在这里,我们把它换成我们与文字匹配的浮动令牌INT。位置信息被保留,但它“匹配”的文本已被更改。

在您使用与以前相同的代码之后,检查您的令牌流。

+0

感谢您的信息。你有什么想法,为什么在个人令牌上调用setText不起作用? – mmcdole 2010-02-09 18:59:43

+0

@Simucal,你尝试使用'TokenRewriteStream'代替'CommonTokenStream'吗? – 2010-02-09 19:07:15

+0

@Simucal,我没有深入到antlr的java源代码,因为我通常使用C++,但我想象你正在修改令牌流的副本而不是实际的流。 – chollida 2010-02-09 19:10:47

2

在ANTLR 4中,有一个使用分析树监听器和TokenStreamRewriter(注意名称差异)的新工具,可用于观察或转换树。 (答复提示TokenRewriteStream应用于ANTLR 3和不会与ANTLR 4.工作)

在ANTL4为您生成与回调在语法进入和离开每个非末端节点(例如enterClassDeclaration一个XXXBaseListener类() )。

您可以使用监听器有两种方式:

1)作为观察员 - 只需重写方法来产生与输入文本任意输出 - 例如重写enterClassDeclaration()并为程序中声明的每个类输出一行。

2)作为使用TokenRewriteStream在原始文本通过时对其进行修改的转换器。为此,您可以使用重写器在回调方法中进行修改(添加,删除,替换)令牌,并使用重写器和结束来输出已修改的文本。

请参阅从ANTL4书下面的例子为如何做转换的例子:

https://github.com/mquinn/ANTLR4/blob/master/book_code/tour/InsertSerialIDListener.java

https://github.com/mquinn/ANTLR4/blob/master/book_code/tour/InsertSerialID.java

+0

GitHub回购的链接现在已经死了。 – 2017-09-18 18:30:46