BNFC 简单产生式规则的解析问题

Parsing problem for BNFC simple production rules

这是我的 BNFC 格式语法的简化版本:

-- programs -------------------------------------
entrypoints Program ;
Prog.           Program ::= [ Def ] ;
terminator      Def "" ;


-- definitions ----------------------------------
DVar.           Def ::= VarDecl ;
DFun.           Def ::= FunDef;

-- functions definitions ------------------------
DFunAll.        FunDef ::= FunType Id "(" [ Arg ] ")" FunBody;
DFunBody.       FunBody ::= "{" RetStmt "}" ;

-- function statements --------------------------
StmtRetVoid.    RetStmt ::= "return" ";" ;

-- variable declarations ------------------------
DVarSimple.     VarDecl ::= Type Id ";" ;
DVarList.       VarDecl ::= Type Id "," [Id] ";" ;

-- arguments ------------------------------------
ArgDecl.        Arg ::= Type Id ;
separator       Arg "," ;

-- types -----------------------------------------
TInt.           Type ::= "int" ;
TFunRetT.       FunType ::= Type ;
TFunRetV.       FunType ::= "void" ;

-- identifiers ------------------------------------
position token  Id (letter (letter | digit | '_')*) ;
separator       Id "," ;

对于此语法 happy 生成 1 个未使用的规则和 1 个 shift/reduce 冲突警告。我在这里面临的问题是我无法正确解析函数返回类型。闻起来超级简单,但我卡住了。

更具体地说,int foo(int x) {return;} 解析错误,而 void foo(int x) {return;} 解析成功。所以这三个规则似乎不能像预期的那样一起工作:

DFunAll.        FunDef ::= FunType Id "(" [ Arg ] ")" FunBody;
TInt.           Type ::= "int" ;
TFunRetT.       FunType ::= Type ;

如果我将 FunDef 规则更改为 FunDef ::= Type Id "(" [ Arg ] ")" FunBody; 解析会顺利进行,但我想保持 TypeFunType 区分,以免 void 作为常规 Type.

I want to keep Type and FunType distinguished so as not to have void as a regular Type.

但是你要求解析器在它有足够的信息来做出决定之前决定 intType 还是 FunType。由于无法确定是否应用产生式 FunType ::= Type,因此它选择移动 Id(因为 shift/reduce 冲突的默认解决方案是移动)。这使得无法使用 FunType ::= Type 生产,这会触发未使用的规则警告。

唯一简单的解决办法就是完全放弃 FunType,代价是一些重复:

DFunAllT.        FunDef ::= Type Id "(" [ Arg ] ")" FunBody;
DFunAllV.        FunDef ::= "void" Id "(" [ Arg ] ")" FunBody;

如果重复困扰您,您可以留下因素:

DFunTI.   FunTypeId ::= Type Id;
DFunVI.   FunTypeId ::= "void" Id;
DFunAll.  FunDef ::= FunTypeId "(" [ Arg ] ")" FunBody;

第一个可能更适合你的 AST。