【问题标题】:Multiple Variables Calc Bison多变量计算野牛
【发布时间】:2014-02-22 01:31:14
【问题描述】:

我一直在使用 Flex 和 Bison 开发计算器。我可以单独分配变量,例如

m = 5
m * 5
25

但现在我希望能够分配多个变量,例如m = n = 5

这是我现在的野牛文件。我将如何将其修改为上述内容?

%{
 #include <stdio.h>
 void yyerror(char *);
 int yylex(void);
 int sym[26];
%}
%token INTEGER VARIABLE
%left '+' '-' '*'
%%
program:
 program statement '\n'
 | /* NULL */
 ;
statement:
 expression { printf("%d\n", $1); }
 | VARIABLE '=' expression { sym[$1] = $3; }
 ;
expression:
 INTEGER
 | VARIABLE { $$ = sym[$1]; }
 | '-' expression { $$ = -$2; }
 | expression '+' expression { $$ = $1 + $3; }
 | expression '-' expression { $$ = $1 - $3; }
 | expression '*' expression { $$ = $1 * $3; }
 | '(' expression ')' { $$ = $2; }
 ;
%%
void yyerror(char *s) {
 fprintf(stderr, "%s\n", s);
}
int main(void) {
 yyparse();
}

【问题讨论】:

    标签: calculator bison flex-lexer


    【解决方案1】:

    三个快速观察:

    1) 您将+-* 设置为具有相同的优先级,这意味着

     1 + 2 * 3
    

    将被解析为好像它是

     (1 + 2) * 3   (9)
    

    而正常的解释是

     1 + (2 * 3)   (7)
    

    2) 实际上,赋值可以被视为表达式的另一种情况,其中= 运算符的优先级低于任何其他运算符,并且关联到右侧。

    3) 最后,您应该为一元减号设置优先级。更多解释请参见bison manual

    进行这些更改会产生以下简单语法:

    %token INTEGER VARIABLE
    %right '='
    %left '+' '-'
    %left '*'
    %right UNOP
    %%
    
    program:
     program statement '\n'
     | /* NULL */
     ;
    statement:
     expression { printf("%d\n", $1); }
     | /* EMPTY; added to allow empty lines */
     ;
    expression:
     INTEGER
     | VARIABLE                  { $$ = sym[$1]; }
     | VARIABLE '=' expression   { $$ = sym[$1] = $3; }  /* CHANGED; return result */
     | '-' expression %prec UNOP { $$ = -$2; }           /* CHANGED; add precedence */
     | expression '+' expression { $$ = $1 + $3; }
     | expression '-' expression { $$ = $1 - $3; }
     | expression '*' expression { $$ = $1 * $3; }
     | '(' expression ')'        { $$ = $2; }
     ;
    

    请注意,expression 用于上述规则中的左递归和右递归上下文。这很正常。尽管野牛手册中关于右递归的说法相当危言耸听,但当语言的语义表明时,编写右递归规则实际上是完全合理的。 (如果没有语义差异,最好使用左递归以避免使用解析器堆栈,但使用解析器堆栈很少有问题。)

    正如 Chris Dodd 在评论中指出的那样,上述简化语法改变了程序的语义,因为它会打印出赋值表达式的值;在最初,赋值语句是静音的。这很容易解决,但有点乏味。我没有在上面的答案中这样做,因为它分散了解决方案的结构。

    最简单的解决方法是复制分配产生式,作为statementexpression 的一部分;这会导致减少/减少冲突,野牛会警告您,但由于野牛更喜欢文件中较早的减少,它将产生正确的结果。许多人(包括我)会认为在生产代码中依赖该规则是丑陋且难以维护的。

    另一种选择是将表达式分为“安静”和“大声”表达式,它们对于statement 具有不同的单位产生式。这看起来更像是原始语法,但优先级是固定的。

    另一种选择是构建抽象语法树 (AST),而不是立即执行程序。执行 AST 时,如果顶部节点是赋值,您可以轻松地禁止打印结果。 (与安静/响亮的解决方案不同,简单的 AST 实现会抑制简单赋值 a = 3 和明显的表达式赋值 (a = 3) 的打印,但您也可以解决这个问题。)

    【讨论】:

    • 一个小注意——这确实改变了程序的行为,导致它在赋值语句之后打印分配的值。
    • @chrisDodd:是的。我本来要提的,但最后我不得不去做别的事情。我会添加注释。
    【解决方案2】:

    使用右递归很容易做到这一点,不幸的是,这在野牛中效率不高:

    statement:
      expression {}
      | assignment {}
      ;
    assigment:
      VARIABLE '=' assignment {}
      | VARIABLE '=' expression {}
      ;
    

    或者,左递归解决方案:

    statement:
      expression {}
      | assignment {}
      ;
    assigment:
      recursive_assignment '=' expression {}
      ;
    recursive_assignment:
      recursive_assignment '=' VARIABLE {}
      | VARIABLE  {}
      ;
    

    Here 是有关野牛递归的一些有用信息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多