例3:计算器—double类型加法

  下面我们对上个例子的代码进行进一步的修改,使得代码具有简单的四则运算的功能。

  第一步修改,我们将打印出每一行的值,使得计算器更具交互性。一开始,我们只是把数字加起来,然后再关注其他运算,比如减法、乘法和除法。

1.Options和class声明块

  描述文件calculator0.jj的第一部分如下所示:

/* calculator0.jj An interactive calculator. */
options {
STATIC = false ;
}
PARSER_BEGIN(Calculator)
import java.io.PrintStream ;
class Calculator {
public 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之间可能有import导入声明;这些代码都会被原样复制到生成的语法解析类和token管理类中去。同样还可以有包package的声明,package的声明将会被复制到最后生成的所有java类中去。

2.词法描述文件

  词法分析器的描述文件在这里将会发生一些变化,首先一行的结束符也被声明为了token,并给这些行结束符命名为EOL,这样一来这个token也可以被传递给语法分析器了。

SKIP : { " " }
TOKEN : { < EOL : "\n" | "\r" | "\r\n" > }
TOKEN : { < PLUS : "+" > }

  接下来,我们要定义合适的token使得允许输入中的数值有小数点。为此我们修改NUMBER这个token的定义,使得它可以识别decimal类型的数值。当数值中有小数点,它可以有如下的4中类型,我们分别用竖线分隔开来了,这4中类型分别是:整型,没有小数点、小数点在中间、小数点在末尾和小数点在开头。满足此需求的描述串如下:

TOKEN { < NUMBER : (["0"-"9"])+
| (["0"-"9"])+ "." (["0"-"9"])+
| (["0"-"9"])+ "."
| "." (["0"-"9"])+ >
}

