移位-减少与表达式的冲突

时间:2018-09-11 03:57:10

标签: expression grammar bison yacc shift-reduce-conflict

我正在为自己设计的完整编程语言编写语法。这种语言有几种类型的表达式,它们在不同的情况下以不同的方式组合在一起。对于如何工作,我有一个很好的主意,但是在排除移位/减少和减少/减少冲突方面遇到了麻烦。我正在使用Xubuntu 16.04下的Bison v3.0.4。完整的语法(包括* .output文件)可以在我的github上的https://github.com/chucktilbury/Simple1上看到。 (请参阅expressions.y和expressions.output)

我已经走得很远了。我知道这不是最好的,但是我正在学习。如果有人可以提供一些帮助我摆脱困境的指示,我将不胜感激。

这是给我带来问题的语法部分的片段:

%{
#include <stdio.h>
%}

%token OPAREN_TOK CPAREN_TOK OCURLY_TOK CCURLY_TOK OBOX_TOK CBOX_TOK
%token COMMA_TOK SCOLON_TOK DOT_TOK COLON_TOK 
%token CLASS_TOK FUNC_TOK PRIVATE_TOK PUBLIC_TOK PROTECTED_TOK
%token CREATE_TOK DESTROY_TOK IMPORT_TOK STRUCT_TOK

%token PLUS_TOK MINUS_TOK MULT_TOK DIV_TOK MODULO_TOK ASSIGN_TOK 

%token BIT_NOT_TOK BIT_OR_TOK BIT_AND_TOK BIT_XOR_TOK BIT_LSH_TOK BIT_RSH_TOK

%token INT_TOK FLOAT_TOK UNSD_TOK STRG_TOK
%token BOOL_TOK 

%token RETURN_TOK BREAK_TOK CONT_TOK IF_TOK ELSE_TOK WHILE_TOK
%token FOR_TOK SWITCH_TOK CASE_TOK 

%token OR_TOK AND_TOK NOT_TOK EQ_TOK GEQ_TOK LEQ_TOK
%token NEQ_TOK MORE_TOK LESS_TOK 

%token TRUE_TOK FALSE_TOK NOTHING_TOK

%token SYMBOL_TOK UNSIGNED_TOK INTEGER_TOK FLOATING_TOK STRING_TOK

%left MINUS_TOK PLUS_TOK
%left MULT_TOK DIV_TOK
%left NEGATION
%right CARAT_TOK    /* exponentiation        */

%%

expression
    : arithmetic_expression
    | boolean_expression
    | bitwise_expression
    ;

compound_symbol
    : SYMBOL_TOK
    | compound_symbol DOT_TOK SYMBOL_TOK
    ;

exponent_numeric_value
    : FLOATING_TOK
    | INTEGER_TOK
    ;

arithmetic_factor
    : INTEGER_TOK
    | FLOAT_TOK
    | UNSIGNED_TOK
    | exponent_numeric_value CARAT_TOK exponent_numeric_value
    | compound_symbol
    ;

arithmetic_expression
    : arithmetic_factor
    | arithmetic_expression PLUS_TOK arithmetic_expression
    | arithmetic_expression MINUS_TOK arithmetic_expression
    | arithmetic_expression MULT_TOK arithmetic_expression
    | arithmetic_expression DIV_TOK arithmetic_expression
    | MINUS_TOK arithmetic_expression %prec NEGATION
    | OPAREN_TOK arithmetic_expression CPAREN_TOK
    ;

boolean_factor
    : arithmetic_factor
    | TRUE_TOK
    | FALSE_TOK
    | STRING_TOK
    ;

boolean_expression
    : boolean_factor
    | boolean_expression OR_TOK boolean_expression
    | boolean_expression AND_TOK boolean_expression 
    | boolean_expression EQ_TOK boolean_expression 
    | boolean_expression NEQ_TOK boolean_expression 
    | boolean_expression LEQ_TOK boolean_expression 
    | boolean_expression GEQ_TOK boolean_expression 
    | boolean_expression MORE_TOK boolean_expression 
    | boolean_expression LESS_TOK boolean_expression 
    | NOT_TOK boolean_expression 
    | OPAREN_TOK boolean_expression CPAREN_TOK
    ;

