ImageVerifierCode 换一换
你正在下载:

JavaCC.docx

[预览]
格式:DOCX , 页数:28 ,大小:62.59KB ,
资源ID:7962878      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/7962878.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(JavaCC.docx)为本站会员(b****5)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

JavaCC.docx

1、JavaCCJavaCCJavaCC简介简介Theodore S. Norvell写的一本The JavaCC Tutorial的第一章Introduction to JavaCCJavaCC入门1. JavaCC和分析器生成程序JavaCC是一个能生成语法和词法分析器的生成程序。语法和词法分析器是字符串处理软件的重要组件。编译器和解释器集成了词法和语法分析器来解释那些含有程序的文件,不管怎样,词法和预防分析器被广泛用于各种应用,所以我希望这本书中的示例能阐述清楚。那么什么是词法和语法分析器呢?词法分析器能把一串字符切分成一溜叫做token的子串并把它们归类。看一个用C 语言写的小程序。int

2、 main() return 0 ;C 编译器的词法分析器会把上面的程序切割成下面的一串token“int”, “ ”, “main”, “(”, “)”,“ ”, “”, “n”, “t”, “return”“ ”, “0”, “ ”, “;”, “n”,“”, “n”, “” .词法分析器还会识别每个token 的类型;在这个例子中这个token 串的类型可能是KWINT, SPACE, ID, OPAR, CPAR,SPACE, OBRACE, SPACE, SPACE, KWRETURN,SPACE, OCTALCONST, SPACE, SEMICOLON, SPACE,CBRACE

3、, SPACE, EOF .EOF 这种token 表示输入文件结束。Token 流将会传给分析器。在这个C 语言的例子中,分析器并不需要所有的token;本例那些被归为SPACE 的 token 不会传给分析器。分析器将会分析token流以决定程序的结构。通常在编译器中,分析器输出一棵代表程序结构的树。这棵树将会作为编译器语法分析和代码生成的输入的一部分。考虑某个程序里的一个语句:fahrenheit = 32.0 + 9.0 * celcius / 5.0 ;分析器根据语法规则分析这个表达式并生成一棵树:图1fahrenheit = 32.0 + 9.0 * celcius / 5.0 (

4、译注:原文缺失该图,此图为译者根据前后语境所画)如果输入不遵循语言的词法或句法规则,词法分析器同时也负责产生出错信息。JavaCC本身并不是一个词法分析器或是语法分析器,而是一个分析程序生成器。这就是说它可以根据输入的语言规范输出一个词法和语法分析器。JavaCC生成的是用Java 写成的词法和语法分析器。见下图TBD:DIAGRAM TBD词法和语法分析器本身就是一个冗长而又复杂的组件。手工编写一个这样的程序需要仔细考虑各种语法规则的相互影响。比如在一个C 的词法分析器中,处理整数的代码和处理浮点常量的代码不能相互独立,因为整数和浮点数的开头都是数字。而使用像JavaCC这一点分析程序生成器

5、时,处理整数的规则和处理浮点数的是分开书写的,而它们之间公共的代码在生成器处理时被抽取出来了。模块性的增加意味着语言规范比手写的Java 程序更容易编写、阅读和修改。通过使用JavaCC这样的分析程序生成器,使软件工程师节约了不少时间,同时也增强了所编写软件的质量。2. 第一个例子整数相加作为第一个例子,我们把一串数字加起来,像这样99 + 42 + 0 + 15我们忽略所有数字和符号间的空格和换行符,除此之外,我们不接受除了10个数字和加号之外的其他字符。本节后面的代码都出自一个叫“adder.jj”的文件。这个文件含有符合JavaCC规范的词法和语法说明,用来作为JavaCC的输入。2.1

6、. 选项和类声明文件的第一部分/* adder.jj Adding up numbers */options STATIC = false ;PARSER BEGIN(Adder)class Adder public static void main( String args )throws ParseException, TokenMgrError Adder parser = new Adder( System.in ) ;parser.Start() ; PARSER END(Adder)在第一个注释之后的是选项段;除了STATIC 这一项(缺省为true),所有其他的JavaCC选都为

