/***********************************
  (C) Copyright 1992-1993; dit/upm
   Distributed under the conditions stated in the
   TOPO General Public License (see file LICENSE)
 ***********************************
 $Log: preproc.c,v $
 * Revision 1.19  1993/08/02  17:01:09  lotos
 * fix static fuinctions declarations
 *
 * Revision 1.18  1993/06/10  14:00:13  lotos
 * new annotation CALL
 *
 * Revision 1.17  1993/03/29  18:09:10  lotos
 * add constructor verification/s
 *
 * Revision 1.16  1993/03/24  17:51:24  lotos
 * suppress some preprocessing (now in glad)
 *
 * Revision 1.15  1993/01/18  18:12:15  lotos
 * distribution issues
 *
 * Revision 1.14  1992/12/02  10:57:25  lotos
 * option -s removes ldc and ldcinit annotations
 * remove error message: unnamed sort
 * a warning moved into an error: unnamed operation
 *
 * Revision 1.13  1992/11/17  18:42:13  lotos
 * parse annotation must be a single word
 * fix error when processing external expressions
 *
 * Revision 1.12  1992/10/14  17:43:13  lotos
 * forget about ophuscation
 *
 * Revision 1.11  1992/09/02  15:44:45  lotos
 * new option to remove externa annotations
 * new option to print and label the equations
 * new debugging options: save modified CAST
 * overall debugging and improvements
 *
 * Revision 1.10  92/05/06  18:49:46  lotos
 * suppress spureous errors assoc'ed to extrnal expressions
 * 
 * Revision 1.9  92/02/21  17:17:08  lotos
 * minor fixs
 * 
 * Revision 1.8  92/01/30  18:09:56  lotos
 * better error reporting
 * 
 * Revision 1.7  92/01/14  15:25:08  lotos
 * distribution issues
 * 
 * Revision 1.6  92/01/13  19:24:25  lotos
 * adaptec to ophuscate
 * 
 * Revision 1.5  91/11/20  13:10:15  lotos
 * parse annotation added
 * draw and parse annotations must be together
 * 
 * Revision 1.4  91/11/14  20:16:11  lotos
 * allowed internal operations of a external sort
 * 
 * Revision 1.3  91/10/02  16:49:05  lotos
 * color sort is now in the AT
 * 
 * Revision 1.2  91/04/11  18:32:04  lotos
 * code to transform value premisses into equations, removed
 * there may be external operations that yield a non-external sort
 * 
 * Revision 1.1  91/02/06  20:13:00  lotos
 * Initial revision
 * 
 ***********************************/

#ifndef lint
static char rcsid[]= "$Id: preproc.c,v 1.19 1993/08/02 17:01:09 lotos Exp $";
#endif

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

     "preproc.o": modulo preprocesador del AST de la especificacion.

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

#include <stdio.h>
#include "idle.h"
#include "tabop.h"
#include "lisec.h"
#include "ecuaciones.h"
#include "preproc.h"

static TNODE *ProxTermExtMEc();
static TNODE *ProxVarMEc();
static TNODE *ProxNoConsMEc();

void AsigEtiqEc()
/* Asigna etiquetas a las ecuaciones. */
{ EstManEc *EcAct;	/* Ecuacion actual. */
  TNODE *RaizEc;
  TNODE *PrimOp;
  int EtiqEc;

  for (EcAct= PrimEcTab(), EtiqEc= 1;
       EcAct != NULL;
       EcAct= EcAct->SigEc, ++EtiqEc)
  { RaizEc= gt_ft(EcAct->EcSimple);
    if (EcAct->Premisas == NULL)
      PrimOp= OpRaizMEc(gt_fs(EcAct->EcSimple));
    else
      PrimOp= OpRaizMEc(gt_fs(EcAct->Premisas));
    while (idclass(idref(PrimOp)) == opn_id &&
	   infix(idref(PrimOp)) == 0)
      PrimOp= PrimArgOp(PrimOp);
    add_line(RaizEc, ask_line(PrimOp));
    add_eqno(RaizEc, EtiqEc);
  }
}

