/*	Copyright (C) 1992 Free Software Foundation, Inc.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this software; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "rx.h"
#include "sp.h"

#ifdef __STDC__
extern void *malloc (int);
extern void *calloc (int, int);
extern void *realloc (void *, int);
extern void free (void *);
#else
extern void *malloc ();
extern void *calloc ();
extern void *realloc ();
extern void free ();
#endif

#define remalloc(M, S) (M ? realloc (M, S) : malloc (S))
#define bitset(N) ((ut_Bitset)calloc (ut_sizeof_bitset (N), sizeof(UT_subset)))


const char *rx_msg[] =
{
  0,				/* REG_NORX */
  "No match",			/* REG_NOMATCH */
  "Invalid regular expression",	/* REG_BADPAT */
  "Invalid collation character",/* REG_ECOLLATE */
  "Invalid character class name",	/* REG_ECTYPE */
  "Trailing backslash",		/* REG_EESCAPE */
  "Invalid back reference",	/* REG_ESUBREG */
  "Unmatched [ or [^",		/* REG_EBRACK */
  "Unmatched ( or \\(",		/* REG_EPAREN */
  "Unmatched \\{",		/* REG_EBRACE */
  "Invalid content of \\{\\}",	/* REG_BADBR */
  "Invalid range end",		/* REG_ERANGE */
  "Memory exhausted",		/* REG_ESPACE */
  "Invalid preceding regular expression",	/* REG_BADRPT */
  "Premature end of regular expression",	/* REG_EEND */
  "Regular expression too big",	/* REG_ESIZE */
  "Unmatched ) or \\)",		/* REG_ERPAREN */
};


ut_Bitset
cset (rx)
     struct rx *rx;
{
  ut_Bitset b = (ut_Bitset) calloc (1, ut_sizeof_bitset (rx->local_cset_size));
  return b;
}

ut_Bitset
copy_cset (rx, a)
     struct rx *rx;
     ut_Bitset a;
{
  ut_Bitset cs = cset (rx);

  if (cs)
    ut_bitset_union (rx->local_cset_size, cs, a);

  return cs;
}

void
free_cset (c)
     ut_Bitset c;
{
  if (c)
    free (c);
}

#if RXDEBUG
/* shared debugging code */

static
void print_cset (rx, cset, fp)
     struct rx *rx;
     ut_Bitset cset;
     FILE * fp;
{
  int x;
  fputc ('[', fp);
  for (x = 0; x < rx->local_cset_size; ++x)
    if (isprint(x) && UT_bitset_member (cset, x))
      fputc (x, fp);
  fputc (']', fp);
}

#endif

struct rexp_node *
rexp_node (rx, type)
     struct rx *rx;
     enum rexp_node_type type;
{
  struct rexp_node *n = (struct rexp_node *) calloc (1, sizeof (*n));

  if (n)
    n->type = type;
  return n;
}

extern void
free_rexp (node)
     struct rexp_node * node;
{
  if (node)
    {
      switch (node->type)
	{
	case r_cset:
	  if (node->params.cset)
	    free (node->params.cset);

	case r_side_effect:
	  break;
	  
	case r_concat:
	case r_alternate:
	case r_opt:
	case r_star:
	  free_rexp (node->params.pair.right);
	  break;
	}
      free (node);
    }
}

struct rexp_node * 
copy_rexp (rx, node)
     struct rx *rx;
     struct rexp_node *node;
{
  if (!node)
    return 0;
  else
    {
      struct rexp_node *n = rexp_node (rx, node->type);
      if (!n)
	return 0;
      switch (node->type)
	{
	case r_cset:
	  n->params.cset = copy_cset (rx, node->params.cset);
	  if (!n->params.cset)
	    {
	      free_rexp (n);
	      return 0;
	    }
	  break;

	case r_side_effect:
	  n->params.side_effect = node->params.side_effect;
	  break;

	case r_concat:
	case r_alternate:
	case r_opt:
	case r_star:
	  n->params.pair.left =
	    copy_rexp (rx, node->params.pair.left);
	  n->params.pair.right =
	    copy_rexp (rx, node->params.pair.right);
	  if (!n->params.pair.left || !n->params.pair.right)
	    {
	      free_rexp  (n);
	      return 0;
	    }
	}
      return n;
    }
}


