/** * Modified error recovery: tries to do a reduce if no shift out can be found. * * @param debug * @return <code>true</code> if error recovery was successful * @throws Exception */ @Override protected boolean error_recovery(boolean debug) throws Exception { /* the current action code */ int act; /* the Symbol/stack element returned by a reduce */ Symbol lhs_sym = null; /* information about production being reduced with */ short handle_size, lhs_sym_num; for (_done_parsing = false; !_done_parsing; ) { act = get_action(((Symbol) stack.peek()).parse_state, error_sym()); /* decode the action -- > 0 encodes shift */ if (act > 0) { // stop break; } /* if its less than zero, then it encodes a reduce action */ else if (act < 0) { /* perform the action for the reduce */ lhs_sym = do_action((-act) - 1, this, stack, tos); /* look up information about the production */ lhs_sym_num = production_tab[(-act) - 1][0]; handle_size = production_tab[(-act) - 1][1]; if (debug) debug_reduce((-act) - 1, lhs_sym_num, handle_size); /* pop the handle off the stack */ for (int i = 0; i < handle_size; i++) { stack.pop(); tos--; } /* look up the state to go to from the one popped back to */ act = get_reduce(((Symbol) stack.peek()).parse_state, lhs_sym_num); if (debug) debug_message( "# Reduce rule: top state " + ((Symbol) stack.peek()).parse_state + ", lhs sym " + lhs_sym_num + " -> state " + act); /* shift to that state */ lhs_sym.parse_state = act; // not accessible from here, just forget it. Let's just assume // our lexer does what he should do... // lhs_sym.used_by_parser = true; stack.push(lhs_sym); tos++; if (debug) debug_message("# Goto state #" + act); } /* finally if the entry is zero, we have an error */ else if (act == 0) { // stop and try normal recover break; } } return super.error_recovery(debug); }
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(); }