小型计算器
一个小型的软件项目,代码大概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;
}