意图:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
适用:
当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。
而当存在以下情况时该模式效果最好:
1、该文法相对简单。对于复杂的文法, 文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式, 这样可以节省空间而且还可能节省时间。
2、效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下, 转换器仍可用解释器模式实现, 该模式仍是有用的。
参与者:
AbstractExpression
TerminalExpression
Context
Client
效果:
1、易于改变和扩展文法;
2、易于实现文法;
3、增加了新的解释表达式的方式,可通过访问者模式创建新的解释表达式的方式;
缺点:
复杂的文法难于维护,文法非常复杂时,可使用语法分析程序或编译器生成器更合适;
实现:
1、创建抽象语法树:该模式不涉及语法分析,从而不必涉及到语法树的创建。抽象语法树的创建可以通过client,亦可由表驱动的语法分析程序生成或手写的(递归下将法)语法分析程序生成;
2、定义解释操作:解释操作并不一定要放到表达式类中,如果经常需要添加一种操作,可以将其作为一个访问者;
3、通过享元模式共享终结符:终结符通常不存储它们在抽象语法树中的位置信息;
相关:Composite[一般用其表示]、Flyweight[共享终结符点]、Iterator[迭代器遍历结点]与Visitor[实现行为]
延伸:
Microsoft:我尝试在微软的框架中寻找该模式的应用,CodeDom与XMLDocument均只是用来表示文档对象模型,并没有采用解释器模式。关于Linq中的Expression是否是采用解释器模式,我也没有仔细分析,毕竟Linq文法本身属性框架内的东西,微软没有必要将相关细节public,然而从实现上来说,完全可能基于解释器模式。在Linq中的Expression实现上,文法结构树可通过静态成员来实现,Expression本身可用来表示一个文法实例上下文(Context)。Reduce可作为解释器的一个行为。
Sysnet:
C#
下面一段C#代码用来进行将字符串中的相关替代符替换成对象或xml结点值,其语法描述大值如下:
beginstring
@#!
{.tostring()}%原义字符%
@#^
%原义字符%{xpath表达式}
#@
#@
endstring
这是其中一段代码:
public interface IStringExpressionInterpreter
{
void Interpreter(IStringContext context, ExpressionDocument document);
}
/// <summary>
/// 解析总的表达式
/// </summary>
class ExpressionInterpreter : IStringExpressionInterpreter
{
static StringInterpreter StringInterpreter = new StringInterpreter();
public void Interpreter(IStringContext context, ExpressionDocument document)
{
while (context.Current < context.String.Length)
StringInterpreter.Interpreter(context, document);
}
}
class StringInterpreter : IStringExpressionInterpreter
{
static NormalStringInterpreter NormalStringInterpreter = new NormalStringInterpreter();
static ExpressionStringInterpreter ExpressionStringInterpreter = new ExpressionStringInterpreter();
public void Interpreter(IStringContext context, ExpressionDocument document)
{
if (context.Current + 2 < context.String.Length && context.String[context.Current] == '@' && context.String[context.Current+1] == '#')
{
context.Current += 2;
ExpressionStringInterpreter.Interpreter(context,document);
context.Current += 2;
}
else
{
NormalStringInterpreter.Interpreter(context, document);
}
}
}
class NormalStringInterpreter : IStringExpressionInterpreter
{
public void Interpreter(IStringContext context, ExpressionDocument document)
{
string v = "";
while (context.Current < context.String.Length)
{
if (context.String[context.Current++] == '\\')
if (context.Current < context.String.Length)
v += context.String[context.Current];
else if (context.String[context.Current] == '@')
{
if (context.Current + 1 == context.String.Length || context.String[context.Current] != '#')
v += '@';
else
{
--context.Current;
break;
}
}
else
v += context.String[context.Current];
++context.Current;
context.CurrentNode.Add(new NormalStringNode() { Value = v });
}
}
}
上述代码并没有通过对象或xmldocument执行计算返回字符串,而是通过解析器模式生成一个表达式的dom树,并在dom树中再次利用解释器模式计算表达式树。
C++:
下面是一个解释ini文件中一个节并将其转换成相关配置类的解释器:
class CContextInterpreter
{
public:
virtual ~CContextInterpreter(void);
virtual void Interpret(ProfileStringContext & context,CMonitorProgressConfig & config) = 0 ;
};
class CSectionInterpreter :
public CContextInterpreter
{
static CKeyValueInterpreter s_keyValueInterpreter;
public:
virtual ~CSectionInterpreter(void);
virtual void Interpret(ProfileStringContext & context,CMonitorProgressConfig & config);
};
void CSectionInterpreter::Interpret(ProfileStringContext & context,CMonitorProgressConfig & config)
{
while ( context.Current < context.Length )
{
s_keyValueInterpreter.Interpret( context,config);
++ context.Current;
}
}
CKeyValueInterpreter CSectionInterpreter::s_keyValueInterpreter = CKeyValueInterpreter();
class CKeyValueInterpreter :
public CContextInterpreter
{
static CKeyStringInterpreter s_KeyStringInterpreter;
static CValueStringInterpreter s_ValueStringInterpreter;
public:
virtual void Interpret(ProfileStringContext & context,CMonitorProgressConfig & config);
};
void CKeyValueInterpreter::Interpret(ProfileStringContext & context,CMonitorProgressConfig & config)
{
config.get_Key().Empty();
s_KeyStringInterpreter.Interpret(context,config);
if ( context.Buffer[ context.Current++] == TEXT('='))
{
s_ValueStringInterpreter.Interpret(context,config);
}
}
CKeyStringInterpreter CKeyValueInterpreter::s_KeyStringInterpreter = CKeyStringInterpreter();
CValueStringInterpreter CKeyValueInterpreter:: s_ValueStringInterpreter = CValueStringInterpreter();
class CKeyStringInterpreter :
public CContextInterpreter
{
static CEscapeStringInterpreter s_EscapeStringInterpreter;
static CNormalStringInterpreter s_NormalStringInterpreter;
public:
virtual void Interpret(ProfileStringContext & context,CMonitorProgressConfig & config);
};
void CKeyStringInterpreter::Interpret(ProfileStringContext & context,CMonitorProgressConfig & config)
{
if ( context.Current == context.Length ) return;
if ( context.Buffer[context.Current] == TEXT('"'))
{
++context.Current;
s_EscapeStringInterpreter.Interpret(context,config);
}
else
s_NormalStringInterpreter.Interpret(context,config);
}
CEscapeStringInterpreter CKeyStringInterpreter::s_EscapeStringInterpreter = CEscapeStringInterpreter();
CNormalStringInterpreter CKeyStringInterpreter::s_NormalStringInterpreter = CNormalStringInterpreter();
class CNormalStringInterpreter :
public CContextInterpreter
{
public:
virtual void Interpret(ProfileStringContext & context,CMonitorProgressConfig & config);
};
void CNormalStringInterpreter::Interpret(ProfileStringContext & context,CMonitorProgressConfig & config)
{
while ( context.Current < context.Length )
{
if ( context.Buffer[context.Current] != TEXT('='))
{
config.get_Key(). AppendChar(context.Buffer[context.Current] );
++ context.Current;
}
else
break;
}
}
class CEscapeStringInterpreter :
public CContextInterpreter
{
public:
virtual void Interpret(ProfileStringContext & context,CMonitorProgressConfig & config);
};
void CEscapeStringInterpreter::Interpret(ProfileStringContext & context,CMonitorProgressConfig & config)
{
while ( context.Current < context.Length )
{
if ( context.Buffer[ context.Current++ ] == TEXT('\\'))
{
config.get_Key().AppendChar( context.Buffer[ context.Current++]);
}
else if ( context.Buffer[ context.Current++ ] == TEXT('"'))
{
return;
}
else
config.get_Key().AppendChar( context.Buffer[context.Current++]);
}
}
class CValueStringInterpreter :
public CContextInterpreter
{
static CValueStringItemInterpreter m_splitStringInterpreter;
public:
virtual void Interpret(ProfileStringContext & context,CMonitorProgressConfig & config);
};
class CValueStringItemInterpreter:
public CContextInterpreter
{
static CItemStringInterpreter s_itemStringInterpreter;
public:
virtual void Interpret(ProfileStringContext & context,CMonitorProgressConfig & config);
};
CValueStringInterpreter::CValueStringInterpreter(void)
{
}
void CValueStringInterpreter::Interpret(ProfileStringContext & context,CMonitorProgressConfig & config)
{
while (context.Current < context.Length && context.Buffer[context.Current] != TEXT('\0'))
{
m_splitStringInterpreter.Interpret(context,config);
if( context.Buffer[context.Current] != TEXT('\0')) ++context.Current;
}
}
CValueStringItemInterpreter CValueStringInterpreter::m_splitStringInterpreter = CValueStringItemInterpreter();
void CValueStringItemInterpreter::Interpret(ProfileStringContext & context,CMonitorProgressConfig & config)
{
while (context.Current < context.Length && context.Buffer[context.Current] != TEXT('\0') && context.Buffer[context.Current] != TEXT('|'))
{
config.get_Value().Empty();
s_itemStringInterpreter.Interpret(context,config);
config.Add(config.get_Key(),config.get_Value());
if ( context.Buffer[context.Current]== 0) break;
}
}
CItemStringInterpreter CValueStringItemInterpreter::s_itemStringInterpreter = CItemStringInterpreter();
通常情况下,解释器用来解释一个文法,然而上下文不必一定是字符串,亦可以是文档结构树等其它上下文。但同时也要明白文档结构树与语法树的区别。语法通常情况下是固定的,而文档结构是符合某种文法结构的文档实例的内存表示。