/*******************************************************************************
 * Copyright (c) 2000, 2014 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.parser.diagnose;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.parser.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;

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 {
							this.nextStackTop -= (Parser.rhs[act]-1);
							act = Parser.ntAction(this.nextStack[this.nextStackTop], Parser.lhs[act]);
						} while(act <= NUM_RULES);
						pos = pos < this.nextStackTop ? pos : this.nextStackTop;
					}

					if (this.nextStackTop + 1 >= this.stackLength)
						reallocateStacks();

					this.tempStackTop = this.nextStackTop;
					this.nextStack[++this.nextStackTop] = act;
					next_pos = this.nextStackTop;

					//
					// Simulate the parser through the next token without
					// destroying STACK or next_stack.
					//
					this.currentToken = this.lexStream.getToken();
					tok = this.lexStream.kind(this.currentToken);
					act = Parser.tAction(act, tok);
					while(act <= NUM_RULES) {
						//
						// ... Process all goto-reduce actions following
						// reduction, until a goto action is computed ...
						//
						do {
							int lhs_symbol = Parser.lhs[act];
							if(DEBUG) {
								System.out.println(Parser.name[Parser.non_terminal_index[lhs_symbol]]);
							}
							this.tempStackTop -= (Parser.rhs[act]-1);
							act = (this.tempStackTop > next_pos
									   ? this.tempStack[this.tempStackTop]
									   : this.nextStack[this.tempStackTop]);
							act = Parser.ntAction(act, lhs_symbol);
						}   while(act <= NUM_RULES);

						//
						// ... Update the maximum useful position of the
						// (STATE_)STACK, push GOTO state into stack, and
						// compute next action on current symbol ...
						//
						if (this.tempStackTop + 1 >= this.stackLength)
							reallocateStacks();

						next_pos = next_pos < this.tempStackTop ? next_pos : this.tempStackTop;
						this.tempStack[this.tempStackTop + 1] = act;
						act = Parser.tAction(act, tok);
					}

	//				if((tok != TokenNameRBRACE || (forceRecoveryToken != currentToken && (lexStream.flags(currentToken) & LexStream.LBRACE_MISSING) != 0))
	//					&& (lexStream.flags(currentToken) & LexStream.IS_AFTER_JUMP) !=0) {
	//					act = ERROR_ACTION;
	//					if(forceRecoveryToken != currentToken
	//						&& (lexStream.flags(currentToken) & LexStream.LBRACE_MISSING) != 0) {
	//						forceRecoveryAfterLBracketMissing = true;
	//						forceRecoveryToken = currentToken;
	//					}
	//				}

					//
					// No error was detected, Read next token into
					// PREVTOK element, advance CURTOK pointer and
					// update stacks.
					//
					if (act != ERROR_ACTION) {
						this.prevStackTop = this.stateStackTop;
						for (int i = prev_pos + 1; i <= this.prevStackTop; i++)
							this.prevStack[i] = this.stack[i];
						prev_pos = pos;

						this.stateStackTop = this.nextStackTop;
						for (int i = pos + 1; i <= this.stateStackTop; i++)
							this.stack[i] = this.nextStack[i];
						this.locationStack[this.stateStackTop] = this.currentToken;
						this.locationStartStack[this.stateStackTop] = this.lexStream.start(this.currentToken);
						pos = next_pos;
					}
				}

				//
				// At this stage, either we have an ACCEPT or an ERROR
				// action.
				//
				if (act == ERROR_ACTION) {
					//
					// An error was detected.
					//
					RepairCandidate candidate = errorRecovery(this.currentToken, forceRecoveryAfterLBracketMissing);

					forceRecoveryAfterLBracketMissing = false;

					if(this.parser.reportOnlyOneSyntaxError) {
						return;
					}

					if(this.parser.problemReporter().options.maxProblemsPerUnit < this.parser.compilationUnit.compilationResult.problemCount) {
						if(this.recoveryScanner == null || !this.recoveryScanner.record) return;
						this.reportProblem = false;
					}

					act = this.stack[this.stateStackTop];

					//
					// If the recovery was successful on a nonterminal candidate,
					// parse through that candidate and "read" the next token.
					//
					if (candidate.symbol == 0) {
						break;
					} else if (candidate.symbol > NT_OFFSET) {
						int lhs_symbol = candidate.symbol - NT_OFFSET;
						if(DEBUG) {
							System.out.println(Parser.name[Parser.non_terminal_index[lhs_symbol]]);
						}
						act = Parser.ntAction(act, lhs_symbol);
						while(act <= NUM_RULES) {
							this.stateStackTop -= (Parser.rhs[act]-1);
							act = Parser.ntAction(this.stack[this.stateStackTop], Parser.lhs[act]);
						}
						this.stack[++this.stateStackTop] = act;
						this.currentToken = this.lexStream.getToken();
						tok = this.lexStream.kind(this.currentToken);
						this.locationStack[this.stateStackTop] = this.currentToken;
						this.locationStartStack[this.stateStackTop] = this.lexStream.start(this.currentToken);
					} else {
						tok = candidate.symbol;
						this.locationStack[this.stateStackTop] = candidate.location;
						this.locationStartStack[this.stateStackTop] = this.lexStream.start(candidate.location);
					}
				}
			} while (act != ACCEPT_ACTION);
		} finally {
			if(this.recoveryScanner != null) {
				this.recoveryScanner.record = oldRecord;
			}
			this.parser.scanner.setActiveParser(null);
		}
		return;
	}

	private static char[] displayEscapeCharacters(char[] tokenSource, int start, int end) {
		StringBuffer tokenSourceBuffer = new StringBuffer();
		for (int i = 0; i < start; i++) {
			tokenSourceBuffer.append(tokenSource[i]);
		}
		for (int i = start; i < end; i++) {
			char c = tokenSource[i];

			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);
				if (j > repair.distance) {
					repair.misspellIndex = k;
					repair.distance = j;
					repair.symbol = symbol;
					repair.code = SUBSTITUTION_CODE;
				} else if (j == repair.distance && k > repair.misspellIndex) {
					repair.misspellIndex = k;
					repair.symbol = symbol;
					repair.code = SUBSTITUTION_CODE;
				}
				i = symbol;
				symbol = this.list[symbol];
				this.list[i] = 0;                             // reset element
			}
		}


		//
		// Next, we try to insert a nonterminal candidate in front of the
		// error token, or substituting a nonterminal candidate for the
		// error token. Precedence is given to insertion.
		//
		 for (i = Parser.nasi(stck[stack_top]); Parser.nasr[i] != 0; i++) {
			 symbol = Parser.nasr[i] + NT_OFFSET;
			 j = parseCheck(stck, stack_top, symbol, repair.bufferPosition+1);
			 if (j > repair.distance) {
				 repair.misspellIndex = 0;
				 repair.distance = j;
				 repair.symbol = symbol;
				 repair.code = INVALID_CODE;
			 }

			 j = parseCheck(stck, stack_top, symbol, repair.bufferPosition);
			 if ((j > repair.distance) || (j == repair.distance && repair.code == INVALID_CODE)) {
				 repair.misspellIndex = 0;
				 repair.distance = j;
				 repair.symbol = symbol;
				 repair.code = INSERTION_CODE;
			 }
		 }

		return repair;
	}


//
//	   This procedure is invoked to issue a diagnostic message and
//	   adjust the input buffer.  The recovery in question is either
//	   the insertion of one or more scopes, the merging of the error
//	   token with its successor, the deletion of the error token,
//	   the insertion of a single token in front of the error token
//	   or the substitution of another token for the error token.
//
	private RepairCandidate primaryDiagnosis(PrimaryRepairInfo repair) {
		int name_index;

		//
		//  Issue diagnostic.
		//
		int prevtok = this.buffer[repair.bufferPosition - 1];
		int	curtok  = this.buffer[repair.bufferPosition];

		switch(repair.code) {
			case INSERTION_CODE:
			case BEFORE_CODE: {
				if (repair.symbol > NT_OFFSET)
					 name_index = getNtermIndex(this.stack[this.stateStackTop],
												repair.symbol,
												repair.bufferPosition);
				else name_index = getTermIndex(this.stack,
											   this.stateStackTop,
											   repair.symbol,
											   repair.bufferPosition);

				int t = (repair.code == INSERTION_CODE ? prevtok : curtok);
				reportError(repair.code, name_index, t, t);
				break;
			}
			case INVALID_CODE: {
				name_index = getNtermIndex(this.stack[this.stateStackTop],
										   repair.symbol,
										   repair.bufferPosition + 1);
				reportError(repair.code, name_index, curtok, curtok);
				break;
			}
			case SUBSTITUTION_CODE: {
				if (repair.misspellIndex >= 6)
					name_index = Parser.terminal_index[repair.symbol];
				else
				{
					name_index = getTermIndex(this.stack, this.stateStackTop,
											  repair.symbol,
											  repair.bufferPosition + 1);
					if (name_index != Parser.terminal_index[repair.symbol])
						repair.code = INVALID_CODE;
				}
				reportError(repair.code, name_index, curtok, curtok);
				break;
			}
			case MERGE_CODE: {
				reportError(repair.code,
							 Parser.terminal_index[repair.symbol],
							 curtok,
							 this.lexStream.next(curtok));
				break;
			}
			case SCOPE_CODE: {
	            for (int i = 0; i < this.scopeStackTop; i++) {
	                reportError(repair.code,
	                            -this.scopeIndex[i],
	                            this.locationStack[this.scopePosition[i]],
	                            prevtok,
	                            Parser.non_terminal_index[Parser.scope_lhs[this.scopeIndex[i]]]);
	            }

	            repair.symbol = Parser.scope_lhs[this.scopeIndex[this.scopeStackTop]] + NT_OFFSET;
	            this.stateStackTop = this.scopePosition[this.scopeStackTop];
	            reportError(repair.code,
	                        -this.scopeIndex[this.scopeStackTop],
	                        this.locationStack[this.scopePosition[this.scopeStackTop]],
	                        prevtok,
	                        getNtermIndex(this.stack[this.stateStackTop],
	                                      repair.symbol,
	                                      repair.bufferPosition)
	                       );
	            break;
	        }
			default: {// deletion
				reportError(repair.code, Parser.terminal_index[ERROR_SYMBOL], curtok, curtok);
			}
		}

		//
		//  Update buffer.
		//
		RepairCandidate candidate = new RepairCandidate();
		switch (repair.code) {
			case INSERTION_CODE:
			case BEFORE_CODE:
			case SCOPE_CODE: {
				candidate.symbol = repair.symbol;
				candidate.location = this.buffer[repair.bufferPosition];
				this.lexStream.reset(this.buffer[repair.bufferPosition]);
				break;
			}
			case INVALID_CODE:
			case SUBSTITUTION_CODE: {
				candidate.symbol = repair.symbol;
				candidate.location = this.buffer[repair.bufferPosition];
				this.lexStream.reset(this.buffer[repair.bufferPosition + 1]);
				break;
			}
			case MERGE_CODE: {
				candidate.symbol = repair.symbol;
				candidate.location = this.buffer[repair.bufferPosition];
				this.lexStream.reset(this.buffer[repair.bufferPosition + 2]);
				break;
			}
			default: {// deletion
				candidate.location = this.buffer[repair.bufferPosition + 1];
				candidate.symbol =
						  this.lexStream.kind(this.buffer[repair.bufferPosition + 1]);
				this.lexStream.reset(this.buffer[repair.bufferPosition + 2]);
				break;
			}
		}

		return candidate;
	}


//
//	   This function takes as parameter an integer STACK_TOP that
//	   points to a STACK element containing the state on which a
//	   primary recovery will be made; the terminal candidate on which
//	   to recover; and an integer: buffer_position, which points to
//	   the position of the next input token in the BUFFER.  The
//	   parser is simulated until a shift (or shift-reduce) action
//	   is computed on the candidate.  Then we proceed to compute the
//	   the name index of the highest level nonterminal that can
//	   directly or indirectly produce the candidate.
//
	private int getTermIndex(int stck[], int stack_top, int tok, int buffer_position) {
		//
		// Initialize stack index of temp_stack and initialize maximum
		// position of state stack that is still useful.
		//
		int act = stck[stack_top],
			max_pos = stack_top,
			highest_symbol = tok;

		this.tempStackTop = stack_top - 1;

		//
		// Compute all reduce and associated actions induced by the
		// candidate until a SHIFT or SHIFT-REDUCE is computed. ERROR
		// and ACCEPT actions cannot be computed on the candidate in
		// this context, since we know that it is suitable for recovery.
		//
		this.lexStream.reset(this.buffer[buffer_position]);
		act = Parser.tAction(act, tok);
		while(act <= NUM_RULES) {
			//
			// Process all goto-reduce actions following reduction,
			// until a goto action is computed ...
			//
			do {
				this.tempStackTop -= (Parser.rhs[act]-1);
				int lhs_symbol = Parser.lhs[act];
				act = (this.tempStackTop > max_pos
									  ? this.tempStack[this.tempStackTop]
									  : stck[this.tempStackTop]);
				act = Parser.ntAction(act, lhs_symbol);
			} while(act <= NUM_RULES);

			//
			// Compute new maximum useful position of (STATE_)stack,
			// push goto state into the stack, and compute next
			// action on candidate ...
			//
			max_pos = max_pos < this.tempStackTop ? max_pos : this.tempStackTop;
			this.tempStack[this.tempStackTop + 1] = act;
			act = Parser.tAction(act, tok);
		}

		//
		// At this stage, we have simulated all actions induced by the
		// candidate and we are ready to shift or shift-reduce it. First,
		// set tok and next_ptr appropriately and identify the candidate
		// as the initial highest_symbol. If a shift action was computed
		// on the candidate, update the stack and compute the next
		// action. Next, simulate all actions possible on the next input
		// token until we either have to shift it or are about to reduce
		// below the initial starting point in the stack (indicated by
		// max_pos as computed in the previous loop).  At that point,
		// return the highest_symbol computed.
		//
		this.tempStackTop++; // adjust top of stack to reflect last goto
						  // next move is shift or shift-reduce.
		int threshold = this.tempStackTop;

		tok = this.lexStream.kind(this.buffer[buffer_position]);
		this.lexStream.reset(this.buffer[buffer_position + 1]);

		if (act > ERROR_ACTION) {  // shift-reduce on candidate?
			act -= ERROR_ACTION;
		} else {
			this.tempStack[this.tempStackTop + 1] = act;
			act = Parser.tAction(act, tok);
		}

		while(act <= NUM_RULES) {
			//
			// Process all goto-reduce actions following reduction,
			// until a goto action is computed ...
			//
			do {
				this.tempStackTop -= (Parser.rhs[act]-1);

				if (this.tempStackTop < threshold) {
					return (highest_symbol > NT_OFFSET
						 ? Parser.non_terminal_index[highest_symbol - NT_OFFSET]
						 : Parser.terminal_index[highest_symbol]);
				}

				int lhs_symbol = Parser.lhs[act];
				if (this.tempStackTop == threshold)
					highest_symbol = lhs_symbol + NT_OFFSET;
				act = (this.tempStackTop > max_pos
									  ? this.tempStack[this.tempStackTop]
									  : stck[this.tempStackTop]);
				act = Parser.ntAction(act, lhs_symbol);
			} while(act <= NUM_RULES);

			this.tempStack[this.tempStackTop + 1] = act;
			act = Parser.tAction(act, tok);
		}

		return (highest_symbol > NT_OFFSET
							 ? Parser.non_terminal_index[highest_symbol - NT_OFFSET]
							 : Parser.terminal_index[highest_symbol]);
	}

//
//	   This function takes as parameter a starting state number:
//	   start, a nonterminal symbol, A (candidate), and an integer,
//	   buffer_position,  which points to the position of the next
//	   input token in the BUFFER.
//	   It returns the highest level non-terminal B such that
//	   B =>*rm A.  I.e., there does not exists a nonterminal C such
//	   that C =>+rm B. (Recall that for an LALR(k) grammar if
//	   C =>+rm B, it cannot be the case that B =>+rm C)
//
	private int getNtermIndex(int start, int sym, int buffer_position) {
		int highest_symbol = sym - NT_OFFSET,
			tok = this.lexStream.kind(this.buffer[buffer_position]);
		this.lexStream.reset(this.buffer[buffer_position + 1]);

		//
		// Initialize stack index of temp_stack and initialize maximum
		// position of state stack that is still useful.
		//
		this.tempStackTop = 0;
		this.tempStack[this.tempStackTop] = start;

		int act = Parser.ntAction(start, highest_symbol);
		if (act > NUM_RULES) { // goto action?
			this.tempStack[this.tempStackTop + 1] = act;
			act = Parser.tAction(act, tok);
		}

		while(act <= NUM_RULES) {
			//
			// Process all goto-reduce actions following reduction,
			// until a goto action is computed ...
			//
			do {
				this.tempStackTop -= (Parser.rhs[act]-1);
				if (this.tempStackTop < 0)
					return Parser.non_terminal_index[highest_symbol];
				if (this.tempStackTop == 0)
					highest_symbol = Parser.lhs[act];
				act = Parser.ntAction(this.tempStack[this.tempStackTop], Parser.lhs[act]);
			} while(act <= NUM_RULES);
			this.tempStack[this.tempStackTop + 1] = act;
			act = Parser.tAction(act, tok);
		}

		return Parser.non_terminal_index[highest_symbol];
	}

//
//		   Check whether or not there is a high probability that a
//	   given string is a misspelling of another.
//	   Certain singleton symbols (such as ":" and ";") are also
//	   considered to be misspelling of each other.
//
	private int misspell(int sym, int tok) {


		//
		//
		//
		char[] name = Parser.name[Parser.terminal_index[sym]].toCharArray();
		int n = name.length;
		char[] s1 = new char[n + 1];
		for (int k = 0; k < n; k++) {
			char c = name[k];
			s1[k] = ScannerHelper.toLowerCase(c);
		}
		s1[n] = '\0';

		//
		//
		//
		char[] tokenName = this.lexStream.name(tok);
		int len = tokenName.length;
		int m = len < MAX_NAME_LENGTH ? len : MAX_NAME_LENGTH;
		char[] s2 = new char[m + 1];
		for (int k = 0; k < m; k++) {
			char c = tokenName[k];
			s2[k] = ScannerHelper.toLowerCase(c);
		}
		s2[m] = '\0';

		//
		//  Singleton mispellings:
		//
		//  ;      <---->     ,
		//
		//  ;      <---->     :
		//
		//  .      <---->     ,
		//
		//  '      <---->     "
		//
		//
		if (n == 1  &&  m == 1) {
			if ((s1[0] == ';'  &&  s2[0] == ',')  ||
				(s1[0] == ','  &&  s2[0] == ';')  ||
				(s1[0] == ';'  &&  s2[0] == ':')  ||
				(s1[0] == ':'  &&  s2[0] == ';')  ||
				(s1[0] == '.'  &&  s2[0] == ',')  ||
				(s1[0] == ','  &&  s2[0] == '.')  ||
				(s1[0] == '\'' &&  s2[0] == '\"')  ||
				(s1[0] == '\"'  &&  s2[0] == '\'')) {
					return 3;
			}
		}

		//
		// Scan the two strings. Increment "match" count for each match.
		// When a transposition is encountered, increase "match" count
		// by two but count it as an error. When a typo is found, skip
		// it and count it as an error. Otherwise we have a mismatch; if
		// one of the strings is longer, increment its index, otherwise,
		// increment both indices and continue.
		//
		// This algorithm is an adaptation of a boolean misspelling
		// algorithm proposed by Juergen Uhl.
		//
		int count = 0;
		int prefix_length = 0;
		int num_errors = 0;

		int i = 0;
		int j = 0;
		while ((i < n)  &&  (j < m)) {
			if (s1[i] == s2[j]) {
				count++;
				i++;
				j++;
				if (num_errors == 0) {
					prefix_length++;
				}
			} else if (s1[i+1] == s2[j]  &&  s1[i] == s2[j+1]) {
				count += 2;
				i += 2;
				j += 2;
				num_errors++;
			} else if (s1[i+1] == s2[j+1]) {
				i++;
				j++;
				num_errors++;
			} else {
				if ((n - i) > (m - j)) {
					 i++;
				} else if ((m - j) > (n - i)) {
					 j++;
				} else {
					i++;
					j++;
				}
				num_errors++;
			}
		}

		if (i < n  ||  j < m)
			num_errors++;

		if (num_errors > ((n < m ? n : m) / 6 + 1))
			 count = prefix_length;

		return(count * 10 / ((n < len ? len : n) + num_errors));
	}

	private PrimaryRepairInfo scopeTrial(int stck[], int stack_top, PrimaryRepairInfo repair) {
	    this.stateSeen = new int[this.stackLength];
	    for (int i = 0; i < this.stackLength; i++)
	        this.stateSeen[i] = NIL;

	    this.statePoolTop = 0;
	    this.statePool = new StateInfo[this.stackLength];

	    scopeTrialCheck(stck, stack_top, repair, 0);

	    this.stateSeen = null;
	    this.statePoolTop = 0;

	    repair.code = SCOPE_CODE;
	    repair.misspellIndex = 10;

	    return repair;
	}

	private void scopeTrialCheck(int stck[], int stack_top, PrimaryRepairInfo repair, int indx) {
		if(indx > 20) return; // avoid too much recursive call to improve performance

		int act = stck[stack_top];

	    for (int i = this.stateSeen[stack_top]; i != NIL; i = this.statePool[i].next) {
	        if (this.statePool[i].state == act) return;
	    }

	    int old_state_pool_top = this.statePoolTop++;
	    if(this.statePoolTop >= this.statePool.length) {
	    	System.arraycopy(this.statePool, 0, this.statePool = new StateInfo[this.statePoolTop * 2], 0, this.statePoolTop);
	    }

	    this.statePool[old_state_pool_top] = new StateInfo(act, this.stateSeen[stack_top]);
	    this.stateSeen[stack_top] = old_state_pool_top;

	    next : for (int i = 0; i < SCOPE_SIZE; i++) {
	        //
	        // Use the scope lookahead symbol to force all reductions
	        // inducible by that symbol.
	        //
	        act = stck[stack_top];
	        this.tempStackTop = stack_top - 1;
	        int max_pos = stack_top;
	        int tok = Parser.scope_la[i];
	        this.lexStream.reset(this.buffer[repair.bufferPosition]);
	        act = Parser.tAction(act, tok);
	        while(act <= NUM_RULES) {
	            //
	            // ... Process all goto-reduce actions following
	            // reduction, until a goto action is computed ...
	            //
	            do  {
	                this.tempStackTop -= (Parser.rhs[act]-1);
	                int lhs_symbol = Parser.lhs[act];
	                act =  (this.tempStackTop > max_pos
	                            ?  this.tempStack[this.tempStackTop]
	                            :  stck[this.tempStackTop]);
	                act = Parser.ntAction(act, lhs_symbol);
	            }  while(act <= NUM_RULES);
	            if (this.tempStackTop + 1 >= this.stackLength)
	                return;
	            max_pos = max_pos < this.tempStackTop ? max_pos : this.tempStackTop;
	            this.tempStack[this.tempStackTop + 1] = act;
	            act = Parser.tAction(act, tok);
	        }

	        //
	        // If the lookahead symbol is parsable, then we check
	        // whether or not we have a match between the scope
	        // prefix and the transition symbols corresponding to
	        // the states on top of the stack.
	        //
	        if (act != ERROR_ACTION) {
	        	int j, k;
	            k = Parser.scope_prefix[i];
	            for (j = this.tempStackTop + 1;
	                 j >= (max_pos + 1) &&
	                 Parser.in_symbol(this.tempStack[j]) == Parser.scope_rhs[k]; j--) {
	                 k++;
	            }
	            if (j == max_pos) {
	                for (j = max_pos;
	                     j >= 1 && Parser.in_symbol(stck[j]) == Parser.scope_rhs[k];
	                     j--) {
	                    k++;
	                }
	            }
	            //
	            // If the prefix matches, check whether the state
	            // newly exposed on top of the stack, (after the
	            // corresponding prefix states are popped from the
	            // stack), is in the set of "source states" for the
	            // scope in question and that it is at a position
	            // below the threshold indicated by MARKED_POS.
	            //
	            int marked_pos = (max_pos < stack_top ? max_pos + 1 : stack_top);
	            if (Parser.scope_rhs[k] == 0 && j < marked_pos) { // match?
	                int stack_position = j;
	                for (j = Parser.scope_state_set[i];
	                     stck[stack_position] != Parser.scope_state[j] &&
	                     Parser.scope_state[j] != 0;
	                     j++){/*empty*/}
	                //
	                // If the top state is valid for scope recovery,
	                // the left-hand side of the scope is used as
	                // starting symbol and we calculate how far the
	                // parser can advance within the forward context
	                // after parsing the left-hand symbol.
	                //
	                if (Parser.scope_state[j] != 0) {     // state was found
	                    int previous_distance = repair.distance;
	                    int distance = parseCheck(stck,
	                                          stack_position,
	                                          Parser.scope_lhs[i]+NT_OFFSET,
	                                          repair.bufferPosition);
	                    //
	                    // if the recovery is not successful, we
	                    // update the stack with all actions induced
	                    // by the left-hand symbol, and recursively
	                    // call SCOPE_TRIAL_CHECK to try again.
	                    // Otherwise, the recovery is successful. If
	                    // the new distance is greater than the
	                    // initial SCOPE_DISTANCE, we update
	                    // SCOPE_DISTANCE and set scope_stack_top to INDX
	                    // to indicate the number of scopes that are
	                    // to be applied for a succesful  recovery.
	                    // NOTE that this procedure cannot get into
	                    // an infinite loop, since each prefix match
	                    // is guaranteed to take us to a lower point
	                    // within the stack.
	                    //
	                    if ((distance - repair.bufferPosition + 1) < MIN_DISTANCE) {
	                        int top = stack_position;
	                        act = Parser.ntAction(stck[top], Parser.scope_lhs[i]);
	                        while(act <= NUM_RULES) {
	                        	if(Parser.rules_compliance[act] > this.options.sourceLevel) {
								 	continue next;
								}
	                            top -= (Parser.rhs[act]-1);
	                            act = Parser.ntAction(stck[top], Parser.lhs[act]);
	                        }
	                        top++;

	                        j = act;
	                        act = stck[top];  // save
	                        stck[top] = j;    // swap
	                        scopeTrialCheck(stck, top, repair, indx+1);
	                        stck[top] = act; // restore
	                    } else if (distance > repair.distance) {
	                        this.scopeStackTop = indx;
	                        repair.distance = distance;
	                    }

	                    if (this.lexStream.kind(this.buffer[repair.bufferPosition]) == EOFT_SYMBOL &&
	                        repair.distance == previous_distance) {
	                        this.scopeStackTop = indx;
	                        repair.distance = MAX_DISTANCE;
	                    }

	                    //
	                    // If this scope recovery has beaten the
	                    // previous distance, then we have found a
	                    // better recovery (or this recovery is one
	                    // of a list of scope recoveries). Record
	                    // its information at the proper location
	                    // (INDX) in SCOPE_INDEX and SCOPE_STACK.
	                    //
	                    if (repair.distance > previous_distance) {
	                        this.scopeIndex[indx] = i;
	                        this.scopePosition[indx] = stack_position;
	                        return;
	                    }
	                }
	            }
	        }
	    }
	}