7、默认值。关于JavaCC选项的更多信息,请参考JavaCC的文档、本书的以后的章节和FAQ。接下来定义了一个叫做Adder 的Java 类,但在这你所看到的不是Adder 类的全部;JavaCC会在处理时为这个类添加其他代码。main方法宣称可能在运行时隐式的抛出两个异常:ParseException 和TokenMgrError;这些类都会由JavaCC生成。2010-12-13 19:23回复叶流征41位粉丝铁杆会员83楼2.2. 详述词法分析器我们待会儿再看那个main函数,现在我们首先来定义一个词法分析器。在这个简单的例子中,词法分析器的定义只有4行:SKIP : ” ” SKIP :

8、 ”n” | ”r” | ”rn” TOKEN : TOKEN : 第一行说明了空格是一个token,但是会被忽略。所以解析器并不会收到任何单独的空格。第二行也说了差不多的事情,只不过被忽略的是换行符,换行符会因操作系统而不同。Unix/Linux 采用LF (linefeed)字符;DOS 和Windows 则用CR+LF (carriage + linefeed),在老的Macintoshes 机子上,就用一个回车表示。我们要告之JavaCC所有的可能,就如上面用一个小竖线”|” 把不同的匹配模式隔开。第三行告诉JavaCC一个单独的加号是一个token,而且给这个Token取了一个名字:

9、PLUS。最后一行告诉JavaCC数字的语法并为它们取名为NUMBER。如果你熟悉Perl或者Java的正则表达式包,就不难明白这些式子的含义。让我们仔细看一下这个表达式(“0”-“9”)+。圆括号中的 “0”-“9” 是一个匹配任意数字的正则表达式,这表明unicode编码中的0-9之间的字符都能被匹配。一个形如 (x)+ 的正则式可以匹配任意重复的x 串。所以表达式 (“0”-“9”)+ 就可以匹配任意连续数字串。这四行每一行都是一个正则表达式实例(regular expression production )。还有一种由词法分析器生成的token,它的名字是EOF,正如其名,它代表了输入

10、的终止。不能,也不需要任何对EOF的匹配,JavaCC会自动生成它们。考虑一个包含如下字符串的输入文件:“123 + 456n”我们定义的词法分析器将会找到7个token: NUMBER, 空格, PLUS, 又一个空格, 另一个数字,一个换行, 然后是EOF。当然,标记了SKIP的token不会被传到解析器。所以,解析器只会看到这些东西:NUMBER, PLUS, NUMBER, EOF设想一个包含未定义字符的输入文件,例如:“123 456n”在处理完第一个空格之后,我们的可爱的词法分析器将遇到一个不认识的字符:减号。由于没有任何token的定义是以减号打头,词法分析器会扔出一个Token

11、MgrError异常。现在我们看看另一种情况:“123+456n”我们的词法分析器会提交一个这样的串:NUMBER,PLUS,PLUS,NUMBER,EOF词法分析器还没有智能到判断一个token 序列是否有意义,这通常是语法分析器的工作。我们接下来要讨论的解析器会在词法分析器提交第二个PLUS 之后发觉这个错误,然后拒绝处理之后的任何token。所以解析器实际上处理的只有:NUMBER,PLUS,PLUS同时,跳过(skip )一个token并不代表忽略(ignore )它。考虑下列输入:“123 456n”词法分析器会识别出3个token:两个NUMBER和夹在它们中间的空格;然后报错。2

12、.3. 详述语法分析器语法分析器的定义使用了一种叫BNF范式的东西,这看起来有点像Java的方法定义:void Start() :()*这个BNF范式声明了一个正确的输入序列的模式。我们解释一下它的意思:它以NUMBER开头的序列,以EOF结束,中间存在零个或多个由一个PLUS 后面跟一个NUMBER 组成的子序列。正如所见,语法分析器只会检查一个输入序列是否合法,而并没有真的把数字加起来。待会儿我们还会修改这个语法分析器,但现在我们先让它生成Java 组件,然后run 起来。2.4. 生成一个解析器和一个词法分析器我们现在用JavaCC根据我们写好的adder.jj文件生成分析器。具体怎么做

