ANTLR实现简单计算器[C#]

来源:互联网 发布:系统温度监控软件 编辑:程序博客网 时间:2024/06/05 11:10

编译原理课程设计
本文是2012年暑假编译原理课程设计的成果。
计算器实现

1.功能

本计算器,首先分析一个计算表达式组成的语句串,然后计算出表达式的结果并输出。计算器中能实现以下功能:

ü  支持小数和负数的输入和运算,例如2.1-1.732.3E-2(科学数),-3+(-90)*5.0等;

ü  支持常见的运算符,如+-*/^(乘幂)。例如3+24^242);

ü  支持常规的运算符优先结合关系,即先乘除,最后加减,同级运算符按照从左到右的顺序结合;

ü  支持括号及括号的嵌套;

ü  支持三角函数和对数函数以及幂运算(实现了SinCosTanLnLogLg^等);

ü  表达式中支持变量的引入,可以对变量进行赋值,取值的操作比如a=1b=2a+b

ü  变量名以字母打头,变量名中只能出现字母;

ü  对于语法匹配错误给出相应的出错提示,比如变量未定义。

2. g文件

2.1. Calc.g文件

grammar Calc;

 

options

{  

    output=AST;  

    ASTLabelType=CommonTree;  

    language=CSharp3;   

}

tokens

{

   NEGSYM;

}

public

prog: ( stat {System.Console.WriteLine($stat.tree==null?"null":$stat.tree.ToStringTree());} )+ ;  

stat: expr NEWLINE -> expr  

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

    | NEWLINE ->  

    ;  

 

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

multExpr: atomSYM (('*'^ |'/' ^)  atomSYM)* ;

atomSYM          :

  '-' atom  -> ^(NEGSYM atom)

  |  atom

  ;

atom: INT  

    | ID 

    | DOUBLE

    | '(' ! expr ')' !  

    | 'Sin''(' ! expr')' !

    | 'Cos''(' ! expr')' !

    | 'Tan''(' ! expr')' !

    |  '^''(' ! expr ',' ! expr')' !  

    | 'Ln''(' ! expr')' !

    | 'Lg''(' ! expr')' !

    | 'Log''(' ! expr ',' ! expr')' ! 

    ;  

 

ID  :   ('a'..'z'|'A'..'Z')+ ;  

INT :   '0'..'9'+ ;

DOUBLE           :'_'?

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

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

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

    ;

WS  :   (' '|'\t'|'\r'|'\n')+ { Skip(); } ;

NEWLINE: (';')+ ; 

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

2.2. CalcTree.g文件

tree grammar CalcTree;

options

{  

    tokenVocab=Calc;   

    ASTLabelType=CommonTree;  

    language=CSharp3;  

}  

 

@header

{

     using System;

     using System.Collections;

}

@members { Hashtable memory = new Hashtable();}  

 

public

prog: stat+ ;  

public

stat: expr {System.Console.WriteLine($expr.value);}  

    | ^('=' ID expr) {memory.Add($ID.text, $expr.value);}  

    ;  

public  

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;} 

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

    | ^(NEGSYM a=expr ) {$value = -1*a;}

    | #('^' a=expr b=expr) {$value = Math.Pow(a,b);}

    | #('Sin' x=expr) {$value=Math.Sin(x);}

    | #('Cos' x=expr){$value=Math.Cos(x);}

    | #('Tan' x=expr){$value=Math.Tan(x);}

    | #('Ln' x=expr){$value=Math.Log(x);}

    | #('Lg' x=expr){$value=Math.Log10(x);}

    | #('Log' a=expr b=expr){$value=Math.Log(a)/Math.Log(b);}

    | ID  

    {  

        $value = 0;   

        if ( memory.Contains($ID.text)) $value = (double)memory[$ID.text];  

        else System.Console.WriteLine("undefined variable "+$ID.text);             

    }  

    | INT

    {

        $value = (double)System.Convert.ToDouble($INT.text);

    }  

    | DOUBLE

     {

            $value = System.Convert.ToDouble($DOUBLE.text);

     }

;  

3.代码文件及使用

使用antlrworksg文件生成了相应的C#代码文件。可使用CTRL+SHIFT+G键生成,CTRL+R可检测语法错误。启动antlrworks 1.4.3使用java –jar antlrworks-1.4.3.jar,见下图:
   
ANTLR实现简单计算器[C] - WorldsList - 走在云海之巅

Calc.g文件经编译生成了CalcLexer.csCalcParser.csCalc.tokens文件。

CalcTree.g文件经编译生成了CalcTree.csCalcTree.tokens文件。

然后在Visual Studio 2010中新建一个Calc项目,同时添加ANTLR运行库文件Antlr3.Runtime.dll。并且把生成的文件添加到Calc项目中,并且增加input.txt输入文件(存放输入数据)以及Calc.cs主文件(Main函数所在)。最终完成后如图所示:
 
ANTLR实现简单计算器[C] - WorldsList - 走在云海之巅

 

为了不必每次都要重新添加g文件生成的代码到文件,让他们自动替换,我把antlrworks的代码生成目录设置为Calc项目代码所在文件。见下图
    
ANTLR实现简单计算器[C] - WorldsList - 走在云海之巅

 

现在就该编写Calc.cs文件了,文件代码如下:

using System;

using System.Collections.Generic;

using System.Text;

using Antlr.Runtime;

using Antlr.Runtime.Tree;

using System.IO;

 

namespace Calc

{

    class Calc

    {

        public static void Main(String[] args)

        {

            try

            {

                Console.WriteLine("CSharp计算器 V1.0 By wangyun 12.6.6\n");

                Console.WriteLine("【输入】");

                Console.WriteLine("input.txt文件读取表达式...");

                string filePath = "input.txt";

                FileStream forOutput = File.Open(filePath, FileMode.Open);

                StreamReader sr = new StreamReader(forOutput);

                Console.WriteLine("读入表达式:");

                Console.WriteLine(sr.ReadToEnd());

                Console.WriteLine("表达式读入已完成");

                sr.Close();

                forOutput.Close();

                FileStream fs = File.Open(filePath, FileMode.Open);

                ANTLRInputStream input = new ANTLRInputStream(fs);

                fs.Close();

                CalcLexer lexer = new CalcLexer(input);

                CommonTokenStream tokens = new CommonTokenStream(lexer);

                CalcParser parser = new CalcParser(tokens);

 

                Console.WriteLine("\n【输出】");

                Console.WriteLine("语法树:");

                AstParserRuleReturnScope<CommonTree, IToken> t = parser.prog();//取得语法树

                CommonTree ct = (CommonTree)t.Tree;

                CommonTreeNodeStream nodes = new CommonTreeNodeStream(ct);

                CalcTree walker = new CalcTree(nodes);

                Console.WriteLine("计算结果:");

                walker.prog();//计算结果

            }

            catch (System.Exception ex)

            {

                Console.Write("出现错误:");

                Console.WriteLine(ex.Message);

            }

            Console.ReadKey();

        }

    }

}

然后在input.txt文件里面输入需要测试的语句,保存,然后生成Calc。之后运行就可查看结果。这里以3.0+(-4);为例,input文件及运行结果如下:
 
ANTLR实现简单计算器[C] - WorldsList - 走在云海之巅