在快乐语法中转换/减少冲突

时间:2017-02-01 23:52:55

标签: haskell grammar parser-generator happy ambiguous-grammar

我有以下(严重剥离)快乐语法

%token
   '{'   { Langle }
   '}'   { Rangle }
   '..'  { DotDot }
   '::'  { ColonColon }
   '@'   { At }
   mut   { Mut }
   ident { Ident }

 %%

 pattern
   : binding_mode ident at_pat  { error "identifier pattern" }
   | expr_path                  { error "constant expression" }
   | expr_path '{' '..' '}'     { error "struct pattern" }

 binding_mode
   : mut                        { }
   |                            { }

 at_pat
   : '@' pat                    { }
   |                            { }

 expr_path
   : expr_path '::' ident       { }
   | ident                      { }

在模式中的标识符周围移位/减少冲突。默认情况下,Happy选择转移,但在这种情况下,这不是我想要的:即使它可能是constant expression,它也试图将所有内容都加入identifier pattern

我已经读到优先级/关联性是解决这类问题的方法,但是我添加的任何内容都无法使语法朝着正确的方向发展(公平地说,我一直在主要拍摄在黑暗中。)

使用一些明显的标记化,我想:

  • x以获得identifier pattern
  • mut x以获得identifier pattern
  • std::pi以获得constant expression
  • point{..}以获得struct pattern
  • std::point{..}以获得struct pattern

基本上,除非等待使用{::令牌,否则标识符应转到identifier pattern个案例。

如果我的问题不清楚,我道歉 - 部分问题是我很难确定问题甚至是什么。 :(

1 个答案:

答案 0 :(得分:5)

首先,了解转变是很重要的。移位是接受下一个输入令牌并将其放在解析器堆栈上的结果(最终它将成为生产的一部分,但是没有必要知道哪一个。)减少需要零个或多个令牌在堆栈顶部与某些生产的右侧相匹配,并用左侧替换它们。

当解析器决定从identifier pattern中创建binding_mode ident at_pat时,at_pat为空,它不会移位;它正在减少。实际上,它减少了两次:首先它将零堆叠符号减少为空at_pat,然后将前三个堆栈符号减少为identifier pattern。如果没有binding_mode,则可以将ident缩减为expr_path,然后将expr_path缩减为constant_expression。这将是减少/减少冲突。

但还有另一个问题,正是因为binding_mode可以为空。当解析器看到ident时,它不知道binding_mode是否可行,因此它不知道是否减少空binding_mode或移位{{} 1}}。这是一个转变/减少冲突。由于它更喜欢shift to reduce,因此它选择移动ident,这意味着无法生成空ident,这反过来会排除减少/减少冲突(并阻止binding_mode完全被认可。)

因此,为了解决所有这些问题,我们需要首先避免减少空ident @ pat的必要性。我们通过通常的可空产生消除算法来做到这一点,该算法包括制作右侧的两个副本,一个具有可空的非终结,另一个没有;然后我们删除可空的生产。一旦我们这样做,就会出现减少/减少冲突。

为了避免减少/减少冲突,我们需要明确哪个生产是首选。减少/减少冲突不能通过优先级声明来解决,因为优先级算法总是涉及生产(可以减少)和终端(可以转移)之间的比较。所以解决方案必须是明确的,这意味着我们需要说明binding_mode是一个模式,而ident不是expr_path是一个常量表达式。这给我们留下了以下内容:

(请注意,我使用非终端标记ident的三个不同作品,而不是依赖于行动。对我来说,这更容易思考和阅读。)

pattern

以下是空产生消除:

pattern: identifier_pattern | constant_expression | struct_pattern

以下是对观点的明确禁止:

identifier_pattern:   ident at_pat 
                  |   binding_mode ident at_pat

constant_expression: complex_expr_path struct_pattern: expr_path '{' '..' '}' 不再可以为空:

binding_mode

这里我们创建两个不同的expr_paths:

binding_mode: mut

at_pat
   : '@' pat
   | %empty

我希望解决方案与原始语法有一定的关系。