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

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

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;
}
posted @ 2012-10-27 16:37  ay27  阅读(253)  评论(0编辑  收藏  举报