public Symbol parse() { // Reset the errors count and set up problem requestor errorsDetected = 0; if (problemRequestor == null) problemRequestor = new AccumulatingSyntaxErrorRequestor(); // Set up the parse tables for local access production_tab = production_table(); action_tab = action_table(); reduce_tab = reduce_table(); // Set up the action object CUP$Parser$actions actionObject = new CUP$Parser$actions(this); // Set up the scouting stack to prevent any premature reductions in the presense of errors SlimParseStack scoutStack = new SlimParseStack(); scoutStack.shiftStartState(); // Set up the real parse stack boolean[] isNonTerminal = new boolean [INITIAL_STACK_SIZE]; // Set up an array to remember whether a given symbol on the stack // is a nonterminal Stack realStack = new Stack(); realStack.push((new Symbol(0, 0 /* start state */))); int realStackTop = 0; isNonTerminal[0] = true; // The left most node that is artificial (i.e. in error -- created by recovery). This is to // control // whether we should run parse actions or not // Value of 0 indicates that there are no error nodes on the stack int leftMostErrorNodeIndex = 0; // Perform the parsing for (_done_parsing = false; !_done_parsing; ) { // Obtain look ahead Terminal lookAhead = stream.getLookAhead(); // Test if the current lookahead can be shifted on the scouting stack // If it can, then process all the actions associated with this lookahead on the real stack // Otherwise, reset the scouting stack and perform recovery if (scoutStack.canShift(lookAhead)) { // Repeat until all the actions associated with this lookahead is performed while (true) { Symbol lookAheadSymbol = stream.getLookAheadSymbol(); int action = get_action(((Symbol) realStack.peek()).parse_state, lookAheadSymbol.sym); if (action > 0) { // Parse state of -2 on a terminal node indicates that it is artificially created by // error recovery boolean isErrorNode = lookAheadSymbol.parse_state == -2; // Perform the shift lookAheadSymbol.parse_state = action - 1; realStack.push(lookAheadSymbol); realStackTop++; try { isNonTerminal[realStackTop] = false; } catch (ArrayIndexOutOfBoundsException e) { isNonTerminal = enlargeStack(isNonTerminal); isNonTerminal[realStackTop] = false; } // Record the new error situation if applicable if (isErrorNode) { leftMostErrorNodeIndex = (leftMostErrorNodeIndex == 0 ? realStackTop : Math.min(leftMostErrorNodeIndex, realStackTop)); } // After we have shifted, should break out of the action loop break; } else { // Information about the current rule int nonTerminalType = production_tab[(-action) - 1][0]; int handleSize = production_tab[(-action) - 1][1]; Symbol nonTerminal; // We run actions normally if there are no errors nodes on stack if (leftMostErrorNodeIndex == 0) { try { nonTerminal = actionObject.CUP$Parser$do_action((-action) - 1, this, realStack, realStackTop); } catch (Exception e) { throw new RuntimeException("Parse action failed with exception", e); } } else { // Create the non-terminal symbol ourselves int left = ((Symbol) realStack.elementAt( realStackTop - handleSize + (handleSize == 0 ? 0 : 1))) .left; int right = ((Symbol) realStack.peek()).right; nonTerminal = new Symbol(nonTerminalType, left, right); // Remember whether we have really nulled out an error node boolean nullable = false; // We have to simulate the actions for sequence nodes and some special opt nodes if (scoutStack.isConstructPlus(nonTerminalType)) { nullable = true; if (handleSize == 1) { nonTerminal.value = new ArrayList(); } else { Symbol elementAt = (Symbol) realStack.elementAt(realStackTop - handleSize + 1); Object value = (elementAt).value; if (Collections.EMPTY_LIST == value && scoutStack.isConstructStar(elementAt.sym)) { nonTerminal.value = new ArrayList(); } else if (value == null) { nonTerminal.value = new ArrayList(); } else { nonTerminal.value = value; } } } else { nullable = true; switch (nonTerminalType) { case NodeTypes.fieldsOpt: nonTerminal.value = Collections.EMPTY_LIST; case NodeTypes.returnsOpt: case NodeTypes.partSubTypeOpt: case NodeTypes.packageDeclarationOpt: break; default: nullable = false; } } // If we have nulled out a nonterminal and there are no more error nodes on stack if (nullable && realStackTop - handleSize < leftMostErrorNodeIndex) { leftMostErrorNodeIndex = 0; } } // Pop the stack for (int i = 0; i < handleSize; i++) { realStack.pop(); realStackTop--; } // Push the newly created nonterminal back to the stack nonTerminal.parse_state = get_reduce(((Symbol) realStack.peek()).parse_state, nonTerminalType); realStack.push(nonTerminal); realStackTop++; try { isNonTerminal[realStackTop] = true; } catch (ArrayIndexOutOfBoundsException e) { isNonTerminal = enlargeStack(isNonTerminal); isNonTerminal[realStackTop] = true; } // The left most error node may have been absorbed into this new node // So we have may have to adjust leftMostErrorNodeIndex if (realStackTop < leftMostErrorNodeIndex) { leftMostErrorNodeIndex = realStackTop; } // If we have reduced to $START, we should break out of the action loop if (nonTerminalType == NodeTypes.$START) break; } } // Repeat until all the actions associated with this lookahead is performed // Advance look ahead stream.advanceLookAhead(); } else { // Recover from the error int errorNodeIndex = recover(realStack, isNonTerminal); // Reset the scout stack to be in sync with the real stack scoutStack.reset(realStack); // Check if leftMostErrorNodeIndex needs to be updated if (errorNodeIndex != 0) { leftMostErrorNodeIndex = (leftMostErrorNodeIndex == 0 ? errorNodeIndex : Math.min(leftMostErrorNodeIndex, errorNodeIndex)); } // Update the realStackTop because the error recovery may modify realStack but they don't // have access to this variable realStackTop = realStack.size() - 1; // Phrase recovery and panic recovery and dump symbols from stack, it may have dumped an // error node if (realStackTop < leftMostErrorNodeIndex) { leftMostErrorNodeIndex = 0; } } } // Perform the parsing // Record syntax errors File file = (File) ((Symbol) realStack.peek()).value; if (problemRequestor instanceof AccumulatingSyntaxErrorRequestor) { List errors = ((AccumulatingSyntaxErrorRequestor) problemRequestor).getSyntaxErrors(); errors.addAll(stream.getLexerErrors()); file.setSyntaxErrors(errors); } // Record whitespaces Scanner lexer = stream.getLexer(); if (lexer instanceof Lexer) { Lexer eglLexer = (Lexer) lexer; file.blockComments = eglLexer.blockComments; file.lineBreaks = eglLexer.lineBreaks; file.lineComments = eglLexer.lineComments; } // Return the value return (Symbol) realStack.peek(); }