/*******************************************************************************
* 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 InvalidTerminalRecoverer extends AbstractRecoverer {
TerminalNode bestCandidate;
int bestDistance = -1;
public int recoverDistance(ParseStack stack, TokenStream tokenStream) {
// Reset the recovery distance
bestCandidate = null;
bestDistance = -1;
// Try the different missing terminals
short[] terminalCandidates = grammar.getTerminalCandidates(stack.getCurrentState());
for (int i = 0; i < terminalCandidates.length; i++) {
int curDistance = recoveryDistance(terminalCandidates[i], stack, tokenStream);
if(curDistance > bestDistance) {
bestCandidate = new ErrorTerminalNode(terminalCandidates[i]);
bestDistance = curDistance;
}
}
return bestDistance;
}
private int recoveryDistance(int candidate, ParseStack stack, TokenStream tokenStream) {
if(isImportantTerminal(tokenStream.lookAhead())) return 0;
stack = stack.copy();
tokenStream = tokenStream.copy();
tokenStream.deleteInput(1);
processTerminal(stack, new ErrorTerminalNode(candidate));
int distance = tryParseAhead(stack, tokenStream);
Reporter.getInstance().invalidTerminalDistance(candidate, distance);
return distance;
}
public void recover(ParseStack stack, TokenStream tokenStream) {
// The tracing stuff will be improved or removed later
Reporter.getInstance().recoverInvalidTerminal(tokenStream.lookAhead().text, bestCandidate.terminalType);
// Report the error
errorMessage(stack, tokenStream, tokenStream.lookAhead());
// Remove the lookahead that need to be deleted
tokenStream.deleteInput(1);
ParseNode[] substitutedSymbols = tokenStream.getUnprocessedTerminals();
// Figure out what the highest symbol is
ParseStack tempStack = stack.copy();
processTerminal(tempStack, bestCandidate);
ParseNode highestSymbol = getHighestSymbol(tempStack, tokenStream.lookAhead());
// First we need to perform all the reductions
performAllReductions(stack, bestCandidate);
// Get all the substituted symbols chained up
ParseNode substitutedNode = chainNodes(substitutedSymbols);
// Attach the substitued symbols to the error node (the type of error nodes depend
// on whether there is any chain-reduction on the substituted symbol
ParseNode errorNode;
if(highestSymbol.isTerminal()) {
TerminalNode hsTerminal = (TerminalNode) highestSymbol;
errorNode = new ErrorTerminalNode(hsTerminal.terminalType, substitutedNode);
}
else {
NonTerminalNode hsNonTerminal = (NonTerminalNode) highestSymbol;
errorNode = new ErrorNonTerminalNode(hsNonTerminal.nonTerminalType, substitutedNode);
}
// Shift the error node
stack.shift(errorNode);
}
public void errorMessage(ParseStack stack, TokenStream tokenStream, TerminalNode errorTerminal) {
ParseStack tempStack = stack.copy();
processTerminal(tempStack, bestCandidate);
TokenStream tempTokenStream = tokenStream.copy();
tempTokenStream.deleteInput(1);
String highestSymbolName = getHighestSymbolName(tempStack, tempTokenStream.lookAhead());
String message;
if(highestSymbolName.toUpperCase().equals(highestSymbolName)) {
message = "\t\"" + errorTerminal + "\" is unexpected, expecting " + EGLNodeNameUtility.getTerminalName(bestCandidate.terminalType) + " instead.";
}
else {
message = "\t\"" + errorTerminal + "\" is an invalid " + highestSymbolName;
}
ErrorMarkerCollector.instance.add(errorTerminal.offset, errorTerminal.offset + errorTerminal.text.length(), errorTerminal.line, message);
}
}