Parsing 在Menhir中,如果规则没有运算符,是否可以使其左关联?

Parsing 在Menhir中,如果规则没有运算符,是否可以使其左关联?,parsing,ocaml,lalr,menhir,Parsing,Ocaml,Lalr,Menhir,我正在尝试使用Menhir为正则表达式语言编写解析器。在修改语法以消除歧义之前,我想要的语法看起来有点像下面的示例。请注意,“排序/连接”是隐式的,并且没有与该操作关联的标记 %token LPAREN RPAREN %token CHAR STAR PIPE %token EOF %start <unit> parse %% parse: re EOF {()} re: | LPAREN re RPAREN {()} (* Grouping *) | CHAR

我正在尝试使用Menhir为正则表达式语言编写解析器。在修改语法以消除歧义之前,我想要的语法看起来有点像下面的示例。请注意,“排序/连接”是隐式的,并且没有与该操作关联的标记

%token LPAREN RPAREN
%token CHAR STAR PIPE
%token EOF

%start <unit> parse

%%

parse: re EOF {()}

re:
  | LPAREN re RPAREN {()}  (* Grouping *)
  | CHAR             {()}  (* Single character *)
  | re STAR          {()}  (* Kleene star *)
  | re re            {()}  (* Sequencing / Concatenation *)
  | re PIPE re       {()}  (* Alternation *)
我认为这可能是因为menhir无法判断序列应该是左关联的,但我不能100%确定这是否是问题的原因

到目前为止,我能找到的唯一解决方案是将
re
规则分解为一系列不同的规则,以明确优先级和关联性:

%token LPAREN RPAREN
%token CHAR STAR PIPE
%token EOF

%start <unit> parse

%%

parse: re EOF {()}

re: re3 {()}

re0:
  | LPAREN re RPAREN {()}  (* Grouping *)
  | CHAR             {()}  (* Single character *)

re1:
  | re0              {()}
  | re0 STAR         {()}  (* Kleene star *)

re2:
  | re1              {()}
  | re2 re1          {()}  (* Sequencing / Concatenation *)

re3:
  | re2              {()}
  | re3 PIPE re2     {()}  (* Alternation *)
%LPAREN RPAREN令牌
%令牌字符星型管
%令牌EOF
%开始解析
%%
parse:re-EOF{()}
re:re3{()}
re0:
|LPAREN re RPAREN{()}(*分组*)
|字符{()}(*单个字符*)
re1:
|re0{()}
|re0星{()}(*Kleene星*)
re2:
|re1{()}
|re2 re1{()}(*排序/串联*)
re3:
|re2{()}
|re3管道re2{()}(*替换*)

虽然最后一个例子很好,但我真的很好奇,是否可以通过使用优先级和关联性声明来删除所有的umbiguity和冲突,而不需要重写语法。

首先,这在Menhir上并不完全是个问题,但在Menhir接受的语法中,它位于
LR(1)
集合中。如果您提供的语法不需要优先级注释,则该语法将被视为
SLR(1)
,是
LR(1)
的子集

上一个示例之所以有效,是因为每个优先级都有产品(比如解析表达式语法,本质上是明确的),这绝对不是一种糟糕的编写方式;一些现代编译器使用这种符号来处理更复杂的语言

为了了解您的问题,让我们首先请Menhir向我们解释冲突发生的地点:

menhir——解释解析器.mly

它将生成一个
解析器。冲突
文件,解释它可以在哪些状态下同时执行操作、减少和转移:

...
** In state 8, looking ahead at STAR, shifting is permitted
** because of the following sub-derivation:

re re
   re . STAR

** In state 8, looking ahead at STAR, reducing production
** re -> re re
** is permitted because of the following sub-derivation:

re STAR // lookahead token appears
re re .

** Conflict (shift/reduce) in state 7.
** Tokens involved: STAR PIPE LPAREN CHAR
** The following explanations concentrate on token STAR.
** This state is reached from parse after reading:

re PIPE re

** The derivations that appear below have the following common factor:
** (The question mark symbol (?) represents the spot where the derivations begin to differ.)

parse
re EOF
(?)
对于
LR(1)
,语法非常模糊,因此:

CHAR CHAR STAR
可计算为:

  • (CHAR)星号
  • CHAR(CHAR-STAR)
重写语法而不产生冲突的另一种方法是通过
list
实现连接:

re:
| term PIPE re
| term { } (* Left associativity *)

term:
| list(base STAR* { }) { } (* Concatenation is taken by list *)

base:
| CHAR
| LPAREN re RPAREN { }

谢谢你的回答!我得到的印象是,这里真正的教训是,可以使用多个级别的
re
规则来创建更明确的语法,而不是试图巧妙地使用优先级声明。即使在上一个示例中,真正解决问题的是您有单独的
re
term
base
规则。
CHAR CHAR STAR
re:
| term PIPE re
| term { } (* Left associativity *)

term:
| list(base STAR* { }) { } (* Concatenation is taken by list *)

base:
| CHAR
| LPAREN re RPAREN { }