算符优先文法——中缀表达式转换为后缀表达式并求值
根据编译原理相关知识,我们可将整个程序分为三个部分,词法分析,文法分析,后缀表达式计算,在文法分析中会涉及到运算符优先级的判断。接下来我将分模块对代码进行解释,希望可以对读者理解算符优先文法、词法分析有和后缀表达式求值有所帮助。
代码可以识别包含括号的基本四则运算和乘方运算,可以识别浮点数,未进行错误运算时的错误处理。
运算符优先级
如数组sp所示,1表示栈内优先级高于栈外优先级,-1表示栈内优先级低于栈外优先级, 0表示优先级相等,需要特别处理,99表示输入错误,符号不匹配或输入字符不可识别。
对于+、-、*、/四个运算符来说,由于运算符的左结合性,相同的符号栈内的优先级高于栈外的优先级;而幂运算具有右结合性,因此,两个^相比,栈外优先级高于栈内优先级。
对于不同的两个运算符,()中的优先级最高,幂运算高于乘除,乘除高于加减。
int SignPriority(char in, char out) {
int sp[9][9] = {{ 1, 1, -1, -1, -1, -1, 1, 1, 99}, //'+'
{ 1, 1, -1, -1, -1, -1, 1, 1, 99}, //'-'
{ 1, 1, 1, 1, -1, -1, 1, 1, 99}, //'*'
{ 1, 1, 1, 1, -1, -1, 1, 1, 99}, //'/'
{ 1, 1, 1, 1, -1, -1, 1, 1, 99}, //'^'
{-1, -1, -1, -1, -1, -1, 0, 99, 99}, //'('
{ 1, 1, 1, 1, 1, 99, 1, 1, 99}, //')'
{-1, -1, -1, -1, -1, -1, 99, 0, 99}, //'#'
{99, 99, 99, 99, 99, 99, 99, 99, 99}}; //other
// +, -, *, /, ^, (, ), #, other
return sp[Sign2Num(in)][Sign2Num(out)];
}
必要数据结构
struct Word表示词法分析识别出的一个词,flag=true表示该词为数字,flag=false表示该词为运算符。
struct Word {
bool flag;
double num;
char sign;
};
词法分析
词法分析中,可以识别浮点数,对于负数可用零减去该数。将算式以字符串读入,并在末尾补充"#",作为结束。从第一个字符开始识别,如果识别到数字,继续向后识别,直到识别到运算符,将数字作为一个词压入容器,回退一个字符。如果识别到运算符,直接作为一个次压入容器,直到识别到"#"为止。
vector<Word> LexicalAnalysis(string func) {
func = func+ "#";
int l = func.length();
vector<Word> ans;
for (int i = 0; i < l; i++) {
if (func[i] >= '0' && func[i] <= '9') {
double num = 0;
bool df = false;
double mi = 0;
for ( ; i < l; i++) {
if (!df) {
if (func[i] >= '0' && func[i] <= '9') {
num = num * 10 + static_cast<double>(func[i] - '0');
}
else if (func[i] == '.') {
df = true;
mi = 10.0;
}
}
else {
if (func[i] >= '0' && func[i] <= '9') {
num = num + (func[i] - '0') / mi;
mi *= 10;
}
}
if (!(func[i] >= '0' && func[i] <= '9') && (func[i] != '.')){
Word n;
n.flag = true;
n.num = num;
ans.push_back(n);
num = 0;
break;
}
}
i--;
}
else {
Word s;
s.flag = false;
s.sign = func[i];
if (s.sign != '#') {
ans.push_back(s);
}
}
}
return ans;
}
语法分析
在语法分析中,我们使用一个容器存储后缀表达式,顺序读出容器中的运算即为所求的后缀表达式。从词法容器中读取一个词,如果词为数字,压入后缀表达式容器中;如果该词为运算符,在比较后压入运算符栈中,在运算符栈中,预先压入"#",便于运算符优先级的比较。
每读取到一个运算符,比较该运算符与栈顶运算符的优先级,读取的运算符为栈外运算符,栈顶的运算符为栈内运算符。如果栈内运算符优先级高于栈外运算符优先级,则栈顶元素出栈,读取的运算符进栈;反之低于时,则读取的运算符直接进栈。当读取到右括号")“时,运算符栈全部出栈,直到遇到左括号”("。
在读取完全部词后,将运算符栈中的全部运算符出栈。所有出栈的运算符存入后缀表达式容器中。
vector<Word> ParseAnalysis(vector<Word> lpword) {
stack<Word> SignStack;
vector<Word> ans;
Word fst;
fst.flag = false;
fst.sign = '#';
SignStack.push(fst);
for (Word w : lpword) {
if (w.flag) {
ans.push_back(w);
}
else {
if (w.sign == ')') {
while (!SignStack.empty()) {
if (SignStack.top().sign == '(') {
SignStack.pop();
break;
}
ans.push_back(SignStack.top());
SignStack.pop();
}
}
else if (SignPriority(SignStack.top().sign, w.sign) > 0) {
ans.push_back(SignStack.top());
SignStack.pop();
SignStack.push(w);
}
else {
SignStack.push(w);
}
}
}
while (!SignStack.empty()) {
if (SignStack.top().sign != '#') {
ans.push_back(SignStack.top());
}
SignStack.pop();
}
return ans;
}
后缀表达式计算
在后缀表达式运算中,读取到数字压入数字栈中,读取到运算符,从数据栈中取出两个元素进行计算,将计算结果压回数字栈中,读取完全部元素后,数字栈中的元素即为运算结果。
double Calculatoin(vector<Word> paword) {
stack<double> ans;
for (Word w : paword) {
if (w.flag) {
ans.push(w.num);
}
else {
double a, b;
b = ans.top(); ans.pop();
a = ans.top(); ans.pop();
switch (w.sign) {
case '+':
ans.push(a + b);
break;
case '-':
ans.push(a - b);
break;
case '*':
ans.push(a * b);
break;
case '/':
ans.push(a / b);
break;
case '^':
ans.push(pow(a, b));
break;
}
}
}
return ans.top();
}
完整代码请点击下方链接:
完整代码Github链接