设计模式之解析器(Interpreter)的C++实现

2023-09-17 13:01:16

1、解析模式的提出

在软件开发的过程中,需要实现一种需求,该需求的结构稳定,但是需求的业务内容会频繁变化,如果使用普通语法实现需求,需要经常更新代码,不具有灵活性。可以使用解析器模式解决实现该类需求,解析器是实现一种能够解析该需求结构语法的功能代码,针对不同的业务调用对应的解析规则代码。

2、需求描述

有一个字符串的加减计算,如'a+b+c-d-e',其中a,b,c,d,e分别对应带计算的值。设计一个功能代码,可以实现该功能,且该代码可以适配不定个数的加减数运算。

3、功能实现

(1)UML图如下:

 (2)代码实现如下:

#include <iostream>
#include <map>
#include <stack>

class AbsExpression
{
public:
    bool isVarType = false;
public:
    void   setExpressionType(bool bFlag){isVarType= bFlag;};
    virtual int interpreter(std::map<char,int>var)=0;
    virtual ~AbsExpression(){};
};

//变量表达式
class ValExpression:public AbsExpression
{
private:
    char m_key;
public:
    ValExpression(const char&key)
    {
        std::cout << "ValExpression " << std::endl;
        this->m_key = key;
        setExpressionType(true);
    }

    int interpreter(std::map<char, int> var) override
    {
        return var[m_key];
    }
    ~ValExpression()
    {
     std::cout << "~ValExpression " << std::endl;
    }
};

//符号表达式
class SymbolExpression:public AbsExpression
{
protected:
    AbsExpression* m_left;
    AbsExpression* m_right;

public:
    SymbolExpression(AbsExpression* left,AbsExpression* right):
        m_left(left),m_right(right)
    {
    }
    ~SymbolExpression()
    {
        //left对象是直接取的栈容器的上一个top,故可以显式清理。
        //right的new对象不会直接push进栈容器,而是做了符号实例对象的成员变量。故需要对其进行对象释放
        if(m_right != nullptr)
        {
            delete m_right;
            m_right = nullptr;
        }
        std::cout << "~SymbolExpression" << std::endl;
    }
};

//加法运算符
class AddExpression:public SymbolExpression
{
public:
    AddExpression(AbsExpression* left,AbsExpression* right):
        SymbolExpression(left,right){
        std::cout << "AddExpression " << std::endl;
    }

    int interpreter(std::map<char,int>var)
    {
        return m_left->interpreter(var) + m_right->interpreter(var);
    }
};
//减法运算符
class SubExpression:public SymbolExpression
{
public:
    SubExpression(AbsExpression* left,AbsExpression* right):
        SymbolExpression(left,right){
        std::cout << "SubExpression " << std::endl;
    }

    int interpreter(std::map<char,int>var)
    {
        return m_left->interpreter(var) - m_right->interpreter(var);
    }
};

std::stack<AbsExpression*> analyse(std::string strLanguage){
    std::stack<AbsExpression*>stackExp;
    AbsExpression* left = nullptr;
    AbsExpression* right = nullptr;

    for(size_t i = 0; i < strLanguage.length();i++)
    {
        switch (strLanguage[i]) {
        case '+':
        {
            left = stackExp.top();
            right = new ValExpression(strLanguage[++i]);
            stackExp.push(new AddExpression(left,right));
        }break;
        case '-':
        {   left = stackExp.top();
            right = new ValExpression(strLanguage[++i]);
            stackExp.push(new SubExpression(left,right));

        }break;
        default:
            stackExp.push(new ValExpression(strLanguage[i]));

        }
    }
    return stackExp;
};

void release( std::stack<AbsExpression*> exp)
{
    while (exp.size() > 0) {
       AbsExpression* it =  exp.top();
       delete it;
       it = nullptr;
       exp.pop();
    }
    std::cout << "clear exp finshed " << std::endl;
}
class Client
{
public:
    void doWork()
    {
        std::string str = "a+b+c-d";
        std::map<char,int>mapVar;
        mapVar.insert(std::make_pair('a',1));
        mapVar.insert(std::make_pair('b',2));
        mapVar.insert(std::make_pair('c',3));
        mapVar.insert(std::make_pair('d',4));
        std::stack<AbsExpression*> exp = analyse(str);
        int ret = exp.top()->interpreter(mapVar);

        std::cout << "ret is " << ret << std::endl;
        release(exp);
    }
};

int main()
{
    Client obj;
    obj.doWork();
    return 0;
}

运行结果如下:

注意:解析器适用于比较简单的语法解析,对于复杂的语法,可以使用语法分析生成器工具,具体的语法分析生成器用法可自行搜索查看。

