如何使用中间规则操作在野牛中创建短路评估
How to create a short circuit evaluation in bison using mid-rules action?
我被指派编写一个计算器,该计算器还支持变量定义和赋值,布尔表达式和递减/递增运算。
我的计算器还应该支持条件(三元)运算? :
,与某些编程语言支持的相同,其用法如下:
condition ? value_if_true : value_if_false ;
或者正如我的语法规则中所定义的那样:
bool_expression ? int_expression : int_expression ;
问题是,它必须是短路评估,这意味着如果bool_expression的计算结果为true,则只应评估第一个int_expression,否则,如果bool_expression的计算结果为false,则只应评估第二个int_expression。
我被告知在中间规则功能中使用野牛的动作,但我看不出这有什么帮助。
我还在网上环顾四周,发现这是最接近我想要完成的事情,但不幸的是,那里的答案并不完全是我想要的。那里的答案确实描述了应该做什么,但没有具体说明如何做(全局标志是不可能的,当你看到代码时,你会明白为什么)。
这是我到目前为止所拥有的一小部分:
%{
#include<stdio.h>
#include<iostream>
#include <string>
#include <map>
using namespace std;
map<string, int> symbolTable;
int yylex();
void yyerror(const char*);
%}
%union{
int int_val;
char* string_val;
bool b;
}
%token <int_val> T_NUMBER
%token <string_val> T_VAR
%type <int_val> expr
%type <b>bool_expression
%nonassoc ':'
%nonassoc T_EQUALS
%left '+' '-'
%left '*' '/'
%left UMINUS
%left '$' '~'
%%
commands:
/* eps rule */ {cout<<"create commands"<<endl;}
| commands command {cout<<"add command"<<endl;};
command: expr 'n' {cout<<"Expression value: "<<$1<<endl;}
| T_VAR '=' expr 'n' {cout<<"Assignment: "<<$1<<"="<<$3<<endl; symbolTable[$1] = $3;}
| bool_expression 'n' {$1 ? cout<<"true"<<endl : cout<<"false"<<endl;}
;
expr:
T_NUMBER {$$ = $1;}
| T_VAR {$$ = symbolTable[$1];}
| expr '+' expr {$$ = $1 + $3;}
| expr '-' expr {$$ = $1 - $3;}
| expr '*' expr {$$ = $1 * $3;}
| expr '/' expr {if ($3 == 0) {yyerror ("Division by zero!"); return 1;} $$ = $1 / $3;}
| '$' T_VAR {$$ = ++symbolTable[$2]; }
| '~' T_VAR {$$ = --symbolTable[$2]; }
| '-' expr %prec UMINUS {$$=-$2;}
| bool_expression '?' {/* this is a mid-rule; what should go here... */}
expr ':' expr {/* in order to evaluates only one of these two, depending on bool_expression */}
;
bool_expression: expr T_EQUALS expr {$$=($1==$3);}
%%
void yyerror(const char* errorInfo){
cout<<errorInfo<<endl;
}
int main(){
yyparse();
}
您在那里看到的"$"运算符与 C 中的运算符 ++ 具有相同的含义,而"~"运算符与 --.
回到我的问题:以上只是相关的代码,为了可读性,我省略了更多规则。这也意味着在这里使用一些全局标志不是我的最佳选择,因为它需要在我的文件中的每个规则中检查它。现在,考虑输入:
a=5
1==1 ?$a : $a
将上述规则定义为:
| bool_expression '?' expr ':' expr {$1 ? $$=$3 : $$=$5 ;}
此代码导致值为 7 而不是 6 的"a"。
那么,如何在这里使用"中间规则中的操作"来防止第一个 expr 在bool_expression为假时被移动/减少,并在bool_expression为真时防止第二个 expr 被移动/减少?
你需要一个全局标志eval
,在解析应评估的代码时设置(true)。 虽然它是错误的,但解析器应该跳过事情,而不是实际评估任何东西。 因此,您的分配规则变为:
command: T_VAR '=' expr 'n' {
if (eval) {
cout << "Assignment: " << $1 << "=" << $3 << endl;
symbolTable[$1] = $3; } }
同样,对于可能具有副作用的所有其他规则(对于没有副作用的规则,您可以无条件运行并生成将被忽略的值,也可以类似地保护它。
然后,您的条件规则将变为:
expr: expr { if ($$ = eval) eval = $1; } '?'
expr { if ($2) eval = !eval; } ':'
expr { if (eval = $2) $$ = $1 ? $4 : $7; }
这会将 eval
标志保存在第一个内联操作的结果中,在最后一个操作中还原它,如果已设置,则为应计算的表达式而不是另一个表达式设置它。
这样做的一个大问题是错误恢复变得更加困难,因为您需要弄清楚何时/何地恢复eval
标志。
编辑
有了你所拥有的东西,$
和~
实际上什么都不做。 如果您希望它们等效于 ++
和 --
,则需要:
expr: `$` T_VAR { $$ = eval ? symbolTable[$2]++ : 0; }
| `~` T_VAR { $$ = eval ? symbolTable[$2]-- : 0; }
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 使用std::multimap迭代器创建std::list
- 在全局变量中保存类的实例以重新创建类(创建"backup")
- 使用CMake创建QML插件
- 如何在c++中为模板函数实例创建快捷方式
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 创建一个函数以在输入为负数或零时输出字符串.第一次执行用户定义的函数
- OpenCV EqualizeHist()从彩色图像创建黑白图像
- 试图在visual studio上用C++创建一个桌面应用程序
- std::threads可以从Windows DLL中的全局变量创建/销毁吗?
- 如何在C++20中创建模板别名的推导指南
- 如何为模板化对象创建模板向量?VS正在投掷C3203
- 如何创建一个空的全局类并在启动时实例化它
- 无法创建抽象类的实例
- 链接到自行创建的dll失败
- 为什么我不能在不创建字符串变量的情况下使用函数的字符串输出
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 如何在C++类内存结构中创建"spacer"?
- 终端不会为C++文件创建.exe文件吗
- 如何使用中间规则操作在野牛中创建短路评估