#if RXDEBUG
extern void
print_rexp (rx, node, depth, seprint, fp)
     struct rx *rx;
     struct rexp_node *node;
     int depth;
     side_effect_printer seprint;
     FILE * fp;
{
  if (!node)
    return;
  else
    {
      switch (node->type)
	{
	case r_cset:
	  {
	    fprintf (fp, "%*s", depth, "");
	    print_cset (rx, node->params.cset, fp);
	    fputc ('\n', fp);
	    break;
	  }

 	case r_opt:
	case r_star:
	  fprintf (fp, "%*s%s\n", depth, "",
		   node->type == r_opt ? "opt" : "star");
	  print_rexp (rx, node->params.pair.left, depth + 3, seprint, fp);
	  break;

	case r_alternate:
	case r_concat:
	  fprintf (fp, "%*s%s\n", depth, "",
		   node->type == r_alternate ? "alt" : "concat");
	  print_rexp (rx, node->params.pair.left, depth + 3, seprint, fp);
	  print_rexp (rx, node->params.pair.right, depth + 3, seprint, fp);
	  break;
	case r_side_effect:
	  fprintf (fp, "%*sSide effect: ", depth, "");
	  seprint (rx, node->params.side_effect, fp);
	  fputc ('\n', fp);
	}
    }
}

#endif


struct nfa_state *
nfa_state (rx)
     struct rx *rx;
{
  char * mem = (char *)malloc (sizeof (struct nfa_state));
  struct nfa_state *n = (struct nfa_state *) mem;
  if (n)
    {
      n->next = rx->nfa_states;
      n->id = 0;
      n->edges = 0;
      n->futures = 0;
      n->mark = 0;
      rx->nfa_states = n;
    }
  return n;
}

struct nfa_state * 
id_to_nfa_state (rx, id)
     struct rx * rx;
     int id;
{
  struct nfa_state * n;
  for (n = rx->nfa_states; n; n = n->next)
    if (n->id == id)
      return n;
  return 0;
}


struct nfa_edge * 
nfa_edge (rx, type, start, dest)
     struct rx *rx;
     enum nfa_edge_type type;
     struct nfa_state *start;
     struct nfa_state *dest;
{
  struct nfa_edge *e = (struct nfa_edge *) malloc (sizeof (*e));
  if (e)
    {
      e->next = start->edges;
      start->edges = e;
      e->type = type;
      e->dest = dest;
    }
  return e;
}

void free_nfa (rx)
     struct rx *rx;
{
  struct nfa_state *n;
  struct nfa_edge *e;
  while (rx->nfa_states)
    {
      while (rx->nfa_states->edges)
	{
	  switch (rx->nfa_states->edges->type)
	    {
	    case ne_cset:
	      free_cset (rx->nfa_states->edges->params.cset);
	      break;
	    default:
	      break;
	    }
	  e = rx->nfa_states->edges;
	  rx->nfa_states->edges = rx->nfa_states->edges->next;
	  free (e);
	}
      n = rx->nfa_states;
      rx->nfa_states = rx->nfa_states->next;
      free (n);
    }
}

int build_nfa (rx, rexp, start, end)
     struct rx *rx;
     struct rexp_node *rexp;
     struct nfa_state **start;
     struct nfa_state **end;
{
  struct nfa_edge *edge;
  struct nfa_state *err_free1 = 0;
  struct nfa_state *err_free2 = 0;

  if (!rexp)
    return 0;

