例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. C#开发微信小程序(三)

    导航:C#开发微信小程序系列 关于小程序项目结构,框架介绍,组件说明等,请查看微信小程序官方文档,关于以下贴出来的代码部分我只是截取了一些片段,方便说明问题,如果需要查看完整源代码,可以在我的项目库中 ...

  2. HTTP 400 与 SpringMVC的 HttpPutFormContentFilter、FormContentFilter

    前端发送了一个http PUT请求,如下, json_xml: {,},,}},,},,}},},}},,},{,}],,},,}},,,,},,}},},}},,},{,}],,},,}},,},{ ...

  3. macbook无法下载软件问题解决

    今天新买了一台MacBook Pro,但是发现无法下载软件,在App Store中一直转圈圈. 方法:修改网络DNS为114.114.114.114和8.8.8.8,即可解决.

  4. vue app项目 第一天 基本架构和路由配置

    一.前言 今天开始打算花6天时间 完成一个基于vue的买菜app 在这里记录一下项目的流程和第一次做这个项目遇到的坑 以及解决的办法 二.第一步 脚手架的安装 使用npm 安装好脚手架  安装脚手架教 ...

  5. 【转载】C#string.Formart的字符串格式化

    String.Format 方法的几种定义: String.Format (String, Object) 将指定的 String 中的格式项替换为指定的 Object 实例的值的文本等效项.Stri ...

  6. [转]RPA Developer Advanced Certification - Exam #1 UiPath 练习

    本文转自:https://github.com/miyaichi/CertificationExam1 RPA Developer Advanced Certification - Exam #1 E ...

  7. RunLoop 和 autoreleasepool

    RunLoop 和 autoreleasepool RunLoop和线程的一一对应的,对应的方式是以key-value的方式保存在一个全局字典中 主线程的RunLoop会在初始化全局字典时创建 子线程 ...

  8. phpMyAdmin开启IP地址登录

    根本没有其他文章说的那么麻烦,又是修改配置文件,又是修改首页文件.在根目录下有个libraries文件夹,进去有个config.default.php文件,修改里面的AllowArbitrarySer ...

  9. Java面试准备基础篇_11.24

    Java类加载机制 Java内存模型JMM 为什么 Redis 单线程能支撑高并发? 高并发下的接口幂等性解决方案! 面试官问:平常你是怎么对 Java 服务进行调优的? JAVA虚拟机(JVM)六: ...

  10. 关于AttributeError: 'NoneType' object has no attribute 'send_keys'

    在学web自动化测试时,通过PO模型将特定页面的一些元素及元素操作放在特定页面模块中, 然后提取公共的部分, 如元素等待WebDriverWait, 元素操作send_keys, click, 获取元 ...