Antlr v4入门教程和实例

1 重逢ANTLR

最早知道ANTLR是当年学习Apache Derby数据库源码时,在看到SQL解析那一层时,第一次看到编译原理在实际项目中的应用,惊叹之余也只能望而却步。之前也根据网上一些资料尝试了一下,看介绍说ANTLR v4更加易用了,于是又好奇地试用一下。以下入门介绍主要参考ANTLR作者写的《The Definitive ANTLR 4 Reference》

1.1 ANTLR全景

当我们实现一种语言时,我们需要构建读取句子(sentence)的应用,并对输入中的元素做出反应。如果应用计算或执行句子,我们就叫它解释器(interpreter),包括计算器、配置文件读取器、Python解释器都属于解释器。如果我们将句子转换成另一种语言,我们就叫它翻译器(translator),像Java到C#的翻译器和编译器都属于翻译器。不管是解释器还是翻译器,应用首先都要识别出所有有效的句子、词组、字词组等,识别语言的程序就叫解析器(parser)语法分析器(syntax analyzer)。我们学习的重点就是如何实现自己的解析器,去解析我们的目标语言,像DSL语言、配置文件、自定义SQL等等。

1.2 元编程

手动编写解析器是非常繁琐的,所以我们有了ANTLR。只需编写ANTLR的语法文件,描述我们要解析的语言的语法,之后ANTLR就会自动生成能解析这种语言的解析器。也就是说,ANTLR是一种能写出程序的程序。在学习LISP或Ruby的宏时,我们经常能接触到元编程的概念。而用来声明我们语言的ANTLR语言的语法,就是元语言(meta-language)

1.3 解析过程

为了简单起见,我们将解析分为两个阶段,对应我们的大脑读取文字时的过程。当我们读到一个句子时,在第一阶段,大脑会下意识地将字符组成单词,然后像查词典一样识别出它们的意思。在第二阶段,大脑会根据已识别的单词去识别句子的结构。第一阶段的过程叫词法分析(lexical analysis),对应的分析程序叫做lexer,负责将符号(token)分组成符号类(token class or token type)。而第二阶段就是真正的parser,默认ANTLR会构建出一棵分析树(parse tree)或叫语法树(syntax tree)。如下图,就是简单的赋值表达式的解析过程:

语法树的叶子是输入token,而上级结点时包含其孩子结点的词组名(phase),线性的句子其实是语法树的序列化。最终生成语法树的好处是:

1) 树形结构易于遍历和处理,并且易被程序员理解,方便了应用代码做进一步处理。

2) 多种解释或翻译的应用代码都可以重用一个解析器。但ANTLR也支持像传统解析器生成器那样,将应用处理代码嵌入到语法中。

3) 对于因为计算依赖而需要多趟处理的翻译器来说,语法树非常有用!我们不用多次调用解析器去解析,只需高效地遍历语法树多次。

1.4 深入ANTLR

ANTLR生成的解析器叫做递归下降解析器(recursive-descent parser),属于自顶向下解析器(top-down parser)的一种。顾名思义,递归下降指的就是解析过程是从语法树的根开始向叶子(token)递归,比较酷的是代码的调用图能与树结点对应上。还是以前面的赋值表达式解析为例,其递归下降解析器的代码大概是下面这个样子:

Assign很简单,直接顺序读取输入字符,不用做任何选择。相比之下,根结点Stat要复杂一些,因为它有多种选择。解析时,要向前看(lookahead)一些字符才能确认走哪个分支代码,有时甚至要读取完所有输入才能预测出,而ANTLR默默为我们处理了一切!

1.5 解析树上的应用

在内部,ANTLR的数据结构会尽可能地共享数据来节约内存,这种考量在Nginx的String中也能看到。如下图所示,解析树的叶子节点指向Token流中的Token,而Token中的起止字符索引指向字符流,而非拷贝子字符串。而像空格这种不与任何Token相关的字符会直接被Lexer丢弃掉。

ANTLR为每个Rule都会生成一个Context对象,它会记录识别时的所有信息。ANTLR提供了Listener和Visitor两种遍历机制。Listener是全自动化的,ANTLR会主导深度优先遍历过程,我们只需处理各种事件就可以了。而Visitor则提供了可控的遍历方式,我们可以自行决定是否显示地调用子结点的visit方法。

1.6 ANTLR v4新特性

