乐者为王

Do one thing, and do it well.

ANTLR 4权威参考读书笔记(13)

现在我们准备构建一个工具,用来把ANTLR 4权威参考读书笔记(7)中idata.txt里的数据按group分行显示,就像这样:

1
2
2 9 10
3 1 2 3

我们可以借助语法分析树的监听器机制来对词法分析结束后生成的记号流进行改写,我们不需要实现每一个监听器接口方法,只需要在捕获到group的时候把换行符插到它末尾就行。实现改写的代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
public class RewriteListener extends IDataBaseListener {
    TokenStreamRewriter rewriter;

    public RewriteListener(TokenStream tokens) {
        rewriter = new TokenStreamRewriter(tokens);
    }

    @Override
    public void enterGroup(IDataParser.GroupContext ctx) {
        rewriter.insertAfter(ctx.stop, '\n');
    }
}

接着就是写一个小程序来调用我们上面的改写类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class IData {

    public static void main(String[] args) throws Exception {
        InputStream is = args.length > 0 ? new FileInputStream(args[0]) : System.in;

        ANTLRInputStream input = new ANTLRInputStream(is);
        IDataLexer lexer = new IDataLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        IDataParser parser = new IDataParser(tokens);
        ParseTree tree = parser.file();

        RewriteListener listener = new RewriteListener(tokens);

        System.out.println("Before Rewriting");
        System.out.println(listener.rewriter.getText());

        ParseTreeWalker walker = new ParseTreeWalker();
        walker.walk(listener, tree);

        System.out.println("After Rewriting");
        System.out.println(listener.rewriter.getText());
    }
}

这里的关键是TokenStreamRewriter对象知道如何在不修改流的情况下提供一个记号流的修改过的视图。它把所有的操作方法当作指令并把它们排进队列,等到在遍历记号流把它作为文本渲染回去的时候再执行。每次我们调用getText()时rewriter就会执行那些指令。

最后就是构建和测试应用:

1
2
3
antlr IData.g
compile *.java
run IData idata.txt

以下是输出结果:

1
2
3
4
5
Before Rewriting
29103123
After Rewriting
2910
3123

仅用几行代码,我们就能够没有任何烦恼地对某些内容做轻微的调整。这种策略对于源代码检测或重构这类一般性的问题是非常有效的。TokenStreamRewriter是一个非常强大且有效的操作记号流的工具。

Comments