/******************************************************************************* * 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 AdvancedPhraseRecovery extends AbstractRecovery { public static final int INPUT_DELETION_LIMIT = 15; private RecoveryConfiguration bestConfiguration; private static class RecoveryConfiguration { private int stackDeleted; private int inputDeleted; private int nonTerminalSubstitution; private int parseCheckDistance; public RecoveryConfiguration(int stackDeleted, int inputDeleted, int nonTerminalSubstitution, int parseCheckDistance) { this.stackDeleted = stackDeleted; this.inputDeleted = inputDeleted; this.nonTerminalSubstitution = nonTerminalSubstitution; this.parseCheckDistance = parseCheckDistance; } public boolean isBetterThan(RecoveryConfiguration other) { if(this.parseCheckDistance < ErrorCorrectingParser.RECOVERY_SUCCESS) return false; if(other == null) return true; // The number of tokens deleted is the most import thing int thisTotal = this.inputDeleted + this.stackDeleted; int otherTotal = other.inputDeleted + other.stackDeleted; if(thisTotal < otherTotal) { return true; } else { return false; } } } public AdvancedPhraseRecovery(ParseStack errorStack, Stack realStack, ITokenStream tokenStream, ISyntaxErrorRequestor problemRequestor) { super(errorStack, realStack, tokenStream, problemRequestor); } protected void performTrial() { ParseStack poppingStack = errorStack.createCopy(); for(int stackDeleted = 0; !poppingStack.isEmpty(); stackDeleted++) { // Perform the trials on this popped stack performTrial(poppingStack, stackDeleted); // Pop one more of the stack symbols from the parse stack poppingStack.pop(); poppingStack.breakRightEdge(); } } protected void performTrial(ParseStack poppedStack, int stackDeleted) { for(int inputDeleted = 0; inputDeleted < INPUT_DELETION_LIMIT; inputDeleted++) { // Set up the stream ITokenStream trialStream = tokenStream.createTokenStreamAtOffset(inputDeleted); // Try nonterminal substitution first short[] nonTerminalCandidates = poppedStack.getNonTerminalCandidates(poppedStack.getCurrentState()); for(int i = 0; i < nonTerminalCandidates.length; i++) { int nonTerminalCandidate = nonTerminalCandidates[i]; ParseStack trialStack = poppedStack.createCopy(); trialStack.processNonTerminal(nonTerminalCandidate); int parseCheckDistance = trialStack.parseCheck(trialStream); RecoveryConfiguration configuration = new RecoveryConfiguration(stackDeleted, inputDeleted, nonTerminalCandidate, parseCheckDistance); if(configuration.isBetterThan(bestConfiguration)) { bestConfiguration = configuration; } // If this is already a successful recovery, no other recovery of this stack depth will be better // since they will delete more input tokens if(parseCheckDistance > ErrorCorrectingParser.RECOVERY_SUCCESS) { return; } } // Try just deleting the tokens { ParseStack trialStack = poppedStack.createCopy(); int parseCheckDistance = trialStack.parseCheck(trialStream); RecoveryConfiguration configuration = new RecoveryConfiguration(stackDeleted, inputDeleted, -1, parseCheckDistance); if(configuration.isBetterThan(bestConfiguration)) { bestConfiguration = configuration; } // If this is already a successful recovery, no other recovery of this stack depth will be better // since they will delete more input tokens if(parseCheckDistance > ErrorCorrectingParser.RECOVERY_SUCCESS) { return; } } } } public float getMisspellingIndex() { return 0; } public int getParseCheckDistance() { return bestConfiguration == null ? 0 : bestConfiguration.parseCheckDistance; } public int getNumTokensDeleted() { return bestConfiguration == null ? 0 : bestConfiguration.inputDeleted; } public int performRecovery() { // Remember the region that is deleted int deleteStart = tokenStream.getLookAhead().left; int deleteEnd = errorStack.getStackRightEdge(); // Pop the error stack and real stack for(int i = 0; i < bestConfiguration.stackDeleted; i++) { // Remember the region that is deleted deleteStart = errorStack.getStackLeftEdge(); // Actually the pop the stacks errorStack.pop(); realStack.pop(); // Remember to break the right edge (see Crash6.egl) errorStack.breakRightEdge(); ((Symbol) realStack.peek()).parse_state = errorStack.getCurrentState(); // Use the error stack as a hint -- not that break right edge currently doesn't alter state size } // Advance the real token stream -- affects both parsers for(int i = 0; i < bestConfiguration.inputDeleted; i++) { deleteEnd = tokenStream.getLookAhead().right; tokenStream.advanceLookAhead(); } if(bestConfiguration.nonTerminalSubstitution != -1) { // Push the artificial non-terminal into the real stack int currentState = errorStack.getCurrentState(); int gotoState = errorStack.get_reduce(currentState, bestConfiguration.nonTerminalSubstitution); if(gotoState != -1) { Symbol nonTerminal = new Symbol(bestConfiguration.nonTerminalSubstitution, gotoState); realStack.push(nonTerminal); // Push the substituted non-terminal into the error stack errorStack.processNonTerminal(bestConfiguration.nonTerminalSubstitution); // Report problem problemRequestor.incorrectPhrase(bestConfiguration.nonTerminalSubstitution, deleteStart, deleteEnd); // The nonterminal is artificially created return realStack.size() - 1; } else { problemRequestor.unexpectedPhrase(deleteStart, deleteEnd); return 0; } } else { problemRequestor.unexpectedPhrase(deleteStart, deleteEnd); return 0; } } }