/* 
    Zz Dynamic Parser Library
    Copyright (C) 1989 - I.N.F.N - S.Cabasino, P.S.Paolucci, G.M.Todesco
    The Zz Library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
    The Zz Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "zz.h"
#include "zlex.h"
#include "rule.h"
#include "err.h"
#include "avl.h"
#include "trace.h"
/*PROTOTYPES*/
int next_token(struct s_content *);
void syntax_error(int (*info_routine)());
void action(struct s_rule *rule, struct s_content stack[], struct s_content *ret);
int get_error_number(void);
int errprintf(char *fmt,...);
int param_substitute(struct s_content *token,char **paramname);
/*FORWARD*/
int check_reduce(int, struct s_rule *);
void lr_reduce(struct s_rule *, int, struct s_content *);
void dump_stack(void);
/*---------------------------------------------------------------------------*/
extern struct s_nt *nt_any,*nt_param,*nt_gparam;
static const char *first_prompt="";
static int max_dot=0,tot_dot=0,ndot=0,max_sp=0;
static int reduction_count=0;
#define EXPECTED_SIZE 30
static struct s_content expected[EXPECTED_SIZE];
static int expected_n;
/*---------------------------------------------------------------------------*/
/*   2 macros to search the AVL trees  */
#define AVL_LOCATE_TERM_TRAN(TR,VA,NO) {\
	struct avl_node *TMPnode=TR->root;\
	struct s_term_tran *TMPtran;NO=0;\
    	while (TMPnode) {\
    	  TMPtran = (struct s_term_tran*) TMPnode->key.ptr;\
      	  if      ( (long)TMPtran->term.tag < (long)(VA)->tag) \
		TMPnode = TMPnode->right;\
          else if ( (long)TMPtran->term.tag > (long)(VA)->tag) \
		TMPnode = TMPnode->left;\
          else {\
          if      ( s_content_value(TMPtran->term) < s_content_value(*(VA))) \
		TMPnode = TMPnode->right;\
	  else if ( s_content_value(TMPtran->term) > s_content_value(*(VA))) \
		TMPnode = TMPnode->left;\
	  else {NO=TMPnode->data;break;}}}}
#define AVL_LOCATE_LONG(TR,VA,NO) {\
	struct avl_node *node = TR->root;NO=0;\
    	while (node)\
      	  if      ( node->key.val < (long)VA) node = node->right;\
          else if ( node->key.val > (long)VA) node = node->left;\
          else {NO=node->data;break;}}        
/*----------------------------------------------------------------------*/
struct s_cur_token {
   struct s_content cnt;
   char *param_name,is_eof,is_param;
   struct s_nt *nt;
  } cur_token;
/*----------------------------------------------------------------------*/
#define LR_NextToken {\
   cur_token.is_eof= !next_token(&(cur_token.cnt));\
   cur_token.is_param=\
     param_substitute(&(cur_token.cnt),&(cur_token.param_name));\
   cur_token.nt=find_nt(cur_token.cnt.tag->name);\
  }
/*---------------------------------------------------------------------------*/
#define RECOVERY_SIZE 100
static struct {struct s_nt *nt; char *termlist;} recovery_array[RECOVERY_SIZE];
static int recovery_n=0;
/*---------------------------------------------------------------------------*/
#define WORKAREA_SIZE 100
#define A_REDUCE 1
#define A_SHIFT_KEYWORD 2
#define A_SHIFT_MATCHED_TAG 3
#define A_SHIFT_PARAM 4
#define A_SHIFT_ANY 5
#define NO_PARAM 0
#define PARAM 1
#define GPARAM 2
static struct {
  int type,set,is_param;
  struct s_content keyword;
  struct s_rule *rule;
 } workarea[WORKAREA_SIZE];