  /* Start & end nodes may have been allocated by the parent. */
  *start = *start ? *start : (err_free1 = nfa_state (rx));
  *end = *end ? *end : (err_free2 = nfa_state (rx));

  if (!(start && end))
    {
      if (err_free1)
	free (err_free1);
      if (err_free2)
	free (err_free2);
      return 0;
    }

  switch (rexp->type)
    {
    case r_cset:
      edge = nfa_edge (rx, ne_cset, *start, *end);
      if (!edge)
	return 0;
      edge->params.cset = copy_cset (rx, rexp->params.cset);
      if (!edge->params.cset)
	return 0;
      return 1;
 
    case r_opt:
      return (build_nfa (rx, rexp->params.pair.left, start, end)
	      && nfa_edge (rx, ne_epsilon, *start, *end));

    case r_star:
      return (build_nfa (rx, rexp->params.pair.left, start, end)
	      && nfa_edge (rx, ne_epsilon, *start, *end)
	      && nfa_edge (rx, ne_epsilon, *end, *start));

    case r_concat:
      {
	struct nfa_state *shared = 0;
	return
	  (build_nfa (rx, rexp->params.pair.left, start, &shared)
	   && build_nfa (rx, rexp->params.pair.right, &shared, end));
      }

    case r_alternate:
      {
	struct nfa_state *ls = 0;
	struct nfa_state *le = 0;
	struct nfa_state *rs = 0;
	struct nfa_state *re = 0;
	return (build_nfa (rx, rexp->params.pair.left, &ls, &le)
		&& build_nfa (rx, rexp->params.pair.right, &rs, &re)
		&& nfa_edge (rx, ne_epsilon, *start, ls)
		&& nfa_edge (rx, ne_epsilon, *start, rs)
		&& nfa_edge (rx, ne_epsilon, le, *end)
		&& nfa_edge (rx, ne_epsilon, re, *end));
      }

    case r_side_effect:
      edge = nfa_edge (rx, ne_side_effect, *start, *end);
      if (!edge)
	return 0;
      edge->params.side_effect = rexp->params.side_effect;
      return 1;
    };
}



/* NAME_RX->NFA_STATES identifies all nodes with non-epsilon transitions.
   These nodes can occur in super-states.  Their integer id will correspond
   to a bit position in superstate bitsets. */

int
name_nfa_states (rx)
     struct rx *rx;
{
  struct nfa_state *n = rx->nfa_states;

  rx->nodec = 0;
  rx->epsnodec = -1;

  while (n)
    {
      struct nfa_edge *e = n->edges;

      while (e)
	{
	  switch (e->type)
	    {
	    case ne_epsilon:
	    case ne_side_effect:
	      break;

	    case ne_cset:
	      n->id = rx->nodec++;
	      goto cont;
	    }
	  e = e->next;
	}
      n->id = rx->epsnodec--;
    cont:
      n = n->next;
    }
  rx->epsnodec = -rx->epsnodec;
}

#if RXDEBUG
void
print_nfa (rx, n, seprint, fp)
     struct rx * rx;
     struct nfa_state * n;
     side_effect_printer seprint;
     FILE * fp;
{
  while (n)
    {
      struct nfa_edge *e = n->edges;
      struct possible_future *ec = n->futures;
      fprintf (fp, "node %d %s\n", n->id,
	       n->is_final ? "final" : (n->is_start ? "start" : ""));
      while (e)
	{
	  fprintf (fp, "   edge to %d, ", e->dest->id);
	  switch (e->type)
	    {
	    case ne_epsilon:
	      fprintf (fp, "epsilon\n");
	      break;
	    case ne_side_effect:
	      fprintf (fp, "side effect ");
	      seprint (rx, e->params.side_effect, fp);
	      fputc ('\n', fp);
	      break;
	    case ne_cset:
	      fprintf (fp, "cset ");
	      print_cset (rx, e->params.cset, fp);
	      fputc ('\n', fp);
	      break;
	    }
	  e = e->next;
	}

      while (ec)
	{
	  int x;
	  struct nfa_state_set * s;
	  struct rx_side_effect_list * l;
	  fprintf (fp, "   eclosure to {");
	  for (s = ec->destset; s; s = s->cdr)
	    fprintf (fp, "%d ", s->car->id);
	  fprintf (fp, "} (");
	  for (l = ec->effects; l; l = l->cdr)
	    {
	      seprint (rx, l->car, fp);
	      fputc (' ', fp);
	    }
	  fprintf (fp, ")\n");
	  ec = ec->next;
	}
      n = n->next;
    }
}
#endif


