解释器(Interpreter)
解释器模式属于设计模式::行为模式的一种,特别的,属于行为类模式。
行为模式涉及到算法和对象间职责的分配。行为模式不仅描述对象或类的模式,还描述他们之间的通信模式(协作)。
行为模式分为两种:
- 行为类模式:使用继承机制在类间分配行为。
- 行为对象模式:使用对象复合而不是继承。
一言以蔽之
为了对某种规范进行解析,子类(抽象语法树上的节点)实现父类的虚接口,把某种规范变成抽象语法树(调用堆栈),使用循环或递归调用接口。注意:子类可以调用不同类型的子类以建立抽象语法树。
举例
#include <iostream>
#include <map>
#include <stack>
class Expression {
public:
virtual int interpreter(std::map<char, int>) = 0;
virtual ~Expression() = default;
};
// 下面这一行的在Clion下编译不过去,结果发现Clion的编码是没有Bom的UTF-8...解决办法:....太长了,放下面
// 变量表达式
class VarExpression : public Expression {
char m_key;
public:
explicit VarExpression(const char& key) : m_key(key) {}
int interpreter(std::map<char, int> var) override {
return var[m_key];
}
};
// 符号表达式
class SymbolExpression : public Expression {
protected:
// 左右两个运算符
Expression* m_left;
Expression* m_right;
public:
SymbolExpression(Expression* left, Expression* right) : m_left(left), m_right(right) {}
};
// 加法
class AddExpression : public SymbolExpression {
public:
explicit AddExpression(Expression* left, Expression* right) : SymbolExpression(left, right) {}
int interpreter(std::map<char, int> var) override {
return m_left->interpreter(var) + m_right->interpreter(var);
}
};
// 减法
class SubExpression : public SymbolExpression {
public:
explicit SubExpression(Expression* left, Expression* right) : SymbolExpression(left, right) {}
int interpreter(std::map<char, int> var) override {
return m_left->interpreter(var) - m_right->interpreter(var);
}
};
Expression* analyse(const std::string& expStr) {
std::stack<Expression*> expStack;
Expression* left{ nullptr };
Expression* right{ nullptr };
for (int i = 0; i < expStr.size(); ++i) {
switch (expStr[i]) {
case '+':
left = expStack.top();
right = new VarExpression(expStr[++i]);
expStack.push(new AddExpression(left, right)); // 加法
break;
case '-':
left = expStack.top();
right = new VarExpression(expStr[++i]);
expStack.push(new SubExpression(left, right)); // 加法
break;
default:
expStack.push(new VarExpression(expStr[i]));
}
}
Expression* expression = expStack.top();
return expression;
}
void release(Expression* expression)
{
// 堆栈需要从根节点开始删,效率是问题,并且调试困难
}
int main() {
std::string expStr{ "a+b-c+d" };
std::map<char, int> var;
var.insert(std::make_pair('a', 5));
var.insert(std::make_pair('b', 6));
var.insert(std::make_pair('c', 7));
var.insert(std::make_pair('d', 8));
Expression* expression = analyse(expStr);
int result = expression->interpreter(var);
std::cout << "result: " << result << std::endl; // 12
return 0;
}
运行结果:
result: 12
限制
- 这个例子只能算
int,如果使用模板函数扩充类型,将会导致类膨胀 - 使用了大量的循环和递归调用,效率需要权衡
适用场景
- 对简单的语法进行解析,且易于扩展
- 比如:实现正则表达式…
相关模式
- 复合模式(Composite):抽象语法树是一个复合模式的实例
- 享元模式(FlyWeight):说明了如何在抽象语法树中共享终结符
- 迭代器(Iterator):迭代遍历抽象语法树
- 访问器(Visitor):可以用来在一个类中维护抽象语法树中各节点(子类)的行为
PS: Windows编译编码问题
由于Windows-MSVC中的utf8是加了BOM的,但是CLion默认编码是没有BOM的utf8。解决方法有如下:
- 最简单的方法,编译选项中加入
/utf-8,CMake中这样:
cmake_minimum_required(VERSION 3.24)
project(InterpreterDemo)
add_executable(InterpreterDemo main.cpp)
target_compile_options(InterpreterDemo
PRIVATE /utf-8
)
- 在使用utf-8的源文件中加入编译宏,长这样
#pragma execution_character_set("utf-8")
- 在CLion底栏的
UTF-8中选择ADD BOM,手动添加BOM - 不要写中文注释…