解釋器模式(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),而非手動實現解釋器模式。