ftp.nice.ch/pub/next/developer/languages/ada/Adaed.1.11.s.tar.gz#/Adaed-1.11.0a/prserr.c

This is prserr.c in view mode; [Download] [Up]

/*
 * Copyright (C) 1985-1992  New York University
 * 
 * This file is part of the Ada/Ed-C system.  See the Ada/Ed README file for
 * warranty (none) and distribution info and also the GNU General Public
 * License for more details.

 */
/********************************************************************
 *								    
 *			  *****************			   
 *			  *  P R S E R R  
 *			  *****************			 
 *								
 *			     Written by			       
 *			   Brian Siritzky		      
 *				1983			     
 *							    
 ********************************************************************/

/********************************************************************/

/********************************************************************/
#include "ada.h"
#include "adalexprots.h"
#include "actionprots.h"
#include "pspansprots.h"
#include "errsprots.h"
#include "lookupprots.h"
#include "prsutilprots.h"
#include "recoverprots.h"
#include "makecorrprots.h"
#include "prserrprots.h"

static void get_poss_tok();
static void get_next(int);

/*	
 * Variables needed for scope and secondary recoveries
 */

struct two_pool *closer_toksyms ;
struct two_pool *closer_bottom ;

struct two_pool *POSS_TOK;
/* struct prsstack **tokens; deleted on Sam's suggestion 12-11-84 */
int	TOKSYMS[S_TOKSYMS], n_TOKSYMS;
int	BACKUPTOKS = 0;
int	MAX_CHECK = MIN_LEN ;

PCAND next_c, y, next_cand, CANDIDATES[C_BOUND];
static PCAND tmp_cand,temp_cand;
int	n_CANDIDATES[C_BOUND] ;
int nps;
int n_open_seq;
int *open_seq;
int n_closer_toksyms;

void prserr(struct prsstack *curtok)							/*;prserr*/
{

	/********************************************************************
	 *
	 * This routine is the syntactic error	recovery  routine.   It	 at-
	 * tempts  to  correct	simple errors and when that is not possible,
	 * deletes tokens possibly to the left and to the right of the error
	 * point until the parse can safely resume.  The process is thus di-
	 * vided naturally into two  parts,  called  primary  and  secondary
	 * recovery.  Both primary and secondary recovery include efforts at
	 * "scope recovery": i.e.  the	closing	 of  lexically	open  scopes
	 * through the insertion of one or more closer token sequences.	 Ex-
	 * amples of such sequences are right parentheses, "END ;", and "END
	 * IF;".
	 * 
	 * Primary recovery consists of the simple corrections - merging to-
	 * kens,  substituting	a  token  (including a reserved word that is
	 * misspelled as an identifier), inserting a token, and	 deleting  a
	 * token - along with scope recovery.
	 * 
	 * An attempt at simple correction at either the error token  or  at
	 * some parse stack element is called a trial.
	 * 
	 * For the first trial simple corrections are attempted at the token
	 * at which the error was detected (the error token), with the parse
	 * in the configuration obtaining just after  the  shifting  of	 the
	 * previous  token. In order to back up to the succeeding trial, the
	 * top elements are peeled from the state and parse stacks, with the
	 * top element of the parse stack appended to the front of the input
	 * token sequence. Again attempts at simple correction are made. The
	 * process  is	repeated  until	 the determined extent of the backup
	 * move has been accomplished.
	 * 
	 * The criterion by which the effectiveness of a  simple  correction
	 * candidate is measured is the distance it allows the parse to pro-
	 * gress into the forward context (up to  MAX_LOOK_AHEAD  =  25	 to-
	 * kens).   During  the	 simple correction trials we gather together
	 * the CANDIDATES that allow the parse	to  progress  the  furthest,
	 * provided  that  an  advance of at least MIN_CHECK is accomplished
	 * (if not, then CANDIDATES is empty and simple	 correction  fails).
	 * If  CANDIDATES is not empty, it is pruned in accordance with cer-
	 * tain restrictions described below, and then one of them is chosen
	 * as the appropriate correction provided that a condition described
	 * below is satisfied.
	 * 
	 * No attempt is made to delete or substitute for a nonterminal.
	 * 
	 * If no simple correction is chosen, scope recovery is attempted at
	 * each	 point	at  which  simple  recovery was attempted. The scope
	 * recovery procedure determines whether the insertion of a sequence
	 * of  scope  closers  allows the parse to progress MIN_LEN distance
	 * into the forward context.  If  so,  this  multiple  insertion  is
	 * chosen as the correction candidate.
	 * 
	 * If scope recovery also fails, then secondary recovery is invoked.
	 * The	parse  is  restored  to the configuration obtaining upon en-
	 * trance to PRSERR, and each parse stack element is tested - in se-
	 * quence from the top - to see whether parsing can resume from that
	 * point, perhaps with the inclusion of one or more closer token se-
	 * quences.  The  extent  of  the  advance required in order for the
	 * parse to be regarded as successfully	 resumed  depends  upon	 the
	 * current token, but is at least MIN_LEN.
	 * 
	 * If secondary recovery at the current token does not	succeed,  it
	 * is  ignored	and  the next one obtained and the same check is re-
	 * peated. Eventually, there must be an input for  which  the  parse
	 * can	continue, because the end of file token, EOFT, is compatible
	 * with a state on the state stack.
	 * 
	 * When the recovery point is found,  control  is  returned  to	 the
	 * parser.  It is assured that parsing can continue beyond the error
	 * token.
	 * 
	 ********************************************************************/

	/* PARSE STACK and BINARY TUPLE STRUCTURES */
	typedef struct two_pool *TUPLE;


	struct prsstack *ERROR_TOKEN;
	struct prsstack *oldprevtok;/* Saved for scope and secondary recovery */
	struct prsstack *tmp_ps;

	TUPLE STATE_STACK = NULL;
	TUPLE prs_toks = NULL;	/* Used to determine the no of */
	TUPLE tmp_tp;			/* trials to be performed */
	/* Used for freeing storage */
	TUPLE prs_stack_syms = NULL;

	int pb;			/* Used as a flag to check for a parse block */
	int threshold,		/* Used to prune candidates */
	exists;		/* Flag used in list searching */

	short trials,		/* Number of trials performed	*/
	  j, r, trial,
	  i,			/* Loop and auxilliary		*/
	  bk, cc, x, si ;

	int n_PARSE_STACK;
	int save_n_TOKSYMS ;
	struct two_pool *states;

	int	    n_single_cand_modes, total_CANDIDATES;
	int	    n_STATE_STACK, n_sta_stack;

	ERROR_TOKEN = copytoken (curtok);
	MAX_CHECK = MIN_LEN;

	/* ERR_MSGS = NULL ; CLOSER_TOKSYMS = NULL ; */

	copystack (sta_stack, &STATE_STACK, &n_sta_stack);
	/* save for scope and secondary recovery */
	if (PREVTOK != NULL)
		oldprevtok = copytoken (PREVTOK);

	n_STATE_STACK = n_sta_stack;

	BACKUPTOKS = 0;

	get_next (MAX_LOOK_AHEAD);

	/* prs_stack_syms := [r(1) : r in PRS_STACK];		    
	 * Loop over PRS_STACK, collecting the symbols in a list  
	 * headed by prs_stack_syms. While doing this, count up the 
	 * number of elements in the parse stack, keeping this in  
	 * n_PARSE_STACK and nps				   
	 */

	n_PARSE_STACK = 0;

	if ((tmp_ps= prs_stack) == NULL)/* Check for empty stacks */
		prs_stack_syms = NULL;
	else {				/* otherwise copy the list */
		/* Treat the first element as a special case */
		tmp_tp = prs_stack_syms = TALLOC ();
		tmp_tp -> link = NULL;
		tmp_tp -> val.state = tmp_ps -> symbol;
		n_PARSE_STACK = 1;
		/* Loop over the rest of the stack */
		while (tmp_ps -> prev != NULL) {
			tmp_tp -> link = TALLOC ();
			tmp_tp = tmp_tp -> link;
			tmp_ps = tmp_ps -> prev;
			tmp_tp -> val.state = tmp_ps -> symbol;
			n_PARSE_STACK++;
		}			/* while */
		tmp_tp -> link = NULL;
	}				/* else */
	nps = n_PARSE_STACK;

	/* * TOKSYMS := [t(1) : t in TOKENS];			 
	 * Loop over TOKENS, collecting the symbols in a list	
	 * The integer tokind is a count of the number of     
	 * elements in the array tokens.		      
	 *
	 * Note that tokens[tokind] is the next token, so we must
	 * reverse the order of TOKSYMS							
	 */

	/* Put the current token at the top of the token stack */
	tokens[tokind = (tokind + 1) % toksiz] = curtok;
	i = 0;
	j = tokbottom;

	for (;;) {
		TOKSYMS[i] = tokens[j]->symbol;
		if (j == tokind)
			break;
		j = (j + 1) % toksiz;
		i++;
	}

	save_n_TOKSYMS	= n_TOKSYMS = (toksiz + tokind - tokbottom) % toksiz;
	/*
    for (i = 0; i <= tokind; i++)
	TOKSYMS[i] = tokens[i] -> symbol;

    n_TOKSYMS = tokind;
	*/
	dump_toksyms ("At creation");

	/******************************************************************* 
	 *
	 *					S I M P L E    R E C O V E R Y		    
	 *	       
	 *******************************************************************/

	/*	Simple Recovery begins here	 */

	/* Determine number of trials to be performed.  */
	prs_toks = TALLOC ();
	prs_toks -> val.state = curtok -> symbol;
	prs_toks -> link = NULL;

	/* trials = (nps>0) ? nps : 1 ; */
	trials = (n_sta_stack>0) ? n_sta_stack : 1 ;

	states = TALLOC ();
	states -> link = NULL;

	/* for (j = nps; j >= 1; j--) */
	for (j = n_sta_stack; j >= 2; j--) {
		int	jj;
		/* prs_stack_syms(j) is at position (nps - j)  in the linked list.
		 * For this reason we need to follow the links this many times
		 */
		/* jj = nps - j + 1; */
		jj = n_sta_stack - j + 1;
		tmp_tp = prs_stack_syms;
		while (jj-- > 1)
			tmp_tp = tmp_tp -> link;

		r = tmp_tp -> val.state;	/* r := prs_stack_syms(j) */

#ifdef DEBUG
		if (trcopt) {
			fprintf(errfile,"j = %d	 n_sta_stack = %d  r = %d\n",
			  j,n_sta_stack,r);
		}
#endif
		/* dump_stack(prs_toks); */

		/* Check whether it is possible to parse past symbol
		 *	and through the error token.			
		 *
		 *	if forall s in SHIFT_STATES(r) | prs_block(s, prs_toks)
		 *	then
		 *	    trials := nps - j + (if is_terminal(r) then 2 else 1 end);
		 *	    quit;
		 *	end if;
		 */
		pb = TRUE;		/* Assume that the parse will block */
		/* Loop through SHIFT_STATES(r). This is the array SHIFT_STATES from
		 * position SHIFT_STATE_INDEX(r-1) to SHIFT_STATE_INDEX(r) - 1
		 * Each time check if the parse blocks, pb. If it does not then end the
		 * testing.
		 */
		for (si = SHIFT_STATE_INDEX[r - 1];
		  ((si <= (SHIFT_STATE_INDEX[r] - 1)) && pb);
		  si++)
		{
			states -> val.state = SHIFT_STATE[si];
			pb = pb && prs_block (states, prs_toks);

#ifdef DEBUG
			if (trcopt)
				fprintf(errfile,"state: %d	 prsblock: %d\n",
				  states->val.state, prs_block(states,prs_toks) );
#endif
		}

		if (pb) {			/*  If the parse blocks then terminate	*/
			/*trials = 1 + nps - j + (is_terminal (r - 1) ? 2 : 1);*/
			trials = 1 + n_sta_stack - j + (is_terminal (r) ? 2 : 1);
			break;		/* Outer loop */
		}

		tmp_tp = TALLOC ();
		tmp_tp -> link = prs_toks;
		tmp_tp -> val.state = r;
		prs_toks = tmp_tp;

	}				/* end for */

	if (nps == 0)
		trials = 1;	/* To deal with empty parse stacks */
#ifdef DEBUG
	if (trcopt)
		fprintf(errfile,"trials = %d\n",trials);
#endif

	for (trial = 1; trial <= trials; trial++) {
		/*	Attempt simple recovery at token backed up to.	 */
#ifdef DEBUG
		if (trcopt) {
			fprintf(errfile,"Backup Recovery, trial number is: %d\n",trial);
			fprintf(errfile,"STATE STACK:\n");
			dump_stack(sta_stack);
			fprintf(errfile,"PARSE STACK:\n");
			dump_prssyms(prs_stack_syms);
			fprintf(errfile,"TOKENS:\n");
			dump_toksyms("");
		}
#endif

		/* Form set of candidates for insertion and substitution. */

		get_poss_tok ();

		/* Make insertion test.   */

		try_insertion ();

		/* Attempt substitution for current token */

		try_substitution ();

		/* Try merging tokens. */

		if (trial == 1)
			try_merge (curtok, nexttok);
		else
			if (trial == 2)
				try_merge (PREVTOK, curtok);

		/* Check whether parsing can resume with the next token. */

		try_deletion ();

		/* Now we peel off the top state and try again.		   
		 * Put the "token" (terminal or nonterminal) for previous state on 
		 * the list of tokens, and increase backuptoks accordingly.	   
		 */

		tmp_tp = sta_stack;
		sta_stack = sta_stack -> link;
		n_sta_stack--;

		tmp_tp = prs_stack_syms;
		if (prs_stack != NULL) {
			TOKSYMS[++n_TOKSYMS] = prs_stack_syms -> val.state;
			prs_stack_syms = prs_stack_syms -> link;
		}
		BACKUPTOKS++;

	}				/* for */


	/* Form misspelling candidates by applying string distance test in cases
	 *   where a reserved word to be substituted for an identifier.	 
	 */

	if (0 && CANDIDATES[SUBST] != NULL) {
#ifdef DEBUG
		if (trcopt)
			fprintf (errfile, "CANDIDATES[SUBST] != NULL, (#=%d)\n",
			    n_CANDIDATES[SUBST]);
#endif
		/*  spell_substs := {[u, v, w] in CANDIDATES(SUBST) |
		 *	     is_reserved(u) and v = ID_SYM 
		 *	    and spell(namelist(u), namelist(
		 *	     (w = 0 ? curtok : PRS_STACK(nps - w + 1) )(2)))};
		 *
		 *   Note : u == token_sym, v == backup_toks, w == t3
		 *
		 *   Loop over the list headed by CANDIDATES[SUBST], selecting
		 *  all those elements which satisfy the above conditions
		 *
		 *	The spell function used here is a less accurate measure than the
		 *  SETL version. It returns an integer value in the range [0,3],
		 *  where 0, 1 & 2 imply misspellings.
		 */
		nps = stack_count(prs_stack) ;
		next_c = CANDIDATES[SUBST];
		while (next_c != NULL) {
			short   symb;	/* Utility variable */
			PCAND	follow_c ;

			follow_c = next_c-> next ;

			/* Calculate the word to be compared to according to
			 *		symb = (w==0)?curtok : prs_stack(nps -w + 1)
			 */

			if (next_c -> backup_toks == 0)
				symb = curtok -> symbol;
			else {			/* PRSSTACK[nps-w-1].symbol */
				int kk = nps - next_c->backup_toks ;

				tmp_ps = prs_stack;
				while(--kk)
					tmp_ps = tmp_ps -> prev;
				symb = tmp_ps -> symbol;
			}

			if ( (is_reserved (next_c -> token_sym))
			  && (next_c -> t3 == ID_SYM)
			  && ((spell (TOKSTR (next_c -> token_sym), TOKSTR (symb))) < 3 ) )
			{
				/* Add it to the list of SPELL_SUBSTs. */

				next_c-> next = CANDIDATES[SPELL_SUBST] ;
				CANDIDATES[SPELL_SUBST] = next_c ;
				n_CANDIDATES[SPELL_SUBST]++;
				n_CANDIDATES[SUBST]--;
			}

			next_c = follow_c ;
		}

	}
#ifdef DEBUG
	if (trcopt) {
		fprintf(errfile,"Candidates BEFORE pruning\n");
		dump_cand();
	}
#endif

	/* The correction candidates now include only those that have checked
	 * the furthest distance into the forward context.
	 * Prune this set by applying the preferences and restrictions relevant 
	 * in each case.
	 */

	/* If a long parse check has been achieved, set threshold to true.  */

	threshold = (MAX_CHECK >= (MIN_LEN + 3));

	/*  (for y = CANDIDATES(x)) */

	for (x = DELETE; x <= SPELL_SUBST; x++)
	{
		y = CANDIDATES[x];

		if (y == NULL)		/* Check for null candidate list */
			continue;

		switch (x) {

		case DELETE:

			/* Remove all reserved word deletions if another deletion exists
	    	 * and all such deletions if below the threshold		    
	    	 */

			next_cand = CANDIDATES[DELETE];
			exists = FALSE;
			while ((next_cand != NULL) && (!exists)) {
				exists = exists && (next_cand -> token_sym > RESERVED_SYMS);
				next_cand = next_cand -> next;
			}

			if ((!threshold) || exists) {
				/*  y -:= {[token_sym,-,-] in y | is_reserved(token_sym) }
				 *  This is achieved by looping over the list headed by y
				 *  and deleting those elements which satisfy the condition 
				 */
				next_cand = CANDIDATES[x];
				n_CANDIDATES[x] = 0;
				CANDIDATES[x] = NULL;
				while (next_cand != NULL) {
					/* there are more candidates */
					tmp_cand = next_cand -> next;
					if (!is_reserved (next_cand -> token_sym)) {
						/* Add it to the front of the list  */
						n_CANDIDATES[x]++;
						next_cand -> next = CANDIDATES[x];
						CANDIDATES[x] = next_cand;
					}
					next_cand = tmp_cand;
				}
			}

			/* Prefer deletion closest to error token.	*/

			if (y != NULL) {
				/* bk := min/{t(2) : t in y}; */
				next_cand = y;
				bk = next_cand -> backup_toks;
				next_cand = next_cand -> next;
				while (next_cand != NULL) {
					bk = MIN (bk, next_cand -> backup_toks);
					next_cand = next_cand -> next;
				}

				n_CANDIDATES[x] = 0;
				next_cand = CANDIDATES[x];
				CANDIDATES[x] = NULL;
				while (next_cand != NULL) {
					/* there are more candidates */
					tmp_cand = next_cand -> next;
					if (next_cand -> backup_toks == bk) {
						next_cand -> next = CANDIDATES[x];
						CANDIDATES[x] = next_cand;
						n_CANDIDATES[x]++;
					}

					next_cand = tmp_cand;
				}
			}
			break;

		case INSERT:

			/* Remove all insertions of reserved words if below threshold */

			if (!threshold) {
				/* y -:= {[token_sym,-,-] in y | is_reserved(token_sym) }     
				 * This is achieved by looping over the list headed by y   
				 * and deleting those elements which satisfy the condition 
				 */
				n_CANDIDATES[x] = 0;
				next_cand = CANDIDATES[x];
				CANDIDATES[x] = NULL;
				while (next_cand != NULL) {
					/* there are more candidates */
					tmp_cand = next_cand -> next;
					if (!is_reserved (next_cand -> token_sym)) {
						next_cand -> next = CANDIDATES[x];
						CANDIDATES[x] = next_cand;
						n_CANDIDATES[x]++;
					}
					next_cand = tmp_cand;
				}
			}

			/* Prefer insertions closest to error token. */

			if (CANDIDATES[x] != NULL) {
				/* bk := min/{t(3) : t in y}; */
				next_cand = y;
				bk = next_cand -> backup_toks;
				next_cand = next_cand -> next;
				while (next_cand != NULL) {
					bk = MIN (bk, next_cand -> backup_toks);
					next_cand = next_cand -> next;
				}
				/* y := {[-,backup_toks,-] in y | backup_toks = bk) }	   
				 *
				 * This is achieved by looping over the list headed by y   
				 * and deleting those elements which satisfy the condition
				 * While doing this, check if there are any elements  that 
				 * satisfy the condition		    
				 *		(token_sym in ALWAYS_PREFERRED_SYMS) 
				 * If there are any they can be used to further prune the  
				 * list. A flag 'exists' will be used for this purpose.
				 */

				n_CANDIDATES[x] = 0;
				next_cand = CANDIDATES[x];
				CANDIDATES[x] = NULL;
				exists = FALSE;
				/* Assume none in ALWAYS_PREFERRED_SYMS */
				while (next_cand != NULL) {
					/* there are more candidates */
					tmp_cand = next_cand -> next;
					if (next_cand -> backup_toks == bk) {
						exists = exists
						  || in_ALWAYS_PREFERRED_SYMS (next_cand -> token_sym);
						next_cand -> next = CANDIDATES[x];
						CANDIDATES[x] = next_cand;
						n_CANDIDATES[x]++;
					}
					next_cand = tmp_cand;
				}
			}

			/* 	Apply preferred insertions (if any exist)
	    	 *
	    	 *	pi := {[u, v, w] in y | u in ALWAYS_PREFERRED_SYMS};
	    	 *	if (pi != {}) y = pi;
	    	 */

			if (exists) {		/* then prune the list accordingly */
				/* y := {[token_sym,-,-] in y | token_sym in		      
				 *	    ALWAYS_PREFERRED_SYMS }			   
				 * This is achieved by looping over the list headed by y    
				 * and deleting those elements which satisfy the condition  
				 */

				n_CANDIDATES[x] = 0;
				next_cand = CANDIDATES[x];
				CANDIDATES[x] = NULL;
				while (next_cand != NULL) {
					/* there are more candidates */
					tmp_cand = next_cand -> next;
					if (in_ALWAYS_PREFERRED_SYMS (next_cand -> token_sym)) {
						next_cand -> next = CANDIDATES[x];
						CANDIDATES[x] = next_cand;
						n_CANDIDATES[x]++;
					}
					next_cand = tmp_cand;
				}
			}
			break;

		case SUBST:

			/* 		Apply preferred substitutions 
	    	 * 
	    	 *	Check to see whether there are any elements which satisfy   
	    	 *	either of the conditions	    
	    	 *			token_sym in PREFERRED_FOR_SYMS{v}
	    	 *		   or	token_sym = ID_SYM and is_reserved(v) 
	    	 *	If at least one is found then set y to be ps, where ps is   
	    	 *
	    	 *	    ps := {[u, v, w] in y | u in PREFERRED_FOR_SYMS{v}	
	    	 *				or (u = ID_SYM and is_reserved(v))};	    
	    	 *
	    	 *	This is achieved by looping over the list headed by y	
	    	 *	and deleting those elements which satisfy the condition
	    	 */

			/* check for existence */

			next_cand = CANDIDATES[x];
			exists = FALSE; /* Assume none */
			while ((next_cand != NULL) && (!exists)) {
				/* there are more candidates */
				exists = exists
				  || in_PREFERRED_FOR_SYMS( next_cand -> t3,
				   next_cand -> token_sym)
				  || ((next_cand -> token_sym == ID_SYM)
				  && (is_reserved (next_cand -> t3)));
				next_cand = next_cand -> next;
			}

			/* If some exist then y is set to be those only */

			if (exists) {
				n_CANDIDATES[x] = 0;
				next_cand = CANDIDATES[x];
				CANDIDATES[x] = NULL;
				while (next_cand != NULL) {
					/* there are more candidates */
					temp_cand = next_cand -> next;
					if ( in_PREFERRED_FOR_SYMS( next_cand -> t3,
					  next_cand -> token_sym )
					  || ((next_cand -> token_sym == ID_SYM)
					  && is_reserved (next_cand -> t3)))
					{
						next_cand -> next = CANDIDATES[x];
						CANDIDATES[x] = next_cand;
						n_CANDIDATES[x]++;
					}
					next_cand = temp_cand;
				}
			}
			else if (!threshold) {		/* None exist */
				/* Remove all substitutions involving reserved words if below	
				 * the threshold.						
				 */
				/* y -:= {[u, v, w] in y |	
		   	 	 *			 is_reserved(u) or is_reserved(v)};  
		    	 */

				n_CANDIDATES[x] = 0;
				next_cand = CANDIDATES[x];
				CANDIDATES[x] = NULL;
				while (next_cand != NULL) {
					temp_cand = next_cand -> next;
					if (!((is_reserved (next_cand -> t3))
					  || (is_reserved (next_cand -> token_sym))) )
					{
						next_cand -> next = CANDIDATES[x];
						CANDIDATES[x] = next_cand;
						n_CANDIDATES[x]++;
					}
					next_cand = temp_cand;
				}
			}

			/* Prefer substitutions closest to error token. */

			if (CANDIDATES[x] != NULL) {
				/* bk := min/{t(3) : t in y}; */
				next_cand = y;
				bk = next_cand -> backup_toks;
				next_cand = next_cand -> next;
				while (next_cand != NULL) {
					bk = MIN (bk, next_cand -> backup_toks);
					next_cand = next_cand -> next;
				}

				n_CANDIDATES[x] = 0;
				next_cand = CANDIDATES[x];
				CANDIDATES[x] = NULL;
				while (next_cand != NULL) {
					/* there are more candidates */
					temp_cand = next_cand -> next;
					if (next_cand -> backup_toks == bk) {
						next_cand -> next = CANDIDATES[x];
						CANDIDATES[x] = next_cand;
						n_CANDIDATES[x]++;
					}
					next_cand = temp_cand;
				}
			}
			break;

		case SPELL_SUBST:

			/* Prefer closest to error token. */

			/* bk := min/{t(3) : t in y}; */

			next_cand = y;
			bk = next_cand -> backup_toks;
			next_cand = next_cand -> next;
			while (next_cand != NULL) {
				bk = MIN (bk, next_cand -> backup_toks);
				next_cand = next_cand -> next;
			}

			/* 		y := {[-,-,t3] in y | t3=bk }			
	    	 * This is achieved by looping over the list headed by y   
	    	 * and deleting those elements which satisfy the condition 
	    	 */

			if (CANDIDATES[x] != NULL) {
				n_CANDIDATES[x] = 0;
				next_cand = CANDIDATES[x];
				CANDIDATES[x] = NULL;
				while (next_cand != NULL) {
					/* there are more candidates */
					temp_cand = next_cand -> next;
					if (next_cand -> backup_toks == bk) {
						next_cand -> next = CANDIDATES[x];
						CANDIDATES[x] = next_cand;
						n_CANDIDATES[x]--;
					}
					next_cand = temp_cand;
				}
			}
			break;

		case MERGE:

			break;

		}			/* switch */

	}				/* for */
#ifdef DEBUG
	if (trcopt) {
		fprintf(errfile,"Candidates AFTER pruning:\n");
		dump_cand();
	}
#endif


	/* For each mode - merge, spelling, insertion, substitution, deletion -
	 * there are zero or more correction candidates.  
	 * If one or mode has exactly one correction candidate, then one of those
	 * candidates is chosen.
	 * 
	 * If no mode has a single candidate, then a simple correction candidate 
	 * is only chosen if the long check distance has been achieved 
	 *		    (MAX_CHECK >= MIN_LEN + 3).
	 * 
	 * The preference order among the correction modes is : merge, spelling,
	 * insertion, deletion, substitution.
	 */

	/* Calculate the number of single_cand_modes	 */
	/* and the total number of CANDIDATES		 */

	n_single_cand_modes = 0;
	total_CANDIDATES = 0;

	for (cc = DELETE; cc <= SPELL_SUBST; cc++) {
		if (n_CANDIDATES[cc] == 1)
			n_single_cand_modes++;
		total_CANDIDATES += n_CANDIDATES[cc];
	}

	if (n_single_cand_modes > 0) {
		/* Apply one final preference rule where there remains a single 
    	 *	deletion and a single insertion candidate - prefer deletion of 
    	 *	operator or punctuation symbol to insertion of such a symbol.  
    	 */
		if ((n_CANDIDATES[DELETE] == 1)
		  && ((n_CANDIDATES[INSERT]) == 1)
		  && is_operator (CANDIDATES[DELETE] -> token_sym)
		  && is_operator (CANDIDATES[INSERT] -> token_sym))
		{
			/* Set CANDIDATES[INSERT] to NULL and dispose of the list */
			CANDIDATES[INSERT] = NULL;
			n_CANDIDATES[INSERT] = 0;
			n_single_cand_modes--;
			total_CANDIDATES--;
		}

		/* Set all CANDIDATES which have more than 1 element to NULL	
    	 * and dispose of their lists						
    	 */

		for (cc = DELETE; cc <= SPELL_SUBST; cc++)
			if (n_CANDIDATES[cc] > 1) {
				CANDIDATES[cc] = NULL;
				n_CANDIDATES[cc] = 0;
			}

	}				/* if */

	if ((n_single_cand_modes > 0) || ((total_CANDIDATES > 0) && threshold)) {
		make_correction (STATE_STACK);
		return;
	}

	/*  Primary recovery has failed if we reach this point. 
	 *  Reset parse configuration for scope recovery.	
	 */

	copystack (STATE_STACK, &sta_stack, &n_STATE_STACK);
	/*  TOKSYMS(n_TOKENS + 1 .. ) := []; */
	n_TOKSYMS = save_n_TOKSYMS ;
	BACKUPTOKS = 0;

	/************************************************************************
	 *								      
	 *	    E N D     O F     P R I M A R Y    R E C O V E R Y	       
	 *									
	************************************************************************/

	/************************************************************************
	 *								      
	 *					Scope recovery 
	 *									
	************************************************************************/

	/* SCOPE */
	{

		int	 j;
		struct prsstack *prstmp;
		struct two_pool *tuptmp;
		struct two_pool *statmp;
		struct tok_loc *prev_tok_loc;
		/* struct tok_loc *prev_tok_loc = &PREVTOK->ptr.token->loc; */

		if (PREVTOK != NULL)
			prev_tok_loc = &PREVTOK->ptr.token->loc;

		/*	Allocate an array the size of the parse stack */
		open_seq = (int *)malloc((unsigned) (nps * sizeof(int)));

#ifdef DEBUG
		if (trcopt)
			fprintf(errfile,"SCOPE OPENERS = \n");
#endif
		for (n_open_seq = 0,j = nps, prstmp = prs_stack; prstmp != NULL;
		  prstmp = prstmp->prev, j--) {
			if (is_opener(prstmp->symbol)) {
				open_seq[n_open_seq++] = j;
#ifdef DEBUG
				if (trcopt)
					fprintf(errfile,"[ %s %d ] ",TOKSTR(prstmp->symbol),j);
#endif
			}
		}
#ifdef DEBUG
		if (trcopt)
			fprintf(errfile,"\n");
#endif

		/* for debugging purposes try one less trial */
		trials-- ;


		closer_toksyms = NULL;
		closer_bottom = NULL;

		for (trial = 1 ; trial <= trials ; trial ++ ) {
#ifdef DEBUG
			if (trcopt)
				fprintf(errfile,"Scope Recovery Trial = %d\n",trial);
#endif
			if (scope_recovery(n_STATE_STACK,0,open_seq)) {
				for (tuptmp = closer_toksyms; tuptmp != NULL;
				  tuptmp = tuptmp->link)
				{
					prstmp = PRSALLOC();
					prstmp->ptr.token = TOKALLOC();
					prstmp->symbol =prstmp->ptr.token->index =tuptmp->val.state;
					prstmp->ptr.token->loc.line = prev_tok_loc->line;
					prstmp->ptr.token->loc.col = prev_tok_loc->col;
					ADDTOTOP(prstmp);
				}
				/* if (closer_toksyms != NULL)
				 * TFREE(closer_toksyms,closer_bottom);
				 */
				n_closer_toksyms = 0 ;
				return;
			}
			/* if (closer_toksyms != NULL)
			 *    TFREE(closer_toksyms,closer_bottom);
			 */
			n_closer_toksyms = 0 ;
			/*	Back up for the next trial */

			if (trial == trials)
				break;

			statmp = sta_stack;
			sta_stack = sta_stack->link;
			TFREE(statmp,statmp);
			n_STATE_STACK -- ;

			prstmp = prs_stack;
			prs_stack = prs_stack->prev;
			nps-- ;

			ADDTOTOP(prstmp);

			TOKSYMS[++n_TOKSYMS] = prstmp->symbol;

			BACKUPTOKS++;

			prev_tok_loc = RLOC(prs_stack);
		}

		/*	To get here scope recovery has failed, try other recoveries */

		/*  First restore the parse configuration for secondary recovery */
		copystack(STATE_STACK,&sta_stack,&n_sta_stack) ;

		for (trial = 1 ; trial < trials ; trial ++) {
			struct prsstack *tmp_ps ;

			tmp_ps = tokens[tokind];
			n_TOKSYMS -- ;
			TOKMINUS ;
			tmp_ps->prev = prs_stack ;
			prs_stack = tmp_ps ;
			nps ++ ;
		}

		PREVTOK = oldprevtok ;
		BACKUPTOKS = 0 ;
	}

	/************************************************************************
	 *								      
	 *					Secondary recovery 
	 *									
	 ************************************************************************/
	/*SEC*/

	/* Now try secondary recoveries.	
	 * First try deleting the error token and some sequence of tokens in the 
	 * forward context - stop at a beacon symbol.	
	 */
	{
#define sec_check	MIN_LEN

		char    tmp_str[500],
		err_msgs[500];
		int	    equal;
		int	    nst;
		int	    n_stack_del_syms;
		int	    t, kk, j, k;
		int	   *stack_delete_syms;
		struct tok_loc  leftt,
		rightt;

		err_msgs[0] = '\0' ; /* initialize to null string */

		for (k = 1; k <= MAX_LOOK_AHEAD - (MIN_LEN + 3); k++) {

			t = TOKSYMS[n_TOKSYMS--];
#ifdef DEBUG
			if (trcopt)
				fprintf (errfile, "take token off TOKSYMS (%s)\n", TOKSTR (t));
#endif

			if((kk=prs_check(sta_stack, TOKSYMS, n_TOKSYMS)) >= (MIN_LEN + 3)) {
				/* changed +2 to +3, gcs */
				/* delete k tokens */

				/*	rightt := TOKENS(n_TOKENS - k + 1);
				 *  TOKENS(n_TOKENS - k + 1 ..) := [];
				 */
				for (i = 1; i < k; i++)
					TOKMINUS;

				rightt = r_span (tokens[tokind]);
				TOKMINUS;

				syntax_err (LLOC (ERROR_TOKEN), &rightt, "Unexpected input");

				return;
			}

			/* For now, ignore to conform with SETL (BEACON_SYMS is empty) 
			 *	if (in_BEACON_SYMS (t))
	 		 *   break;
			 */
		}

		/* Try to resume the parse - perhaps with the inclusion of a sequence of
		 * scope closers-  from some state on the state stack.	
		 * After each attempt the current token is deleted.
		 * To succeed on arbitrary input, some state must accept EOFT.
		 */

#ifdef DEBUG
		if (trcopt)
			fprintf(errfile,"********** SECONDARY RECOVERY **********\n");
#endif

		nst = stack_size (sta_stack);

		while (1) {
			struct two_pool *sta_stack_copy;

			get_next(sec_check + 2);	 /* ensure that there are ample */

			/*	TOKSYMS := [t(1) : t in TOKENS]; */

			i = 0;
			j = tokbottom;

			for (;;) {
				TOKSYMS[i] = tokens[j] -> symbol;
				if (j == tokind)
					break;
				j = (j + 1) % toksiz;
				i++;
			}

			n_TOKSYMS = (toksiz + tokind - tokbottom) % toksiz;

			curtok = tokens[tokind];

#ifdef DEBUG
			if (trcopt) {
				fprintf(errfile,"TRYING: %s\n",TOKSTR(TOKSYMS[n_TOKSYMS]) );
				fprintf(errfile,
				  "\ttoksize = %d  tokbottom = %d  tokind = %d  curtok = %s\n",
				  toksiz,tokbottom,tokind,find_name(curtok) );
			}
#endif

			copystack (sta_stack, &sta_stack_copy, &nst);

			for (k = nst; k >= 1; k--) {

				/* Try peeling stacks. */

				/* Strip n_sta_stack - k - 1 elements off the state stack */

				kk = stack_size (sta_stack_copy) - k;

				/* while (--kk > 0)
				 *  sta_stack_copy = sta_stack_copy -> link;
				 */

				if ( (kk=prs_check (sta_stack_copy, TOKSYMS, n_TOKSYMS)) >=
				  (sec_check + 2)) { /* (sec_check + 1)) */
					/* Form the error message and quit the outer loop */
					sprintf (err_msgs, "%s", err_message (k,curtok));
#ifdef DEBUG
					if (trcopt)
						fprintf(errfile,"prs_check succeeded for k = %d\n",k);
#endif
					goto quit_loop_do;
				}

				/* Try closer recovery. */

				/* Make a copy of the state stack for later restoration */

				if (scope_recovery (k, 0, open_seq)) {
					/* Form the error message and quit the outer loop */
					sprintf (tmp_str, "%s", err_msgs);
					sprintf (err_msgs, "%s -- %s", err_message(k,curtok),
					  tmp_str);
#ifdef DEBUG
					if (trcopt)
						fprintf(errfile,
						  "scope_recovery succeeded for k = %d\n",k);
#endif

					goto quit_loop_do;
				}
				/* pop element of the state stack  */
				sta_stack_copy = sta_stack_copy -> link;

				closer_toksyms = NULL;
			}

			/* Get next token */

#ifdef IGNORE
			PREVTOK = tokens[tokind];
			/* This is a fix proposed by Sam Chin for cases when the entire
			 * text is garbage. If there are tokens on the list of lookahead
			 * tokens then take the token, otherwise, if not then take it from
			 * the input stream. If while deleting the tokens an end of file
			 * is reached, output the appropriate message and quit the
			 * secondary recovery
			 */
			curtok = NEXTTOK ;
#ifdef DEBUG
			if (trcopt)
				fprintf(errfile,"curtok = %s\n",find_name(curtok));
#endif
			if (curtok->symbol == EOFT_SYM) {
				sprintf(err_msgs,"End-of-file reached while trying to recover");
				goto quit_loop_do ;
			}

			if (tokind >= MAX_LOOK_AHEAD) {
				curtok = copytoken(tokens[tokind]) ;
#ifdef DEBUG
				if (trcopt)
					fprintf(errfile,"curtok updated to  %s\n",
					  find_name(curtok));
#endif
			}
#endif

			/* implement next_token macro (correctly), as done in SETL */
			PREVTOK = curtok;
			TOKMINUS;
			if ((toksiz + tokind - tokbottom + 1) % toksiz == 0)
				tokens[tokbottom=(toksiz+tokbottom-1)%toksiz] = gettok();

			/* NEXTTOK; */

			/* copytoken (curtok, tokens[tokind]); */

		}

quit_loop_do: 
		;			/* LABEL for goto */


		/* At this point we have recovered.	
		 * Locate the range of the error.	
		 */


		n_stack_del_syms = 0;
		if (nst > k	 &&  k > 0  &&	prs_stack != NULL) {
			/* check for empty parse stack and k not zero (i.e. end of file) */

			/* stack_delete_syms :=
				[PRS_STACK(t)(1) : t in [nps, nps - 1 .. k + 1]]; */

			/* nps = stack_count (prs_stack); */
			tmp_ps = prs_stack;
			stack_delete_syms =
			  (int *) malloc ((unsigned) ((nst - k ) * sizeof (int)));
			/* stack_delete_syms = (int *) malloc ((k + 1) * sizeof (int)); */
			for (t = nst; t >= k + 1; t--) {
				stack_delete_syms[n_stack_del_syms++] = tmp_ps -> symbol;
				tmp_ps = tmp_ps -> prev;
			}

			leftt = l_span (tmp_ps);

			/* 		PRS_STACK(k + 1 .. ) := [];
    		 *				STA_STACK(k + 1 .. ) := [];	
    		 */

			kk = stack_size (sta_stack) - k;
			while ( kk-- > 0) {
				sta_stack = sta_stack -> link;
				prs_stack = prs_stack -> prev;
			}
		}
		else
			leftt = l_span (ERROR_TOKEN);


		/* Form error location. */

		/*
		 *	Check whether 
		 *		stack_delete_syms = 
		 *			TOKSYMS(#TOKSYMS - #stack_del_syms + 1 ..  #stack_del_syms)
		 *
		 *	Assume equal and check element by element
		 */

		equal = 1;
		for (j = n_stack_del_syms - 1 , k = n_TOKSYMS ;
		  ((j >= 0) && (equal)) ; k--, j--)
			equal = equal && (stack_delete_syms[j] == TOKSYMS[k]);

		if ((n_stack_del_syms != 0) && (n_stack_del_syms <= n_TOKSYMS) && equal)
		{
			/* PRSERR_LOC = loc_range(LLOC(ERROR_TOKEN), LLOC(TOKENS[(tokind -
       		 * n_stack_del_syms + 1) % toksiz] ), RLOC(ERROR_TOKEN));
			 * for now just print the spans
			 */
			syntax_err (LLOC (ERROR_TOKEN), RLOC (ERROR_TOKEN), err_msgs);
		}
		else {
			/* PRSERR_LOC = 
     		 *  loc_range(LLOC(leftt), LLOC(PREVTOK), RLOC(ERROR_TOKEN));
     		 *  for now just print the spans
			 */
			syntax_err (&leftt, RLOC (ERROR_TOKEN), err_msgs);
		}

		/* If the secondary recovery involves a scope recovery, insert the
		 * closer tokens.	
		 */

		if (closer_toksyms != NULL) {
			/* TOKENS +:= [[t, t, top(PREVTOK)] : t in CLOSER_TOKSYMS]; */
			for(tmp_tp =closer_toksyms; tmp_tp != NULL; tmp_tp = tmp_tp -> link)
			{
				ADDTOTOP (PRSALLOC());
				tokens[tokind] -> symbol = tmp_tp -> val.state;
				tokens[tokind] -> ptr.token = TOKALLOC ();
				tokens[tokind] -> ptr.token -> index = tmp_tp -> val.state;
				tokens[tokind] -> ptr.token -> loc = PREVTOK -> ptr.token ->loc;
			}
		}

		return;
	}
}				/* End of Procedure prserr */

