nonspec.c   [plain text]


/*
 * Copyright (c) 2000-2001
 *      The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include "banshee.h"
#include "flowrow-sort.h"
#include "flowrow-var.h"
#include "setif-sort.h"
#include "setif-var.h"
#include "setst-sort.h"
#include "setst-var.h"
#include "term-sort.h"
#include "term-var.h"

struct constructor
{
  sort_kind sort;
  int type;
  int arity;
  char *name;
  sig_elt *sig;
};

typedef struct constructor *constructor;

typedef enum 
{
  vnc_pos,
  vnc_neg,
  vnc_non
} vnc_kind;

struct sig_elt
{
  vnc_kind variance;
  sort_kind sort;
};

typedef struct sig_elt sig_elt;

typedef struct proj_pat
{
  sort_kind sort;
  int type;
  stamp st;
  int i;
  gen_e exp;
  vnc_kind variance;
  constructor c;
} *proj_pat;


typedef struct cons_expr  
{
  sort_kind sort;
  int type;
  stamp st;
  int arity;
  char *name;
  sig_elt *sig;
  gen_e *exps;
} * cons_expr;


static int new_type()
{
  static int type = 10;
  int ret = type;
  if (type > 2000)
    {
      fprintf(stderr, "Exceeded maximum number of constructors\n");
      assert(0);
    }
  type += 2;
  return ret;
}

static bool fixed_sort(sort_kind s)
{
  return !(s == sort_term || s == sort_set);
}

/* 
   Convention : constructor types are even, pats are odd.
   The smallest specialized type is 10.
*/
static bool setif_is_pat(gen_e e)
{
  int type = ((setif_term)e)->type;
  return ( (type & 1) && (type > 10) );
}

static bool setst_is_pat(gen_e e)
{
  int type = ((setst_term)e)->type;
  return ( (type & 1) && (type > 10) );
}

static gen_e get_proj_var(sort_kind s, bool large)
{
  switch (s)
    {
    case setif_sort:
      {
	if (large)
	  return (gen_e)sv_fresh_large(get_sort_region(setif_sort),NULL);
	else return (gen_e)sv_fresh(get_sort_region(setif_sort),NULL);	  
      }
      break;
    case setst_sort:
      {
	if (large)
	  return (gen_e)st_fresh_large(get_sort_region(setst_sort),NULL);
	else return (gen_e)st_fresh(get_sort_region(setst_sort),NULL);	
      }
      break;
    case flowrow_sort:
      {
	if (large)
	  return (gen_e)fv_fresh_large(get_sort_region(flowrow_sort),NULL);
	else return (gen_e)fv_fresh(get_sort_region(flowrow_sort),NULL);
      }
      break;
    case term_sort:
      {
	if (large)
	  return (gen_e)tv_fresh_large(get_sort_region(term_sort),NULL);
	else return (gen_e)tv_fresh(get_sort_region(term_sort),NULL)
      }	
      break;
    default:
      {
	fail("Unmatched sort in get_proj_var\n");
	return NULL;
      }
      break;
    }

  return NULL;
}

static gen_e get_sort_zero(sort_kind s)
{
switch (s)
    {
    case setif_sort:
      return setif_zero();
    case setst_sort:
      return setst_zero();
    case flowrow_sort:
      return flowrow_zero();
    case term_sort:
      return term_zero();
    default:
      fail("Unmatched sort in get_sort_zero\n");
      return NULL;
    }
  return NULL;
}

static gen_e get_sort_one(sort_kind s)
{
switch (s)
    {
    case setif_sort:
      return setif_one();
    case setst_sort:
      return setst_one();
    case flowrow_sort:
      return flowrow_one();
    case term_sort:
      return term_one();
    default:
      fail("Unmatched sort in get_sort_zero\n");
      return NULL;
    }
  return NULL;
}

static region get_sort_region(sort s)
{
  switch (s)
    {
    case setif_sort:
      return setif_region;
    case setst_sort:
      return setst_region;
    case flowrow_sort:
      return flowrow_region;
    case term_sort:
      return term_region:
    default:
      fail("Unmatched sort in get_sort_region\n");
      return NULL;
    }
  return NULL;
}

