javaCC "java.lang.ArrayIndexOutOfBoundsException: -1" 异常

javaCC "java.lang.ArrayIndexOutOfBoundsException: -1" exception

我有一个 IT 研究项目,它是为只为该项目制作的简单语言制作一个编译器(它是一个简化的 java)。为了解析语法,我在 javacc 中为第一个版本制作了一个 jjtree 文件(我们必须在这个项目中使用 SCRUM 方法),所以它还没有完整的语法。起初它似乎工作得很好,并且可以正确阅读我输入的内容。但是,当我们与项目中的另一个人(我们是一个五人小组)一起为 jjtree 生成的每个访问者编写编译规则时,我们发现了多项选择规则的一些问题。我们看到要解决这个问题,我们必须在语法上使用标签,这就是我们开始遇到问题的地方。

我在一些规则上添加了标签(尤其是有多项选择的规则),现在,当我们测试解析器时,它在变量声明中给了我们一个异常。例如:

class C{
    int i=1;
    main {
       i++;
    }
}

当我输入“;”时,在int i=1之后给出了异常。

class C{
    int i[1];
    main {
        i++;
    }
}

在int i[1]

中第二个“]”后给出异常

当我输入“;”时,在int i=1之后给出了异常。

class C{
    int i;
    int x;
    main {
        i++;
    }
}

进入main时报异常

例外情况:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
        at java.util.ArrayList.elementData(ArrayList.java:418)
        at java.util.ArrayList.remove(ArrayList.java:495)
        at JJTGrammaireJavaCCASA2State.closeNodeScope(JJTGrammaireJavaCCASA2State.java:86)
        at GrammaireJavaCCASA2.classe(GrammaireJavaCCASA2.java:50)
        at GrammaireJavaCCASA2.main(GrammaireJavaCCASA2.java:9)

有人可以帮助我吗?我是 javaCC 的新手,我坚持了两周。我找不到哪里有问题,而且我还有一些标签问题,所以它可能来自这里。

这是老师给我们的基本语法(它是法语的,但我认为即使不懂法语也很容易理解)和我们为 javaCC 重写的语法(我们使用黄色的版本 1) :

http://imgur.com/a/WASPr

最后是 javaCC 中的代码(我删除了 space 的评论,因为它们是法语的,但我可以重新添加):

options {
    VISITOR = true;
    MULTI=true;
}

PARSER_BEGIN(GrammaireJavaCCASA2)

import java.io.*;


public class GrammaireJavaCCASA2 {
    public static void main(String args[]) throws ParseException {
        GrammaireJavaCCASA2 parser = new GrammaireJavaCCASA2(System.in);
        SimpleNode root = parser.classe();
        root.dump("");
    }
}

PARSER_END(GrammaireJavaCCASA2)

SKIP : {
      " "
    | "\t"
    | "\n"
    | "\r"
}

TOKEN :{
    <VIRGULE : ",">
    | <PVIRGULE : ";">
    | <PAROUV : "(">
    | <PARFER : ")">
    | <ACCOLOUV: "{">
    | <ACCOLFER: "}">
    | <CROOUV: "[">
    | <CROFER : "]">
    | <PLUS : "+">
    | <MOINS : "-">
    | <MULT : "*">
    | <DIV : "/">
    | <AFFECTATION : "=">
    | <PLUSEGAL : "+=">
    | <INCREMENT : "++">
    | <EGALE : "==">
    | <SUPERIEUR : ">">
    | <DIFFERENT : "!">
    | <ET : "&&">
    | <OU : "||" >
    | <CLASSE : "class">
    | <FINAL  :  "final">
    | <MAIN : "main">
    | <VOID : "void">
    | <RETURN : "return">
    | <IF : "if">
    | <ELSE : "else">
    | <WHILE : "while">
    | <TRUE : "true">
    | <FALSE : "false">
    | <NOMBRE : (["0"-"9"])+>
    | <INT : "int">
    | <BOOLEAN : "boolean">
    | <IDENT : ["a"-"z","A"-"Z"] ( ["a"-"z","A"-"Z","0"-"9"] )*>
}