int VerifAnotTipo()
/* Verifica las anotaciones del tipo de datos. */
{ int HayError= FALSE;
  EstManOp *ClAct;	/* Clase actual. */
  EstManOp *OpAct;	/* Operacion actual. */
  int EsClExt, EsOpExt;

  /* Verificacion de las clases. */
  for (ClAct= PrimClTab(); ClAct != NULL; ClAct= ClAct->Sig)
    if (v_extern(iddec(ClAct->Nodo)) != STR_ERR)
    { if (v_free(iddec(ClAct->Nodo)) == NULL)
	;
      if (v_equal(iddec(ClAct->Nodo)) == NULL)
	HayError= TRUE;
      if (v_draw(iddec(ClAct->Nodo)) == NULL)
	if (v_parse(iddec(ClAct->Nodo)) == NULL)
	  ;
	else
	  HayError= TRUE;
      else if (v_parse(iddec(ClAct->Nodo)) == NULL)
	;
    }
    else
    { if (v_free(iddec(ClAct->Nodo)) != NULL)
	HayError= TRUE;
      if (v_equal(iddec(ClAct->Nodo)) != NULL)
	HayError= TRUE;
      if (v_draw(iddec(ClAct->Nodo)) != NULL)
	HayError= TRUE;
      if (v_parse(iddec(ClAct->Nodo)) != NULL)
	HayError= TRUE;
    }
  /* Verificacion de las operaciones. */
  for (OpAct= PrimOpTab(); OpAct != NULL; OpAct= OpAct->Sig)
  { EsOpExt= v_extern(iddec(OpAct->Nodo)) != STR_ERR;
    EsClExt= v_extern(sort(iddec(OpAct->Nodo))) != STR_ERR;
    if (EsOpExt)
    { if (!EsClExt)
	if (nonconstructor(iddec(OpAct->Nodo)) == STR_ERR)
	  HayError= TRUE;
      if (name(iddec(OpAct->Nodo)) == NULL &&
	  call(iddec(OpAct->Nodo)) == NULL)
	HayError= TRUE;
      if (partial(iddec(OpAct->Nodo)) != NULL)
	HayError= TRUE;
    }
    else
    { if (EsClExt)
	if (nonconstructor(iddec(OpAct->Nodo)) == STR_ERR)
	  HayError= TRUE;
      if (call(iddec(OpAct->Nodo)) != NULL)
	HayError= TRUE;
    }
  }
  return !HayError;
}

static TNODE *PrimTermExtMEc(NodoRaizMEc)
/* Devuelve un puntero al primer termino de clase externa de una ec. */
TNODE *NodoRaizMEc;	/* Puntero al nodo raiz del miembro. */
{ TNODE *IdOp;
  TNODE *ProxTermExtMEc();

  IdOp= PrimArgOp(OpRaizMEc(NodoRaizMEc));
  if (IdOp != NULL &&
      v_extern(sort(idref(IdOp))) == STR_ERR)
    IdOp= ProxTermExtMEc(IdOp);
  return IdOp;
}

static TNODE *ProxTermExtMEc(IdOp)
/* Devuelve un puntero al termino de clase externa mas proximo. */
/* (Se mueve por un miembro de una ecuacion usando preorden). */
TNODE *IdOp;	/* Puntero a un nodo con una operacion. */
{ TNODE *SigIdOp;

  aborta_si(NoEstaEnEc(IdOp))
  if (v_extern(sort(idref(IdOp))) == STR_ERR)
    SigIdOp= PrimArgOp(IdOp);
  else
    SigIdOp= NULL;
  for (;;)
  { if (SigIdOp == NULL)
      if ((SigIdOp= SigArgOp(IdOp)) == NULL)
	while ((IdOp= OpArg(IdOp)) != NULL)
	  if ((SigIdOp= SigArgOp(IdOp)) != NULL)
	    break;
    if (SigIdOp == NULL ||
	v_extern(sort(idref(SigIdOp))) != STR_ERR)
      break;
    IdOp= SigIdOp;
    SigIdOp= PrimArgOp(IdOp);
  }
  return SigIdOp;
}

static int EsOpRaizExpVar(IdOp)
/* Indica si es la operacion raiz de una expresion con variables. */
TNODE *IdOp;		/* Puntero a la operacion. */
{ TNODE *IdArg;

  aborta_si(NoEstaEnEc(IdOp) ||
	    idclass(idref(IdOp)) == value_id)
  for (IdArg= PrimArgOp(IdOp); IdArg != NULL; IdArg= SigArgOp(IdArg))
    if (idclass(idref(IdArg)) == value_id)
      return TRUE;
    else if (EsOpRaizExpVar(IdArg))
      return TRUE;
    else
      continue;
  return FALSE;
}

static TNODE *CreaPremExpExt(EcDato, TermExt)
/* Crea una premisa por cada expresion externa y devuelve la lista. */
/* (Sustituye las expresiones por variables). */
EstManEc *EcDato;	/* Ecuacion dato. */
TNODE *TermExt;		/* Puntero al termino de clase externa. */
{ TNODE *LisPrem;	/* Lista de premisas. */
  int  HayPrem;		/* Indica si hay que crear una premisa. */
  TNODE *IdVarSust;	/* Puntero a la variable sustituta. */
  TNODE *ExpAct;	/* Puntero a la expresion actual. */
  TNODE *PremAct;	/* Puntero a la premisa actual. */
  TNODE *SigTermExt;	/* Puntero al siguiente termino externo. */

  aborta_si(NoEstaEnEc(TermExt) ||
	    v_extern(sort(idref(TermExt))) == STR_ERR)
  if (idclass(idref(TermExt)) == value_id)
    HayPrem= FALSE;
  else if (EsOpRaizExpVar(TermExt))
  { EcDato= NULL;
    Error(ERR_MIEM_IZ_EXPEXT, lexv(idref(TermExt)),
	  file(TermExt), line(TermExt));
    HayPrem= FALSE;
  }
  else
    HayPrem= EcDato == NULL? FALSE: TRUE;
  SigTermExt= ProxTermExtMEc(TermExt);
  if (SigTermExt == NULL)
    LisPrem= EcDato == NULL? TND_ERR: (TNODE *) NULL;
  else
    LisPrem= CreaPremExpExt(EcDato, SigTermExt);
  if (HayPrem && LisPrem == TND_ERR)
    HayPrem= FALSE;
  if (HayPrem)
  { if (LisPrem == NULL)
    { TNODE *RaizEc;

      RaizEc= gt_ft(EcDato->EcSimple);
      if (oveq(RaizEc) == TND_ERR)
	add_oveq(RaizEc, cp_tree(RaizEc, TRUE));
    }
    IdVarSust= CreaIdVar(TermExt);
    ExpAct= SustExp(RaizExp(TermExt), CreaExpVar(IdVarSust));
    TermExt= IdVarSust;
    PremAct= CreaPrem(CreaExpVar(cp_node(IdVarSust, TRUE)), ExpAct);
    lnbrothers(PremAct, LisPrem);
    LisPrem= PremAct;
  }
  return LisPrem;
}

