【问题标题】:YACC Grammar: Operator precedence issueYACC 语法:运算符优先级问题
【发布时间】:2014-03-05 15:24:53
【问题描述】:

我试图得到:(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

【问题讨论】:

    标签: yacc


    【解决方案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,而是针对那些会遇到类似问题的人!

    我想知道是哪个学校设置了这个作业,因为我已经看到很多关于 SO 的 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;
                                            }
    

    你看到我在那里做了什么吗?我围绕运算符旋转了语法规则。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-04
      • 1970-01-01
      • 2011-07-07
      相关资源
      最近更新 更多