2015-05-25 5 views
2

У меня есть следующий код для синтаксического анализатора, который принимает связанный список как параметр родительского класса. Он выдает ошибку переполнения стека после ввода выражения.Рекурсивный спуск Parser Stack Overflow

Я передаю входное выражение из jTextField в классе GUI Swing, а затем возвращает логический результат в 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

У вас есть пример ввода? – MadProgrammer

+0

Да, привет, это переполнение стека, как мы можем вам помочь? (И, как вы знаете, обычно лучше обрезать код только до минимальной суммы. И некоторые из ваших комментариев - это мусор - объясните, почему, а не то, что и в скобках неверно для Java. И ... Er, почему я критикую ваш код вместо ответа? И почему я шепчу? О, хорошо. Собираюсь притворяться умным где-то в другом месте.) –

+0

Для обработки левых рекурсивных правил в левом рекурсивном синтаксическом анализаторе см. Http: // stackoverflow. com/questions/2245962/is-there-an-an-an-for-flex-bison-that-is-use-on-8-bit-embedded-systems/2336769 # 2336769 –

ответ

2

Ваша проблема в том, что рекурсивный спуск синтаксического анализа не может обрабатывать влево рекурсивных грамматик , ваше первое производство говорит «E -> E + T», мы называем это левым рекурсивным, потому что первое, что происходит, - это то, что вы определяете. Как работает рекурсивный спуск, сначала нужно сопоставить «E», затем «+», затем «T». Проблема в том, что ваш метод «E» сначала вызывает метод «E1», который затем снова вызывает «E», который вызывает «E1» и встречает бесконечный рекурсивный цикл. Вам нужно оставить фактор вашей грамматики, если вы хотите использовать рекурсивный спуск. Я скопировал ссылку, которая содержит более подробную информацию о левом факторинге: http://en.wikipedia.org/wiki/Left_recursion. Итак, в общем, вы переполняете стек, потому что у вас бесконечный рекурсивный цикл.

0

Обычно, когда вы получаете переполнение стека, это означает, что программа рекурсивно называет один или методы без конца, поэтому давайте посмотрим, как будет работать ваша программа.

Оказывается, что ваш код выполняется с помощью метода Синтаксического (кстати в 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()).

Это часть проблемы рекурсивного анализа спуска. Это изящное решение для синтаксического анализа, но грамматика иногда требует некоторой перезаписи, иначе это точная проблема, с которой вы сталкиваетесь, где вы попадаете в ловушку правила грамматики. Для этого вам нужно переписать грамматику (см. this document on recursive descent parsing, которую я нашел с помощью быстрого поиска Google).

Существует два требования к грамматике: оно должно быть детерминированным и не может содержать левую рекурсию.

Проблема запуска остается в рекурсию, почти в каждом правиле:

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

Это говорит о том, что токен E может быть токеном E + T, и для его распознавания вам необходимо распознать токен E, который может быть E + T, ... (навсегда). Это вызвало проблему в вашей программе.

Переписывание грамматики путем устранения левого рекурсии позволит решить эту проблему и убедиться, что когда вы закончите, у вас есть детерминированная грамматика.

Смежные вопросы