BISON 一元减去模运算

BISON unary minus with modulo operations

我正在用模运算实现一个简单的计算器(环 Z_p,所以我有 {0, 1, 2, ..., p - 1},其中 p 是质数)。如果输入是 -x 我想在输出上打印正确的值 p-x 。我的语法:

exp: num { std::cout <<  << " "; $$ = ; }
     | '-' exp %prec NEG { $$ = negate(); os << "~ "; } // for expressions

num:
        MINUS NUMBER %prec NEG { $$ = negate(); }
        | NUMBER { $$ = ; }

以前我有 NUMBER,现在 numexp。然后打印 x 而不是 p-x。我没有看到任何其他方法可以做到这一点 - 我怎么知道下一步我是否会有终端?所以我想到了这个。然而,它给了我关于 reduction/reduction 冲突的警告(好吧,我明白为什么)但它给出了正确的结果。

我的问题是:我怎样才能做得更好?我可以去掉警告但仍然有我想要的吗?

编辑: 假设 p= 7。 然后,如果我写 -(8+5),我希望输出(反向波兰表示法)为 1 5 + 作为 8mod7=1。这可以通过 exp 中的第二条规则来处理。 但是如果我写 -5 那么我想显示 2 因为它是正确的值。这不能仅在 exp 中使用规则来完成,因为我在那里打印了确切的数字:

exp:
    NUMBER { std::cout <<  << " "; $$ = ; } /* value of rule is  */
    | exp PLUS exp { $$ = add(, ); std::cout << "+ "; }
    | MINUS exp %prec NEG { $$ = negate(); }
    ;

这些规则不符合我的要求,所以我想出了上面介绍的解决方法。

我猜你想做的是这个:

  1. 接受算术表达式作为输入,使用带符号的整数操作数和代数运算符的常用范围,包括一元取反。

  2. 通过要求一元否定的参数不是整数文字来避免歧义。

  3. 对环Zp中的每个表达式求值,对于一个给定的素数 p.

  4. 此外,对于每个表达式,在反向抛光中打印出等价物,所有整数操作数标准化模 p

这是本题与一般计算器不同的最后一个要求。通常,建议是放弃尝试区分一元否定运算符的不同用途,因为这是不必要的。

但如果你真的想区分这两种用途,你可以这样做。您只需要编写更明确的规则。这意味着放弃否定运算符的运算符优先级,因为该规则需要明确定义可能的操作数。

首先,我们有两种写数字常量的方法,这两种方法都需要打印出常量(标准化)值。

num : NUMBER        { $$ = ring.normal(); std::cout << $$ << ' '; }
negnum
    : '-' NUMBER    { $$ = ring.negate(ring.normal()); std::cout << $$ << ' '; }

为什么我把它写成两个不同的非终结符?因为下一步是确定哪些句法结构可以作为一元否定运算符的操作数。 num 不能,因为 -3 必须被解析为 negnum 并打印为 4。但是 negnum 可以(大概)是一元否定的操作数; --3 应打印为 4 ~(假设 p == 7)。所以他们必须分开。其他可以作为一元负数操作数的东西是括号表达式和应用一元负数的结果。 (另外,也许,变量,如果有这样的东西。问题中不清楚,所以我将它们排除在外):

neg : negnum
    | '('  expr ')' { $$ = ; }
    | '-' neg       { $$ = ring.negate(); std::cout << "~ "; }

完成后,我们现在可以编写表达式的语法了。对于这部分,我们可以正常使用优先级(虽然级联语法也很简单):

%left '+' '-' ;
%left '*' '/' ;

expr: num
    | neg
    | expr '+' expr { $$ = ring.add(, ); std::cout << "+ "; }
    | expr '-' expr { $$ = ring.sub(, ); std::cout << "- "; }
    | expr '*' expr { $$ = ring.mul(, ); std::cout << "* "; }
    | expr '/' expr { $$ = ring.div(, ); std::cout << "/ "; }

测试(使用顶级生产打印每个表达式的评估结果):

$ ./ringcalc 7
> 3
3 = 3
> 3+4
3 4 + = 0
> 3-4
3 4 - = 6
> 3+4*5
3 4 5 * + = 2
> 3*4+5
3 4 * 5 + = 3
> -3
4 = 4
> --3
4 ~ = 3
> -(8+5)
1 5 + ~ = 1