antlr4-遍历器例子(四)

在上文中,我们介绍了antlr4的2中遍历方式。下面,我们将对着2中遍历方式进行更为实际的例子,以确定他们各自的使用场合。

使用Parse-Tree Listeners

为了构建一个不混淆应用代码和语法的应用,关键点是解析器创建一个解析树然后遍历它,在遍历的过程中触发应用代码。我们可以使用我们最喜爱的技术或者antlr生成的树遍历机制来遍历。在这一小节中,我们将用antlr内置的ParseTreeWalker构建一个基于监听版本的属性文件应用。

//语法
grammar PropertyFile;
file : prop+ ;
prop : ID '=' STRING '\n' ;
ID   : [a-z]+ ;
STRING : '"' .*? '"' ;

//文件
user="parrt"
machine="maniac"

解析树

PropertyFileListener

PropertyFileBaseListener

public static class PropertyFileLoader extends PropertyFileBaseListener {
    Map<String,String> props = new HashMap<String, String>();
    public void exitProp(PropertyFileParser.PropContext ctx) {
        String id = ctx.ID().getText(); // prop : ID '=' STRING '\n' ;
        String value = ctx.STRING().getText();
        props.put(id, value);
    }
}

下面的类的依赖图,我就不解释了:

最后直接看结果吧,so easy:

public static void main(String[] args) throws Exception {
    String inputFile = "E:\\code\\antlr\\antlr4-example\\part2\\listeners\\src\\main\\resources\\t.properties";
    InputStream is = new FileInputStream(inputFile);
    ANTLRInputStream input = new ANTLRInputStream(is);
    PropertyFileLexer lexer = new PropertyFileLexer(input);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    PropertyFileParser parser = new PropertyFileParser(tokens);
    ParseTree tree = parser.file();

    // create a standard ANTLR parse tree walker
    ParseTreeWalker walker = new ParseTreeWalker();
    // create listener then feed to walker
    PropertyFileLoader loader = new PropertyFileLoader();
    walker.walk(loader, tree);        // walk parse tree
    System.out.println(loader.props); // print results
    //print {machine="maniac", user="parrt"}
}

使用Parse-Tree Visitors

当使用访问器时,我们仅仅是实现Visitor的代码即可。所以,无图无真相,无代码无…直接上代码了…

public static class PropertyFileVisitor extends
        PropertyFileBaseVisitor<Void>
{
    Map<String,String> props = new HashMap<String, String>();
    public Void visitProp(PropertyFileParser.PropContext ctx) {
        String id = ctx.ID().getText(); // prop : ID '=' STRING '\n' ;
        String value = ctx.STRING().getText();
        props.put(id, value);
        return null; // Java says must return something even when Void
    }
}

在这个例子中,prop节点没有子节点,所以,visitProp()方法不需要调用visit()方法,在下一节中我们将查看更通用一点的例子。