13、依赖于操作系统。下面是在Windows NT, 2000 和 XP 上完成的。首先使用“命令提示符”程序(CMD.EXE)运行JavaCC:2010-12-13 19:23回复叶流征41位粉丝铁杆会员84楼D:homeJavaCC-Bookadderjavacc adder.jjJava Compiler Compiler Version 2.1 (Parser Generator)Copyright (c) 1996-2001 Sun Microsystems, Inc.Copyright (c) 1997-2001 WebGain, Inc.(type javacc with no arg

14、uments for help)Reading from file adder.jj . . .File TokenMgrError.java does not exist. Will create one.File ParseException.java does not exist. Will create one.File Token.java does not exist. Will create one.File SimpleCharStream.java does not exist. Will create one.Parser generated successfully.这个

15、操作生成了七个Java 类,每一个在独立的文件中:l TokenMgrError是一个简单的错误类;词法分析器用它来侦测错误,父类是Throwable.l ParserException是另一个错误类;解析器用它侦测错误,父类是Exception,因此也是Throwable的子类。l Token 是一个表示token 的类。每个Token 对象都有一个整数域kind 表示token的类型(PLUS, NUMBER, 或者EOF),和一个String 域image,存储token 所代表的内容。l SimpleCharStream是一个把字符串提交给词法分析器的接口转换类。l AdderCons

16、tants是一个接口,定义了一组在词法分析器和解析器中都要用到的类。l AdderTokenManager就是词法分析器。l Adder 是解析器。现在我们可以用一个Java 编译器编译这些类了:D:homeJavaCC-Bookadderjavac *.java2.5. 让它跑起来现在我们换个角度来看Adder类的main 方法。static void main( String args )throws ParseException, TokenMgrError Adder parser = new Adder( System.in ) ;parser.Start() ;最先注意到main

17、可能会抛出继承自Throwable的两个子类(译注:TokenMgrError和ParserException)中的任意一个。这风格不是很好,我们应该捕捉这些异常。但是为了保持第一个例子简洁(译注:为了让读者能迅速把握要点,而不是陷入无穷的细节之中),我们忽略了这些东西。第一个语句创建了一个解析器实例,构建函数使用了自动生成的接受一个java.io.InputStream的重载。其实还有一个(更好的)接受Reader实例的重载(java建议在处理字符串时尽量使用Reader(Writer)而不是InputStream(OutputStream),这样能更好的避免字符编码带来的问题译者如是说)。

18、这个构建函数创建了一个SimpleCharStream对象和一个词法分析器AdderTokenManager的实例。这样,词法分析器通过SimpleCharStream顺利地获取到了我们的输入。第二句调用了一个由JavaCC生成的方法Start()。对语法规范中的每个BNF产生式,JavaCC都会生成一个对应的方法。这个方法负责尝试在输入序列中寻找符合模式的输入。例如,调用Start时会使解析器试图寻找一个匹配下面模式的输入序列:()*我们可以准备一个合适的输入然后运行这条命令D:homeJavaCC-Bookadderjava Adder input.txt我们运行程序,输入表达式以后,会出

19、现以下三种不同的情况:1. 出现词法错误。本例中,词法错误只出现在遇到未知字符时。我们可以通过下面的输入引发一个词法错误:“123-456n”这种情况下,程序会抛出一个TokenMrgError异常。这个异常的message域是:Exception in thread “main” TokenMgrError: Lexical error at line 1,column 5. Encountered: “-“ (45), after : “”2. 出现一个解析错误。这发生在输入序列不符合Start的BNF范式时。例如2010-12-13 19:23回复叶流征41位粉丝铁杆会员85楼“123+

20、456n”或者“123 456n”或者“n”这时,程序会扔出一个ParseException异常。这种异常的第一条信息分别是:Exception in thread “main” ParseException: Encountered ”+” at line 1, column 6.Was expecting: . 3. 输入串符合Start的定义。这时,程序不抛出任何异常,只会默默的停止。由于解析器除了挑错什么都不做,所有现在这个程序除了检查输入合法性以外什么都做不了。在下一节,我们将会做一些改变让它更有用。2.6. 生成的代码为了了解JavaCC生成的代码是如何工作的,最好的办法是看看它生

21、成的代码。final public void Start() throws ParseException jj consume token(NUMBER);label 1:while (true) jj consume token(PLUS);jj consume token(NUMBER);switch (jjntk = -1) ? jjntk() : jjntk) case PLUS:;break;default:jj la10 = jj gen;break label 1;jj consume token(0);方法jj_consume_token将试图从输入中读取一个指定类型的toke