static int workarea_n=0;
/*---------------------------------------------------------------------------*/
/* storage for the dot */
#define DOT_POOL_SIZE 8000
static struct s_dot *dots[DOT_POOL_SIZE];
static int dots_n=0;
/* LR parser stack */
#define LRSTACK_SIZE 500
static struct {
   int a,b,prev;
  } lrstack[LRSTACK_SIZE];
struct s_content valuestack[LRSTACK_SIZE];
typedef struct {
 int sp,a,b;
 } LRENV;
static LRENV cur_lrenv = {0,0,-1};
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* Initialize the structure cur_lrenv (at the start of parse()) */
#define LR_InitStack {\
  cur_lrenv.a=cur_lrenv.b+1;}
/*----------------------------------------------------------------------*/
/* add a DOT to the working set */
#define LR_AddDot(DOT) {\
  if(cur_lrenv.b>=DOT_POOL_SIZE-1)\
    {zz_error(INTERNAL_ERROR,"dot_pool overflow");exit(1);}\
  else dots[++cur_lrenv.b]=DOT;}
/*----------------------------------------------------------------------*/
/* cancel all the dot(s) of the working set */
#define LR_ClearDots {\
  cur_lrenv.a=cur_lrenv.b-1;}
/*----------------------------------------------------------------------*/
/* mette il working set sullo stack (PREV e' il set precedente)
   apre il working set successivo */
#define LR_PushSet(PREV) {int sp=cur_lrenv.sp++;\
  if(cur_lrenv.sp>LRSTACK_SIZE)\
     {zz_error(INTERNAL_ERROR,"lrstack overflow");exit(1);}\
  else {\
   lrstack[sp].a=cur_lrenv.a;\
   lrstack[sp].b=cur_lrenv.b;\
   lrstack[sp].prev=(PREV);\
   cur_lrenv.a=cur_lrenv.b+1;}}
/*----------------------------------------------------------------------*/
/* 'SET' diventa il set sul top dello stack. il workset e' vuoto */
#define LR_RestoreSet(SET) { \
   cur_lrenv.sp = SET+1;cur_lrenv.a=lrstack[SET].b+1;\
   cur_lrenv.b=cur_lrenv.a-1;}
/*----------------------------------------------------------------------*/
static void compute_expected_from_set(int set_index);
/*------------------------------------------------=-------------------*/
static long int setid=0;
/* calcola la chiusura del work set */
void make_closure(void)
{
  void lr_add_nt();
  int i,a=cur_lrenv.a,b=cur_lrenv.b;
  setid++;
  for(i=a;i<=b;i++)dots[i]->set = setid;
  for(i=a;i<=cur_lrenv.b;i++)
    avl_scan(dots[i]->nttree,lr_add_nt);
}
/*----------------------------------------------------------------------*/
void lr_add_nt(tran)
struct s_nt_tran *tran;
{
int i;
struct s_dot *dot;
if((dot = tran->nt->first_dot) && dot->set!=setid)
  {
/*
   for(i=cur_lrenv.a;i<=cur_lrenv.b;i++)
     if(dots[i]==dot) return ;
*/
   dot->set=setid;
   LR_AddDot(dot);
  }
}
/*----------------------------------------------------------------------*/
/* 
   crea il nuovo kernel (set senza chiuso)  con tutti gli shift
   che ammettono il token corrente.
   il set di partenza e' 'set_index' (vale sempre il tos, ma non
   e' necessario).
   set ci riesce aggiunge una voce a workarea e mette il nuovo set
   sullo stack. N.B. in questo caso si aggiorna lo stack pointer!!
   
 */
void try_shift(int set_index)
{
struct s_term_tran *ttran;
struct s_nt_tran *nttran;
int i,a,b,lev,count,zero,paramtype;
struct s_dot *dot;
a=lrstack[set_index].a;
b=lrstack[set_index].b;
lev=0;count=zero=cur_lrenv.b;
paramtype=PARAM;
for(i=a;i<=b;i++)
  {
   dot=dots[i];
   if(cur_token.is_param)
     {
      AVL_LOCATE_LONG(dot->nttree,nt_param,nttran)
      if(nttran) 
         {
	  if(lev<4) {lev=4;count=zero;} cur_lrenv.b=count++;
          LR_AddDot(nttran->next)
	  continue;
         }
      AVL_LOCATE_LONG(dot->nttree,nt_gparam,nttran)
      if(nttran) 
         {
	  if(lev<4) {lev=4;count=zero;} cur_lrenv.b=count++;
          LR_AddDot(nttran->next)
	  paramtype=GPARAM;
	  continue;
         }
     }
   AVL_LOCATE_TERM_TRAN(dot->termtree,&(cur_token.cnt),ttran)
   if(ttran) 
     {
      if(lev>3) continue;if(lev<3) {lev=3;count=zero;} cur_lrenv.b=count++;
      LR_AddDot(ttran->next)
     }
   AVL_LOCATE_LONG(dot->nttree,cur_token.nt,nttran)
   if(nttran) 
     {
      if(lev>2) continue;if(lev<2) {lev=2;count=zero;} cur_lrenv.b=count++;
      LR_AddDot(nttran->next)
     }
   if(!cur_token.is_eof)
     {
      AVL_LOCATE_LONG(dot->nttree,nt_any,nttran)
      if(nttran) 
        {
         if(lev>1)continue;if(lev<1){lev=1;count=zero;}cur_lrenv.b=count++;
         LR_AddDot(nttran->next)
	}
     }
  }
if(cur_lrenv.b>=cur_lrenv.a)
  {
   workarea[workarea_n].set = cur_lrenv.sp;
   workarea[workarea_n].rule=0;
   workarea[workarea_n].is_param = lev==4?paramtype:0;
   workarea_n++;
   LR_PushSet(set_index)
  }
}
/*----------------------------------------------------------------------*/
/* ritorna 1 se lo shift e' ammissibile */
int check_shift(int set_index)
{
struct s_term_tran *ttran;
struct s_nt_tran *nttran;
int i,a,b;
struct s_dot *dot;
a=lrstack[set_index].a;
b=lrstack[set_index].b;
for(i=a;i<=b;i++)
  {
   dot=dots[i];
   if(cur_token.is_param && dot->match_param) return 1;
   AVL_LOCATE_TERM_TRAN(dot->termtree,&(cur_token.cnt),ttran) 
     if(ttran) return 1;
   AVL_LOCATE_LONG(dot->nttree,cur_token.nt,nttran) if(nttran) return 1;
   if(!cur_token.is_eof && dot->match_any) return 1;
  }
return 0;
}
/*----------------------------------------------------------------------*/
/*
  crea (se possibile) un nuovo set (gia' chiuso) sul top dello stack riducendo
  da 'set_index' la regola 'rule'
  se ci riesce aggiunge una riga a workarea
  
  */
