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;
解析会顺利进行,但我想保持 Type
和 FunType
区分,以免 void
作为常规 Type
.
I want to keep Type
and FunType
distinguished so as not to have void as a regular Type
.
但是你要求解析器在它有足够的信息来做出决定之前决定 int
是 Type
还是 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。
这是我的 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;
解析会顺利进行,但我想保持 Type
和 FunType
区分,以免 void
作为常规 Type
.
I want to keep
Type
andFunType
distinguished so as not to have void as a regularType
.
但是你要求解析器在它有足够的信息来做出决定之前决定 int
是 Type
还是 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。