static void get_poss_tok()					/*;get_poss_tok*/
{
	struct two_pool *temp_sta_stack; /* Points to saved copy of state stack	*/
	struct two_pool *act_sta = NULL ; /* Stored list of acceptable actions	*/
	int	ss = sta_stack->val.state ;	/* The top of the state stack			*/
	struct two_pool *tmp_tp ;			/* utility variables			*/
	struct two_pool *tmp ;
	short	red,dr ;
	int	n ,n_temp_sta_stack ;

	/* Get the tokens that are acceptable to the top state on the stack.  */

	copystack(sta_stack,&temp_sta_stack,&n_temp_sta_stack);

	while ((dr = def_action[ss - 1]) != 0) {
		tmp = TALLOC() ;		/* act_sta with := ss	*/
		tmp->val.state = ss ;
		tmp->link = act_sta ;
		act_sta = tmp ;

		red = dr - NUM_STATES - 1 ;

		/*	Strip "rhslen[red]" elements from the temp_sta_stack */
		n = rhslen[red] ;
		if(!n) {
			tmp = TALLOC() ;
			tmp->link = temp_sta_stack ;
			temp_sta_stack = tmp ;
		}
		else if( n > 1 ) {
			while( --n > 1 )
				temp_sta_stack = temp_sta_stack->link ;
			tmp = temp_sta_stack ;
			temp_sta_stack = temp_sta_stack->link ;
		}
		else if (n < 0)
			break ;
		temp_sta_stack->val.state =	
		  action((int)(temp_sta_stack->link->val.state),lhs[red]);
		ss = temp_sta_stack->val.state ;
	}

	tmp = TALLOC() ;
	tmp->val.state = ss ;
	tmp->link = act_sta ;
	act_sta = tmp ;


	/* 
	 * POSS_TOK := +/{ACTION_TOKENS(s) : s in act_sta};				
	 * Since ACTION_TOKENS(s) is a set of numbers, for each s we must
	 * loop over the set, adding the values to the list POSS_TOK
	 */
	POSS_TOK = NULL ;
	while (act_sta != NULL) {
		int a ;

		for(a = ACTION_TOKENS_INDEX[(int)(act_sta->val.state) - 1] ;
		  a <= (ACTION_TOKENS_INDEX[(int)(act_sta->val.state)] - 1) ; a ++ )
		{
			/* Add ACTION_TOKENS(a) to the list */
			tmp_tp = TALLOC() ;
			tmp_tp->link = POSS_TOK ;
			tmp_tp->val.state = ACTION_TOKENS[a] ;
			POSS_TOK = tmp_tp ;
		}
		act_sta = act_sta->link ;
	}
}

static void get_next(int k)				/*;get_next*/
{
	/* This procedure ensures that tokens contains at least k tokens.  
	 * Note that tokind always points to the next token, so that the
	 * array must be read in in reverse order
	 */

	int i;

	/* First check if this is the first time, and if so allocate space for
	 * tokens.
	 */

	if (tokind == -1) {
		tokens =
		  (struct prsstack **)malloc((unsigned)(2*k*sizeof(struct prsstack *)));
		toksiz = 2 * k;
	}

	/* for now only put in k-1 tokens, since the current token has to be
	 * inserted.
	 */
	for (i = k - ((toksiz + (tokind - tokbottom + 1)) % toksiz); i>0 ; i--) {
		tokbottom = (toksiz + tokbottom - 1) % toksiz;
		tokens[tokbottom] = gettok();
	}
	if (tokind == -1)
		tokind = toksiz - 1;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.