int CorrExpExtMIEc()
/* Corrige el AST si hay expresiones externas en alguna ecuacion. */
/* (Solo importa el miembro izquierdo). */
{ int HayError= FALSE;
  EstManEc *EcAct;	/* Ecuacion actual. */
  TNODE *TermExt;	/* Puntero al termino de clase externa. */
  TNODE *LisPremCorr;	/* Lista de premisas correctoras. */

  for (EcAct= PrimEcTab(); EcAct != NULL; EcAct= EcAct->SigEc)
  { TermExt= PrimTermExtMEc(gt_fs(EcAct->EcSimple));
    if (TermExt == NULL)
      continue;
    LisPremCorr= CreaPremExpExt(EcAct, TermExt);
    if (LisPremCorr == TND_ERR)
    { HayError= TRUE;
      continue;
    }
    if (LisPremCorr == NULL)
      continue;
    InsLisPremEc(gt_ft(EcAct->EcSimple), LisPremCorr);
    EcAct->Premisas= LisPremCorr;
  }
  return !HayError;
}

static TNODE *PrimVarMEc(NodoRaizMEc)
/* Devuelve un puntero a la primera variable de un miembro de una ec. */
TNODE *NodoRaizMEc;	/* Puntero al nodo raiz del miembro. */
{ TNODE *PrimIdVar;
  TNODE *ProxVarMEc();

  PrimIdVar= OpRaizMEc(NodoRaizMEc);
  if (idclass(idref(PrimIdVar)) != value_id)
    PrimIdVar= ProxVarMEc(PrimIdVar);
  return PrimIdVar;
}

static TNODE *ProxVarMEc(IdOp)
/* Devuelve un puntero a la variable mas proxima. */
/* (Se mueve por un miembro de una ecuacion usando preorden). */
TNODE *IdOp;	/* Puntero a un nodo con una operacion. */
{ TNODE *IdVar;

  for (;;)
  { if ((IdVar= PrimArgOp(IdOp)) == NULL)
      if ((IdVar= SigArgOp(IdOp)) == NULL)
	while ((IdOp= OpArg(IdOp)) != NULL)
	  if ((IdVar= SigArgOp(IdOp)) != NULL)
	    break;
    if (IdVar == NULL ||
	idclass(idref(IdVar)) == value_id)
      break;
    IdOp= IdVar;
  }
  return IdVar;
}

static TNODE *CreaPremVarRep(EcDato, PrimIdVar)
/* Crea una premisa por cada variable repetida y devuelve la lista. */
/* (Modifica las variables repetidas para que sean diferentes). */
EstManEc *EcDato;	/* Ecuacion dato. */
TNODE *PrimIdVar;	/* Puntero a la primera variable. */
{ TNODE *SegIdVar;	/* Puntero a la segunda variable. */
  TNODE *IdVarAct;	/* Puntero a la variable actual. */
  int PrimIdRef;	/* Atributo "c_idref" de la primera variable. */
  TNODE *LisPrem;	/* Lista de premisas. */
  TNODE *PremAct;	/* Puntero a la premisa actual. */

  aborta_si(NoEstaEnEc(PrimIdVar) ||
	    idclass(idref(PrimIdVar)) != value_id)
  aborta_si(EcDato == NULL)
  SegIdVar= ProxVarMEc(PrimIdVar);
  if (SegIdVar == NULL)
    return NULL;
  PrimIdRef= idref(PrimIdVar);
  IdVarAct= SegIdVar;
  do
    if (idref(IdVarAct) == PrimIdRef)
      break;
  while ((IdVarAct= ProxVarMEc(IdVarAct)) != NULL);
  LisPrem= CreaPremVarRep(EcDato, SegIdVar);
  if (IdVarAct != NULL)
  { if (LisPrem == NULL)
    { TNODE *RaizEc;

      RaizEc= gt_ft(EcDato->EcSimple);
      if (oveq(RaizEc) == TND_ERR)
	add_oveq(RaizEc, cp_tree(RaizEc, TRUE));
    }
    ModIdVar(IdVarAct);
    PremAct= CreaPrem(CreaExpVar(cp_node(PrimIdVar, TRUE)),
		      CreaExpVar(cp_node(IdVarAct, TRUE)));
    lnbrothers(PremAct, LisPrem);
    LisPrem= PremAct;
  }
  return LisPrem;
}

int CorrRepVarMIEc()
/* Corrige el AST si hay repeticion de variables en alguna ecuacion. */
/* (Solo importa el miembro izquierdo). */
{ EstManEc *EcAct;	/* Ecuacion actual. */
  TNODE *PrimIdVar;	/* Puntero a la primera variable. */
  TNODE *LisPremCorr;	/* Lista de premisas correctoras. */

  for (EcAct= PrimEcTab(); EcAct != NULL; EcAct= EcAct->SigEc)
  { PrimIdVar= PrimVarMEc(gt_fs(EcAct->EcSimple));
    if (PrimIdVar == NULL)
      continue;
    LisPremCorr= CreaPremVarRep(EcAct, PrimIdVar);
    if (LisPremCorr == NULL)
      continue;
    InsLisPremEc(gt_ft(EcAct->EcSimple), LisPremCorr);
    EcAct->Premisas= LisPremCorr;
  }
  return TRUE;
}

int EnVarMIEc()
/* Enlaza variables en cada una de las ecuaciones. */
/* (Miembro derecho y premisas, con el miembro izquierdo). */
{ int HayError= FALSE;
  EstManEc *EcAct;	/* Ecuacion actual. */
  TNODE *NodoRaizMIEc;
  TNODE *PrimIdVarMIEc;
  TNODE *Premisa;
  TNODE *NodoRaizMEc;
  TNODE *IdVar, *IdVarRef;
  TNODE *IdRaizMIEc;
  int i;

  /* Comienza la seleccion sucesiva de ecuaciones. */
  for (EcAct= PrimEcTab(); EcAct != NULL; EcAct= EcAct->SigEc)
  { /* Seleccion del miembro izquierdo. */
    NodoRaizMIEc= gt_fs(EcAct->EcSimple);
    PrimIdVarMIEc= PrimVarMEc(NodoRaizMIEc);
    /* Seleccion y procesamiento de las premisas. */
    for (Premisa= EcAct->Premisas;
	 Premisa != NULL;
	 Premisa= gt_rb(Premisa))
    { NodoRaizMEc= gt_fs(Premisa);
      for (i= 1; i <= 2; ++i)
      { IdVar= PrimVarMEc(NodoRaizMEc);
	for (; IdVar != NULL; IdVar= ProxVarMEc(IdVar))
	{ IdVarRef= PrimIdVarMIEc;
	  for (; IdVarRef != NULL; IdVarRef= ProxVarMEc(IdVarRef))
	    if (idref(IdVar) == idref(IdVarRef))
	    { add_pvar(IdVar, IdVarRef);
	      break;
	    }
	  if (IdVarRef == NULL)
	  { Error(ERR_PREMISA_INCOR, lexv(idref(IdVar)),
		  file(IdVar), line(IdVar));
	    HayError= TRUE;
	  }
	}
	NodoRaizMEc= gt_rb(NodoRaizMEc);
      }
    }
    /* Analisis del miembro izquierdo. */
    IdRaizMIEc= OpRaizMEc(NodoRaizMIEc);
    if (idclass(idref(IdRaizMIEc)) == value_id)
    { Error(ERR_MIEM_IZ_VAR, lexv(idref(IdRaizMIEc)),
	    file(IdRaizMIEc), line(IdRaizMIEc));
      HayError= TRUE;
    }
    /* Seleccion y procesamiento del miembro derecho. */
    NodoRaizMEc= gt_rb(NodoRaizMIEc);
    IdVar= PrimVarMEc(NodoRaizMEc);
    for (; IdVar != NULL; IdVar= ProxVarMEc(IdVar))
    { IdVarRef= PrimIdVarMIEc;
      for (; IdVarRef != NULL; IdVarRef= ProxVarMEc(IdVarRef))
	if (idref(IdVar) == idref(IdVarRef))
	{ add_pvar(IdVar, IdVarRef);
	  break;
	}
      if (IdVarRef == NULL)
      { Error(ERR_MIEM_DCHO_INCOR, lexv(idref(IdVar)),
	      file(IdVar), line(IdVar));
	HayError= TRUE;
      }
    }
  }
  return !HayError;
}

static TNODE *PrimNoConsMEc(NodoRaizMEc)
/* Devuelve un puntero al primer no constructor de una ecuacion. */
TNODE *NodoRaizMEc;	/* Puntero al nodo raiz del miembro. */
{ TNODE *IdOp;
  TNODE *ProxNoConsMEc();

  IdOp= PrimArgOp(OpRaizMEc(NodoRaizMEc));
  if (IdOp != NULL &&
      (idclass(idref(IdOp)) != opn_id ||
       v_extern(sort(idref(IdOp))) != STR_ERR ||
       nonconstructor(idref(IdOp)) == STR_ERR))
    IdOp= ProxNoConsMEc(IdOp);
  return IdOp;
}

static TNODE *ProxNoConsMEc(IdOp)
/* Devuelve un puntero al no constructor mas proximo. */
/* (Se mueve por un miembro de una ecuacion usando preorden). */
TNODE *IdOp;	/* Puntero a un nodo con una operacion. */
{ TNODE *SigIdOp;

  aborta_si(NoEstaEnEc(IdOp))
  if (v_extern(sort(idref(IdOp))) == STR_ERR)
    SigIdOp= PrimArgOp(IdOp);
  else
    SigIdOp= NULL;
  for (;;)
  { if (SigIdOp == NULL)
      if ((SigIdOp= SigArgOp(IdOp)) == NULL)
	while ((IdOp= OpArg(IdOp)) != NULL)
	  if ((SigIdOp= SigArgOp(IdOp)) != NULL)
	    break;
    if (SigIdOp == NULL)
      break;
    IdOp= SigIdOp;
    if (idclass(idref(SigIdOp)) == opn_id &&
	v_extern(sort(idref(SigIdOp))) == STR_ERR)
      if (nonconstructor(idref(SigIdOp)) == STR_ERR)
	SigIdOp= PrimArgOp(IdOp);
      else
	break;
    else
      SigIdOp= NULL;
  }
  return SigIdOp;
}

int VerifConsTipo()
/* Verifica la consistencia de los constructores del tipo de datos. */
{ int HayError= FALSE;
  EstManEc *EcAct;	/* Ecuacion actual. */
  EstManOp *OpAct;	/* Operacion actual. */
  TNODE *NoCons;
  TNODE *IdOp;

  for (EcAct= PrimEcTab(); EcAct != NULL; EcAct= EcAct->SigEc)
  { NoCons= PrimNoConsMEc(gt_fs(EcAct->EcSimple));
    if (NoCons == NULL)
      continue;
    HayError= TRUE;
    do
      Error(ERR_MIEM_IZ_NOCONS, lexv(idref(NoCons)),
	    file(NoCons), line(NoCons));
    while ((NoCons= ProxNoConsMEc(NoCons)) != NULL);
  }
  for (EcAct= PrimEcTab(); EcAct != NULL; EcAct= EcAct->SigEc)
  { IdOp= OpRaizMEc(gt_fs(EcAct->EcSimple));
    if (anyeq(idref(IdOp)) < 0)
      add_anyeq(idref(IdOp), 0);
  }
  for (OpAct= PrimOpTab(); OpAct != NULL; OpAct= OpAct->Sig)
  { if (v_extern(iddec(OpAct->Nodo)) == STR_ERR &&
	nonconstructor(iddec(OpAct->Nodo)) != STR_ERR &&
	anyeq(iddec(OpAct->Nodo)) < 0)
    { HayError= TRUE;
      Error(ERR_NOCONS_NOEC, lexv(iddec(OpAct->Nodo)),
	    file(OpAct->Nodo), line(OpAct->Nodo));
    }
  }
  return !HayError;
}
