【问题标题】:Trying to find Shift/Reduce conflict in Grammar试图在语法中找到 Shift/Reduce 冲突
【发布时间】:2016-12-16 16:35:02
【问题描述】:

我有以下语法(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; }

我确实知道冲突是在我添加 MATH_OP 时开始的(我有所有的数学运算符,并得到 5 个冲突,而不是删除除 PLUS_OP 之外的所有运算符并得到 1 个移位/减少冲突)。

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

我怎样才能找到冲突?

【问题讨论】:

    标签: parsing compilation grammar bison yacc


    【解决方案1】:

    你的语法包括产生式:

    EXP : EXP MATH_OP EXP
    

    这本质上是模棱两可的。假设您有两个运算符:

    1 + 2 * 3
    

    很明显,上面是EXP MATH_OP EXP(因为没有其他选择),但就是这样

    [EXP: 1 + 2] [MATH_OP *] [EXP: 3]
    

    是吗

    [EXP: 1] [MATH_OP +] [EXP: 2 * 3]
    

    这两个解析显然具有不同的语义,并且语法允许两者。

    即使你只有一个运算符,也会有歧义(事实上,同样的歧义),尽管碰巧使用 + 的通常定义,评估将是相同的。 (使用单个运算符 - 会有所不同,这会使歧义更加清晰。)

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

    1. 明确指出如何解析表达式。 (Example from Wikipedia)。

    2. 使用优先声明来隐式限制解析。 (Example from bison manual.)

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

    在这两种情况下,您都不能将所有运算符集中到一个非终结符中,例如 MATH_OP,因为具有不同优先级的运算符在语法上是不同的。

    您还可以在此站点中找到许多相关的问题和答案。


    顺便说一句,将纯语法标记的字符串值(如+)传递给解析器很少有用。解析器不需要该值,因为它已经知道令牌类型,因此strdup() 是不必要的开销,并且相应的free() 会使语法操作变得混乱(而且,将其放在哪里并不明显)。如果您认为需要字符串来跟踪语法操作,请查看bison's debug facilities,它比在整个解析器中散布 printfs 更容易使用和更可靠。如果您根本不使用运算符的语义值,那么您显然不需要费心复制字符串然后释放或泄漏内存。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多