/*******************************************************************************
* Copyright © 2012, 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.ide.core.internal.errors;
import org.eclipse.edt.ide.core.internal.model.document.EGLNodeNameUtility;
public class InvalidPhraseRecoverer extends AbstractRecoverer {
int inputDeleted;
int contextDeleted;
int nonTerminalSubstitution;
boolean performDefaultReductions = true;
public int recoverDistance(ParseStack stack, TokenStream tokenStream) {
if (performDefaultReductions) {
stack = stack.copy();
stack.performDefaultReductions();
}
int totalDeleted = 1;
while(true) {
for (contextDeleted = 0; contextDeleted <= stack.availableContext() && contextDeleted <= totalDeleted; contextDeleted++) {
inputDeleted = totalDeleted - contextDeleted;
if(inputDeleted > tokenStream.numTokensLeft()) {
continue;
}
if(recoverDistance(inputDeleted, contextDeleted, stack, tokenStream) >= SUCCESS_DISTANCE) {
return SUCCESS_DISTANCE;
}
}
totalDeleted++;
}
}
private int recoverDistance(int inputDeleted, int contextDeleted, ParseStack stack, TokenStream tokenStream) {
// Try deleting the phrase first
if(deletePhrase(inputDeleted, contextDeleted, stack, tokenStream) >= SUCCESS_DISTANCE) return SUCCESS_DISTANCE;
// Try substitution with different nonterminals
ParseStack localStack = stack.copy();
localStack.deleteContext(contextDeleted);
// Try the different missing nonTerminals
short[] nonTerminalCandidates = grammar.getNonTerminalCandidates(localStack.getCurrentState());
for (int i = 0; i < nonTerminalCandidates.length; i++) {
if(substitutePhrase(nonTerminalCandidates[i], inputDeleted, contextDeleted, stack, tokenStream) >= SUCCESS_DISTANCE) {
return SUCCESS_DISTANCE;
}
}
return 0;
}
private int deletePhrase(int inputDeleted, int contextDeleted, ParseStack stack, TokenStream tokenStream) {
nonTerminalSubstitution = -1;
stack = stack.copy();
tokenStream = tokenStream.copy();
stack.deleteContext(contextDeleted);
if(!tokenStream.deleteInput(inputDeleted)) return 0;
int distance = tryParseAhead(stack, tokenStream);
return distance;
}
private int substitutePhrase(int ntSubstitute, int inputDeleted, int contextDeleted, ParseStack stack, TokenStream tokenStream) {
nonTerminalSubstitution = ntSubstitute;
stack = stack.copy();
tokenStream = tokenStream.copy();
stack.deleteContext(contextDeleted);
if(!tokenStream.deleteInput(inputDeleted)) return 0;
stack.shift(new ErrorNonTerminalNode(ntSubstitute));
int distance = tryParseAhead(stack, tokenStream);
return distance;
}
private void specialRecover(ParseStack stack, TokenStream tokenStream) {
stack = stack.copy();
tokenStream = tokenStream.copy();
System.out.println("Stack Symbols:");
ParseNode[] stackSymbols = stack.deleteContext(contextDeleted);
for (int i = 0; i < stackSymbols.length; i++) {
System.out.println(" " + ParseTreePrinter.getLabel(stackSymbols[i]));
}
System.out.println("Input Symbols:");
ParseNode[] inputSymbols = new ParseNode[inputDeleted];
for(int i = 0; i < inputDeleted; i++) {
inputSymbols[i] = tokenStream.lookAhead();
System.out.println(" " + inputSymbols[i]);
tokenStream.shift();
}
int[] symbols = new int[] {
ErrorNodeTypes.ID, 1,
ErrorNodeTypes.END, 0
};
}
public void recover(ParseStack stack, TokenStream tokenStream) {
if (performDefaultReductions) {
stack.performDefaultReductions();
}
if(nonTerminalSubstitution == ErrorNodeTypes.ErrorNode) {
specialRecover(stack, tokenStream);
}
// Remove the stack context needs to be deleted
ParseNode[] deletedStackSymbols = stack.deleteContext(contextDeleted);
// Remove the lookahead that need to be deleted
tokenStream.deleteInput(inputDeleted);
ParseNode[] deletedInputSymbols = tokenStream.getUnprocessedTerminals();
// Form the concatenation of the deleted symbols
ParseNode[] deletedSymbols = new ParseNode[deletedStackSymbols.length + deletedInputSymbols.length];
System.arraycopy(deletedStackSymbols, 0, deletedSymbols, 0, deletedStackSymbols.length);
System.arraycopy(deletedInputSymbols, 0, deletedSymbols, deletedStackSymbols.length, deletedInputSymbols.length);
// Chain all the deleted symbols up as a whitespace
ParseNode deletedNode = chainNodes(deletedSymbols);
// Perform the stack repairs based on whether it is a deletion recovery or a subsitution recovery
if(nonTerminalSubstitution >= 0) { // Subsitution
// Cause the stack to shift the error node
ParseNode substitutionNode = new ErrorNonTerminalNode(nonTerminalSubstitution, deletedNode);
stack.shift(substitutionNode);
}
else { // Deletion
// Create an error node to hold on to the deleted symbols
ParseNode errorNode = new ErrorNonTerminalNode(ErrorNodeTypes.ErrorNode, deletedNode);
// Connect the deleted symbols as whitespace
stack.connect(errorNode);
}
// Report the error
TerminalNode errorTerminal = findFirstNonWSTerminal(deletedStackSymbols);
if(errorTerminal == null) {
errorTerminal = findFirstNonWSTerminal(deletedInputSymbols);
}
errorMessage(stack, tokenStream, errorTerminal);
}
public void errorMessage(ParseStack stack, TokenStream tokenStream, TerminalNode errorTerminal) {
TerminalNode startErrorTerminal = errorTerminal;
TerminalNode endErrorTerminal = tokenStream.previousNonWSTerminal(tokenStream.lookAhead());
String message = "\tThe phrase \"" + errorTerminal.text + " ... " + endErrorTerminal.text;
if(nonTerminalSubstitution < 0) {
message += "\" is unexpected " + contextDeleted + ":" + inputDeleted;
}
else {
message += "\" is not a valid " + EGLNodeNameUtility.getNonterminalName(nonTerminalSubstitution) + " " + contextDeleted + ":" + inputDeleted;
}
ErrorMarkerCollector.instance.add(startErrorTerminal.offset, endErrorTerminal.offset + endErrorTerminal.text.length(), startErrorTerminal.line, message);
}
}