unify.c 4.3 KB
/* Unify.c *******************************************************************************

                                    The Anubis Compiler. 
                                   Unification of types. 

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

#include "compil.h"

/* Environments are just A-lists ((v1 . T1) ...) where v_i are type unknowns and where T_i
   are  types.  The environment  just  tells  which type  unknowns  are  already bound  to
   types. */ 
   
/* We use an auxiliary function */ 
static int unify_aux(Expr x,               // first expression
                     Expr y ,              // second expression 
                     Expr *env);           // environment


/* 'depends_on' is used for circularity testing.  It returns 1 if 'y' contains a reference
   to 'x'. */ 
static int depends_on(Expr x,             // x is an unknown
                      Expr y,             // y is an expression 
                      Expr env)
{
  assert(is_unknown(x));           // only meaningful if 'x' is an unknown. 
  /* It is also assumed that 'x' is not a key of env. It is actually the case: check below
   all calls to 'depends_on'. */ 
   
 begin: 
  /* find if y  contains the unknown x. This is  relative to env: i.e. if  y is an unknown
     and y is bound in env, we must first replace y by its value.  */ 
  if (y == x) return 1;     
   
  if (is_unknown(y))
    {
      Expr yval = assoc(y,env);
      if (yval != key_not_found)
        { /* replace y by its value */ 
          y = yval; 
          goto begin;    // and restart
        }
    }

  if (!consp(y))  // y is a 'constant'
    return 0; 
  else
    {
      if (depends_on(x,car(y),env))
 return 1; 
      y = cdr(y); 
      goto begin; 
    }
}


/* Join two environments. */ 
Expr join_envs(Expr e1, Expr e2)
{
  Expr key, val, entry, val2; 

  while (consp(e1))
    {
      entry = car(e1); 
      key = car(entry);
      val = cdr(entry); 
      e1 = cdr(e1); 

      /* if 'key' is not  a key of e2, just add the entry to  e2. Otherwise, if the values
     of  'key'  in  e1   and  e2  do  not  unify  (with  envs   nil  and  e2),  return
     'not_unifiable'. If they unify, replace e2 by the result of this unification. */ 
      val2 = assoc(key,e2); 

      if (val2 == key_not_found)
 e2 = cons(entry,e2); 

      else 
 {
   e2 = unify(val,nil,val2,e2); 
   if (e2 == not_unifiable)
     {
       return e2; 
     }
 }
    }
  return e2; 
}



/* interface to unification */ 
Expr                /* returns a new environment */ 
_unify(Expr x,       /* first expression to unify */
       Expr ex,      /* environment for first expression */ 
       Expr y,       /* second expression to unify */ 
       Expr ey)      /* environment for second expression */ 
{
  Expr env, result; 
  int old_next_pair = next_pair; 

  /* the two environments must join together */ 
  if ((env = join_envs(ex,ey)) == not_unifiable) 
    return env; 

  if (unify_aux(x,y,&env))    /* unify and record instanciations in env */ 
    result = env; 
  else
    {
      next_pair = old_next_pair; 
      result = not_unifiable; 
    }
  return result;

}


/* the unification itself */ 
static int unify_aux(Expr x, 
       Expr y, 
       Expr *env)
{
  Expr aux; 
 begin:

  if (x == y)
    return 1; 

  if (is_unknown(x))
    {
      Expr v = assoc(x,*env); 
      if (v == key_not_found)  
 {
   /* x not instanciated */ 
   /* pairs ($x . $y) in env must always satisfy $x > $y */ 
   if (is_unknown(y) && x <= y)
     {
       /* exchange x and y, and try again */ 
       aux = x; 
       x = y; 
       y = aux; 
       goto begin; 
     }
   else
     {
       if (depends_on(x,y,*env)) 
  return 0; 
       else
  {
    *env = cons(cons(x,y),*env); 
    return 1; 
  }
     }
 }
      else
 {
   x = v; 
   goto begin; 
 }
    }

  if (is_unknown(y))
    {
      Expr v = assoc(y,*env); 
      if (v == key_not_found)
 {
   /* y not instanciated */ 
   {
     if (depends_on(y,x,*env))
       return 0; 
     else
       {
  *env = cons(cons(y,x),*env); 
  return 1; 
       }
   }
 }
      else
 {
   y = v; 
   goto begin; 
 }
    }

  if (!(consp(x) && consp(y)))
    return 0; 

  /* now x and y are pairs */ 
  if (!unify_aux(car(x),car(y),env))
    return 0; 

  /* the two heads have unified */ 
  x = cdr(x); 
  y = cdr(y);
  goto begin; 
}