/***   declarer.c   ***/

#include "algol.h"
#include "declarer.h"
#include "coercion.h"	/* for meek_integer */
#include "unit.h"
         /***  ERRORS:  311, 321-5 331-5, 341-7, 351-2, 361-6  ***/

static bool struct_declarer(int,int*,int);
static void union_declarer(int*,int);
static bool row_declarer(int,int,int*,int,int);
static void ravel(int);
static int  count_declared_tags(void), count_declarers(void),
	    count_dimension(void);

bool declarer(victal,t,Q,sure) int victal,*t,Q; bool sure;
{int v,el,j,Pold,jold,t1;
#if DEBUG
	fprintf(O,"declarer, Q=%c, P=%d\n",Q==G?'G':Q==H?'H':'F',P);
#endif
	if(spy_declarer()){
	  Pold=P;
	  switch(X[P++]){
	    case INT:    if(Q==G)more_instance(*t=int_mode); return(TRUE);
	    case REAL:	 if(Q==G)more_instance(*t=real_mode); return(TRUE);
	    case BOOL:   if(Q==G)more_instance(*t=bool_mode); return(TRUE);
	    case CHAR:   if(Q==G)more_instance(*t=char_mode); return(TRUE);
	    case BITS:	 if(Q==G)more_instance(*t=bits_mode); return(TRUE);
	    case VOID:   if(Q==G)more_instance(*t=void_mode); return(TRUE);
	    case STRING: if(Q==G)more_instance(*t=string_mode); return(TRUE);
	    case COMPL:  if(Q==G)more_instance(*t=cmpl_mode); return(TRUE);
	    case STRUCT:
		return(struct_declarer(victal,t,Q));
	    case REF:
		must(declarer(VIRTUAL,&v,Q,TRUE),311);
		if(Q==G){A[*t=ask()]=REF; B[*t]=v; C[*t]=0;}
		return(TRUE);
	    case PROC:
		if(plan(FORMAL,t,Q))return(TRUE); P=Pold; return(FALSE);
	    case UNION:
		union_declarer(t,Q); if(Q==G)ravel(*t); return(TRUE);
	    case FLEX:    /*flex not allowed in FORMAL*/
		must(inp(OPEN),346);
		must(row_declarer(victal!=FORMAL,victal,t,Q,TRUE),347);
		return(TRUE);
	    case OPEN:
		if(row_declarer(FALSE,victal,t,Q,sure))return(TRUE);
		P=Pold; return(FALSE);
	  }
	  P--; if(Q!=G && sure) return(inp_ind(&dummy));
	  if(inp_ind(&el) && search(el,&j) && A[v=VIND[j]]==MODE){
	      if(Q!=G)return(TRUE);
	      if(B[v]>0){more_instance(*t=B[v]); return(TRUE);}
	      B[v]=ask(); adm_parameter(&jold); adm_routine(&jold,j);
	      Pold=P; P=C[v]; must(declarer(victal,&t1,Q,TRUE),352);
	      P=Pold; adm_close(jold);
	      if(references(B[v])==1){
		  free(B[v]); *t=t1;
	      }else{
		  must(A[t1],351);
		  if(recursive(t1)) adjust_pointer(t1,B[v]);
		  copy(t1,B[v]); free(t1); 
		  ravel(*t=B[v]); put_recursivity(*t);
	      }
	      B[v]= -1; return(TRUE);
	  } P=Pold;
	}
	return(FALSE);
}

static bool struct_declarer(victal,t,Q) int victal, *t, Q;
{int i,v,w,n,t1;
	must(inp(OPEN),321);
	if(Q!=G){P=match(); return(TRUE);}
	A[v=ask()]=STRUCT; C[v]=n=count_declared_tags(); B[v]=w=askn(n);
	for(i=0;i<n;i++){
	    if(inp_tag(&C[w+i])){
		must(i,322); more_instance(B[w+i]=t1);
	    }else{
		must(declarer(victal,&t1,Q,TRUE),323);
		B[w+i]=t1; must(inp_tag(&C[w+i]),324);
	    }
	    must(inp(i<n-1?COMMA:CLOSE),325);
	}
	*t=v; return(TRUE);
}

