解释器模式C++
解释器模式(Interpreter Pattern)是一种行为型设计模式,它用于定义一种语言的语法规则,并构建一个解释器来解释该语言中的句子。这种模式适用于需要处理固定语法规则的场景,如表达式解析、配置文件解析等。
解释器模式的核心角色
- AbstractExpression(抽象表达式):声明解释操作的接口,通常包含一个
interpret()
方法 - TerminalExpression(终结符表达式):实现与语法规则中终结符相关的解释操作
- NonterminalExpression(非终结符表达式):实现与语法规则中非终结符相关的解释操作,通常包含其他表达式
- Context(上下文):包含解释器之外的一些全局信息
- Client(客户端):构建表示特定句子的抽象语法树,调用解释操作
C++实现示例
以下以"简单算术表达式解析"为例实现解释器模式,支持加法、减法运算和数字解析:
#include <iostream>
#include <string>
#include <memory>
#include <map>
#include <cctype>
#include <stdexcept>// 前向声明
class Expression;// 上下文:存储变量值和表达式字符串
class Context {
private:std::map<std::string, int> variables; // 变量映射表std::string expression; // 表达式字符串size_t position; // 当前解析位置public:Context(std::string expr) : expression(std::move(expr)), position(0) {}// 设置变量值void setVariable(const std::string& name, int value) {variables[name] = value;}// 获取变量值int getVariable(const std::string& name) const {auto it = variables.find(name);if (it != variables.end()) {return it->second;}throw std::runtime_error("变量未定义: " + name);}// 获取当前字符char currentChar() const {if (position < expression.size()) {return expression[position];}return '\0';}// 移动到下一个字符void nextChar() {if (position < expression.size()) {position++;}}// 跳过空白字符void skipWhitespace() {while (std::isspace(currentChar())) {nextChar();}}// 解析标识符(变量名)std::string parseIdentifier() {std::string id;skipWhitespace();if (!std::isalpha(currentChar())) {throw std::runtime_error("无效的标识符");}while (std::isalnum(currentChar())) {id += currentChar();nextChar();}skipWhitespace();return id;}// 解析数字int parseNumber() {int num = 0;skipWhitespace();if (!std::isdigit(currentChar())) {throw std::runtime_error("无效的数字");}while (std::isdigit(currentChar())) {num = num * 10 + (currentChar() - '0');nextChar();}skipWhitespace();return num;}// 获取当前位置size_t getPosition() const {return position;}// 设置当前位置void setPosition(size_t pos) {position = pos;}
};// 抽象表达式
class Expression {
public:virtual ~Expression() = default;virtual int interpret(Context& context) const = 0;
};// 终结符表达式:数字
class NumberExpression : public Expression {
private:int number;public:explicit NumberExpression(int num) : number(num) {}int interpret(Context& /*context*/) const override {return number;}
};// 终结符表达式:变量
class VariableExpression : public Expression {
private:std::string name;public:explicit VariableExpression(std::string varName) : name(std::move(varName)) {}int interpret(Context& context) const override {return context.getVariable(name);}
};// 非终结符表达式:加法
class AddExpression : public Expression {
private:std::unique_ptr<Expression> left;std::unique_ptr<Expression> right;public:AddExpression(std::unique_ptr<Expression> l, std::unique_ptr<Expression> r): left(std::move(l)), right(std::move(r)) {}int interpret(Context& context) const override {return left->interpret(context) + right->interpret(context);}
};// 非终结符表达式:减法
class SubtractExpression : public Expression {
private:std::unique_ptr<Expression> left;std::unique_ptr<Expression> right;public:SubtractExpression(std::unique_ptr<Expression> l, std::unique_ptr<Expression> r): left(std::move(l)), right(std::move(r)) {}int interpret(Context& context) const override {return left->interpret(context) - right->interpret(context);}
};// 解析器:构建抽象语法树
class Parser {
private:// 解析因子(数字或变量)std::unique_ptr<Expression> parseFactor(Context& context) {char c = context.currentChar();if (c == '(') {context.nextChar(); // 跳过 '('auto expr = parseExpression(context);if (context.currentChar() != ')') {throw std::runtime_error("缺少右括号");}context.nextChar(); // 跳过 ')'return expr;} else if (std::isalpha(c)) {// 变量std::string varName = context.parseIdentifier();return std::make_unique<VariableExpression>(varName);} else if (std::isdigit(c)) {// 数字int num = context.parseNumber();return std::make_unique<NumberExpression>(num);}throw std::runtime_error("语法错误: 意外字符 '" + std::string(1, c) + "'");}// 解析项(处理乘法和除法,这里简化只处理加减)std::unique_ptr<Expression> parseTerm(Context& context) {auto expr = parseFactor(context);while (true) {char c = context.currentChar();if (c != '+' && c != '-') {break;}context.nextChar(); // 跳过运算符auto rhs = parseFactor(context);if (c == '+') {expr = std::make_unique<AddExpression>(std::move(expr), std::move(rhs));} else {expr = std::make_unique<SubtractExpression>(std::move(expr), std::move(rhs));}}return expr;}public:// 解析表达式std::unique_ptr<Expression> parseExpression(Context& context) {return parseTerm(context);}
};// 客户端代码
int main() {try {// 测试1:纯数字表达式Context context1("10 + 20 - 5");Parser parser;auto expr1 = parser.parseExpression(context1);std::cout << "10 + 20 - 5 = " << expr1->interpret(context1) << std::endl;// 测试2:带变量的表达式Context context2("a + b - c");context2.setVariable("a", 15);context2.setVariable("b", 30);context2.setVariable("c", 10);auto expr2 = parser.parseExpression(context2);std::cout << "a + b - c = " << expr2->interpret(context2) << std::endl;// 测试3:带括号的表达式Context context3("(x + y) - (z + 5)");context3.setVariable("x", 20);context3.setVariable("y", 10);context3.setVariable("z", 5);auto expr3 = parser.parseExpression(context3);std::cout << "(x + y) - (z + 5) = " << expr3->interpret(context3) << std::endl;} catch (const std::exception& e) {std::cerr << "错误: " << e.what() << std::endl;}return 0;
}
代码解析
-
抽象表达式(
Expression
):- 定义了
interpret()
方法作为解释操作的接口 - 所有具体表达式都必须实现这个方法
- 定义了
-
终结符表达式:
NumberExpression
:解释数字常量,直接返回存储的数值VariableExpression
:解释变量,从上下文获取变量值
-
非终结符表达式:
AddExpression
:解释加法运算,计算左右两个表达式的和SubtractExpression
:解释减法运算,计算左右两个表达式的差
-
上下文(
Context
):- 存储表达式字符串、当前解析位置和变量映射表
- 提供解析辅助方法(如跳过空白、解析标识符和数字)
-
解析器(
Parser
):- 负责将输入的表达式字符串转换为抽象语法树(由各种表达式对象组成)
- 使用递归下降法解析,支持数字、变量、加减运算和括号
解释器模式的优缺点
优点:
- 易于扩展新的语法规则,只需添加新的表达式类
- 语法规则清晰地体现在表达式类的层次结构中
- 便于实现简单的语法解释器,逻辑明确
缺点:
- 对于复杂语法,表达式类的数量会急剧增加,导致系统庞大
- 复杂语法的解释效率可能较低
- 难以维护复杂的语法规则
适用场景
- 当需要处理简单的语法规则时(如配置文件解析、简单表达式计算)
- 当语法规则可以表示为抽象语法树时
- 当需要频繁添加新的语法规则时
常见应用:
- 表达式计算器(如数学表达式、布尔表达式)
- 配置文件解析器
- 简单的脚本语言解释器
- 正则表达式引擎
解释器模式通过将语法规则分解为一系列表达式对象,实现了语法的灵活解析。但对于复杂语法,通常会选择更专业的解析工具(如ANTLR),而非手动实现解释器模式。