/*******************************************************************************
* Copyright (c) 2000, 2008 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.RecoveryScanner;
import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.util.Util;
public class DiagnoseParser implements ParserBasicInformation, TerminalTokens {
private static final boolean DEBUG= false;
private boolean DEBUG_PARSECHECK= false;
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 RecoveryScanner recoveryScanner;
private boolean reportProblem;
private static class RepairCandidate {
public int symbol;
public int location;
public RepairCandidate() {
this.symbol= 0;
this.location= 0;
}
}
private static 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;
}
}
static class SecondaryRepairInfo {
public int code;
public int distance;
public int bufferPosition;
public int stackPosition;
public int numDeletions;
public int symbol;
boolean recoveryOnNextStack;
}
private static 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, Util.EMPTY_INT_ARRAY, Util.EMPTY_INT_ARRAY, Util.EMPTY_INT_ARRAY, 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);
this.recoveryScanner= parser.recoveryScanner;
}
private ProblemReporter problemReporter() {
return this.parser.problemReporter();
}
private void reallocateStacks() {
int old_stack_length= this.stackLength;
this.stackLength+= STACK_INCREMENT;
if (old_stack_length == 0) {
this.stack= new int[this.stackLength];
this.locationStack= new int[this.stackLength];
this.locationStartStack= new int[this.stackLength];
this.tempStack= new int[this.stackLength];
this.prevStack= new int[this.stackLength];
this.nextStack= new int[this.stackLength];
this.scopeIndex= new int[this.stackLength];
this.scopePosition= new int[this.stackLength];
} else {
System.arraycopy(this.stack, 0, this.stack= new int[this.stackLength], 0, old_stack_length);
System.arraycopy(this.locationStack, 0, this.locationStack= new int[this.stackLength], 0, old_stack_length);
System.arraycopy(this.locationStartStack, 0, this.locationStartStack= new int[this.stackLength], 0, old_stack_length);
System.arraycopy(this.tempStack, 0, this.tempStack= new int[this.stackLength], 0, old_stack_length);
System.arraycopy(this.prevStack, 0, this.prevStack= new int[this.stackLength], 0, old_stack_length);
System.arraycopy(this.nextStack, 0, this.nextStack= new int[this.stackLength], 0, old_stack_length);
System.arraycopy(this.scopeIndex, 0, this.scopeIndex= new int[this.stackLength], 0, old_stack_length);
System.arraycopy(this.scopePosition, 0, this.scopePosition= new int[this.stackLength], 0, old_stack_length);
}
return;
}
public void diagnoseParse(boolean record) {
this.reportProblem= true;
boolean oldRecord= false;
if (this.recoveryScanner != null) {
oldRecord= this.recoveryScanner.record;
this.recoveryScanner.record= record;
}
try {
this.lexStream.reset();
this.currentToken= this.lexStream.getToken();
int prev_pos;
int pos;
int next_pos;
int act= START_STATE;
reallocateStacks();
//
// Start parsing
//
this.stateStackTop= 0;
this.stack[this.stateStackTop]= act;
int tok= this.lexStream.kind(this.currentToken);
this.locationStack[this.stateStackTop]= this.currentToken;
this.locationStartStack[this.stateStackTop]= this.lexStream.start(this.currentToken);
boolean forceRecoveryAfterLBracketMissing= false;
// int forceRecoveryToken = -1;
//
// Process a terminal
//
do {
//
// Synchronize state stacks and update the location stack
//
prev_pos= -1;
this.prevStackTop= -1;
next_pos= -1;
this.nextStackTop= -1;
pos= this.stateStackTop;
this.tempStackTop= this.stateStackTop - 1;
for (int i= 0; i <= this.stateStackTop; i++)
this.tempStack[i]= this.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 {
this.tempStackTop-= (Parser.rhs[act] - 1);
act= Parser.ntAction(this.tempStack[this.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 (this.tempStackTop + 1 >= this.stackLength)
reallocateStacks();
pos= pos < this.tempStackTop ? pos : this.tempStackTop;
this.tempStack[this.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 ?
this.nextStackTop= this.tempStackTop + 1;
for (int i= next_pos + 1; i <= this.nextStackTop; i++)
this.nextStack[i]= this.tempStack[i];
for (int i= pos + 1; i <= this.nextStackTop; i++) {
this.locationStack[i]= this.locationStack[this.stateStackTop];
this.locationStartStack[i]= this.locationStartStack[this.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 {
this.nextStackTop-= (Parser.rhs[act] - 1);
act= Parser.ntAction(this.nextStack[this.nextStackTop], Parser.lhs[act]);
} while (act <= NUM_RULES);
pos= pos < this.nextStackTop ? pos : this.nextStackTop;
}
if (this.nextStackTop + 1 >= this.stackLength)
reallocateStacks();
this.tempStackTop= this.nextStackTop;
this.nextStack[++this.nextStackTop]= act;
next_pos= this.nextStackTop;
//
// Simulate the parser through the next token without
// destroying STACK or next_stack.
//
this.currentToken= this.lexStream.getToken();
tok= this.lexStream.kind(this.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]]);
}
this.tempStackTop-= (Parser.rhs[act] - 1);
act= (this.tempStackTop > next_pos
? this.tempStack[this.tempStackTop]
: this.nextStack[this.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 (this.tempStackTop + 1 >= this.stackLength)
reallocateStacks();
next_pos= next_pos < this.tempStackTop ? next_pos : this.tempStackTop;
this.tempStack[this.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) {
this.prevStackTop= this.stateStackTop;
for (int i= prev_pos + 1; i <= this.prevStackTop; i++)
this.prevStack[i]= this.stack[i];
prev_pos= pos;
this.stateStackTop= this.nextStackTop;
for (int i= pos + 1; i <= this.stateStackTop; i++)
this.stack[i]= this.nextStack[i];
this.locationStack[this.stateStackTop]= this.currentToken;
this.locationStartStack[this.stateStackTop]= this.lexStream.start(this.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(this.currentToken, forceRecoveryAfterLBracketMissing);
forceRecoveryAfterLBracketMissing= false;
if (this.parser.reportOnlyOneSyntaxError) {
return;
}
if (this.parser.problemReporter().options.maxProblemsPerUnit < this.parser.compilationUnit.compilationResult.problemCount) {
if (this.recoveryScanner == null || !this.recoveryScanner.record)
return;
this.reportProblem= false;
}
act= this.stack[this.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) {
this.stateStackTop-= (Parser.rhs[act] - 1);
act= Parser.ntAction(this.stack[this.stateStackTop], Parser.lhs[act]);
}
this.stack[++this.stateStackTop]= act;
this.currentToken= this.lexStream.getToken();
tok= this.lexStream.kind(this.currentToken);
this.locationStack[this.stateStackTop]= this.currentToken;
this.locationStartStack[this.stateStackTop]= this.lexStream.start(this.currentToken);
} else {
tok= candidate.symbol;
this.locationStack[this.stateStackTop]= candidate.location;
this.locationStartStack[this.stateStackTop]= this.lexStream.start(candidate.location);
}
}
} while (act != ACCEPT_ACTION);
} finally {
if (this.recoveryScanner != null) {
this.recoveryScanner.record= oldRecord;
}
}
return;
}
private static char[] displayEscapeCharacters(char[] tokenSource, int start, int end) {
StringBuffer tokenSourceBuffer= new StringBuffer();
for (int i= 0; i < start; i++) {
tokenSourceBuffer.append(tokenSource[i]);
}
for (int i= start; i < end; i++) {
char c= tokenSource[i];
switch (c) {
case '\r':
tokenSourceBuffer.append("\\r"); //$NON-NLS-1$
break;
case '\n':
tokenSourceBuffer.append("\\n"); //$NON-NLS-1$
break;
case '\b':
tokenSourceBuffer.append("\\b"); //$NON-NLS-1$
break;
case '\t':
tokenSourceBuffer.append("\t"); //$NON-NLS-1$
break;
case '\f':
tokenSourceBuffer.append("\\f"); //$NON-NLS-1$
break;
case '\"':
tokenSourceBuffer.append("\\\""); //$NON-NLS-1$
break;
case '\'':
tokenSourceBuffer.append("\\'"); //$NON-NLS-1$
break;
case '\\':
tokenSourceBuffer.append("\\\\"); //$NON-NLS-1$
break;
default:
tokenSourceBuffer.append(c);
}
}
for (int i= end; i < tokenSource.length; i++) {
tokenSourceBuffer.append(tokenSource[i]);
}
return tokenSourceBuffer.toString().toCharArray();
}
//
// 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= this.lexStream.start(error_token);
int prevtok= this.lexStream.previous(error_token);
int prevtokKind= this.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;
this.lexStream.reset(error_token);
this.stateStackTop= this.nextStackTop;
for (int j= 0; j <= this.stateStackTop; j++) {
this.stack[j]= this.nextStack[j];
}
this.locationStack[this.stateStackTop]= error_token;
this.locationStartStack[this.stateStackTop]= this.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 (this.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 (this.lexStream.kind(this.buffer[BUFF_UBOUND]) != EOFT_SYMBOL) {
candidate= secondaryPhase(this.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; this.lexStream.kind(this.buffer[i]) == EOFT_SYMBOL; i--) {/*empty*/
}
reportError(DELETION_CODE,
Parser.terminal_index[prevtokKind],//Parser.terminal_index[lexStream.kind(prevtok)],
error_token,
this.buffer[i]);
candidate.symbol= 0;
candidate.location= this.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= (this.nextStackTop >= 0 ? 3 : 2);
this.buffer[i]= error_token;
for (int j= i; j > 0; j--)
this.buffer[j - 1]= this.lexStream.previous(this.buffer[j]);
for (int k= i + 1; k < BUFF_SIZE; k++)
this.buffer[k]= this.lexStream.next(this.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 (this.nextStackTop >= 0) {
repair.bufferPosition= 3;
repair= checkPrimaryDistance(this.nextStack, this.nextStackTop, repair);
}
//
// ... Next, try primary recovery on the current token...
//
PrimaryRepairInfo new_repair= repair.copy();
new_repair.bufferPosition= 2;
new_repair= checkPrimaryDistance(this.stack, this.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 (this.prevStackTop >= 0) {
new_repair= repair.copy();
new_repair.bufferPosition= 1;
new_repair= checkPrimaryDistance(this.prevStack, this.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 (this.nextStackTop >= 0) {// next_stack available
if (secondaryCheck(this.nextStack, this.nextStackTop, 3, repair.distance)) {
return candidate;
}
} else if (secondaryCheck(this.stack, this.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 (this.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) {
this.stateStackTop= this.prevStackTop;
for (int j= 0; j <= this.stateStackTop; j++) {
this.stack[j]= this.prevStack[j];
}
} else if (this.nextStackTop >= 0 && repair.bufferPosition >= 3) {
this.stateStackTop= this.nextStackTop;
for (int j= 0; j <= this.stateStackTop; j++) {
this.stack[j]= this.nextStack[j];
}
this.locationStack[this.stateStackTop]= this.buffer[3];
this.locationStartStack[this.stateStackTop]= this.lexStream.start(this.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= this.lexStream.name(this.buffer[buffer_position]);
char[] name2= this.lexStream.name(this.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 (this.buffer[repair.bufferPosition] != 0 && this.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,
this.lexStream.kind(this.buffer[repair.bufferPosition + 1]),
repair.bufferPosition + 2);
if (this.lexStream.kind(this.buffer[repair.bufferPosition]) == EOLT_SYMBOL &&
this.lexStream.afterEol(this.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;
this.tempStackTop= stack_top - 1;
tok= this.lexStream.kind(this.buffer[repair.bufferPosition]);
this.lexStream.reset(this.buffer[repair.bufferPosition + 1]);
act= Parser.tAction(next_state, tok);
while (act <= NUM_RULES) {
do {
this.tempStackTop-= (Parser.rhs[act] - 1);
symbol= Parser.lhs[act];
act= (this.tempStackTop > max_pos
? this.tempStack[this.tempStackTop]
: stck[this.tempStackTop]);
act= Parser.ntAction(act, symbol);
} while (act <= NUM_RULES);
max_pos= max_pos < this.tempStackTop ? max_pos : this.tempStackTop;
this.tempStack[this.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) {
this.list[symbol]= symbol;
} else {
this.list[symbol]= this.list[root];
this.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 && this.list[symbol] == 0) {
if (root == 0) {
this.list[symbol]= symbol;
} else {
this.list[symbol]= this.list[root];
this.list[root]= symbol;
}
root= symbol;
}
}
}
i= this.list[root];
this.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 && this.lexStream.afterEol(this.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;
}
symbol= this.list[symbol];
}
//
// Next, Try substitution for each possible candidate available
// in the current state, except EOFT and ERROR_SYMBOL.
//
symbol= root;
if (this.buffer[repair.bufferPosition] != 0) {// do not replace the first token
while (symbol != 0) {
if (symbol == EOLT_SYMBOL && this.lexStream.afterEol(this.buffer[repair.bufferPosition + 1])) {
k= 10;
} else {
k= misspell(symbol, this.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;
}
i= symbol;
symbol= this.list[symbol];
this.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= this.buffer[repair.bufferPosition - 1];
int curtok= this.buffer[repair.bufferPosition];
switch (repair.code) {
case INSERTION_CODE:
case BEFORE_CODE: {
if (repair.symbol > NT_OFFSET)
name_index= getNtermIndex(this.stack[this.stateStackTop],
repair.symbol,
repair.bufferPosition);
else
name_index= getTermIndex(this.stack,
this.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(this.stack[this.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(this.stack, this.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,
this.lexStream.next(curtok));
break;
}
case SCOPE_CODE: {
for (int i= 0; i < this.scopeStackTop; i++) {
reportError(repair.code,
-this.scopeIndex[i],
this.locationStack[this.scopePosition[i]],
prevtok,
Parser.non_terminal_index[Parser.scope_lhs[this.scopeIndex[i]]]);
}
repair.symbol= Parser.scope_lhs[this.scopeIndex[this.scopeStackTop]] + NT_OFFSET;
this.stateStackTop= this.scopePosition[this.scopeStackTop];
reportError(repair.code,
-this.scopeIndex[this.scopeStackTop],
this.locationStack[this.scopePosition[this.scopeStackTop]],
prevtok,
getNtermIndex(this.stack[this.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= this.buffer[repair.bufferPosition];
this.lexStream.reset(this.buffer[repair.bufferPosition]);
break;
}
case INVALID_CODE:
case SUBSTITUTION_CODE: {
candidate.symbol= repair.symbol;
candidate.location= this.buffer[repair.bufferPosition];
this.lexStream.reset(this.buffer[repair.bufferPosition + 1]);
break;
}
case MERGE_CODE: {
candidate.symbol= repair.symbol;
candidate.location= this.buffer[repair.bufferPosition];
this.lexStream.reset(this.buffer[repair.bufferPosition + 2]);
break;
}
default: {// deletion
candidate.location= this.buffer[repair.bufferPosition + 1];
candidate.symbol=
this.lexStream.kind(this.buffer[repair.bufferPosition + 1]);
this.lexStream.reset(this.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;
this.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.
//
this.lexStream.reset(this.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 {
this.tempStackTop-= (Parser.rhs[act] - 1);
int lhs_symbol= Parser.lhs[act];
act= (this.tempStackTop > max_pos
? this.tempStack[this.tempStackTop]
: stck[this.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 < this.tempStackTop ? max_pos : this.tempStackTop;
this.tempStack[this.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.
//
this.tempStackTop++; // adjust top of stack to reflect last goto
// next move is shift or shift-reduce.
int threshold= this.tempStackTop;
tok= this.lexStream.kind(this.buffer[buffer_position]);
this.lexStream.reset(this.buffer[buffer_position + 1]);
if (act > ERROR_ACTION) { // shift-reduce on candidate?
act-= ERROR_ACTION;
} else {
this.tempStack[this.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 {
this.tempStackTop-= (Parser.rhs[act] - 1);
if (this.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 (this.tempStackTop == threshold)
highest_symbol= lhs_symbol + NT_OFFSET;
act= (this.tempStackTop > max_pos
? this.tempStack[this.tempStackTop]
: stck[this.tempStackTop]);
act= Parser.ntAction(act, lhs_symbol);
} while (act <= NUM_RULES);
this.tempStack[this.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= this.lexStream.kind(this.buffer[buffer_position]);
this.lexStream.reset(this.buffer[buffer_position + 1]);
//
// Initialize stack index of temp_stack and initialize maximum
// position of state stack that is still useful.
//
this.tempStackTop= 0;
this.tempStack[this.tempStackTop]= start;
int act= Parser.ntAction(start, highest_symbol);
if (act > NUM_RULES) { // goto action?
this.tempStack[this.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 {
this.tempStackTop-= (Parser.rhs[act] - 1);
if (this.tempStackTop < 0)
return Parser.non_terminal_index[highest_symbol];
if (this.tempStackTop == 0)
highest_symbol= Parser.lhs[act];
act= Parser.ntAction(this.tempStack[this.tempStackTop], Parser.lhs[act]);
} while (act <= NUM_RULES);
this.tempStack[this.tempStackTop + 1]= act;
act= Parser.tAction(act, tok);
}
return Parser.non_terminal_index[highest_symbol];
}
//
// 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]= ScannerHelper.toLowerCase(c);
}
s1[n]= '\0';
//
//
//
char[] tokenName= this.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]= ScannerHelper.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) {
this.stateSeen= new int[this.stackLength];
for (int i= 0; i < this.stackLength; i++)
this.stateSeen[i]= NIL;
this.statePoolTop= 0;
this.statePool= new StateInfo[this.stackLength];
scopeTrialCheck(stck, stack_top, repair, 0);
this.stateSeen= null;
this.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= this.stateSeen[stack_top]; i != NIL; i= this.statePool[i].next) {
if (this.statePool[i].state == act)
return;
}
int old_state_pool_top= this.statePoolTop++;
if (this.statePoolTop >= this.statePool.length) {
System.arraycopy(this.statePool, 0, this.statePool= new StateInfo[this.statePoolTop * 2], 0, this.statePoolTop);
}
this.statePool[old_state_pool_top]= new StateInfo(act, this.stateSeen[stack_top]);
this.stateSeen[stack_top]= old_state_pool_top;
next: 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];
this.tempStackTop= stack_top - 1;
int max_pos= stack_top;
int tok= Parser.scope_la[i];
this.lexStream.reset(this.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 {
this.tempStackTop-= (Parser.rhs[act] - 1);
int lhs_symbol= Parser.lhs[act];
act= (this.tempStackTop > max_pos
? this.tempStack[this.tempStackTop]
: stck[this.tempStackTop]);
act= Parser.ntAction(act, lhs_symbol);
} while (act <= NUM_RULES);
if (this.tempStackTop + 1 >= this.stackLength)
return;
max_pos= max_pos < this.tempStackTop ? max_pos : this.tempStackTop;
this.tempStack[this.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= this.tempStackTop + 1; j >= (max_pos + 1) &&
Parser.in_symbol(this.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) {
if (Parser.rules_compliance[act] > this.options.sourceLevel) {
continue next;
}
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) {
this.scopeStackTop= indx;
repair.distance= distance;
}
if (this.lexStream.kind(this.buffer[repair.bufferPosition]) == EOFT_SYMBOL &&
repair.distance == previous_distance) {
this.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) {
this.scopeIndex[indx]= i;
this.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,
this.lexStream.kind(this.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 (this.nextStackTop >= 0) {
int save_location;
this.buffer[2]= error_token;
this.buffer[1]= this.lexStream.previous(this.buffer[2]);
this.buffer[0]= this.lexStream.previous(this.buffer[1]);
for (k= 3; k < BUFF_UBOUND; k++)
this.buffer[k]= this.lexStream.next(this.buffer[k - 1]);
this.buffer[BUFF_UBOUND]= this.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 &&
this.lexStream.kind(this.buffer[next_last_index]) == EOFT_SYMBOL; next_last_index--) {/*empty*/
}
next_last_index= next_last_index + 1;
save_location= this.locationStack[this.nextStackTop];
int save_location_start= this.locationStartStack[this.nextStackTop];
this.locationStack[this.nextStackTop]= this.buffer[2];
this.locationStartStack[this.nextStackTop]= this.lexStream.start(this.buffer[2]);
misplaced.numDeletions= this.nextStackTop;
misplaced= misplacementRecovery(this.nextStack, this.nextStackTop,
next_last_index,
misplaced, true);
if (misplaced.recoveryOnNextStack)
misplaced.distance++;
repair.numDeletions= this.nextStackTop + BUFF_UBOUND;
repair= secondaryRecovery(this.nextStack, this.nextStackTop,
next_last_index,
repair, true);
if (repair.recoveryOnNextStack)
repair.distance++;
this.locationStack[this.nextStackTop]= save_location;
this.locationStartStack[this.nextStackTop]= save_location_start;
} else { // next_stack not available, initialize ...
misplaced.numDeletions= this.stateStackTop;
repair.numDeletions= this.stateStackTop + BUFF_UBOUND;
}
//
// Try secondary recovery on the "stack" configuration.
//
this.buffer[3]= error_token;
this.buffer[2]= this.lexStream.previous(this.buffer[3]);
this.buffer[1]= this.lexStream.previous(this.buffer[2]);
this.buffer[0]= this.lexStream.previous(this.buffer[1]);
for (k= 4; k < BUFF_SIZE; k++)
this.buffer[k]= this.lexStream.next(this.buffer[k - 1]);
for (last_index= MAX_DISTANCE - 1; last_index >= 1 && this.lexStream.kind(this.buffer[last_index]) == EOFT_SYMBOL; last_index--) {/*empty*/
}
last_index++;
misplaced= misplacementRecovery(this.stack, this.stateStackTop,
last_index,
misplaced, false);
repair= secondaryRecovery(this.stack, this.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) {
this.stateStackTop= this.nextStackTop;
for (i= 0; i <= this.stateStackTop; i++)
this.stack[i]= this.nextStack[i];
this.buffer[2]= error_token;
this.buffer[1]= this.lexStream.previous(this.buffer[2]);
this.buffer[0]= this.lexStream.previous(this.buffer[1]);
for (k= 3; k < BUFF_UBOUND; k++)
this.buffer[k]= this.lexStream.next(this.buffer[k - 1]);
this.buffer[BUFF_UBOUND]= this.lexStream.badtoken();// elmt not available
this.locationStack[this.nextStackTop]= this.buffer[2];
this.locationStartStack[this.nextStackTop]= this.lexStream.start(this.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(this.stack, this.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= this.scopeIndex[this.scopeStackTop]; // upper bound
repair.symbol= Parser.scope_lhs[i] + NT_OFFSET;
repair.stackPosition= this.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 && this.lexStream.kind(this.buffer[last_index]) == EOFT_SYMBOL) {
PrimaryRepairInfo scope_repair= new PrimaryRepairInfo();
scope_repair.bufferPosition= last_index;
scope_repair.distance= 0;
for (top= this.stateStackTop; top >= 0 && repair.code == 0; top--) {
scope_repair= scopeTrial(this.stack, top, scope_repair);
if (scope_repair.distance > 0) {
repair.code= SCOPE_CODE;
i= this.scopeIndex[this.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= this.buffer[2];
candidate.symbol= this.lexStream.kind(this.buffer[2]);
this.lexStream.reset(this.lexStream.next(this.buffer[2]));
break;
case DELETION_CODE:
candidate.location= this.buffer[repair.bufferPosition];
candidate.symbol=
this.lexStream.kind(this.buffer[repair.bufferPosition]);
this.lexStream.reset(this.lexStream.next(this.buffer[repair.bufferPosition]));
break;
default: // SCOPE_CODE || SECONDARY_CODE
candidate.symbol= repair.symbol;
candidate.location= this.buffer[repair.bufferPosition];
this.lexStream.reset(this.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= this.buffer[2];
int stack_deletions= 0;
for (int top= stack_top - 1; top >= 0; top--) {
if (this.locationStack[top] < previous_loc) {
stack_deletions++;
}
previous_loc= this.locationStack[top];
int j= parseCheck(stck, top, this.lexStream.kind(this.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= this.buffer[2];
for (int top= stack_top; top >= 0 && repair.numDeletions >= stack_deletions; top--) {
if (this.locationStack[top] < previous_loc) {
stack_deletions++;
}
previous_loc= this.locationStack[top];
for (int i= 2; i <= (last_index - MIN_DISTANCE + 1) &&
(repair.numDeletions >= (stack_deletions + i - 1)); i++) {
int j= parseCheck(stck, top, this.lexStream.kind(this.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 < this.stateStackTop) {
reportError(DELETION_CODE,
Parser.terminal_index[ERROR_SYMBOL],
this.locationStack[repair.stackPosition],
this.buffer[1]);
}
for (int i= 0; i < this.scopeStackTop; i++) {
reportError(SCOPE_CODE,
-this.scopeIndex[i],
this.locationStack[this.scopePosition[i]],
this.buffer[1],
Parser.non_terminal_index[Parser.scope_lhs[this.scopeIndex[i]]]);
}
repair.symbol= Parser.scope_lhs[this.scopeIndex[this.scopeStackTop]] + NT_OFFSET;
this.stateStackTop= this.scopePosition[this.scopeStackTop];
reportError(SCOPE_CODE,
-this.scopeIndex[this.scopeStackTop],
this.locationStack[this.scopePosition[this.scopeStackTop]],
this.buffer[1],
getNtermIndex(this.stack[this.stateStackTop],
repair.symbol,
repair.bufferPosition));
break;
}
default: {
reportError(repair.code,
(repair.code == SECONDARY_CODE
? getNtermIndex(this.stack[repair.stackPosition],
repair.symbol,
repair.bufferPosition)
: Parser.terminal_index[ERROR_SYMBOL]),
this.locationStack[repair.stackPosition],
this.buffer[repair.bufferPosition - 1]);
this.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) {
this.tempStackTop= stack_top;
if (this.DEBUG_PARSECHECK) {
System.out.println(this.tempStackTop);
}
max_pos= stack_top;
indx= buffer_position;
ct= this.lexStream.kind(this.buffer[indx]);
this.lexStream.reset(this.lexStream.next(this.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 {
this.tempStackTop-= (Parser.rhs[act] - 1);
if (this.DEBUG_PARSECHECK) {
System.out.print(this.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= (this.tempStackTop > max_pos
? this.tempStack[this.tempStackTop]
: stck[this.tempStackTop]);
act= Parser.ntAction(act, lhs_symbol);
} while (act <= NUM_RULES);
max_pos= max_pos < this.tempStackTop ? max_pos : this.tempStackTop;
}
} else {
this.tempStackTop= stack_top - 1;
if (this.DEBUG_PARSECHECK) {
System.out.println(this.tempStackTop);
}
max_pos= this.tempStackTop;
indx= buffer_position - 1;
ct= first_token;
this.lexStream.reset(this.buffer[buffer_position]);
}
process_terminal: for (;;) {
if (this.DEBUG_PARSECHECK) {
System.out.print(this.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 (++this.tempStackTop >= this.stackLength) // Stack overflow!!!
return indx;
this.tempStack[this.tempStackTop]= act;
act= Parser.tAction(act, ct);
if (act <= NUM_RULES) { // reduce action
this.tempStackTop--;
if (this.DEBUG_PARSECHECK) {
System.out.print(this.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= this.lexStream.kind(this.buffer[indx]);
this.lexStream.reset(this.lexStream.next(this.buffer[indx]));
if (act > ERROR_ACTION) {
act-= ERROR_ACTION;
if (this.DEBUG_PARSECHECK) {
System.out.print(this.tempStackTop);
System.out.print("\tshift reduce"); //$NON-NLS-1$
System.out.println();
}
} else {
if (this.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 {
this.tempStackTop-= (Parser.rhs[act] - 1);
if (this.DEBUG_PARSECHECK) {
System.out.print(this.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= (this.tempStackTop > max_pos
? this.tempStack[this.tempStackTop]
: stck[this.tempStackTop]);
act= Parser.ntAction(act, lhs_symbol);
} while (act <= NUM_RULES);
max_pos= max_pos < this.tempStackTop ? max_pos : this.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= Util.EMPTY_STRING;
}
int errorStart= this.lexStream.start(token);
int errorEnd= this.lexStream.end(token);
int currentKind= this.lexStream.kind(token);
String errorTokenName= Parser.name[Parser.terminal_index[this.lexStream.kind(token)]];
char[] errorTokenSource= this.lexStream.name(token);
if (currentKind == TerminalTokens.TokenNameStringLiteral) {
errorTokenSource= displayEscapeCharacters(errorTokenSource, 1, errorTokenSource.length - 1);
}
int addedToken= -1;
if (this.recoveryScanner != null) {
if (nameIndex >= 0) {
addedToken= Parser.reverse_index[nameIndex];
}
}
switch (msgCode) {
case BEFORE_CODE:
if (this.recoveryScanner != null) {
if (addedToken > -1) {
this.recoveryScanner.insertToken(addedToken, -1, errorStart);
} else {
int[] template= getNTermTemplate(-addedToken);
if (template != null) {
this.recoveryScanner.insertTokens(template, -1, errorStart);
}
}
}
if (this.reportProblem)
problemReporter().parseErrorInsertBeforeToken(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName,
name);
break;
case INSERTION_CODE:
if (this.recoveryScanner != null) {
if (addedToken > -1) {
this.recoveryScanner.insertToken(addedToken, -1, errorEnd);
} else {
int[] template= getNTermTemplate(-addedToken);
if (template != null) {
this.recoveryScanner.insertTokens(template, -1, errorEnd);
}
}
}
if (this.reportProblem)
problemReporter().parseErrorInsertAfterToken(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName,
name);
break;
case DELETION_CODE:
if (this.recoveryScanner != null) {
this.recoveryScanner.removeTokens(errorStart, errorEnd);
}
if (this.reportProblem)
problemReporter().parseErrorDeleteToken(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName);
break;
case INVALID_CODE:
if (name.length() == 0) {
if (this.recoveryScanner != null) {
this.recoveryScanner.removeTokens(errorStart, errorEnd);
}
if (this.reportProblem)
problemReporter().parseErrorReplaceToken(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName,
name);
} else {
if (this.recoveryScanner != null) {
if (addedToken > -1) {
this.recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
} else {
int[] template= getNTermTemplate(-addedToken);
if (template != null) {
this.recoveryScanner.replaceTokens(template, errorStart, errorEnd);
}
}
}
if (this.reportProblem)
problemReporter().parseErrorInvalidToken(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName,
name);
}
break;
case SUBSTITUTION_CODE:
if (this.recoveryScanner != null) {
if (addedToken > -1) {
this.recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
} else {
int[] template= getNTermTemplate(-addedToken);
if (template != null) {
this.recoveryScanner.replaceTokens(template, errorStart, errorEnd);
}
}
}
if (this.reportProblem)
problemReporter().parseErrorReplaceToken(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName,
name);
break;
case SCOPE_CODE:
StringBuffer buf= new StringBuffer();
int[] addedTokens= null;
int addedTokenCount= 0;
if (this.recoveryScanner != null) {
addedTokens= new int[Parser.scope_rhs.length - Parser.scope_suffix[-nameIndex]];
}
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 (addedTokens != null) {
int tmpAddedToken= Parser.reverse_index[Parser.scope_rhs[i]];
if (tmpAddedToken > -1) {
int length= addedTokens.length;
if (addedTokenCount == length) {
System.arraycopy(addedTokens, 0, addedTokens= new int[length * 2], 0, length);
}
addedTokens[addedTokenCount++]= tmpAddedToken;
} else {
int[] template= getNTermTemplate(-tmpAddedToken);
if (template != null) {
for (int j= 0; j < template.length; j++) {
int length= addedTokens.length;
if (addedTokenCount == length) {
System.arraycopy(addedTokens, 0, addedTokens= new int[length * 2], 0, length);
}
addedTokens[addedTokenCount++]= template[j];
}
} else {
addedTokenCount= 0;
addedTokens= null;
}
}
}
}
if (addedTokenCount > 0) {
System.arraycopy(addedTokens, 0, addedTokens= new int[addedTokenCount], 0, addedTokenCount);
int completedToken= -1;
if (scopeNameIndex != 0) {
completedToken= -Parser.reverse_index[scopeNameIndex];
}
this.recoveryScanner.insertTokens(addedTokens, completedToken, errorEnd);
}
if (scopeNameIndex != 0) {
if (this.reportProblem)
problemReporter().parseErrorInsertToComplete(
errorStart,
errorEnd,
buf.toString(),
Parser.readableName[scopeNameIndex]);
} else {
if (this.reportProblem)
problemReporter().parseErrorInsertToCompleteScope(
errorStart,
errorEnd,
buf.toString());
}
break;
case EOF_CODE:
if (this.reportProblem)
problemReporter().parseErrorUnexpectedEnd(
errorStart,
errorEnd);
break;
case MERGE_CODE:
if (this.recoveryScanner != null) {
if (addedToken > -1) {
this.recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
} else {
int[] template= getNTermTemplate(-addedToken);
if (template != null) {
this.recoveryScanner.replaceTokens(template, errorStart, errorEnd);
}
}
}
if (this.reportProblem)
problemReporter().parseErrorMergeTokens(
errorStart,
errorEnd,
name);
break;
case MISPLACED_CODE:
if (this.recoveryScanner != null) {
this.recoveryScanner.removeTokens(errorStart, errorEnd);
}
if (this.reportProblem)
problemReporter().parseErrorMisplacedConstruct(
errorStart,
errorEnd);
break;
default:
if (name.length() == 0) {
if (this.recoveryScanner != null) {
this.recoveryScanner.removeTokens(errorStart, errorEnd);
}
if (this.reportProblem)
problemReporter().parseErrorNoSuggestion(
errorStart,
errorEnd,
currentKind,
errorTokenSource,
errorTokenName);
} else {
if (this.recoveryScanner != null) {
if (addedToken > -1) {
this.recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
} else {
int[] template= getNTermTemplate(-addedToken);
if (template != null) {
this.recoveryScanner.replaceTokens(template, errorStart, errorEnd);
}
}
}
if (this.reportProblem)
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= Util.EMPTY_STRING;
}
int errorStart= -1;
if (this.lexStream.isInsideStream(leftToken)) {
if (leftToken == 0) {
errorStart= this.lexStream.start(leftToken + 1);
} else {
errorStart= this.lexStream.start(leftToken);
}
} else {
if (leftToken == this.errorToken) {
errorStart= this.errorTokenStart;
} else {
for (int i= 0; i <= this.stateStackTop; i++) {
if (this.locationStack[i] == leftToken) {
errorStart= this.locationStartStack[i];
}
}
}
if (errorStart == -1) {
errorStart= this.lexStream.start(rightToken);
}
}
int errorEnd= this.lexStream.end(rightToken);
int addedToken= -1;
if (this.recoveryScanner != null) {
if (nameIndex >= 0) {
addedToken= Parser.reverse_index[nameIndex];
}
}
switch (msgCode) {
case MISPLACED_CODE:
if (this.recoveryScanner != null) {
this.recoveryScanner.removeTokens(errorStart, errorEnd);
}
if (this.reportProblem)
problemReporter().parseErrorMisplacedConstruct(
errorStart,
errorEnd);
break;
case SCOPE_CODE:
// error start is on the last token start
errorStart= this.lexStream.start(rightToken);
StringBuffer buf= new StringBuffer();
int[] addedTokens= null;
int addedTokenCount= 0;
if (this.recoveryScanner != null) {
addedTokens= new int[Parser.scope_rhs.length - Parser.scope_suffix[-nameIndex]];
}
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 (addedTokens != null) {
int tmpAddedToken= Parser.reverse_index[Parser.scope_rhs[i]];
if (tmpAddedToken > -1) {
int length= addedTokens.length;
if (addedTokenCount == length) {
System.arraycopy(addedTokens, 0, addedTokens= new int[length * 2], 0, length);
}
addedTokens[addedTokenCount++]= tmpAddedToken;
} else {
int[] template= getNTermTemplate(-tmpAddedToken);
if (template != null) {
for (int j= 0; j < template.length; j++) {
int length= addedTokens.length;
if (addedTokenCount == length) {
System.arraycopy(addedTokens, 0, addedTokens= new int[length * 2], 0, length);
}
addedTokens[addedTokenCount++]= template[j];
}
} else {
addedTokenCount= 0;
addedTokens= null;
}
}
}
}
if (addedTokenCount > 0) {
System.arraycopy(addedTokens, 0, addedTokens= new int[addedTokenCount], 0, addedTokenCount);
int completedToken= -1;
if (scopeNameIndex != 0) {
completedToken= -Parser.reverse_index[scopeNameIndex];
}
this.recoveryScanner.insertTokens(addedTokens, completedToken, errorEnd);
}
if (scopeNameIndex != 0) {
if (this.reportProblem)
problemReporter().parseErrorInsertToComplete(
errorStart,
errorEnd,
buf.toString(),
Parser.readableName[scopeNameIndex]);
} else {
if (this.reportProblem)
problemReporter().parseErrorInsertToCompletePhrase(
errorStart,
errorEnd,
buf.toString());
}
break;
case MERGE_CODE:
if (this.recoveryScanner != null) {
if (addedToken > -1) {
this.recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
} else {
int[] template= getNTermTemplate(-addedToken);
if (template != null) {
this.recoveryScanner.replaceTokens(template, errorStart, errorEnd);
}
}
}
if (this.reportProblem)
problemReporter().parseErrorMergeTokens(
errorStart,
errorEnd,
name);
break;
case DELETION_CODE:
if (this.recoveryScanner != null) {
this.recoveryScanner.removeTokens(errorStart, errorEnd);
}
if (this.reportProblem)
problemReporter().parseErrorDeleteTokens(
errorStart,
errorEnd);
break;
default:
if (name.length() == 0) {
if (this.recoveryScanner != null) {
this.recoveryScanner.removeTokens(errorStart, errorEnd);
}
if (this.reportProblem)
problemReporter().parseErrorNoSuggestionForTokens(
errorStart,
errorEnd);
} else {
if (this.recoveryScanner != null) {
if (addedToken > -1) {
this.recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
} else {
int[] template= getNTermTemplate(-addedToken);
if (template != null) {
this.recoveryScanner.replaceTokens(template, errorStart, errorEnd);
}
}
}
if (this.reportProblem)
problemReporter().parseErrorReplaceTokens(
errorStart,
errorEnd,
name);
}
}
return;
}
private int[] getNTermTemplate(int sym) {
int templateIndex= Parser.recovery_templates_index[sym];
if (templateIndex > 0) {
int[] result= new int[Parser.recovery_templates.length];
int count= 0;
for (int j= templateIndex; Parser.recovery_templates[j] != 0; j++) {
result[count++]= Parser.recovery_templates[j];
}
System.arraycopy(result, 0, result= new int[count], 0, count);
return result;
} else {
return null;
}
}
public String toString() {
StringBuffer res= new StringBuffer();
res.append(this.lexStream.toString());
return res.toString();
}
}