解释器(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

限制

  1. 这个例子只能算int,如果使用模板函数扩充类型,将会导致类膨胀
  2. 使用了大量的循环和递归调用,效率需要权衡

适用场景

  1. 对简单的语法进行解析,且易于扩展
  2. 比如:实现正则表达式…

相关模式

  1. 复合模式(Composite):抽象语法树是一个复合模式的实例
  2. 享元模式(FlyWeight):说明了如何在抽象语法树中共享终结符
  3. 迭代器(Iterator):迭代遍历抽象语法树
  4. 访问器(Visitor):可以用来在一个类中维护抽象语法树中各节点(子类)的行为

PS: Windows编译编码问题

由于Windows-MSVC中的utf8是加了BOM的,但是CLion默认编码是没有BOM的utf8。解决方法有如下:

  1. 最简单的方法,编译选项中加入/utf-8,CMake中这样:
cmake_minimum_required(VERSION 3.24)

project(InterpreterDemo)

add_executable(InterpreterDemo main.cpp)

target_compile_options(InterpreterDemo
    PRIVATE /utf-8
)
  1. 在使用utf-8的源文件中加入编译宏,长这样
#pragma execution_character_set("utf-8")
  1. 在CLion底栏的UTF-8中选择ADD BOM,手动添加BOM
  2. 不要写中文注释…