如何使用ANTLR修改CommonTokenStream中的标记文本?

mmc*_*ole 5 compiler-construction antlr lexical-analysis antlr3

我正在尝试学习ANTLR,同时将它用于当前项目.

我已经达到了可以在一大块代码上运行词法分析器并将其输出到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());
    }  
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试将所有Identifier token的文本设置为字符串文字"V".

  1. 当我调用tokens.toString()时,为什么我对令牌文本的更改没有反映出来?

  2. 我怎么想知道各种令牌类型ID?我使用我的调试器,看到IDENTIFIER标记的ID为"4"(因此我的常量位于顶部).但是我怎么知道呢?是否有其他方法将令牌类型ID映射到令牌名称?


编辑:

对我来说重要的一件事是我希望令牌具有原始的开始和结束角色位置.也就是说,我不希望他们反映他们的新职位,变量名称改为"V".这是我知道原始源文本中令牌的位置.

Bar*_*ers 5

ANTLR有一种方法可以在它的语法文件中执行此操作.

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

grammar Foo;

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

value
  :  Number
  |  String
  ;

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

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

Space
  :  ( ' ' | '\t' ) {skip();}
  ;
Run Code Online (Sandbox Code Playgroud)

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

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();}
  ;
Run Code Online (Sandbox Code Playgroud)

如您所见,我options在顶部添加了一些,并在解析器规则->之后添加了重写规则(之后的所有内容).Numbervalue

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

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());
  }
}
Run Code Online (Sandbox Code Playgroud)

产生:

parsing: 12, "34", 56, "a\"b", 78
tokens: [12],"34",[56],"a\"b",[78]
Run Code Online (Sandbox Code Playgroud)


cho*_*ida 3

如果您想在所有情况下全局替换文本,则更改词法分析器中的文本的另一个给定示例效果很好,但是您通常只想在某些情况下替换标记的文本。

使用 TokenRewriteStream 使您可以灵活地仅在某些上下文中更改文本。

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

因此,您将让 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]
;
Run Code Online (Sandbox Code Playgroud)

这里我们替换了与文本 float 匹配的 token int。位置信息被保留,但它“匹配”的文本已被更改。

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