解釋器模式實例分析


 

模式適用環境

在以下情況下可以使用解釋器模式:

·可以將一個需要解釋執行的語言中的句子表示為一個抽象語法樹。

·一些重復出現的問題可以用一種簡單的語言來進行表達。

·文法較為簡單。

·效率不是關鍵問題。

·一些重復發生的事情包含固定的一系列操作類型,比較適合用解釋器模式來實現。

解決問題

·加減乘除四則運算,但是公式每次都不同,比如可配置,有時是a + b - c x d,有時是a x b + c - d,等等等等個,公式千變萬化,但是都是由加減乘除四個非終結符來連接的,這時我們就可以使用解釋器模式。

·解釋器模式在使用面向對象語言實現的編譯器中得到了廣泛的應用,如Smalltalk語言的編譯器。

·目前有一些基於Java抽象語法樹的源代碼處理工具,如在Eclipse中就提供了Eclipse AST,它是Eclipse JDT的一個重要組成部分,用來表示Java語言的語法結構,用戶可以通過擴展其功能,創建自己的文法規則。

·可以使用解釋器模式,通過C++JavaC#等面向對象語言開發簡單的編譯器,如數學表達式解析器、正則表達式解析器等,用於增強這些語言的功能,使之增加一些新的文法規則,用於解釋一些特定類型的語句。

 

解決方案

·文法規則定義語言

文法規則實例:

expression ::= value | symbol

symbol ::= expression '+' expression | expression '-' expression

value ::= an integer //一個整數值

在文法規則定義中可以使用一些符號來表示不同的含義,如使用|”表示或,使用“{”和“}”表示組合,使用“*”表示出現0次或多次等,其中使用頻率最高的符號是表示 或關系的“|” 。

·抽象語法數定義語言

除了使用文法規則來定義一個語言,在解釋器模式中還可以通過一種稱之為抽象語法樹(Abstract Syntax Tree, AST)的圖形方式來直觀地表示語言的構成,每一棵抽象語法樹對應一個語言實例。抽象語法樹描述了如何構成一個復雜的句子,通過對抽象語法樹的分析,可以識別出語言中的終結符和非終結符類。抽象語法樹描述了如何構成一個復雜的句子,通過對抽象語法樹的分析,可以識別出語言中的終結符和非終結符類。在解釋器模式中,每一種終結符和非終結符都有一個具體類與之對應,正因為使用類來表示每一個語法規則,使得系統具有較好的擴展性和靈活性。

實例

實例:數學運算解釋器

現需要構造一個語言解釋器,使得系統可以執行整數間的乘、除和求模運算。如用戶輸入表達式3 * 4 / 2 % 4”,輸出結果為2。使用解釋器模式實現該功能。

 

 

 

 

解釋器模式實例與解析

實例:數學運算解釋器

//行為型模式:解釋器模式
//場景:四則運算
 
#include <iostream>
#include <string>
#include <map>
#include <stack>
#include <typeinfo>
 
using namespace std;
 
//*******************************************抽象表達式類***********************************
class Expression
{
public:
    //解析公式和數值,其中var中的key是公式中的參數,value值是具體的數字
    //如a = 100; b = 20; c = 40
    virtual int interpreter(map<string, int>& var) = 0;
    virtual ~Expression(){};
};
 
 
//變量解析器(終結符表達式)
class VarExpression : public Expression
{
    string key;
public:
    VarExpression(string key)
    {
        this->key = key;
    }
 
    //從map中取出變量的值
    int interpreter(map<string, int>& var)
    {
        return var[key];
    }
 
    ~VarExpression()
    {
        cout << "~VarExpression()" << endl;
    }
};
//**********抽象運算符號解析器***********************
//抽象運算符號解析器
class SymbolExpression : public Expression
{
protected:
    Expression* left;
    Expression* right;
public:
    SymbolExpression(Expression* left, Expression* right)
    {
        this -> left = left;
        this -> right = right;
    }
 
    Expression* getLeft()
    {
        return left;
    }
    Expression* getRight()
    {
        return right;
    }
};
 