SimpleNode classe() #CLASSE(3): {}{ 
    <CLASSE> ident() <ACCOLOUV> decls() methmain() <ACCOLFER>{return jjtThis;}
}

void ident() #IDENT: {Token t;}{
    t=<IDENT> {jjtThis.value = t.image;}
}

void decls() #DECLS(2): {}{
    decl() <PVIRGULE> decls() 
    |{} #VNIL 
}

void decl() #DECL(1) : {}{
    vars() 
}

void vars() #VARS(2): {}{
    var() <PVIRGULE> vars() 
    |{} #VNIL
}

void var() #void : {}{
    typemeth() ident() (<CROOUV> exp() <CROFER> #TABLEAU(3)|vexp() #VAR(3))
}

/*void Var2() : {}{
    Vexp()
}*/

void vexp() #AFFECTATIONINIT(1) : {}{
    <AFFECTATION> exp() 
    |{} #OMEGA
}

void methmain() #MAIN(2): {}{
    <MAIN> <ACCOLOUV> vars() instrs() <ACCOLFER> 
}

void instrs() #INSTRS(2): {}{
    instr() <PVIRGULE> instrs() 
    |{} #INIL
}

void instr() #void : {}{
    ident1() (<AFFECTATION> exp() #AFFECTATION(2) 
    |<PLUSEGAL> exp() #SOMME(2)
    |<INCREMENT> #INCREMENT(1))
}


/*void Instr2() : {}{
    <AFFECTATION> Exp()
    |<PLUSEGAL> Exp()
    |<INCREMENT>
}*/

void exp() #EXP1GO(1) : {}{
    exp1()
}

void exp1() #EXP1(1): {}{
    exp2()
}

void exp2() #void : {}{
    <MOINS> terme() [exp2prime()] #NEGATIF(2)
    |terme() [exp2prime()] #TERMEEXP(2)
}

void exp2prime() #void : {}{
    <PLUS> terme() [exp2prime()] #PLUS(2)
    |<MOINS> terme() [exp2prime()] #MOINS(2)
}

void terme() #TERME(2): {}{
    fact() [termeprime()]
}

void termeprime() #void : {}{
    <MULT> fact() [termeprime()] #PRODUIT(2)
    |<DIV> fact() [termeprime()] #DIVISION(2)
}

void fact() #void : {}{
    ident1() #IDENT1GO(1)
    |<TRUE> #VRAI
    |<FALSE> #FAUX
    |<NOMBRE> #NOMBRE
}

void ident1() #IDENT1(1) : {}{
    ident() 
}

void typemeth() #TYPE(1): {}{
    type() 
}

void type() #void : {}{
    <INT> #ENTIER
    |<BOOLEAN> #BOOLEEN
}

在此先感谢您的帮助,抱歉我的英语不好。如果您需要更多信息,请随时询问。

我觉得下面的作品不对

void decls() #DECLS(2): {}{
    decl() <PVIRGULE> decls() 
    |{} #VNIL 
}

考虑没有声明的情况。然后,首先,一个 VNIL 将被压入堆栈,其次,两个节点将从堆栈中弹出,一个 DECL 节点将被压入堆栈。因此,它不会按照您的意愿向堆栈中添加一个节点,而是用 DECL 节点替换顶部节点。

这样写比较好:

void decls() #void : {}{
    (decl() <PVIRGULE> decls()) #DECLS
|
    {} #VNIL 
}

void decls() #void : {}{ someDecls() | nothing() }

void someDecls() #DECLS : {} { decl() <PVIRGULE> decls() }

void nothing() #VNIL : {} { }

类似的评论适用于您语法中的许多作品。

您也可以像这样避免使用 VNIL

void decls() #DECLS : {} { decl() ( <PVIRGULE> decl() )* }

祝你好运。