Antlr4学习2-Java开发

Antlr4学习2-Java开发

0x00 系列文章目录

  1. Antlr4学习1-基本概念和入门示例
  2. Antlr4学习2-Java开发

0x01 摘要

本文主要讲下使用Java来围绕Antlr开发和应用。

0x02 简单例子

2.1 Maven依赖

在pom.xml里加入以下依赖:

 <dependency>
            <groupId>org.antlrgroupId>
            <artifactId>antlr4artifactId>
            <version>4.7.1version>
dependency>

2.2 解析文件

新建一个解析文件SelectExample1.g4

grammar SelectExample1;
sql : 'select' WHAT 'from' tables ';';
WHAT : [a-z]+ ;
tables : WHAT;
WS : [ \t\r\n]+ -> skip ;

这是一个很简单的例子,匹配一条最简单的sql。

2.3 自动生成解析相关java类

在类上点右键,然后如下图所示操作:
自动生成解析相关java类
可以找到生成的一批java类如下图:
Antlr4学习2-Java开发_第1张图片
我们按需把这些文件拷贝到目录下。

2.4 编写解析测试类

创建一个SelectDemo.java文件如下内容:

package demos.antlr.select.demo1;

import demos.antlr.select.demo1.gen.SelectExample1Lexer;
import demos.antlr.select.demo1.gen.SelectExample1Parser;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;

/**
 * Created by chengc on 2018/9/30.
 */
public class SelectDemo
{
    public static void main(String[] args)
    {
        // first, form input charstream from string 
        CharStream input = CharStreams.fromString("select name from studetnt;");

        // 1. Lexer-Lexical analysis
        // Create a lexer that feeds off of input CharStream.
        SelectExample1Lexer lexer = new SelectExample1Lexer(input);

        // 2. Create a buffer of tokens pulled from the lexer.
        CommonTokenStream tokens = new CommonTokenStream(lexer);

        // 3. Parser-Syntax analysis
        // Create a parser that feeds off the tokens buffer.
        SelectExample1Parser parser = new SelectExample1Parser(tokens);

        // 4. Begin parsing at "select" rule.
        ParseTree tree = parser.sql();
        System.out.println(tree.toStringTree(parser));
    }
}

运行该main方法,输出的解析语法树结果如下:

(sql select name from (tables studetnt) ;)

0x03 语法树遍历模式

在idea里面通过.g4文件生成java代码的时候,可配两种方式:
Antlr4学习2-Java开发_第2张图片
默认是listener模式。

3.1 Listener模式 (观察者模式,通过结点监听,触发处理方法)

在遍历语法树时,自动回调Listener中回调方法。
实现简单,全自动,只需要实现listener中的各个接口方法。即可完成语义表达。并采用深度优先遍历。

优点:

  • 程序员不需要显示定义遍历语法树的顺序,实现简单

缺点:

  • 不能控制遍历语法树的顺序。例如,我们需要遍历一个C程序的语法树,希望跳过函数体语法子树,即忽略函数的函数体部分
  • 动作代码与文法产生式解耦,利于文法产生式的重用
  • 监听器模式无法使用函数返回值来传递数据,需要使用map、栈等结构在节点间传值

下面是一个Java集合类查询引擎CQEngine中的SQLParser例子:

@Override
    public ParseResult<O> parse(String query) {
        try {
            if (query == null) {
                throw new IllegalArgumentException("Query was null");
            }
            SQLGrammarLexer lexer = new SQLGrammarLexer(new ANTLRInputStream(query));
            lexer.removeErrorListeners();
            lexer.addErrorListener(SYNTAX_ERROR_LISTENER);

            CommonTokenStream tokens = new CommonTokenStream(lexer);

            SQLGrammarParser parser = new SQLGrammarParser(tokens);
            parser.removeErrorListeners();
            parser.addErrorListener(SYNTAX_ERROR_LISTENER);

            SQLGrammarParser.StartContext queryContext = parser.start();

            ParseTreeWalker walker = new ParseTreeWalker();
            SQLAntlrListener<O> listener = new SQLAntlrListener<O>(this);
            walker.walk(listener, queryContext);
            return new ParseResult<O>(listener.getParsedQuery(), listener.getQueryOptions());
        }
        catch (InvalidQueryException e) {
            throw e;
        }
        catch (Exception e) {
            throw new InvalidQueryException("Failed to parse query", e);
        }
    }

3.2 Vistor模式 (访问者模式,主动遍历)

则提供了可以控制遍历过程的一种方式,通过显示调用visit方法,可以访问子树节点。

特点如下:

  • 程序员可以显示定义遍历语法树的顺序
  • 不需要与antlr遍历类ParseTreeWalker一起使用,直接遍历tree
  • 动作代码与文法产生式解耦,利于文法产生式的重用
  • visitor方法可以直接返回值,返回值的类型必须一致,不需要使用map这种节点间传值方式,效率高

0xFE 总结

未完待续

0xFF 参考文档

Antlr4 入门

你可能感兴趣的:(antlr)