算符优先文法——中缀表达式转换为后缀表达式并求值

算符优先文法——中缀表达式转换为后缀表达式并求值

根据编译原理相关知识,我们可将整个程序分为三个部分,词法分析,文法分析,后缀表达式计算,在文法分析中会涉及到运算符优先级的判断。接下来我将分模块对代码进行解释,希望可以对读者理解算符优先文法、词法分析有和后缀表达式求值有所帮助。

代码可以识别包含括号的基本四则运算和乘方运算,可以识别浮点数,未进行错误运算时的错误处理。

运算符优先级

如数组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链接

如果代码中有疏漏之处,请各位在评论区中指出,欢迎大家留言讨论。
发布了9 篇原创文章 · 获赞 2 · 访问量 319

猜你喜欢

转载自blog.csdn.net/Z_Pythagoras/article/details/104743280