ANTLR实现加法计算器(通过AST形式来实现)

 

一、加法器功能:

整数、浮点数的加减运算

二、实现形式:

支持赋值语句和表达式计算,遇到不能识别给出提示。生成对应程序的抽象语法树AST(以字符形式表示各节点)和最后结果

说明:

整数为一般语言的整数定义int,以’0’开头的非零整数(如023)为非法;

浮点数,一般意义上即double,带小数点和尾数的;

这里由于支持赋值语句,所以需要规定变量名命名规则,即以字母或下划线开头的字母、下划线、数字组成的字符串;

这里个语句见以回车或换行来区分,没有用‘;’来分隔。

如下为运行时示例:

 

 

 

着色的为输入的赋值语句和表达式计算语句,黑色的为生成的语法树节点和最后的结果

三、具体过程:

1、  编写描述加法语法结构的文法文件,文件名为Expr.g.gANTLR识别的文法文件的后缀。 ANTLR中是使用DSLdomain-specific language)语言来描述文法的,这样一种专门设计来描述其它语言的语言通常叫做元语言(metalanguage),ANTLR支持EBNFExtended BNF),这种文法是上下文无关文法(context-free grammar),允许可选项和重复的元素。在这个加法器的例子中就是用这种文法来描述的。

其文法Expr.g见附件,其中各部分功能如下:

(1)       开头为文法名称,这个名称需要与文法文件名相同(不带后缀),ANTLR中支持四种类型文法:lexerparsercombinedtree grammar。在这里grammar Expr;表示是lexergrammar的混合(combined)文法,里面既有词法文法又有语法文法,一般情况除了是tree grammar都不用给出grammarType

(2)       接着的是文法的options section部分,这里option可以有languageoutputbacktrackASTLabelType等等。在加法的这个例子中,声明如下:

options {

                   output=AST;

                ASTLabelType=CommonTree; // type of $stat.tree ref etc...

}

由于要生成AST树,所以要指明outputASToutput用于指定生成的数据结构,现在(即ANTLR v3)只能生成ASTtemplate

ASTLabelType则是指明生成的树的种类,因为默认情况下,ANTLR会将每个节点生成为Object类型,那样后续工作进行时会有很多的类型转换。而一般可以指定为CommonTree类型从而省去了一些类型转换。

(3)       接下来是一些grammar actions,这些对应actions生成的method可以被打包到生成的class文件中。

其语法形式是:

@action-name {…..}

@action-scope-name{…..}//这个用在combined grammar

具体如: @members{}@header{}

在本例中如下:

//the fllowing two actions set the package for the generated Lexer.java and Parser.java

@lexer::header{

                package output;

}

@parser::header{

    package output;

}

 分别用于配置生成的词法文件和语法文件的环境配置。

(4)       接下来就是描述这个加法器的一些规则:

prog:   ( stat {System.out.println(

                $stat.tree==null?"null":$stat.tree.toStringTree());} )+ ;

          

stat:   expr NEWLINE        -> expr

               |   ID '=' expr NEWLINE -> ^('=' ID expr)

                |   NEWLINE             ->

                 ;

// END:stat

          

// START:expr

expr:   multExpr (('+'^|'-'^) multExpr)*

            ;

          

multExpr

              :   atom ('*'^ atom)*

    ;

          

atom:

    INT  

    |   DOUBLE

    |   ID

    |   '('! expr ')'!

    ;

// END:expr

其中有5rules,分别为:progstatexprmultExpratom

 

prog作为程序的开始,描述了(stat{…})+;即大于等于一个stat的可识别形式,其中{…}中为嵌入在文法中的以目标语言编写(本例中为Java)的一些操作,本例中为向控制台以StringTree形式输出这些树节点。

 

stat为实际上的识别语句,有3个可选项(alternative),分别为:

expr NEWLINE

ID ‘=’ expr NEWLINE

NEWLINE

其中NEWLINEID为词法识别的tokenNEWLINE这里为了方便用来代替‘;’,ID为标识符,用于记录变量;expr为表达式,这本是又是一个rule

expr:   multExpr (('+'^|'-'^) multExpr)*

是识别加减表达式的语法,即为multExpr后面跟着大于等于零个(('+'^|'-'^) multExpr),+multExpr或是-multExpr,用这种有点递归的方式识别出了表达式的语法;multExpr为进一步的一个rule,可以用来识别比加减运算符优先级高的乘法表达式:

: atom(‘*’^atom)*

atom是最为原子的rule,可直接由最简单的token来实现,其中除了INTDOUBLE外,ID'('! expr ')'!也作为可选项是因为multExpr也支持内层的嵌套。

 

最后还要说一点,上面的->,^,!符号是用来构造AST树的,->是将对应的alternative 构造成一个tree node^用来表示这个节点要被设成树的根节点,!表示只生成这个功能而不生成对应节点。

(5)       最后是词法的token识别,如下:

// START:tokens

ID  :   ('a'..'z'|'A'..'Z'|'_')('a'..'z'|'A'..'Z'|'_'|'0'..'9')* ;

INT :   ('1'..'9')('0'..'9')*|'0' ;

