Compiler construction 如何使用ANTLR修改CommonTokenStream中的令牌文本?
我试图学习ANTLR,同时将其用于当前项目 我已经到了可以在代码块上运行lexer并将其输出到CommonTokenStream的地步。这工作正常,并且我已经验证了源文本被分解成适当的标记 现在,我希望能够修改这个流中某些令牌的文本,并显示现在修改的源代码 例如,我尝试过:Compiler construction 如何使用ANTLR修改CommonTokenStream中的令牌文本?,compiler-construction,antlr,antlr3,lexical-analysis,Compiler Construction,Antlr,Antlr3,Lexical Analysis,我试图学习ANTLR,同时将其用于当前项目 我已经到了可以在代码块上运行lexer并将其输出到CommonTokenStream的地步。这工作正常,并且我已经验证了源文本被分解成适当的标记 现在,我希望能够修改这个流中某些令牌的文本,并显示现在修改的源代码 例如,我尝试过: import org.antlr.runtime.*; import java.util.*; public class LexerTest { public static final int IDENTIFIER
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());
}
}
import org.antlr.runtime.*;
导入java.util.*;
公共类词汇测试
{
公共静态最终整数标识符_TYPE=4;
公共静态void main(字符串[]args)
{
String input=“publicstaticvoidmain(String[]args){int myVar=0;}”;
CharStream cs=新的AntlStringStream(输入);
JavaLexer-lexer=新的JavaLexer(cs);
CommonTokenStream令牌=新的CommonTokenStream();
setTokenSource(lexer);
int size=tokens.size();
对于(int i=0;i
我正在尝试将所有标识符标记的文本设置为字符串文字“V”
编辑:
对我来说重要的一点是,我希望代币有其原始的开始和结束字符位置。也就是说,我不希望它们在变量名更改为“V”时反映它们的新位置。这就是我知道标记在原始源文本中的位置。ANTLR在其语法文件中有一种方法可以做到这一点 假设您正在解析一个由数字和逗号分隔的字符串组成的字符串。语法如下所示:
grammar Foo;
parse
: value ( ',' value )* EOF
;
value
: Number
| String
;
String
: '"' ( ~( '"' | '\\' ) | '\\\\' | '\\"' )* '"'
;
Number
: '0'..'9'+
;
Space
: ( ' ' | '\t' ) {skip();}
;
/** 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]
;
这对你来说应该很熟悉。假设您希望将所有整数值用方括号括起来。以下是如何做到这一点:
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();}
;
产生:
parsing: 12, "34", 56, "a\"b", 78
tokens: [12],"34",[56],"a\"b",[78]
如果您希望在所有情况下全局替换文本,那么在lexer中更改文本的另一个给定示例效果很好,但是您通常只希望在某些情况下替换令牌的文本 使用TokenRewriteStream允许您仅在特定上下文中灵活地更改文本 这可以使用您使用的令牌流类的子类来完成。您可以使用
TokenRewriteStream
而不是使用CommonTokenStream
类
因此,您可以让令牌流使用lexer,然后运行解析器
在你的语法中,你通常会这样做:
grammar Foo;
parse
: value ( ',' value )* EOF
;
value
: Number
| String
;
String
: '"' ( ~( '"' | '\\' ) | '\\\\' | '\\"' )* '"'
;
Number
: '0'..'9'+
;
Space
: ( ' ' | '\t' ) {skip();}
;
/** 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]
;
这里,我们用文本float替换了匹配的标记int。位置信息被保留,但其“匹配”的文本已被更改
要在使用与以前相同的代码后检查令牌流。在ANTLR 4中,有一个新的工具,使用解析树侦听器和令牌流重写器(注意名称差异),可用于观察或转换树。(流的回复适用于ANTLR 3,不适用于ANTLR 4。) 在ANTL4中,会为您生成一个XXXBaseListener类,其中包含用于进入和退出语法中每个非终端节点的回调(例如enterClassDeclaration()) 您可以通过两种方式使用侦听器: 1) 作为观察者—只需重写方法以生成与输入文本相关的任意输出—例如,重写enterClassDeclaration(),并为程序中声明的每个类输出一行 2) 作为转换器,使用令牌重写流在原始文本通过时修改原始文本。为此,您可以使用重写器在回调方法中修改(添加、删除、替换)标记,并使用重写器和结束输出修改后的文本 有关如何进行转换的示例,请参见ANTL4手册中的以下示例: 及
我使用示例Java语法创建了一个ANTLR脚本来处理
R.Java
文件,并用R.string.*
,R.id.*
,R.layout.*
等格式的值重写反编译Android应用程序中的所有十六进制值
关键是使用TokenStreamRewriter
处理令牌,然后输出结果
该项目(Python)称为
修改后的ANTLR侦听器用于重写
我使用一个侦听器进行解析,读取R.java文件,创建一个从整数到字符串的映射,然后将十六进制值替换为另一个包含重写器实例的侦听器,我使用另一个侦听器解析程序java文件
class RValueReplacementListener(ParseTreeListener):
replacements = 0
r_mapping = {}
rewriter = None
def __init__(self, tokens):
self.rewriter = TokenStreamRewriter(tokens)
// Code removed for the sake of brevity
# Enter a parse tree produced by JavaParser#integerLiteral.
def enterIntegerLiteral(self, ctx:JavaParser.IntegerLiteralContext):
hex_literal = ctx.HEX_LITERAL()
if hex_literal is not None:
int_literal = int(hex_literal.getText(), 16)
if int_literal in self.r_mapping:
# print('Replace: ' + ctx.getText() + ' with ' + self.r_mapping[int_literal])
self.rewriter.replaceSingleToken(ctx.start, self.r_mapping[int_literal])
self.replacements += 1
谢谢你的信息。你知道为什么调用单个令牌上的SETTY不起作用吗?@ SimuCUL,你尝试使用<代码> TokReReWestRestRAM< <代码>代替“<代码>通俗英语流> /Cord>?@ Simucal,我还没有为ANTLR挖掘java源代码,因为我通常使用C++,但我可以想象,您正在修改令牌流的副本,而不是实际的流。只是想知道-您是否需要为此使用ANTLR?到GitHub repo的链接现在已经不存在了。我在这里找到了一个示例: