/******************************************************************************* * Copyright © 2011, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.compiler.core.ast; import java.util.Stack; import java_cup.runtime.Symbol; /** * @author winghong */ public class PreviousNonTerminalInsertionRecovery extends AbstractRecovery { private int parseCheckDistance; private int missingNonTerminal; public PreviousNonTerminalInsertionRecovery(ParseStack errorStack, Stack realStack, ITokenStream tokenStream, ISyntaxErrorRequestor problemRequestor) { super(errorStack, realStack, tokenStream, problemRequestor); } protected void performTrial() { ParseStack previousStack = errorStack.createCopy(); Terminal previousTerminal = previousStack.undoLastTerminal(); if(previousTerminal == null) return; // In some rare cases where an error has just occurred (see crash1), there is no previous terminal short[] nonterminalCandidates = previousStack.getNonTerminalCandidates(previousStack.getCurrentState()); for(int i = 0; i < nonterminalCandidates.length; i++) { // Process the inserted non terminal ParseStack trialStack = previousStack.createCopy(); trialStack.processNonTerminal(nonterminalCandidates[i]); // Process the previous terminal if(trialStack.canShift(previousTerminal)) { trialStack.processLookAhead(previousTerminal); } else { continue; // Some times the previous terminal is not actually in the follow set of the subsituted nonterminal; } int trialDistance = trialStack.parseCheck(tokenStream); if(trialDistance > parseCheckDistance) { parseCheckDistance = trialDistance; missingNonTerminal = nonterminalCandidates[i]; } } } public float getMisspellingIndex() { return 0; } public int getParseCheckDistance() { return parseCheckDistance; } public int performRecovery() { // Unwind the last processed terminal on the error stack Terminal previousTerminal = errorStack.undoLastTerminal(); // Unwind the last processed terminal by using the error stack as a hint // So that we don't have to repeat all the logic Symbol previousSymbol = (Symbol) realStack.peek(); int errorStackTop = errorStack.getStackTop(); for(int i = realStack.size(); i > errorStackTop + 1; i--) { realStack.pop(); } ((Symbol) realStack.peek()).parse_state = errorStack.getCurrentState(); // Compute the logical nonterminal substituted ParseStack messageStack = errorStack.createCopy(); messageStack.processNonTerminal(missingNonTerminal); int highestNonTerminalType = messageStack.getHighestNonTerminal(previousTerminal.symbolType); problemRequestor.incorrectPreviousNonTerminal(previousTerminal.left, previousTerminal.right, highestNonTerminalType); // Push the artificial non-terminal into the real stack int currentState = errorStack.getCurrentState(); int gotoState = errorStack.get_reduce(currentState, highestNonTerminalType); realStack.push(new Symbol(highestNonTerminalType, gotoState)); // Process the inserted nonterminal on the error stack errorStack.processNonTerminal(highestNonTerminalType); // Put the previous terminal back into processing tokenStream.rollBack(previousTerminal, previousSymbol); // The nonterminal pushed is artificially constructed return realStack.size() - 1; } }