一个递归下降的数字表达式解析器
何谓数字表达式?形如这样的东西:
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; }