我被分配编写一个计算器,除其他一些东西外,还支持变量定义和赋值,布尔表达式和递减/递增操作。
我的计算器还应该支持条件(三元)操作? :
,这是一些编程语言支持的操作,使用如下:
condition ? value_if_true : value_if_false ;
或者在我的语法规则中定义:
bool_expression ? int_expression : int_expression ;
问题是,它必须是short circuit evaluation,这意味着如果 bool_expression 评估为true,则只应评估第一个 int_expression ,否则,如果 bool_expression 的计算结果为false,则只应计算第二个 int_expression 。
我被告知要使用野牛的Action in mid-rule功能,但我看不出它有多大帮助。
我也在网上看了一下,发现this与我想要完成的事情最接近,但不幸的是,答案并不是我想要的。那里的答案确实描述了应该做什么,但没有具体说明如何做到(全局标志是不可能的,你会理解为什么当你看到代码时)。
到目前为止,这只是我的一小部分:
%{
#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();
}
&#39; $&#39;运算符你看到它与C中的operator ++具有相同的含义,并且&#39;〜&#39;运算符与 - 。
具有相同的含义回到我的问题:以上只是相关的代码,为了便于阅读,我已经省略了很多规则。这也意味着在这里使用一些全局标志不是我的最佳选择,因为它需要在我的文件中的每个规则中进行检查。 现在,考虑输入:
A = 5
1 == 1? $ a:$ a
将上述规则定义为:
的天真方法| bool_expression '?' expr ':' expr {$1 ? $$=$3 : $$=$5 ;}
此代码导致&#39; a&#39;值为7而不是6 那么如何才能在中期规则中采取行动?如果 bool_expression 为false,则在此处使用以防止第一个expr被移位/减少,并且如果 bool_expression 为真,则阻止第二个expr被移位/减少?
答案 0 :(得分:1)
您需要一个全局标志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
标志保存在第一个内联操作的结果中,并在最终操作中恢复它,如果已设置,则将其设置为应评估的表达式而不是另一个。< / p>
这样做的一个大问题是错误恢复变得更加困难,因为你需要弄清楚何时/何地恢复eval
标志。
修改强>
根据您的情况,$
和~
实际上并没有做任何事情。如果您希望它们等同于++
和--
,则需要:
expr: `$` T_VAR { $$ = eval ? symbolTable[$2]++ : 0; }
| `~` T_VAR { $$ = eval ? symbolTable[$2]-- : 0; }