发表时间:2011-10-15
最后修改:2011-10-15
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
等语句的解析。
工具准备
-
首先下载
ANTLR3.4。http://www.antlr.org/download.html
我们要下载的是ANTLR
3.4 distribution (Source for tools, targets, complete binaries)
-
下载
ANTLRWorks。
ANTLRWorks
是一个可视化的开发工具,为语法的编写和调试提供了很大的方便。
-
下载
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';
|