//加法解析器
class AddExpression : public SymbolExpression
{
public:
    AddExpression(Expression* left, Expression* right): SymbolExpression(left,right)
    {
    }
 
    //把左右兩個表達式運算的結果加起來
    int interpreter(map<string, int>& var)
    {
        return left->interpreter(var) + right ->interpreter(var);
    }
    ~AddExpression()
    {
        cout << "~AddExpression()" << endl;
    }
};
 
//減法解析器
class SubExpression : public SymbolExpression
{
public:
    SubExpression(Expression* left, Expression* right): SymbolExpression(left,right)
    {
    }
 
    //把左右兩個表達式運算的結果相減
    int interpreter(map<string, int>& var)
    {
        return left->interpreter(var) - right ->interpreter(var);
    }
 
    ~SubExpression()
    {
        cout << "~SubExpression()" << endl;
    }
};
 
//*********************************解析器封裝類***************************************
//解析器封裝類,這個類是根據迪米特法則進行封裝,目的是讓Client只與直接朋友打交道,相當於Facade
class Calculator
{
private:
    Expression* expression;
public:
    //構造函數傳參,並解析表達式,構建語法樹
    Calculator(string expStr)
    {
        expression = NULL;
 
        //棧,用來暫存中間結果
        stack<Expression*> stkExp;
 
        Expression* left  = NULL;
        Expression* right = NULL;
 
        /*從左到向分析表達式(如:a+b-c),最終的語法樹如下:
         *           -
         *         /   \
         *       +     c
         *     /   \
         *    a     b
        */
        for(unsigned int i = 0; i< expStr.length(); i++)
        {
            switch(expStr[i])
            {
            case '+':  //加法
                //1.先從棧中取出左操作數
                left = stkExp.top();
                stkExp.pop();
 
                //2.從表達式中取出+號后面的右操作數,並生成終結符解析對象
                right = new VarExpression(expStr.substr(++i,1));
 
                //3.將左右操作數相加,並把結果放入棧中
                stkExp.push(new AddExpression(left, right));
 
                break;
 
            case '-':
                //1.先從棧中取出左操作數
                left = stkExp.top();
                stkExp.pop();
 
                //2.從表達式中取出+號后面的右操作數,並生成終結符解析對象
                right = new VarExpression(expStr.substr(++i,1));
 
                //3.將左右操作數相減,並把結果放入棧中
                stkExp.push(new SubExpression(left, right));
 
                break;
 
            default:
                //如果是變量(終結符):如a+b+c中的a\b\c,
                //則直接生成對應的變量解析器對象
                stkExp.push(new VarExpression(expStr.substr(i,1)));
            }
        }
 
        //棧中保存的就是最終語法樹的根結點(本例為SuuExpression對象)
        if(!stkExp.empty())
        {
            expression = stkExp.top();
            stkExp.pop();
        }
    }
 
    void deltree(Expression* expression)
    {
        SymbolExpression* branch = dynamic_cast<SymbolExpression*>(expression);
        //葉子結點
        if (branch == NULL)
        {
            delete expression;
        }
        else  //分支結點
        {
            //左子樹
            deltree(branch->getLeft());
 
            //右子樹
            deltree(branch->getRight());
 
            //結點
            delete expression;
        }
    }
 
    ~Calculator()
    {
        deltree(expression);
        expression = NULL;
    }
 
    //開始運算
    int run(map<string, int>& var)
    {
        return (expression == NULL) ? 0 : expression->interpreter(var);
    }
};
int main()
{
    string expStr = "a+b-c"; //為簡化處理,這里必須是合法的表達式
 
    map<string, int> var;   //相當於Interpreter模式中的Context
    var["a"] = 100;
    var["b"] = 20;
    var["c"] = 40;
 
    Calculator cal(expStr);
 
    cout <<"運算結果為:" << expStr << " = " << cal.run(var) << endl;
    return 0;
}
/*
運算結果為:a+b-c = 80
~VarExpression()
~VarExpression()
~AddExpression()
~VarExpression()
~SubExpression()
*/

 

 
 

 

語法樹構建:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM