Parsing 使用Cup移动/减少误差
您好,我正在为我的大学使用的一种编程语言编写一个解析器,使用jflex和Cup 我从最初的基本结构开始,比如进程和变量声明 我得到以下错误Parsing 使用Cup移动/减少误差,parsing,compiler-construction,grammar,shift-reduce-conflict,cup,Parsing,Compiler Construction,Grammar,Shift Reduce Conflict,Cup,您好,我正在为我的大学使用的一种编程语言编写一个解析器,使用jflex和Cup 我从最初的基本结构开始,比如进程和变量声明 我得到以下错误 Warning : *** Shift/Reduce conflict found in state #4 between vardecls ::= (*) and vardecl ::= (*) IDENT COLON vartyp SEMI and vardecl ::= (*) IDENT COLON v
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.
我在Cup中的代码如下所示:
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); :}
;
非终端程序;
非终端程序类型;
非末端vardecl;
非末端vardecls;
非终端进程DECL;
非终端进程;
非终端变量;
PROGRAM::=PROGRAMTYPE:pt vardecls:vd processdecls:pd
{:结果=新的解决方案节点(pt、vd、pd);:};
程序类型::=IDENT:v
{:RESULT=ProblemType.KA;:};
vardecls::=vardeclv1 vardecls:v2
{:v2.add(v1);
结果=v2;:}
|
{:ArrayList list=new ArrayList();
结果=列表;:}
;
vardecl::=IDENT:id冒号vartyp:vt SEMI
{:RESULT=newvardecl(id,vt);:}
|IDENT:id冒号变量类型:vt EQEQ INT:i1 SEMI
{:RESULT=newvardecl(id,vt,i1);:}
;
变量类型::=整数
{:RESULT=VarType.Integer;:}
;
processdecls::=processdecl:v1 processdecls:v2
{:v2.add(v1);
结果=v2;:}
|{:ArrayList list=new ArrayList();
结果=列表;:}
;
processdecl::=IDENT:id冒号进程vardecls:vd开始结束
{:RESULT=newprocessdecl(id,vd);:}
;
我想之所以会出现错误,是因为进程声明和VariableDeclaration都以标识符开头,然后是“:”,然后是终端进程或类似终端的整数。如果是这样的话,我想知道如何让解析器向前看一点。或者任何可能的解决方案
谢谢你的回答。你的诊断完全正确。由于解析器无法知道
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 ;
(就我个人而言,我会让声明列表左递归而不是右递归,但这取决于您喜欢在列表的操作中追加还是前置。左递归使用更少的解析器堆栈。)
如果您确实希望坚持所有变量声明都在任何进程声明之前,您可以在声明的操作列表中检查它
或者,您可以从使这两种类型的声明列表左递归而不是右递归开始。这几乎可以实现,但它仍然会在与原始语法相同的状态下生成一个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
;
最后,一个丑陋但可能的替代方法是使变量声明列表左递归,而进程声明列表右递归。在这种情况下,在最后一个变量声明和第一个流程声明之间没有空的产品