ANTLR 有一个 TokenStreamRewriterTokenStreamRewriter 类,它非常擅长保存您的源输入。
它具有非常强大的功能。它允许您删除、插入或替换输入流中的文本。 IT 实际上存储了一系列待处理的更改,然后在您请求修改后的输入流时应用它们(甚至允许回滚更改以及多组更改)。
我最近所做的一次演示中涉及重写器的几个示例:
private void plus0(RefactorUtilContext ctx, String pName) {
for (var match : plus0PatternA.findAll(ctx, ANY_EXPR_XPATH)) {
var matchCtx = (AddSubExprContext) (match.getTree());
rewriter.delete(pName, matchCtx.op, matchCtx.rhs.getStop());
}
for (var match : plus0PatternB.findAll(ctx, ANY_EXPR_XPATH)) {
var matchCtx = (AddSubExprContext) (match.getTree());
rewriter.delete(pName, matchCtx.lhs.getStart(), matchCtx.op);
}
}
private void times1(RefactorUtilContext ctx, String pName) {
for (var match : times1PatternA.findAll(ctx, ANY_EXPR_XPATH)) {
var matchCtx = (MulDivExprContext) (match.getTree());
// rewriter.delete(pName, matchCtx.op, matchCtx.rhs.getStop());
rewriter.insertBefore(pName, matchCtx.op, "/* ");
rewriter.insertAfter(pName, matchCtx.rhs.getStart(), " */");
}
for (var match : times1PatternB.findAll(ctx, ANY_EXPR_XPATH)) {
var matchCtx = (MulDivExprContext) (match.getTree());
// rewriter.delete(pName, matchCtx.lhs.getStart(), matchCtx.op);
rewriter.insertBefore(pName, matchCtx.lhs.getStart(), "/* ");
rewriter.insertAfter(pName, matchCtx.op, " */");
}
}
TokenStreamRewriter,基本上,只是存储了一组关于如何修改输入流的指令,所以关于你输入流的所有内容,你不修改的是,嗯,未修改:)。
您可能还希望查看 ANTLR 拥有的XPath capabilities。这些允许您在解析树中找到非常具体的模式来定位您想要重构的部分。顾名思义,其语法与 XML 文档的 XPath 非常相似,但在解析树而不是 XML DOM 上工作。
注意:所有这些都在解析树上运行,而不是 AST(这必然是您自己的设计,因此 ANTLR 不会知道它的结构。此外,AST 的本质是丢弃不相关的信息(像 cmets、空格等),所以它们不是您想要保留格式的任何内容的良好起点。)
我为此演示文稿整理了一个很小的项目,它位于 GitHub 上的 LittleCalc。最相关的文件是LittleCalcExecutionVisitor。
您可以通过运行 LittleCalcRepl.java 来启动 REPL 并进行测试