递归下降分析器堆栈溢出

时间:2015-05-25 01:08:37

标签: java parsing stack-overflow recursive-descent

我有一个解析器的以下代码,它接受链接列表作为父类的参数。输入表达式后,它会抛出堆栈溢出错误。

我从Swing GUI类中的jTextField传递输入表达式,然后将布尔结果返回到同一类中的jLabel。什么可能导致堆栈溢出错误?请帮助,谢谢!!

示例输入:

1 + 2 + 3

(1 + 2)/(3 + 4)

import java.util.LinkedList;

class Token {
    public static final int PLUS_MINUS = 0;
    public static final int MULTIPLY_DIVIDE = 1;
    public static final int EXPONENT = 2;
    public static final int NUMBER = 3;
    public static final int IDENTIFIER = 4;
    public static final int OPEN = 5;
    public static final int CLOSE = 6;
    //public static final int NEGATIVE = 7;

    public final int token; // FIELDS TO HOLD DATA PER TOKEN
    public final String sequence;

    public Token (int token, String sequence) {
        super();
        this.token = token;
        this.sequence = sequence;
    }
}

public class Parser {

    private Token next; // POINTER FOR NEXT TOKEN
    private LinkedList<Token> tokens; // LIST OF TOKENS PRODUCED BY TOKENIZER
    private int counter = 0;

    public Parser(LinkedList tokens) 
    {
        this.tokens = (LinkedList<Token>) tokens.clone(); // GET LINKEDLIST
        this.tokens.getFirst(); // ASSIGNS FIRST ELEMENT OF LINKEDLIST
    }


    //////// START OF PARSING METHODS ////////

    /*
        GRAMMAR:
        E -> E+T | E-T | T | -E
        T -> T*X | T/X | X
        X -> X^F | F
        F -> (E) | NUMBERS | IDENTIFIERS
                        F -> (E) | N | I
                        N -> D | ND
                        I -> IDENTIFIERS
    */

    public boolean Parse ()
    {
        return E(); // INVOKE START SYMBOL
    }

    private boolean term (int token) // GETS NEXT TOKEN
    {
        boolean flag = false;

        if(next.token == token)
            flag = true;

        counter++; // INCREMENT COUNTER

        if(counter < tokens.size()) // POINT TO NEXT TOKEN
            next = tokens.get(counter);

        return flag;
    }

    ///////// START OF LIST OF PRODUCTIONS /////////

    //////// E -> E+T | E-T | T | -E ////////

    private boolean E() 
    {
        return E1() || E2() || E3();
    }

    private boolean E1 ()
    {
        // E -> E+T | E-T

        int flag = counter;
        boolean result = true;

        if(!(E() && term(Token.PLUS_MINUS) && T() ))
        {
            counter = flag; // BACKTRACK

            if(counter < tokens.size()) // POINT TO PREVIOUS TOKEN
                next = tokens.get(counter);

            result = false;
        }
        return result;
    }

    private boolean E2 ()
    {
        // E -> T

        int flag = counter;
        boolean result = true;

        if(!T())
        {
            counter = flag; // BACKTRACK

            if(counter < tokens.size()) // POINT TO PREVIOUS TOKEN
                next = tokens.get(counter);

            result = false;
        }
        return result;
    }

    private boolean E3 ()
    {
        // E -> -E

        int flag = counter;
        boolean result = true;

        if(!(term(Token.PLUS_MINUS) && E() ))
        {
            counter = flag; // BACKTRACK

            if(counter < tokens.size()) // POINT TO PREVIOUS TOKEN
                next = tokens.get(counter);

            result = false;
        }
        return result;
    }


    //////// T -> T*X | T/X | X ////////

    private boolean T()
    {
        return T1() || T2();
    }

    private boolean T1 ()
    {
        // T -> T*X | T/X

        int flag = counter;
        boolean result = true;

        if(!(T() && term(Token.MULTIPLY_DIVIDE) && X() ))
        {
            counter = flag; // BACKTRACK

            if(counter < tokens.size()) // POINT TO PREVIOUS TOKEN
                next = tokens.get(counter);

            result = false;
        }
        return result;
    }

    private boolean T2 ()
    {
        // T -> X

        int flag = counter;
        boolean result = true;

        if(!X())
        {
            counter = flag; // BACKTRACK

            if(counter < tokens.size()) // POINT TO PREVIOUS TOKEN
                next = tokens.get(counter);

            result = false;
        }
        return result;
    }


    //////// X -> X^F | F ////////

    private boolean X()
    {
        return X1() || X2();
    }

