邓羽平
骑士
骑士
  • UID280
  • 粉丝1
  • 关注2
  • 发帖数5
阅读:6616回复:0

Antlr v4 介绍

楼主#
更多 发布于:2019-04-23 20:39
环境准备
1.IntelliJ IDEA 2018.3.2 x64   安装Antlr插件(ANTLR v4 grammar plugin)
2.Antlrjar包(我是通过maven引入)
<dependency>
    <groupId>
org.antlr</groupId>
    <artifactId>
antlr4-runtime</artifactId>
    <version>
4.7.2</version>
</dependency>

一 .Antlr是什么
Antlr是一款用java语言开发的语法分析生成工具。
二.Antlr有什么作用
Antlr可用来读取、处理、执行和翻译结构化的文本或二进制文件。
Antlr被广泛运用于学术领域及工业生产实践,如:
Twitter搜索使用ANTLR进行语法分析,每天处理超过20亿次查询;
Hadoop生态系统中的Hive Pig、数据仓库和分析系统所使用的语言都用到了ANTLR;
Oracle公司在SQL开发者IDE和迁移工具中使用了ANTLR;
NetBeans公司的IDE使用ANTLR来解析C++;
Hibernate对象-关系映射框架(ORM) 使用ANTLR来处理HQL语言。
三.Antlr语法
1.示例:
在环境准备中创建的项目下新建一个文件,命名为Hello.g4
编写文件

grammar Hello;
r : 'hello' ID ; // 匹配'hello'
ID : [a-z]+ ; // 匹配小写字母
WS : [ \t\r\n]+ -> skip ; // 跳过空格、制表符和换行
之后配置Antlr代码生成选项,在g4文件上右键,选择“Configure ANTLR...”选项,弹出配置窗口

图片:antlr04.png


配置好后右键g4文件,选择“Generate ANTLR Recognizer”选项
此时可以看到,Antlr自动在我们配置的目录下生成了一系列的文件:

图片:abtlr06.png


可观察,所有文件都是以g4文件的名称开头。
打开g4文件,在“r”语法规则上方点击鼠标右键,选择Test Rule r
此时切换到下方的ANTLR Preview窗口

图片:antlr08.png


此时,我们如下操作:
选择input,并在下方输入hello world
然后在g4文件中的相应语法(r)上再次点击右键选择Test Rule r,此时观察右方窗口

图片:antlr10.png


可以看到,系统将我们输入的文字根据我们编写的语法检测规则自动生成了语法树。
2.总述
         规则以 : 开始, ; 结束, 多规则以 "|" 分隔。
3.grammer
声明语法头,应该和该语法文件名称相同,即示例中的语法文件应名为Hello.g4
4.语法分析器规则
         r : 'hello' ID ;
         识别语言的程序称为语法分析器(parser) 或者句法分析器(syntax analyzer) 。句法(syntax) 是指约束语言中的各个组成部分之间关系的规则
         其中r必须为小写字母为开头,后面可以跟字母、数字、下划线
5.词法分析器规则
         ID : [a-z]+ ;
         其中ID为大写开头
6.注释
         java中类似,/**   */只能用在开头,然后是/*  */  //  用法和java一致
7.->skip
         该标志代表跳过此词法,不解析它
四.Antlr遍历模式
         遍历模式分为监听器模式与访问器模式。
         监听器模式能够对特定规则的进入和退出事件(即识别到某些词组的事件)作出晌应,这些事件分别由语法分析树遍历器在开始和完成对节点的访间时触发。并且监听器方法不负责显式调用子节点的访间方法。
         优点:a.实现简单,不用主动设置节点访问顺序
                   b.语法规则与处理代码解耦,方便重用
         缺点:a.不能设置节点访问顺序
                   b.处理方法没有返回值,若存在值传递问题的话可能需要其他数据结构
         实现方式:
                   首先,我们需要编写一个类,继承BaseListener类。
                   关于BaseListener类,查看其方法结构:
               

图片:antlr12.png

        可以发现,他会将我们g4文件中规定的语法分析规则r生成了enterRexitR方法。
        我们编写的监听类:


public class MyListener extends HelloBaseListener {
    Map<String, String> props = new HashMap<String, String>() ;
    @Override
    public void exitR(HelloParser.RContext ctx) {
        String r = ctx.getText(); //
        props.put ("r", r) ;
    }
}
然后我们编写一个测试方法:


String filePath = "E:\\t\\hello.txt";
try{
//处理文件
File file = new File(filePath);
CharStream input = CharStreams.fromPath(file.toPath());
HelloLexer lexer = new HelloLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
HelloParser parser = new HelloParser(tokens);
ParseTree parseTree = parser.r();
// 新建一个标准的ANTLR 语法分析树遍历器
ParseTreeWalker walker = new ParseTreeWalker();
//新建一个监盺器,将其传递给凅历器
MyListener myListener= new MyListener() ;
walker.walk(myListener,parseTree); // 遍历语法分析树
System.out.println(myListener.props); //打印结果
}catch (Exception e){
e.printStackTrace();
}
运行这个类,结果为:
{r=helloworld}

         访问器模式必须显式触发对子节点的访间以便树的遍历过程能够正常进行。因为访间器机制需要显式调用方法来访间子节点,所以它能够控制遍历过程中的访间顺序,以及节点被访间的次数。
         优点:a.能够设置节点访问顺序以及访问次数
                            b.语法规则与处理代码解耦,方便重用
                            c.处理方法能直接返回值
         实现方式:
         首先,Antlr在开发工具中生成java类时默认是不会生成visitor相关文件的,我们需要在配置Antlrjava类生成时选中下方的选项:

图片:antlr13.png



         然后再次右键g4文件,选择“Generate ANTLR Recognizer”选项,再次生成一遍

图片:antlr14.png


可看到生成了visitor相关文件。
同样先观察HelloBaseVisitor类的方法

图片:antlr15.png


同样生成了和语法规则名称相同的方法
我们写一个类,继承之


public class MyVisitor extends HelloBaseVisitor {
    Map<String, String> props = new HashMap<String, String>() ;
    @Override
    public Object visitR(HelloParser.RContext ctx) {
        String r = ctx.getText(); //
        props.put ("r", r) ;
        return super.visitR(ctx);
    }
}
然后写一个测试方法

try{
        String filename = "E:\\t\\hello.txt";
        File file = new File(filename);
        CharStream input = CharStreams.fromPath(file.toPath());
        HelloLexer lexer = new HelloLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        HelloParser parser = new HelloParser(tokens);
        ParseTree parseTree = parser.r();
        MyVisitor myVisitor = new MyVisitor();
        myVisitor.visit(parseTree);
        System.out.println(myVisitor.props);
}catch (Exception e){
        e.printStackTrace();
}
同样会输出
{r=helloworld}
五.思考
根据以上所罗列的Antlr的特点以及其运用场景,我们可以想到其实Antlr可以做很多很多事。比如说,我们有一个从数据库导出的建表语句。若想获得其中的元数据信息,一般方法为通过正则表达式的方式处理脚本文件。而对于Antlr来说,同样可以做这件事,并且用Antlr编写语法规则文件比用正则表达式要简单一些,门槛相对来说比较低。也就是说,Antlr可以通过一种较简单的指定语法规则的方式去解析具有某种规则的脚本,可扩展性也比较好。
六.相关链接
ANTLR 官方网址 http://www.antlr.org/
ANTLR 官方 Github https://github.com/antlr/antlr4
大量语法文件例子 https://github.com/antlr/grammars-v4

最新喜欢:

何万里何万里
游客

返回顶部