ANTLR3 简介及示例
ANTLR(pronounced Antler) 是一个语言识别工具,Another Tool forLanguage Recognition 的缩写。ANTLR由旧金山大学(University of San Francisco)的教授 Terence Parr 开发并维护的,其始于1989年,到了现在过了20多年,一直都是一个很活跃的项目。
ANTLR 一般用于构建 Domain-Specific Languages (DSL)。用户编写好特定语言的语法文件后,ANTLR 会根据该语法文件生成相应的源代码来识别该语言。ANTLR 3.4 (截至2011-10-15最新的版本) 支持的编程语言(runtime)包括:ActionScript, Csharp2, Delphi, JavaScript, Perl5, Ruby , C, CSharp3, Java, ObjC,Python。其中 C runtime 由 Jim Idle 维护,C
runtime 也是本文关注的部分。在早期的版本中提供有C++的runtime,但是在最新的版本中只提供 C 语言的 runtime。
很多的开源的软件都使用了ANTLR 作为自己的DSL 解析工具,比如: Hibernate,一个在javaEE 中运用非常广泛的ORM 框架,使用ANTLR解析 HQL –一种类似于SQL 的面向对象的数据库查询语言;Apache Hive,一个建于Hadoop 之上的数据仓库查询语言,使用ANTLR解析HiveQL;TOra,Toolkit for Oracle,一个用 Qt写的Oracle 数据库管理工具,使用ANTLR解析Oracle SQL 和 PL/SQL。还有其他很多的软件也使用了ANTLR,比如Esper,StreamBase,这两个都是用 Java 写的数据流处理引擎。
本文主要基于一个例子简单的讲解如果使用 ANTLR,介绍一些ANTLR的基本概念,本文不包括语言识别的理论知识,和ANTLR的一些高级应用,想了解这些知识应该学习编译理论相关知识,并阅读Terence Parr编写的The Definitive ANTLR Reference 。
ANTLR支持生成词法分析器,语法解析器和树解析器,同时也支持把代码和语法规则混合在一起编写,个人觉得两者混合在一起写对于开发会很方便,可是这样会影响语法的可读性,所以在本文中语法与代码不会混合在一起写。
本文将一步步的从头开始使用C语言构造一个简单的StreamSQL 解析器。该StreamSQL 解析器实现对 CREATE INPUT STREAM,CREATE OUTPUT STREAM,CREATE SCHEMA,CREATE WINDOW,SELECT 等语句的解析。
工具准备
1.首先下载 ANTLR3.4。http://www.antlr.org/download.html 我们要下载的是ANTLR 3.4 distribution (Source for tools, targets, complete binaries)
2. 下载 ANTLRWorks。 ANTLRWorks 是一个可视化的开发工具,为语法的编写和调试提供了很大的方便。
3. 下载 ANTLR v3 plugin for eclipse。这是一个Eclipse的插件,可以在Eclipse 里为ANTLR语法文件提供语法高亮,也可以作为语法的调试工具,不过它的调试功能没有 ANTLRWorks 强大。
语法描述
我们要解析的语言如下表所示。
|
CREATE INPUT STREAM Packet PacketTuple; CREATE OUTPUT STREAM Aggregate AggregateTuple; CREATE SCHEMA PacketTuple (time INT,price DOUBLE ); CREATE SCHEMA AggregateTuple( time INT, maxprice DOUBLE , currenttime INT); CREATE WINDOW Dimension1(SIZE 180 ADVANCE 10 ON time); SELECT max(price) AS maxprice, max(time) AS currenttime FROM Packet[Dimension1] INTO Aggregate; |
用来解析上面语言的ANTLR语法。
|
grammar StreamSQL; options { language=C; ASTLabelType=pANTLR3_BASE_TREE; output=AST; } tokens { TOK_CREATE_SCHEMA; TOK_CREATE_STREAM; TOK_CREATE_WINDOW; TOK_SELECT; TOK_SCHEMA_LIST; TOK_NAME_TYPE; TOK_SELEXPR; TOK_SELITEM; TOK_SELLIST; } statement : selectStatement EOF | createStatement EOF ; selectStatement : KW_SELECT selectList KW_FROM instreamName=Identifier LSQUARE windowName=Identifier RSQUARE KW_INTO outstreamName=Identifier -> ^(KW_SELECT selectList $instreamName $windowName $outstreamName) ; selectList : selectColumn (COMMA selectColumn)* -> ^(TOK_SELLIST selectColumn+) ; selectColumn : selectItem | selectExpression ; selectItem : Identifier KW_AS Identifier -> ^(TOK_SELITEM Identifier Identifier) ; selectExpression : functionName=Identifier LPAREN itemName=Identifier RPAREN KW_AS asName=Identifier -> ^(TOK_SELEXPR $functionName $itemName $asName) ; createStatement : KW_CREATE KW_SCHEMA Identifier schemaList -> ^(TOK_CREATE_SCHEMA Identifier schemaList) | KW_CREATE streamType KW_STREAM streamName=Identifier schemaName=Identifier -> ^(TOK_CREATE_STREAM streamType $streamName $schemaName) | KW_CREATE KW_WINDOW windowName=Identifier LPAREN KW_SIZE Number KW_ADVANCE Number KW_ON onWhat=Identifier RPAREN -> ^(TOK_CREATE_WINDOW $windowName Number Number $onWhat) ; schemaList : LPAREN columnNameType (COMMA columnNameType)* RPAREN -> ^(TOK_SCHEMA_LIST columnNameType+) ; streamType : (KW_INPUT | KW_OUTPUT) ; columnNameType : coluName=Identifier dataType -> ^(TOK_NAME_TYPE $coluName dataType) ; dataType : KW_INT | KW_DOUBLE ; // Keywords KW_FROM : 'FROM'; KW_AS : 'AS'; KW_SELECT : 'SELECT'; KW_ON : 'ON'; KW_CREATE: 'CREATE'; KW_INT: 'INT'; KW_DOUBLE: 'DOUBLE'; KW_INTO: 'INTO'; KW_SCHEMA: 'SCHEMA'; KW_INPUT: 'INPUT'; KW_OUTPUT: 'OUTPUT'; KW_STREAM: 'STREAM'; KW_WINDOW: 'WINDOW'; KW_SIZE: 'SIZE'; KW_ADVANCE: 'ADVANCE'; // Operators // NOTE: if you add a new function/operator, add it to sysFuncNames so that describe function _FUNC_ will work. DOT : '.'; // generated as a part of Number rule COMMA : ',' ; SEMICOLON : ';' ; LPAREN : '(' ; RPAREN : ')' ; LSQUARE : '[' ; RSQUARE : ']' ; MINUS : '-'; PLUS : '+'; // LITERALS fragment Letter : 'a'..'z' | 'A'..'Z' ; fragment Digit : '0'..'9' ; fragment Exponent : 'e' ( PLUS|MINUS )? (Digit)+ ; fragment Number : (Digit)+ ( DOT (Digit)* (Exponent)? | Exponent)? ; Identifier : (Letter | Digit) (Letter | Digit | '_')* ; WS : (' '|'\r'|'\t'|'\n') {$channel=HIDDEN;} ; |
将该语法文件保存,文件名为 StreamSQL.g 文件名必须与语法名相同。
|
java -cp /opt/java_lib/antlr-3.4-complete.jar org.antlr.Tool StreamSQL.g |
运行上面的命令,ANTLR就会生成相应的词法和语法解析代码。
|
ls | egrep Lexer\|Parser\|tokens StreamSQLLexer.c StreamSQLLexer.h StreamSQLParser.c StreamSQLParser.h StreamSQL.tokens |
代码实现
|
pANTLR3_INPUT_STREAM input; pSQLLexer lxr; pANTLR3_COMMON_TOKEN_STREAM tstream; pSQLParser psr; const char * inputString = sqlStatement.c_str(); input = antlr3StringStreamNew((uint8_t *) inputString, ANTLR3_ENC_UTF8, strlen(inputString), (uint8_t *) "test_statement"); lxr = SQLLexerNew(input); tstream = antlr3CommonTokenStreamSourceNew(ANTLR3_SIZE_HINT, TOKENSOURCE(lxr)); psr = SQLParserNew(tstream); SQLParser_statement_return statementAST = psr->statement(psr); /* get the AST root */ pANTLR3_BASE_TREE root = statementAST.tree; pANTLR3_BASE_TREE treeNode; treeNode = (pANTLR3_BASE_TREE) root->getChild(root, 0); ANTLR3_UINT32 treeType = treeNode->getType(treeNode); string result; switch (treeType) { case TOK_CREATE_SCHEMA: result = parseCreateSchema(treeNode); break; case TOK_CREATE_STREAM: result = parseCreateStream(treeNode); break; case TOK_CREATE_WINDOW: result = parseCreateWindow(treeNode); break; case TOK_SELECT: result = parseSelect(treeNode); break; default: WARN << "Unknown tree type... " << treeType; break; } input->close(input); lxr->free(lxr); tstream->free(tstream); psr->free(psr); |
ANTLR 的 runtime 会根据输入生成对应的抽象语法树AST,我们只要从根节点遍历该AST 即可以完成该语句的解析。下图为CREATE SCHMEA 的一个抽象语法树:

