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 ...
随机推荐
- 重启docker服务应用,自启停命令.
#重启docker服务应用,不自动开启docker容器 docker update --restart=no (docker容器CONTAINER ID 或 docekr容器NAMES) #重启doc ...
- 非关系型数据库--redis
0.1 新单词 expire 美 /ɪk'spaɪɚ/ 到期 range 美 /rendʒ/ 范围 idle美 /'aɪdl/ 闲置的 0.2 面试题:mysql和redis和memcached区别? ...
- 如何把Mybatis的Mapper.xml配置文件和dao接口放在同一个包下
有的时候我们在Maven项目中写关于Mybatis的项目时,会涉及到很多的实体类,也就会涉及到很多的dao接口,如果此时我们仍然把dao接口和xml写在同一个包下,会让项目接口变得很乱,杂七杂八的,所 ...
- ConcurrentHashMap源码走读
目录 ConcurrentHashMap源码走读 简介 放入数据 容器元素总数更新 容器扩容 协助扩容 遍历 ConcurrentHashMap源码走读 简介 在从JDK8开始,为了提高并发度,Con ...
- SpringAOP中切入点的高级使用
上一篇 SpringAOP之使用切入点创建通知 SpringAOP中切点的高级使用 一.使用控制流切入点(ControlFlowPointcut) 什么是控制流切入点呢?看下面的代码(为了方便,就写进 ...
- 【CF528D】Fuzzy Search
Problem Description 你有一个长度为 \(n\) 的串 \(S\),以及长度为 \(m\) 的串 \(T\). 现给定一个数 \(k\) ,我们说 \(T\) 在 \(S\) 的位置 ...
- 收藏收藏:工作用了很久的自主开发的Sql Server代码生成器,我开源了(.NET Winform)
大家好,已经一年没见了,我的技术已经有了惊人的进步.于是乎就来为大家分享技术了,今天呢为大家带来的是很早之前开发的老工具分享给大家. 相信大家都使用过代码生成器,用起来顺手的有多少?根据自己的业务配置 ...
- spring单元测试下模拟rabbitmq
gradle添加引用 compile 'org.springframework.boot:spring-boot-starter-amqp' testCompile 'com.github.fridu ...
- 快速幂取模&快速乘取模
快速幂取模 即快速求出(a^b)mod c 的值.由于当a.b的值非常大时直接求a^b可能造成溢出,并且效率低. 思路 原理就是基于\(a*b \% c = ((a \% c)*(b \% c))\% ...
- CSS 学习手册
目录 CSS 简介 1.CSS 简介 CSS 概述 层叠次序 2.CSS 基础语法 CSS 语法 值的不同写法和单位 记得写引号 多重声明: 空格和大小写 3.CSS 高级语法 选择器的分组 继承及其 ...