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