  有时候,同样的规则表达式可能会出现多次。为了更好的可读性,最好是给这些重复出现的表达式起一个名字。对于那些只在词法描述文件中使用到,但又不是token的规则表达式,我们创建了一个特殊的标识来表示它:#。因此,对于上面的词法描述,可以替换成如下:

TOKEN : { < NUMBER : <DIGITS>
| <DIGITS> "." <DIGITS>
| <DIGITS> "."
| "."<DIGITS> >
}
TOKEN : { < #DIGITS : (["0"-"9"])+ > }

  可以看到,我们把([”0”-”9”])+这串规则表达式提取了出来,并将其命名为了DIGITS。但是要注意到,DIGITS这个并不是token,这意味着在后面生成的Token类中,将不会有DIGITS对应的属性,而在语法分析器中也无法使用DIGITS。

3.语法描述文件

  语法分析器的输入由零行或多行组成。每行包含一个表达式。通过使用BNF符号表达式,语法分析器可以写成如下:

Start -->(Expression EOL) * EOF

  由此我们可以得出BNF生产式如下:

void Start() :
{}
{
(
Expression()
<EOL>
)*
<EOF>
}

  我们在上面的BNF生产式中填充上java代码,使得它具备接收入参、记录并打印每一行的计算结果:

void Start(PrintStream printStream) throws NumberFormatException :
{}
{
(
previousValue = Expression()
<EOL> { printStream.println( previousValue ) ; }
)*
<EOF>
}

  每个表达式由一个或多个数字组成,这些数字目前用加号隔开。用BNF符号表达式如下:

Expression --> Primary(PLUS Primary)*

  在这里的Primary,我们暂时用它来表示数值。

  上面的BNF符号表达式用JavaCC表示出来如下所示:

double Expression() throws NumberFormatException : {
double i ;
double value ;
}
{
value = Primary()
(
<PLUS>
i = Primary()
{ value += i ; }
)*
{ return value ; }
}

  这个跟我们前面例子中的Start BNF生产式差不多,我们只是将数值的类型由int修改成了double类型而已。至于Primary(),跟上面例子也非常类似,它用BNF符号表达式来表示:

Primary --> NUMBER

  Primary()对应的JavaCC描述文件其实也差不多,只不过在这里它是对double精度的数值进行的转换计算:

double Primary() throws NumberFormatException :
{
Token t ;
}
{
t = <NUMBER>
{ return Double.parseDouble( t.image ) ; }
}

  下面我们用BNF符号表达式将语法分析器的逻辑表示出来:

Start --> (Expression EOL) * EOF
Expression --> Primary (PLUS Primary)*
Primary --> NUMBER

  至此,我们就把calculator0.jj描述文件都修改完了,接下来可以跑几个例子进行测试。

4.测试

4.1 calculator0.jj

  经过上面的修改,最后得到的.jj描述文件如下:

/* calculator0.jj An interactive calculator. */
options {
STATIC = false ;
}
PARSER_BEGIN(Calculator)
import java.io.PrintStream ;
class Calculator {
public 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) SKIP : { " " }
TOKEN : { < EOL : "\n" | "\r" | "\r\n" > }
TOKEN : { < PLUS : "+" > }
TOKEN : { < NUMBER : <DIGITS>
| <DIGITS> "." <DIGITS>
| <DIGITS> "."
| "."<DIGITS> >
}
TOKEN : { < #DIGITS : (["0"-"9"])+ > } void Start(PrintStream printStream) throws NumberFormatException :
{}
{
(
previousValue = Expression()
<EOL> { printStream.println( previousValue ) ; }
)*
<EOF>
} double Expression() throws NumberFormatException : {
double i ;
double value ;
}
{
value = Primary()
(
<PLUS>
i = Primary()
{ value += i ; }
)*
{ return value ; }
} double Primary() throws NumberFormatException :
{
Token t ;
}
{
t = <NUMBER>
{ return Double.parseDouble( t.image ) ; }
}

4.2 运行

  我们首先计算1+2,如下所示:

  接下来计算1+2.,即小数点在末尾的情形:

  接下来计算1 + 2.3,即小数点在中间的情形:

  接下来计算1 + .2,即小数点在开头的情形:

8.JavaCC官方入门指南-例3的更多相关文章

  1. 11.JavaCC官方入门指南-例6

    例6:计算器--添加括号.一元运算符和历史记录 1.calculator3.jj   我们只需要再添加一些特色,就可以得到一个可用的四则运算计算器.在这一版的修改中 ,我们将使得程序可以接收括号.负值 ...

  2. 9.JavaCC官方入门指南-例4

    例4:计算器--添加减法运算 1. calculator1.jj   为了使得计算器具备更多功能,我们需要更多的操作符,比如减法.乘法和除法.接下来我们添加减法运算.   在词法分析器的描述部分,我们 ...

  3. 7.JavaCC官方入门指南-例2

    例2:整数加法运算--改良版(增强语法分析器) 1.修改   上一个例子中,JavaCC为BNF生产式所生成的方法,比如Start(),这些方法默认只简单的检查输入是否匹配BNF生产式指定的规范.但是 ...

  4. 6.JavaCC官方入门指南-例1

    例1:整数加法运算   在这个例子中,我们将判断如下输入的式子是否是一个合法的加法运算: 99 + 42 + 0 + 15   并且在输入上面式子的时候,数字与加号之间的任何位置,都是可以有空格或者换 ...

  5. 10.JavaCC官方入门指南-例5

    例5:计算器--添加乘除法运算 1.calculator2.jj 根据上一个例子,可知要添加乘法和除法运算是很简单的,我们只需在词法描述部分添加如下两个token: TOKEN : { < TI ...

  6. 5.JavaCC官方入门指南-概述

    一.前言   在最开始使用JavaCC的时候,从网上查询了许多资料,但是网上的资料水平是参差不齐的,走了许多弯路,不得已自己查阅了英文版官网文档.令我伤心的是最后我回过头来再看那些博客资料时,发现其实 ...

  7. 分布式服务框架 Zookeeper(三)官方入门指南

    入门指南:使用ZooKeeper来协调分布式应用 这篇文档包含了让你快速上手ZooKeeper的信息.主要是针对那些想要试一把ZooKeeper的开发人员,包含了安装一个单一ZooKeeper服务器的 ...

  8. Asp.Net MVC4.0 官方教程 入门指南之五--控制器访问模型数据

    Asp.Net MVC4.0 官方教程 入门指南之五--控制器访问模型数据 在这一节中,你将新创建一个新的 MoviesController类,并编写代码,实现获取影片数据和使用视图模板在浏览器中展现 ...

  9. Asp.Net MVC4.0 官方教程 入门指南之四--添加一个模型

    Asp.Net MVC4.0 官方教程 入门指南之四--添加一个模型 在这一节中,你将添加用于管理数据库中电影的类.这些类是ASP.NET MVC应用程序的模型部分. 你将使用.NET Framewo ...

随机推荐

  1. vue-cli 引用elementUI打包后文件过大

    解决方案:使用externals引用第三方资源,防止element资源被打包到自己项目中,(总共修改3个页面index.html.webpack.base.conf.js.main.js) 1.修改i ...

  2. AI-Web1靶机渗透

    先上一张图: 靶机信息及地址:https://www.vulnhub.com/entry/ai-web-1,353/ 下载到靶机后,在VMware里打开,kali使用arp-scan -l 扫到 在浏 ...

  3. 解决adb网络连接中出现的“由于目标计算机积极拒绝,无法连接”错误

    在调试一块全志A83T安卓工控板(已root),启动后,安卓系统正常,设置好以太网 的静态IP地址:192.168.1.181,并接好网线,同时开发电脑WIN7系统IP地址 也是129.168.1.x ...

  4. JavaWeb开发——软件国际化(动态元素国际化)

    软件国际化的第二个部分,就是动态元素国际化. 数值,货币,时间,日期等数据由于可能在程序运行时动态产生,所以无法像文字一样简单地将它们从应用程序中分离出来,而是需要特殊处理.Java 中提供了解决这些 ...

  5. Redis之高可用、集群、云平台搭建(非原创)

    文章大纲 一.基础知识学习二.Redis常见的几种架构及优缺点总结三.Redis之Redis Sentinel(哨兵)实战四.Redis之Redis Cluster(分布式集群)实战五.Java之Je ...

  6. Android项目模块化/组件化开发(非原创)

    文章大纲 一.项目模块化初步介绍二.项目模块化的两种模式与比较三.大型项目模块化的演进四.项目模块化总结五.参考文章   一.项目模块化初步介绍 1. 前言 在Android开发中,随着项目的不断扩展 ...

  7. SQL注入:POST注入

    POST注入简介 POST注入属于注入的一种,相信大家在之前的课程中都知道POST\GET两种传参方式. POST注入就是使用POST进行传参的注入,本质上和GET类型的没什么区别. POST注入高危 ...

  8. springmvc的入门

    1. 什么是springMVC? Spring Web MVC是一种基于Java的实现了MVC设计模式的.请求驱动类型的.轻量级Web框架. 2. SpringMVC处理请求的流程 2.1 首先用户发 ...

  9. 201871010116-祁英红《面向对象程序设计(java)》第八周学习总结

    项目 内容 <面向对象程序设计(java)> https://home.cnblogs.com/u/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.c ...

  10. os 和 sys 的模块使用方法和模块

    os  的模块  方法 os.remove()删除文件 os.rename()重命名文件 os.walk()生成目录树下的所有文件名 os.chdir()改变目录 os.mkdir/maked ...