撰写本文的目的:

给出使用JFlex、JavaCUP来为一个计算器建立分析器的示例的完整代码,使读者能充分领会JavaCUP的使用方法。虽然本文仅仅给出了计算器的代码,但只要你会写你的语言的翻译模式,则只要照抄这个模版,并改改相应动作就可以了。

引用到的资料:

《CUP User's Manual》,作者:Scott E. Hudson地址为李老师那里下载下来的JavaCUP-11a.rar\CUP-develop.tar.gz\develop\manual.html,有详细的英文说明和示例代码,但有很多错。本文中简称为《手册》。

《使用CUP进行语法分析》,摘自Apollo的博客,貎似是转载的(竟然不注明[转]和真实出处?!,BS之~),作者待考。有详尽的解释,但缺乏示例代码。本文中简称为《语法分析》。

详细步骤:

1、准备工作。

JavaCUP和JFlex一样,压缩包里边有许多的文件夹和文件,我不知道正统的做法是否要求使用javaCUP也像Jflex一样要设置一堆path啊、classpath啊、jflex_home之类的环境变量,但如果你像我一样只打算用它几次,你只要执行下述的两个简单步骤就可以了,它并不需要你设置任何的环境变量(以下假设你的工作目录是work\):

1)   将JavaCUP压缩包里的java-cup-11a.jar解压到work\下。

2)   将JavaCUP压缩包里的CUP-develop.tar.gz\develop\src下的java_cup文件夹整个解压到work\下。

现在你可以使用JavaCUP了。

2、为这个计算器写一个词法分析器。或者用JFlex生成一个词法分析器

两种方法都可以生成词法分析器,其中,直接写分析器的代码如下:

scanner.java//Simple Example Scanner Class

//scanner.javaimportjava_cup.runtime.*;

importjava.io.*;

//import sym;

publicclassscannerimplementsjava_cup.runtime.Scanner{

/**//*single lookahead character*/protectedstaticintnext_char;

//since cup v11 we use SymbolFactories rather than SymbolsprivateSymbolFactory sf=newDefaultSymbolFactory();

privatestaticFileReader fileReader;

publicscanner(FileReader fr){

this.fileReader=fr;

  }

/**//*advance input by one character*/protectedstaticvoidadvance()

throwsjava.io.IOException

{ next_char=fileReader.read(); }

/**//*initialize the scanner*/publicstaticvoidinit()

throwsjava.io.IOException

{ advance(); }

/**//*recognize and return the next complete token*/publicSymbol next_token()

throwsjava.io.IOException

{

for(;;)

switch(next_char)

{

case'0':case'1':case'2':case'3':case'4':

case'5':case'6':case'7':case'8':case'9':

/**//*parse a decimal integer*/inti_val=0;

do{

            i_val=i_val*10+(next_char-'0');

            advance();

          }while(next_char>='0'&&next_char<='9');

returnsf.newSymbol("NUMBER",sym.NUMBER,newInteger(i_val));

case';': advance();returnsf.newSymbol("SEMI",sym.SEMI);

case'+': advance();returnsf.newSymbol("PLUS",sym.PLUS);

case'-': advance();returnsf.newSymbol("MINUS",sym.MINUS);

case'*': advance();returnsf.newSymbol("TIMES",sym.TIMES);

case'/': advance();returnsf.newSymbol("DIVIDE",sym.DIVIDE);

case'%': advance();returnsf.newSymbol("MOD",sym.MOD);

case'(': advance();returnsf.newSymbol("LPAREN",sym.LPAREN);

case')': advance();returnsf.newSymbol("RPAREN",sym.RPAREN);

case-1:returnsf.newSymbol("EOF",sym.EOF);

default:

/**//*in this simple scanner we just ignore everything else*/          advance();

break;

      }    }};

以上代码来自《手册》的附录B,但有以下修改:

