• 一个递归下降的数字表达式解析器


    何谓数字表达式?形如这样的东西:

    1+2*3-4/5-3^2+(3-1)

    为简单起见,我的程序只包括以下符号:

    数字0~9,+-*/%^(),当然还有正负号   (注意^符号是次方的意思,比如说2^3,指2的3次方,%指求余符号)

    对于一个数字表达式,必定有以下规则:

    1.对于二目运算符,即+-*/^,运算符的左右两边必须是数字或括号;

    2.对于单目运算符,即正负号,运算符的左边不能是数字或括号,右边必须是数字或括号;

    3.括号内部运算完成后必定产生一个数字,因此括号的两边(指一对括号的两边),必须是运算符;

    4.运算顺序必须正确,即符合数学习惯上的运算顺序;

    5.除数不能为0;

    6.。。。。。

    简单起见,我把这解析器写成了类的形式,并且假设所有的输入都符合上面的规则。

    这是我定义的parser类:

    class parser{
    private:
        //表达式的结尾标记符
        string EOE;
    
        string exp;  //保存表达式的一个副本
        string token;  //保存当前待处理的字符串
        int exp_id;   //保存当前处理的位置
    
    public:
        double begin(string str);  //解析表达式的入口,同时初始化类中的变量 
        double work1();  //处理加法和减法
        double work2();  //处理乘法除法和求余
        double work3();  //处理乘方
        double work4();  //处理正负号
        double work5();  //处理括号
        double work();  //返回数字
        void get_token();  //寻找下一组字符
        bool isdelim(char c);   //判断是否为运算符
        bool isnumber(char c);    //判断是否为数字
    };

    注意到,处理的顺序是按照运算符的运算顺序进行的。

    主程序中调用parser类是直接调用begin方法的,其他的方法由begin调用。

    这是主程序:

    #include <iostream>
    #include <string>
    #include "parser.h";
    using namespace std;
    
    int main()
    {
        cout<<"Enter an expression:"<<endl;
        string s;
        parser p; 
        cin>>s;
        while (s!="")      //当输入空行时退出
        {
            cout<<p.begin(s)<<endl;   //调用begin方法
            cin>>s;
        }
        return 0;
    }

    现在的主要问题是,parser类的实现。

    为什么叫递归下降?

    这样说,当每遇到一个运算符(假设运算符的左边已经完成计算并且产生了一个数),数学规律上运算符的右边是一个数字,那么,运算符的右边必须进行递归求解,递归的结果是产生一个新数,来参与当前运算符的运算。

    首先,由get_token()产生第一组字符,这组字符可能是正负号,可能是数字,可能是括号。此时,exp_id移到第一组字符后的一个字符上。然后对运算符右边进行递归求解。

    当然,递归求解过程中必须按照运算符顺序调用!

    一系列的work函数不是最关键的地方,关键在于get_token函数的编写,因为work函数只需按照数学习惯直接编写即可。调用get_token函数来返回一个数字串或者一个运算符,同时exp_id随之移动。

    其他的看代码:

    double parser::begin(string str)
    {
        exp_id=0;
        exp=str;
        
        get_token();
        
        return work1();
    }
    
    double parser::work1()
    {
        char op;
        double result1,result2;
    
        result1=work2();
        
        while ((op=token[0]) =='+' || op=='-')
        {
            get_token(); 
            result2=work2();
            if (op=='+')
                result1+=result2;
            else if (op=='-')
                result1-=result2;
        }
        return result1;
    }
    
    double parser::work2()
    {
        char op;
        double result1,result2;
        
        result1=work3();
    
        while ((op=token[0])=='*' || op=='/')
        {
            get_token();
            result2=work3();
            
            if (op=='*') result1*=result2;
            else result1/=result2;
        }
    
        return result1;
    }
    
    double parser::work3()
    {
        double result1,ex,result2;
        
        result1=work4();
    
        if (token=="^") {
            get_token();
            result2=work4();
            ex=result1;
    
            if (result2==0) result1=1;
            else
                for (int i=1;i<int(result2);i++)
                    result1*=ex;
        }
        return result1;
    }
    
    double parser::work4()
    {
        double result;
        string op;
    
        op="";
        if (token=="+" || token=="-")
        {
            op=token;
            get_token();
        }
        result=work5();
    
        if (op=="-") return -result;
        else return result;
    }
    
    double parser::work5()
    {
        double result;
    
        if (token=="(") {
            get_token();
            result=work1();
            get_token();
        }
        else result=work();
    
        return result;
    }
    
    double parser::work()
    {
        bool flag=true;
        double result=0,tt=0.1;
        for (int i=0;i<token.length();i++)
        {
            if (!flag)
            {
                result+=tt*(token[i]-'0');
                tt=tt/10;
            }
            else{
                if (token[i]=='.'){ flag=false; continue;}
                result=result*10+token[i]-'0';
            }
        }
        get_token();
        return result;
    }
    
    void parser::get_token()
    {
        token="";
    
        if (exp_id==exp.length())
        {
            token=EOE;
            return;
        }
        
        while (exp_id<=exp.length() && exp[exp_id]==' ')
            exp_id++;
        
        if (exp_id>exp.length()) 
        {
            token=EOE;
            return;
        }
    
        if (isdelim(exp[exp_id]))
        {
            token+=exp[exp_id];
            exp_id++;
        }
        else if (isnumber(exp[exp_id]))
        {
            while (exp_id<=exp.length() && isnumber(exp[exp_id]))
            {
                token+=exp[exp_id];
                exp_id++;
            }
        }
    }
    
    bool parser::isdelim(char c)
    {
        if (c=='+'||c=='-'||c=='*'||c=='/'||c=='^' || c=='(' || c==')')
            return true;
        else return false;
    }
    
    bool parser::isnumber(char c)
    {
        if (c-'0'>=0 && c-'0'<=9 || c=='.') return true;
        else return false;
    }
  • 相关阅读:
    210
    209
    208
    207
    定时任务crontab
    Python的zip与*zip函数的应用
    Python的reduce函数与map函数
    解析:cpu与io密集在何场景适合使用多进程,多线程,协程
    Python上下文(转载)
    C10K与C10M的问题
  • 原文地址:https://www.cnblogs.com/ay27/p/2742601.html
Copyright © 2020-2023  润新知