如果第二个规则失败,请尝试第一个规则

时间:2014-01-27 15:31:57

标签: parsing ocaml ocamlyacc

我定义了两组标识符IDENTIFIER_ONEIDENTIFIER_TWO,它们都是IDENTIFIER的排除子集。我想编写一个解析器:

"i1(arg) EOS" can't be parsed (1)
"i2(arg) EOS" can be parsed (2)
"i1(arg) = value EOS" can be parsed (3)
"i2(arg) = value EOS" can be parsed (4)

其中i1(分别为i2)属于IDENTIFIER_ONE(分别为IDENTIFIER_TWO); argvalue属于IDENTIFIER。以下parser.mly已经实现了我追求的所有要点,除了(4)

identifier:
| IDENTIFIER_ONE { $1 }
| IDENTIFIER_TWO { $1 } 

block_statement_EOS:
| identifier LPAREN identifier RPAREN EQUAL identifier EOS { BSE_Let ($1, $3, $6) }
| IDENTIFIER_TWO LPAREN identifier RPAREN EOS { BSE_I_I ($1, $3) }

作为输入i1(arg) = value EOS,作为目标(3),它被正确读取为BSE_Let (i1, arg, value)。但是,给定i2(arg) = value EOS作为输入,它会在读取EQUAL后停止解析。我想这是因为一旦解析符合i2(arg),就会转到block_statement_EOS的第二条规则,之后EQUAL无法解析。

理想情况下,如果第二条规则失败,我希望解析器可以尝试block_statement_EOS的第一条规则。任何人都可以帮助我实现这个目标吗?

PS :如果我按如下方式编写parser.mly,则可以实现所有目标。有谁知道为什么?另外,我真的不喜欢这种解决方法,因为我确实需要在许多其他规则中编写identifier而不是两个子集,我希望有一个更优雅的解决方案......

block_statement_EOS:
| IDENTIFIER_ONE LPAREN identifier RPAREN EQUAL identifier EOS { BSE_Let ($1, $3, $6) }
| IDENTIFIER_TWO LPAREN identifier RPAREN EQUAL identifier EOS { BSE_Let ($1, $3, $6) }
| IDENTIFIER_TWO LPAREN identifier RPAREN EOS { BSE_I_I ($1, $3) }

1 个答案:

答案 0 :(得分:0)

当您的解析器在LPAREN之后遇到IDENTIFIER_TWO时,它必须决定是转移还是缩小:

  • shift:将LPAREN放在堆栈上;
  • reduce:IDENTIFIER_TWO替换堆栈顶部的identifier

如果您的解析器选择转换,它永远不会将此特定IDENTIFIER_TWO减少为identifier(因为此特定IDENTIFIER_TWO将永远不会再次位于堆栈顶部),这意味着它将永远减少block_statement_EOS的第二条规则。

如果您的解析器选择缩减,则永远不会减少block_statement_EOS的第二条规则,因为此规则以IDENTIFIER_TWO而不是identifier开头。

这就是您的第二个版本有效的原因,因为在IDENTIFIER_TWO之后无需在转换和缩小之间进行选择。如果您愿意,可以在以后做出选择。

相关问题