DOUBLE  :   (('1'..'9')('0'..'9')*|'0') ('.')('0'..'9')+;

NEWLINE:'/r'? '/n' ;

WS  :   (' '|'/t'|'/n'|'/r')+ {skip();} ;

// END:tokens

ID为标识符,字母或下划线开头,由字母数字下划线组成的字符串;

INT为整形,0或是1..9开头的数字串;

DOUBLE为浮点型,含整数、小数点和尾数部分;

NEWLINE为换行,? 表示可选,即'/r'可选,'/n'一定包含;

WS为空白字符,包括空格、换行、回车等,{..}中为嵌入的目标语言操作,本例中为忽略这个token

2、  使用ANTLR来生成Lexer.java Parser.java Expr.tokens,这三个文件默认都生成在output文件夹中,本例中先是new –>Java project,并加入Liabries antlrworks-1.4.jar(带works图形工具的ANTLR最新版本包)链接库,在srceclipse中新Java project内都有这个)下new ->fileExpr.g,按照步骤一中完成文法文件的编写,通过ANTLRWorks来生成上述三个文件。

这时如果调用生成类的方法来测试的,只能由输入程序段得到AST,输出的结果为一些树节点,没有最后计算结果,因为并没有对AST进行visit进而加入一些操作来计算出最后结果。

3、  编写tree grammar文法文件,visit  AST,加上一些actions,使得一些表达式能返回结果,其文法文件见附件:Eval.G, 各部分功能简介如下:

(1)       开始这部分属于声明部分:

tree grammar Eval;

          

options {

    tokenVocab=Expr;

    ASTLabelType=CommonTree;

}

          

// START:members

@header {

package output;

import java.util.HashMap;

}

          

@members {

/** Map variable name to Double object holding value */

HashMap memory = new HashMap();

}

   这是个tree grammar,用来visit前面已经生成的AST树,tokenVocab=Expr;是指示tree grammar Eval中的IDtokengrammar Expr中的IDtoken有相同的含义,从而这个tree grammar才可以正确的访问grammar Expr生成的AST树;@header option是在tree grammar生成的class中声明包和导入一些Java库函数,以便下面调用;@members option是用目标语言(本例是Java)声明或是创建一些全局变量或是对象,本例中是创建一个hash对象用来存放visit过程中的double类型变量信息的。

(2)       这部分是walk tree的主体部分:

// START:stat

prog:   stat+ ;

          

stat:   expr

             {System.out.println($expr.value);}

    |   ^('=' ID expr)

                     {memory.put($ID.text, new Double($expr.value));}

               ;

// END:stat

             

// START:expr

expr returns [double value]

               :   ^('+' a=expr b=expr) {$value = a+b;}

           |   ^('-' a=expr b=expr) {$value = a-b;}  

    |   ^('*' a=expr b=expr) {$value = a*b;}

    |   ID

                 {

                   Double v = (Double)memory.get($ID.text);

                   if ( v!=null ) $value = v.doubleValue();

                 else System.err.println("undefined variable "+$ID.text);

                 }

|   INT       

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

    |   DOUBLE               

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

               ;

              rule prog没有什么antion动作,只是识别一些赋值语句或是表达式;

              rule stat做了两件事:

1、 匹配expr的话就输出exprvalue

2、  匹配的是赋值语句的话,则将结果映射到变量中去并保存到hash表中。

rule expr如果匹配的是算式,则标准化为<result=a operator b>形式;如果匹配了IDINTDOUBLE则执行对应的actions

 

       在上一个文法文件Expr.g中有构造AST树的文法,如:

ID '=' expr NEWLINE -> ^('=' ID expr)

->符右边的为构造的AST节点,由于要表示一个二维的树节点不好表示,所以ANTLR中用这种一维的stream形式来表示一个节点,在tree grammar文件中就直接对这些个节点进行操作;就如上面这个被构造的节点,expr如果匹配的是:

multExpr+multExpr

tree grammar中会有{$value = a+b;}这种操作,并返回double型的结果,其结果保存在之前申请的hash表中,而如上面所示:

|   ^('=' ID expr)

                     {memory.put($ID.text, new Double($expr.value));}

^表示'='将被置成根节点,IDexpr为其子节点,其操作为将IDtext值赋成exprvalue值,并将其保存在hash表中。

 

4、  最后需要一个主程序来调用生成的各个类来实现程序的功能,主程序见附件Test.java

首先通过接受控制台输入,调用lexer来生成token流,parser类接受tokens可以得到一个result tree,然后创建一个Eval类型的visit来按rule prog来访问这个树,最后得到结果。

PS:修改下,添加下附件Caculator。。。整个工程文件都打包在附件中,感兴趣的话可以测试一下(需要加入antlrworks-1.4.jar外部链接库),附件中工程文件夹Caculatorsrc中为程序文件,Expr.gEval.g为编写的文法文件,output文件夹下 Lexer.javaParser.javaEval.javaExpr.tokensEval.tokens都是由文法文件通过ANTLRWorks生成的,Test.java含有main函数,是测试主程序。

笑话了,怎么只能上传图片格式!!

我放在下载空间里了,地址:http://download.csdn.net/source/2798964

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值