static struct possible_future * 
possible_future (rx, effects)
     struct rx * rx;
     struct rx_side_effect_list * effects;
{
  struct possible_future *ec;
  ec = (struct possible_future *) malloc (sizeof (*ec));
  if (ec)
    {
      ec->destset = 0;
      ec->next = 0;
      ec->effects = effects;
      return ec;
    }
  return 0;
}

static int 
se_list_cmp (va, vb)
     void * va;
     void * vb;
{
  struct rx_side_effect_list * a = (struct rx_side_effect_list *)va;
  struct rx_side_effect_list * b = (struct rx_side_effect_list *)vb;

  return ((va == vb)
	  ? 0
	  : (!va
	     ? -1
	     : (!vb
		? 1
		: (a->car < b->car
		   ? 1
		   : (a->car > b->car
		      ? -1
		      : se_list_cmp ((void *)a->cdr, (void *)b->cdr))))));
}

static struct rx_side_effect_list * 
side_effect_cons (rx, se, list)
     struct rx * rx;
     void * se;
     struct rx_side_effect_list * list;
{
  struct rx_side_effect_list template;
  struct sp_tree_node * node;
  template.car = se;
  template.cdr = list;
  node = sp_tree_make (rx->se_memo, &template);
  if (!node)
    {
      return 0;
    }
  if (node->key == &template)
    {
      struct rx_side_effect_list * l =
	((struct rx_side_effect_list *)
	 (node->key = (void *) malloc (sizeof (struct rx_side_effect_list))));
      if (!l)
	{
	  return 0;
	}
      *l = template;
    }
  return (struct rx_side_effect_list *)node->key;
}

static struct rx_side_effect_list * 
side_effect_add_to_end (rx, se, list)
     struct rx * rx;
     void * se;
     struct rx_side_effect_list * list;
{
  if (!list)
    return side_effect_cons (rx, se, 0);
  else if (se == list->car)
    return list;
  else
    return side_effect_cons (rx, list->car,
			     side_effect_add_to_end (rx, se, list->cdr));
}



static int 
nfa_set_cmp (va, vb)
     void * va;
     void * vb;
{
  struct nfa_state_set * a = (struct nfa_state_set *)va;
  struct nfa_state_set * b = (struct nfa_state_set *)vb;

  return ((va == vb)
	  ? 0
	  : (!va
	     ? -1
	     : (!vb
		? 1
		: (a->car->id < b->car->id
		   ? 1
		   : (a->car->id > b->car->id
		      ? -1
		      : nfa_set_cmp ((void *)a->cdr, (void *)b->cdr))))));
}

struct nfa_state_set * 
nfa_set_cons (rx, state, set)
     struct rx * rx;
     struct nfa_state * state;
     struct nfa_state_set * set;
{
  struct nfa_state_set template;
  struct sp_tree_node * node;
  template.car = state;
  template.cdr = set;
  node = sp_tree_make (rx->nfa_set_memo, &template);
  if (!node)
    return 0;
  if (node->key == &template)
    {
      struct nfa_state_set * l =
	((struct nfa_state_set *)
	 (node->key = (void *) malloc (sizeof (struct nfa_state_set))));
      if (!l)
	return 0;
      *l = template;
    }
  return (struct nfa_state_set *)node->key;
}

