ANTLR 构建 全功能 计算器

ANTLR 构建 全功能 计算器

“antlr是什么” 不是本文的重点,请自行百度。

为了方便编写文法,我们使用antlrworks来一边写,一边检查,这还有一个好处:可以显示各个文法的DFA图,方便检查,方便排错。

同时使用antlrworks可以严格检查是否有语法错误,也可以方便的生成java代码(不用命令行了哦)

下面开始了。。。。

第一步,打开antlrworks,出现窗口提示选择文法格式,这里我们选择*.g格式

ANTLR 构建 全功能 计算器_第1张图片

接下来,会要求我们进行一些简单的设置,来自动生成一些简单的内容,对于设计一个计算器,我建议的设置如下图:

ANTLR 构建 全功能 计算器_第2张图片

这样就会产生如下的内容:

grammar Calc;

 

INT :'0'..'9'+

    ;

 

FLOAT

    :   ('0'..'9')+ '.' ('0'..'9')* EXPONENT?

    |   '.' ('0'..'9')+ EXPONENT?

    |   ('0'..'9')+ EXPONENT

    ;

 

WS  :   ( ' '

        | '\t'

        | '\r'

        | '\n'

        ) {$channel=HIDDEN;}

    ;

 

fragment

EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;

/////////////////////////////////////此时建议保存一下,但是注意:保存文件名必须为Calc.g(与grammar Calc保持一致,这点和java很像哦)我们可以把鼠标点到FLOAT所在的区域,下面就会显示DFA了,如果你学过编译原理,那应该很容易看明白是什么意思: ANTLR 构建 全功能 计算器_第3张图片 第二步,我们要来修改一下,加入一些新的东西
在grammar Calc;下面加入三段:

expr returns[double value]:

        e=highLevelExpr {$value=$e.value;}

        (

                  '+' e=highLevelExpr {$value+=$e.value;}

                 | '-' e=highLevelExpr {$value-=$e.value;}

        )*

;

highLevelExpr returns[double value]:

           e=atom{$value=$e.value;}

           (

                 '^' e=atom {$value=Math.pow($value,$e.value);}

                 |'*' e=atom {$value*=$e.value;}

                 |'/' e=atom {$value/=$e.value;}

           )*

;

atom returns [double value]:

        INT{$value = Integer.parseInt($INT.text);}

       |FLOAT{$value = Double.parseDouble($FLOAT.text);}

        |'('expr')'{$value = $expr.value;}

;

说明:

expr,表达式段,这是指级别较低的表达式,它的操作仅限+-(就一个元素时直接求值),它操作的元素是高级别的表达式,如果我们要计算一个式子的值,这个式子最终可以归结为expr

highLevelExpr ,高级别表达式,所谓高级别就是值运算级别高,它进行的操作可以为*/^,运算级别很明显级别比+-高,它操作的元素的原子元素(粒度最小的单位,直接的数字或括号表达式)

atom,原子表达式,具有直接值的元素,或带括号的表达式expr,因为括号的级别很高,他也看做原子元素,但是他的值是递归求出来的

 

每一个段以一个单词(段名,会对应产生java代码的一个方法)开始,首字母小写,returns [double value] 表示有返回值且类型为double,返回的引用是value(在下面会给value赋值),段体以冒号:开始,以分号;结束,中间描述文法定义,同时给value赋值。

 

第三步,开始产生代码

在生成代码之前先让我们检查一下语法,菜单Grammar - Check Grammar,或直接使用快捷键Ctrl + R

ANTLR 构建 全功能 计算器_第4张图片

如果提示错误请检查一下是否有写错的东西,接下来生成java代码,菜单Generate- Generate Code,或直接使用快捷键Ctrl + Shift + G

ANTLR 构建 全功能 计算器_第5张图片

之后就会在桌面上生成一个文件夹件output里面有3个文件

Calc.tokens

CalcLexer.java

CalcParser.java

我们再在手动编写一个测试类(请确保antlr-runtime能够被编译器找到)

import org.antlr.runtime.ANTLRStringStream;

import org.antlr.runtime.CommonTokenStream;

 

public class Test {

    public static void main(String[] args) throws Exception {

        CalcLexer lexer = new CalcLexer(new ANTLRStringStream("4^2*2/4-(6+2)+4"));

        CommonTokenStream tokens = new CommonTokenStream(lexer);

        CalcParser parser = new CalcParser(tokens);

        System.out.println(parser.expr());

    }

}

然后在当前目录下执行命令javac *.javajava Test如果没有错误的话,就会看到输出4.0
至此,一个简单的计算器就诞生了,这和我文章标题说的“全功能”似乎有点不符,别先着急,既然一般的步骤我们都已经知道了,接下来我们只要在文法中继续加入一些atom元素的计算方法即可,例如计算sin,cos,ln等,修改后atom段为:

atom returns [double value]:

            INT{$value = Integer.parseInt($INT.text);}

        |   FLOAT{$value = Double.parseDouble($FLOAT.text);}

        |   '('expr')'{$value = $expr.value;}

        |   'sin' e=atom {$value = Math.sin($e.value);}

        |   'cos' e=atom {$value = Math.cos($e.value);}

        |   'tan' e=atom {$value = Math.tan($e.value);}

        |   'ln' e=atom {$value = Math.log($e.value);}

        |   'lg' e=atom {$value = Math.log10($e.value);}

        |   'log(' e1=expr ','e2=expr')'{$value = Math.log($e1.value)/Math.log($e2.value);}

;

甚至可以加入阶乘,排列,组合等,但是java的Math类没直接提供方法,我们可以写到一个类里面,像调用Math.sin(..)那样调用

 

接下来再生成java代码,修改一下Test.java,试一个复杂一点的表达式吧


注:自动生成的识别  INT 和 FLOAT 文法可能不是很完美,如果你是个完美控可以再写一个复杂的文法来识别

你可能感兴趣的:(ANTLR 构建 全功能 计算器)