我一直在使用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();
}
答案 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在评论中指出的那样,上面简化的语法改变了程序的语义,因为它将打印出赋值表达式的值;在原文中,赋值声明是静音的。这很容易修复,但有点单调乏味。我没有在上面的答案中这样做,因为它分散了解决方案的结构。
最简单的解决方法是复制作业生产,作为statement
和expression
的一部分;这导致减少/减少冲突,野牛将警告你,但由于野牛喜欢文件中较早的减少,它将产生正确的结果。许多人(包括我)会认为在生产代码中依赖该规则是丑陋且难以维护的。
另一种选择是将表达式分为“安静”和“响亮”表达式,这些表达式具有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是关于野牛递归的一些有用信息。