blob: c8fbbc11f5ec55c90c1b1078de4a32ffe706372d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* This is an implementation of an early-draft specification developed under the Java
* Community Process (JCP) and is made available for testing and evaluation purposes
* only. The code is not compatible with any specification of the JCP.
*
* Contributors:
* IBM Corporation - initial API and implementation
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended 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.ConflictedParser;
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;
/**
* OTDT changes:
* What: Switch modes for 'base': Keyword vs. Identifier.
* How: Use LexStream.{forceBaseIsIdentifier,restoreBaseKeyword}()
*
* What: disable messages saying "expecting Foo" where Foo is a disabled keyword.
*
* @version $Id: DiagnoseParser.java 19876 2009-04-13 19:39:46Z stephan $
*/
public class DiagnoseParser implements ParserBasicInformation, TerminalTokens, ConflictedParser {
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;
}
this.parser.scanner.setActiveParser(this);
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 {
//{ObjectTeams: our scanner is stateful:
setScannerState(act);
// SH}
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]]);
}
//{ObjectTeams: our scanner is stateful:
setScannerState(act);
// SH}
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]]);
}
//{ObjectTeams: our scanner is stateful:
setScannerState(act);
// SH}
act = Parser.ntAction(act, lhs_symbol);
while(act <= NUM_RULES) {
//{ObjectTeams: our scanner is stateful:
setScannerState(act);
// SH}
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;
}
this.parser.scanner.setActiveParser(null);
}
return;
}
//{ObjectTeams: WATCHOUT: needs to be updated with each new grammar!!!!
private void setScannerState(int act) {
switch(act) {
case 239: this.lexStream.forceBaseIsIdentifier(); break; // ForceBaseIsIdentifier
case 240: this.lexStream.restoreBaseKeyword(); break; // RestoreBaseKeyword
case 334: this.parser.scanner._insideParameterMapping = true; break; // NestedParamMappings
}
}
//SH}
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];
Util.appendEscapedChar(tokenSourceBuffer, c, true);
}
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);
//{ObjectTeams: if misspell() found disabled token, rank distance as worst case:
if (k == -1)
j= 0;
// SH}
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: {
//{ObjectTeams: avoid message: Syntax error on token "foo", foo expected
// for disabled OT-keywords:
if (!this.parser.scanner.isTokenEnabled(repair.symbol)) {
int start = this.lexStream.start(curtok);
int end = this.lexStream.end(curtok);
this.parser.problemReporter().otKeywordInRegularClass(start, end);
break;
}
// SH}
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;
//{ObjectTeams: disabled token is considered only if 100% matched:
if (!this.parser.scanner.isTokenEnabled(sym))
if (num_errors > 0 || n != len)
return -1; // not a good candidate
// SH}
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];
//{ObjectTeams: integrate stateful scanner:
setScannerState(act);
// SH}
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);
//{ObjectTeams: integrate stateful scanner:
setScannerState(act);
// SH}
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]];
}
int insertedToken = TokenNameNotAToken;
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(' ');
else
insertedToken = Parser.reverse_index[Parser.scope_rhs[i]];
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 (insertedToken == TokenNameElidedSemicolonAndRightBrace) {
/* https://bugs.eclipse.org/bugs/show_bug.cgi?id=383046, we should never ever report the diagnostic, "Syntax error, insert ElidedSemicolonAndRightBraceto complete LambdaBody"
as it is a synthetic token. Instead we should simply repair and move on. See how the regular Parser behaves at Parser.consumeElidedLeftBraceAndReturn and Parser.consumeExpression.
See also: point (4) in https://bugs.eclipse.org/bugs/show_bug.cgi?id=380194#c15
*/
break;
}
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]];
}
int insertedToken = TokenNameNotAToken;
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(' ');
else
insertedToken = Parser.reverse_index[Parser.scope_rhs[i]];
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 (insertedToken == TokenNameElidedSemicolonAndRightBrace) {
/* https://bugs.eclipse.org/bugs/show_bug.cgi?id=383046, we should never ever report the diagnostic, "Syntax error, insert ElidedSemicolonAndRightBraceto complete LambdaBody"
as it is a synthetic token. Instead we should simply repair and move on. See how the regular Parser behaves at Parser.consumeElidedLeftBraceAndReturn and Parser.consumeExpression.
See also: point (4) in https://bugs.eclipse.org/bugs/show_bug.cgi?id=380194#c15
*/
break;
}
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;
}
}
@Override
public String toString() {
StringBuffer res = new StringBuffer();
res.append(this.lexStream.toString());
return res.toString();
}
@Override
public boolean atConflictScenario(int token) {
/* There is too much voodoo that goes on here in DiagnoseParser (multiple machines, lexer stream reset etc.)
So we take a simple minded view that we will always ask for disambiguation, except there is one scenario
that needs special handling, we let the lexer stream deal with that: In X<String>.Y<Integer>:: the second
'<' should not be tagged for disambiguation. If a synthetic token gets injected there, there will be syntax
error. See that this is not a problem for the regular/normal parser.
*/
return (token == TokenNameLPAREN || token == TokenNameAT || (token == TokenNameLESS && !this.lexStream.awaitingColonColon()));
}
@Override
public boolean isParsingModuleDeclaration() {
//
return this.parser.isParsingModuleDeclaration();
}
}