您好我正在为大学使用的编程语言编写解析器,使用jflex和Cup 我从第一个基本结构开始,例如处理变量声明。
我收到以下错误
Warning : *** Shift/Reduce conflict found in state #4
between vardecls ::= (*)
and vardecl ::= (*) IDENT COLON vartyp SEMI
and vardecl ::= (*) IDENT COLON vartyp EQEQ INT SEMI
under symbol IDENT
Resolved in favor of shifting.
Warning : *** Shift/Reduce conflict found in state #2
between vardecls ::= (*)
and vardecl ::= (*) IDENT COLON vartyp SEMI
and vardecl ::= (*) IDENT COLON vartyp EQEQ INT SEMI
under symbol IDENT
Resolved in favor of shifting.
我的杯中代码如下:
non terminal programm;
non terminal programmtype;
non terminal vardecl;
non terminal vardecls;
non terminal processdecl;
non terminal processdecls;
non terminal vartyp;
programm ::= programmtype:pt vardecls:vd processdecls:pd
{: RESULT = new SolutionNode(pt, vd, pd); :} ;
programmtype ::= IDENT:v
{: RESULT = ProblemType.KA; :} ;
vardecls ::= vardecl:v1 vardecls:v2
{: v2.add(v1);
RESULT = v2; :}
|
{: ArrayList<VarDecl> list = new ArrayList<VarDecl>() ;
RESULT = list; :}
;
vardecl ::= IDENT:id COLON vartyp:vt SEMI
{: RESULT = new VarDecl(id, vt); :}
| IDENT:id COLON vartyp:vt EQEQ INT:i1 SEMI
{: RESULT = new VarDecl(id, vt, i1); :}
;
vartyp ::= INTEGER
{: RESULT = VarType.Integer ; :}
;
processdecls ::= processdecl:v1 processdecls:v2
{: v2.add(v1);
RESULT = v2; :}
| {: ArrayList<ProcessDecl> list = new ArrayList<ProcessDecl>() ;
RESULT = list; :}
;
processdecl ::= IDENT:id COLON PROCESS vardecls:vd BEGIN END SEMI
{: RESULT = new ProcessDecl(id, vd); :}
;
我猜我得到了错误,因为Process Declaration和VariableDeclaration都以Identifiers开头,然后是“:”,然后是Terminal PROCESS或者像INTEGER这样的终端。如果是这样,我想知道我怎么能告诉我的Parser更多地展望未来。或者无论解决方案是什么。
感谢您的回答。
答案 0 :(得分:1)
您的诊断绝对正确。因为解析器无法知道IDENT
是否在没有两个超前标记的情况下启动processdecl
或vardecl
,因此无法知道它何时缩短了vardecl
并且正在查看IDENT
是否要查看另一个vardecl
或processdecl
。
在第一种情况下,必须将IDENT
作为以下vardecl
的一部分进行转移。在第二种情况下,它需要首先减少空vardecls
,然后连续减少vardecls
,直到它构建完整列表。
要摆脱转移减少冲突,您需要简化解析器的决策。
最简单的解决方案是允许解析器以任何顺序接受声明。然后你最终得到这样的东西:
program ::= program_type declaration_list ;
declaration_list ::=
var_declaration declaration_list
| process_declaration declaration_list
|
;
var_declaration_list ::=
var_declaration var_declaration_list
|
;
process_declaration ::=
IDENT:id COLON PROCESS var_declaration_list BEGIN END SEMI ;
(就个人而言,我使声明列表左递归而不是右递归,但这取决于你是否更喜欢在列表的动作中追加或添加。左递归使用较少的解析器堆栈。)
如果您真的想要坚持所有变量声明都在任何流程声明之前,您可以在declaration_list
的操作中检查它。
或者,您可以从使两种类型的声明列表左递归而不是右递归开始。这将几乎工作,但它仍然会在与原始语法相同的状态下生成shift-reduce冲突,这次是因为它需要在第一个进程声明之前减少一个空进程声明列表降低。
幸运的是,这更容易解决。如果流程声明列表不能为空,则没有问题,所以这只是重新排列作品的问题:
program ::= program_type var_declaration_list process_declaration_list
| program_type var_declaration_list
;
var_declaration_list ::=
var_declaration var_declaration_list
|
;
process_declaration_list ::=
process_declaration_list process_declaration
| process_declaration
;
最后,一个丑陋但可能的替代方法是使变量声明列表左递归,并且进程声明列表是右递归的。在这种情况下,最后一个变量声明和第一个流程声明之间没有空的生产。