未完成的章节
1. 出错提示:当遇到无法解析的语句时,应该可以给出提示在那一行那一个字符解析的时候出错了。
2. ANTLR的高级特性:backtracking, memoization, syntactic predicates, LL(*) parsing
同类的工具
1. flex/bison PostgreSQL 用的是这个。
2. JavaCC
3. Yacc MySQL 用的是这个
4. Lemon 一个小巧的词法语法解析器,SQLite 用的是这个。
参考资料
1. The Definitive ANTLR Reference Building Domain-Specific Languages – Terence Parr
2. http://www.antlr.org/wiki/display/ANTLR3/ANTLR+3+Wiki+Home
- ANTLR3Introduce.pdf (96 KB)
- 下载次数: 65
ANTLR3 简介及示例的更多相关文章
- Android查缺补漏(IPC篇)-- 进程间通讯之Socket简介及示例
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8425736.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...
- IdentityServer4 中文文档 -6- (简介)示例服务器和测试
IdentityServer4 中文文档 -6- (简介)示例服务器和测试 原文:http://docs.identityserver.io/en/release/intro/test.html 目 ...
- Ruby简介,附带示例程序
Ruby语言是日本人松本行弘于1993年器开始着手研发,经历2年时间,发布了Ruby语言的第一个版本:0.95版. Ruby是一种非常简介的解释性语言,一种纯粹的面向对象编程语言,甚至比Jav ...
- IText简介及示例
一.iText简介 iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库.通过iText不仅可以生成PDF或rtf的文档,而且可以将XML.Html文 ...
- Unity 3(一):简介与示例
本文关注以下方面(环境为VS2012..Net Framework 4.5以及Unity 3): Ioc/DI简介: Unity简单示例 一.Ioc/DI简介 IoC 即 Inversion of C ...
- JavaScript简介及示例
JavaScript简介及使用 一.简介 JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛 ...
- Lambda表达式 简介 语法 示例
Lambda 表达式也称为闭包,是匿名类的简短形式.Lambda 表达式简化了[单一抽象方法声明接口]的使用,因此 lambda 表达式也称为功能接口. 在 Java SE 7 中,单一方法接口可使用 ...
- Spring IO Platform简介及示例
什么是Spring IO Platform Spring IO Platform,简单的可以认为是一个依赖维护平台,该平台将相关依赖汇聚到一起,针对每个依赖,都提供了一个版本号: 这些版本对应的依赖都 ...
- Lambda表达式 简介 语法 示例 匿名内部类
在AS中使用 Lambda 表达式 Demo地址:https://github.com/baiqiantao/MultiTypeTest.git Gradle(Project级别)中添加classpa ...
- 003-unity3d 物理引擎简介以及示例
一.概述 物理引擎就是模拟真实世界中物体碰撞.跌落等反应的引擎,通过ballence.愤怒的小鸟等理解.Unity3D的物理引擎使用的是Nvidia的PhysX. 物理引擎是一个计算机程序模拟牛顿力学 ...
随机推荐
- dotnet 读 WPF 源代码笔记 从 WM_POINTER 消息到 Touch 事件
本文记录我读 WPF 源代码的笔记,在 WPF 底层是如何从 Win32 的消息循环获取到的 WM_POINTER 消息处理转换作为 Touch 事件的参数 由于 WPF 触摸部分会兼顾开启 Poin ...
- Mongodb入门2
$push 修饰符:用于往数组中追加元素 db.workmates.update({name:'xiaowang'},{$push:{interseta:"PHP"}}) ...
- FALCON:打破界限,粗粒度标签的无监督细粒度类别推断,已开源| ICML'24
在许多实际应用中,相对于反映类别之间微妙差异的细粒度标签,我们更容易获取粗粒度标签.然而,现有方法无法利用粗标签以无监督的方式推断细粒度标签.为了填补这个空白,论文提出了FALCON,一种从粗粒度标记 ...
- Angular 18+ 高级教程 – Signals
前言 首先,我必须先说明清楚.Signal 目前不是 Angular 的必备知识. 你的项目不使用 Signal 也不会少了条腿,断了胳膊. Angular 官方维护的 UI 组件库 Angular ...
- Go runtime 调度器精讲(十):异步抢占
原创文章,欢迎转载,转载请注明出处,谢谢. 0. 前言 前面介绍了运行时间过长和系统调用引起的抢占,它们都属于协作式抢占.本讲会介绍基于信号的真抢占式调度. 在介绍真抢占式调度之前看下 Go 的两种抢 ...
- 主流流媒体的综合性能大 PK ( smart_rtmpd, srs, zlm, nginx rtmp )
简述 随着互联网的发展,音视频行业越来越火,自然而然的流媒体服务器也是百花齐放.市面上也有很多种类的流媒体服务器,让人眼花缭乱.特别是对技术了解不深的朋友,更不知道怎么选择. 其实作为服务器,主要考察 ...
- ChatGPT转发工具-springboot
背景 国内服务器无法访问openAI接口,我想过有两种实现方式 代理工具类似 tinyproxy.nginx 开发一个转发客户端(java.python都可以实现),提供一个api接口 源码 gith ...
- HBase集群安装及集成
Hbase集群搭建 注意:在安装hbase或者hadoop的时候,要注意hadoop和hbase的对应关系.如果版本不对应可能造成系统的不稳定和一些其他的问题.在hbase的lib目录下可以看到had ...
- Element PRO 破解版 3.18.1版插件
百度网盘提取 链接:https://pan.baidu.com/s/1KKOm7O1ghhIoYNiQPdzG_Q 提取码:1234
- torch.max()使用讲解
output = torch.max(input, dim) input输入的是一个tensor dim是max函数索引的维度0/1,0是每列的最大值,1是每行的最大值 实例: import torc ...