//
//	   This function computes the ParseCheck distance for the best
//	   possible secondary recovery for a given configuration that
//	   either deletes none or only one symbol in the forward context.
//	   If the recovery found is more effective than the best primary
//	   recovery previously computed, then the function returns true.
//	   Only misplacement, scope and manual recoveries are attempted;
//	   simple insertion or substitution of a nonterminal are tried
//	   in CHECK_PRIMARY_DISTANCE as part of primary recovery.
//
	private boolean secondaryCheck(int stck[], int stack_top, int buffer_position, int distance) {
		int top, j;

		for (top = stack_top - 1; top >= 0; top--) {
			j = parseCheck(stck, top,
						   this.lexStream.kind(this.buffer[buffer_position]),
						   buffer_position + 1);
			if (((j - buffer_position + 1) > MIN_DISTANCE) && (j > distance))
				return true;
		}

		PrimaryRepairInfo repair = new PrimaryRepairInfo();
	    repair.bufferPosition = buffer_position + 1;
	    repair.distance = distance;
	    repair = scopeTrial(stck, stack_top, repair);
	    if ((repair.distance - buffer_position) > MIN_DISTANCE && repair.distance > distance)
	         return true;
		return false;
	}


//
//	   Secondary_phase is a boolean function that checks whether or
//	   not some form of secondary recovery is applicable to one of
//	   the error configurations. First, if "next_stack" is available,
//	   misplacement and secondary recoveries are attempted on it.
//	   Then, in any case, these recoveries are attempted on "stack".
//	   If a successful recovery is found, a diagnosis is issued, the
//	   configuration is updated and the function returns "true".
//	   Otherwise, the function returns false.
//
	private RepairCandidate secondaryPhase(int error_token) {
		SecondaryRepairInfo repair = new SecondaryRepairInfo();
		SecondaryRepairInfo misplaced = new SecondaryRepairInfo();

		RepairCandidate candidate = new RepairCandidate();

		int i, j, k, top;
		int	next_last_index = 0;
		int	last_index;

		candidate.symbol = 0;

		repair.code = 0;
		repair.distance = 0;
		repair.recoveryOnNextStack = false;

		misplaced.distance = 0;
		misplaced.recoveryOnNextStack = false;

		//
		// If the next_stack is available, try misplaced and secondary
		// recovery on it first.
		//
		if (this.nextStackTop >= 0) {
			int  save_location;

			this.buffer[2] = error_token;
			this.buffer[1] = this.lexStream.previous(this.buffer[2]);
			this.buffer[0] = this.lexStream.previous(this.buffer[1]);

			for (k = 3; k < BUFF_UBOUND; k++)
				this.buffer[k] = this.lexStream.next(this.buffer[k - 1]);

			this.buffer[BUFF_UBOUND] = this.lexStream.badtoken();// elmt not available

			//
			// If we are at the end of the input stream, compute the
			// index position of the first EOFT symbol (last useful
			// index).
			//
			for (next_last_index = MAX_DISTANCE - 1;
				 next_last_index >= 1 &&
				 this.lexStream.kind(this.buffer[next_last_index]) == EOFT_SYMBOL;
				 next_last_index--){/*empty*/}
			next_last_index = next_last_index + 1;

			save_location = this.locationStack[this.nextStackTop];
			int save_location_start = this.locationStartStack[this.nextStackTop];
			this.locationStack[this.nextStackTop] = this.buffer[2];
			this.locationStartStack[this.nextStackTop] = this.lexStream.start(this.buffer[2]);
			misplaced.numDeletions = this.nextStackTop;
			misplaced = misplacementRecovery(this.nextStack, this.nextStackTop,
											 next_last_index,
											 misplaced, true);
			if (misplaced.recoveryOnNextStack)
				misplaced.distance++;

			repair.numDeletions = this.nextStackTop + BUFF_UBOUND;
			repair = secondaryRecovery(this.nextStack, this.nextStackTop,
									   next_last_index,
									   repair, true);
			if (repair.recoveryOnNextStack)
				repair.distance++;

			this.locationStack[this.nextStackTop] = save_location;
			this.locationStartStack[this.nextStackTop] = save_location_start;
		} else {            // next_stack not available, initialize ...
			misplaced.numDeletions = this.stateStackTop;
			repair.numDeletions = this.stateStackTop + BUFF_UBOUND;
		}

		//
		// Try secondary recovery on the "stack" configuration.
		//
		this.buffer[3] = error_token;

		this.buffer[2] = this.lexStream.previous(this.buffer[3]);
		this.buffer[1] = this.lexStream.previous(this.buffer[2]);
		this.buffer[0] = this.lexStream.previous(this.buffer[1]);

		for (k = 4; k < BUFF_SIZE; k++)
			this.buffer[k] = this.lexStream.next(this.buffer[k - 1]);

		for (last_index = MAX_DISTANCE - 1;
			 last_index >= 1 && this.lexStream.kind(this.buffer[last_index]) == EOFT_SYMBOL;
			 last_index--){/*empty*/}
		last_index++;

		misplaced = misplacementRecovery(this.stack, this.stateStackTop,
										 last_index,
										 misplaced, false);

		repair = secondaryRecovery(this.stack, this.stateStackTop,
								   last_index, repair, false);

		//
		// If a successful misplaced recovery was found, compare it with
		// the most successful secondary recovery.  If the misplaced
		// recovery either deletes fewer symbols or parse-checks further
		// then it is chosen.
		//
		if (misplaced.distance > MIN_DISTANCE) {
			if (misplaced.numDeletions <= repair.numDeletions ||
			   (misplaced.distance - misplaced.numDeletions) >=
			   (repair.distance - repair.numDeletions)) {
				repair.code = MISPLACED_CODE;
				repair.stackPosition = misplaced.stackPosition;
				repair.bufferPosition = 2;
				repair.numDeletions = misplaced.numDeletions;
				repair.distance = misplaced.distance;
				repair.recoveryOnNextStack = misplaced.recoveryOnNextStack;
			}
		}

		//
		// If the successful recovery was on next_stack, update: stack,
		// buffer, location_stack and last_index.
		//
		if (repair.recoveryOnNextStack) {
			this.stateStackTop = this.nextStackTop;
			for (i = 0; i <= this.stateStackTop; i++)
				this.stack[i] = this.nextStack[i];

			this.buffer[2] = error_token;
			this.buffer[1] = this.lexStream.previous(this.buffer[2]);
			this.buffer[0] = this.lexStream.previous(this.buffer[1]);

			for (k = 3; k < BUFF_UBOUND; k++)
				this.buffer[k] = this.lexStream.next(this.buffer[k - 1]);

			this.buffer[BUFF_UBOUND] = this.lexStream.badtoken();// elmt not available

			this.locationStack[this.nextStackTop] = this.buffer[2];
			this.locationStartStack[this.nextStackTop] = this.lexStream.start(this.buffer[2]);
			last_index = next_last_index;
		}

	    //
	    // Next, try scope recoveries after deletion of one, two, three,
	    // four ... buffer_position tokens from the input stream.
	    //
	    if (repair.code == SECONDARY_CODE || repair.code == DELETION_CODE) {
	        PrimaryRepairInfo scope_repair = new PrimaryRepairInfo();

	        scope_repair.distance = 0;
	        for (scope_repair.bufferPosition = 2;
	             scope_repair.bufferPosition <= repair.bufferPosition &&
	             repair.code != SCOPE_CODE; scope_repair.bufferPosition++) {
	            scope_repair = scopeTrial(this.stack, this.stateStackTop, scope_repair);
	            j = (scope_repair.distance == MAX_DISTANCE
	                                        ? last_index
	                                        : scope_repair.distance);
	            k = scope_repair.bufferPosition - 1;
	            if ((j - k) > MIN_DISTANCE && (j - k) > (repair.distance - repair.numDeletions)) {
	                repair.code = SCOPE_CODE;
	                i = this.scopeIndex[this.scopeStackTop];       // upper bound
	                repair.symbol = Parser.scope_lhs[i] + NT_OFFSET;
	                repair.stackPosition = this.stateStackTop;
	                repair.bufferPosition = scope_repair.bufferPosition;
	            }
	        }
	    }

	    //
	    // If no successful recovery is found and we have reached the
	    // end of the file, check whether or not scope recovery is
	    // applicable at the end of the file after discarding some
	    // states.
	    //
	    if (repair.code == 0 && this.lexStream.kind(this.buffer[last_index]) == EOFT_SYMBOL) {
	        PrimaryRepairInfo scope_repair = new PrimaryRepairInfo();

	        scope_repair.bufferPosition = last_index;
	        scope_repair.distance = 0;
	        for (top = this.stateStackTop;
	             top >= 0 && repair.code == 0; top--)
	        {
	            scope_repair = scopeTrial(this.stack, top, scope_repair);
	            if (scope_repair.distance > 0)
	            {
	                repair.code = SCOPE_CODE;
	                i = this.scopeIndex[this.scopeStackTop];    // upper bound
	                repair.symbol = Parser.scope_lhs[i] + NT_OFFSET;
	                repair.stackPosition = top;
	                repair.bufferPosition = scope_repair.bufferPosition;
	            }
	        }
	    }

		//
		// If a successful repair was not found, quit!  Otherwise, issue
		// diagnosis and adjust configuration...
		//
		if (repair.code == 0)
			return candidate;

		secondaryDiagnosis(repair);

		//
		// Update buffer based on number of elements that are deleted.
		//
		switch(repair.code) {
			case MISPLACED_CODE:
				 candidate.location = this.buffer[2];
				 candidate.symbol = this.lexStream.kind(this.buffer[2]);
				 this.lexStream.reset(this.lexStream.next(this.buffer[2]));

				 break;

			case DELETION_CODE:
				 candidate.location = this.buffer[repair.bufferPosition];
				 candidate.symbol =
						   this.lexStream.kind(this.buffer[repair.bufferPosition]);
				 this.lexStream.reset(this.lexStream.next(this.buffer[repair.bufferPosition]));

				 break;

		default: // SCOPE_CODE || SECONDARY_CODE
				 candidate.symbol = repair.symbol;
				 candidate.location = this.buffer[repair.bufferPosition];
				 this.lexStream.reset(this.buffer[repair.bufferPosition]);

				 break;
		}

		return candidate;
	}


//
//	   This boolean function checks whether or not a given
//	   configuration yields a better misplacement recovery than
//	   the best misplacement recovery computed previously.
//
	private SecondaryRepairInfo misplacementRecovery(int stck[], int stack_top, int last_index, SecondaryRepairInfo repair, boolean stack_flag) {
		int  previous_loc = this.buffer[2];
		int stack_deletions = 0;

		for (int top = stack_top - 1; top >= 0; top--) {
			if (this.locationStack[top] < previous_loc) {
				stack_deletions++;
			}
			previous_loc = this.locationStack[top];

			int j = parseCheck(stck, top, this.lexStream.kind(this.buffer[2]), 3);
			if (j == MAX_DISTANCE) {
				 j = last_index;
			}
			if ((j > MIN_DISTANCE) && (j - stack_deletions) > (repair.distance - repair.numDeletions)) {
				repair.stackPosition = top;
				repair.distance = j;
				repair.numDeletions = stack_deletions;
				repair.recoveryOnNextStack = stack_flag;
			}
		}

		return repair;
	}


//
//	   This boolean function checks whether or not a given
//	   configuration yields a better secondary recovery than the
//	   best misplacement recovery computed previously.
//
	private SecondaryRepairInfo secondaryRecovery(int stck[],int stack_top, int last_index, SecondaryRepairInfo repair, boolean stack_flag) {
		int previous_loc;
		int stack_deletions = 0;

		previous_loc = this.buffer[2];
		for (int top = stack_top; top >= 0 && repair.numDeletions >= stack_deletions; top--) {
			if (this.locationStack[top] < previous_loc) {
				stack_deletions++;
			}
			previous_loc = this.locationStack[top];

			for (int i = 2;
				 i <= (last_index - MIN_DISTANCE + 1) &&
				 (repair.numDeletions >= (stack_deletions + i - 1)); i++) {
				int j = parseCheck(stck, top, this.lexStream.kind(this.buffer[i]), i + 1);

				if (j == MAX_DISTANCE) {
					 j = last_index;
				}
				if ((j - i + 1) > MIN_DISTANCE) {
					int k = stack_deletions + i - 1;
					if ((k < repair.numDeletions) ||
						(j - k) > (repair.distance - repair.numDeletions) ||
						((repair.code == SECONDARY_CODE) && (j - k) == (repair.distance - repair.numDeletions))) {
						repair.code = DELETION_CODE;
						repair.distance = j;
						repair.stackPosition = top;
						repair.bufferPosition = i;
						repair.numDeletions = k;
						repair.recoveryOnNextStack = stack_flag;
					}
				}

				for (int l = Parser.nasi(stck[top]); l >= 0 && Parser.nasr[l] != 0; l++) {
					int symbol = Parser.nasr[l] + NT_OFFSET;
					j = parseCheck(stck, top, symbol, i);
					if (j == MAX_DISTANCE) {
						 j = last_index;
					}
					if ((j - i + 1) > MIN_DISTANCE) {
						int k = stack_deletions + i - 1;
						if (k < repair.numDeletions || (j - k) > (repair.distance - repair.numDeletions)) {
							repair.code = SECONDARY_CODE;
							repair.symbol = symbol;
							repair.distance = j;
							repair.stackPosition = top;
							repair.bufferPosition = i;
							repair.numDeletions = k;
							repair.recoveryOnNextStack = stack_flag;
						}
					}
				}
			}
		}

		return repair;
	}


//
//	   This procedure is invoked to issue a secondary diagnosis and
//	   adjust the input buffer.  The recovery in question is either
//	   an automatic scope recovery, a manual scope recovery, a
//	   secondary substitution or a secondary deletion.
//
	private void secondaryDiagnosis(SecondaryRepairInfo repair) {
		switch(repair.code) {
			case SCOPE_CODE: {
	            if (repair.stackPosition < this.stateStackTop) {
	                reportError(DELETION_CODE,
	                            Parser.terminal_index[ERROR_SYMBOL],
	                            this.locationStack[repair.stackPosition],
	                            this.buffer[1]);
	            }
	            for (int i = 0; i < this.scopeStackTop; i++) {
	                reportError(SCOPE_CODE,
	                            -this.scopeIndex[i],
	                            this.locationStack[this.scopePosition[i]],
	                            this.buffer[1],
	                            Parser.non_terminal_index[Parser.scope_lhs[this.scopeIndex[i]]]);
	            }

	            repair.symbol = Parser.scope_lhs[this.scopeIndex[this.scopeStackTop]] + NT_OFFSET;
	            this.stateStackTop = this.scopePosition[this.scopeStackTop];
	            reportError(SCOPE_CODE,
	                        -this.scopeIndex[this.scopeStackTop],
	                        this.locationStack[this.scopePosition[this.scopeStackTop]],
	                        this.buffer[1],
	                        getNtermIndex(this.stack[this.stateStackTop],
	                                      repair.symbol,
	                                      repair.bufferPosition)
	                       );
	            break;
	        }
			default: {
				reportError(repair.code,
							(repair.code == SECONDARY_CODE
										  ? getNtermIndex(this.stack[repair.stackPosition],
														  repair.symbol,
														  repair.bufferPosition)
										  : Parser.terminal_index[ERROR_SYMBOL]),
							this.locationStack[repair.stackPosition],
							this.buffer[repair.bufferPosition - 1]);
				this.stateStackTop = repair.stackPosition;
			}
		}
	}




