如果你来到这个问题并认为你终于找到了想要的答案,但是感到失望,那么这里有一个更明确的答案:
你不能使用 %prec,因为Thelema提到的原因。所以你必须在建立递归规则时定义结合性。
这是一个简化的parser.mly文件。
%token <int> Num
%token <string> Id
%token TRUE FALSE
%token LET REC EQ IN FUN ARROW IF THEN ELSE
%token PLUS MINUS MUL DIV LT LE NE AND OR
%token EOF
%start exp
%type <Simple.expr> exp
%%
exp:
LET Id EQ exp IN exp { Let($2,$4,$6) }
| LET REC Id EQ exp IN exp { Letrec($3,$5,$7) }
| FUN Id ARROW exp { Fun($2,$4) }
| IF exp THEN exp ELSE exp { If($2,$4,$6) }
| exp2 { $1 }
exp2:
exp2 OR exp3 { Bin($1,Or,$3) }
| exp3 { $1 }
exp3:
exp3 AND exp4 { Bin($1,And,$3) }
| exp4 { $1 }
exp4:
exp4 EQ exp5 { Bin($1,Eq,$3) }
| exp4 NE exp5 { Bin($1,Ne,$3) }
| exp4 LT exp5 { Bin($1,Lt,$3) }
| exp4 LE exp5 { Bin($1,Le,$3) }
| exp5 { $1 }
exp5:
exp5 PLUS exp6 { Bin($1,Plus,$3) }
| exp5 MINUS exp6 { Bin($1,Minus,$3) }
| exp6 { $1 }
exp6:
exp6 MUL exp7 { Bin($1,Mul,$3)}
| exp6 DIV exp7 { Bin($1,Div,$3)}
| exp7 { $1 }
exp7:
exp7 exp8 { Apply($1,$2) }
| exp8 { $1 }
exp8:
Num { Const($1) }
| Id { Var($1) }
| TRUE { True }
| FALSE { False }
| LPAREN exp RPAREN { $2 }
递归解决方案实际上是为了捕捉与此问题相关的情况,但很容易看出它也可以应用于为其他表达式定义结合性。这种方法的要点是尝试将问题中的模式与起始情况(exp)中定义的模式匹配,并将对随后情况(exp2)的调用留作万能模式,如果您的模式没有匹配到任何其他模式,则继续使用这种方法,直到最终匹配到模式。这意味着最高优先级的模式存在于最下面的情况中,在本例中为exp8。
在本例中,应用(函数应用)的情况在exp7中。这是因为Apply被定义为具有此示例中任何模式中最高结合性。它没有超过exp8中的情况是因为Apply计算为进一步调用表达式情况,而不是值调用。如果exp8不存在,我们就会遇到无限循环的情况。
在假想的simple.ml中,函数应用被定义为以下属性的表达式:expr * expr的Apply。由于Apply是左递归的,我们正在评估右表达式(exp8)并在左侧(exp7)递归。