解释器模式是一种行为型设计模式,它用于定义语言的文法表示,并提供解释器来处理该文法中的句子。在C++中实现特定领域语言的语法解析时,解释器模式是非常合适的选择,能够将语法规则拆解为一个个独立的解释单元,降低整体解析逻辑的复杂度。

解释器模式核心结构
解释器模式主要包含以下几个核心角色:
- 抽象表达式角色:定义解释操作的接口,所有具体表达式都需要实现这个接口
- 终结符表达式角色:对应文法中的终结符,比如特定领域语言中的数值、变量等基础元素
- 非终结符表达式角色:对应文法中的非终结符,比如加减乘除等运算规则
- 上下文角色:包含解释器需要的全局信息,比如变量的值存储
- 客户端角色:构建抽象语法树,调用解释器执行解析
特定领域语言示例设计
我们设计一个简单的数学运算特定领域语言,支持变量定义和加减乘除运算,语法规则如下:
- 变量定义:var name = value
- 运算表达式:expr op expr,其中op可以是+、-、*、/
- 变量引用:直接使用变量名
核心代码实现
抽象表达式接口
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <sstream>
#include <stdexcept>
// 上下文类,存储变量值
class Context {
private:
std::unordered_map<std::string, double> variables;
public:
void setVariable(const std::string& name, double value) {
variables[name] = value;
}
double getVariable(const std::string& name) const {
auto it = variables.find(name);
if (it != variables.end()) {
return it->second;
}
throw std::runtime_error("未定义的变量: " + name);
}
};
// 抽象表达式接口
class Expression {
public:
virtual ~Expression() = default;
virtual double interpret(const Context& context) const = 0;
};
终结符表达式实现
// 数值表达式,终结符
class NumberExpression : public Expression {
private:
double value;
public:
explicit NumberExpression(double val) : value(val) {}
double interpret(const Context& context) const override {
return value;
}
};
// 变量表达式,终结符
class VariableExpression : public Expression {
private:
std::string name;
public:
explicit VariableExpression(const std::string& varName) : name(varName) {}
double interpret(const Context& context) const override {
return context.getVariable(name);
}
};
非终结符表达式实现
// 加法表达式,非终结符
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) {}
double interpret(const Context& context) const override {
return left->interpret(context) + right->interpret(context);
}
};
// 减法表达式,非终结符
class SubtractExpression : public Expression {
private:
std::shared_ptr<Expression> left;
std::shared_ptr<Expression> right;
public:
SubtractExpression(std::shared_ptr<Expression> l, std::shared_ptr<Expression> r)
: left(l), right(r) {}
double interpret(const Context& 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) {}
double interpret(const Context& context) const override {
return left->interpret(context) * right->interpret(context);
}
};
// 除法表达式,非终结符
class DivideExpression : public Expression {
private:
std::shared_ptr<Expression> left;
std::shared_ptr<Expression> right;
public:
DivideExpression(std::shared_ptr<Expression> l, std::shared_ptr<Expression> r)
: left(l), right(r) {}
double interpret(const Context& context) const override {
double divisor = right->interpret(context);
if (divisor == 0) {
throw std::runtime_error("除数不能为0");
}
return left->interpret(context) / divisor;
}
};
语法解析与客户端调用
// 简单的语法解析器,构建抽象语法树
class Parser {
public:
static std::shared_ptr<Expression> parseExpression(const std::string& expr, const Context& context) {
// 简化处理,仅支持两个数的加法示例
std::istringstream iss(expr);
std::string token;
std::shared_ptr<Expression> leftExpr;
std::string op;
std::shared_ptr<Expression> rightExpr;
// 解析左操作数
if (iss >> token) {
try {
double num = std::stod(token);
leftExpr = std::make_shared<NumberExpression>(num);
} catch (...) {
leftExpr = std::make_shared<VariableExpression>(token);
}
}
// 解析运算符
if (iss >> op) {
// 解析右操作数
if (iss >> token) {
try {
double num = std::stod(token);
rightExpr = std::make_shared<NumberExpression>(num);
} catch (...) {
rightExpr = std::make_shared<VariableExpression>(token);
}
}
// 根据运算符构建对应表达式
if (op == "+") {
return std::make_shared<AddExpression>(leftExpr, rightExpr);
} else if (op == "-") {
return std::make_shared<SubtractExpression>(leftExpr, rightExpr);
} else if (op == "*") {
return std::make_shared<MultiplyExpression>(leftExpr, rightExpr);
} else if (op == "/") {
return std::make_shared<DivideExpression>(leftExpr, rightExpr);
} else {
throw std::runtime_error("不支持的运算符: " + op);
}
}
return leftExpr;
}
};
int main() {
Context context;
// 定义变量
context.setVariable("a", 10);
context.setVariable("b", 20);
// 解析并执行表达式
std::string expr1 = "a + b";
auto expression1 = Parser::parseExpression(expr1, context);
std::cout << "表达式 " << expr1 << " 的结果: " << expression1->interpret(context) << std::endl;
std::string expr2 = "a * 5";
auto expression2 = Parser::parseExpression(expr2, context);
std::cout << "表达式 " << expr2 << " 的结果: " << expression2->interpret(context) << std::endl;
std::string expr3 = "b / 4";
auto expression3 = Parser::parseExpression(expr3, context);
std::cout << "表达式 " << expr3 << " 的结果: " << expression3->interpret(context) << std::endl;
return 0;
}
模式优缺点分析
使用解释器模式实现特定领域语言语法解析的优势在于:
- 易于扩展语法规则,新增表达式类型只需要新增对应的表达式类即可
- 每个表达式的职责单一,符合单一职责原则,便于维护
- 抽象语法树结构清晰,能够直观反映语法规则
同时该模式也存在一定的局限性:
- 对于复杂的文法,会生成大量的表达式类,增加系统复杂度
- 递归解释的过程效率相对较低,不适合对性能要求极高的场景
- 语法解析器的实现较为复杂,需要处理词法分析、语法分析等多个环节
实际应用场景
解释器模式结合特定领域语言语法解析适合用在以下场景:
- 需要为特定业务场景设计简化配置语言时
- 实现简单的脚本语言或规则引擎时
- 需要解析和执行自定义表达式的业务系统中
- SQL解析、正则表达式引擎等基础工具的简化实现