bool plan(victal,t,Q) int victal, *t, Q;
{int Pold,n,i,t1,w;
	Pold=P;
	if(Q!=G){
	   if(inp(OPEN))P=match();
	   return( declarer(FORMAL,&dummy,F,TRUE) );
	}
	if(!plan(victal,t,F))return(FALSE);
	P=Pold; *t=ask();
	n = inp(OPEN)?
	  (victal==ACTUAL?count_declared_tags():count_declarers()) : 0;
	A[*t]=PROC; B[*t]=w=askn(n+1); C[*t]=n; A[w]=0; C[w]=0;
	for(i=0;i<n;i++){
	  if(inp_tag(&C[w+i+1])){
	      must(i,361); must(victal==ACTUAL,362);
	      more_instance(B[w+i+1]=t1);
	  }else{
	      must(declarer(FORMAL,&t1,Q,TRUE),363); B[w+i+1]=t1;
	      if(victal==ACTUAL){must(inp_tag(&C[w+i+1]),364);}
	  }
	  must(inp(i<n-1?COMMA:CLOSE),365);
	}
	must(declarer(FORMAL,&B[w],Q,TRUE),366); return(TRUE);
}

static void union_declarer(t,Q) int *t,Q;
{int n,w,i;
	must(inp(OPEN),331);
	if(Q!=G){P=match(); return;}
	must((n=count_declarers())>1,332);
	A[*t=ask()]=UNION; B[*t]=w=askn(n); C[*t]=0; C[w]=n;
	for(i=0;i<n;i++){
	    must(declarer(FORMAL,&B[w+i],Q,TRUE),333);
	    must(inp(i<n-1?COMMA:CLOSE),334);
	}
}

static bool row_declarer(flex,victal,t,Q,sure) bool flex,sure; int victal,*t,Q;
{int Pold,dim,w,i,n;
	if(Q!=G){
	    dummy=count_dimension();
	    return(inp(CLOSE) && declarer(victal,t,F,sure));
	}
	Pold=P; dim=count_dimension();
	if(!(inp(CLOSE) && declarer(victal,t,F,sure)))return(FALSE);
	P=Pold; A[*t=ask()]=ROW; B[*t]=w=askn(dim+1); C[*t]=!flex;
	for(i=w+1;i<=w+dim;i++){
	    if(victal==ACTUAL){
		must(unit(&Q),341); A[i]=short_meek_integer(&Q);
		if(inp(COLON)){
		    must(unit(&Q),342); B[i]=short_meek_integer(&Q);
		}else{
		    B[i]=A[i]; A[i]=1;
		}
	    }else{
		A[i]=1; B[i]=0; bummy=unit(&F); bummy=inp(COLON) && unit(&F);
	    }
	    must(inp(i<w+dim?COMMA:CLOSE),344);
	}
	must(Q==G,343); must(declarer(victal,&B[w],Q,TRUE),345);
	A[w]=dim; C[w]=0; C[w+dim]=1;
	for(i=w+dim;i>w+1;i--)C[i-1]=((n=B[i]-A[i])<0?0:n+1)*C[i];
	return(TRUE);
}

/*------------------------------------------------------------------------*/

static void ravel(t) int t;
{int bi,new_bi,bbi,i,j,n,v,dim,w;
	if(A[t]!=UNION) return;
	v=B[t]; n=dim=C[v]; must(A[v] ^= 1,335);
	for(i=0;i<dim;i++){
	    if(A[bi=B[v+i]]==UNION){ravel(bi); n += C[B[bi]]-1;}
	}
	if(n==dim){A[v]=0; return;}
	C[B[t]=w=askn(n)]=n;
	for(i=0;i<dim;i++){
	    if(A[bi=B[v+i]]==UNION){
		new_bi=unfold(bi); bbi=B[new_bi];
		for(j=0;j<C[bbi];j++)more_instance(B[w++]=B[j+bbi]);
		relax(new_bi); relax(bi);
	    }else{B[w++]=bi;}
	}
	freen(v,dim);
}

static int count_declared_tags()
{int Pold,i=1;
	Pold=P;
	while((declarer(FORMAL,&dummy,F,TRUE) || TRUE)
	     && inp_tag(&dummy) && inp(COMMA))i++;
	P=Pold; return(i);
}

static int count_declarers()
{int Pold,i=1;
	Pold=P;
	while(declarer(FORMAL,&dummy,F,TRUE) && inp(COMMA))i++;
	P=Pold; return(i);
}

static int count_dimension()
{int i=0;
	do{i++; bummy=unit(&F); bummy=inp(COLON) && unit(&F);}
	   while(inp(COMMA));
	return(i);
}

