编译器一日一练(DIY系列之四则运算)
Posted 嵌入式-老费
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译器一日一练(DIY系列之四则运算)相关的知识,希望对你有一定的参考价值。
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
前面说到了javacc,也谈到了加法运算、减法运算、加减法运算、连续加减法运算。今天正好可以说一下乘法、除法运算。大家都知道,运算里面是分优先级的。也就是说,表达式里面,如果有乘法和除法,那么先做乘除,再做其他的加减运算。
因此为了实现四则运算,我们需要先处理乘除运算,再慢慢加入加减运算。
代码链接:https://github.com/feixiaoxing/DIYCompiler
1、乘除运算
单独的乘除运算还是比较简单的,只需要把原来的加减运算替换一下符号即可。比如,修改成这样,
options
STATIC = false;
PARSER_BEGIN(Adder)
import java.io.*;
public class Adder
public static void main(String[] args)
for (String arg : args)
try
System.out.println(evaluate(arg));
catch (ParseException ex)
System.err.println(ex.getMessage());
public static long evaluate(String src) throws ParseException
Reader reader = new StringReader(src);
return new Adder(reader).expr();
PARSER_END(Adder)
SKIP: <[" ", "\\t", "\\r", "\\n"]>
TOKEN:
<INTEGER: (["0"-"9"])+>
long expr() throws NumberFormatException :
Token a ;
Token b ;
int value = 0 ;
a = <INTEGER> value = Integer.parseInt( a.image );
(
"*" b = <INTEGER>
value *= Integer.parseInt(b.image);
|
"/" b = <INTEGER>
value /= Integer.parseInt(b.image);
)
<EOF>
return value ;
有了这一步修改之后,不管是乘法,还是除法,都是可以拿来进行处理的。生成java代码、编译之后,就会有这样的效果出来,
C:\\Users\\feixiaoxing\\Desktop\\test>java Adder 2*3
6
C:\\Users\\feixiaoxing\\Desktop\\test>java Adder 4/3
1
2、四则运算
有了乘除,有了加减,下面考虑的就是怎么把它们整合在一起了。这中间涉及到一个优先级的概念。在语法表达式当中,优先级越高的,越靠近变量表达式;优先级越低的,越远离变量表达式。这么说可能有一点艰深晦涩,下面可以用具体的实例来表达,
options
STATIC = false;
PARSER_BEGIN(Adder)
import java.io.*;
public class Adder
public static void main(String[] args)
for (String arg : args)
try
System.out.println(evaluate(arg));
catch (ParseException ex)
System.err.println(ex.getMessage());
public static long evaluate(String src) throws ParseException
Reader reader = new StringReader(src);
return new Adder(reader).expr();
PARSER_END(Adder)
SKIP: <[" ", "\\t", "\\r", "\\n"]>
TOKEN:
<INTEGER: (["0"-"9"])+>
long expr() throws NumberFormatException :
long a ;
long b ;
long value = 0 ;
a = primary() value = a;
(
"+" b = primary()
value += b;
|
"-" b = primary()
value -= b;
)*
<EOF>
return value ;
long primary() throws NumberFormatException :
Token a ;
Token b ;
long value = 0 ;
a = <INTEGER> value = Integer.parseInt( a.image );
(
"*" b = <INTEGER>
value *= Integer.parseInt(b.image);
|
"/" b = <INTEGER>
value /= Integer.parseInt(b.image);
)*
return value ;
大家可以观察一下,整个代码除了之前expr之外,还多了一个primary。此外,expr中的描述变成了加减运算,primary则变成了乘除运算。有了这个改变,javacc就会帮我们优先处理乘除,其次再处理加减了。再次经过javacc处理生成java、编译之后,就会有这样的计算效果,
C:\\Users\\feixiaoxing\\Desktop\\test>java Adder 1+2
3
C:\\Users\\feixiaoxing\\Desktop\\test>java Adder 2*2
4
C:\\Users\\feixiaoxing\\Desktop\\test>java Adder 1+2*2
5
C:\\Users\\feixiaoxing\\Desktop\\test>java Adder 1*1+2*2
5
C:\\Users\\feixiaoxing\\Desktop\\test>java Adder 1*1+2*4/1
9
是不是看上去还蛮容易的。可是换一个思路,大家如果不用javacc,单纯用编程语言去处理,可以考虑一下,代码需要写多久。我想,这就是javacc这一类工具的厉害之处吧。
3、带括号的四则运算
括号的引入在于,某些情况下,我们需要强制进行先加减、再乘除的运算。比如这样,(2+3)*5-1,这个时候2+3就变成了最高优先级。所以大家可以考虑下,jj文件应该如何修改。下面给出的是我这里的一个方法,供大家参考,
options
STATIC = false;
PARSER_BEGIN(Adder)
import java.io.*;
public class Adder
public static void main(String[] args)
for (String arg : args)
try
System.out.println(evaluate(arg));
catch (ParseException ex)
System.err.println(ex.getMessage());
public static long evaluate(String src) throws ParseException
Reader reader = new StringReader(src);
return new Adder(reader).expr();
PARSER_END(Adder)
SKIP: <[" ", "\\t", "\\r", "\\n"]>
TOKEN:
<INTEGER: (["0"-"9"])+>
long expr() throws NumberFormatException :
long value = 0 ;
value = main_expr()
<EOF>
return value ;
long main_expr() throws NumberFormatException :
long a ;
long b ;
long value = 0 ;
a = primary() value = a;
(
"+" b = primary()
value += b;
|
"-" b = primary()
value -= b;
)*
return value ;
long primary() throws NumberFormatException :
long a ;
long b ;
long value = 0 ;
a = secondary() value = a;
(
"*" b = secondary()
value *= b;
|
"/" b = secondary()
value /= b;
)*
return value ;
long secondary() throws NumberFormatException:
Token a;
long b = 0;
long value = 0;
(
a = <INTEGER> value = Integer.parseInt( a.image ); |
"(" b =main_expr() ")" value = b;
)
return value;
和之前的四则运算相比较,这里引入了main_expr和secondary两个表达式。之所以引入main_expr,我们先看一下secondary。它总共有两种形式,一种是INTEGER,这个无可厚非。另外一种是"(" main_expr() ")",看到这里大家应该就明白了。括号中的内容就是之前expr的内容。但是因为expr中包含有<EOF>,所以需要把expr提出去,重新创建一个main_expr作为新的、可以供递归来解析的表达式来处理。代码弄好了,接着就可以拿出来编译测试了,
C:\\Users\\feixiaoxing\\Desktop\\test>java Adder (1+2)*3
9
C:\\Users\\feixiaoxing\\Desktop\\test>java Adder (1+2)*3-1+(2+3)*4
28
C:\\Users\\feixiaoxing\\Desktop\\test>java Adder (1+2)*3-1+(2+3)*4-5-6-7-8
2
从实验效果来看,加了括号之后的表达式效果更好,灵活性也更强。再想一想,如果不依靠工具,仅仅是手写代码,实现这样的四则表达式功能,不知道要走多少冤枉路。
以上是关于编译器一日一练(DIY系列之四则运算)的主要内容,如果未能解决你的问题,请参考以下文章