2010-02-09 89 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