对于优先级较低的运算符跟在优先级较高的运算符后面的输入,C分析器无法正常工作

ecfsfe2w  于 2023-03-01  发布在  其他
关注(0)|答案(1)|浏览(123)

此代码用于在C中实现一个简单的算术表达式计算器。字符串"1+1"生成整数2"2+3*4"生成14,依此类推。运算符优先级得到尊重,并反映了C。子表达式可以用括号分组。

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
static jmp_buf jmp;
static unsigned long parse(const char **const str, const unsigned l) {
    unsigned long r;
    if (isdigit(**str))
        r = strtoul(*str, (char **)str, 0);
    else switch (*(*str)++) {
        case '+':
            return +parse(str, 6);
        case '-':
            return -parse(str, 6);
        case '~':
            return ~parse(str, 6);
        case '(':
            r = parse(str, 0);
            if (*(*str)++ == ')')
                return r;
        default:
            longjmp(jmp, errno = EINVAL);
    }
    for (;;) {
        switch (*(*str)++) {
            case '*':
                if (l <= 5)
                    r *= parse(str, 5);
                break;
            case '/':
                if (l <= 5)
                    r /= parse(str, 5);
                break;
            case '%':
                if (l <= 5)
                    r %= parse(str, 5);
                break;
            case '+':
                if (l <= 4)
                    r += parse(str, 4);
                break;
            case '-':
                if (l <= 4)
                    r -= parse(str, 4);
                break;
            case '<':
                if (l <= 3)
                    r <<= parse(str, 3);
                break;
            case '>':
                if (l <= 3)
                    r >>= parse(str, 3);
                break;
            case '&':
                if (l <= 2)
                    r &= parse(str, 2);
                break;
            case '^':
                if (l <= 1)
                    r ^= parse(str, 1);
                break;
            case '|':
                if (l <= 0)
                    r |= parse(str, 0);
                break;
            default:
                --*str;
                return r;
        }
    }
}
int main(const int argc, const char **argv) {
    while (*++argv) {
        if (setjmp(jmp)) {
            perror(*argv);
            errno = 0;
        } else if (printf("%lu\n", parse(&(const char *){*argv}, 0)) < 0)
            return errno;
    }
    return 0;
}

代码只完成了一半。"2+3*4"之类的表达式被正确解析,并按预期生成14。但是,如果优先级较低的运算符跟在优先级较高的运算符后面,如"2*3+4",丢弃较高优先级运算符的最后操作数之后的所有内容。因此,表达式"2*3+4"错误地导致6,因为+4由于某种原因被忽略了。它应该导致10。我该如何修复这个bug?
注意:I already figured out how to perform this task,但我正在尝试找出如何做到这一点,而不为每个优先级定义一个新的函数。

xqkwcwgp

xqkwcwgp1#

原始代码有几个问题:

  • switch表达式中可疑的增量,如果遇到较低优先级的运算符,则不会正确地递减。这样,前面的递归就不会看到它应该看到的较低优先级的运算符。
  • 在重新排列逻辑以解决此问题时,代码是从右到左关联的。它应该是从左到右关联的。可以通过更新优先级检查来解决此问题,以便仅在遇到较高优先级运算符而不是较高或相等优先级运算符时才递归。

沿着一些优先级较低的改进:

  • 代码应使用有符号整数,以便正确显示负数。
  • 代码应检查/%<<>>的无效操作数。
  • 代码应跳过表达式中的空格。

此修订代码按预期工作:

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <setjmp.h>
static jmp_buf jmp;
static long u(const char **);
static long d(const long r) {
    if (r)
        return r;
    longjmp(jmp, errno = EDOM);
}
static long s(const long r) {
    if (r < sizeof(long)*CHAR_BIT)
        return r;
    longjmp(jmp, errno = EDOM);
}
static long b(const char **const str, const unsigned l) {
    *str += l != 0;
    for (long r = u(str);;) {
        while (isspace(**str))
            ++*str;
        switch (**str) {
            case '*':
                if (l < 6)
                    r *= b(str, 6);
                else
                    break;
                continue;
            case '/':
                if (l < 6)
                    r /= d(b(str, 6));
                else
                    break;
                continue;
            case '%':
                if (l < 6)
                    r %= d(b(str, 6));
                else
                    break;
                continue;
            case '+':
                if (l < 5)
                    r += b(str, 5);
                else
                    break;
                continue;
            case '-':
                if (l < 5)
                    r -= b(str, 5);
                else
                    break;
                continue;
            case '<':
                if (l < 4)
                    r <<= s(b(str, 4));
                else
                    break;
                continue;
            case '>':
                if (l < 4)
                    r >>= s(b(str, 4));
                else
                    break;
                continue;
            case '&':
                if (l < 3)
                    r &= b(str, 3);
                else
                    break;
                continue;
            case '^':
                if (l < 2)
                    r ^= b(str, 2);
                else
                    break;
                continue;
            case '|':
                if (l < 1)
                    r |= b(str, 1);
                else
                    break;
                continue;
        }
        return r;
    }
}
static long u(const char **const str) {
    while (isspace(**str))
        ++*str;
    if (isdigit(**str))
        return strtoul(*str, (char **)str, 0);
    switch (*(*str)++) {
        case '+':
            return +u(str);
        case '-':
            return -u(str);
        case '~':
            return ~u(str);
        case '(': {
            register const long r = b(str, 0);
            if (*(*str)++ == ')')
                return r;
        }
    }
    longjmp(jmp, errno = EINVAL);
}
int main(const int argc, const char *const *argv) {
    while (*++argv)
        if (setjmp(jmp))
            perror(*argv);
        else {
            const long r = b(&(const char *){*argv}, 0);
            if (printf("%ld %lX\n", r, r) < 0)
                return EXIT_FAILURE;
        }
    return EXIT_SUCCESS;
}

相关问题