目录
解释器模式(Interpreter Pattern)是一种【行为型】设计模式,它定义了一种语言的语法表示,并提供一个解释器来解释该语言中的句子。这种模式将语言中的每个语法规则映射为一个类,使得可以通过组合这些类来解析和执行复杂的表达式。
一、模式核心概念与结构
解释器模式包含四个核心角色:
- 抽象表达式(Expression):定义解释操作的接口,所有表达式类都必须实现该接口。
- 终结符表达式(Terminal Expression):实现与语法中的终结符相关的解释操作,是表达式的基本构建块。
- 非终结符表达式(Non-terminal Expression):包含其他表达式作为子表达式,并定义它们之间的解释关系。
- 上下文(Context):包含解释器需要的全局信息,在解释过程中传递。
二、C++ 实现示例:简单算术表达式解释器
以下是一个简单的算术表达式解释器示例,支持加法和乘法:
#include <iostream>
#include <string>
#include <memory>
#include <unordered_map>
#include <vector>
#include <sstream>
// 抽象表达式
class Expression {
public:
virtual ~Expression() = default;
virtual int interpret(const std::unordered_map<std::string, int>& context) const = 0;
};
// 终结符表达式:变量
class VariableExpression : public Expression {
private:
std::string name;
public:
VariableExpression(const std::string& n) : name(n) {}
int interpret(const std::unordered_map<std::string, int>& context) const override {
auto it = context.find(name);
if (it != context.end()) {
return it->second;
}
return 0;
}
};
// 终结符表达式:常量
class ConstantExpression : public Expression {
private:
int value;
public:
ConstantExpression(int val) : value(val) {}
int interpret(const std::unordered_map<std::string, int>& context) const override {
return value;
}
};
// 非终结符表达式:加法
class AddExpression : public Expression {
private:
std::shared_ptr<Expression> left;
std::shared_ptr<Expression> right;
public:
AddExpression(std::shared_ptr<Expression> l, std::shared_ptr<Expression> r)
: left(l), right(r) {}
int interpret(const std::unordered_map<std::string, int>& context) const override {
return left->interpret(context) + right->interpret(context);
}
};
// 非终结符表达式:乘法
class MultiplyExpression : public Expression {
private:
std::shared_ptr<Expression> left;
std::shared_ptr<Expression> right;
public:
MultiplyExpression(std::shared_ptr<Expression> l, std::shared_ptr<Expression> r)
: left(l), right(r) {}
int interpret(const std::unordered_map<std::string, int>& context) const override {
return left->interpret(context) * right->interpret(context);
}
};
// 解析器
class Parser {
public:
static std::shared_ptr<Expression> parse(const std::string& input) {
// 简单实现:假设输入格式为 "变量 + 变量" 或 "变量 * 变量"
std::istringstream iss(input);
std::string token1, op, token2;
iss >> token1 >> op >> token2;
std::shared_ptr<Expression> left, right;
// 判断是变量还是常量
if (isdigit(token1[0])) {
left = std::make_shared<ConstantExpression>(std::stoi(token1));
} else {
left = std::make_shared<VariableExpression>(token1);
}
if (isdigit(token2[0])) {
right = std::make_shared<ConstantExpression>(std::stoi(token2));
} else {
right = std::make_shared<VariableExpression>(token2);
}
// 根据操作符创建相应的表达式
if (op == "+") {
return std::make_shared<AddExpression>(left, right);
} else if (op == "*") {
return std::make_shared<MultiplyExpression>(left, right);
}
return nullptr; // 无效表达式
}
};
// 客户端代码
int main() {
// 上下文:变量赋值
std::unordered_map<std::string, int> context = {
{"x", 5},
{"y", 3}
};
// 解析并解释表达式
std::string expr1 = "x + y";
std::string expr2 = "x * y";
std::string expr3 = "5 + y";
auto expression1 = Parser::parse(expr1);
auto expression2 = Parser::parse(expr2);
auto expression3 = Parser::parse(expr3);
std::cout << expr1 << " = " << expression1->interpret(context) << std::endl;
std::cout << expr2 << " = " << expression2->interpret(context) << std::endl;
std::cout << expr3 << " = " << expression3->interpret(context) << std::endl;
return 0;
}
三、解释器模式的关键特性
- 语法表示:
- 每个语法规则都被表示为一个类,便于维护和扩展。
- 递归解释:
- 非终结符表达式通过递归组合其他表达式来解释复杂语句。
- 上下文传递:
- 解释过程中使用上下文对象存储和传递状态信息。
- 扩展性:
- 易于添加新的语法规则(表达式类),符合开闭原则。
四、应用场景
- 领域特定语言(DSL):
- 实现简单的脚本语言、配置文件解析器。
- 例如,正则表达式引擎、SQL 查询解析器。
- 数学表达式计算:
- 计算器、公式编辑器等需要解析和计算表达式的场景。
- 自然语言处理:
- 简单的语义分析、关键词提取。
- 编译器前端:
- 词法分析、语法分析阶段的实现。
- 规则引擎:
- 解释和执行业务规则(如 IF-THEN 条件)。
五、解释器模式与其他设计模式的关系
- 组合模式:
- 解释器模式通常使用组合模式来构建表达式树。
- 非终结符表达式是组合节点,终结符表达式是叶节点。
- 访问者模式:
- 可以结合访问者模式来分离解释逻辑和表达式结构。
- 通过访问者模式可以在不修改表达式类的情况下添加新的解释操作。
- 状态模式:
- 解释器的上下文可以使用状态模式来管理解释过程中的不同状态。
六、C++ 标准库中的解释器模式应用
- 正则表达式库:
std::regex
是解释器模式的典型应用,将正则表达式语法解释为匹配逻辑。
- 字符串流:
std::stringstream
可用于解析简单的格式化字符串。
- 数值转换函数:
std::stoi
、std::stof
等函数可看作是简单的解释器。
七、优缺点分析
优点:
- 语法灵活:易于修改和扩展语法规则。
- 可维护性:每个语法规则对应一个类,代码结构清晰。
- 可扩展性:新增语法规则只需添加新的表达式类。
缺点:
- 类数量膨胀:每个语法规则都需要一个类,复杂语法会导致类过多。
- 解析效率低:递归解释可能导致性能问题,尤其对复杂语法。
- 调试困难:表达式树的构建和解释过程可能难以理解和调试。
八、实战案例:简单 SQL 查询解释器
以下是一个简单 SQL 查询解释器的实现示例,支持 SELECT-FROM-WHERE 语句:
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <unordered_map>
#include <regex>
// 数据模型
struct Record {
std::unordered_map<std::string, std::string> fields;
std::string get(const std::string& field) const {
auto it = fields.find(field);
return it != fields.end() ? it->second : "";
}
bool matches(const std::string& field, const std::string& value) const {
return get(field) == value;
}
};
using Table = std::vector<Record>;
// 抽象表达式
class SQLExpression {
public:
virtual ~SQLExpression() = default;
virtual Table interpret(const std::unordered_map<std::string, Table>& context) const = 0;
};
// 终结符表达式:表名
class TableExpression : public SQLExpression {
private:
std::string tableName;
public:
TableExpression(const std::string& name) : tableName(name) {}
Table interpret(const std::unordered_map<std::string, Table>& context) const override {
auto it = context.find(tableName);
if (it != context.end()) {
return it->second;
}
return {}; // 空表
}
};
// 非终结符表达式:WHERE子句
class WhereExpression : public SQLExpression {
private:
std::shared_ptr<SQLExpression> tableExpr;
std::string field;
std::string value;
public:
WhereExpression(std::shared_ptr<SQLExpression> table,
const std::string& f, const std::string& v)
: tableExpr(table), field(f), value(v) {}
Table interpret(const std::unordered_map<std::string, Table>& context) const override {
Table result;
Table table = tableExpr->interpret(context);
for (const auto& record : table) {
if (record.matches(field, value)) {
result.push_back(record);
}
}
return result;
}
};
// 非终结符表达式:SELECT子句
class SelectExpression : public SQLExpression {
private:
std::shared_ptr<SQLExpression> tableExpr;
std::vector<std::string> columns;
public:
SelectExpression(std::shared_ptr<SQLExpression> table,
const std::vector<std::string>& cols)
: tableExpr(table), columns(cols) {}
Table interpret(const std::unordered_map<std::string, Table>& context) const override {
Table result;
Table table = tableExpr->interpret(context);
// 简化实现:这里只是演示,实际应只选择指定列
return table;
}
};
// SQL解析器
class SQLParser {
public:
static std::shared_ptr<SQLExpression> parse(const std::string& sql) {
// 简化实现:使用正则表达式匹配简单的SELECT-FROM-WHERE语句
std::regex selectPattern(R"(SELECT\s+(\w+(?:,\s*\w+)*)\s+FROM\s+(\w+)(?:\s+WHERE\s+(\w+)\s*=\s*'([^']+)')?)");
std::smatch matches;
if (std::regex_search(sql, matches, selectPattern)) {
// 提取表名
std::string tableName = matches[2].str();
auto tableExpr = std::make_shared<TableExpression>(tableName);
// 处理WHERE子句(如果有)
std::shared_ptr<SQLExpression> whereExpr = tableExpr;
if (matches.size() > 3 && !matches[3].str().empty()) {
std::string field = matches[3].str();
std::string value = matches[4].str();
whereExpr = std::make_shared<WhereExpression>(tableExpr, field, value);
}
// 处理SELECT子句
std::string columnsStr = matches[1].str();
std::vector<std::string> columns;
std::istringstream iss(columnsStr);
std::string column;
while (std::getline(iss, column, ',')) {
columns.push_back(column);
}
return std::make_shared<SelectExpression>(whereExpr, columns);
}
return nullptr; // 无效SQL
}
};
// 客户端代码
int main() {
// 数据库上下文
std::unordered_map<std::string, Table> database;
// 创建用户表
Table usersTable;
usersTable.push_back({{"id", "1"}, {"name", "Alice"}, {"age", "25"}});
usersTable.push_back({{"id", "2"}, {"name", "Bob"}, {"age", "30"}});
usersTable.push_back({{"id", "3"}, {"name", "Charlie"}, {"age", "35"}});
database["users"] = usersTable;
// 解析并执行SQL查询
std::string sql1 = "SELECT name, age FROM users";
std::string sql2 = "SELECT * FROM users WHERE name = 'Bob'";
auto expr1 = SQLParser::parse(sql1);
auto expr2 = SQLParser::parse(sql2);
// 执行查询
Table result1 = expr1->interpret(database);
Table result2 = expr2->interpret(database);
// 输出结果
std::cout << "Result of query 1:" << std::endl;
for (const auto& record : result1) {
std::cout << "Name: " << record.get("name")
<< ", Age: " << record.get("age") << std::endl;
}
std::cout << "\nResult of query 2:" << std::endl;
for (const auto& record : result2) {
std::cout << "ID: " << record.get("id")
<< ", Name: " << record.get("name")
<< ", Age: " << record.get("age") << std::endl;
}
return 0;
}
九、实现注意事项
- 语法复杂度:
- 对于复杂语法,考虑使用词法分析器(如 Flex)和语法分析器(如 Bison)工具。
- 解释器性能:
- 递归解释可能导致性能问题,可考虑使用编译技术(如将表达式编译为中间代码)。
- 错误处理:
- 实现健壮的错误处理机制,捕获并报告语法错误。
- 上下文管理:
- 设计清晰的上下文对象,避免在解释过程中传递过多参数。
解释器模式是 C++ 中实现自定义语言解释的重要工具,通过将语法规则映射为类层次结构,使系统更具灵活性和可扩展性,特别适合需要自定义表达式或简单语言解析的场景。