行为型模式--解释器
1、意图
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
2、结构
3、参与者
AbstractExpression:声明一个抽象的解释操作,这个接口为抽象语法树中所有节点所共享。
TerminalExpression:实现与文法中的终结符相关联的解释操作;一个句子中的每个终结符需要该类的一个实例。
NonterminalExpression:对文法中的每一条规则R ::= R1R2...Rn都需要一个 NonterminalExpression类;为从R1到Rn的每个符合都维护一个AbstractExpression类型的实例变量;为文法中的非终结符实现解释(Interpret)操作。解释一般要递归地调用表示R1到Rn的那些对象的解释操作。
Context:包含解释器之外的一些全局信息。
Client:构建(或被给定)表示该文法定义的语言中的一个特定的句子的抽象语法树,该抽象语法树由NonterminalExpression和TerminalExpression的实例装配而成;调用解释操作。
4、适用性
当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:
该文法简单。对于复杂的文法,文法的类层次变得庞大而无法管理,不适合用解释器模式,此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式,这样可以节省空间还可能节省时间;
不要求效率。最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下,转换器仍可用解释器模式实现,该模式仍是有用的。
5、代码示例
场景:对布尔表达式进行操作和求值。在这个语言中终结符是布尔变量,即常量true和false。非终结符表示包含运算符and,or和not的布尔表达式。文法定义如下:
AndExp ::= BooleanExp 'and' BooleanExp OrExp ::= BooleanExp 'or' BooleanExp NotExp ::= 'not' BooleanExp Constant ::= 'true' | 'false' VariableExp := 'A' | 'B' | ... | 'X' | 'Y' | 'Z'
代码如下:
// BooleanExp为所有定义一个布尔表达式的类定义了一个接口 class BooleanExp { public: BooleanExp(); virtual ~BooleanExp(); virtual bool Evaluate(Context&)= 0; virtual BooleanExp* Replace(const char*,BooleanExp&)=0; virtual BooleanExp* Copy()const =0; }; // 类Context定义从变量到布尔值的一个映射,这些布尔值我们可用C++中的常量true和false来表示。Context有以下接口 class Context { public: bool Lookup(const char*) const; void Assign(VariableExp*, bool); };
// 一个VariableExp表示一个有名变量 class VariableExp : public BooleanExp { public: VariableExp(const char*); virtual ~VariableExp(); virtual bool Evaluate(Context&); virtual BooleanExp* Replace(const char*,BooleanExp&); virtual BooleanExp* Copy()const; private: char* _name; }; // 构造器将变量的名字作为参数 VariableExp::VariableExp (const char* name) { _name = strdup(name); } // 求一个变量的值,返回它在当前上下文中的值。 bool VariableExp::Evaluate (Context& aContext) { return aContext.Lookup(_name); } // 拷贝一个变量返回一个新的VariableExp BooleanExp* VariableExp::Copy ()const { return new VariableExp(_name); } // 在用一个表达式替换一个变量时,我们检查该待替换变量是否就是本对象代表的变量 BooleanExp* VariableExp::Replace( const char* name, BooleanExp& exp) { if(strcmp(name, _name) == 0) { return exp.Copy(); } else { return new VariableExp(_name); } }
// AndExp表示由两个布尔表达式与操作得到的表达式 class AndExp : public BooleanExp { public: AndExp(BooleanExp*, BooleanExp*); virtual ~AndExp(); virtual bool Evaluate(Context&); virtual BooleanExp* Replace(const char*, BooleanExp&); virtual BooleanExp* Copy() const; private: BooleanExp* _operand1; BooleanExp* _operand2; }: AndExp::AndExp (BooleanExp* op1, BooleanExp*op2) { _operand1 op1; _operand2 op2; } // 一个AndExp的值求是它的操作数的值的逻辑“与”。 bool AndExp::Evaluate (Context& acontext) { return _operandl->Evaluate(acontext)&& _operand2->Evaluate(aContext); } // AndExp的Copy和Replace操作将递归调用它的操作数的Copy和Replace操作 BooleanExp* AndExp::Copy () const { return new AndExp(_operand1->Copy(), _operand2->Copy ()); } BooleanExp* AndExp::Replace (const char* name, BooleanExp& exp) { return new AndExp(_operand1->Replace(name, exp), _operand2->Replace(name, exp)); }
// 现在我们可以定义布尔表达式 //(true and x) or (y and (not x)) // 并对给定的以true或false赋值的x和y求这个表达式值: BooleanExp* expression; Context context; VariableExp* x = new VariableExp("X"); VariableExp* y = new VariableExp("Y"); expression = new OrExp( new AndExp(new Constant(true), x), new AndExp(y, new NotExp(x)) ); context.Assign(x, false); context.Assign(y, true); // 对x和y的这一赋值,求得该表达式值为true。 bool result = expression->Evaluate(context);
// 要对其它赋值情况求该表达式的值仅需改变上下文对象即可。 // 最后,我们可用一个新的表达式替换变量y,并重新求值: VariableExp* z = new VariableExp(Z"); NotExp not_z(z); BooleanExp* replacement = expression->Replace("Y", not_z); context.Assign(z, true); result = replacement->Evaluate(context);
6、总结
解释器模式主要用在文法分析场景,且针对的是简单的文法规则,对于复杂的文法则难以维护。在解释器中,每个语法规则至少要定义一个类,如果规则太多,类的个数也会急剧增加,导致系统难以管理维护,此时可以用语法分析程序代替解释器模式;
解释器模式执行效率低,通常在解释器中需要使用大量的循环和递归调用来解析复杂的句子。
本文来自博客园,作者:流翎,转载请注明原文链接:https://www.cnblogs.com/hjx168/p/16220001.html