更多推荐

C++ STL库 list(链表)

C++Lists(链表)赋值(assign)语法:voidassign(input_iteratorstart,input_iteratorend);voidassign(size_typenum,constTYPE&val);assign()函数以迭代器start和end指示的范围为list赋值或者为list赋值nu

HTML5 游戏开发实战 | 黑白棋

黑白棋,又叫反棋(Reversi)、奥赛罗棋(Othello)、苹果棋、翻转棋。黑白棋在西方和日本很流行。游戏通过相互翻转对方的棋子,最后以棋盘上谁的棋子多来判断胜负。黑白棋的棋盘是一个有8×8方格的棋盘。开始时在棋盘正中有两白两黑四个棋子交叉放置,黑棋总是先下子。(1)下子规则。把自己颜色的棋子放在棋盘的空格上,而当

Docker基础入门:Docker基础总结篇--超详细

Docker基础入门:Docker基础总结篇[docker3要素、docker安装配置、容器使用、镜像管理发布]一、Docker3要素1.1、镜像(Image)1.2、容器(Container)1.3、仓库(Registry)1.4、总结二、Docker安装配置2.1、卸载旧版Docker2.2、需要的安装包依赖2.3

【容器】docker基础使用

文章目录一、docker常见命令二、注意事项Reference一、docker常见命令docker是一个容器化平台。Docker介绍:(官网:https://www.docker.com/get-started)Docker是一个开源的应用容器引擎,你可以把它当作一个轻量级的虚拟机。它可以让开发者打包他们的应用以及依赖

Learn Prompt-基础用法

基本法则​相比于搜索引擎,ChatGPT的优势在于其高效的想法关联和信息归纳能力。在进一步讲解提示的构建思路前,我希望你可以了解到两点通用的经验法则,用来提高生成AI模型的输出质量。其中包括尝试提示的多种表述以获得最佳结果使用清晰简短的提示,避免不必要的词语减少不精确的描述当然,这并不是所有的经验法则。我们希望的是,你

Qt浏览器模块的几点说明

Qt5.6以前用的是webkit,Qt5.6版本以后分两种情况,一种是mingw编译器(windows系统)对应的Qt库不再提供浏览器模块。Qt5.6以后的版本在linux系统和mac等系统,都不存在没有浏览器控件的情况,都使用的是webengine。仅仅是windows上的mingw编译器的Qt版本没有,其他系统其实

【线性回归、岭回归、Lasso回归分别预测患者糖尿病病情】数据挖掘实验一

Ⅰ、项目任务要求任务描述:将“diabetes”糖尿病患者数据集划分为训练集和测试集,利用训练集分别结合线性回归、岭回归、Lasso回归建立预测模型,再利用测试集来预测糖尿病患者病情并验证预测模型的拟合能力。具体任务要求如下:搜集并加载“diabetes”患者糖尿病指数数据集。定义训练集和测试集(训练集和测试集比例分别

并发编程系列-AQS

AbstractQueuedSynchronizer(AQS)是一个抽象队列同步器,它用于构建依赖于先进先出(FIFO)等待队列的阻塞锁和相关同步器的框架。该类的目的在于提供基本功能的封装,适用于大多数需要使用单个原子int值表示同步状态的同步器。举例来说,ReentrantLock、Semaphore以及Future

《计算机视觉中的多视图几何》笔记(3)

3ProjectiveGeometryandTransformationsof3D这章主要讲的是3D的射影几何,与2D的射影几何差不多。主要区别是:3D射影几何对偶的是点和平面,直线是自对偶的。3D空间中直线有4个自由度,这一现象并不是那么容易直接得出。一种方法是把直线用正交平面两个交点表示。文章目录3Projecti

【Rust 基础篇】Rust 父trait:扩展和组织trait的继承体系

导言Rust是一种以安全性和高效性著称的系统级编程语言,其设计哲学是在不损失性能的前提下,保障代码的内存安全和线程安全。在Rust中,trait是一种用于抽象类型行为的机制。有时候,我们需要在一个trait的基础上扩展更多的行为,或者将多个trait组合起来形成一个更大的trait继承体系。这时,Rust的父trait

vue3 - 前端 Vue 项目提交GitHub 使用Actions自动化部署

GitHubDemo地址在线预览参考文章使用GithubActions发布Vue网站到GithubPage使用GithubActions将Vue项目部署到GithubPages前端使用githubpages部署自己的网站GitHubActions自动化部署前端项目指南前言vue前端项目写好之后,想部署到线上通过在线地址

热文推荐