目前还未深入使用,对v4的新特性了解的不多,摘录一段“antlr v4新特性总结及与antlr v3的不同”

1) 学习曲线低。antlr v4相对于v3,v4更注重于用更接近于自然语言的方式去解析语言。比如运算符优先级,排在最前面的规则优先级最高;

2) 层次更清晰、更易维护。引入访问者、监听器模式,使解析与应用代码分离;新増import功能,lexer、parser可以成为公共组件,増加可复用性;

3) 新算法。改进LL()算法,使用新的Adative LL()算法,在运行时动态分析语法,而LL(*)需要静态分析语法,考虑各种语法的可能性。

4) 新用法。引入了一些新用法,如rewrite the input stream、sending token in different channels、island grammars、associativity,可以更方便、灵活地在应用中处理解析对象。

5) 性能。相对于v3,解析代码跟应用代码都是自动生成的,而v4分离了解析与应用代码的实现,应用代码的实现及性能则可以由开发人员自主地控制,但新算法据官方指引说会消耗一定的速度上的性能,因此提供了SLL()、LL()的开关,可通过api控制。

2 准备工作

2.1 安装IDE插件

我这里使用的是Intellij IDEA,所以就去Plugins中搜“ANTLR v4 grammar plugin”插件,重启IDEA即可使用。如果想在IDE外使用,需要下载ANTLR包,是JAVA写成的,后面在IDEA中的各种操作都可以手动执行命令来完成。

2.2 实用的小例子

没有搜到很好的例子,偶然看到《The Definitive ANTLR 4 Reference》中第三章的入门实例不错,就参照着动手实现一下。简单介绍一下这个实用的小程序是要做什么?我们将short[] data = {1, 2, 3} 翻译成 string data = "\u0001\u0002\u0003"

3 动手实现解析器

3.1 编写.g4文件

创建一个文件,后缀名是g4,只有这样在文件上点右键才能看到ANTLR插件的菜单。

3.2 自动生成代码

在.g4文件上右键就能看到ANTLR插件的两个菜单,分别用来配置ANTLR生成工具的参数(在命令行中都有对应)和触发生成文件。首先选配置菜单,将目录选择到main/java或test/java。注意:ANTLR会自动根据Package/namespace的配置,生成出包的文件夹,不用预先创建出来。

之后就点生成菜单,于是就在我们配置的目录下,自动生成出的如下代码:

4 构建应用代码

有了生成好的解析器,我们就可以在它上面构建出好玩的应用了。

4.1 ANLTR运行时

在开始编写应用代码之前,我们要引入ANTLR运行时。因为我们的解析器其实只是一堆回调hook,真正的通用解析流程实现是在ANTLR runtime包中。所以,以Maven为例ANTLR v4的依赖是:

<dependency>
    <groupId>org.antlr</groupId>
    <artifactId>antlr4-runtime</artifactId>
    <version>4.5</version>
</dependency>

4.2 应用代码

运用前面学习过的知识,我们实现一个Listener完成翻译工作。然后在main()中构建起词法分析器和解析器,以及连接它们的数据流和语法树。

时间: 2024-08-01 22:38:56

Antlr v4入门教程和实例的相关文章

JsRender实用入门教程

这篇文章主要介绍了JsRender实用入门实例,包含了tag else使用.循环嵌套访问父级数据等知识点,并提供了完整的实例下载,非常具有实用价值,需要的朋友可以参考下 本文是一篇JsRender的实用入门教程,实例讲述了tag else使用.循环嵌套访问父级数据等知识点.分享给大家供大家参考.具体如下: 前言 JsRender是一款基于jQuery的JavaScript模版引擎,它具有如下特点: ·  简单直观 ·  功能强大 ·  可扩展的 ·  快如闪电 这些特性看起来很厉害,但几乎每个模

WebSocket入门教程(五)-- WebSocket实例:简单多人聊天室

from:https://blog.csdn.net/u010136741/article/details/51612594 [总目录] WebSocket入门教程--大纲 [实例简介] 本文,我们通过nodejs和javascript实现一个网页聊天室的demo.主要包括,聊天,改用户名,查看其他用户在线状态的功能.大致流程为,用户访问网页,即进入聊天状态,成为新游客,通过底部的输入框,可以输入自己想说的话,点击发布,信息呈现给所有在聊天的人的页面.用户可以实时修改自己的昵称,用户离线上线都会

Spring MVC注解配置结合Hibernate的入门教程及其代码实例

