(1)定义
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
①文法:即语法规则。在解释器模式中每一个语法都将对应一个解释器对象,用来处理相应的语法规则。它对于扩展、改变文法以及增加新的文法规则都很方便。
②解释器模式描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。
③在解释器模式中可以通过一种称之为抽象语法树(Abstract Syntax Tree, AST)的图形方式来直观地表示语言的构成,每一棵抽象语法树对应一个语言实例
(2)解释器模式的结构和说明
①AbstractExpression:定义解释器的接口,约定解释器的解释操作。其中的Interpret接口,正如其名字那样,它是专门用来解释该解释器所要实现的功能。(如加法解释器中的Interpret接口就是完成两个操作数的相加功能)。
②TerminalExpression:终结符解释器,用来实现语法规则中和终结符相关的操作,不再包含其他的解释器,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的叶子对象,可以有多种终结符解释器。
③NonterminalExpression:非终结符解释器,用来实现语法规则中非终结符相关的操作,通常一个解释器对应一个语法规则,可以包含其他解释器,如果用组合模式构建抽象语法树的话,就相当于组合模式中的组合对象。可以有多种非终结符解释器。
④Context:上下文,通常包含各个解释器需要的数据或是公共的功能。这个Context在解释器模式中起着非常重要的作用。一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
⑤Client:客户端,指的是使用解释器的客户端,通常在这里将按照语言的语法做的表达式转换成使用解释器对象描述的抽象语法树,然后调用解释操作。
【编程实验】四则运算(注意终结符解释器与非终结解释器的划分)
//声明文件
//行为型模式:解释器模式
//场景:四则运算
#include <iostream>
#include <string>
#include <map>
#include <stack>
#include <typeinfo>
using namespace std;
//*******************************************抽象表达式类***********************************
class CAbsExp{
public:
//解析公式和数值,其中var中的key是公式中的参数,value值是具体的数字
//如a = 100; b = 20; c = 40
virtual int Interpreter(map<string, int>& mpKeyValue) = 0;
virtual ~CAbsExp();
};
//变量解析器(终结符表达式)
class CVarExp : public CAbsExp{
private:
string strKey;
public:
CVarExp(string key);
//从map中取出变量的值
int Interpreter(map<string, int>& mpKeyValue);
~CVarExp();
};
//**********抽象运算符号解析器***********************
//抽象运算符号解析器
class CSymExp : public CAbsExp{
protected:
CAbsExp* pLeft;
CAbsExp* pRight;
public:
CSymExp(CAbsExp* left, CAbsExp* right);
CAbsExp* GetLeft();
CAbsExp* GetRight();
};
//加法解析器
class CAddExp : public CSymExp{
public:
CAddExp(CAbsExp* left, CAbsExp* right);
//把左右两个表达式运算的结果加起来
int Interpreter(map<string, int>& mpKeyValue);
~CAddExp();
};
//减法解析器
class CSubExp : public CSymExp{
public:
CSubExp(CAbsExp* left, CAbsExp* right);
//把左右两个表达式运算的结果相减
int Interpreter(map<string, int>& mpKeyValue);
~CSubExp();
};
//*********************************解析器封装类***************************************
//解析器封装类,这个类是根据迪米特法则进行封装,目的是让Client只与直接朋友打交道,相当于Facade
class CDiyCalc{
private:
CAbsExp* pRoot;
public: //构造函数传参,并解析表达式,构建语法树
CDiyCalc();
void BulidTree(string exp);
void DelTree(CAbsExp* exp);
~CDiyCalc();
int Calc(string, map<string, int>& mpKeyValue);
};
//实现文件
//*******************************************抽象表达式类***********************************
CAbsExp::~CAbsExp(){}
//变量解析器(终结符表达式)
CVarExp::CVarExp(string key){strKey = key;}
//从map中取出变量的值
int CVarExp::Interpreter(map<string, int>& mpKeyValue){return mpKeyValue[strKey];}
CVarExp::~CVarExp(){cout << "~CVarExp" << endl;}
//**********抽象运算符号解析器***********************
//抽象运算符号解析器
CSymExp::CSymExp(CAbsExp* left, CAbsExp* right){pLeft = left; pRight = right;}
CAbsExp* CSymExp::GetLeft(){return pLeft;}
CAbsExp* CSymExp::GetRight(){return pRight;}
//加法解析器
CAddExp::CAddExp(CAbsExp* left, CAbsExp* right) : CSymExp(left, right){}
//把左右两个表达式运算的结果加起来
int CAddExp::Interpreter(map<string, int>& mpKeyValue){return pLeft->Interpreter(mpKeyValue) + pRight->Interpreter(mpKeyValue);}
CAddExp::~CAddExp(){cout << "~CAddExp" << endl;}
//减法解析器
CSubExp::CSubExp(CAbsExp* left, CAbsExp* right) : CSymExp(left, right){}
//把左右两个表达式运算的结果相减
int CSubExp::Interpreter(map<string, int>& mpKeyValue){return pLeft->Interpreter(mpKeyValue) - pRight->Interpreter(mpKeyValue);}
CSubExp::~CSubExp(){cout << "~CSubExp" << endl;}
//*********************************解析器封装类***************************************
CDiyCalc::CDiyCalc(){pRoot = NULL;}
void CDiyCalc::BulidTree(string exp){
if(pRoot != NULL){
DelTree(pRoot);
pRoot = NULL;
}
stack<CAbsExp*> sExp;//栈,用来暂存中间结果
CAbsExp* pLeft = NULL;
CAbsExp* pRight = NULL;
/*从左到向分析表达式(如:a+b-c),最终的语法树如下:
* -
* / \
* + c
* / \
* a b
*/
int iLen = exp.length();
for(int i = 0; i < iLen; i++){
if(exp[i] == '+'){//加法
pLeft = sExp.top(); sExp.pop();//1.先从栈中取出左操作数
pRight = new CVarExp(exp.substr(++i, 1));//2.从表达式中取出+号后面的右操作数,并生成终结符解析对象
sExp.push(new CAddExp(pLeft, pRight));//3.将左右操作数相加,并把结果放入栈中
}
else if(exp[i] == '-'){
pLeft = sExp.top(); sExp.pop(); //1.先从栈中取出左操作数
pRight = new CVarExp(exp.substr(++i, 1));//2.从表达式中取出+号后面的右操作数,并生成终结符解析对象
sExp.push(new CSubExp(pLeft, pRight)); //3.将左右操作数相减,并把结果放入栈中
}
else if(exp[i] >= 'a' && exp[i] <= 'z'){
//如果是变量(终结符):如a+b+c中的a\b\c,
//则直接生成对应的变量解析器对象
sExp.push(new CVarExp(exp.substr(i, 1)));
}
}
//栈中保存的就是最终语法树的根结点(本例为SuuExpression对象)
if(!sExp.empty()){
pRoot = sExp.top(); sExp.pop();
}
}
void CDiyCalc::DelTree(CAbsExp* exp){
CSymExp* pBranch = dynamic_cast<CSymExp*>(exp);
if(pBranch == NULL) delete pBranch;//叶子结点
else{ //分支结点
DelTree(pBranch->GetLeft()); //左子树
DelTree(pBranch->GetRight()); //右子树
delete exp;//结点
}
}
CDiyCalc::~CDiyCalc(){
if(pRoot != NULL){
DelTree(pRoot);
pRoot = NULL;
}
}
int CDiyCalc::Calc(string exp, map<string, int>& mpKeyValue){ //开始运算
BulidTree(exp);
return (pRoot == NULL) ? 0 : pRoot->Interpreter(mpKeyValue);
}
//测试客户端
void main()
{
string strExp = "a+b-c";//为简化处理,这里必须是合法的表达式
map<string, int> mpKeyValue; //相当于Interpreter模式中的Context
mpKeyValue["a"] = 1000; mpKeyValue["b"] = 2000; mpKeyValue["c"] = 400;
CDiyCalc oDiyCalc; cout << strExp << " = " << oDiyCalc.Calc(strExp, mpKeyValue) << endl;
strExp = "a+b+c";
cout << strExp << " = " << oDiyCalc.Calc(strExp, mpKeyValue) << endl;
}