修改概要注释掉第4行的import sym;

   原文第6行改成public class CalcLex implements java_cup.runtime.Scanner {

因为语法分析器要求其词法分析器必须派生自Scanner类。

   删去原文第23行的static。因为其超类Scanner的next_token()方法不是静态的。

   删掉原文第48行其中一个return(无聊的语法错误!)

   新增加了一个构造函数scanner(FileReader)和静态属性FileReader fr,(当然要import System.io.*;)它们之后将会用到。

   修改了advance()的定义

这时scanner.java还未能通过编译的,因为其需要引用到的sym类还未生成,不用管它,继续下一步。

如果用JFlex来生成一个词法分析器,则要先写一个scanner.flex,代码如下:

scanner.flex//scanner.flex

//用户代码段importjava_cup.runtime.*;

importjava.io.*;

%%//参数设置和声明段%classscanner

%line

%column

%cup

%unicode

%{

publicstaticvoidinit(){}/**//*Just为了兼容手写版*/

privateSymbol symbol(inttype){

returnnewSymbol(type,yyline,yycolumn);

    }

privateSymbol symbol(inttype,Object value){

returnnewSymbol(type,yyline,yycolumn,value);

    }%}digit=[0-9]

number={digit}+LineTerminator=\r|\n|\r\n

WhiteSpace={LineTerminator}|[ \t\f]

%%//词法规则段

{

";"{returnsymbol(sym.SEMI);/**//*case ";"*/}

"+"{returnsymbol(sym.PLUS);/**//*case "+"*/}

"-"{returnsymbol(sym.MINUS);/**//*case "-"*/}

"*"{returnsymbol(sym.TIMES);/**//*case "*"*/}

"/"{returnsymbol(sym.DIVIDE);/**//*case "/"*/}

"%"{returnsymbol(sym.MOD);/**//*case "%"*/}

"("{returnsymbol(sym.LPAREN);/**//*case "("*/}

")"{returnsymbol(sym.RPAREN);/**//*case ")"*/}

{number}{returnsymbol(sym.NUMBER,newInteger(yytext()));/**//*case {number}*/}

{WhiteSpace}{/**//*case {WhiteSpace}:  do nothing*/}

}

.{

    System.out.println("Error:"+yytext()+"is illegal!");

}

3、使用javaCUP生成一个语法分析器。

在这一步里,你需要写一个parser.cup文件,代码如下:

parser.cup//CUP specification for a simple expression evaluator (w/ actions)

//parser.cupimportjava_cup.runtime.*;

/**//*Preliminaries to set up and use the scanner.*/

init with{: scanner.init();              :};

scan with{:returngetScanner().next_token(); :};

/**//*Terminals (tokens returned by the scanner).*/terminal           SEMI, PLUS, MINUS, TIMES, DIVIDE, MOD;

terminal           UMINUS, LPAREN, RPAREN;

terminal Integer   NUMBER;

/**//*Non-terminals*/non terminal            expr_list, expr_part;

non terminal Integer    expr;

/**//*Precedences*/precedence left PLUS, MINUS;

precedence left TIMES, DIVIDE, MOD;

precedence left UMINUS;

/**//*The grammar*/expr_list ::=expr_list expr_part

|              expr_part;

expr_part ::=expr:e

{: System.out.println("="+e); :}              SEMI

          ;

expr      ::=expr:e1 PLUS expr:e2

{: RESULT=newInteger(e1.intValue()+e2.intValue()); :}|              expr:e1 MINUS expr:e2

{: RESULT=newInteger(e1.intValue()-e2.intValue()); :}|              expr:e1 TIMES expr:e2

{: RESULT=newInteger(e1.intValue()*e2.intValue()); :}|              expr:e1 DIVIDE expr:e2

{: RESULT=newInteger(e1.intValue()/e2.intValue()); :}|              expr:e1 MOD expr:e2

{: RESULT=newInteger(e1.intValue()%e2.intValue()); :}|              NUMBER:n

{: RESULT=n; :}|              MINUS expr:e

{: RESULT=newInteger(0-e.intValue()); :}%prec UMINUS

|              LPAREN expr:e RPAREN

{: RESULT=e; :}          ;

现在你需要用JavaCUP来分析你的cup文件,请在命令行下输入

java-jar java-cup-11a.jarparser.cup

如果屏幕出现以下输出,就说明你已经成功了,这时javaCUP自动生成了parser.java和sym.java两个文件。现在你的scanner.java也可以成功通过编译了。

-------CUP v0.11a beta20060608Parser Generation Summary-------0errors and0warnings

12terminals,4non-terminals, and13productions declared,

  producing24unique parse states.

0terminals declared but not used.

0non-terminals declared but not used.

0productions never reduced.

0conflicts detected (0expected).

  Code written to"parser.java", and"sym.java".

----------------------------------------------------(v0.11a beta20060608)

4、编写主函数。

现在你的计算器的语法分析器已经做好,你还要做的就是编写一个主函数来调用这个分析器。请在work\下新建一个Calc.java,然后输入以下代码:

Calc.java//Calc.javaimportjava.io.*;

publicclassCalc{

publicstaticvoidmain(String argv[])throwsException{

        parser p=newparser(newscanner(newFileReader(argv[0])));

        p.parse();

    }}

5、测试用例。

你还需要设计一些测试例子来检查你是否已经成功完成了这个计算器。在work\下新建一个test.txt,输入一些数值表达式,例如:

test.txt2*4+6;

7*(5+3);

(5-3)/(2*4+3);

然后在命令行输入:

java Calc test.txt

如果屏幕输出:

=14=56=0

这就表示你已经大功告成了

~~

补充两点:

自我贴出这篇博文后,有很多朋友跟我说在输入java Calc test.txt时出现以下输出:

Exception in thread "main" java.lang.NoClassDefFoundError: calc

甚至

Exception in thread "main" java.lang.NoSuchMethodError: calc

但此前所有的代码生成、编译工作都是成功的。

经过我的分析,这是因为.java文件和.class文件版本不匹配造成的。通常是你用jflex或javaCUP生成了新的.java文件,却没有对它重新编译,因此.class里边的是旧的.java文件的内容,因此在运行的时候就会产生错误。解决方法是在命令行输入del *.class并重新编译,这时java会重新生成所有.class文件。

觉得每次都要手工输入命令去生成代码、编译、运行很慢很麻烦?呵呵~~像我这么懒的人当然不会这么笨啦

~~你可以写一个bat文件去自动帮你完成所有的工作,自然也可以解决上面那个“.NoClassDefFoundError”问题。不懂bat?去google一下“批处理文件”吧。这里再教你一个技巧,就是如何在bat中使用分支:

run.batcall jflex scanner.flex

if errorlevel 1 goto EXIT

java -jar cup.jar parser.cup

if errorlevel 1 goto EXIT

javac scanner.java

if errorlevel 1 goto EXIT

javac parser.java

if errorlevel 1 goto EXIT

javac Calc.java

if errorlevel 1 goto EXIT

java Calc test.txt

:EXIT

这样你只要轻轻的输入run,然后回车,就可以自动调用Jflex、JavaCUP生成代码、编译、运行了,并且当其中一步出错时,其后所有的步骤都不会被执行。是不是很好玩?

posted on 2007-05-09 15:35 踏雪赤兔 阅读(9171) 评论(24)  编辑 收藏 引用 所属分类: 玩转编程

java程序中空一阁_如何使用JFlex、JavaCUP(详细代码模版) by 踏雪赤兔相关推荐

  1. 怎么在Java里辨别小数_求教java中如何判断一个数是不是小数,求详细代码及解释...

    求教java中如何判断一个数是不是小数,求详细代码及解释 关注:62  答案:2  mip版 解决时间 2021-01-29 22:49 提问者孤酒醉人心 2021-01-29 03:13 求教jav ...

  2. java程序运行结果题_(Java程序设计)试题

    装 订 线 内 不 答 题 要 二.多选题 (每题2分,共10分) . A.Java 语言是面向对象的.解释执行的网络编程语言. B.Java 语言具有可移植性,是与平台无关的编程语言. C.Java ...

  3. 所有java程序都有线程_若所有的用户线程都终止了,Java程序就会结束。( )_学小易找答案...

    [单选题]关于链表结构,陈述错误的是 . [单选题]下列关于构造方法的特点的描述中,错误的是 . [判断题]在构造方法中如调用 super() 语句,则必须使其成为构造方法中的第一条语句. [单选题] ...

  4. java程序包不存在_第一章 Java语言简介

    Java语言简介 Java之父 James Gosling(詹姆斯·高斯林) 正式诞生时间 1995年,已有二十多年历史 三大方向 JavaSE(桌面版,基础需重点掌握) JavaME(移动版,现在基 ...

  5. java程序笑脸怎么打_我编写的JAVA程序为什么在编译执行后会先打出一个笑脸呢?...

    嗯,我个人认为,从键盘输入再输出意义不是很大,如果楼主有C的基础,那么这个输入将是一个很好练习,因为java的输入和输出与C有很大的区别,废话不多说 首先,无论是在Eclipse的控制台上还是CMD模 ...

  6. java 程序怎么设置中文_怎么让这个简单JAVA程序读写中文字符

    怎么让这个简单JAVA程序读写并正确显示中文字符,现在它只能读写显示英文字符.//这是个简单的读写文本的程序import .*;import t.*;import javax.swing.*;impo ...

  7. 编写java程序计算梯形面积_【Java】编写一个应用程序计算梯形和圆形的面积。...

    说明:这是武汉理工大学计算机学院[Java语言程序设计]课程实验1:编写一个应用程序计算梯形和圆形的面积. >>点击查看WUTer计算机专业实验汇总 谨记:纸上得来终觉浅,绝知此事要躬行. ...

  8. 简述java程序的工作过程_简述 Java 程序的开发过程。_学小易找答案

    [填空题]两个啮合齿轮在啮合区内,一个齿轮的轮齿用()绘制,另一个齿轮的轮齿被遮挡的部分用()绘制,被遮挡的部分也可以省略不画. [单选题]只有建立了统一的学校集体,才能在儿童的意识中唤起舆论的强大力 ...

  9. 简述如何编写java程序_1-4 简述 Eclipse 编写 Java 程序的流程。_学小易找答案

    [填空题]三四年级是英语入门初学阶段,更加注重( ). [单选题]不属于pos终端收银机的基本构成是( ). A . 显示器 B . 小票打印机 C . 键盘 D . 条形码电子称 [单选题]( ) ...

  10. java程序运行5步骤_浅析Java程序的执行过程

    一 .jdk内容概述 当安装好jdk后(本示例jdk版本为11.0.1,不同版本,目录有细微差别,但bin目录包含的exe是一样的),打开bin目录,有两个重要的exe文件:javac.exe(编译器 ...

最新文章

  1. FreeImage加速保存图像
  2. 前端将数据转化为弹幕效果的实现方式
  3. MybatisPlus入门之快速入门
  4. SAP Fiori Elements - how is read only field implemented in UI
  5. GraphQL 的前世今生
  6. java ssl证书_Java安全教程–创建SSL连接和证书的分步指南
  7. mysql操作库命令_MYSQL数据库------操作命令笔记
  8. 网络连接的netstat命令
  9. 怎么在表格中转换html格式,图解Excel与Html格式之间的互相转换
  10. 实现MFC扩展DLL中导出类和对话框
  11. Mybatis的生命周期及作用域
  12. 如何恢复原来数据库中的用户?
  13. thinkphp mysql order_ThinkPHP中order()的使用方法
  14. JimuReport积木报表 — SQL数据源报表制作
  15. 四万字长文说operator new operator delete
  16. 宝莱坞机器人 西瓜_《宝莱坞机器人之恋》电影完整版免费在线观看_2010西瓜影音 - 辛集电影院...
  17. SkyWalking服务应用
  18. 河南本科计算机科学与技术排名,河南计算机科学与技术专业大学排名
  19. Android 自定义SeekBar 的thumb遮挡问题解决
  20. alibaba pc safe service无法删除,一直在后台运行怎么办?

热门文章

  1. Jeecg框架中的一些常用属性(query,dictionary,funname,formatterjs,replace)
  2. i310100和i59400f哪个好 i3 10100和i5 9400f差距大吗
  3. 算法——排序——冒泡排序图解动画
  4. 程序员音乐_预订音乐会的程序员指南
  5. 基于stm32单片机外文文献_基于STM32的智能家居系统设计毕业论文+任务书+开题报告+文献综述+外文翻译及原文+程序+原理图+参考资料+答辩PPT+仿真设计...
  6. 单片机制作时钟倒计时
  7. windowsxp主题包教大家如何制作windows7主题包
  8. 超市便利店批发行业企业仓库,使用盘点机PDA扫描商品条码高效盘点,库存管不准怎么办
  9. rapidxml 文件读写,增加删除节点
  10. rapidxml解析xml文档