int try_reduce(int set_index, struct s_rule *rule)
{
LRENV oldenv;
int oldset,i,j,k,a,b,curset,ret,length,base;
struct s_nt *nt;
struct s_dot *dot,*dot1;
struct s_rule *rule1;
struct s_nt_tran *tran;
length=rule->bead_n-1;
nt=(struct s_nt *)s_content_value(rule->beads[0].cnt);
/* pop */
i=set_index;
while(i>=0 && length-->0) i=lrstack[i].prev;
if(i<0) {zz_error(INTERNAL_ERROR,"try_reduce: stack empty");exit(1);}
a=lrstack[i].a;
b=lrstack[i].b;
oldset=i; /* il set ''sotto' la regola */
/* creo il set GOTO */
cur_lrenv.b=cur_lrenv.a-1;
oldenv = cur_lrenv;
for(j=a;j<=b;j++)
  {
   dot1=dots[j];
   AVL_LOCATE_LONG(dot1->nttree,nt,tran)
   if(tran) LR_AddDot(tran->next)
  }
if(cur_lrenv.a>cur_lrenv.b)
  {
   zz_error(INTERNAL_ERROR,"try_reduce: GOTO not found reducing %r",rule);
   exit(1);
  }
make_closure();
LR_PushSet(oldset)
curset=cur_lrenv.sp-1;
ret=check_shift(curset);
if(!ret)
  {
   a=lrstack[curset].a;
   b=lrstack[curset].b;
   for(i=a;i<=b;i++)
     if((rule1=dots[i]->rule)!=NULL && check_reduce(curset,rule1)) 
       {ret=1;break;}
  }
if(ret)
  {
   workarea[workarea_n].rule=rule;
   workarea[workarea_n].set=curset;
   workarea[workarea_n].is_param = 0;
   workarea_n++;   
  }
else
  {
   /* la riduzione non e' ammissibile; come non detto */
   cur_lrenv = oldenv;
  }
return ret;
}
/*----------------------------------------------------------------------*/
int check_reduce(int set_index, struct s_rule *rule)
{
int oldset,i,j,k,a,b,curset,ret,length,base;
struct s_nt *nt;
struct s_dot *dot,*dot1;
struct s_rule *rule1;
struct s_nt_tran *tran;
LRENV oldenv;
oldenv = cur_lrenv;
length=rule->bead_n-1;
nt=(struct s_nt *)s_content_value(rule->beads[0].cnt);
i=set_index;
while(i>=0 && length-->0) i=lrstack[i].prev;
if(i<0) {zz_error(INTERNAL_ERROR,"check_reduce: stack empty");exit(1);}
a=lrstack[i].a;
b=lrstack[i].b;
oldset=i;
for(j=a;j<=b;j++)
  {
   dot1=dots[j];
   AVL_LOCATE_LONG(dot1->nttree,nt,tran)
   if(tran) LR_AddDot(tran->next)
  }
if(cur_lrenv.a>cur_lrenv.b)
  {
   zz_error(INTERNAL_ERROR,"try_reduce: GOTO not found reducing %r",rule);
   exit(0);
  }
make_closure();
LR_PushSet(oldset)
curset=cur_lrenv.sp-1;
ret=check_shift(curset);
if(!ret)
  {
   a=lrstack[curset].a;
   b=lrstack[curset].b;
   for(i=a;i<=b;i++)
     if((rule1=dots[i]->rule)!=NULL && check_reduce(curset,rule1)) 
       {ret=1;break;}
  }
cur_lrenv=oldenv;
return ret;
}
/*----------------------------------------------------------------------*/
int lr_loop(struct s_nt *main_nt)
{
  struct s_content ret_token,shift_token;
  int i,j,dst,a,b,newa,newb,olda,oldb,is_param;
  int cur_set,old_set,new_set;
  struct s_rule *rule;
  while(1)
    {
      workarea_n=0;
      cur_set = cur_lrenv.sp-1;
      a=lrstack[cur_set].a;
      b=lrstack[cur_set].b;
      /* lazy_rec(dots+a,b-a+1); */
   
      try_shift(cur_set);
      for(i=a;i<=b;i++)
	if(rule=dots[i]->rule)
	  {
	    if((struct s_nt *)s_content_pvalue(rule->beads[0].cnt)==main_nt)
	      return 1;
	    try_reduce(cur_set,rule);
	  }   
      if(workarea_n==0) 
	return 0;
      if(workarea_n>1)
	{
	  zz_error(ERROR,"Ambiguous syntax (%d)",workarea_n);
	  for(i=0;i<workarea_n;i++)
	    if(workarea[i].rule)
	      errprintf("  (%d) reduce %r\n",i,workarea[i].rule);
	    else
	      errprintf("  (%d) shift %z\n",i,&cur_token.cnt);
	  return -1;
	}
      if(workarea_n==1)
	{
	  new_set = workarea[0].set;
	  rule=workarea[0].rule;
	  is_param=workarea[0].is_param;
	  if(rule) 
	    lr_reduce(rule,cur_set,&ret_token);
	  newa = lrstack[new_set].a;
	  newb = lrstack[new_set].b;
	  old_set = lrstack[new_set].prev;
	  oldb = lrstack[old_set].b;
	  dst = newa-(oldb+1);
	  if(dst>0)
	    {
	      for(i=newa;i<=newb;i++)
		dots[i-dst]=dots[i];
	      newa-=dst;
	      newb-=dst;
	    }
	  cur_lrenv.a=newa;
	  cur_lrenv.b=newb;
	  cur_lrenv.sp=old_set+1;
	  if(rule==0) 
	    {
	      make_closure();
	      switch(is_param)
		{
		case PARAM:
		  valuestack[cur_lrenv.sp].tag = tag_ident;
		  s_content_svalue(valuestack[cur_lrenv.sp]) = cur_token.param_name;
		  break;
		case GPARAM:
		  if(cur_token.is_param==GPARAM)
		    {
		      valuestack[cur_lrenv.sp].tag = tag_ident;
		      s_content_svalue(valuestack[cur_lrenv.sp]) = cur_token.param_name;
		    }
		  else
		    valuestack[cur_lrenv.sp] = cur_token.cnt;	        
		  break;
		default:
		  valuestack[cur_lrenv.sp] = cur_token.cnt;
		}
	      shift_token = cur_token.cnt;
	      LR_NextToken;
	    }
	  else
	    {
	      valuestack[cur_lrenv.sp] = ret_token;
	    }
	  LR_PushSet(old_set)
	    if(zz_trace_mask()&TRACE_LRSTACK)
	      {
		if(rule)
		  {printz("  @ REDUCE %r\n",rule);dump_stack();}
		else
		  {printz("  @ SHIFT %z\n",&shift_token);dump_stack();}
	      }
	}
      else break;
    }
  return 0;
}
/*----------------------------------------------------------------------*/
void lr_reduce(struct s_rule *rule, int set_index, struct s_content *ret_token)
{
int i,length;
reduction_count++;
length = rule->bead_n-1;
set_index-=length-1;
if(zz_trace_mask()&TRACE_REDUCE)
  {
   printz("   @ reduce %r  args:",rule,length);
   for(i=0;i<length;i++)
     printz(" %z",&valuestack[set_index+i]);
   printz("\n");
  }
action(rule,valuestack+set_index,ret_token);
if(zz_trace_mask()&TRACE_REDUCE)
   printz("   @ action ret: %z\n",ret_token);
}
/*----------------------------------------------------------------------*/
static int add_expected(tag,value)
struct s_tag *tag;
long value;
{
char *name,*s;
int i;
if(expected_n>=EXPECTED_SIZE) return 0;
if(tag==tag_sint)
  {
   name=((struct s_nt *)value)->name;
   for(s=name;*s && *s!='$';s++);
   if(*s=='$') return 1;
  }
else if(tag==tag_ident)
  {
   name=(char*)value;
   for(s=name;*s && *s!='$';s++);
   if(*s=='$') return 1;
  }
for(i=0;i<expected_n;i++)
  if(expected[i].tag==tag && s_content_value(expected[i])==value)
    return 1;
expected[expected_n].tag=tag;
s_content_value(expected[expected_n])=value;
expected_n++;
return 1;
}
/*----------------------------------------------------------------------*/
static int compute_expected_from_reduction(set_index,rule)
int set_index;
struct s_rule *rule;
{
int i,j,k,a,b,curset;
LRENV oldenv;
struct s_dot *dot;
struct s_term_tran *ttran;
struct s_nt_tran *nttran;
int length;
struct s_nt *nt;
length=rule->bead_n-1;
nt=(struct s_nt*)s_content_value(rule->beads[0].cnt);
oldenv=cur_lrenv;
while(set_index>=0 && length-->0)
  set_index=lrstack[set_index].prev;
if(set_index<0) {printf("\n*** Internal error. stackempty ***\n");return 0;}
a=lrstack[set_index].a;
b=lrstack[set_index].b;
/* creo il set GOTO */
cur_lrenv.b=cur_lrenv.a-1;
for(i=a;i<=b;i++)
  {
   dot=dots[i];
   AVL_LOCATE_LONG(dot->nttree,nt,nttran)
   if(nttran) LR_AddDot(nttran->next)
  }
if(cur_lrenv.a>cur_lrenv.b)
  {
   printf("\n*** Internal error. GOTO not found ***\n");
   cur_lrenv = oldenv;return 0;
  }
make_closure();
LR_PushSet(set_index)
compute_expected_from_set(cur_lrenv.sp-1);
cur_lrenv = oldenv;
}
/*----------------------------------------------------------------------*/
static void compute_expected_from_set(int set_index)
{
struct s_dot *dot;
int i,a,b;
struct s_term_tran *ttran;
struct s_nt_tran *nttran;
a=lrstack[set_index].a;
b=lrstack[set_index].b;
for(i=a;i<=b;i++)
  {
   dot=dots[i];
   for(ttran=avl_first(dot->termtree);ttran;ttran=avl_next(dot->termtree))
     if(!add_expected(ttran->term.tag,s_content_value(ttran->term))) return;
   for(nttran=avl_first(dot->nttree);nttran;nttran=avl_next(dot->nttree))
     if(!add_expected(tag_sint,nttran->nt)) return;
  }
for(i=a;i<=b;i++)
  {
   dot=dots[i];
   if(dot->rule)
     compute_expected_from_reduction(set_index,dot->rule);
  }
}
/*----------------------------------------------------------------------*/
static int print_expected()
{
char buffer[256];
int i,j,k;
expected_n=0;
compute_expected_from_set(cur_lrenv.sp-1);
if(expected_n==0)
  errprintf("| no token accessible here\n");
else
  {
   sprintz(buffer,"got: '%z'\n| ", &(cur_token.cnt)); 
   strcat(buffer,"expected one of: ");
   j=strlen(buffer);
   for(i=0;i<expected_n;i++)
     {
      buffer[j++]=' ';
      if(expected[i].tag==tag_sint)
        strcpy(buffer+j,((struct s_nt*)s_content_value(expected[i]))->name);
      else
        sprintz(buffer+j,"'%z'",expected+i);
      if(i>=EXPECTED_SIZE-1) 
	strcat(buffer+j," ....");
      k=j;
      while(buffer[j]) j++;
      if(j>70) {
        buffer[k]='\0';
        errprintf("| %s\n",buffer);
        i--;strcpy(buffer,"   ");j=strlen(buffer);
       }
     }
   
   if(j>0) errprintf("| %s\n",buffer);
  }
  return 0;
}
/*----------------------------------------------------------------------*/
int recovery(void)
{
struct {struct s_content cnt; struct s_nt *nt;int lrset;} 
  tmpterm,termlist[1000];
int a,b,tmp,termlist_n,found;
struct s_nt_tran *nttran;
struct s_nt *nt;
struct s_dot *dot;
struct s_content cnt;
char *s;
int pop,i,j,k,newpop;
int set_index;
/* guardo i set sullo stack e la lista delle coppie di
   recovery. Mi costruisco l'insieme dei terminali su cui si puo'
   fare recovery */
set_index = cur_lrenv.sp-1;
termlist_n=0;
while(set_index>=0)
  {
   a=lrstack[set_index].a;
   b=lrstack[set_index].b;
   for(i=a;i<=b;i++)
     {
      dot=dots[i];
      nttran=0;
      for(k=0;nttran==0 && k<recovery_n;k++)
        {
         nt = recovery_array[k].nt;
         AVL_LOCATE_LONG(dot->nttree,nt,nttran)
         if(nttran) 
	   {
             s=recovery_array[k].termlist;
             while(*s)
               {
		 zlex(&s,&cnt);
		 termlist[termlist_n].cnt = cnt;
		 termlist[termlist_n].nt = nt;
		 termlist[termlist_n].lrset = set_index;
		 termlist_n++;
	       }
	   }
	}
     }
   set_index=lrstack[set_index].prev;   
  }
for(i=termlist_n-1;i>0;i--)
  for(j=0;j<i;j++)
    if(termlist[j].lrset<termlist[j+1].lrset)
       {
	tmpterm=termlist[j];termlist[j]=termlist[j+1];termlist[j+1]=tmpterm;
       }
/*
printf("recovery term:\n");
for(k=0;k<termlist_n;k++)
  {
   printz("%z on nt %s (setstack %d)\n",
      &(termlist[k].cnt),
      termlist[k].nt->name,
      termlist[k].lrset);
  }
*/
/* scandisco il file finche' non trovo un terminale di recovery
   (o un EOF) */
found=0;
while(!cur_token.is_eof)
  {
   for(i=0;i<termlist_n;i++)
     if(cur_token.cnt.tag==termlist[i].cnt.tag &&
        s_content_value(cur_token.cnt)==s_content_value(termlist[i].cnt))
        break;
   if(i<termlist_n) {found=1;break;}
   LR_NextToken;
  }
/* se ho trovato un terminale ammissibile: */
if(found)
  {
   /* svuoto lo stack fino al nt corrispondente */
   j = termlist[i].lrset;
   nt = termlist[i].nt;
   LR_RestoreSet(j)
   a=lrstack[j].a;
   b=lrstack[j].b;
   for(i=a;i<=b;i++)
     {
      dot=dots[i];
      for(nttran=avl_first(dot->nttree);nttran;nttran=avl_next(dot->nttree))
	LR_AddDot(nttran->next)
     }
   make_closure();
   LR_PushSet(j)   
   return 1;
  }
else
  return 0;
}
/*------------------------------------------------------------------*/
/*
  Associa una lista di termini di recovery ad un non-terminale
*/
void set_recovery(char *ntname, char *termlist)
{
struct s_nt *nt;
int i;
nt = find_nt(ntname);
for(i=0;i<recovery_n && recovery_array[i].nt!=nt;i++);
if(i>=recovery_n)
  {
   if(recovery_n>=RECOVERY_SIZE)
     {
      printf("set_recovery: too many recovery pairs\n");return;
     }
   i=recovery_n++;
   recovery_array[i].nt=nt;
  }
else
  {
   if(recovery_array[i].termlist) 
     free(recovery_array[i].termlist);
  }
recovery_array[i].termlist = (char*)malloc(strlen(termlist)+1);
strcpy(recovery_array[i].termlist,termlist);
}
/*------------------------------------------------------------------*/
static int find_prompt(prompt)
     const char **prompt;
{
struct s_nt_tran *nttran;
struct s_dot *dot;
struct s_nt *nt;
int i,set;
set=cur_lrenv.sp-1;
if(lrstack[set].prev<0)
  {
/*
   ep & gmt -- 9/11/94
   *prompt=first_prompt;
*/
    *prompt = zz_get_prompt();
    return 1;
  }
for(i=cur_lrenv.a;i<=cur_lrenv.b;i++)
  {
   dot = dots[i];
   if(dot->rule)
     {
      nt=(struct s_nt*)s_content_value(dot->rule->beads[0].cnt);
      if(nt->prompt)
        {
         *prompt=nt->prompt;
         return 1;
        }
     }
  }
return 0;
}
/*------------------------------------------------------------------*/
/*
  associa un prompt ad un non terminale
*/
void set_nt_prompt(char *ntname, const char *prompt)
{
struct s_nt *nt;
if(ntname)
  {
   nt = find_nt(ntname);
   nt->prompt=prompt;
  }
else
  first_prompt = prompt;
}
/*----------------------------------------------------------------------*/
void dump_dot(struct s_dot *dot, int off)
{
int length;
struct s_term_tran *ttran;
struct s_nt_tran *nttran;
struct s_rule *rule;
rule=dot->rule;
if(rule)
  {
   length=rule->bead_n-1;
   printz("  (%d) %r\n",length-off,rule);
  }
for(nttran=avl_first(dot->nttree);nttran;nttran=avl_next(dot->nttree))
   dump_dot(nttran->next,off+1);
for(ttran=avl_first(dot->termtree);ttran;ttran=avl_next(dot->termtree))
   dump_dot(ttran->next,off+1);
}
/*----------------------------------------------------------------------*/
void dump_stack(void)
{
#define IMAGE_SIZE 10
int image[IMAGE_SIZE],image_n;
struct s_dot *dot;
int i,j,a,b;
i = cur_lrenv.sp-1;
image_n=0;
while(i>=0 && image_n<IMAGE_SIZE)
  {
   image[image_n++]=i;
   i=lrstack[i].prev;
  }
printf("  @ lrstack[]= %s",image_n>=IMAGE_SIZE?" ...":"   |");
while(--image_n>=0)
  {
   a=lrstack[image[image_n]].a;
   b=lrstack[image[image_n]].b;
   for(j=a;j<=b;j++) {printf("%s%d ",dots[j]->rule?"'":"",dots[j]->id);}
   printf("| ");
  }
printf("\n");
}
/*----------------------------------------------------------------------*/
void dump_set(int set_index)
{
int i,a,b;
a=lrstack[set_index].a;
b=lrstack[set_index].b;
printf("set %d (prev=%d)\n",set_index,lrstack[set_index].prev);
for(i=a;i<=b;i++)
  {
   if(i>a) printf("  ----\n");
   dump_dot(dots[i],0);
  }
printf("\n");
}
/*----------------------------------------------------------------------*/
void write_dot_stat(void)
{
printf("dot n.: max=%d, mean=%d\n",max_dot,ndot==0?0:tot_dot/ndot);
printf("lr sp: max=%d\n",max_sp);
}
/*------------------------------------------------------------------*/
void print_report(void)
{
static int old_reduction_count=0;
printf("%d reductions done (%+d)\n",
       reduction_count,reduction_count-old_reduction_count);
old_reduction_count=reduction_count;
printf("%d dots used\n",cur_lrenv.b); 
}
/*------------------------------------------------------------------*/
void fprint_param(chan)
FILE *chan;
{
if(cur_token.is_param)
  {
   fprintz(chan,"| %s == %z\n",
     cur_token.param_name,&cur_token.cnt);
  }
}
/*------------------------------------------------------------------*/
int parse(struct s_nt *start_nt)
{
  int ret;
  struct s_cur_token old_token;
  LRENV old_lrenv;
  //extern int print_expected();
  extern int (*find_prompt_proc)();
  find_prompt_proc=find_prompt;
  /* Save the current configuration */
  old_lrenv = cur_lrenv;
  old_token = cur_token;
  /* Initialize the stack */
  LR_InitStack;
  LR_AddDot(start_nt->first_dot);
  make_closure();
  LR_PushSet(-1);
  LR_NextToken;
  while(1)
    {
      ret=lr_loop(start_nt);
      if(ret>0) break;
      if(ret==0) syntax_error(print_expected);
      if(!recovery())
        {
          zz_error(FATAL_ERROR,"unrecoverable error");
          break;
        }
    }
  cur_lrenv = old_lrenv;
  cur_token = old_token;
  if(get_error_number())
    return 0;
  else 
    return 1;
}
static char sccsid[]="@(#)parse.c	6.2\t11/9/94";
static char rcsid[] = "$Id: parse.c,v 1.12 2002/01/24 18:28:28 ckjamesb Exp $ ";