Parsing 试图找到移位/减少语法冲突

Parsing 试图找到移位/减少语法冲突,parsing,compilation,grammar,bison,yacc,Parsing,Compilation,Grammar,Bison,Yacc,我有以下语法(Yacc),这是一个简单C编译器的开始,我从一个简单的if语句开始: S : E ; E : COND_NO_ELSE ; COND_NO_ELSE : IF BOOL_EXP BLOCK ; BLOCK : LC EXP RC BOOL_EXP : LP EXP BOOL_OP EXP RP ; BOOL_OP : LT_OP | GT_OP | LE_OP | GE_OP | EQ_OP | NE_O

我有以下语法(Yacc),这是一个简单C编译器的开始,我从一个简单的
if
语句开始:

S : E
  ;

E : COND_NO_ELSE
  ;

COND_NO_ELSE : IF BOOL_EXP BLOCK
;

BLOCK : LC EXP RC

BOOL_EXP : LP EXP BOOL_OP EXP RP
     ;

BOOL_OP : LT_OP
     | GT_OP
     | LE_OP
     | GE_OP
     | EQ_OP
     | NE_OP
     ;

MATH_OP : PLUS_OP
    ;

EXP : IDENTIFIER
    | EXP MATH_OP EXP
    ;
以下是相关规则的词法分析器扫描程序:

"="       { yylval.string = strdup(yytext); return ASSIGN;}
"+"       { yylval.string = strdup(yytext); return PLUS_OP;}
"-"       { yylval.string = strdup(yytext); return MINUS_OP;}
"*"       { yylval.string = strdup(yytext); return MULTIPLY_OP;}
"/"       { yylval.string = strdup(yytext); return DIV_OP;}
"%"       { yylval.string = strdup(yytext); return MOD_OP;}

"<"       { yylval.string = strdup(yytext); return LT_OP;}
">"       { yylval.string = strdup(yytext); return GT_OP; }
"<="       { yylval.string = strdup(yytext); return LE_OP; }
">="       { yylval.string = strdup(yytext); return GE_OP; }
"=="       { yylval.string = strdup(yytext); return EQ_OP; }
"!="       { yylval.string = strdup(yytext); return NE_OP; }
"("       { yylval.string = strdup(yytext); return LP; }
")"       { yylval.string = strdup(yytext); return RP; }
"{"       { yylval.string = strdup(yytext); return LC; }
"}"       { yylval.string = strdup(yytext); return RC; }
if { return IF; }
“=”{yylval.string=strdup(yytext);返回ASSIGN;}
“+”{yylval.string=strdup(yytext);返回加号_OP;}
“-”{yylval.string=strdup(yytext);返回减号_OP;}
“*”{yylval.string=strdup(yytext);返回乘法运算;}
“/”{yylval.string=strdup(yytext);返回DIV_OP;}
“%”{yylval.string=strdup(yytext);返回MOD_OP;}
“{yylval.string=strdup(yytext);返回GT_OP;}”
“=”{yylval.string=strdup(yytext);返回GE_OP;}
“=”{yylval.string=strdup(yytext);返回EQ_OP;}
“!=”{yylval.string=strdup(yytext);返回NE_OP;}
(“{yylval.string=strdup(yytext);返回LP;}
“{yylval.string=strdup(yytext);返回RP;}”
{{yylval.string=strdup(yytext);返回LC;}
“}”{yylval.string=strdup(yytext);返回RC;}
if{返回if;}
我确实知道,当我添加
MATH_OP
时,冲突就开始了(我的所有数学运算符都大于,得到了5个冲突,除了
PLUS_OP
之外,我删除了所有运算符,得到了1个shift/reduce冲突)

我按照建议为输出文件使用了
-v
标志,并检查了问题,但它与我的语法不太相似


如何找到冲突?

您的语法包括生产:

EXP : EXP MATH_OP EXP
这本身就是模棱两可的。假设您有两个运算符:

1 + 2 * 3
显然,上面是
EXP MATH\u OP EXP
(因为没有其他选项),但是

[EXP: 1 + 2] [MATH_OP *] [EXP: 3]
还是这样

[EXP: 1] [MATH_OP +] [EXP: 2 * 3]
这两种语法显然具有不同的语义,语法允许这两种语法

即使只有一个运算符,也会有歧义(事实上,是相同的歧义),尽管在+的通常定义中,计算结果是相同的。(与单个运算符不同,这使得模糊性稍微清晰一些。)

为算术表达式创建yacc/bison语法有两种常见策略:

  • 显式指示如何解析表达式。()

  • 使用优先级声明隐式限制解析。()

  • 第一种策略更冗长但更灵活;如果您试图了解LR解析是如何工作的,可能会更好,因为它是显式的。第二种策略更紧凑,也更容易阅读(因为许多普通读者比上下文无关语法更了解运算符优先级列表),但要了解它的详细工作原理则需要更多的工作

    在这两种情况下,您都不能将所有运算符合并到一个非终结符(如
    MATH_OP
    )中,因为具有不同前导的运算符在语法上是不同的

    你也可以在这个网站上找到很多相关的问题和答案



    顺便说一下,将纯语法标记(如
    +
    )的字符串值传递给解析器很少有用。解析器不需要该值,因为它已经知道令牌类型,因此
    strdup()
    是不必要的开销,而相应的
    free()
    会使语法操作变得杂乱无章(同样,放在哪里也不明显)。如果您认为需要字符串来跟踪语法动作,那么请检查哪些比在整个解析器中使用printfs更容易使用和更可靠。如果您根本不使用运算符的语义值,那么显然不需要麻烦复制字符串,然后释放或泄漏内存。

    您的语法包括以下内容:

    EXP : EXP MATH_OP EXP
    
    这本身就是模棱两可的。假设您有两个运算符:

    1 + 2 * 3
    
    显然,上面是
    EXP MATH\u OP EXP
    (因为没有其他选项),但是

    [EXP: 1 + 2] [MATH_OP *] [EXP: 3]
    
    还是这样

    [EXP: 1] [MATH_OP +] [EXP: 2 * 3]
    
    这两种语法显然具有不同的语义,语法允许这两种语法

    即使只有一个运算符,也会有歧义(事实上,是相同的歧义),尽管在+的通常定义中,计算结果是相同的。(与单个运算符不同,这使得模糊性稍微清晰一些。)

    为算术表达式创建yacc/bison语法有两种常见策略:

  • 显式指示如何解析表达式。()

  • 使用优先级声明隐式限制解析。()

  • 第一种策略更冗长但更灵活;如果您试图了解LR解析是如何工作的,可能会更好,因为它是显式的。第二种策略更紧凑,也更容易阅读(因为许多普通读者比上下文无关语法更了解运算符优先级列表),但要了解它的详细工作原理则需要更多的工作

    在这两种情况下,您都不能将所有运算符合并到一个非终结符(如
    MATH_OP
    )中,因为具有不同前导的运算符在语法上是不同的

    你也可以在这个网站上找到很多相关的问题和答案


    顺便说一下,将纯语法标记(如
    +
    )的字符串值传递给解析器很少有用。解析器不需要该值,因为它已经知道令牌类型,因此
    strdup()
    是不必要的开销,而相应的
    free()
    会使语法操作变得杂乱无章(同样,放在哪里也不明显)。如果您认为需要字符串来跟踪语法动作,那么请检查哪些比在整个解析器中使用printfs更容易使用和更可靠。如果您根本不使用运算符的语义值,那么显然不需要麻烦地复制字符串,然后释放或l