减少野牛的订单
Reduce order in Bison
我正在使用 Bison 编写一个编译器,并且我正在尝试将相同的函数与 while 和 repeat 循环一起使用。我需要解析器在“statements_off
”之前减少“condition
”。 ¿我可以指定减少订单吗?
这是循环的代码:
loop_stmt:
WHILE line condition DO ENDL line statements_off DONE
{ $$ = process_while($<elem>3, $<elem>7, $<ival>2, $<ival>6); }
| REPEAT ENDL line statements_off UNTIL line condition
{ $$ = process_not($<elem>7);
$$ = process_while($$, $<elem>4, $<ival>6, $<ival>3); }
| FOR for_condition DO ENDL statements_off DONE
{ $$ = process_for($<elem>5, $<ival>2); }
;
condition: '(' expr_bool_or ')' { validate_bool_condition($<elem>2); $$ = $<elem>2; }
;
野牛的减少是自下而上和从左到右评估的。这是算法的重要组成部分。
这给了你一点自由度——有时你可以写一个语法,这样自下而上的归约会产生从右到左的效果——但你真的不应该这样做。当您解析为 AST(理想情况下独立于动作评估的顺序)然后根据需要和方便地遍历 AST 时,语义分析效果最佳。
通过将语言直接线性化为三地址代码来编写一次性编译器,这看起来像是一种简化或优化。但它最终会感觉更像一件紧身衣,因为很难将设计用于生成树的过程转变为线性控制流。
一次性编译器——我想到了 Lua 编译器——通常最终不得不重新排序生成的代码块,这既是因为这个问题的构造,也是为了实现非窥孔优化。
(将 while
和 for
语句中的预测试打乱到循环末尾实际上是很常见的,这似乎与这里考虑的相反。)
要清楚这些可能性,请考虑以下两者之间的区别:
expressions:
| expressions expression { printf("%d\n", ); }
和
expressions:
| expression expressions { printf("%d\n", ); }
这两个都是从左到右减少 expression
,但是 expressions
自下而上的减少导致第二个摘录以相反的顺序打印表达式的值。如果 expression
不是递归的(几乎可以肯定是),您可以将其分解到 expressions
产生式中,从而使表达式本身的归约与自下而上的逻辑相对应。但是这种转换非常有限,因为它只会在你遇到递归产生式之前起作用,而且几乎所有有趣的产生式都是递归的。 (statement
和 expression
只是两个例子。)
我正在使用 Bison 编写一个编译器,并且我正在尝试将相同的函数与 while 和 repeat 循环一起使用。我需要解析器在“statements_off
”之前减少“condition
”。 ¿我可以指定减少订单吗?
这是循环的代码:
loop_stmt:
WHILE line condition DO ENDL line statements_off DONE
{ $$ = process_while($<elem>3, $<elem>7, $<ival>2, $<ival>6); }
| REPEAT ENDL line statements_off UNTIL line condition
{ $$ = process_not($<elem>7);
$$ = process_while($$, $<elem>4, $<ival>6, $<ival>3); }
| FOR for_condition DO ENDL statements_off DONE
{ $$ = process_for($<elem>5, $<ival>2); }
;
condition: '(' expr_bool_or ')' { validate_bool_condition($<elem>2); $$ = $<elem>2; }
;
野牛的减少是自下而上和从左到右评估的。这是算法的重要组成部分。
这给了你一点自由度——有时你可以写一个语法,这样自下而上的归约会产生从右到左的效果——但你真的不应该这样做。当您解析为 AST(理想情况下独立于动作评估的顺序)然后根据需要和方便地遍历 AST 时,语义分析效果最佳。
通过将语言直接线性化为三地址代码来编写一次性编译器,这看起来像是一种简化或优化。但它最终会感觉更像一件紧身衣,因为很难将设计用于生成树的过程转变为线性控制流。
一次性编译器——我想到了 Lua 编译器——通常最终不得不重新排序生成的代码块,这既是因为这个问题的构造,也是为了实现非窥孔优化。
(将 while
和 for
语句中的预测试打乱到循环末尾实际上是很常见的,这似乎与这里考虑的相反。)
要清楚这些可能性,请考虑以下两者之间的区别:
expressions:
| expressions expression { printf("%d\n", ); }
和
expressions:
| expression expressions { printf("%d\n", ); }
这两个都是从左到右减少 expression
,但是 expressions
自下而上的减少导致第二个摘录以相反的顺序打印表达式的值。如果 expression
不是递归的(几乎可以肯定是),您可以将其分解到 expressions
产生式中,从而使表达式本身的归约与自下而上的逻辑相对应。但是这种转换非常有限,因为它只会在你遇到递归产生式之前起作用,而且几乎所有有趣的产生式都是递归的。 (statement
和 expression
只是两个例子。)