struct nfa_state_set * 
nfa_set_enjoin (rx, state, set)
     struct rx * rx;
     struct nfa_state * state;
     struct nfa_state_set * set;
{
  if (!set || state->id < set->car->id)
    return nfa_set_cons (rx, state, set);
  if (state->id == set->car->id)
    return set;
  else
    {
      struct nfa_state_set * newcdr = nfa_set_enjoin (rx, state, set->cdr);
      if (newcdr != set->cdr)
	set = nfa_set_cons (rx, set->car, newcdr);
      return set;
    }
}

struct nfa_state_set * 
nfa_set_union (rx, seta, setb)
     struct rx * rx;
     struct nfa_state_set * seta;
     struct nfa_state_set * setb;
{
  if (!seta)
    return setb;
  if (!setb)
    return seta;
  if (seta->car->id == setb->car->id)
    return nfa_set_union (rx, seta->cdr, setb);
  if (seta->car->id < setb->car->id)
    return nfa_set_cons (rx, seta->car, nfa_set_union (rx, seta->cdr, setb));
  return nfa_set_cons (rx, setb->car, nfa_set_union (rx, seta, setb->cdr));
}



struct eclose_frame
{
  struct rx_side_effect_list *prog;
};

static int 
eclose_node (rx, outnode, node, frame)
     struct rx *rx;
     struct nfa_state *outnode;
     struct nfa_state *node;
     struct eclose_frame *frame;
{
  struct nfa_edge *e = node->edges;

  if (node->mark)
    return 1;
  node->mark = 1;

  if (node->id >= 0 || node->is_final)
    {
      struct possible_future **ec;

      ec = &outnode->futures;

      while (*ec)
	{
	  if ((*ec)->effects == frame->prog)
	    break;
	  ec = &(*ec)->next;
	}
      if (!*ec)
	{
	  *ec = possible_future (rx, frame->prog);
	  if (!*ec)
	    return 0;
	}
      if (node->id >= 0)
	{
	  (*ec)->destset = nfa_set_enjoin (rx, node, (*ec)->destset);
	  if (!(*ec)->destset)
	    return 0;
	}
    }

  while (e)
    {
      switch (e->type)
	{
	case ne_epsilon:
	  if (!eclose_node (rx, outnode, e->dest, frame))
	    return 0;
	  break;
	case ne_side_effect:
	  {
	    struct rx_side_effect_list * old_prog = frame->prog;
	    frame->prog = side_effect_add_to_end (rx, e->params.side_effect,
						  frame->prog);
	    if (!frame->prog)
	      return 0;
	    if (!eclose_node (rx, outnode, e->dest, frame))
	      return 0;
	    frame->prog = old_prog;
	    break;
	  }
	default:
	  break;
	}
      e = e->next;
    }
  return 1;
}


static void 
clear_marks (rx)
     struct rx *rx;
{
  struct nfa_state *n = rx->nfa_states;

  while (n)
    {
      n->mark = 0;
      n = n->next;
    }
}

int 
eclose_nfa (rx)
     struct rx *rx;
{
  struct nfa_state *n = rx->nfa_states;
  struct eclose_frame frame;
  frame.prog = 0;
  rx->se_memo = sp_tree (se_list_cmp);
  rx->nfa_set_memo = sp_tree (nfa_set_cmp);

  if (!rx->se_memo)		/* freed during compactification */
    return 0;
  while (n)
    {
      if (!eclose_node (rx, n, n, &frame))
	return 0;
      clear_marks (rx);
      n = n->next;
    }
  return 1;
}


void 
delete_epsilon_transitions (rx)
     struct rx *rx;
{
  struct nfa_state *n = rx->nfa_states;
  struct nfa_edge **e;

