Parser类的功能:对表达式进行解析,生成一颗表达式树
Parser类与Scanner类之间的关系是什么? 关联关系,具体原因看代码
Parse()解析,生成一个表达式树Node* Expr();是否是表达式 Term();是否是项 Factor():是否是因式
解析过程中的状态: 正确; 退出; 错误;
解析表达式 Node* Parser::Expr() 项+表达式,以此类推
解析项,Node* Parser::Term() 因子乘以项,以此类推
解析因式,Node* Parser::Factor() 变量标识符Identifier后面再做
eg: P42\Parser.h
#ifndef _PARSER_H #define _PARSER_H //使用前向声明而不是包含Scanner的头文件的原因是,如果在cpp文件中多次包含了这样的头文件,使得生成的可执行文件增大 class Scanner; class Node;//解决在 Node* Expr();中没有定义Node enum STATUS { STATUS_OK; STATUS_ERROR; STATUS_QUIT; }; //Parser类:根据扫描结果,进行扫描,递归下降解析,直到生成一颗树 //Parser类与Scanner类之间的关系是什么? //依赖关系:一个类作为另一个类成员函数的参数,或者内部的局部变量调用了某个类的静态成员函数 //而Scanner作为Parser类的成员,而且是引用: Scanner& scanner_; //说明:Scanner类与Parser类具有一定的固定关系,在Parser类的生命周期内都要对scanner_的引用固定关系 //所以把他们看成关联关系 class Parser { public: Parser(Scanner& scanner); void Parse(); Node* Expr(); Node* Term(); Node* Factor(); double Calculate() const; private: Scanner& scanner_;//这里是引用,即使Parser类销毁了,Scanner类也不不一定销毁,Parser类不负责Scanner类的生命周期 //若这里不是引用,Parser对象销毁,Scanner对象也跟着销毁,这就是组合关系了 //这里也可以用引用,但是会拷贝一个Scanner类对象拷贝到Parser类内部,组合的方式效率低一些,没必要用了 Node* tree_; STATUS status_; }; #endif /* _PARSER_H */P42\Parser.cpp
#include "Parser.h" #include "Scanner.h"//因为会使用到Scanner的一些接口进行扫描 #include "Node.h" #include <cassert> #include <iostream> //引用的初始化只能才初始化列表中进行初始化 Parser::Parser(Scanner& scanner) : scanner_(scanner), tree_(0) { } //解析表达式: Node* Parser::Expr() { Node* node = Term(); EToken token = scanner_.Token(); if (token == TOKEN_PLUS)//扫描到+ { scanner_.Accept();//accept+号,扫描下一个字符,看看是不是一个Expression Node* nodeRight = Expr(); node = new AddNode(node, nodeRight);//返回的是加法节点,(左节点,右节点),Expression is Term + Expression } else if (token == TOKEN_MINUS) { scanner_.Accept(); Node* nodeRight = Expr(); node = new SubNode(node, nodeRight);//Expression is Term - Expression } return node;//Expression is Term } //解析项 Node* Parser::Term() { Node* node = Factor(); EToken token = scanner_.Token(); if (token == TOKEN_MULTIPLY) { scanner_.Accept(); Node* nodeRight = Term(); node = new MultiplyNode(node, nodeRight);//Term is Factory * Term } else if (token == TOKEN_DIVIDE) { scanner_.Accept(); Node* nodeRight = Term(); node = new DivideNode(node, nodeRight);//Term is Factory / Term } return node;//Expression is Factory } //解析因式 Node* Parser::Factor() { //or (Expression) Node* node = 0; EToken token = scanner_.Token(); if (token == TOKEN_LPARENTHESIS) { scanner_.Accept(); //accept '(' node = Expr();//先解析表达式,右边应该有个右括号 if (scanner_ == TOKEN_RPARENTHESIS) { scanner_.Accept(); //accept ')' } else { status = STATUS_ERROR; //to do:抛出异常 std::cout<<"missing parenthesis"<<std::endl; node = 0; } else if (token == STATUS_NUMBER) { node = new NumberNode(scanner_.Number());//新建一个数字节点 scanner_.Accept(); } else if(token == STATUS_MINUS) { scanner_.Accept();//接受一个负号,目的是指针偏移到下一个字符,让下一次的解析做准备 node = new UminusNode(Factor());//传递一个子节点进去,这个子节点就是因式 } else { status = STATUS_ERROR; //to do:抛出异常 std::cout<<"Not a valid expression"<<std::endl; node = 0; } return node; } } void Parser::Parse() { tree_ = Expr();//解析完后,将整个表达式赋值给tree_ } //注意:带const的成员函数与不带const的成员函数可以构成重载 double Parser::Calculate() const { assert(tree_ != 0);//0,NULL都行 //求表达式的值,实际上就是求其根节点的值 return tree_->Calc(); }P42\Scanner.h
#ifndef _SCANNER_H_ #define _SCANNER_H_ #include <string> enum EToken { TOKEN_END; TOKEN_ERROR; TOKEN_NUMBER; TOKEN_PLUS; TOKEN_MINUS; TOKEN_MULTIPLY; TOKEN_DIVIDE; TOKEN_LPARENTHESIS; TOKEN_RPARENTHESIS; TOKEN_IDENTIFIER; TOKEN_ASSIGN;//eg:a=5 }; //Scanner类:只负责扫描,并且登记当前的状态 class Scanner { public: Scanner(const std::string& buf); void Accept(); double Number() const; EToken Token() const; private: void SkipWhite(); const std:string buf_; unsigned int curPos_; EToken token_;//返回状态 double number_;//返回数字 }; #endif/*_SCANNER_H_*/P42\Scanner.cpp
#include "Scanner.h" #include <cctype> Scanner::Scan(const std::string& buf) : buf_(buf), curPos_(0) { Accept();//一个字符一个字符的扫描 } double Scanner::Number() const { return number_; } EToken Scanner::Token() const { return token_; } //忽略空白字符 void Scanner::SkipWhite() { while (isspace(buf_[curPos_])) ++curPos_; } void Scanner::Accept() { SkipWhite();//首先忽略空白字符 switch (buf_[curPos_]) { case '+': token_ = TOKEN_ADD; ++curPos_; break; case '-': token_ = TOKEN_MINUS; ++curPos_; break; case '*': token_ = TOKEN_MULTIPLY; ++curPos_; break; case '/': token_ = TOKEN_DIVIDE; ++curPos_; break; case '(': token_ = TOKEN_LPARENTHESIS; ++curPos_; break; case ')': token_ = TOKEN_RPARENTHESIS; ++curPos_; break; case '0': case '1': case '2' : case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': token_ = TOKEN_NUMBER; char* p;//实际上这里的指针并没有指向 //buf_是一个字符串,buf_[curPos_]是一个字符,相当于得到了内部字符串的字符 //这里的指针p,指针变量p本身发送改变,也就是说它指向了其他地方,改变了指针的指向 number_ = strtod(&buf_[curPos_], &p);//返回第一个不是数字的位置 //将地址p转换为数字curPos_,用以更新curPos_ curPos_ = p - &buf[0];// &buf[0]是字符串的首地址 break; case '\0' : case '\n' : case '\r' : case EOF://认为表达式结束了 token_ = TOKEN_END; break; default: token_ = TOKEN_ERROR; break; } }P42\Node.h
#ifndef _NODE_H #define _NODE_H //(1)【采用】禁止对象拷贝的eg演示 //Noncopyable不能构造对象,因为构造对象没意义,仅仅用来继承 class Noncopyable { protected: Noncopyable() {}; ~Noncopyable() {}; private: Noncopyable(const Noncopyable&); const Noncopyable& operator=(const Noncopyable&); }; //用private继承的原因是:并没有继承Noncopyable类的接口,即:这不是接口继承,而是实现继承 //实现继承:仅仅利用基类的内部函数,仅仅能在派生类的内部使用,并不能成为派生类额接口 //Node变成了对象语义,因为:要构造Node,要先构造Noncopyable,而Noncopyable既不能拷贝构造,也不能赋值了 class Node : private Noncopyable { public: //每个节点都有一个计算的纯虚函数 //类Node用于多态,派生类都要实现Calc //Calc声明为const的,因为Calc不会改变类的成员 virtual double Calc() const = 0; //类Node是多态类,析构函数也要声明为虚析构函数,否则基类指针指向派生类对象, //通过基类指针释放对象的时候,是不会调用派生类的析构函数 virtual ~Node() {}; }; //NumerNode要实现这个纯虚函数Calc,为具体类;若没实现,还是抽象类 class NumberNode : public Node { public: NumberNode(double number) : number_(number) {} double Calc() const; private: const double number_;//加const的原因:因为number_初始化后就不会改变 }; //BinaryNode节点有2个子节点 //BinaryNode类没有实现Calc方法,BinaryNode类仍然是抽象类,只有它的派生类,加、减、乘、除节点才知道该如何计算 class BinaryNode : public Node { public: BinaryNode(Node* left, Node* right) : left_(left), right_(right) {} ~BinaryNode();//记得要释放left_和right_节点 protected: Node* const left_;//const的作用:指针不能改变(即指针不能指向其它的节点),而不是指针所指向的内容不能改变 Node* const right_; }; //与BinaryNode相比,它只有1个孩子 //UnaryNod也是抽象类,因为它没有实现Calc方法 class UnaryNode : public Node { public: UnaryNode(Node* child) : child_(child) {} ~UnaryNode(); protected: Node* child_; } //加法运算节点AddNode class AddNode : public BinaryNode { public: //构造函数初始化,要调用基类部分的构造函数 AddNode(Node* left, Node* right) : BinaryNode(left, right) {} //要实现Calc方法,AddNode类是具体类 double Calc() const; }; class SubNode : public BinaryNode { public: SubNode(Node* left, Node* right) : BinaryNode(left, right) {} double Calc() const; }; class MultiplyNode : public BinaryNode { public: MultiplyNode(Node* left, Node* right) : BinaryNode(left, right) {} double Calc() const; }; class DivideNode : public BinaryNode { public: DivideNode(Node* left, Node* right) : BinaryNode(left, right) {} double Calc() const; }; class UminusNode : public UnaryNode { public: UminusNode(Node* child) : UnaryNode(child) {} double Calc() const; }; #endif/* _NODE_H */P42\Node.cpp
#include "Node.h" #include <cmath.h> #include <iostream> //数字节点的计算方法就等于数字节点本身 double NumberNode::Calc() const { return number_; } BinaryNode::~BinaryNode() { delete left_; delete right_; } UnaryNode::~UnaryNode(); { delete child_; } double AddNode::Calc() const { //AddNode节点的值等于左计算节点得到的值+右计算节点得到的值 return left_->Calc() + right_->Calc(); } double SubNode::Calc() const { return left_->Calc() - right_->Calc(); } double MultiplyNode::Calc() const { return left_->Calc() * right_->Calc(); } double AddNode::Calc() const { double divisor = right_->Calc(); if (divisor != 0.0) return left_->Calc() / divisor; else { std::cout << "Error: Divisor by zero" <<std::endl; return HUGE_VAL; } } double UnaryNode::Calc() const { //孩子节点前面加一个负号,对照改进类继承体系的图看更好理解 return - child_->Calc(); }P42\main.cpp
#include <iostream> #include <string> #include "Scanner.h" #include "Parser.h" int main(void) { STATUS status= STATUS_OK; do { std::cout<<">"; std::string buffer; std::getline(std::cin, buffer);//输入一行表达式放到buf当中 // std::cout<<buffer<<std::endl; Scanner scanner(buffer); Parser parser(scanner); parser.Parse();//实际上计算表达式的值,就是计算这颗树的根节点的值 std::cout<<paese.Calculate()<<std::endl; }while(status != STATUS_QUIT); return 0; } 测试: