ANTLR 权威参考 第一章 开始antlr

第一部分 介绍ANTLR和计算机语言翻译

第一章    开始antlr

 

本书是antlr的参考指南:一个复杂的解析程序生成器,你可以用这个解析程序实现语言的解释器,编译器或其他翻译器。这不是一本编译器的书,也不是语言理论的教科书。虽然你能找到很多关于编译器和编译原理基础的书,但很多语言类应用并不是编译器。这本书更适合构建普通,日常使用的语言类应用。本书附带大量例子,解释和参考资料,立足于一个语言工具和方法。

           

程序员常使用antlr来构建领域语言(简称DSLs)的翻译器和解析器.DSls通常是面向特定任务的高级语言。他们设计的目的尤其是为了在某个领域用户使用的效率。DSLs有很大的应用范围,很大一部分你可能不认为他们是语言。DSLs包括数据格式,配置文件格式,网络协议,文本处理类语言,蛋白模型,基于序列,太空控制器语言和某些特定领域内的编程语言。

           

DSLs尤其对软件开发很重要,因为你只是简单的使用这个语言编写软件,就可以以一种很自然的,高保真的,健壮的和可维护的方式将一个问题封装起来。例如,美国航天局就为太空任务使用一种航天特定的命令语言,来提高可重用性,降低风险和成本,提高开发速度。甚至上世纪六十年代首次阿波罗项目中的导引控制器就使用DSLs来进行向量计算(http://www.ibiblio.org/apollo/assembly_language_manual.html)。

           

本章介绍antlr的主要部件以及他们如何组织在一起。你会看到一个DSL 翻译问题如何被分解为多个更小的问题。这些更小问题就是一些固有的翻译阶段(词法,解析,树解析这些阶段),这些阶段使用固有的数据类型和结构(文本字符,标记符,树,如符号表等的辅助结构)来通讯的。阅读完本章节之后,你会大大的熟悉antlr的这些部件,可以应对本章之后的细节讨论。让我们从蓝图开始吧。

 

1.1      蓝图

           

一个翻译器将一种语言的每个句子映射为输出句子。为了映射,翻译器会对输入符号,执行你提供的代码。翻译器必须对不同的句子执行不同的动作,这意味着必须能够识别出不同的句子。

           

如果将识别器分解为两个类似但不同的任务或阶段,识别器就变得容易多了。这两个阶段模拟我们如何读英文。你不会一个字母一个字母的读句子,而是认为句子有一串单词组成。人脑潜意识的将字母组合成单词,在字典中找到单词,然后进行语法结构识别。第一个阶段叫词法分析,作用在输入的字母上。第二个阶段为解析,作用在词汇上,叫标记(从第一个阶段产生的)。antlr通过你提供的语法自动生成词法分析器和解析器。

           

执行翻译通常就是在语法中嵌入动作(代码)。antlr按照在语法中的 位置来执行这些动作。按照这种方式,你可以对不同的短语(一个句子中的片段)执行不同的代码。比如表达式规则中的动作,只有在解析器将句子识别为表达式时候才执行。

           

一些翻译要分解为更多的阶段。经常翻译过程需要进行多遍,在一些情况下,在多个阶段里,大大的容易编码。相比每个阶段去解析输入的文本,构造一种在不同阶段之间传递的中间形式,这样会更好。

           



    

1.1:翻译器数据流程:边代表数据结构,正方形代表各个阶段。(译者:图中有characters, tokens, AST 3种数据结构,lexer, parser, tree walker 3个阶段)

 

 

这个中间形式通常是课树结构,叫抽象语法树(AST),是高度处理的、对输入的压缩版本。每个阶段都搜集更多信息或执行更多的计算。在最后阶段,生成器阶段,使用前面所有阶段的数据结构和计算结果,生成输出。

 

1.1示例了一个翻译器接受字符输入,生成输出的基本数据流程。词法分析器或者叫词法器,将输入的字符分解为标记。解析器接受这些标记,并且试图识别句子结构。最简单的翻译器执行输出结果这个动作,忽略其他后续阶段(过程)。

           

另一种简单的翻译器仅仅构造某种内部数据结构———不输出任何内容。读取配置文件就是这种翻译器的最后例子。更复杂的翻译器仅使用解析器去构造AST。多个树遍历器(深度优先)再遍历AST,为后续阶段计算出对应的其他数据结构和信息。虽然在这个图中看不出来,最后的生成器能根据模版产生结构化的文本内容。

 

模版就是一个带有参数的文本文档,生成器可以给这些参数赋值。这些参数也可以是对传入的数据进行计算的表达式。Antlr集成了StringTemplate引擎,这样可以更容易的构造生成器(参见第九章 使用模版和语法生成结构化文本)。

 

StringTemplate是从内部数据结构生成结构化文本的面向领域的语言,有点输出语法的意思。特征包括模版组继承,模版多态,迟计算,递归,输出自动缩进。Stringtemplate是在解决复杂系统中遇到的实际问题中成熟起来的。Antlr很大程度上使用stringtemplate来将语法翻译为可执行的识别器代码。每个antlr目标语言(如java代码,c代码)都是一组纯粹的模版,被antlr内部的重定向代码生成器使用。

 

好,让我们仔细看看在上页中图1.1中在各个阶段传递的数据对象。在下页的图1.2中,说明了字符,标记,AST之间的关系。词法器接受ANTLRStringStreamANTLRFileStreamCharStream提供的字符。这些预先定义的Stream会将整个输入装进内存,然后将这些字符缓存起来。没有为标记建立单独的字符串对象,但标记可以被追踪在字符缓存中的索引位置。

 

类似的,没有将数据从标记中复制到树节点中,ANTLR AST节点简单指向创建他们的标记。例如,CommonTree,是预先定义的含有标记指针的节点。ANTLR AST 节点的类型被认为是 Object(对象),这样在你树节点上的数据类型就没有限制。()下页中图1.2描述的数据类型之间的关系是很高效和灵活的。

 

在图中有检查框的标记位于解析器看不到的隐藏通道。解析器可以调到一个通道上,这样忽略其他通道上的任何标记。在词法器上搞点小动作,你可以将不同的标记在不同通道上发给解析器。例如,在解析java时候,你可能会想将空格和普通注释在一个通道上,javadoc注释放在另一个通道上。标记缓存区保留相关标记而不考虑他们的通道号。标记通道号机制对忽略但不扔掉空格和注释(因为一些翻译器需要保留格式和注释)这个问题是个极好的解决办法。

 

 

   

1.2 字符串,标记,抽象语法树之间的关系;CharStream,Token,CommannTreeantlr运行时期数据类型

 

 

牢记下节的模拟或许对你看本书后面的例子和讨论有帮助。

1.2      一个迷宫的比喻

           

本书主要聚焦2个主题:发现在输入语句后的隐藏树结构和生成结构化文本。第一眼,你可能对本书中的一些语言术语和技术感到陌生。别急,我会定义和解释所有,但你读的时候记住下面的比喻是有用的。

 

想象下一个迷宫,只有一个入口和出口,在地板上写着单词。每条按顺序读出从入口到出口的单词生成一个句子。感觉上,迷宫就好像是定义一种语言的语法。

 

你也可以把迷宫想象成句子识别器。给定一个句子,你可以按顺序将句子中的单词和地板上的单词匹配起来。任何能将你引导到出口的句子都是正确的句子(或短语),按照这个迷宫定义的语言。

   

语言识别器必须发现句子隐含的部分树结构,每次一个单词。几乎对每个单词,识别器必须做出一个短语(或子短语)的解释决定。有时这些决定会很复杂。比如,有时一个决定需要参考前面的决策或甚至后面的决策。但大部分时间,只需要前瞻一点即可。前瞻信息类似于第一个单词或你在迷宫岔路口沿路看到的单词。在岔路口,句子中的下个单词会告诉你要走哪条路,因为沿每条路的单词是不同的。第二章,计算机语言特性,使用这个类比,更详细的描述了计算机语言特性。你也可以先读那一章,或直接跳到第三章,antlr快速开始。

 

    在下面2节中,你会看到怎样将图1.1中的蓝图变为java代码,如何执行antlr

 

1.3      安装ANTLR

 

Antlr使用java编写,所以就算你打算使用antlr生成Python,也要在机器上安装javaAntlr需要的java版本为1.4或以上。你先要下载(http://www.antlr .org/download.html),并解压到适当目录。你不必运行配置脚本或修改配置文件就可以正确安装antlr。如果你安装antlr到目录 /usr/local/antlr-3.0,如下:

   

$ cd /usr/local

$ tar xvfz antlr-3.0.tar.gz

antlr-3.0/

antlr-3.0/build/

antlr-3.0/build.properties

antlr-3.0/build.xml

antlr-3.0/lib/

antlr-3.0/lib/antlr-3.0.jar

...

$

 

作为3.0antlr v3 还使用了以前antlr 版本,也就是2.7.7,还有 StringTemplate 3.0.这就是说你为了运行antlr工具必须要有这2个包。但运行生成的解析器,你不需要 antlr 2.7.2,也不需要stringtemplate 3.0(除非你使用了构造模版规则)。Java会从CLASSPATH环境变量来寻找含有.class文件的包文件和目录。你必须修改CLASSPATH环境变量来包含antlr-2.7.2.jar, stringtemple-3.0.jar antlr-3.0.jar

 

安装唯一可能出错的地方就是设置CLASSPATH不正确或者在CLASSPATH里有另一个版本的antlr。注意其他java库(比如BEAWebLogic)可能在你不知道的情况下使用antlr

 

Mac OS X和其他类 unix等使用bashshell中,如下:

 

$ export CLASSPATH="$CLASSPATH:/usr/local/antlr-3.0/lib/antlr-3.0.jar:\

/usr/local/antlr-3.0/lib/stringtemplate-3.0.jar:\

/usr/local/antlr-3.0/lib/antlr-2.7.7.jar"

$

 

别忘了export。没这个,你随后启动的子进程如java则看不到这个环境变量。

 

windows xp上设置CLASSPATH,你要通过 系统属性|高级 来设置。点击 系统变量,再点击在变量列表上面的新建。同样注意windows上的路径分隔符是分号(;)而不是冒号(:)。

 

现在,你的antlr准备好运行了。下节给了一个简单的语法例子,你可用来检查antlr是否正确安装了。

 

1.4 执行      ANTLR,调用识别器

一旦安装好antlr,你就可以将语法翻译为可执行的java代码了。这是个简单语法:

文件名T.g:

grammar T;

/** Match things like "call foo;" */

r : 'call' ID ';' {System.out.println("invoke "+$ID.text);} ;

ID: 'a'..'z'+ ;

WS: (' '|'\n'|'\r')+ {$channel=HIDDEN;} ; // ignore whitespace

 

 

org.antlr.Tool类中有主程序,在语法T.g上执行antlr

$ java org.antlr.Tool T.g

ANTLR Parser Generator Version 3.0 1989-2007

$ ls

T.g         TLexer.java     T__.g

T.tokens    TParser.java

$

 

你看到了,antlr除生成了词法器——TLexer.java, 解析器——TParser.java,还有几个支持文件。

 

为了测试语法,你要有个主程序,来调用语法中的开始规则r。下面这个程序Test.java,就体现了图1.1中的数据流程中一部分:

 

Test.java

 

import org.antlr.runtime.*;

public class Test {

public static void main(String[] args) throws Exception {

// create a CharStream that reads from standard input

ANTLRInputStream input = new ANTLRInputStream(System.in);

// create a lexer that feeds off of input CharStream

TLexer lexer = new TLexer(input);

// create a buffer of tokens pulled from the lexer

CommonTokenStream tokens = new CommonTokenStream(lexer);

// create a parser that feeds off the tokens buffer

TParser parser = new TParser(tokens);

// begin parsing at rule r

parser.r();

}

}

 

 

编译和运行测试:

$ javac TLexer.java TParser.java Test.java

$ java Test

call foo;

EOF

invoke foo

$

输入 “call foo;“紧跟回车, 翻译器输出”invoke foo“ 紧跟回车. 注意你要输入 文件结束符(EOF)(译者:在dos下是Ctrl+Z, unix下是Ctrl+D)来终止键盘输入,否则将一直等待你的输入. 这个例子没有含有一些辅助数据结构或中间形式的树结构, 而在语法内嵌入的动作直接输出 “invoke foo.

 

开发语法之前,你应该熟悉下节要讲的antlrWorks。当你构造或调试语法时,这个antlr GUI(图形界面) 会使你生活轻松得多。

 

1.5      语法开发环境—ANTLRWorks



  

 

1.3ANTLRWorks 语法开发环境|语法编辑器视图

 

 

ANTLRWorks Jean Bovet开发的工作在antlr上,帮助你修改、浏览、调试语法的图形工具(see http://www.antlr.org/works)。可能最重要的是,它帮你解决语法分析中的错误,而手工找出很难。AntlrWorks有以下主要特征:

  •   感知语法的编辑器
  •   可以快速原型的翻译器
  •   语言诊断调试器,可以隔离语法错误
  •   对语法视图,有不确定路径提示
  •   前瞻策略(DFA)可视化
  •   对很多常见操作如“移除左递归“,”内联规则“,重构这些模式
  •   动态解析树视图
  •   动态AST视图

 

 

 

1.4,在解析java代码时的antlrWorks调试器。输入、解析树和语法在任何时候总是同步的。

 

 

ANTLRWorks使用高可移植的java(使用Swing)编写,使用BSD许可的开源项目。因为antlrWorks 和运行的解析器使用套接字通信,所以antlrworks 调试器可工作在任何antlr 目标语言上。

 

前面的图1.3,显示的是 在语法的动作(action)中的 “跳转到规则“ 弹出对话框。正如你期待的,antlrworks 有 规则和标记的自动完成和语法加亮。下面的面板显示的是java语法中的规则 field的语法图。当语法中有歧义和其他不确定时,语法图显示可以识别你输入的多条路径。通过这个可视化,你能直接解决这些歧义。本书第三部分 antlrLL*)解析细节策略,大量使用了antlrworks提供的对不确定路径的可视化。

 

1.4,示例了antlrworks的调试器。调试器提供了很多信息,而且你也看到了,总能使各种视图同步。在这个例子中,语法将输入(Input)中的lexer标识和语法元素 Identifier匹配;解析树面板显示输入所隐含的树结构。更多antlrworks信息,参考用户手册(http://www.antlr.org/works/doc/antlrworks.pdf)。

 

本章介绍使你对antlr是什么和如何使用有一个整体了解。下一章示例了如何从语言的特性推导出为了规范语言而使用语法。第一部分的最后一章即第三章,“快速开始“,通过构造一个计算器,演示了antlr的更多特征。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值