blob: 5adee6f64c971c1087ea2be13b0c301e8eff6636 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 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.jdt.internal.compiler.parser.diagnose;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.parser.ParserBasicInformation;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
public class DiagnoseParser implements ParserBasicInformation, TerminalTokens {
private static final boolean DEBUG = false;
private boolean DEBUG_PARSECHECK = false;
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
private static final int STACK_INCREMENT = 256;
// private static final int ERROR_CODE = 1;
private static final int BEFORE_CODE = 2;
private static final int INSERTION_CODE = 3;
private static final int INVALID_CODE = 4;
private static final int SUBSTITUTION_CODE = 5;
private static final int DELETION_CODE = 6;
private static final int MERGE_CODE = 7;
private static final int MISPLACED_CODE = 8;
private static final int SCOPE_CODE = 9;
private static final int SECONDARY_CODE = 10;
private static final int EOF_CODE = 11;
private static final int BUFF_UBOUND = 31;
private static final int BUFF_SIZE = 32;
private static final int MAX_DISTANCE = 30;
private static final int MIN_DISTANCE = 3;
private CompilerOptions options;
private LexStream lexStream;
private int errorToken;
private int errorTokenStart;
private int currentToken = 0;
private int stackLength;
private int stateStackTop;
private int[] stack;
private int[] locationStack;
private int[] locationStartStack;
private int tempStackTop;
private int[] tempStack;
private int prevStackTop;
private int[] prevStack;
private int nextStackTop;
private int[] nextStack;
private int scopeStackTop;
private int[] scopeIndex;
private int[] scopePosition;
int[] list = new int[NUM_SYMBOLS + 1];
int[] buffer = new int[BUFF_SIZE];
private static final int NIL = -1;
int[] stateSeen;
int statePoolTop;
StateInfo[] statePool;
private Parser parser;
private class RepairCandidate {
public int symbol;
public int location;
public RepairCandidate(){
this.symbol = 0;
this.location = 0;
}
}
private class PrimaryRepairInfo {
public int distance;
public int misspellIndex;
public int code;
public int bufferPosition;
public int symbol;
public PrimaryRepairInfo(){
this.distance = 0;
this.misspellIndex = 0;
this.code = 0;
this.bufferPosition = 0;
this.symbol = 0;
}
public PrimaryRepairInfo copy(){
PrimaryRepairInfo c = new PrimaryRepairInfo();
c.distance = this.distance;
c.misspellIndex = this.misspellIndex;
c.code = this.code;
c.bufferPosition = this .bufferPosition;
c.symbol = this.symbol;
return c;
}
}
private class SecondaryRepairInfo {
public int code;
public int distance;
public int bufferPosition;
public int stackPosition;
public int numDeletions;
public int symbol;
boolean recoveryOnNextStack;
}
private class StateInfo {
int state;
int next;
public StateInfo(int state, int next){
this.state = state;
this.next = next;
}
}
public DiagnoseParser(Parser parser, int firstToken, int start, int end, CompilerOptions options) {
this(parser, firstToken, start, end, new int[0], new int[0], new int[0], options);
}
public DiagnoseParser(Parser parser, int firstToken, int start, int end, int[] intervalStartToSkip, int[] intervalEndToSkip, int[] intervalFlagsToSkip, CompilerOptions options) {
this.parser = parser;
this.options = options;
this.lexStream = new LexStream(BUFF_SIZE, parser.scanner, intervalStartToSkip, intervalEndToSkip, intervalFlagsToSkip, firstToken, start, end);
}
private ProblemReporter problemReporter(){
return parser.problemReporter();
}
private void reallocateStacks() {
int old_stack_length = stackLength;
stackLength += STACK_INCREMENT;
if(old_stack_length == 0){
stack = new int[stackLength];
locationStack = new int[stackLength];
locationStartStack = new int[stackLength];
tempStack = new int[stackLength];
prevStack = new int[stackLength];
nextStack = new int[stackLength];
scopeIndex = new int[stackLength];
scopePosition = new int[stackLength];
} else {
System.arraycopy(stack, 0, stack = new int[stackLength], 0, old_stack_length);
System.arraycopy(locationStack, 0, locationStack = new int[stackLength], 0, old_stack_length);
System.arraycopy(locationStartStack, 0, locationStartStack = new int[stackLength], 0, old_stack_length);
System.arraycopy(tempStack, 0, tempStack = new int[stackLength], 0, old_stack_length);
System.arraycopy(prevStack, 0, prevStack = new int[stackLength], 0, old_stack_length);
System.arraycopy(nextStack, 0, nextStack = new int[stackLength], 0, old_stack_length);
System.arraycopy(scopeIndex, 0, scopeIndex = new int[stackLength], 0, old_stack_length);
System.arraycopy(scopePosition, 0, scopePosition = new int[stackLength], 0, old_stack_length);
}
return;
}
public void diagnoseParse() {
lexStream.reset();
currentToken = lexStream.getToken();
int prev_pos;
int pos;
int next_pos;
int act = START_STATE;
reallocateStacks();
//
// Start parsing
//
stateStackTop = 0;
stack[stateStackTop] = act;
int tok = lexStream.kind(currentToken);
locationStack[stateStackTop] = currentToken;
locationStartStack[stateStackTop] = lexStream.start(currentToken);
boolean forceRecoveryAfterLBracketMissing = false;
// int forceRecoveryToken = -1;
//
// Process a terminal
//
do {
//
// Synchronize state stacks and update the location stack
//
prev_pos = -1;
prevStackTop = -1;
next_pos = -1;
nextStackTop = -1;
pos = stateStackTop;
tempStackTop = stateStackTop - 1;
for (int i = 0; i <= stateStackTop; i++)
tempStack[i] = stack[i];
act = Parser.tAction(act, tok);
//
// When a reduce action is encountered, we compute all REDUCE
// and associated goto actions induced by the current token.
// Eventually, a SHIFT, SHIFT-REDUCE, ACCEPT or ERROR action is
// computed...
//
while (act <= NUM_RULES) {
do {
tempStackTop -= (Parser.rhs[act]-1);
act = Parser.ntAction(tempStack[tempStackTop], Parser.lhs[act]);
} while(act <= NUM_RULES);
//
// ... Update the maximum useful position of the
// (STATE_)STACK, push goto state into stack, and
// compute next action on current symbol ...
//
if (tempStackTop + 1 >= stackLength)
reallocateStacks();
pos = pos < tempStackTop ? pos : tempStackTop;
tempStack[tempStackTop + 1] = act;
act = Parser.tAction(act, tok);
}
//
// At this point, we have a shift, shift-reduce, accept or error
// action. STACK contains the configuration of the state stack
// prior to executing any action on curtok. next_stack contains
// the configuration of the state stack after executing all
// reduce actions induced by curtok. The variable pos indicates
// the highest position in STACK that is still useful after the
// reductions are executed.
//
while(act > ERROR_ACTION || act < ACCEPT_ACTION) { // SHIFT-REDUCE action or SHIFT action ?
nextStackTop = tempStackTop + 1;
for (int i = next_pos + 1; i <= nextStackTop; i++)
nextStack[i] = tempStack[i];
for (int i = pos + 1; i <= nextStackTop; i++) {
locationStack[i] = locationStack[stateStackTop];
locationStartStack[i] = locationStartStack[stateStackTop];
}
//
// If we have a shift-reduce, process it as well as
// the goto-reduce actions that follow it.
//
if (act > ERROR_ACTION) {
act -= ERROR_ACTION;
do {
nextStackTop -= (Parser.rhs[act]-1);
act = Parser.ntAction(nextStack[nextStackTop], Parser.lhs[act]);
} while(act <= NUM_RULES);
pos = pos < nextStackTop ? pos : nextStackTop;
}
if (nextStackTop + 1 >= stackLength)
reallocateStacks();
tempStackTop = nextStackTop;
nextStack[++nextStackTop] = act;
next_pos = nextStackTop;
//
// Simulate the parser through the next token without
// destroying STACK or next_stack.
//
currentToken = lexStream.getToken();
tok = lexStream.kind(currentToken);
act = Parser.tAction(act, tok);
while(act <= NUM_RULES) {
//
// ... Process all goto-reduce actions following
// reduction, until a goto action is computed ...
//
do {
int lhs_symbol = Parser.lhs[act];
if(DEBUG) {
System.out.println(Parser.name[Parser.non_terminal_index[lhs_symbol]]);
}
tempStackTop -= (Parser.rhs[act]-1);
act = (tempStackTop > next_pos
? tempStack[tempStackTop]
: nextStack[tempStackTop]);
act = Parser.ntAction(act, lhs_symbol);
} while(act <= NUM_RULES);
//
// ... Update the maximum useful position of the
// (STATE_)STACK, push GOTO state into stack, and
// compute next action on current symbol ...
//
if (tempStackTop + 1 >= stackLength)
reallocateStacks();
next_pos = next_pos < tempStackTop ? next_pos : tempStackTop;
tempStack[tempStackTop + 1] = act;
act = Parser.tAction(act, tok);
}
// if((tok != TokenNameRBRACE || (forceRecoveryToken != currentToken && (lexStream.flags(currentToken) & LexStream.LBRACE_MISSING) != 0))
// && (lexStream.flags(currentToken) & LexStream.IS_AFTER_JUMP) !=0) {
// act = ERROR_ACTION;
// if(forceRecoveryToken != currentToken
// && (lexStream.flags(currentToken) & LexStream.LBRACE_MISSING) != 0) {
// forceRecoveryAfterLBracketMissing = true;
// forceRecoveryToken = currentToken;
// }
// }
//
// No error was detected, Read next token into
// PREVTOK element, advance CURTOK pointer and
// update stacks.
//
if (act != ERROR_ACTION) {
prevStackTop = stateStackTop;
for (int i = prev_pos + 1; i <= prevStackTop; i++)
prevStack[i] = stack[i];
prev_pos = pos;
stateStackTop = nextStackTop;
for (int i = pos + 1; i <= stateStackTop; i++)
stack[i] = nextStack[i];
locationStack[stateStackTop] = currentToken;
locationStartStack[stateStackTop] = lexStream.start(currentToken);
pos = next_pos;
}
}
//
// At this stage, either we have an ACCEPT or an ERROR
// action.
//
if (act == ERROR_ACTION) {
//
// An error was detected.
//
RepairCandidate candidate = errorRecovery(currentToken, forceRecoveryAfterLBracketMissing);
forceRecoveryAfterLBracketMissing = false;
if(parser.reportOnlyOneSyntaxError) {
return;
}
if(this.parser.problemReporter().options.maxProblemsPerUnit < this.parser.compilationUnit.compilationResult.problemCount) {
return;
}
act = stack[stateStackTop];
//
// If the recovery was successful on a nonterminal candidate,
// parse through that candidate and "read" the next token.
//
if (candidate.symbol == 0) {
break;
} else if (candidate.symbol > NT_OFFSET) {
int lhs_symbol = candidate.symbol - NT_OFFSET;
if(DEBUG) {
System.out.println(Parser.name[Parser.non_terminal_index[lhs_symbol]]);
}
act = Parser.ntAction(act, lhs_symbol);
while(act <= NUM_RULES) {
stateStackTop -= (Parser.rhs[act]-1);
act = Parser.ntAction(stack[stateStackTop], Parser.lhs[act]);
}
stack[++stateStackTop] = act;
currentToken = lexStream.getToken();
tok = lexStream.kind(currentToken);
locationStack[stateStackTop] = currentToken;
locationStartStack[stateStackTop] = lexStream.start(currentToken);
} else {
tok = candidate.symbol;
locationStack[stateStackTop] = candidate.location;
locationStartStack[stateStackTop] = lexStream.start(candidate.location);
}
}
} while (act != ACCEPT_ACTION);
return;
}
//
// This routine is invoked when an error is encountered. It
// tries to diagnose the error and recover from it. If it is
// successful, the state stack, the current token and the buffer
// are readjusted; i.e., after a successful recovery,
// state_stack_top points to the location in the state stack
// that contains the state on which to recover; curtok
// identifies the symbol on which to recover.
//
// Up to three configurations may be available when this routine
// is invoked. PREV_STACK may contain the sequence of states
// preceding any action on prevtok, STACK always contains the
// sequence of states preceding any action on curtok, and
// NEXT_STACK may contain the sequence of states preceding any
// action on the successor of curtok.
//
private RepairCandidate errorRecovery(int error_token, boolean forcedError) {
this.errorToken = error_token;
this.errorTokenStart = lexStream.start(error_token);
int prevtok = lexStream.previous(error_token);
int prevtokKind = lexStream.kind(prevtok);
if(forcedError) {
int name_index = Parser.terminal_index[TokenNameLBRACE];
reportError(INSERTION_CODE, name_index, prevtok, prevtok);
RepairCandidate candidate = new RepairCandidate();
candidate.symbol = TokenNameLBRACE;
candidate.location = error_token;
lexStream.reset(error_token);
stateStackTop = nextStackTop;
for (int j = 0; j <= stateStackTop; j++) {
stack[j] = nextStack[j];
}
locationStack[stateStackTop] = error_token;
locationStartStack[stateStackTop] = lexStream.start(error_token);
return candidate;
}
//
// Try primary phase recoveries. If not successful, try secondary
// phase recoveries. If not successful and we are at end of the
// file, we issue the end-of-file error and quit. Otherwise, ...
//
RepairCandidate candidate = primaryPhase(error_token);
if (candidate.symbol != 0) {
return candidate;
}
candidate = secondaryPhase(error_token);
if (candidate.symbol != 0) {
return candidate;
}
if (lexStream.kind(error_token) == EOFT_SYMBOL) {
reportError(EOF_CODE,
Parser.terminal_index[EOFT_SYMBOL],
prevtok,
prevtok);
candidate.symbol = 0;
candidate.location = error_token;
return candidate;
}
//
// At this point, primary and (initial attempt at) secondary
// recovery did not work. We will now get into "panic mode" and
// keep trying secondary phase recoveries until we either find
// a successful recovery or have consumed the remaining input
// tokens.
//
while(lexStream.kind(buffer[BUFF_UBOUND]) != EOFT_SYMBOL) {
candidate = secondaryPhase(buffer[MAX_DISTANCE - MIN_DISTANCE + 2]);
if (candidate.symbol != 0) {
return candidate;
}
}
//
// We reached the end of the file while panicking. Delete all
// remaining tokens in the input.
//
int i;
for (i = BUFF_UBOUND; lexStream.kind(buffer[i]) == EOFT_SYMBOL; i--){/*empty*/}
reportError(DELETION_CODE,
Parser.terminal_index[prevtokKind],//Parser.terminal_index[lexStream.kind(prevtok)],
error_token,
buffer[i]);
candidate.symbol = 0;
candidate.location = buffer[i];
return candidate;
}
//
// This function tries primary and scope recovery on each
// available configuration. If a successful recovery is found
// and no secondary phase recovery can do better, a diagnosis is
// issued, the configuration is updated and the function returns
// "true". Otherwise, it returns "false".
//
private RepairCandidate primaryPhase(int error_token) {
PrimaryRepairInfo repair = new PrimaryRepairInfo();
RepairCandidate candidate = new RepairCandidate();
//
// Initialize the buffer.
//
int i = (nextStackTop >= 0 ? 3 : 2);
buffer[i] = error_token;
for (int j = i; j > 0; j--)
buffer[j - 1] = lexStream.previous(buffer[j]);
for (int k = i + 1; k < BUFF_SIZE; k++)
buffer[k] = lexStream.next(buffer[k - 1]);
//
// If NEXT_STACK_TOP > 0 then the parse was successful on CURTOK
// and the error was detected on the successor of CURTOK. In
// that case, first check whether or not primary recovery is
// possible on next_stack ...
//
if (nextStackTop >= 0) {
repair.bufferPosition = 3;
repair = checkPrimaryDistance(nextStack, nextStackTop, repair);
}
//
// ... Next, try primary recovery on the current token...
//
PrimaryRepairInfo new_repair = repair.copy();
new_repair.bufferPosition = 2;
new_repair = checkPrimaryDistance(stack, stateStackTop, new_repair);
if (new_repair.distance > repair.distance || new_repair.misspellIndex > repair.misspellIndex) {
repair = new_repair;
}
//
// Finally, if prev_stack_top >= 0 then try primary recovery on
// the prev_stack configuration.
//
if (prevStackTop >= 0) {
new_repair = repair.copy();
new_repair.bufferPosition = 1;
new_repair = checkPrimaryDistance(prevStack,prevStackTop, new_repair);
if (new_repair.distance > repair.distance || new_repair.misspellIndex > repair.misspellIndex) {
repair = new_repair;
}
}
//
// Before accepting the best primary phase recovery obtained,
// ensure that we cannot do better with a similar secondary
// phase recovery.
//
if (nextStackTop >= 0) {// next_stack available
if (secondaryCheck(nextStack,nextStackTop,3,repair.distance)) {
return candidate;
}
}
else if (secondaryCheck(stack, stateStackTop, 2, repair.distance)) {
return candidate;
}
//
// First, adjust distance if the recovery is on the error token;
// it is important that the adjustment be made here and not at
// each primary trial to prevent the distance tests from being
// biased in favor of deferred recoveries which have access to
// more input tokens...
//
repair.distance = repair.distance - repair.bufferPosition + 1;
//
// ...Next, adjust the distance if the recovery is a deletion or
// (some form of) substitution...
//
if (repair.code == INVALID_CODE ||
repair.code == DELETION_CODE ||
repair.code == SUBSTITUTION_CODE ||
repair.code == MERGE_CODE) {
repair.distance--;
}
//
// ... After adjustment, check if the most successful primary
// recovery can be applied. If not, continue with more radical
// recoveries...
//
if (repair.distance < MIN_DISTANCE) {
return candidate;
}
//
// When processing an insertion error, if the token preceeding
// the error token is not available, we change the repair code
// into a BEFORE_CODE to instruct the reporting routine that it
// indicates that the repair symbol should be inserted before
// the error token.
//
if (repair.code == INSERTION_CODE) {
if (buffer[repair.bufferPosition - 1] == 0) {
repair.code = BEFORE_CODE;
}
}
//
// Select the proper sequence of states on which to recover,
// update stack accordingly and call diagnostic routine.
//
if (repair.bufferPosition == 1) {
stateStackTop = prevStackTop;
for (int j = 0; j <= stateStackTop; j++) {
stack[j] = prevStack[j];
}
} else if (nextStackTop >= 0 && repair.bufferPosition >= 3) {
stateStackTop = nextStackTop;
for (int j = 0; j <= stateStackTop; j++) {
stack[j] = nextStack[j];
}
locationStack[stateStackTop] = buffer[3];
locationStartStack[stateStackTop] = lexStream.start(buffer[3]);
}
return primaryDiagnosis(repair);
}
//
// This function checks whether or not a given state has a
// candidate, whose string representaion is a merging of the two
// tokens at positions buffer_position and buffer_position+1 in
// the buffer. If so, it returns the candidate in question;
// otherwise it returns 0.
//
private int mergeCandidate(int state, int buffer_position) {
char[] name1 = lexStream.name(buffer[buffer_position]);
char[] name2 = lexStream.name(buffer[buffer_position + 1]);
int len = name1.length + name2.length;
char[] str = CharOperation.concat(name1, name2);
for (int k = Parser.asi(state); Parser.asr[k] != 0; k++) {
int l = Parser.terminal_index[Parser.asr[k]];
if (len == Parser.name[l].length()) {
char[] name = Parser.name[l].toCharArray();
if (CharOperation.equals(str, name, false)) {
return Parser.asr[k];
}
}
}
return 0;
}
//
// This procedure takes as arguments a parsing configuration
// consisting of a state stack (stack and stack_top) and a fixed
// number of input tokens (starting at buffer_position) in the
// input BUFFER; and some reference arguments: repair_code,
// distance, misspell_index, candidate, and stack_position
// which it sets based on the best possible recovery that it
// finds in the given configuration. The effectiveness of a
// a repair is judged based on two criteria:
//
// 1) the number of tokens that can be parsed after the repair
// is applied: distance.
// 2) how close to perfection is the candidate that is chosen:
// misspell_index.
// When this procedure is entered, distance, misspell_index and
// repair_code are assumed to be initialized.
//
private PrimaryRepairInfo checkPrimaryDistance(int stck[], int stack_top, PrimaryRepairInfo repair) {
int i, j, k, next_state, max_pos, act, root, symbol, tok;
//
// First, try scope and manual recovery.
//
PrimaryRepairInfo scope_repair = scopeTrial(stck, stack_top, repair.copy());
if (scope_repair.distance > repair.distance)
repair = scope_repair;
//
// Next, try merging the error token with its successor.
//
if(buffer[repair.bufferPosition] != 0 && buffer[repair.bufferPosition + 1] != 0) {// do not merge the first token
symbol = mergeCandidate(stck[stack_top], repair.bufferPosition);
if (symbol != 0) {
j = parseCheck(stck, stack_top, symbol, repair.bufferPosition+2);
if ((j > repair.distance) || (j == repair.distance && repair.misspellIndex < 10)) {
repair.misspellIndex = 10;
repair.symbol = symbol;
repair.distance = j;
repair.code = MERGE_CODE;
}
}
}
//
// Next, try deletion of the error token.
//
j = parseCheck(
stck,
stack_top,
lexStream.kind(buffer[repair.bufferPosition + 1]),
repair.bufferPosition + 2);
if (lexStream.kind(buffer[repair.bufferPosition]) == EOLT_SYMBOL &&
lexStream.afterEol(buffer[repair.bufferPosition+1])) {
k = 10;
} else {
k = 0;
}
if (j > repair.distance || (j == repair.distance && k > repair.misspellIndex)) {
repair.misspellIndex = k;
repair.code = DELETION_CODE;
repair.distance = j;
}
//
// Update the error configuration by simulating all reduce and
// goto actions induced by the error token. Then assign the top
// most state of the new configuration to next_state.
//
next_state = stck[stack_top];
max_pos = stack_top;
tempStackTop = stack_top - 1;
tok = lexStream.kind(buffer[repair.bufferPosition]);
lexStream.reset(buffer[repair.bufferPosition + 1]);
act = Parser.tAction(next_state, tok);
while(act <= NUM_RULES) {
do {
tempStackTop -= (Parser.rhs[act]-1);
symbol = Parser.lhs[act];
act = (tempStackTop > max_pos
? tempStack[tempStackTop]
: stck[tempStackTop]);
act = Parser.ntAction(act, symbol);
} while(act <= NUM_RULES);
max_pos = max_pos < tempStackTop ? max_pos : tempStackTop;
tempStack[tempStackTop + 1] = act;
next_state = act;
act = Parser.tAction(next_state, tok);
}
//
// Next, place the list of candidates in proper order.
//
root = 0;
for (i = Parser.asi(next_state); Parser.asr[i] != 0; i++) {
symbol = Parser.asr[i];
if (symbol != EOFT_SYMBOL && symbol != ERROR_SYMBOL) {
if (root == 0) {
list[symbol] = symbol;
} else {
list[symbol] = list[root];
list[root] = symbol;
}
root = symbol;
}
}
if (stck[stack_top] != next_state) {
for (i = Parser.asi(stck[stack_top]); Parser.asr[i] != 0; i++) {
symbol = Parser.asr[i];
if (symbol != EOFT_SYMBOL && symbol != ERROR_SYMBOL && list[symbol] == 0) {
if (root == 0) {
list[symbol] = symbol;
} else {
list[symbol] = list[root];
list[root] = symbol;
}
root = symbol;
}
}
}
i = list[root];
list[root] = 0;
root = i;
//
// Next, try insertion for each possible candidate available in
// the current state, except EOFT and ERROR_SYMBOL.
//
symbol = root;
while(symbol != 0) {
if (symbol == EOLT_SYMBOL && lexStream.afterEol(buffer[repair.bufferPosition])) {
k = 10;
} else {
k = 0;
}
j = parseCheck(stck, stack_top, symbol, repair.bufferPosition);
if (j > repair.distance) {
repair.misspellIndex = k;
repair.distance = j;
repair.symbol = symbol;
repair.code = INSERTION_CODE;
} else if (j == repair.distance && k > repair.misspellIndex) {
repair.misspellIndex = k;
repair.distance = j;
repair.symbol = symbol;
repair.code = INSERTION_CODE;
} else if (j == repair.distance && k == repair.misspellIndex && isBetterSymbol(symbol, repair.symbol)) {
repair.misspellIndex = k;
repair.distance = j;
repair.symbol = symbol;
repair.code = INSERTION_CODE;
}
symbol = list[symbol];
}
//
// Next, Try substitution for each possible candidate available
// in the current state, except EOFT and ERROR_SYMBOL.
//
symbol = root;
if(buffer[repair.bufferPosition] != 0) {// do not replace the first token
while(symbol != 0) {
if (symbol == EOLT_SYMBOL && lexStream.afterEol(buffer[repair.bufferPosition+1])) {
k = 10;
} else {
k = misspell(symbol, buffer[repair.bufferPosition]);
}
j = parseCheck(stck, stack_top, symbol, repair.bufferPosition+1);
if (j > repair.distance) {
repair.misspellIndex = k;
repair.distance = j;
repair.symbol = symbol;
repair.code = SUBSTITUTION_CODE;
} else if (j == repair.distance && k > repair.misspellIndex) {
repair.misspellIndex = k;
repair.symbol = symbol;
repair.code = SUBSTITUTION_CODE;
} else if (j == repair.distance && k > repair.misspellIndex && isBetterSymbol(symbol, repair.symbol)) {
repair.misspellIndex = k;
repair.symbol = symbol;
repair.code = SUBSTITUTION_CODE;
}
i = symbol;
symbol = list[symbol];
list[i] = 0; // reset element
}
}
//
// Next, we try to insert a nonterminal candidate in front of the
// error token, or substituting a nonterminal candidate for the
// error token. Precedence is given to insertion.
//
for (i = Parser.nasi(stck[stack_top]); Parser.nasr[i] != 0; i++) {
symbol = Parser.nasr[i] + NT_OFFSET;
j = parseCheck(stck, stack_top, symbol, repair.bufferPosition+1);
if (j > repair.distance) {
repair.misspellIndex = 0;
repair.distance = j;
repair.symbol = symbol;
repair.code = INVALID_CODE;
}
j = parseCheck(stck, stack_top, symbol, repair.bufferPosition);
if ((j > repair.distance) || (j == repair.distance && repair.code == INVALID_CODE)) {
repair.misspellIndex = 0;
repair.distance = j;
repair.symbol = symbol;
repair.code = INSERTION_CODE;
}
}
return repair;
}
//
// This procedure is invoked to issue a diagnostic message and
// adjust the input buffer. The recovery in question is either
// the insertion of one or more scopes, the merging of the error
// token with its successor, the deletion of the error token,
// the insertion of a single token in front of the error token
// or the substitution of another token for the error token.
//
private RepairCandidate primaryDiagnosis(PrimaryRepairInfo repair) {
int name_index;
//
// Issue diagnostic.
//
int prevtok = buffer[repair.bufferPosition - 1];
int curtok = buffer[repair.bufferPosition];
switch(repair.code) {
case INSERTION_CODE:
case BEFORE_CODE: {
if (repair.symbol > NT_OFFSET)
name_index = getNtermIndex(stack[stateStackTop],
repair.symbol,
repair.bufferPosition);
else name_index = getTermIndex(stack,
stateStackTop,
repair.symbol,
repair.bufferPosition);
int t = (repair.code == INSERTION_CODE ? prevtok : curtok);
reportError(repair.code, name_index, t, t);
break;
}
case INVALID_CODE: {
name_index = getNtermIndex(stack[stateStackTop],
repair.symbol,
repair.bufferPosition + 1);
reportError(repair.code, name_index, curtok, curtok);
break;
}
case SUBSTITUTION_CODE: {
if (repair.misspellIndex >= 6)
name_index = Parser.terminal_index[repair.symbol];
else
{
name_index = getTermIndex(stack, stateStackTop,
repair.symbol,
repair.bufferPosition + 1);
if (name_index != Parser.terminal_index[repair.symbol])
repair.code = INVALID_CODE;
}
reportError(repair.code, name_index, curtok, curtok);
break;
}
case MERGE_CODE: {
reportError(repair.code,
Parser.terminal_index[repair.symbol],
curtok,
lexStream.next(curtok));
break;
}
case SCOPE_CODE: {
for (int i = 0; i < scopeStackTop; i++) {
reportError(repair.code,
-scopeIndex[i],
locationStack[scopePosition[i]],
prevtok,
Parser.non_terminal_index[Parser.scope_lhs[scopeIndex[i]]]);
}
repair.symbol = Parser.scope_lhs[scopeIndex[scopeStackTop]] + NT_OFFSET;
stateStackTop = scopePosition[scopeStackTop];
reportError(repair.code,
-scopeIndex[scopeStackTop],
locationStack[scopePosition[scopeStackTop]],
prevtok,
getNtermIndex(stack[stateStackTop],
repair.symbol,
repair.bufferPosition)
);
break;
}
default: {// deletion
reportError(repair.code, Parser.terminal_index[ERROR_SYMBOL], curtok, curtok);
}
}
//
// Update buffer.
//
RepairCandidate candidate = new RepairCandidate();
switch (repair.code) {
case INSERTION_CODE:
case BEFORE_CODE:
case SCOPE_CODE: {
candidate.symbol = repair.symbol;
candidate.location = buffer[repair.bufferPosition];
lexStream.reset(buffer[repair.bufferPosition]);
break;
}
case INVALID_CODE:
case SUBSTITUTION_CODE: {
candidate.symbol = repair.symbol;
candidate.location = buffer[repair.bufferPosition];
lexStream.reset(buffer[repair.bufferPosition + 1]);
break;
}
case MERGE_CODE: {
candidate.symbol = repair.symbol;
candidate.location = buffer[repair.bufferPosition];
lexStream.reset(buffer[repair.bufferPosition + 2]);
break;
}
default: {// deletion
candidate.location = buffer[repair.bufferPosition + 1];
candidate.symbol =
lexStream.kind(buffer[repair.bufferPosition + 1]);
lexStream.reset(buffer[repair.bufferPosition + 2]);
break;
}
}
return candidate;
}
//
// This function takes as parameter an integer STACK_TOP that
// points to a STACK element containing the state on which a
// primary recovery will be made; the terminal candidate on which
// to recover; and an integer: buffer_position, which points to
// the position of the next input token in the BUFFER. The
// parser is simulated until a shift (or shift-reduce) action
// is computed on the candidate. Then we proceed to compute the
// the name index of the highest level nonterminal that can
// directly or indirectly produce the candidate.
//
private int getTermIndex(int stck[], int stack_top, int tok, int buffer_position) {
//
// Initialize stack index of temp_stack and initialize maximum
// position of state stack that is still useful.
//
int act = stck[stack_top],
max_pos = stack_top,
highest_symbol = tok;
tempStackTop = stack_top - 1;
//
// Compute all reduce and associated actions induced by the
// candidate until a SHIFT or SHIFT-REDUCE is computed. ERROR
// and ACCEPT actions cannot be computed on the candidate in
// this context, since we know that it is suitable for recovery.
//
lexStream.reset(buffer[buffer_position]);
act = Parser.tAction(act, tok);
while(act <= NUM_RULES) {
//
// Process all goto-reduce actions following reduction,
// until a goto action is computed ...
//
do {
tempStackTop -= (Parser.rhs[act]-1);
int lhs_symbol = Parser.lhs[act];
act = (tempStackTop > max_pos
? tempStack[tempStackTop]
: stck[tempStackTop]);
act = Parser.ntAction(act, lhs_symbol);
} while(act <= NUM_RULES);
//
// Compute new maximum useful position of (STATE_)stack,
// push goto state into the stack, and compute next
// action on candidate ...
//
max_pos = max_pos < tempStackTop ? max_pos : tempStackTop;
tempStack[tempStackTop + 1] = act;
act = Parser.tAction(act, tok);
}
//
// At this stage, we have simulated all actions induced by the
// candidate and we are ready to shift or shift-reduce it. First,
// set tok and next_ptr appropriately and identify the candidate
// as the initial highest_symbol. If a shift action was computed
// on the candidate, update the stack and compute the next
// action. Next, simulate all actions possible on the next input
// token until we either have to shift it or are about to reduce
// below the initial starting point in the stack (indicated by
// max_pos as computed in the previous loop). At that point,
// return the highest_symbol computed.
//
tempStackTop++; // adjust top of stack to reflect last goto
// next move is shift or shift-reduce.
int threshold = tempStackTop;
tok = lexStream.kind(buffer[buffer_position]);
lexStream.reset(buffer[buffer_position + 1]);
if (act > ERROR_ACTION) { // shift-reduce on candidate?
act -= ERROR_ACTION;
} else {
tempStack[tempStackTop + 1] = act;
act = Parser.tAction(act, tok);
}
while(act <= NUM_RULES) {
//
// Process all goto-reduce actions following reduction,
// until a goto action is computed ...
//
do {
tempStackTop -= (Parser.rhs[act]-1);
if (tempStackTop < threshold) {
return (highest_symbol > NT_OFFSET
? Parser.non_terminal_index[highest_symbol - NT_OFFSET]
: Parser.terminal_index[highest_symbol]);
}
int lhs_symbol = Parser.lhs[act];
if (tempStackTop == threshold)
highest_symbol = lhs_symbol + NT_OFFSET;
act = (tempStackTop > max_pos
? tempStack[tempStackTop]
: stck[tempStackTop]);
act = Parser.ntAction(act, lhs_symbol);
} while(act <= NUM_RULES);
tempStack[tempStackTop + 1] = act;
act = Parser.tAction(act, tok);
}
return (highest_symbol > NT_OFFSET
? Parser.non_terminal_index[highest_symbol - NT_OFFSET]
: Parser.terminal_index[highest_symbol]);
}
//
// This function takes as parameter a starting state number:
// start, a nonterminal symbol, A (candidate), and an integer,
// buffer_position, which points to the position of the next
// input token in the BUFFER.
// It returns the highest level non-terminal B such that
// B =>*rm A. I.e., there does not exists a nonterminal C such
// that C =>+rm B. (Recall that for an LALR(k) grammar if
// C =>+rm B, it cannot be the case that B =>+rm C)
//
private int getNtermIndex(int start, int sym, int buffer_position) {
int highest_symbol = sym - NT_OFFSET,
tok = lexStream.kind(buffer[buffer_position]);
lexStream.reset(buffer[buffer_position + 1]);
//
// Initialize stack index of temp_stack and initialize maximum
// position of state stack that is still useful.
//
tempStackTop = 0;
tempStack[tempStackTop] = start;
int act = Parser.ntAction(start, highest_symbol);
if (act > NUM_RULES) { // goto action?
tempStack[tempStackTop + 1] = act;
act = Parser.tAction(act, tok);
}
while(act <= NUM_RULES) {
//
// Process all goto-reduce actions following reduction,
// until a goto action is computed ...
//
do {
tempStackTop -= (Parser.rhs[act]-1);
if (tempStackTop < 0)
return Parser.non_terminal_index[highest_symbol];
if (tempStackTop == 0)
highest_symbol = Parser.lhs[act];
act = Parser.ntAction(tempStack[tempStackTop], Parser.lhs[act]);
} while(act <= NUM_RULES);
tempStack[tempStackTop + 1] = act;
act = Parser.tAction(act, tok);
}
return Parser.non_terminal_index[highest_symbol];
}
private boolean isBetterSymbol(int symbol, int actualSymbol) {
// switch (actualSymbol) {
// case TokenNameinterface :
// if(symbol == TokenNameclass) {
// return true;
// }
// break;
// }
return false;
}
//
// Check whether or not there is a high probability that a
// given string is a misspelling of another.
// Certain singleton symbols (such as ":" and ";") are also
// considered to be misspelling of each other.
//
private int misspell(int sym, int tok) {
//
//
//
char[] name = Parser.name[Parser.terminal_index[sym]].toCharArray();
int n = name.length;
char[] s1 = new char[n + 1];
for (int k = 0; k < n; k++) {
char c = name[k];
s1[k] = Character.toLowerCase(c);
}
s1[n] = '\0';
//
//
//
char[] tokenName = lexStream.name(tok);
int len = tokenName.length;
int m = len < MAX_NAME_LENGTH ? len : MAX_NAME_LENGTH;
char[] s2 = new char[m + 1];
for (int k = 0; k < m; k++) {
char c = tokenName[k];
s2[k] = Character.toLowerCase(c);
}
s2[m] = '\0';
//
// Singleton mispellings:
//
// ; <----> ,
//
// ; <----> :
//
// . <----> ,
//
// ' <----> "
//
//
if (n == 1 && m == 1) {
if ((s1[0] == ';' && s2[0] == ',') ||
(s1[0] == ',' && s2[0] == ';') ||
(s1[0] == ';' && s2[0] == ':') ||
(s1[0] == ':' && s2[0] == ';') ||
(s1[0] == '.' && s2[0] == ',') ||
(s1[0] == ',' && s2[0] == '.') ||
(s1[0] == '\'' && s2[0] == '\"') ||
(s1[0] == '\"' && s2[0] == '\'')) {
return 3;
}
}
//
// Scan the two strings. Increment "match" count for each match.
// When a transposition is encountered, increase "match" count
// by two but count it as an error. When a typo is found, skip
// it and count it as an error. Otherwise we have a mismatch; if
// one of the strings is longer, increment its index, otherwise,
// increment both indices and continue.
//
// This algorithm is an adaptation of a boolean misspelling
// algorithm proposed by Juergen Uhl.
//
int count = 0;
int prefix_length = 0;
int num_errors = 0;
int i = 0;
int j = 0;
while ((i < n) && (j < m)) {
if (s1[i] == s2[j]) {
count++;
i++;
j++;
if (num_errors == 0) {
prefix_length++;
}
} else if (s1[i+1] == s2[j] && s1[i] == s2[j+1]) {
count += 2;
i += 2;
j += 2;
num_errors++;
} else if (s1[i+1] == s2[j+1]) {
i++;
j++;
num_errors++;
} else {
if ((n - i) > (m - j)) {
i++;
} else if ((m - j) > (n - i)) {
j++;
} else {
i++;
j++;
}
num_errors++;
}
}
if (i < n || j < m)
num_errors++;
if (num_errors > ((n < m ? n : m) / 6 + 1))
count = prefix_length;
return(count * 10 / ((n < len ? len : n) + num_errors));
}
private PrimaryRepairInfo scopeTrial(int stck[], int stack_top, PrimaryRepairInfo repair) {
stateSeen = new int[stackLength];
for (int i = 0; i < stackLength; i++)
stateSeen[i] = NIL;
statePoolTop = 0;
statePool = new StateInfo[stackLength];
scopeTrialCheck(stck, stack_top, repair, 0);
stateSeen = null;
statePoolTop = 0;
repair.code = SCOPE_CODE;
repair.misspellIndex = 10;
return repair;
}
private void scopeTrialCheck(int stck[], int stack_top, PrimaryRepairInfo repair, int indx) {
if(indx > 20) return; // avoid too much recursive call to improve performance
int act = stck[stack_top];
for (int i = stateSeen[stack_top]; i != NIL; i = statePool[i].next) {
if (statePool[i].state == act) return;
}
int old_state_pool_top = statePoolTop++;
if(statePoolTop >= statePool.length) {
System.arraycopy(statePool, 0, statePool = new StateInfo[statePoolTop * 2], 0, statePoolTop);
}
statePool[old_state_pool_top] = new StateInfo(act, stateSeen[stack_top]);
stateSeen[stack_top] = old_state_pool_top;
for (int i = 0; i < SCOPE_SIZE; i++) {
//
// Use the scope lookahead symbol to force all reductions
// inducible by that symbol.
//
act = stck[stack_top];
tempStackTop = stack_top - 1;
int max_pos = stack_top;
int tok = Parser.scope_la[i];
lexStream.reset(buffer[repair.bufferPosition]);
act = Parser.tAction(act, tok);
while(act <= NUM_RULES) {
//
// ... Process all goto-reduce actions following
// reduction, until a goto action is computed ...
//
do {
tempStackTop -= (Parser.rhs[act]-1);
int lhs_symbol = Parser.lhs[act];
act = (tempStackTop > max_pos
? tempStack[tempStackTop]
: stck[tempStackTop]);
act = Parser.ntAction(act, lhs_symbol);
} while(act <= NUM_RULES);
if (tempStackTop + 1 >= stackLength)
return;
max_pos = max_pos < tempStackTop ? max_pos : tempStackTop;
tempStack[tempStackTop + 1] = act;
act = Parser.tAction(act, tok);
}
//
// If the lookahead symbol is parsable, then we check
// whether or not we have a match between the scope
// prefix and the transition symbols corresponding to
// the states on top of the stack.
//
if (act != ERROR_ACTION) {
int j, k;
k = Parser.scope_prefix[i];
for (j = tempStackTop + 1;
j >= (max_pos + 1) &&
Parser.in_symbol(tempStack[j]) == Parser.scope_rhs[k]; j--) {
k++;
}
if (j == max_pos) {
for (j = max_pos;
j >= 1 && Parser.in_symbol(stck[j]) == Parser.scope_rhs[k];
j--) {
k++;
}
}
//
// If the prefix matches, check whether the state
// newly exposed on top of the stack, (after the
// corresponding prefix states are popped from the
// stack), is in the set of "source states" for the
// scope in question and that it is at a position
// below the threshold indicated by MARKED_POS.
//
int marked_pos = (max_pos < stack_top ? max_pos + 1 : stack_top);
if (Parser.scope_rhs[k] == 0 && j < marked_pos) { // match?
int stack_position = j;
for (j = Parser.scope_state_set[i];
stck[stack_position] != Parser.scope_state[j] &&
Parser.scope_state[j] != 0;
j++){/*empty*/}
//
// If the top state is valid for scope recovery,
// the left-hand side of the scope is used as
// starting symbol and we calculate how far the
// parser can advance within the forward context
// after parsing the left-hand symbol.
//
if (Parser.scope_state[j] != 0) { // state was found
int previous_distance = repair.distance;
int distance = parseCheck(stck,
stack_position,
Parser.scope_lhs[i]+NT_OFFSET,
repair.bufferPosition);
//
// if the recovery is not successful, we
// update the stack with all actions induced
// by the left-hand symbol, and recursively
// call SCOPE_TRIAL_CHECK to try again.
// Otherwise, the recovery is successful. If
// the new distance is greater than the
// initial SCOPE_DISTANCE, we update
// SCOPE_DISTANCE and set scope_stack_top to INDX
// to indicate the number of scopes that are
// to be applied for a succesful recovery.
// NOTE that this procedure cannot get into
// an infinite loop, since each prefix match
// is guaranteed to take us to a lower point
// within the stack.
//
if ((distance - repair.bufferPosition + 1) < MIN_DISTANCE) {
int top = stack_position;
act = Parser.ntAction(stck[top], Parser.scope_lhs[i]);
while(act <= NUM_RULES) {
top -= (Parser.rhs[act]-1);
act = Parser.ntAction(stck[top], Parser.lhs[act]);
}
top++;
j = act;
act = stck[top]; // save
stck[top] = j; // swap
scopeTrialCheck(stck, top, repair, indx+1);
stck[top] = act; // restore
} else if (distance > repair.distance) {
scopeStackTop = indx;
repair.distance = distance;
}
if (lexStream.kind(buffer[repair.bufferPosition]) == EOFT_SYMBOL &&
repair.distance == previous_distance) {
scopeStackTop = indx;
repair.distance = MAX_DISTANCE;
}
//
// If this scope recovery has beaten the
// previous distance, then we have found a
// better recovery (or this recovery is one
// of a list of scope recoveries). Record
// its information at the proper location
// (INDX) in SCOPE_INDEX and SCOPE_STACK.
//
if (repair.distance > previous_distance) {
scopeIndex[indx] = i;
scopePosition[indx] = stack_position;
return;
}
}
}
}
}
}
//
// This function computes the ParseCheck distance for the best
// possible secondary recovery for a given configuration that
// either deletes none or only one symbol in the forward context.
// If the recovery found is more effective than the best primary
// recovery previously computed, then the function returns true.
// Only misplacement, scope and manual recoveries are attempted;
// simple insertion or substitution of a nonterminal are tried
// in CHECK_PRIMARY_DISTANCE as part of primary recovery.
//
private boolean secondaryCheck(int stck[], int stack_top, int buffer_position, int distance) {
int top, j;
for (top = stack_top - 1; top >= 0; top--) {
j = parseCheck(stck, top,
lexStream.kind(buffer[buffer_position]),
buffer_position + 1);
if (((j - buffer_position + 1) > MIN_DISTANCE) && (j > distance))
return true;
}
PrimaryRepairInfo repair = new PrimaryRepairInfo();
repair.bufferPosition = buffer_position + 1;
repair.distance = distance;
repair = scopeTrial(stck, stack_top, repair);
if ((repair.distance - buffer_position) > MIN_DISTANCE && repair.distance > distance)
return true;
return false;
}
//
// Secondary_phase is a boolean function that checks whether or
// not some form of secondary recovery is applicable to one of
// the error configurations. First, if "next_stack" is available,
// misplacement and secondary recoveries are attempted on it.
// Then, in any case, these recoveries are attempted on "stack".
// If a successful recovery is found, a diagnosis is issued, the
// configuration is updated and the function returns "true".
// Otherwise, the function returns false.
//
private RepairCandidate secondaryPhase(int error_token) {
SecondaryRepairInfo repair = new SecondaryRepairInfo();
SecondaryRepairInfo misplaced = new SecondaryRepairInfo();
RepairCandidate candidate = new RepairCandidate();
int i, j, k, top;
int next_last_index = 0;
int last_index;
candidate.symbol = 0;
repair.code = 0;
repair.distance = 0;
repair.recoveryOnNextStack = false;
misplaced.distance = 0;
misplaced.recoveryOnNextStack = false;
//
// If the next_stack is available, try misplaced and secondary
// recovery on it first.
//
if (nextStackTop >= 0) {
int save_location;
buffer[2] = error_token;
buffer[1] = lexStream.previous(buffer[2]);
buffer[0] = lexStream.previous(buffer[1]);
for (k = 3; k < BUFF_UBOUND; k++)
buffer[k] = lexStream.next(buffer[k - 1]);
buffer[BUFF_UBOUND] = lexStream.badtoken();// elmt not available
//
// If we are at the end of the input stream, compute the
// index position of the first EOFT symbol (last useful
// index).
//
for (next_last_index = MAX_DISTANCE - 1;
next_last_index >= 1 &&
lexStream.kind(buffer[next_last_index]) == EOFT_SYMBOL;
next_last_index--){/*empty*/}
next_last_index = next_last_index + 1;
save_location = locationStack[nextStackTop];
int save_location_start = locationStartStack[nextStackTop];
locationStack[nextStackTop] = buffer[2];
locationStartStack[nextStackTop] = lexStream.start(buffer[2]);
misplaced.numDeletions = nextStackTop;
misplaced = misplacementRecovery(nextStack, nextStackTop,
next_last_index,
misplaced, true);
if (misplaced.recoveryOnNextStack)
misplaced.distance++;
repair.numDeletions = nextStackTop + BUFF_UBOUND;
repair = secondaryRecovery(nextStack, nextStackTop,
next_last_index,
repair, true);
if (repair.recoveryOnNextStack)
repair.distance++;
locationStack[nextStackTop] = save_location;
locationStartStack[nextStackTop] = save_location_start;
} else { // next_stack not available, initialize ...
misplaced.numDeletions = stateStackTop;
repair.numDeletions = stateStackTop + BUFF_UBOUND;
}
//
// Try secondary recovery on the "stack" configuration.
//
buffer[3] = error_token;
buffer[2] = lexStream.previous(buffer[3]);
buffer[1] = lexStream.previous(buffer[2]);
buffer[0] = lexStream.previous(buffer[1]);
for (k = 4; k < BUFF_SIZE; k++)
buffer[k] = lexStream.next(buffer[k - 1]);
for (last_index = MAX_DISTANCE - 1;
last_index >= 1 && lexStream.kind(buffer[last_index]) == EOFT_SYMBOL;
last_index--){/*empty*/}
last_index++;
misplaced = misplacementRecovery(stack, stateStackTop,
last_index,
misplaced, false);
repair = secondaryRecovery(stack, stateStackTop,
last_index, repair, false);
//
// If a successful misplaced recovery was found, compare it with
// the most successful secondary recovery. If the misplaced
// recovery either deletes fewer symbols or parse-checks further
// then it is chosen.
//
if (misplaced.distance > MIN_DISTANCE) {
if (misplaced.numDeletions <= repair.numDeletions ||
(misplaced.distance - misplaced.numDeletions) >=
(repair.distance - repair.numDeletions)) {
repair.code = MISPLACED_CODE;
repair.stackPosition = misplaced.stackPosition;
repair.bufferPosition = 2;
repair.numDeletions = misplaced.numDeletions;
repair.distance = misplaced.distance;
repair.recoveryOnNextStack = misplaced.recoveryOnNextStack;
}
}
//
// If the successful recovery was on next_stack, update: stack,
// buffer, location_stack and last_index.
//
if (repair.recoveryOnNextStack) {
stateStackTop = nextStackTop;
for (i = 0; i <= stateStackTop; i++)
stack[i] = nextStack[i];
buffer[2] = error_token;
buffer[1] = lexStream.previous(buffer[2]);
buffer[0] = lexStream.previous(buffer[1]);
for (k = 3; k < BUFF_UBOUND; k++)
buffer[k] = lexStream.next(buffer[k - 1]);
buffer[BUFF_UBOUND] = lexStream.badtoken();// elmt not available
locationStack[nextStackTop] = buffer[2];
locationStartStack[nextStackTop] = lexStream.start(buffer[2]);
last_index = next_last_index;
}
//
// Next, try scope recoveries after deletion of one, two, three,
// four ... buffer_position tokens from the input stream.
//
if (repair.code == SECONDARY_CODE || repair.code == DELETION_CODE) {
PrimaryRepairInfo scope_repair = new PrimaryRepairInfo();
scope_repair.distance = 0;
for (scope_repair.bufferPosition = 2;
scope_repair.bufferPosition <= repair.bufferPosition &&
repair.code != SCOPE_CODE; scope_repair.bufferPosition++) {
scope_repair = scopeTrial(stack, stateStackTop, scope_repair);
j = (scope_repair.distance == MAX_DISTANCE
? last_index
: scope_repair.distance);
k = scope_repair.bufferPosition - 1;
if ((j - k) > MIN_DISTANCE && (j - k) > (repair.distance - repair.numDeletions)) {
repair.code = SCOPE_CODE;
i = scopeIndex[scopeStackTop]; // upper bound
repair.symbol = Parser.scope_lhs[i] + NT_OFFSET;
repair.stackPosition = stateStackTop;
repair.bufferPosition = scope_repair.bufferPosition;
}
}
}
//
// If no successful recovery is found and we have reached the
// end of the file, check whether or not scope recovery is
// applicable at the end of the file after discarding some
// states.
//
if (repair.code == 0 && lexStream.kind(buffer[last_index]) == EOFT_SYMBOL) {
PrimaryRepairInfo scope_repair = new PrimaryRepairInfo();
scope_repair.bufferPosition = last_index;
scope_repair.distance = 0;
for (top = stateStackTop;
top >= 0 && repair.code == 0; top--)
{
scope_repair = scopeTrial(stack, top, scope_repair);
if (scope_repair.distance > 0)
{
repair.code = SCOPE_CODE;
i = scopeIndex[scopeStackTop]; // upper bound
repair.symbol = Parser.scope_lhs[i] + NT_OFFSET;
repair.stackPosition = top;
repair.bufferPosition = scope_repair.bufferPosition;
}
}
}
//
// If a successful repair was not found, quit! Otherwise, issue
// diagnosis and adjust configuration...
//
if (repair.code == 0)
return candidate;
secondaryDiagnosis(repair);
//
// Update buffer based on number of elements that are deleted.
//
switch(repair.code) {
case MISPLACED_CODE:
candidate.location = buffer[2];
candidate.symbol = lexStream.kind(buffer[2]);
lexStream.reset(lexStream.next(buffer[2]));
break;
case DELETION_CODE:
candidate.location = buffer[repair.bufferPosition];
candidate.symbol =
lexStream.kind(buffer[repair.bufferPosition]);
lexStream.reset(lexStream.next(buffer[repair.bufferPosition]));
break;
default: // SCOPE_CODE || SECONDARY_CODE
candidate.symbol = repair.symbol;
candidate.location = buffer[repair.bufferPosition];
lexStream.reset(buffer[repair.bufferPosition]);
break;
}
return candidate;
}
//
// This boolean function checks whether or not a given
// configuration yields a better misplacement recovery than
// the best misplacement recovery computed previously.
//
private SecondaryRepairInfo misplacementRecovery(int stck[], int stack_top, int last_index, SecondaryRepairInfo repair, boolean stack_flag) {
int previous_loc = buffer[2];
int stack_deletions = 0;
for (int top = stack_top - 1; top >= 0; top--) {
if (locationStack[top] < previous_loc) {
stack_deletions++;
}
previous_loc = locationStack[top];
int j = parseCheck(stck, top, lexStream.kind(buffer[2]), 3);
if (j == MAX_DISTANCE) {
j = last_index;
}
if ((j > MIN_DISTANCE) && (j - stack_deletions) > (repair.distance - repair.numDeletions)) {
repair.stackPosition = top;
repair.distance = j;
repair.numDeletions = stack_deletions;
repair.recoveryOnNextStack = stack_flag;
}
}
return repair;
}
//
// This boolean function checks whether or not a given
// configuration yields a better secondary recovery than the
// best misplacement recovery computed previously.
//
private SecondaryRepairInfo secondaryRecovery(int stck[],int stack_top, int last_index, SecondaryRepairInfo repair, boolean stack_flag) {
int previous_loc;
int stack_deletions = 0;
previous_loc = buffer[2];
for (int top = stack_top; top >= 0 && repair.numDeletions >= stack_deletions; top--) {
if (locationStack[top] < previous_loc) {
stack_deletions++;
}
previous_loc = locationStack[top];
for (int i = 2;
i <= (last_index - MIN_DISTANCE + 1) &&
(repair.numDeletions >= (stack_deletions + i - 1)); i++) {
int j = parseCheck(stck, top, lexStream.kind(buffer[i]), i + 1);
if (j == MAX_DISTANCE) {
j = last_index;
}
if ((j - i + 1) > MIN_DISTANCE) {
int k = stack_deletions + i - 1;
if ((k < repair.numDeletions) ||
(j - k) > (repair.distance - repair.numDeletions) ||
((repair.code == SECONDARY_CODE) && (j - k) == (repair.distance - repair.numDeletions))) {
repair.code = DELETION_CODE;
repair.distance = j;
repair.stackPosition = top;
repair.bufferPosition = i;
repair.numDeletions = k;
repair.recoveryOnNextStack = stack_flag;
}
}
for (int l = Parser.nasi(stck[top]); l >= 0 && Parser.nasr[l] != 0; l++) {
int symbol = Parser.nasr[l] + NT_OFFSET;
j = parseCheck(stck, top, symbol, i);
if (j == MAX_DISTANCE) {
j = last_index;
}
if ((j - i + 1) > MIN_DISTANCE) {
int k = stack_deletions + i - 1;
if (k < repair.numDeletions || (j - k) > (repair.distance - repair.numDeletions)) {
repair.code = SECONDARY_CODE;
repair.symbol = symbol;
repair.distance = j;
repair.stackPosition = top;
repair.bufferPosition = i;
repair.numDeletions = k;
repair.recoveryOnNextStack = stack_flag;
}
}
}
}
}
return repair;
}
//
// This procedure is invoked to issue a secondary diagnosis and
// adjust the input buffer. The recovery in question is either
// an automatic scope recovery, a manual scope recovery, a
// secondary substitution or a secondary deletion.
//
private void secondaryDiagnosis(SecondaryRepairInfo repair) {
switch(repair.code) {
case SCOPE_CODE: {
if (repair.stackPosition < stateStackTop) {
reportError(DELETION_CODE,
Parser.terminal_index[ERROR_SYMBOL],
locationStack[repair.stackPosition],
buffer[1]);
}
for (int i = 0; i < scopeStackTop; i++) {
reportError(SCOPE_CODE,
-scopeIndex[i],
locationStack[scopePosition[i]],
buffer[1],
Parser.non_terminal_index[Parser.scope_lhs[scopeIndex[i]]]);
}
repair.symbol = Parser.scope_lhs[scopeIndex[scopeStackTop]] + NT_OFFSET;
stateStackTop = scopePosition[scopeStackTop];
reportError(SCOPE_CODE,
-scopeIndex[scopeStackTop],
locationStack[scopePosition[scopeStackTop]],
buffer[1],
getNtermIndex(stack[stateStackTop],
repair.symbol,
repair.bufferPosition)
);
break;
}
default: {
reportError(repair.code,
(repair.code == SECONDARY_CODE
? getNtermIndex(stack[repair.stackPosition],
repair.symbol,
repair.bufferPosition)
: Parser.terminal_index[ERROR_SYMBOL]),
locationStack[repair.stackPosition],
buffer[repair.bufferPosition - 1]);
stateStackTop = repair.stackPosition;
}
}
}
//
// Try to parse until first_token and all tokens in BUFFER have
// been consumed, or an error is encountered. Return the number
// of tokens that were expended before the parse blocked.
//
private int parseCheck(int stck[], int stack_top, int first_token, int buffer_position) {
int max_pos;
int indx;
int ct;
int act;
//
// Initialize pointer for temp_stack and initialize maximum
// position of state stack that is still useful.
//
act = stck[stack_top];
if (first_token > NT_OFFSET) {
tempStackTop = stack_top;
if(DEBUG_PARSECHECK) {
System.out.println(tempStackTop);
}
max_pos = stack_top;
indx = buffer_position;
ct = lexStream.kind(buffer[indx]);
lexStream.reset(lexStream.next(buffer[indx]));
int lhs_symbol = first_token - NT_OFFSET;
act = Parser.ntAction(act, lhs_symbol);
if (act <= NUM_RULES) {
// same loop as 'process_non_terminal'
do {
tempStackTop -= (Parser.rhs[act]-1);
if(DEBUG_PARSECHECK) {
System.out.print(tempStackTop);
System.out.print(" ("); //$NON-NLS-1$
System.out.print(-(Parser.rhs[act]-1));
System.out.print(") [max:"); //$NON-NLS-1$
System.out.print(max_pos);
System.out.print("]\tprocess_non_terminal\t"); //$NON-NLS-1$
System.out.print(act);
System.out.print("\t"); //$NON-NLS-1$
System.out.print(Parser.name[Parser.non_terminal_index[Parser.lhs[act]]]);
System.out.println();
}
if(Parser.rules_compliance[act] > this.options.sourceLevel) {
return 0;
}
lhs_symbol = Parser.lhs[act];
act = (tempStackTop > max_pos
? tempStack[tempStackTop]
: stck[tempStackTop]);
act = Parser.ntAction(act, lhs_symbol);
} while(act <= NUM_RULES);
max_pos = max_pos < tempStackTop ? max_pos : tempStackTop;
}
} else {
tempStackTop = stack_top - 1;
if(DEBUG_PARSECHECK) {
System.out.println(tempStackTop);
}
max_pos = tempStackTop;
indx = buffer_position - 1;
ct = first_token;
lexStream.reset(buffer[buffer_position]);
}
process_terminal: for (;;) {
if(DEBUG_PARSECHECK) {
System.out.print(tempStackTop + 1);
System.out.print(" (+1) [max:"); //$NON-NLS-1$
System.out.print(max_pos);
System.out.print("]\tprocess_terminal \t"); //$NON-NLS-1$
System.out.print(ct);
System.out.print("\t"); //$NON-NLS-1$
System.out.print(Parser.name[Parser.terminal_index[ct]]);
System.out.println();
}
if (++tempStackTop >= stackLength) // Stack overflow!!!
return indx;
tempStack[tempStackTop] = act;
act = Parser.tAction(act, ct);
if (act <= NUM_RULES) { // reduce action
tempStackTop--;
if(DEBUG_PARSECHECK) {
System.out.print(tempStackTop);
System.out.print(" (-1) [max:"); //$NON-NLS-1$
System.out.print(max_pos);
System.out.print("]\treduce"); //$NON-NLS-1$
System.out.println();
}
} else if (act < ACCEPT_ACTION || // shift action
act > ERROR_ACTION) { // shift-reduce action
if (indx == MAX_DISTANCE)
return indx;
indx++;
ct = lexStream.kind(buffer[indx]);
lexStream.reset(lexStream.next(buffer[indx]));
if (act > ERROR_ACTION) {
act -= ERROR_ACTION;
if(DEBUG_PARSECHECK) {
System.out.print(tempStackTop);
System.out.print("\tshift reduce"); //$NON-NLS-1$
System.out.println();
}
} else {
if(DEBUG_PARSECHECK) {
System.out.println("\tshift"); //$NON-NLS-1$
}
continue process_terminal;
}
} else if (act == ACCEPT_ACTION) { // accept action
return MAX_DISTANCE;
} else {
return indx; // error action
}
// same loop as first token initialization
process_non_terminal:
do {
tempStackTop -= (Parser.rhs[act]-1);
if(DEBUG_PARSECHECK) {
System.out.print(tempStackTop);
System.out.print(" ("); //$NON-NLS-1$
System.out.print(-(Parser.rhs[act]-1));
System.out.print(") [max:"); //$NON-NLS-1$
System.out.print(max_pos);
System.out.print("]\tprocess_non_terminal\t"); //$NON-NLS-1$
System.out.print(act);
System.out.print("\t"); //$NON-NLS-1$
System.out.print(Parser.name[Parser.non_terminal_index[Parser.lhs[act]]]);
System.out.println();
}
if(act <= NUM_RULES) {
if(Parser.rules_compliance[act] > this.options.sourceLevel) {
return 0;
}
}
int lhs_symbol = Parser.lhs[act];
act = (tempStackTop > max_pos
? tempStack[tempStackTop]
: stck[tempStackTop]);
act = Parser.ntAction(act, lhs_symbol);
} while(act <= NUM_RULES);
max_pos = max_pos < tempStackTop ? max_pos : tempStackTop;
} // process_terminal;
}
private void reportError(int msgCode, int nameIndex, int leftToken, int rightToken) {
reportError(msgCode, nameIndex, leftToken, rightToken, 0);
}
private void reportError(int msgCode, int nameIndex, int leftToken, int rightToken, int scopeNameIndex) {
int lToken = (leftToken > rightToken ? rightToken : leftToken);
if (lToken < rightToken) {
reportSecondaryError(msgCode, nameIndex, lToken, rightToken, scopeNameIndex);
} else {
reportPrimaryError(msgCode, nameIndex, rightToken, scopeNameIndex);
}
}
private void reportPrimaryError(int msgCode, int nameIndex, int token, int scopeNameIndex) {
String name;
if (nameIndex >= 0) {
name = Parser.readableName[nameIndex];
} else {
name = EMPTY_STRING;
}
int errorStart = lexStream.start(token);
int errorEnd = lexStream.end(token);
int currentKind = lexStream.kind(token);
String errorTokenName = Parser.name[Parser.terminal_index[lexStream.kind(token)]];
char[] errorTokenSource = lexStream.name(token);
switch(msgCode) {
case BEFORE_CODE:
problemReporter().parseErrorInsertBeforeToken(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName,
name);
break;
case INSERTION_CODE:
problemReporter().parseErrorInsertAfterToken(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName,
name);
break;
case DELETION_CODE:
problemReporter().parseErrorDeleteToken(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName);
break;
case INVALID_CODE:
if (name.length() == 0) {
problemReporter().parseErrorReplaceToken(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName,
name);
} else {
problemReporter().parseErrorInvalidToken(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName,
name);
}
break;
case SUBSTITUTION_CODE:
problemReporter().parseErrorReplaceToken(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName,
name);
break;
case SCOPE_CODE:
StringBuffer buf = new StringBuffer();
for (int i = Parser.scope_suffix[- nameIndex]; Parser.scope_rhs[i] != 0; i++) {
buf.append(Parser.readableName[Parser.scope_rhs[i]]);
if (Parser.scope_rhs[i + 1] != 0) // any more symbols to print?
buf.append(' ');
}
if (scopeNameIndex != 0) {
problemReporter().parseErrorInsertToComplete(
errorStart,
errorEnd,
buf.toString(),
Parser.readableName[scopeNameIndex]);
} else {
problemReporter().parseErrorInsertToCompleteScope(
errorStart,
errorEnd,
buf.toString());
}
break;
case EOF_CODE:
problemReporter().parseErrorUnexpectedEnd(
errorStart,
errorEnd);
break;
case MERGE_CODE:
problemReporter().parseErrorMergeTokens(
errorStart,
errorEnd,
name);
break;
case MISPLACED_CODE:
problemReporter().parseErrorMisplacedConstruct(
errorStart,
errorEnd);
break;
default:
if (name.length() == 0) {
problemReporter().parseErrorNoSuggestion(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName);
} else {
problemReporter().parseErrorReplaceToken(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName,
name);
}
break;
}
}
private void reportSecondaryError(int msgCode, int nameIndex, int leftToken, int rightToken, int scopeNameIndex) {
String name;
if (nameIndex >= 0) {
name = Parser.readableName[nameIndex];
} else {
name = EMPTY_STRING;
}
int errorStart = -1;
if(lexStream.isInsideStream(leftToken)) {
if(leftToken == 0) {
errorStart = lexStream.start(leftToken + 1);
} else {
errorStart = lexStream.start(leftToken);
}
} else {
if(leftToken == errorToken) {
errorStart = errorTokenStart;
} else {
for (int i = 0; i <= stateStackTop; i++) {
if(locationStack[i] == leftToken) {
errorStart = locationStartStack[i];
}
}
}
if(errorStart == -1) {
errorStart = lexStream.start(rightToken);
}
}
int errorEnd = lexStream.end(rightToken);
switch(msgCode) {
case MISPLACED_CODE:
problemReporter().parseErrorMisplacedConstruct(
errorStart,
errorEnd);
break;
case SCOPE_CODE:
// error start is on the last token start
errorStart = lexStream.start(rightToken);
StringBuffer buf = new StringBuffer();
for (int i = Parser.scope_suffix[- nameIndex]; Parser.scope_rhs[i] != 0; i++) {
buf.append(Parser.readableName[Parser.scope_rhs[i]]);
if (Parser.scope_rhs[i+1] != 0)
buf.append(' ');
}
if (scopeNameIndex != 0) {
problemReporter().parseErrorInsertToComplete(
errorStart,
errorEnd,
buf.toString(),
Parser.readableName[scopeNameIndex]);
} else {
problemReporter().parseErrorInsertToCompletePhrase(
errorStart,
errorEnd,
buf.toString());
}
break;
case MERGE_CODE:
problemReporter().parseErrorMergeTokens(
errorStart,
errorEnd,
name);
break;
case DELETION_CODE:
problemReporter().parseErrorDeleteTokens(
errorStart,
errorEnd);
break;
default:
if (name.length() == 0) {
problemReporter().parseErrorNoSuggestionForTokens(
errorStart,
errorEnd);
} else {
problemReporter().parseErrorReplaceTokens(
errorStart,
errorEnd,
name);
}
}
return;
}
public String toString() {
StringBuffer res = new StringBuffer();
res.append(lexStream.toString());
return res.toString();
}
}