bitwise_factor
    : INTEGER_TOK
    | UNSIGNED_TOK
    | compound_symbol
    ;

bitwise_expression
    : bitwise_factor
    | bitwise_expression BIT_AND_TOK bitwise_expression
    | bitwise_expression BIT_OR_TOK bitwise_expression
    | bitwise_expression BIT_XOR_TOK bitwise_expression
    | bitwise_expression BIT_LSH_TOK bitwise_expression
    | bitwise_expression BIT_RSH_TOK bitwise_expression
    | BIT_NOT_TOK bitwise_expression
    | OPAREN_TOK bitwise_expression CPAREN_TOK 
    ; 
%% 

这产生102个shift-reduce和8个reduce-reduce冲突。我知道我在规则中重用了一些令牌,并且非终端根是人为设计的。我在弄清楚如何组织它们以便将正确的(有时是相同的)类型与正确的表达式类型相关联时遇到麻烦。我尝试了各种重组方式。我认为很明显我遗漏了一些东西。也许我的整个方法都错了,但是我不清楚对此正确的方法是什么。

要获得对我真正想做的事情的更好(但非常不完整)的解释,请参见此存储库上的自述文件:https://github.com/chucktilbury/toi

1 个答案:

答案 0 :(得分:0)

如果还没有,请以bison -r all filename.y之类的格式运行bison,然后查看额外的输出文件filename.output。在顶部附近,这给了我

State 9 conflicts: 2 reduce/reduce
State 10 conflicts: 2 reduce/reduce
State 14 conflicts: 2 reduce/reduce
State 16 conflicts: 2 reduce/reduce
State 35 conflicts: 5 shift/reduce
State 38 conflicts: 8 shift/reduce
...

“状态9”的下一个实例是

State 9

   10 arithmetic_factor: UNSIGNED_TOK .  [$end, CPAREN_TOK, PLUS_TOK, MINUS_TOK, MULT_TOK, DIV_TOK, OR_TOK, AND_TOK, EQ_TOK, GEQ_TOK, LEQ_TOK, NEQ_TOK, MORE_TOK, LESS_TOK]
   36 bitwise_factor: UNSIGNED_TOK .  [$end, CPAREN_TOK, BIT_OR_TOK, BIT_AND_TOK, BIT_XOR_TOK, BIT_LSH_TOK, BIT_RSH_TOK]

    $end         reduce using rule 10 (arithmetic_factor)
    $end         [reduce using rule 36 (bitwise_factor)]
    CPAREN_TOK   reduce using rule 10 (arithmetic_factor)
    CPAREN_TOK   [reduce using rule 36 (bitwise_factor)]
    BIT_OR_TOK   reduce using rule 36 (bitwise_factor)
    BIT_AND_TOK  reduce using rule 36 (bitwise_factor)
    BIT_XOR_TOK  reduce using rule 36 (bitwise_factor)
    BIT_LSH_TOK  reduce using rule 36 (bitwise_factor)
    BIT_RSH_TOK  reduce using rule 36 (bitwise_factor)
    $default     reduce using rule 10 (arithmetic_factor)

此输出表示实现解析算法的状态机中的一个可能的“状态”。

首先,有许多行显示了语法规则中的可能位置。句点(.)始终显示规则中的当前位置。如果句号在规则的最后,则野牛可能会在[方括号]中的所有终端标记列表中紧随其后,这些标记可能会在由规则。

接下来是给定当前状态和输入流中的下一个标记的解析器将执行的操作的表。冲突显示为同一令牌的多个条目,并且在[方括号]中的第一个括号之后出现操作(以表明该操作在语法不明确的情况下是有效的,但解析器实际上不会采取任何措施该动作)。

因此,在状态9的输出中,我们可以看到问题是,当解析器输入的末尾或UNSIGNED_TOK令牌后面跟随CPAREN_TOK令牌时,野牛无法确定数字应为arithmetic_factorbitwise_factor。对于输入的结束,也许这没什么大不了,可以通过摆弄根非终结符来避免该问题。但是封闭括号的情况是个问题。由于野牛(默认情况下)使用LALR(1) grammar,因此在处理了文本( 0u )的前两个标记后,解析器需要仅使用单个前瞻标记{ {1}}。但是,如果它决定将其设置为0u并且输入为),那就错了;如果它决定将其设置为arithmetic_factor并且输入为( 0u ) & 1u,那就错了。

