Parsing 使用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

您好,我正在为我的大学使用的一种编程语言编写一个解析器,使用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.   
我在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
          ;
最后,一个丑陋但可能的替代方法是使变量声明列表左递归,而进程声明列表右递归。在这种情况下,在最后一个变量声明和第一个流程声明之间没有空的产品