【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱: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中包含有
- 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
从实验效果来看,加了括号之后的表达式效果更好,灵活性也更强。再想一想,如果不依靠工具,仅仅是手写代码,实现这样的四则表达式功能,不知道要走多少冤枉路。