  while (n)
    {
      e = &n->edges;
      while (*e)
	{
	  struct nfa_edge *t;
	  switch ((*e)->type)
	    {
	    case ne_epsilon:
	    case ne_side_effect:
	      t = *e;
	      *e = t->next;
	      free (t);
	      break;

	    default:
	      e = &(*e)->next;
	      break;
	    }
	}
      n = n->next;
    }
}


static int 
nfacmp (a, b)
     struct nfa_state **a;
     struct nfa_state **b;
{
  return (*a == *b
	  ? 0
	  : (((*a)->id < 0) == ((*b)->id < 0)
	     ? (((*a)->id  < (*b)->id) ? -1 : 1)
	     : (((*a)->id < 0)
		? 1 : -1)));
}

static int 
count_nodes (st)
     struct sp_tree_node * st;
{
  return (st
	  ? 1 + count_nodes (st->kids[0]) + count_nodes (st->kids[1])
	  : 0);
}



static void 
se_memo_freer (node)
     struct sp_tree_node * node;
{
  free (node->key);
}

static void 
nfa_set_freer (node)
     struct sp_tree_node * node;
{
  free (node->key);
}

int 
compactify_nfa (rx, mem, size)
     struct rx *rx;
     void **mem;
     unsigned long *size;
{
  int total_nodec;
  struct nfa_state *n;
  int edgec = 0;
  int eclosec = 0;
  int se_list_consc = count_nodes (rx->se_memo->root);
  int nfa_setc = count_nodes (rx->nfa_set_memo->root);
  unsigned long total_size;

  n = rx->nfa_states;
  total_nodec = 0;
  while (n)
    {
      struct nfa_edge *e = n->edges;
      struct possible_future *ec = n->futures;
      ++total_nodec;
      while (e)
	{
	  ++edgec;
	  e = e->next;
	}
      while (ec)
	{
	  ++eclosec;
	  ec = ec->next;
	}
      n = n->next;
    }

  total_size = (total_nodec * sizeof (struct nfa_state)
		+ edgec * ut_sizeof_bitset (rx->local_cset_size)
		+ edgec * sizeof (struct nfa_edge)
		+ nfa_setc * sizeof (struct nfa_state_set)
		+ eclosec * sizeof (struct possible_future)
		+ se_list_consc * sizeof (struct rx_side_effect_list)
		+ rx->reserved);

  if (total_size > *size)
    {
      *mem = remalloc (*mem, total_size);
      if (*mem)
	*size = total_size;
      else
	return 0;
    }

  {
    static struct nfa_state **scratch = 0;
    static int scratch_alloc = 0;
    struct nfa_state *state_base = (struct nfa_state *) * mem;
    struct nfa_state *new_state = state_base;
    struct nfa_edge *new_edge =
      (struct nfa_edge *)
	((char *) state_base + total_nodec * sizeof (struct nfa_state));
    struct rx_side_effect_list * new_se_list =
      (struct rx_side_effect_list *)
	((char *)new_edge + edgec * sizeof (struct nfa_edge));
    struct possible_future *new_close =
      ((struct possible_future *)
       ((char *) new_se_list
	+ se_list_consc * sizeof (struct rx_side_effect_list)));
    struct nfa_state_set * new_nfa_set =
      ((struct nfa_state_set *)
       ((char *)new_close + eclosec * sizeof (struct possible_future)));
    char *new_bitset =
      ((char *) new_nfa_set + nfa_setc * sizeof (struct nfa_state_set));
    int x;
    struct nfa_state *n;

    if (scratch_alloc < total_nodec)
      {
	scratch = ((struct nfa_state **)
		   remalloc (scratch, total_nodec * sizeof (*scratch)));
	if (scratch)
	  scratch_alloc = total_nodec;
	else
	  {
	    scratch_alloc = 0;
	    return 0;
	  }
      }

    for (x = 0, n = rx->nfa_states; n; n = n->next)
      scratch[x++] = n;

    qsort (scratch, total_nodec, sizeof (struct nfa_state *), nfacmp);

    for (x = 0; x < total_nodec; ++x)
      {
	struct possible_future *eclose = scratch[x]->futures;
	struct nfa_edge *edge = scratch[x]->edges;
	struct nfa_state *cn = new_state++;
	cn->futures = 0;
	cn->edges = 0;
	cn->next = (x == total_nodec - 1) ? 0 : (cn + 1);
	cn->id = scratch[x]->id;
	cn->is_final = scratch[x]->is_final;
	cn->is_start = scratch[x]->is_start;
	cn->mark = 0;
	while (edge)
	  {
	    int index = (edge->dest->id < 0
			 ? (total_nodec + edge->dest->id)
			 : edge->dest->id);
	    struct nfa_edge *e = new_edge++;
	    ut_Bitset cset = (ut_Bitset) new_bitset;
	    new_bitset += ut_sizeof_bitset (rx->local_cset_size);
	    ut_bitset_null (rx->local_cset_size, cset);
	    ut_bitset_union (rx->local_cset_size, cset, edge->params.cset);
	    e->next = cn->edges;
	    cn->edges = e;
	    e->type = edge->type;
	    e->dest = state_base + index;
	    e->params.cset = cset;
	    edge = edge->next;
	  }
	while (eclose)
	  {
	    struct possible_future *ec = new_close++;
	    struct sp_tree_node * sp;
	    struct rx_side_effect_list ** sepos;
	    struct rx_side_effect_list * sesrc;
	    struct nfa_state_set * destlst;
	    struct nfa_state_set ** destpos;
	    ec->next = cn->futures;
	    cn->futures = ec;
	    for (sepos = &ec->effects, sesrc = eclose->effects;
		 sesrc;
		 sesrc = sesrc->cdr, sepos = &(*sepos)->cdr)
	      {
		sp = sp_tree_find (rx->se_memo, sesrc);
		if (sp->data)
		  {
		    sesrc = (struct rx_side_effect_list *)sp->data;
		    break;
		  }
		*new_se_list = *sesrc;
		sp->data = (void *)new_se_list;
		*sepos = new_se_list;
		++new_se_list;
	      }
	    *sepos = sesrc;
	    for (destpos = &ec->destset, destlst = eclose->destset;
		 destlst;
		 destpos = &(*destpos)->cdr, destlst = destlst->cdr)
	      {
		sp = sp_tree_find (rx->nfa_set_memo, destlst);
		if (sp->data)
		  {
		    destlst = (struct nfa_state_set *)sp->data;
		    break;
		  }
		*new_nfa_set = *destlst;
		new_nfa_set->car = state_base + destlst->car->id;
		sp->data = (void *)new_nfa_set;
		*destpos = new_nfa_set;
		++new_nfa_set;
	      }
	    *destpos = destlst;
	    eclose = eclose->next;
	  }
      }
  }
  {
    struct nfa_state * ns = rx->nfa_states;

    while (ns)
      {
	{
	  struct nfa_edge * ne = ns->edges;
	  while (ne)
	    {
	      struct nfa_edge * net = ne;
	      if (ne->type == ne_cset)
		free (ne->params.cset);
	      ne = ne->next;
	      free (net);
	    }
	}
	{
	  struct possible_future * pf = ns->futures;
	  while (pf)
	    {
	      struct possible_future * pft = pf;
	      pf = pf->next;
	      free (pft);
	    }
	}
	{
	  struct nfa_state * nst = ns;
	  ns = ns->next;
	  free (nst);
	}
      }
  }
  free_sp_tree (rx->se_memo, se_memo_freer);
  rx->se_memo = 0;
  free_sp_tree (rx->nfa_set_memo, nfa_set_freer);
  rx->nfa_set_memo = 0;
  rx->nfa_states = (struct nfa_state *)*mem;
  return 1;
}