22、n,如果得到的token与期望的类型不符,则抛出一个异常。表达式(jj_ntk=-1)?jj_ntk():jj_ntk计算下一个未读token的类型。而最后一行则要求匹配一个类型0的token;JavaCC总是用0 来编码EOF类型。2.7. 增强解析器像上文中提到的start方法一样的,由JavaCC根据BNF文法生成的方法,在默认情况下仅仅是检查了输入是否符合规则。但是我们可以在BNF中间夹杂Java代码,这些代码将来会被包含在生成的方法中。JavaCC为我们提供了一个骨架,而我们要让它有血有肉。下面我们改变adder.jj中的BNF规范,为Start 添加一些声明和Java 代码。新的文

23、件叫做adder1.jj。添加或改变的部分用黑体标出:int start() throws NumberFormatException:Token t;int i;int value;t = i = Integer.parseInt(t.image); value = i; (t = i = Integer.parseInt(t.image); value += i; )* return value; 首先,我们定义了BNF产生式的返回类型,这样生成的方法就从void 变为int。然后还声明了NumberFormatException可能会在运行时抛出。我们定义了三个变量。变量t是一个Toke

24、n,Token 是一个生成的类用来表示token;Token 类的image 域记录了匹配的字符串。当一个token 匹配上了一个BNF 产生式,我们就能通过赋上一个引用来记下这个Token 对象。像这样t = 我们可以在BNF产生式的大括号里添加任意的Java语句,这些语句会原封不动的copy到生产的代码里面。由于更改了Start的返回类型,我们有必要更改一下我们的main函数:static void main( String args )throws ParseException, TokenMgrError, NumberFormatException Adder parser = ne

25、w Adder( System.in ) ;intval = parser.Start() ;System.out.println(val);在结束这个例子前,我们再做一点小小的改进。下面的代码在start中出现了两次:t = i = Integer.parseInt( t.image ) ; 虽然在这个例子中不会引起太大的差异,仅仅涉及两行代码,但这种重复会导致维护的问题。所以我们把这两行提出来作为另一个BNF 产生式,叫做Primary。最新的修改依旧用黑体标出。int start() throws NumberFormatException:int i;int value;value =

26、 Primary()( 2010-12-13 19:23回复叶流征41位粉丝铁杆会员86楼 i = Primary() value += i; )* return value; int Primary() throws NumberFormatException :Token t ;t= return Integer.parseInt( t.image ) ; 这时我们再来看看JavaCC所生成的代码:final public int Start() throws ParseException, NumberFormatException int i ;int value ;value = P

27、rimary();label 1:while (true) switch (jjntk=-1)?jjntk():jjntk) case PLUS:;break;default:jj la10 = jj gen;break label 1;jj consume token(PLUS);i = Primary();value += i ;jj consume token(0); if (true) return value ; throw new Error(”Missing return statement in function”);final public int Primary() thr

28、ows ParseException, NumberFormatException Token t ;t = jj consume token(NUMBER); if (true) return Integer.parseInt( t.image ); throw new Error(”Missing return statement in function”);待会儿我们还能看到如何向BNF产生式传递参数。3. 第二个例子:运算器接下来,我们继续改进我们的adder,使它成为一个简易的四则运算计算器。第一步,我们让它能够和我们进行交互,把每行作为一个单独的表达式,并计算输出。稍后,我们会考虑

29、加法之外的其他操作,减法,乘法和除法。3.1. 选项和类定义calculator0.jj的开头如下:/* calculator0.jj An interactive calculator. */options STATIC = false ;PARSER BEGIN(Calculator)import java.io.PrintStream ;class Calculator static void main( String args )throws ParseException, TokenMgrError, NumberFormatException Calculator parser = new Calculator( System.in ) ;parser.Start( System.out ) ;double previousValue = 0.0 ;PARSER END(Calculator)类Calculator 的previousValue域用于保存前一行的计算结果,我们的下一版本将允许在表达式中使用美元符号($)表示这个值。import语句可以写在PARSER_BEGIN和PARSER_END之间,他们将被复制到生成的类文件中,包定义同样也在这时声明。3.2. 词法定义词法定义的改变不大,

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1