11.JavaCC官方入门指南-例6
例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的更多相关文章
- 9.JavaCC官方入门指南-例4
例4:计算器--添加减法运算 1. calculator1.jj 为了使得计算器具备更多功能,我们需要更多的操作符,比如减法.乘法和除法.接下来我们添加减法运算. 在词法分析器的描述部分,我们 ...
- 8.JavaCC官方入门指南-例3
例3:计算器-double类型加法 下面我们对上个例子的代码进行进一步的修改,使得代码具有简单的四则运算的功能. 第一步修改,我们将打印出每一行的值,使得计算器更具交互性.一开始,我们只是把数 ...
- 7.JavaCC官方入门指南-例2
例2:整数加法运算--改良版(增强语法分析器) 1.修改 上一个例子中,JavaCC为BNF生产式所生成的方法,比如Start(),这些方法默认只简单的检查输入是否匹配BNF生产式指定的规范.但是 ...
- 6.JavaCC官方入门指南-例1
例1:整数加法运算 在这个例子中,我们将判断如下输入的式子是否是一个合法的加法运算: 99 + 42 + 0 + 15 并且在输入上面式子的时候,数字与加号之间的任何位置,都是可以有空格或者换 ...
- 10.JavaCC官方入门指南-例5
例5:计算器--添加乘除法运算 1.calculator2.jj 根据上一个例子,可知要添加乘法和除法运算是很简单的,我们只需在词法描述部分添加如下两个token: TOKEN : { < TI ...
- 5.JavaCC官方入门指南-概述
一.前言 在最开始使用JavaCC的时候,从网上查询了许多资料,但是网上的资料水平是参差不齐的,走了许多弯路,不得已自己查阅了英文版官网文档.令我伤心的是最后我回过头来再看那些博客资料时,发现其实 ...
- 分布式服务框架 Zookeeper(三)官方入门指南
入门指南:使用ZooKeeper来协调分布式应用 这篇文档包含了让你快速上手ZooKeeper的信息.主要是针对那些想要试一把ZooKeeper的开发人员,包含了安装一个单一ZooKeeper服务器的 ...
- Asp.Net MVC4.0 官方教程 入门指南之五--控制器访问模型数据
Asp.Net MVC4.0 官方教程 入门指南之五--控制器访问模型数据 在这一节中,你将新创建一个新的 MoviesController类,并编写代码,实现获取影片数据和使用视图模板在浏览器中展现 ...
- Asp.Net MVC4.0 官方教程 入门指南之四--添加一个模型
Asp.Net MVC4.0 官方教程 入门指南之四--添加一个模型 在这一节中,你将添加用于管理数据库中电影的类.这些类是ASP.NET MVC应用程序的模型部分. 你将使用.NET Framewo ...
随机推荐
- Dubbo学习系列之十二(Quartz任务调度)
Quartz词义为"石英"水晶,然后聪明的人类利用它发明了石英手表,因石英晶体在受到电流影响时,它会产生规律的振动,于是,这种时间上的规律,也被应用到了软件界,来命名了一款任务调度 ...
- .Net Core 项目发布到Linux - CentOS 7(二)用Supervisor守护netcore进程
简介 supervisor可以保证程序崩溃后,可以重新把程序启动起来等相关功能. 安装 yum install -y supervisor 安装好后在/etc/会生成一个supervisord.con ...
- (转)两种高效过滤敏感词算法--DFA算法和AC自动机算法
原文:https://blog.csdn.net/u013421629/article/details/83178970 一道bat面试题:快速替换10亿条标题中的5万个敏感词,有哪些解决思路? 有十 ...
- Angular(04)-知识点脑图
点击左键 => 拖拽图片 => 新标签页查看图片 => 放大拖拽查阅
- 利用openssl自建CA体系
使用 OpenSSL 创建私有 CA:1 根证书 使用 OpenSSL 创建私有 CA:2 中间证书 使用 OpenSSL 创建私有 CA:3 用户证书 今天跟着上面的三部曲,做了一下openssl的 ...
- iOS开发中全量日志的获取
我们在app中对崩溃.卡顿.内存问题进行监控.一旦监控到问题,我们就需要记录下来,但是,很多问题的定位仅靠问题发生的那一刹那记录的信息是不够的,我们需要记录app的全量日志来获取更多的信息. 一,使用 ...
- socket简单介绍
一 三种类型的套接字: 1.流式套接字(SOCKET_STREAM) 提供面向连接的可靠的数据传输服务.数据被看作是字节流,无长度限制.例如FTP协议就采用这种. 2.数据报式套接字(SOCK ...
- Anaconda创建环境失败,提示无法定位程序输入点
https://blog.csdn.net/qq_37465638/article/details/100071259 这篇博客写得很清楚,是anaconda下Library下lib下的一个文件和DD ...
- 关于controller层用实体类接收参数为null的问题
如果你的表单标签中包含enctype="multipart/form-data"属性,那么请将它删掉<form action="xxxxxxx" id=& ...
- go语言之map
go语言的map就相当于python的dict 1.map的初始化 //创建map //k的类型是int,v的类型是string var test25_1 map[int]string fmt.Pri ...