多变量Calc Bison

时间:2014-02-22 01:31:15

标签: calculator bison flex-lexer

我一直在使用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();
}

2 个答案:

答案 0 :(得分:2)

三个快速观察:

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)的打印,但您也可以解决这个问题。)

答案 1 :(得分:0)

使用右递归很容易做到这一点,遗憾的是,它在野牛中并不是非常有效:

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

或者,左递归解决方案:

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

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