我有一个解析器的以下代码,它接受链接列表作为父类的参数。输入表达式后,它会抛出堆栈溢出错误。
我从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;
}
}
答案 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,......(永远)。这导致了程序中的问题。
通过消除左递归来重写语法将解决这个问题,并确保在你完成时你有一个确定性的语法。