小型计算器A small software project
2017-02-05 17:09
435 查看
小型计算器
一个小型的软件项目,代码大概1000行左右,包括了很多知识(继承、多态、前向声明、虚函数、动态内存、引用、指针等等),从头到尾写完花了些时间,但觉得很受用。(1)计算器程序的目的是接受用户提供的算术表达式(例:1+2*3),求其值显示结果。
(2)每个算术表达式由解析器分析,将输入的算法表达式(一串字符)转换成一个算术树。
(3)算术树(一种数据结构):算术表达式可以转换为一个二叉树结构。
如:(a+b×(c-d))-e/f
二叉树结构为:
解析器语法
解析式寻找一个表达式,语法定义如下:1. An expression is(一个表达式是)
a. 一个后面有加号或减号的项,加号或减号后面是另一个表达式。例:表达式2-3,2是一个项(后面是减号),3是一个表达式。
b. 如果表达式不含有加号、减号,它等于项。
(例:上例中的3是一个表达式,它也是一个项)
2. A term is(一个项是)
a. 被另一个项乘除的因子。
(例:表达式1+2*3,2*3是一个表达式,满足1b条件,2*3是一个项,满足2a条件,2是一个因子)
b.项如果不包含乘除,它等于此因子。
3. A factor can be(一个因子是)
a. 数字
b. 对应某变量的标识符
c. 后面带有一个因子的减号
d. 小括号中的整个表达式
配合实例理解解析器的语法定义:
1+x*(2-y)
具体实现
计算器由以下几个对象组成:(1)扫描器,扫描用户输入的字符串。
(2)符号表,使计算器可以处理符号变量,如(x=1,变量x代表1)
(3)存储器,让计算器具有记忆能力,可以保存用户自定义变量的值。
(4)函数表,让计算器事先具有一些函数(如sin、cos、log、exp)
(5)结点,对应算术树的每个结点(使用继承实现)。关于结点的简化版本参看C++多态
(6)解析器,实现算数表达式的解析。
代码:只给出主函数、解析器代码,剩下的代码可以从github下载。
计算器Github
主函数 calculator.cpp
#include"SymbolTable.h" #include"FunctionTable.h" #include"Store.h" #include"Scanner.h" #include"Parser.h" const int maxBuf = 100; const int maxSymbols = 40; int main() { char buf[maxBuf]; //状态位 Status status; //符号表 SymbolTable symTab(maxSymbols); //函数表 FunctionTable funTab(symTab, funArr); //存储器 Store store(maxSymbols, symTab); do { std::cout << "> "; std::cin.getline(buf, maxBuf); //扫描器 Scanner scanner(buf); //解析器 Parser parser(scanner, store, funTab, symTab); status = parser.Eval(); } while (status != stQuit); return 0; }
解析器 Parser.h
#ifndef PARSER_H #define PARSER_H class Node; class Scanner; class SymbolTable; class Store; class FunctionTable; //枚举:状态 enum Status { stOK, stQuit, stError }; //解析器类 class Parser { public: Parser(Scanner & scanner, Store& store, FunctionTable& funTab, SymbolTable & symTab); ~Parser(); Status Eval(); private: void Parse(); Node* Expr(); Node* Term(); Node* Factor(); void Execute(); Scanner & _scanner;//扫描器 SymbolTable & _symTab;//符号表 Node* _pTree;//算术树 Status _status;//状态位 Store& _store;//存储器 FunctionTable& _funTab;//函数表 }; #endif
Parser.cpp
#include "Parser.h" #include<iostream> #include"Store.h" #include"Node.h" #include"FunctionTable.h" #include"SymbolTable.h" #include"Scanner.h" #include"AddNode.h" #include "AssignNode.h" #include"SubNode.h" #include"MultNode.h" #include"DivideNode.h" #include"NumNode.h" #include"FunNode.h" #include"VarNode.h" #include"UMinusNode.h" Parser::~Parser() { delete _pTree; } Parser::Parser(Scanner & scanner, Store& store, FunctionTable& funTab, SymbolTable & symTab) :_scanner(scanner), _pTree(0), _status(stOK), _funTab(funTab), _store(store), _symTab(symTab) { std::cout << "Parser Created" << std::endl; } //解析记号 Status Parser::Eval() { Parse(); if (_status == stOK) Execute(); else _status == stQuit; return _status; //for (EToken token = _scanner.Token(); // token != tEnd; // _scanner.Accept()) //{ // token = _scanner.Token(); // switch (token) // { // case tMult: // std::cout << "Times" << std::endl; // break; // case tPlus: // std::cout << "Plus" << std::endl; // break; // case tNumber: // std::cout << "Number: " << _scanner.Number() << std::endl; // break; // case tEnd: // std::cout << "End" << std::endl; // return stQuit; // case tError: // std::cout << "Error" << std::endl; // return stQuit; // default: // std::cout << "Error: bad token" << std::endl; // return stQuit; // } //} //return stOK; } void Parser::Execute() { if (_pTree) { double result = _pTree->Calc(); std::cout << " " << result << std::endl; } } void Parser::Parse() { _pTree = Expr(); } //表达式 Node* Parser::Expr() { Node* pNode = Term(); EToken token = _scanner.Token(); //加 if (token == tPlus) { //识别下一个记号 _scanner.Accept(); Node* pRight = Expr(); pNode = new AddNode(pNode, pRight); } //减 else if (token==tMinus) { _scanner.Accept(); Node* pRight = Expr(); pNode = new SubNode(pNode, pRight); } //赋值 else if (token == tAssign) { _scanner.Accept(); Node* pRight = Expr(); //左值 if (pNode->IsLvalue()) { pNode = new AssignNode(pNode, pRight); } else { _status = stError; delete pNode; pNode = Expr(); } } return pNode; } //项 Node* Parser::Term() { Node* pNode = Factor(); //Term is Factor * Term if (_scanner.Token() == tMult) { _scanner.Accept(); Node* pRight = Term(); pNode = new MultNode(pNode, pRight); } //Term is Factor/Term else if (_scanner.Token()==tDivide) { _scanner.Accept(); Node* pRight = Term(); pNode = new DivideNode(pNode, pRight); } //Term is Factor return pNode; } //因子 Node* Parser::Factor() { Node* pNode; EToken token = _scanner.Token(); //左括号 if (token == tLParen) { _scanner.Accept();//accept '(' pNode = Expr(); if (_scanner.Token() != tRParen) _status = stError; _scanner.Accept();//accept ')' } //数字 else if (token == tNumber) { pNode = new NumNode(_scanner.Number()); _scanner.Accept(); } //符号变量 else if (token == tIdent) { char strSymbol[maxSymLen + 1]; int lenSym = maxSymLen; //复制symbol到strSymbol _scanner.GetSymbolName(strSymbol, lenSym); int id = _symTab.Find(strSymbol); _scanner.Accept(); //函数调用 如sin(x) if (_scanner.Token() == tLParen) { _scanner.Accept(); pNode = Expr(); if (_scanner.Token() == tRParen) _scanner.Accept(); else _status = stError; if (id != idNotFound && id < _funTab.Size()) { //函数结点 pNode = new FunNode(_funTab.GetFun(id), pNode); } else { std::cout << "Unknow function\""; std::cout << strSymbol << "\"\n"; } } else { if (id == idNotFound) id = _symTab.ForcAdd(strSymbol, lenSym); pNode = new VarNode(id, _store); } } //一元减 else if (token == tMinus) { _scanner.Accept(); pNode = new UMinusNode(Factor()); } else { _scanner.Accept(); _status = stError; pNode = 0; } return pNode; }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- PostgreSQL教程(三):表的继承和分区表详解
- Lua编程示例(二):面向对象、metatable对表进行扩展
- C#中面向对象编程机制之多态学习笔记
- 浅谈Lua的面向对象特性
- Lua中调用C++函数示例
- Lua面向对象之类和继承浅析
- Lua教程(一):在C++中嵌入Lua脚本
- 浅析Ruby中继承和消息的相关知识
- Lua教程(二):C++和Lua相互传递数据示例
- JavaScript面向对象的两种书写方法以及差别
- 浅谈c# 面向对象之类与对象
- C#面向对象特征的具体实现及作用详解
- C# 面向对象的基本原则
- C#计算器编写代码
- 设计引导--一个鸭子游戏引发的设计理念(多态,继承,抽象,接口,策略者模式)