要解决此类问题,从语义动作的角度考虑语法规则通常会有所帮助(即使在仅使用语法来确定输入是否有效且不会有任何语义的情况下)动作)。解释器应对表达式bitwise_factor采取什么动作?理想情况下,一点都不做:表达式应该具有与普通( 0u ) + 1u相同的表示和效果。这使它与复合算术表达式和复合按位表达式归为不同的类别,因为它们的用途更为有限(至少在所示语法中)。

但是,如果我们想说( 0u )不是0u,那么似乎( 0u )可能会采用许多荒谬的规则来列出所有可接受的操作数类型。我们可以通过为arithmetic_expression使用规则来避免这种情况,解析器仅将其用于实际算术运算符的子表达式(不包括括号)。要允许多个运算符,任何arithmetic_expression都可以用作arithmetic_operand

所以这是您的语法版本(在相同的标记声明之后),没有reduce-reduce冲突:

arithmetic_expression

仍然存在102个shift-reduce冲突,但是都可以通过在arithmetic_operand%% expression : int_constant | float_constant | bool_constant | string_constant | exponent_constant | symbol_parens | arithmetic_expression | boolean_expression | bitwise_expression ; compound_symbol : SYMBOL_TOK | compound_symbol DOT_TOK SYMBOL_TOK ; symbol_parens : compound_symbol | OPAREN_TOK symbol_parens CPAREN_TOK ; int_constant : INTEGER_TOK | UNSIGNED_TOK | OPAREN_TOK int_constant CPAREN_TOK ; float_constant : FLOAT_TOK | OPAREN_TOK float_constant CPAREN_TOK ; bool_constant : TRUE_TOK | FALSE_TOK | OPAREN_TOK bool_constant CPAREN_TOK ; string_constant : STRING_TOK | OPAREN_TOK string_constant CPAREN_TOK ; exponent_operand : FLOATING_TOK | INTEGER_TOK ; exponent_constant : exponent_operand CARAT_TOK exponent_operand | OPAREN_TOK exponent_constant CPAREN_TOK ; arithmetic_operand : int_constant | float_constant | exponent_constant | symbol_parens | arithmetic_expression ; arithmetic_expression : arithmetic_operand PLUS_TOK arithmetic_operand | arithmetic_operand MINUS_TOK arithmetic_operand | arithmetic_operand MULT_TOK arithmetic_operand | arithmetic_operand DIV_TOK arithmetic_operand | MINUS_TOK arithmetic_operand %prec NEGATION | OPAREN_TOK arithmetic_expression CPAREN_TOK ; boolean_operand : bool_constant | int_constant | float_constant | exponent_constant | string_constant | symbol_parens | boolean_expression ; boolean_expression : boolean_operand OR_TOK boolean_operand | boolean_operand AND_TOK boolean_operand | boolean_operand EQ_TOK boolean_operand | boolean_operand NEQ_TOK boolean_operand | boolean_operand LEQ_TOK boolean_operand | boolean_operand GEQ_TOK boolean_operand | boolean_operand MORE_TOK boolean_operand | boolean_operand LESS_TOK boolean_operand | NOT_TOK boolean_operand | OPAREN_TOK boolean_expression CPAREN_TOK ; bitwise_operand : int_constant | symbol_parens | bitwise_expression ; bitwise_expression : bitwise_operand BIT_AND_TOK bitwise_operand | bitwise_operand BIT_OR_TOK bitwise_operand | bitwise_operand BIT_XOR_TOK bitwise_operand | bitwise_operand BIT_LSH_TOK bitwise_operand | bitwise_operand BIT_RSH_TOK bitwise_operand | BIT_NOT_TOK bitwise_operand | OPAREN_TOK bitwise_expression CPAREN_TOK ; %% 规则中为运算符指定优先级和关联性来解决。

请注意:可能这是无意的,但是您的语法不允许混合使用来自不同“类别”的运算符。因此,例如,输入boolean_expressionbitwise_expression无效。