如何使用中规则动作在野牛中创建短路评估?

时间:2014-11-26 23:45:37

标签: c++ bison ternary-operator

我被分配编写一个计算器,除其他一些东西外,还支持变量定义和赋值,布尔表达式和递减/递增操作。
我的计算器还应该支持条件(三元)操作? :,这是一些编程语言支持的操作,使用如下:

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被移位/减少?

1 个答案:

答案 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; }