例6:计算器--添加括号、一元运算符和历史记录

1.calculator3.jj

  我们只需要再添加一些特色,就可以得到一个可用的四则运算计算器。在这一版的修改中 ,我们将使得程序可以接收括号、负值,并且还可以通过$符号来引用上一次计算的结果。

  对词法描述文件的修改如下所示,我们只添加下面3行:

TOKEN : { < OPEN_PAR : "(" > }
TOKEN : { < CLOSE_PAR : ")" > }
TOKEN : { < PREVIOUS : "$" > }

  我们没有必要专门为负号创建一个token,因为我们已经定义了MINUS这个token了。

  对于语法描述部分的修改,则都是体现在了Primary当中,在Primary当中有4中可能的值:一个数值(跟之前的例子一样)、一个$符号、带有括号的表达式、一个负号然后跟着前3个的任一种。BNF符号表达式如下:

Primary --> NUMBER
| PREVIOUS
| OPEN_PAR Expression CLOSE_PAR
| MINUS Primary

  这个BNF生产式中有两个递归。最后一种选择是直接递归,倒数第二个选择是间接递归,因为Expression最终还是依赖于Primary。在BNF生产式中使用递归是没有任何问题的,当然也有一些限制,我们将在后面谈论到。

  考虑下面的表达式:

- - 22

  那么Primary就是下图中的方块部分:

  在语法分析器执行到上面的输入时,对于每一个方块,都会调用一次Primary。相似的,对于下面的输入:

12 * ( 42 + 19 )

  我们把Primary框出来,可得到如下所示:

  相互嵌套的框框,其实就表示了相互递归调用的Primary方法。

  下面是JavaCC中Primary的生产式:

double Primary() throws NumberFormatException :
{
Token t ;
double d ;
}
{
t=<NUMBER>
{ return Double.parseDouble( t.image ) ; }
| <PREVIOUS>
{ return previousValue ; }
| <OPEN_PAR> d=Expression() <CLOSE_PAR>
{ return d ; }
| <MINUS> d=Primary()
{ return -d ; }
}

2. 测试

  根据上面的修改,得到完整的calculator3.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 : { < MINUS : "-" > }
TOKEN : { < TIMES : "*" > }
TOKEN : { < DIVIDE : "/" > }
TOKEN : { < OPEN_PAR : "(" > }
TOKEN : { < CLOSE_PAR : ")" > }
TOKEN : { < PREVIOUS : "$" > }
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 = Term()
(
<PLUS>
i = Term()
{ value += i ; }
| <MINUS>
i = Term()
{ value -= i ; }
)*
{ return value ; }
} double Term() throws NumberFormatException :
{
double i ;
double value ;
}
{
value = Primary()
(
<TIMES>
i = Primary()
{ value *= i ; }
| <DIVIDE>
i = Primary()
{ value /= i ; }
)*
{ return value ; }
} double Primary() throws NumberFormatException :
{
Token t ;
double d ;
}
{
t=<NUMBER>
{ return Double.parseDouble( t.image ) ; }
| <PREVIOUS>
{ return previousValue ; }
| <OPEN_PAR> d=Expression() <CLOSE_PAR>
{ return d ; }
| <MINUS> d=Primary()
{ return -d ; }
}

计算(1+2)*-3,如下所示,正确计算得到结果-9:

  计算器示例到此结束。官方文档中其实还有一个“文本处理”的例子,时间有限就不翻译了。可以直接去阅读英文原版,其实还是比较容易懂的。上面的示例通过由浅入深的引导,让我们大致知道JavaCC能干什么以及是怎么工作的。如果想要使用JavaCC做更多的事情,建议还是把最后一个例子的英文原版看完看懂,并多加练习。

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

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

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

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

    例3:计算器-double类型加法   下面我们对上个例子的代码进行进一步的修改,使得代码具有简单的四则运算的功能.   第一步修改,我们将打印出每一行的值,使得计算器更具交互性.一开始,我们只是把数 ...

  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. 2019年Java面试题基础系列228道(2)

    21.描述一下 JVM 加载 class 文件的原理机制? JVM 中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java 中的类加载器是一个重要的 Java 运行时系统组件,它 ...

  2. JavaScript-作用域与作用域链

    一.JavaScript作用域:    1.就是代码名字(变量)在某个范围内起作用和效果,目的是为了提高程序的可靠性更重要是减少命名冲突. 2.js的作用域(es6)之前,分为全局作用域.局部作用域 ...

  3. JS Timing

    JS Timing 通过使用 JavaScript,我们有能力做到在一个设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行.我们称之为计时事件. JavaScript 计时事件 通过使用 Ja ...

  4. MySQL 8.0部分弃用的参数整理

    最近整理了一下MySQL 8.0的自动化安装,其中用到了一个MySQL 5.7版本的自定义配置文件,由于没有对(MySQL 8.0)做针对性修改,导致安装过程中出现了一些错误其中部分原因就是MySQL ...

  5. SpringCloudGateway开发详解

    路由简介: SpringCloudGateWay 是用于替代zuul作为API网关,在gateway中有三个重要的名词:过滤器,断言,路由 过滤器与断言是路由的一部分,路由便是将请求进行一系列的处理后 ...

  6. 跨平台版本迁移之 XTTS 方案操作指南

    本文转自 https://blog.csdn.net/Enmotech/article/details/80045576 作者 | 罗贵林: 云和恩墨技术工程师,具有8年以上的 Oracle 数据库工 ...

  7. Linux系统学习 二十、SAMBA服务—介绍、安装、端口

    1.简介 网络数据文件共享服务器 可以和Windows中的网上邻居通用 数据共享的方法: Windows中最常用的是“网上邻居”.网上邻居使用的文件系统是CIFS(通用互联网文件系统)协议进行数据共享 ...

  8. Cocos2d-x开发教程——《萝莉快跑》

    更好的阅读体验请前往<萝莉快跑>开发教程. 配置:win7+Cocos2d-x.2.0.3+VS2012 目标读者:已经了解图形显示.动作.回调函数.定时器的用法. 一.基本知识点 1.动 ...

  9. Excel上下标如何设置?

    Excel表格怎么设置上下标?Excel上下标设置技巧 在21世纪的我们,平时的工作和学习中,经常会使用到一些专业的文档,比如方程式.数据的公式和科学计数等,其中均会涉及到许多的上下标符号输入以及使用 ...

  10. HttpClient发起Http/Https请求工具类

    <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcl ...