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

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.parser.ParserBasicInformation;
import org.eclipse.jdt.internal.compiler.parser.RecoveryScanner;
import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;

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 RecoveryScanner recoveryScanner;
	
	private boolean reportProblem;
	
	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;
			
		}
	}
	
	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);
		this.recoveryScanner = parser.recoveryScanner;
	}
	
	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(boolean record) {
		this.reportProblem = true;
		boolean oldRecord = false;
		if(this.recoveryScanner != null) {
			oldRecord = this.recoveryScanner.record;
			this.recoveryScanner.record = record;
		}
		try {
			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) {						
						if(this.recoveryScanner == null || !this.recoveryScanner.record) return;
						this.reportProblem = false;
					}
	
					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);
		} finally {
			if(this.recoveryScanner != null) {
				this.recoveryScanner.record = oldRecord;
			}
		}
		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] = ScannerHelper.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] = 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) {
	    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;
	
	    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];
	        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) {
	                        	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) {
	                        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);

		int addedToken = -1;
		if(recoveryScanner != null) {
			if (nameIndex >= 0) {
				addedToken = Parser.reverse_index[nameIndex];
			}
		}
		switch(msgCode) {
			case BEFORE_CODE:
				if(recoveryScanner != null) {
					if(addedToken > -1) {
						recoveryScanner.insertToken(addedToken, -1, errorStart);
					} else {
						int[] template = getNTermTemplate(-addedToken);
						if(template != null) {
							recoveryScanner.insertTokens(template, -1, errorStart);
						}
					}
				}
				if(this.reportProblem) problemReporter().parseErrorInsertBeforeToken(
					errorStart, 
					errorEnd, 
					currentKind,
					errorTokenSource, 
					errorTokenName, 
					name);
				 break;
			case INSERTION_CODE:
				if(recoveryScanner != null) {
					if(addedToken > -1) {
						recoveryScanner.insertToken(addedToken, -1, errorEnd);
					} else {
						int[] template = getNTermTemplate(-addedToken);
						if(template != null) {
							recoveryScanner.insertTokens(template, -1, errorEnd);
						}
					}
				}
				if(this.reportProblem) problemReporter().parseErrorInsertAfterToken(
					errorStart, 
					errorEnd, 
					currentKind,
					errorTokenSource, 
					errorTokenName, 
					name);  
				 break;
			case DELETION_CODE:
				if(recoveryScanner != null) {
					recoveryScanner.removeTokens(errorStart, errorEnd);
				}
				if(this.reportProblem) problemReporter().parseErrorDeleteToken(
					errorStart, 
					errorEnd, 
					currentKind,
					errorTokenSource, 
					errorTokenName);
				break;
			case INVALID_CODE:
				if (name.length() == 0) {
					if(recoveryScanner != null) {
						recoveryScanner.removeTokens(errorStart, errorEnd);
					}
					if(this.reportProblem) problemReporter().parseErrorReplaceToken(
						errorStart, 
						errorEnd, 
						currentKind,
						errorTokenSource, 
						errorTokenName, 
						name);
				} else {
					if(recoveryScanner != null) {
						if(addedToken > -1) {
							recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
						} else {
							int[] template = getNTermTemplate(-addedToken);
							if(template != null) {
								recoveryScanner.replaceTokens(template, errorStart, errorEnd);
							}
						}
					}
					if(this.reportProblem) problemReporter().parseErrorInvalidToken(
						errorStart, 
						errorEnd, 
						currentKind,
						errorTokenSource, 
						errorTokenName, 
						name);
				}
				break;
			case SUBSTITUTION_CODE:
				if(recoveryScanner != null) {
					if(addedToken > -1) {
						recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
					} else {
						int[] template = getNTermTemplate(-addedToken);
						if(template != null) {
							recoveryScanner.replaceTokens(template, errorStart, errorEnd);
						}
					}
				}
				if(this.reportProblem) problemReporter().parseErrorReplaceToken(
					errorStart, 
					errorEnd, 
					currentKind,
					errorTokenSource, 
					errorTokenName, 
					name); 
				 break;
			case SCOPE_CODE:
				StringBuffer buf = new StringBuffer();
				
				int[] addedTokens = null;
	            int addedTokenCount = 0;
	            if(this.recoveryScanner != null) {
	            	addedTokens = new int[Parser.scope_rhs.length - Parser.scope_suffix[- nameIndex]];
	            }
	            
				for (int i = Parser.scope_suffix[- nameIndex]; Parser.scope_rhs[i] != 0; i++) {
					buf.append(Parser.readableName[Parser.scope_rhs[i]]);
					if (Parser.scope_rhs[i + 1] != 0) // any more symbols to print?
						buf.append(' ');
					
					if(addedTokens != null) {
	                	int tmpAddedToken = Parser.reverse_index[Parser.scope_rhs[i]];
		                if (tmpAddedToken > -1) {
		                	int length = addedTokens.length;
		                	if(addedTokenCount == length) {
		                		System.arraycopy(addedTokens, 0, addedTokens = new int[length * 2], 0, length);
		                	}
		                	addedTokens[addedTokenCount++] = tmpAddedToken;
		                } else {
		                	int[] template = getNTermTemplate(-tmpAddedToken);
		                	if(template != null) {
			                	for (int j = 0; j < template.length; j++) {
									int length = addedTokens.length;
		                			if(addedTokenCount == length) {
				                		System.arraycopy(addedTokens, 0, addedTokens = new int[length * 2], 0, length);
				                	}
		                			addedTokens[addedTokenCount++] = template[j];
								}
		                	} else {
			                	addedTokenCount = 0;
			                	addedTokens = null;
		                	}
		                }
	                }
				}

				if(addedTokenCount > 0) {
	            	System.arraycopy(addedTokens, 0, addedTokens = new int[addedTokenCount], 0, addedTokenCount);
	            	
	            	int completedToken = -1;
	            	if(scopeNameIndex != 0) {
	            		completedToken = -Parser.reverse_index[scopeNameIndex];
	            	}
	            	this.recoveryScanner.insertTokens(addedTokens, completedToken, errorEnd);
	            }
				
				if (scopeNameIndex != 0) {
					if(this.reportProblem) problemReporter().parseErrorInsertToComplete(
						errorStart, 
						errorEnd,
						buf.toString(),
						Parser.readableName[scopeNameIndex]);
				} else {
					if(this.reportProblem) problemReporter().parseErrorInsertToCompleteScope(
						errorStart, 
						errorEnd,
						buf.toString()); 
				}
				
				break;
			case EOF_CODE:
				if(this.reportProblem) problemReporter().parseErrorUnexpectedEnd(
					errorStart, 
					errorEnd); 
				break;
			case MERGE_CODE:
				if(recoveryScanner != null) {
					if(addedToken > -1) {
						recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
					} else {
						int[] template = getNTermTemplate(-addedToken);
						if(template != null) {
							recoveryScanner.replaceTokens(template, errorStart, errorEnd);
						}
					}
				}
				if(this.reportProblem) problemReporter().parseErrorMergeTokens(
					errorStart, 
					errorEnd,
					name);
				break;
			case MISPLACED_CODE:
				if(recoveryScanner != null) {
					recoveryScanner.removeTokens(errorStart, errorEnd);
				}
				if(this.reportProblem) problemReporter().parseErrorMisplacedConstruct(
					errorStart, 
					errorEnd);
				break;
			default:
				if (name.length() == 0) {
					if(recoveryScanner != null) {
						recoveryScanner.removeTokens(errorStart, errorEnd);
					}
					if(this.reportProblem) problemReporter().parseErrorNoSuggestion(
						errorStart, 
						errorEnd, 
						currentKind,
						errorTokenSource, 
						errorTokenName);
				} else {
					if(recoveryScanner != null) {
						if(addedToken > -1) {
							recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
						} else {
							int[] template = getNTermTemplate(-addedToken);
							if(template != null) {
								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 = 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);
		
		int addedToken = -1;
		if(recoveryScanner != null) {
			if (nameIndex >= 0) {
				addedToken = Parser.reverse_index[nameIndex];
			}
		}
		
		switch(msgCode) {
			case MISPLACED_CODE:
				if(recoveryScanner != null) {
					recoveryScanner.removeTokens(errorStart, errorEnd);
				}
				if(this.reportProblem) problemReporter().parseErrorMisplacedConstruct(
					errorStart, 
					errorEnd); 
				break;
			case SCOPE_CODE:
				// error start is on the last token start
				errorStart = lexStream.start(rightToken);
			
	            StringBuffer buf = new StringBuffer();
	            
	            int[] addedTokens = null;
	            int addedTokenCount = 0;
	            if(this.recoveryScanner != null) {
	            	addedTokens = new int[Parser.scope_rhs.length - Parser.scope_suffix[- nameIndex]];
	            }
	            
	            for (int i = Parser.scope_suffix[- nameIndex]; Parser.scope_rhs[i] != 0; i++) {
	                
	                buf.append(Parser.readableName[Parser.scope_rhs[i]]);
	                if (Parser.scope_rhs[i+1] != 0)
	                     buf.append(' ');
	                
	                if(addedTokens != null) {
	                	int tmpAddedToken = Parser.reverse_index[Parser.scope_rhs[i]];
		                if (tmpAddedToken > -1) {
		                	int length = addedTokens.length;
		                	if(addedTokenCount == length) {
		                		System.arraycopy(addedTokens, 0, addedTokens = new int[length * 2], 0, length);
		                	}
		                	addedTokens[addedTokenCount++] = tmpAddedToken;
		                } else {
		                	int[] template = getNTermTemplate(-tmpAddedToken);
		                	if(template != null) {
			                	for (int j = 0; j < template.length; j++) {
									int length = addedTokens.length;
		                			if(addedTokenCount == length) {
				                		System.arraycopy(addedTokens, 0, addedTokens = new int[length * 2], 0, length);
				                	}
		                			addedTokens[addedTokenCount++] = template[j];
								}
		                	} else {
			                	addedTokenCount = 0;
			                	addedTokens = null;
		                	}
		                }
	                }
	            }
	            if(addedTokenCount > 0) {
	            	System.arraycopy(addedTokens, 0, addedTokens = new int[addedTokenCount], 0, addedTokenCount);
	            	int completedToken = -1;
	            	if(scopeNameIndex != 0) {
	            		completedToken = -Parser.reverse_index[scopeNameIndex];
	            	}
	            	this.recoveryScanner.insertTokens(addedTokens, completedToken, errorEnd);
	            }
	            if (scopeNameIndex != 0) {
	                if(this.reportProblem) problemReporter().parseErrorInsertToComplete(
						errorStart, 
						errorEnd,
						buf.toString(),
						Parser.readableName[scopeNameIndex]);
	            } else {
	            	if(this.reportProblem) problemReporter().parseErrorInsertToCompletePhrase(
						errorStart, 
						errorEnd,
						buf.toString()); 
	            }
	            break;
			case MERGE_CODE:
				if(recoveryScanner != null) {
					if(addedToken > -1) {
						recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
					} else {
						int[] template = getNTermTemplate(-addedToken);
						if(template != null) {
							recoveryScanner.replaceTokens(template, errorStart, errorEnd);
						}
					}
				}
				if(this.reportProblem) problemReporter().parseErrorMergeTokens(
					errorStart, 
					errorEnd,
					name);
				break;
			case DELETION_CODE:
				if(recoveryScanner != null) {
					recoveryScanner.removeTokens(errorStart, errorEnd);
				}
				if(this.reportProblem) problemReporter().parseErrorDeleteTokens(
					errorStart, 
					errorEnd);
				break;
			default:
				if (name.length() == 0) {
					if(recoveryScanner != null) {
						recoveryScanner.removeTokens(errorStart, errorEnd);
					}
					if(this.reportProblem) problemReporter().parseErrorNoSuggestionForTokens(
						errorStart, 
						errorEnd);
				} else {
					if(recoveryScanner != null) {
						if(addedToken > -1) {
							recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd);
						} else {
							int[] template = getNTermTemplate(-addedToken);
							if(template != null) {
								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(lexStream.toString());
		
		return res.toString();
	}
}