    private boolean X1()
    {
        // X-> X^F

        int flag = counter;
        boolean result = true;

        if(!(X() && term(Token.EXPONENT) && F()))
        {
            counter = flag; // BACKTRACK

            if(counter < tokens.size()) // POINT TO PREVIOUS TOKEN
                next = tokens.get(counter);

            result = false;
        }
        return result;
    }

    private boolean X2()
    {
        // X-> F

        int flag = counter;
        boolean result = true;

        if(!F())
        {
            counter = flag; // BACKTRACK

            if(counter < tokens.size()) // POINT TO PREVIOUS TOKEN
                next = tokens.get(counter);

            result = false;
        }
        return result;
    }


    //////// F -> (E) | NUMBERS | IDENTIFIERS ////////
    private boolean F()
    {
        return F1() || F2() || F3();
    }

    private boolean F1()
    {
        // F -> (E)

        int flag = counter;
        boolean result = true;

        if(!(term(Token.OPEN) && E() && term(Token.CLOSE)))
        {
            counter = flag; // BACKTRACK

            if(counter < tokens.size()) // POINT TO PREVIOUS TOKEN
                next = tokens.get(counter);

            result = false;
        }
        return result;
    }

    private boolean F2()
    {
        // F -> NUMBERS

        int flag = counter;
        boolean result = true;

        if(!term(Token.NUMBER))
        {
            counter = flag; // BACKTRACK

            if(counter < tokens.size()) // POINT TO PREVIOUS TOKEN
                next = tokens.get(counter);

            result = false;
        }
        return result;
    }

    private boolean F3()
    {
        // F -> IDENTIFIERS

        int flag = counter;
        boolean result = true;

        if(!term(Token.IDENTIFIER))
        {
            counter = flag; // BACKTRACK

            if(counter < tokens.size()) // POINT TO PREVIOUS TOKEN
                next = tokens.get(counter);

            result = false;
        }
        return result;
    }
}

2 个答案:

答案 0 :(得分:2)

你的问题是递归下降解析不能处理左递归语法。你的第一个作品说&#34; E - &gt; E + T&#34;,我们称之为递归,因为派生的第一件事是你定义的东西。递归下降的方式是先匹配一个&#34; E&#34;那么&#34; +&#34;然后是&#34; T&#34;。问题是你的&#34; E&#34;方法首先调用&#34; E1&#34;方法,然后立即调用&#34; E&#34;再一次,呼叫&#34; E1&#34;并遇到无限的递归循环。如果你想使用递归下降,你需要留下你的语法因素。我复制了一个链接,其中包含有关左分解的更多信息:http://en.wikipedia.org/wiki/Left_recursion。总而言之,因为你有一个无限的递归循环,所以你正在溢出堆栈。

答案 1 :(得分:0)

通常当你得到一个堆栈溢出时,这意味着程序递归地调用一个或多个方法而没有结束,所以让我们来看看你的程序将如何执行。

您的代码似乎是使用Parse方法执行的(在Java中,命名约定是使用小写方法名称)

public boolean Parse() {
    return E(); // INVOKE START SYMBOL
}

到目前为止还不错;语法指定首先解析E,因此调用E()。让我们看看E()的定义。

private boolean E() {
    return E1() || E2() || E3();
}

让我们看看执行时会发生什么。 Java将通过执行E1(),然后执行E2(),最后执行E3()来评估此布尔表达式,现在让我们看一下E1。

private boolean E1 () {
    // E -> E+T | E-T

    int flag = counter;
    boolean result = true;
    if(!(E() && term(Token.PLUS_MINUS) && T() )) {
        counter = flag; // BACKTRACK
    ...

这是问题所在。您的标志已设置,result设置为true,if语句立即执行E()。请记住,E()是刚被评估的,现在E1()再次调用E(),它将永远执行E1()(如果你调试了程序,你会在应用程序堆栈中看到交替调用E1 ()和E())。

这是递归下降解析的一部分。这是一个优雅的解析解决方案,但语法有时需要一些重写,否则这就是你遇到的确切问题,你会陷入语法规则。为了实现这一点,您需要重写语法(请参阅我通过快速Google搜索找到的this document on recursive descent parsing)。

语法有两个要求:它必须是确定性的,不能包含左递归。

您遇到的问题是几乎所有规则中的递归:

  

E - &gt; E + T | E-T | T | -E

这表示令牌E可以是令牌E + T,并且要认识到这一点,你必须识别令牌E,它可以是E + T,......(永远)。这导致了程序中的问题。

通过消除左递归来重写语法将解决这个问题,并确保在你完成时你有一个确定性的语法。