原文:Spring MVC注解配置结合Hibernate的入门教程及其代码实例 源代码下载地址:http://www.zuidaima.com/share/1787210045197312.htm 1.概述 本文旨在搭建Spring MVC+Hibernate开发框架,通过一个简单的demo讲解Spring MVC的相关配置文件,以及通过注解方式实现简单功能. 开发框架:Spring+Spring MVC+Hibernate(Spring所用的版本为3.0.5). 数据库:MySQL(数据库名称

Android基础入门教程——5.2.1 Fragment实例精讲——底部导航栏的实现(方法1)

Android基础入门教程--5.2.1 Fragment实例精讲--底部导航栏的实现(方法1) 标签(空格分隔): Android基础入门教程 本节引言: 在上一节中我们对Fragment进行了一个初步的了解,学习了概念,生命周期,Fragment管理与 Fragment事务,以及动态与静态加载Fragment.从本节开始我们会讲解一些Fragment在实际开发 中的一些实例!而本节给大家讲解的是底部导航栏的实现!而基本的底部导航栏方法有很多种, 比如全用TextView做,或者用RadioB

C#基础与实例教程最简单的C#快速入门教程【转自:http://www.82011433.com/Html/?6982.html】

c#基础与实例教程最简单的C#快速入门教程 在一小时内学会C#.使用例程,简单却完整的探索C#语言的构造和特点.本文特别适合有C++基础却没有太多精力学习C#的读者. 简介 C#是一种具有C++特性,Java样式及BASIC快速建模特性的编程语言.如果你已经知晓C++语言,本文将在不到一小时的时间内带你快速浏览C#的语法.如果熟悉Java语言,Java的编程结构.打包和垃圾回收的概念肯定对你快速学习C#大有帮助.所以我在讨论C#语言构造的时候会假设你知道C++. c#基础与实例教程最简单的C#快

Android基础入门教程——5.2.2 Fragment实例精讲——底部导航栏的实现(方法2)

Android基础入门教程--5.2.2 Fragment实例精讲--底部导航栏的实现(方法2) 标签(空格分隔): Android基础入门教程 本节引言: 上一节中我们使用LinearLayout + TextView实现了底部导航栏的效果,每次点击我们都要重置 所有TextView的状态,然后选中点击的TextView,有点麻烦是吧,接下来我们用另一种方法: RadioGroup + RadioButton来实现我们上一节的效果! 1.一些碎碎念 本节用到的是实现单选效果的RadioButt

Android基础入门教程——5.2.3 Fragment实例精讲——底部导航栏的实现(方法3)

Android基础入门教程--5.2.3 Fragment实例精讲--底部导航栏的实现(方法3) 标签(空格分隔): Android基础入门教程 本节引言 前面我们已经跟大家讲解了实现底部导航栏的两种方案,但是这两种方案只适合普通的情况,如果 是像新浪微博那样的,想在底部导航栏上的item带有一个红色的小点,然后加上一个消息数目这样, 前面两种方案就显得无力了,我们来看看别人的APP是怎么做的,打开手机的开发者选项,勾选里面的: 显示布局边界,然后打开我们参考的那个App,可以看到底部导航栏是这

Scrapy安装、爬虫入门教程、爬虫实例(豆瓣电影爬虫)

Scrapy在window上的安装教程见下面的链接:Scrapy安装教程 上述安装教程已实践,可行.本来打算在ubuntu上安装Scrapy的,但是Ubuntu 磁盘空间太少了,还没扩展磁盘空间,暂时不想再上面装太多软件. Scrapy的入门教程见下面链接:Scrapy入门教程 上面的入门教程是很基础的,先跟着作者走一遍,要动起来哟,不要只是阅读上面的那篇入门教程,下面我简单总结一下Scrapy爬虫过程: 1.在Item中定义自己要抓取的数据: movie_name就像是字典中的“键”,爬到的数

【转载】GBDT(MART) 迭代决策树入门教程 | 简介

      转载地址:http://blog.csdn.net/w28971023/article/details/8240756        GBDT(Gradient Boosting Decision Tree) 又叫 MART(Multiple Additive Regression Tree),是一种迭代的决策树算法,该算法由多棵决策树组成,所有树的结论累加起来做最终答案.它在被提出之初就和SVM一起被认为是泛化能力(generalization)较强的算法.近些年更因为被用于搜索排