YACC语法:运算符优先级问题

时间:2014-03-05 15:24:54

标签: yacc

我想得到:(20 +( - 3))* 3 /(20/3)/ 2等于4.现在它等于17.

所以基本上它正在做(20/3)然后将其除以2,然后将3除以[(20/3)/ 2],然后将其乘以17.不确定如何将我的语法/规则/优先级改为让它正确阅读。任何指导都将不胜感激,谢谢。

%% 

start:              PROGRAMtoken IDtoken IStoken compoundstatement

compoundstatement:          BEGINtoken {print_header();} statement semi ENDtoken {print_end();}

semi:                                   SEMItoken statement semi
                                        |

statement:                              IDtoken EQtoken exp
                                        { regs[$1] = $3; }

                                        | PRINTtoken exp
                                        { cout << $2 << endl; }

                                        | declaration

declaration:                        VARtoken IDtoken comma

comma:                              COMMAtoken IDtoken comma
                                    |

exp:                                    exp PLUStoken term
                                        { $$ = $1 + $3; }

                                        | exp MINUStoken term
                                        { $$ = $1 - $3; }

                                        | term
                                        { $$ = $1; }

                                        | MINUStoken term
                                        { $$ = -$2;}

term:                                   factor
                                        { $$ = $1;
                                        }
                                        | factor TIMEStoken term
                                        {$$ = $1 * $3;
                                        }
                                        | factor DIVIDEtoken term
                                        { $$ = $1 / $3;
                                        }

factor:                                 ICONSTtoken
                                        { $$ = $1;}
                                        | IDtoken
                                        {  $$ = regs[$1];  }
                                        | LPARENtoken exp RPARENtoken
                                        { $$ = $2;}

%%

我的代币和类型如下:

%token BEGINtoken   
%token COMMAtoken

%left  DIVIDEtoken
%left  TIMEStoken

%token ENDtoken
%token EOFtoken 
%token EQtoken

%token <value> ICONSTtoken
%token <value> IDtoken

%token IStoken
%token LPARENtoken

%left  PLUStoken MINUStoken

%token PRINTtoken
%token PROGRAMtoken
%token RPARENtoken
%token SEMItoken

%token VARtoken 

%type <value> exp
%type <value> term
%type <value> factor

1 个答案:

答案 0 :(得分:1)

你真的希望有人努力工作来给你一个答案,这就是为什么问题已经持续了一年。请阅读此帮助页面:https://stackoverflow.com/help/how-to-ask,特别是关于简化问题的部分。语法文件中有许多规则不需要重现问题。我们不需要:

%token BEGINtoken   
%token COMMAtoken
%token ENDtoken
%token EOFtoken 
%token EQtoken
%token <value> IDtoken
%token IStoken
%token PROGRAMtoken
%token VARtoken 
%%
start:              PROGRAMtoken IDtoken IStoken compoundstatement

compoundstatement:          BEGINtoken {print_header();} statement semi ENDtoken {print_end();}

semi:                                   SEMItoken statement semi
                                        |
                                        | declaration

declaration:                        VARtoken IDtoken comma

comma:                              COMMAtoken IDtoken comma
                                    |

您可能刚刚删除了这些令牌和规则,以了解运营商优先级问题的核心。我们不需要任何变量,声明,赋值或程序结构来说明失败。学习简化是有能力的调试和编程的心脏。如果你做了这个简化,那么更多人会有回答。我说的不是OP,而是那些会遇到类似问题的人!

我想知道学校在设置这项任务是什么,因为我在同一个愚蠢的问题上看到了相当数量的yacc问题。我怀疑每年会有更多的人来这里,所以回答这个问题会对他们有所帮助。我知道检查语法的问题是什么,但是为了测试我的解决方案,我必须编写一个有效的词法分析器,一些符号表例程,一个主程序和其他辅助代码。再一次,解决问题的另一个障碍。

让我们了解问题的核心。您有这些令牌声明

%left  DIVIDEtoken
%left  TIMEStoken
%left  PLUStoken MINUStoken

这些告诉yacc,如果操作员关联左边的任何规则是不明确的。您对这些运营商的规则是:

exp:                                    exp PLUStoken term
                                        { $$ = $1 + $3; }

                                        | exp MINUStoken term
                                        { $$ = $1 - $3; }

                                        | term
                                        { $$ = $1; }

                                        | MINUStoken term
                                        { $$ = -$2;}

term:                                   factor
                                        { $$ = $1;
                                        }
                                        | factor TIMEStoken term
                                        {$$ = $1 * $3;
                                        }
                                        | factor DIVIDEtoken term
                                        { $$ = $1 / $3;
                                        }

但是,这些规则不明确,因此不需要运算符优先级声明。 Yacc将遵循您使用的非模糊语法。这些规则的编写方式,告诉yacc操作符具有正确的关联性,这与您想要的相反。现在,从您的示例中的简单算法可以清楚地看出,运算符是以正确的关联方式计算的,而您希望相反。那里有很大的线索吗?

行。如何改变关联性?一种方法是再次使语法不明确,以便使用%left声明,或者只是翻转规则以反转关联性。这就是我所做的:

exp:                                    term PLUStoken exp
                                        { $$ = $1 + $3; }

                                        | term MINUStoken exp
                                        { $$ = $1 - $3; }

                                        | term
                                        { $$ = $1; }

                                        | MINUStoken term
                                        { $$ = -$2;}

term:                                   factor
                                        { $$ = $1;
                                        }
                                        | term TIMEStoken factor
                                        {$$ = $1 * $3;
                                        }
                                        | term DIVIDEtoken factor
                                        { $$ = $1 / $3;
                                        }

你看到我在那里做了什么吗?我围绕操作员旋转了语法规则。

现在有更多的免责声明。我说这是一个 dumb 练习。动态解释表达式是对yacc工具的不良使用,而不是真正的编译器或解释器中发生的事情。在实际实现中,将构建解析树,并且将在树行走期间执行值计算。这样就可以解决未声明变量的问题(这也发生在本练习中)。使用regs数组来存储值也很愚蠢,因为显然有一个辅助符号表用于返回符号的唯一整数ID。在真正的编译器/解释器中,这些值也将存储在该符号表中。

我希望本教程能帮助进一步的学生在课堂作业中理解这些解析问题。