//
//	   Try to parse until first_token and all tokens in BUFFER have
//	   been consumed, or an error is encountered. Return the number
//	   of tokens that were expended before the parse blocked.
//
	private int parseCheck(int stck[], int stack_top, int first_token, int buffer_position) {
		int max_pos;
		int indx;
		int ct;
		int act;

		//
		// Initialize pointer for temp_stack and initialize maximum
		// position of state stack that is still useful.
		//
		act = stck[stack_top];
		if (first_token > NT_OFFSET) {
			this.tempStackTop = stack_top;
			if(this.DEBUG_PARSECHECK) {
				System.out.println(this.tempStackTop);
			}
			max_pos = stack_top;
			indx = buffer_position;
			ct = this.lexStream.kind(this.buffer[indx]);
			this.lexStream.reset(this.lexStream.next(this.buffer[indx]));
			int lhs_symbol = first_token - NT_OFFSET;
			act = Parser.ntAction(act, lhs_symbol);
			if (act <= NUM_RULES) {
				// same loop as 'process_non_terminal'
				do {
					this.tempStackTop -= (Parser.rhs[act]-1);

					if(this.DEBUG_PARSECHECK) {
						System.out.print(this.tempStackTop);
						System.out.print(" ("); //$NON-NLS-1$
						System.out.print(-(Parser.rhs[act]-1));
						System.out.print(") [max:"); //$NON-NLS-1$
						System.out.print(max_pos);
						System.out.print("]\tprocess_non_terminal\t"); //$NON-NLS-1$
						System.out.print(act);
						System.out.print("\t"); //$NON-NLS-1$
						System.out.print(Parser.name[Parser.non_terminal_index[Parser.lhs[act]]]);
						System.out.println();
					}

					if(Parser.rules_compliance[act] > this.options.sourceLevel) {
					 	return 0;
					}
					lhs_symbol = Parser.lhs[act];
					act = (this.tempStackTop > max_pos
										  ? this.tempStack[this.tempStackTop]
										  : stck[this.tempStackTop]);
					act = Parser.ntAction(act, lhs_symbol);
				} while(act <= NUM_RULES);

				max_pos = max_pos < this.tempStackTop ? max_pos : this.tempStackTop;
			}
		} else {
			this.tempStackTop = stack_top - 1;

			if(this.DEBUG_PARSECHECK) {
				System.out.println(this.tempStackTop);
			}

			max_pos = this.tempStackTop;
			indx = buffer_position - 1;
			ct = first_token;
			this.lexStream.reset(this.buffer[buffer_position]);
		}

		process_terminal: for (;;) {
			if(this.DEBUG_PARSECHECK) {
				System.out.print(this.tempStackTop + 1);
				System.out.print(" (+1) [max:"); //$NON-NLS-1$
				System.out.print(max_pos);
				System.out.print("]\tprocess_terminal    \t"); //$NON-NLS-1$
				System.out.print(ct);
				System.out.print("\t"); //$NON-NLS-1$
				System.out.print(Parser.name[Parser.terminal_index[ct]]);
				System.out.println();
			}

			if (++this.tempStackTop >= this.stackLength)  // Stack overflow!!!
				return indx;
			this.tempStack[this.tempStackTop] = act;

			act = Parser.tAction(act, ct);

			if (act <= NUM_RULES) {               // reduce action
				this.tempStackTop--;

				if(this.DEBUG_PARSECHECK) {
					System.out.print(this.tempStackTop);
					System.out.print(" (-1) [max:"); //$NON-NLS-1$
					System.out.print(max_pos);
					System.out.print("]\treduce"); //$NON-NLS-1$
					System.out.println();
				}
			} else if (act < ACCEPT_ACTION ||     // shift action
					 act > ERROR_ACTION) {        // shift-reduce action
				if (indx == MAX_DISTANCE)
					return indx;
				indx++;
				ct = this.lexStream.kind(this.buffer[indx]);
				this.lexStream.reset(this.lexStream.next(this.buffer[indx]));
				if (act > ERROR_ACTION) {
					act -= ERROR_ACTION;

					if(this.DEBUG_PARSECHECK) {
						System.out.print(this.tempStackTop);
						System.out.print("\tshift reduce"); //$NON-NLS-1$
						System.out.println();
					}
				} else {
					if(this.DEBUG_PARSECHECK) {
						System.out.println("\tshift"); //$NON-NLS-1$
					}
					continue process_terminal;
				}
			} else if (act == ACCEPT_ACTION) {           // accept action
				 return MAX_DISTANCE;
			} else {
				return indx;                         // error action
			}

			// same loop as first token initialization
			// process_non_terminal:
			do {
				this.tempStackTop -= (Parser.rhs[act]-1);

				if(this.DEBUG_PARSECHECK) {
					System.out.print(this.tempStackTop);
					System.out.print(" ("); //$NON-NLS-1$
					System.out.print(-(Parser.rhs[act]-1));
					System.out.print(") [max:"); //$NON-NLS-1$
					System.out.print(max_pos);
					System.out.print("]\tprocess_non_terminal\t"); //$NON-NLS-1$
					System.out.print(act);
					System.out.print("\t"); //$NON-NLS-1$
					System.out.print(Parser.name[Parser.non_terminal_index[Parser.lhs[act]]]);
					System.out.println();
				}

				if(act <= NUM_RULES) {
					if(Parser.rules_compliance[act] > this.options.sourceLevel) {
					 	return 0;
					}
				}
				int lhs_symbol = Parser.lhs[act];
				act = (this.tempStackTop > max_pos
									  ? this.tempStack[this.tempStackTop]
									  : stck[this.tempStackTop]);
				act = Parser.ntAction(act, lhs_symbol);
			} while(act <= NUM_RULES);

			max_pos = max_pos < this.tempStackTop ? max_pos : this.tempStackTop;
		} // process_terminal;
	}
	private void reportError(int msgCode, int nameIndex, int leftToken, int rightToken) {
		reportError(msgCode, nameIndex, leftToken, rightToken, 0);
	}

	private void reportError(int msgCode, int nameIndex, int leftToken, int rightToken, int scopeNameIndex) {
		int lToken = (leftToken > rightToken ? rightToken : leftToken);

		if (lToken < rightToken) {
			reportSecondaryError(msgCode, nameIndex, lToken, rightToken, scopeNameIndex);
		} else {
			reportPrimaryError(msgCode, nameIndex, rightToken, scopeNameIndex);
		}
	}

	private void reportPrimaryError(int msgCode, int nameIndex, int token, int scopeNameIndex) {
		String name;
		if (nameIndex >= 0) {
			name = Parser.readableName[nameIndex];
		} else {
			name = Util.EMPTY_STRING;
		}

		int errorStart = this.lexStream.start(token);
		int errorEnd = this.lexStream.end(token);
		int currentKind = this.lexStream.kind(token);
		String errorTokenName = Parser.name[Parser.terminal_index[this.lexStream.kind(token)]];
		char[] errorTokenSource = this.lexStream.name(token);
		if (currentKind == TerminalTokens.TokenNameStringLiteral) {
			errorTokenSource = displayEscapeCharacters(errorTokenSource, 1, errorTokenSource.length - 1);
		}

		int addedToken = -1;
		if(this.recoveryScanner != null) {
			if (nameIndex >= 0) {
				addedToken = Parser.reverse_index[nameIndex];
			}
		}
		switch(msgCode) {
			case BEFORE_CODE:
				if(this.recoveryScanner != null) {
					if(addedToken > -1) {
						this.recoveryScanner.insertToken(addedToken, -1, errorStart);
					} else {
						int[] template = getNTermTemplate(-addedToken);
						if(template != null) {
							this.recoveryScanner.insertTokens(template, -1, errorStart);
						}
					}
				}
				if(this.reportProblem) problemReporter().parseErrorInsertBeforeToken(
					errorStart,
					errorEnd,
					currentKind,
					errorTokenSource,
					errorTokenName,
					name);
				 break;
			case INSERTION_CODE:
				if(this.recoveryScanner != null) {
					if(addedToken > -1) {
						this.recoveryScanner.insertToken(addedToken, -1, errorEnd);
					} else {
						int[] template = getNTermTemplate(-addedToken);
						if(template != null) {
							this.recoveryScanner.insertTokens(template, -1, errorEnd);
						}
					}
				}
				if(this.reportProblem) problemReporter().parseErrorInsertAfterToken(
					errorStart,
					errorEnd,
					currentKind,
					errorTokenSource,
					errorTokenName,
					name);
				 break;
			case DELETION_CODE:
				if(this.recoveryScanner != null) {
					this.recoveryScanner.removeTokens(errorStart, errorEnd);
				}
				if(this.reportProblem) problemReporter().parseErrorDeleteToken(
					errorStart,
					errorEnd,
					currentKind,
					errorTokenSource,
					errorTokenName);
				break;
			case INVALID_CODE:
				if (name.length() == 0) {
					if(this.recoveryScanner != null) {
						this.recoveryScanner.removeTokens(errorStart, errorEnd);
					}
					if(this.reportProblem) problemReporter().parseErrorReplaceToken(
						errorStart,
						errorEnd,
						currentKind,
						errorTokenSource,
						errorTokenName,
						name);
				} else {
					if(this.recoveryScanner != null) {
						if(addedToken > -1) {
							this.recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
						} else {
							int[] template = getNTermTemplate(-addedToken);
							if(template != null) {
								this.recoveryScanner.replaceTokens(template, errorStart, errorEnd);
							}
						}
					}
					if(this.reportProblem) problemReporter().parseErrorInvalidToken(
						errorStart,
						errorEnd,
						currentKind,
						errorTokenSource,
						errorTokenName,
						name);
				}
				break;
			case SUBSTITUTION_CODE:
				if(this.recoveryScanner != null) {
					if(addedToken > -1) {
						this.recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
					} else {
						int[] template = getNTermTemplate(-addedToken);
						if(template != null) {
							this.recoveryScanner.replaceTokens(template, errorStart, errorEnd);
						}
					}
				}
				if(this.reportProblem) problemReporter().parseErrorReplaceToken(
					errorStart,
					errorEnd,
					currentKind,
					errorTokenSource,
					errorTokenName,
					name);
				 break;
			case SCOPE_CODE:
				StringBuffer buf = new StringBuffer();

				int[] addedTokens = null;
	            int addedTokenCount = 0;
	            if(this.recoveryScanner != null) {
	            	addedTokens = new int[Parser.scope_rhs.length - Parser.scope_suffix[- nameIndex]];
	            }

	            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;
    	}
	}

	public String toString() {
		StringBuffer res = new StringBuffer();

		res.append(this.lexStream.toString());

		return res.toString();
	}

	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()));
	}
}