static term_hash get_sort_hash(sort s)
{
  switch (s)
    {
    case setif_sort:
      return setif_hash;
    case setst_sort:
      return setst_hash;
    case flowrow_sort:
      return flowrow_hash;
    case term_sort:
      return term_hash:
    default:
      fail("Unmatched sort in get_sort_hash\n");
      return NULL;
    }
  return NULL;
}

constructor make_constructor(const char *name,sort_kind sort, sig_elt s[],
			     int arity)
{
  constructor c = ralloc(expr_region,struct constructor);
  sig_elt *sig = rarrayalloc(expr_region,arity,sig_elt);
  
  c->type = new_type();

  if (arity)
    {
      memcpy(sig,s,sizeof(sig_elt)*arity);
    }

  if ( fixed_sort(sort) )
    failure("Specified sort does not allow constructor types\n");
  
  c->sort = sort;
  c->arity = arity;
  c->name = rstrdup(expr_region,name);
  c->sig = sig;
  
  return c;
}

gen_e constructor_expr(constructor c, gen_e exps[], int arity)
{
  cons_expr result;
  int i;
  region sort_region = get_sort_region(c->sort);
  term_hash sort_hash = get_sort_hash(c->sort);
  
  stamp *st = rarrayalloc(sort_region,arity + 1,stamp);
  st[0] = c->type;
  
  // Dynamic arity check
  if(arity != c->arity)
      failure("Signature mismatch\n");
  
  // Dynamic sort checks
  for (i = 0; i < arity; i++)
    {
      if ( c->sig[i].sort != exps[i]->sort)
	failure(stderr,"Signature mismatch\n");
      st[i+1] = exps[i]->st;
    }

  // Hash-consing of terms
  if (!(result = term_hash_find(sort_hash,st,arity+1)) || arity == 0 )
    {
      gen_e *e = rarrayalloc(sort_region,arity,gen_e);
      
      if (arity)
	memcpy(e,exps,sizeof(gen_e)*arity);
      else 
	e = NULL;

      result = ralloc(sort_region,struct cons_expr);  
      result->type = st[0];
      result->st = stamp_fresh();
      result->sort = c->sort;
      result->arity = c->arity;
      result->name = c->name;
      result->sig = c->sig;
      result->exps = e;
      
      term_hash_insert(expr_hash,result,st,arity+1);
    }

  return (gen_e)result;
}

static gen_e proj_pat(constructor c, int i, gen_e e)
{
  proj_pat pat;
  region sort_region = get_sort_region(e->sort);
  term_hash sort_hash = get_sort_hash(e->sort);
  
  stamp s[3];
  s[0] = c->type + 1;
  s[1] = e->st;
  s[2] = i;
  
  if (! (pat = term_hash_find(sort_hash,s,3)) )
    {
      pat = ralloc(sort_region,struct proj_pat);
      pat->type = s[0];
      pat->st = stamp_fresh();
      pat->sort = c->sort;
      pat->exp = e;
      pat->variance = c->sig[i].variance;
      pat->c = c;
      pat->i = i;
      term_hash_insert(sort_hash,pat,s,3);
    }
  
  return (gen_e)pat;
}

gen_e setif_proj_pat(constructor c,int i,gen_e e)
{
  return proj_pat(c,i,e);
}

gen_e setst_proj_pat(constructor c, int i, gen_e e)
{
  return proj_pat(c,i,e);
}

gen_e setif_proj(constructor c, int i, gen_e e)
{
  setif_var v;
  gen_e proj_var, proj;
  
  gen_e nonspec_get_proj(gen_e_list arg1)
    {
      proj_pat pat;
      gen_e_list_scanner scan;
      gen_e temp;
      
      gen_e_list_scan(arg1,&scan);
      while (gen_e_list_next(&scan,&temp))
	{
	  pat = (proj_pat)temp;
	  if ( pat_match(pat->type,c->type) && i == pat->i )
	    return pat->exp;
	}
      return NULL;
    }
  
  if (e->sort != setif_sort)
    {
      failure("Sort check : setif_proj\n");
    }

  else if (i < 0 || i > c->arity)
    {
      failure("Signature mismatch\n");
    }
  
  else if (setif_is_zero(e))
    return get_sort_zero(c->sig[i].sort);
  
  else if ( ((setif_term)e)->type == c->type )
    {
      cons_expr constructed = (cons_expr)e;
      return constructed->exps[i];
    }
  
  else if (setif_is_var(e))
    {
      v = (setif_var)e;
      if ( (proj = sv_get_ub_proj(v,nonspec_get_proj)) )
	{
	  return proj;
	}
      else
	{
	  gen_e pat;
	  gen_e_list_scanner scan;
	  gen_e lb;
	  proj_var = get_proj_var(c->sig[i].sort,FALSE);
	  pat = setif_proj_pat(c,i,proj_var);
	  sv_add_ub_proj(sort_region,v,pat);
	  
	  gen_e_list_scan(sv_get_lbs(v),&scan);
	  while (gen_e_list_next(&scan,&lb))
	    {
	      setif_inclusion(lb,pat);
	    }
	  return proj_var;
	}
    }

  else if (setif_is_union(e))
    {
      if( (proj = nonspec_get_proj(setif_get_proj_cache(e))) )
	return proj;
      else
	{
	  gen_e pat;
	  proj_var = get_proj_var(c->sig[i].sort,FALSE);
	  pat = setif_proj_pat(c,i,proj_var);
	  
	  setif_set_proj_cache(e,pat);
	  
	  setif_inclusion(e,pat);
	  return proj_var;
	}
    }
  else 
    {
      gen_e pat;
      proj_var = get_proj_var(c->sig[i].sort,FALSE);
      pat = setif_proj_pat(c,i,proj_var);
      setif_inclusion(e,pat);
      return proj_var;
    }
}

gen_e setst_proj(constructor c, int i, gen_e e)
{
  setst_var v;
  gen_e proj_var, proj;
  
  gen_e nonspec_get_proj(gen_e_list arg1)
    {
      proj_pat pat;
      gen_e_list_scanner scan;
      gen_e temp;
      
      gen_e_list_scan(arg1,&scan);
      while (gen_e_list_next(&scan,&temp))
	{
	  pat = (proj_pat)temp;
	  if ( pat_match(pat->type,c->type) && i == pat->i )
	    return pat->exp;
	}
      return NULL;
    }
  
  if (e->sort != setst_sort)
    {
      failure("Sort check : setst_proj\n");
    }

  else if (i < 0 || i > c->arity)
    {
      failure("Signature mismatch\n");
    }
  
  else if (setst_is_zero(e))
    return get_sort_zero(c->sig[i].sort);
  
  else if ( ((setst_term)e)->type == c->type )
    {
      cons_expr constructed = (cons_expr)e;
      return constructed->exps[i];
    }
  
  else if (setst_is_var(e))
    {
      v = (setst_var)e;
      if ( (proj = sv_get_ub_proj(v,nonspec_get_proj)) )
	{
	  return proj;
	}
      else
	{
	  gen_e pat;
	  gen_e_list_scanner scan;
	  gen_e lb;
	  proj_var = get_proj_var(c->sig[i].sort,FALSE);
	  pat = setst_proj_pat(c,i,proj_var);
	  sv_add_ub_proj(sort_region,v,pat);
	  
	  gen_e_list_scan(sv_get_lbs(v),&scan);
	  while (gen_e_list_next(&scan,&lb))
	    {
	      setst_inclusion(lb,pat);
	    }
	  return proj_var;
	}
    }

  else if (setst_is_union(e))
    {
      if( (proj = nonspec_get_proj(setst_get_proj_cache(e))) )
	return proj;
      else
	{
	  gen_e pat;
	  proj_var = get_proj_var(c->sig[i].sort,FALSE);
	  pat = setst_proj_pat(c,i,proj_var);
	  
	  setst_set_proj_cache(e,pat);
	  
	  setst_inclusion(e,pat);
	  return proj_var;
	}
    }
  else 
    {
      gen_e pat;
      proj_var = get_proj_var(c->sig[i].sort,FALSE);
      pat = setst_proj_pat(c,i,proj_var);
      setst_inclusion(e,pat);
      return proj_var;
    }
}

static void setif_con_match(gen_e e1, gen_e e2)
{
  // Case where e1 is a constructor expression and e2 is a proj_pat
  if (pat_match(((setif_term)e2)->type,((setif_term)e1)->type))
    {
      cons_expr c = (cons_expr)e1;
      proj_pat p = (proj_pat)e2;
      int i = p->i;
      
      if (c->sig[i].variance == vnc_pos)
	call_inclusion_fn(c->exps[i],p->exp);
      else if (c->sig[i].variance == vnc_neg)
	call_inclusion_fn(p->exp,c->exps[i]);
      else
	call_unify_fn(c->exps[i],p->exp);
    }
  else if (setif_is_pat(e2)) 
    {
      return;
    }
  
  // Case where e1 and e2 are constructor expressions
  else 
    {
      cons_expr c1 = (cons_expr)e1,
	c2 = (cons_expr)e2;
      
      if (c1->type != c2->type)
	failure("Constructor mismatch\n");
      else
	{
	  int i;
	  for (i = 0; i < c1->arity; i++)
	    {
	      if (c1->sig[i].variance == vnc_pos)
		call_inclusion_fn(e1,e2);
	      else if (c1->sig[i].variance == vnc_neg)
		call_inclusion_fn(e2,e1);
	      else
		call_unify_fn(e1,e2);
	    }
	  
	}
    } 
}

static void setst_con_match(gen_e e1, gen_e e2)
{
  // Case where e1 is a constructor expression and e2 is a proj_pat
  if (pat_match(((setst_term)e2)->type,((setst_term)e1)->type))
    {
      cons_expr c = (cons_expr)e1;
      proj_pat p = (proj_pat)e2;
      int i = p->i;
      
      if (c->sig[i].variance == vnc_pos)
	call_inclusion_fn(c->exps[i],p->exp);
      else if (c->sig[i].variance == vnc_neg)
	call_inclusion_fn(p->exp,c->exps[i]);
      else
	call_unify_fn(c->exps[i],p->exp);
    }
  else if (setst_is_pat(e2)) 
    {
      return;
    }
  
  // Case where e1 and e2 are constructor expressions
  else 
    {
      cons_expr c1 = (cons_expr)e1,
	c2 = (cons_expr)e2;
      
      if (c1->type != c2->type)
	failure("Constructor mismatch\n");
      else
	{
	  int i;
	  for (i = 0; i < c1->arity; i++)
	    {
	      if (c1->sig[i].variance == vnc_pos)
		call_inclusion_fn(e1,e2);
	      else if (c1->sig[i].variance == vnc_neg)
		call_inclusion_fn(e2,e1);
	      else
		call_unify_fn(e1,e2);
	    }
	  
	}
    } 
}

// given x <= proj(c,i,e)
// proj_merge(region,e,get_proj_i_arg,fresh_large_fn_ptr,
// sort_inclusion_fn_ptr,set_inclusion)
static bool nonspec_res_proj(set_var v1,gen_e e2)
{
  proj_pat projection_pat = (proj_pat)e2;
  
  gen_e setif_get_proj(gen_e_list arg1)
    {
      gen_e_list_scanner scan;
      gen_e temp;
      proj_pat pat;

      gen_e_list_scan(arg1,&scan);
      while(gen_e_list_next(&scan,&temp))
	{
	  pat = (proj_pat)temp;
	  if ( pat->type == ((setif_term)e2)->type && 
	       pat->i == ((proj_pat)e2)->i)
	    return pat->exp;
	}
      return NULL;
    }
  
  gen_e fresh_large(void)
    {
      return get_proj_var( ((proj_pat)e2)->exp->sort,TRUE);
    }
  
  bool sort_inclusion(gen_e e1, gen_e e2)
    {
      if ( projection_pat->variance == vnc_pos )
	return call_inclusion_fn(e1,e2);
      else if ( projection_pat->variance == vnc_neg)
	return call_inclusion_fn(e2,e1);
      else 
	return call_unify_fn(e1,e2);
    }
  
  gen_e proj_con(gen_e e)
    {
      return make_proj_pat( ((proj_pat)e2)->c, ((proj_pat)e2)->i,e);
    }
  
  return setif_proj_merge(setif_region,v1,((proj_pat)e2)->exp,
			  setif_get_proj,proj_con,
			  fresh_large,sort_inclusion,
			  call_setif_inclusion);
  
}


void call_setif_inclusion(gen_e e1,gen_e e2)
{
  setif_inclusion(setif_con_match,setif_res_proj,e1,e2);
}

void call_setif_unify(gen_e e1, gen_e e2)
{
  setif_inclusion(setif_con_match,setif_res_proj,e1,e2);
  setif_inclusion(setif_con_match,setif_res_proj,e2,e1);
}

void call_setst_inclusion(gen_e e1, gen_e e2)
{
  setst_inclusion(setst_con_match,e1,e2);
}

void call_setst_unify(gen_e e1, gen_e e2)
{
  setst_inclusion(setst_con_match,e1,e2);
  setst_inclusion(setst_con_match,e2,e1);
}

void call_flowrow_inclusion(gen_e e1,gen_e e2)
{

  if ( (e1->sort != flowrow_sort) || (e2->sort != flowrow_sort) )
    failure("Constraint system is not well-sorted\n");

  if ( flowrow_base_sort(e1) != flowrow_base_sert(e2))
    failure("Constraint system is not well-sorted\n");


  flowrow_inclusion(fresh,get_stamp,field_incl,zero_elem,e1,e2);
}

void call_flowrow_unify(gen_e e1, gen_e e2)
{
  flowrow_inclusion(fresh,get_stamp,field_incl,zero_elem,e1,e2);
  flowrow_inclusion(fresh,get_stamp,field_incl,zero_elem,e2,e1);
}

static void term_con_match(gen_e e1, gen_e e2)
{
  cons_expr c1 = (cons_expr)e1,
    c2 = (cons_expr)e2;
  
  if (c1->type != c2->type)
    failure("Constructor mismatch\n");
  else
    {
      int i;
      for (i = 0; i < c1->arity; i++)
	{
	  call_unify_fn(e1,e2);
	}
      
    }
}

static void term_occurs(term_var v, gen_e e)
{
  gen_e ecr = term_get_ecr(e);
  
  if (((gen_term)ecr)->type == VAR_TYPE)
    return ( term_get_stamp((gen_e)v) == term_get_stamp(e) );

  else if (((gen_term)ecr)->type >= 10)
    {
      cons_expr c_e = (cons_expr) e;
      int i;
      for (int i = 0; i < arity; i++)
	{
	  if (term_occurs(v,c->exps[i]))
	    return TRUE;
	}
    }
  
  return FALSE;
}

void call_term_unify(gen_e e1, gen_e e2)
{
  term_unify(term_con_match,term_occurs,e1,e2);
}

void call_term_cunify(gen_e e1, gen_e e2)
{
  term_cunify(term_con_match,term_occurs,e1,e2);
}


static void call_inclusion_fn(gen_e e1, gen_e e2)
{
  switch (e1->sort)
    {
    case sort_setif:
      {
	setif_inclusion(setif_con_match,setif_res_proj,e1,e2);
      }
      break;
    case sort_setst:
      {
	setst_inclusion(setst_con_match,e1,e2);
      }
      break;
    case sort_term:
      {
	term_unify(term_con_match,term_occurs,e1,e2);
      }    
      break;
    case sort_row:
      {
	/* TODO */
	flowrow_inclusion(fresh,get_stamp,field_incl,zero_elem,e1,e2);
      }
      break;
    default :
      fail("Unmatched sort in call inclusion\n");
    }
}

static bool call_unify_fn(gen_e e1, gen_e e2)
{

  switch (e1->sort)
    {
    case sort_setif:
      {
	setif_inclusion(setif_con_match,setif_res_proj,e1,e2);
	setif_inclusion(setif_con_match,setif_res_proj,e2,e1);
      }
      break;
    case sort_setst:
      {
	setst_inclusion(setst_con_match,e1,e2);
	setst_inclusion(setst_con_match,e2,e1);
      }
      break;
    case sort_term:
      {
	term_unify(term_con_match,term_occurs,e1,e2);
      }    
      break;
    case sort_row:
      {
	/* TODO */
	flowrow_inclusion(fresh,get_stamp,field_incl,zero_elem,e1,e2);
	flowrow_inclusion(fresh,get_stamp,field_incl,zero_elem,e2,e1);
      }
      break;
    default :
      fail("Unmatched sort in call inclusion\n");
    }
}

void nonspec_init(void)
{
  banshee_init();
  setif_init();
  setst_init();
  flowrow_init();
}

void nonspec_reset(void)
{
  flowrow_reset();
  setst_reset();
  setif_reset();
  banshee_reset();
}

void expr_print(FILE *f,gen_e e)
{

}