OCaml + Menhir:如何像元组模式一样解析OCaml?

时间:2015-10-28 16:07:11

标签: ocaml menhir

我是一个非常初学的menhir。 我想知道如何用我自己的语言解析OCaml像tuple-pattern,这与OCaml非常相似。

例如,在表达式let a,b,c = ...中, a, b, c应该被解析为Tuple (Var "a", Var "b", Var "c")

但是,在下面的解析器定义中,上面的示例被解析为Tuple (Tuple (Var "a", Var "b"), Var "c")。 我想知道如何修复以下定义来解析ocaml等模式。

我已经检查了OCaml的解析器。但是我不确定如何实现这一点。 我认为我的定义类似于OCaml的一个...... 他们用什么神奇的东西?

%token LPAREN
%token RPAREN
%token EOF
%token COMMA
%left COMMA
%token <string> LIDENT
%token UNDERBAR
%nonassoc below_COMMA

%start <Token.token> toplevel
%%

toplevel:
| p = pattern EOF { p }

pattern:
| p = simple_pattern        { p }
| psec = pattern_tuple %prec below_COMMA
  { Ppat_tuple (List.rev psec) }

simple_pattern:
| UNDERBAR                  { Ppat_any }
| LPAREN RPAREN             { Ppat_unit }
| v = lident                { Ppat_var v }
| LPAREN p = pattern RPAREN { p }

pattern_tuple:
| seq = pattern_tuple; COMMA; p = pattern { p :: seq }
| p1 = pattern; COMMA; p2 = pattern { [p2; p1] }

lident:
| l = LIDENT { Pident l }

结果如下:

[~/ocaml/error] menhir --interpret --interpret-show-cst ./parser.mly
File "./parser.mly", line 27, characters 2-42:
Warning: production pattern_tuple -> pattern_tuple COMMA pattern is never reduced.
Warning: in total, 1 productions are never reduced.
LIDENT COMMA LIDENT COMMA LIDENT
ACCEPT
[toplevel:
  [pattern:
    [pattern_tuple:
      [pattern:
        [pattern_tuple:
          [pattern: [simple_pattern: [lident: LIDENT]]]
          COMMA
          [pattern: [simple_pattern: [lident: LIDENT]]]
        ]
      ]
      COMMA
      [pattern: [simple_pattern: [lident: LIDENT]]]
    ]
  ]
  EOF
]

2 个答案:

答案 0 :(得分:4)

它包含典型的shift-reduce冲突,并且您通过指定优先级在其解析中犯了一个错误。请打开任何有关Yacc解析的书籍,并检查shift-reduce冲突及其解决方案。

让我们使用您的规则来看待它。假设我们有以下输入,并且解析器正在展望第二个,

( p1 , p2 , ...
          ↑
         Yacc is looking at this second COMMA token

他们有两种可能性:

  • 减少:将p1 , p2作为pattern。它使用pattern
  • 的第二条规则
  • Shift:使用令牌COMMA并向前移动向前光标以尝试使逗号分隔列表更长。

您可以轻松地从%prec below_COMMA规则中删除pattern冲突:

$ menhir z.mly   # the %prec thing is removed
...
File "z.mly", line 4, characters 0-9:
Warning: the precedence level assigned to below_COMMA is never useful.
Warning: one state has shift/reduce conflicts.
Warning: one shift/reduce conflict was arbitrarily resolved.

许多Yacc文件说在这种情况下Yacc更喜欢轮班,这种默认通常与人的意图相符,包括你的情况。因此,其中一个解决方案就是放弃%prec below_COMMA事情并忘记警告。

如果您不喜欢使用shift减少冲突警告(这就是灵魂!),您可以明确说明在这种情况下应该使用优先级选择哪个规则,就像OCaml&#39; s {{1} }。 (顺便说一下,OCaml&#39; s parser.mly是一个减少分数的珠宝盒。如果你不熟悉,你应该检查其中的一个或两个。)

Yacc在轮班减少冲突时选择优先级更高的规则。对于shift,它的优先级是前瞻光标处的一个标记,在这种情况下为parser.mly。 reduce的优先级可以通过相应规则的COMMA后缀来声明。如果你没有指定它,我猜一个规则的优先级是未定义的,这就是为什么如果你删除%prec TOKEN就会发出shift减少冲突的原因。

现在问题是:哪个优先级更高,%prec below_COMMACOMMA?这应该在below_COMMA文件的前导码中声明。 (这就是为什么我要求提问者展示那部分。)

mly

我跳过... %left COMMA ... %nonassoc below_COMMA %left的意思,因为所有的Yacc书都应该解释它们。此处%nonassoc伪令牌位于 below_COMMA下方。这意味着COMMA的优先级高于<{1}} 更高。因此,上面的示例选择Reduce,并且针对意图得到below_COMMA

正确的优先权声明恰恰相反。要让Shift发生,COMMA必须<{>> <{1}} 优先级

( (p1, p2), ...

请参阅OCaml&#39; below_COMMA。它确实是这样的。把&#34;放在&#34;以上&#34;声音完全疯狂,但这不是Menhir的错。这是Yacc不幸的传统。怪它。 OCaml&#39; COMMA已发表评论:

...
%nonassoc below_COMMA
%left COMMA
...

答案 1 :(得分:3)

这是我重写它的方式:

%token LPAREN
%token RPAREN
%token EOF
%token COMMA
%token  LIDENT
%token UNDERBAR

%start  toplevel
%%

toplevel:
| p = pattern EOF { p }

pattern:
| p = simple_pattern; tail = pattern_tuple_tail
      { match tail with
        | [] -> p
        | _ -> Ppat_tuple (p :: tail)
      }

pattern_tuple_tail:
| COMMA; p = simple_pattern; seq = pattern_tuple_tail { p :: seq }
|                                                     { [] }

simple_pattern:
| UNDERBAR                  { Ppat_any }
| LPAREN RPAREN             { Ppat_unit }
| v = lident                { Ppat_var v }
| LPAREN p = pattern RPAREN { p }

lident:
| l = LIDENT { Pident l }

一个重大变化是元组元素不再是pattern而是simple_patternpattern本身是一个或多个元素的逗号分隔序列。

$ menhir --interpret --interpret-show-cst parser.mly 
LIDENT COMMA LIDENT COMMA LIDENT
ACCEPT
[toplevel:
  [pattern:
    [simple_pattern: [lident: LIDENT]]
    [pattern_tuple_tail:
      COMMA
      [simple_pattern: [lident: LIDENT]]
      [pattern_tuple_tail:
        COMMA
        [simple_pattern: [lident: LIDENT]]
        [pattern_tuple_tail:]
      ]
    ]
  ]
  EOF
]