home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
lifeos2.zip
/
LIFE-1.02
/
SOURCE
/
COPY.C
< prev
next >
Wrap
C/C++ Source or Header
|
1996-06-04
|
19KB
|
732 lines
/* Copyright 1991 Digital Equipment Corporation.
** All Rights Reserved.
*****************************************************************/
/* $Id: copy.c,v 1.2 1994/12/08 23:21:30 duchier Exp $ */
#ifndef lint
static char vcid[] = "$Id: copy.c,v 1.2 1994/12/08 23:21:30 duchier Exp $";
#endif /* lint */
#include "extern.h"
#include "memory.h"
#include "parser.h"
#include "trees.h"
#include "login.h"
#include "copy.h"
/* #include <malloc.h> 11.9 */
jmp_buf env; /* To jump back to main() when copy(..) overflows */
/****************************************************************************/
/* New translation routines for Wild_Life */
/* These routines work for any size structure. */
/* They are based on a hash table with buckets and timestamp. */
/* This allows fast clearing of the table and fast insertion */
/* and lookup. */
/* Size of hash table; must be a power of 2 */
/* A big hash table means it is sparse and therefore fast */
#define HASHSIZE 2048
/* Total number of buckets in initial hash table; */
/* this is dynamically increased if necessary. */
#define NUMBUCKETS 1024
/* Simple hash function */
#define HASH(A) (((long) A + ((long) A >> 3)) & (HASHSIZE-1))
/* Tail of hash bucket */
#define HASHEND (-1)
struct hashbucket {
ptr_psi_term old_value;
ptr_psi_term new_value;
long info;
long next;
};
struct hashentry {
long timestamp;
long bucketindex;
};
static struct hashentry hashtable[HASHSIZE];
static struct hashbucket *hashbuckets; /* Array of buckets */
static long hashtime; /* Currently valid timestamp */
static long hashfree; /* Index into array of buckets */
static long numbuckets; /* Total number of buckets; initially=NUMBUCKETS */
/* #define HASHSTATS 1000 20.8 */
/* long hashstats[HASHSTATS]; 20.8 */
/******** INIT_COPY()
Execute once upon startup of Wild_Life.
*/
void init_copy()
{
long i;
/* for(i=0; i<HASHSTATS; i++) hashstats[i]=0; 20.8 */
for(i=0; i<HASHSIZE; i++) hashtable[i].timestamp = 0;
hashtime = 0;
numbuckets = NUMBUCKETS;
hashbuckets = (struct hashbucket *)
malloc(NUMBUCKETS * sizeof(struct hashbucket));
}
/******** CLEAR_COPY()
Erase the hash table.
This must be done as a prelude to any copying operation.
*/
void clear_copy()
{
hashtime++;
hashfree=0;
}
/******** INSERT_TRANSLATION(a,b,info)
Add the translation of address A to address B in the translation table.
Also add an info field.
*/
/* static */ void insert_translation(a,b,info)
ptr_psi_term a;
ptr_psi_term b;
long info;
{
long index;
long lastbucket;
/* Ensure there are free buckets by doubling their number if necessary */
if (hashfree >= numbuckets) {
numbuckets *= 2;
hashbuckets = (struct hashbucket *)
realloc((void *) hashbuckets, numbuckets * sizeof(struct hashbucket));
/* *** Do error handling here *** */
Traceline("doubled the number of hashbuckets to %d\n", numbuckets);
}
/* Add a bucket to the beginning of the list */
index = HASH(a);
if (hashtable[index].timestamp == hashtime)
lastbucket = hashtable[index].bucketindex;
else {
lastbucket = HASHEND;
hashtable[index].timestamp = hashtime;
}
hashtable[index].bucketindex = hashfree;
hashbuckets[hashfree].old_value = a;
hashbuckets[hashfree].new_value = b;
hashbuckets[hashfree].info = info;
hashbuckets[hashfree].next = lastbucket;
hashfree++;
}
/******** TRANSLATE(a,info)
Get the translation of address A and the info field stored with it.
Return NULL if none is found.
*/
/* static */ ptr_psi_term translate(a,infoptr) /* RM: Jan 27 1993 */
ptr_psi_term a;
long **infoptr;
{
long index;
/* long i; 20.8 */
long bucket;
index = HASH(a);
if (hashtable[index].timestamp != hashtime) return NULL;
bucket = hashtable[index].bucketindex;
/* i=0; 20.8 */
while (bucket != HASHEND && hashbuckets[bucket].old_value != a) {
/* i++; 20.8 */
bucket = hashbuckets[bucket].next;
}
/* hashstats[i]++; 20.8 */
if (bucket != HASHEND) {
*infoptr = &hashbuckets[bucket].info;
return (hashbuckets[bucket].new_value);
}
else
return NULL;
}
/****************************************************************************/
/******** COPY_TREE(t)
Return a pointer to a copy of the binary tree t.
Structure sharing between trees is not preserved by this routine,
this is not a problem seeing that coreferences in the nodes will ensure
coherence.
*/
/* TRUE means: heap_flag==TRUE & only copy to heap those objects not */
/* already on heap, i.e. incremental copy to heap. */
long to_heap;
/* TRUE iff R is on the heap */
#define ONHEAP(R) ((GENERIC)R>=heap_pointer)
/* Allocate a new record on the heap or stack if necessary: */
#define NEW(A,TYPE) (heap_flag==HEAP \
? (to_heap \
? (ONHEAP(A) \
? A \
: HEAP_ALLOC(TYPE) \
) \
: HEAP_ALLOC(TYPE) \
) \
: STACK_ALLOC(TYPE) \
)
/* TRUE iff to_heap is TRUE & work is done, i.e. the term is on the heap. */
#define HEAPDONE(R) (to_heap && ONHEAP(R))
ptr_psi_term copy(); /* Forward declarations */
void mark_quote_c();
static ptr_node copy_tree(t, copy_flag, heap_flag)
ptr_node t;
long copy_flag, heap_flag;
{
ptr_node r;
ptr_psi_term t1,t2;
/* if (t) { RM: Dec 15 1992 this test is useless */
if (HEAPDONE(t)) return t;
r=NEW(t,node);
r->key = t->key;
r->left = (t->left) ? copy_tree(t->left,copy_flag,heap_flag) : NULL;
t1 = (ptr_psi_term)(t->data);
t2 = copy(t1,copy_flag,heap_flag);
r->data = (GENERIC) t2;
r->right = (t->right) ? copy_tree(t->right,copy_flag,heap_flag) : NULL;
/* } else r=NULL; */
return r;
}
/******** COPY(t)
This is the workhorse of the interpreter (alas!).
All copy-related routines are non-interruptible by the garbage collector.
Make a copy in the STACK or in the HEAP of psi_term t, which is located in
the HEAP. A copy is done whenever invoking a rule, so it had better be fast.
This routine uses hash tables with buckets and partial inlining for speed.
The following three versions of copy all rename their variables and return
a completely dereferenced object:
u=exact_copy(t,hf) u is an exact copy of t.
u=quote_copy(t,hf) u is a copy of t that is recursively marked evaluated.
u=eval_copy(t,hf) u is a copy of t that is recursively marked unevaluated.
This version of copy is an incremental copy to the heap. It copies only
those parts of a psi_term that are on the stack, leaving the others
unchanged:
u=inc_heap_copy(t) u is an exact copy of t, on the heap. This is like
hf==HEAP, except that objects already on the heap are
untouched. Relies on no pointers from heap to stack.
hf = heap_flag. hf = HEAP or STACK means allocate in the HEAP or STACK.
Marking eval/uneval is done by modifying the STATUS field of the copied
psi_term.
In eval_copy, a term's status is set to 0 if the term or any subterm needs
evaluation.
Terms are dereferenced when copying them to the heap.
*/
#define EXACT_FLAG 0
#define QUOTE_FLAG 1
#define EVAL_FLAG 2
/* See mark_quote_c: */ /* 15.9 */
#define QUOTE_STUB 3
ptr_psi_term exact_copy(t, heap_flag)
ptr_psi_term t;
long heap_flag;
{ to_heap=FALSE; return (copy(t, EXACT_FLAG, heap_flag)); }
ptr_psi_term quote_copy(t, heap_flag)
ptr_psi_term t;
long heap_flag;
{ to_heap=FALSE; return (copy(t, QUOTE_FLAG, heap_flag)); }
ptr_psi_term eval_copy(t, heap_flag)
ptr_psi_term t;
long heap_flag;
{ to_heap=FALSE; return (copy(t, EVAL_FLAG, heap_flag)); }
/* There is a bug in inc_heap_copy */
ptr_psi_term inc_heap_copy(t)
ptr_psi_term t;
{ to_heap=TRUE; return (copy(t, EXACT_FLAG, TRUE)); }
static long curr_status;
ptr_psi_term copy(t, copy_flag, heap_flag)
ptr_psi_term t;
long copy_flag,heap_flag;
{
ptr_psi_term u;
long old_status;
long local_copy_flag;
long *infoptr;
if (u=t) {
deref_ptr(t); /* Always dereference when copying */
if (HEAPDONE(t)) return t;
u = translate(t,&infoptr);
if (u && *infoptr!=QUOTE_STUB) { /* 24.8 */
/* If it was eval-copied before, then quote it now. */
if (*infoptr==EVAL_FLAG && copy_flag==QUOTE_FLAG) { /* 24.8 25.8 */
mark_quote_c(t,heap_flag);
*infoptr=QUOTE_FLAG; /* I.e. don't touch this term any more */
}
if (copy_flag==EVAL_FLAG) { /* PVR 14.2.94 */
/* If any subterm has zero curr_status (i.e., if u->status==0),
then so does the whole term: */
old_status=curr_status;
curr_status=u->status;
if (curr_status) curr_status=old_status;
}
}
else {
if (heap_pointer-stack_pointer < COPY_THRESHOLD) {
Errorline("psi-term too large -- get a bigger Life!\n");
abort_life(TRUE);
longjmp(env,FALSE); /* Back to main loop */ /* RM: Feb 15 1993 */
}
if (copy_flag==EVAL_FLAG && !t->type->evaluate_args) /* 24.8 25.8 */
local_copy_flag=QUOTE_FLAG; /* All arguments will be quoted 24.8 */
else /* 24.8 */
local_copy_flag=copy_flag;
if (copy_flag==EVAL_FLAG) {
old_status = curr_status;
curr_status = 4;
}
if (u) { /* 15.9 */
*infoptr=QUOTE_FLAG;
local_copy_flag=QUOTE_FLAG;
copy_flag=QUOTE_FLAG;
}
else {
u=NEW(t,psi_term);
insert_translation(t,u,local_copy_flag); /* 24.8 */
}
*u = *t;
u->resid=NULL; /* 24.8 Don't copy residuations */
#ifdef TS
u->time_stamp=global_time_stamp; /* 9.6 */
#endif
if (t->attr_list)
u->attr_list=copy_tree(t->attr_list, local_copy_flag, heap_flag);
if (copy_flag==EVAL_FLAG) {
switch(t->type->type) {
case type:
if (t->type->properties)
curr_status=0;
break;
case function:
curr_status=0;
break;
case global: /* RM: Feb 8 1993 */
curr_status=0;
break;
default:
break;
}
u->status=curr_status;
u->flags=curr_status?QUOTED_TRUE:FALSE; /* 14.9 */
/* If any subterm has zero curr_status,
then so does the whole term: */
if (curr_status) curr_status=old_status;
} else if (copy_flag==QUOTE_FLAG) {
u->status=4;
u->flags=QUOTED_TRUE; /* 14.9 */
}
/* else copy_flag==EXACT_FLAG & u->status=t->status */
if (heap_flag==HEAP) {
if (t->type==cut) u->value=NULL;
} else {
if (t->type==cut) {
u->value=(GENERIC)choice_stack;
Traceline("current choice point is %x\n",choice_stack);
}
}
}
}
return u;
}
/****************************************************************************/
/******** DISTINCT_TREE(t)
Return an exact copy of an attribute tree.
This is used by APPLY in order to build the calling psi-term which is used
for matching.
*/
ptr_node distinct_tree(t)
ptr_node t;
{
ptr_node n;
n=NULL;
if (t) {
n=STACK_ALLOC(node);
n->key=t->key;
n->data=t->data;
n->left=distinct_tree(t->left);
n->right=distinct_tree(t->right);
}
return n;
}
/******** DISTINCT_COPY(t)
Make a distinct copy of T and T's attribute tree, which are identical to T,
only located elsewhere in memory. This is used by apply to build the calling
psi-term which is used for matching. Note that this routine is not
recursive, i.e. it only copies the main functor & the attribute tree.
*/
ptr_psi_term distinct_copy(t)
ptr_psi_term t;
{
ptr_psi_term res;
res=STACK_ALLOC(psi_term);
*res= *t;
#ifdef TS
res->time_stamp=global_time_stamp; /* 9.6 */
#endif
/* res->coref=distinct_copy(t->coref); */
res->attr_list=distinct_tree(t->attr_list);
return res;
}
/****************************************************************************/
/* The new mark_quote to be used from copy. */
extern void mark_quote_tree_c(); /* A forward declaration */
/* Meaning of the info field in the translation table: */
/* With u=translate(t,&infoptr): */
/* If infoptr==QUOTE_FLAG then the whole subgraph from u is quoted. */
/* If infoptr==EVAL_FLAG then anything is possible. */
/* If infoptr==QUOTE_STUB then the term does not exist yet, e.g., there */
/* is a cycle in the term & copy(...) has not created it yet, for */
/* example X:s(L,t(X),R), where non_strict(t), in which R does not */
/* exist when the call to mark_quote_c is done. When the term is */
/* later created, it must be created as quoted. */
/* Mark a psi-term u (which is a copy of t) as completely evaluated. */
/* Only t is given as the argument. */
/* Assumes the psi-term is a copy of another psi-term t, which is made through */
/* eval_copy. Therefore the copy is accessible through the translation table. */
/* Assumes all translation table entries already exist. */
/* The infoptr field is updated so that each subgraph is only traversed once. */
/* This routine is called only from the main copy routine. */
void mark_quote_c(t,heap_flag)
ptr_psi_term t;
long heap_flag;
{
ptr_list l;
long *infoptr;
ptr_psi_term u;
if (t) {
deref_ptr(t);
u=translate(t,&infoptr);
/* assert(u!=NULL); 15.9 */
if (u) {
if (*infoptr==EVAL_FLAG) {
*infoptr=QUOTE_FLAG;
u->status=4;
u->flags=QUOTED_TRUE; /* 14.9 */
mark_quote_tree_c(t->attr_list,heap_flag);
}
}
else { /* u does not exist yet */ /* 15.9 */
/* Create a stub & mark it as to-be-quoted. */
u=NEW(t,psi_term);
insert_translation(t,u,QUOTE_STUB);
}
}
}
void mark_quote_tree_c(n,heap_flag)
ptr_node n;
long heap_flag;
{
if (n) {
mark_quote_tree_c(n->left,heap_flag);
mark_quote_c((ptr_psi_term) (n->data),heap_flag);
mark_quote_tree_c(n->right,heap_flag);
}
}
/****************************************************************************/
/* A (possibly) correct mark_eval and its companion mark_quote. */
/* The translation table is used to record whether a subgraph has already */
/* been quoted or not. */
/* Forward declarations */
void mark_eval_new();
void mark_quote_new();
void mark_eval_tree_new();
void mark_quote_tree_new();
static long mark_nonstrict_flag;
/* Mark a psi-term as to be evaluated (i.e. strict), except for arguments */
/* of a nonstrict term, which are marked quoted. Set status correctly and */
/* propagate zero status upwards. Avoid doing superfluous work: non-shared */
/* terms are traversed once; shared terms are traversed at most twice (this */
/* only occurs if the first occurrence encountered is strict and a later */
/* occurrence is nonstrict). The translation table is used to indicate (1) */
/* whether a term has already been traversed, and if so, (2) whether there */
/* was a nonstrict traversal (in that case, the info field is FALSE). */
void mark_eval(t) /* 24.8 25.8 */
ptr_psi_term t;
{
clear_copy();
mark_nonstrict_flag=FALSE;
mark_eval_new(t);
}
/* Same as above, except that the status is only changed from 0 to 4 when */
/* needed; it is never changed from 4 to 0. */
void mark_nonstrict(t)
ptr_psi_term t;
{
clear_copy();
mark_nonstrict_flag=TRUE;
mark_eval_new(t);
}
/* Mark a term as quoted. */
void mark_quote_new2(t)
ptr_psi_term t;
{
clear_copy();
mark_nonstrict_flag=FALSE;
mark_quote_new(t);
}
void mark_eval_new(t)
ptr_psi_term t;
{
ptr_list l;
long *infoptr,flag;
ptr_psi_term u;
long old_status;
if (t) {
deref_ptr(t);
flag = t->type->evaluate_args;
u=translate(t,&infoptr);
if (u) {
/* Quote the subgraph if it was already copied as to be evaluated. */
if (!flag && *infoptr) {
mark_quote_new(t);
*infoptr=FALSE;
}
/* If any subterm has zero curr_status (i.e., if t->status==0),
then so does the whole term: PVR 14.2.94 */
old_status=curr_status;
curr_status=t->status;
if (curr_status) curr_status=old_status;
}
else {
insert_translation(t,(ptr_psi_term)TRUE,flag);
old_status=curr_status;
curr_status=4;
if (flag) /* 16.9 */
mark_eval_tree_new(t->attr_list);
else
mark_quote_tree_new(t->attr_list);
switch(t->type->type) {
case type:
if (t->type->properties)
curr_status=0;
break;
case function:
curr_status=0;
break;
case global: /* RM: Feb 8 1993 */
curr_status=0;
break;
default:
break;
}
if (mark_nonstrict_flag) { /* 25.8 */
if (curr_status) {
/* Only increase the status, never decrease it: */
t->status=curr_status;
}
}
else {
t->status=curr_status;
t->flags=curr_status?QUOTED_TRUE:FALSE; /* 14.9 */
}
/* If any subterm has zero curr_status, then so does the whole term: */
if (curr_status) curr_status=old_status;
}
}
}
void mark_eval_tree_new(n)
ptr_node n;
{
if (n) {
mark_eval_tree_new(n->left);
mark_eval_new((ptr_psi_term) (n->data));
mark_eval_tree_new(n->right);
}
}
void mark_quote_new(t)
ptr_psi_term t;
{
ptr_list l;
long *infoptr;
ptr_psi_term u;
if (t) {
deref_ptr(t);
u=translate(t,&infoptr);
/* Return if the subgraph is already quoted. */
if (u && !*infoptr) return;
/* Otherwise quote the subgraph */
if (!u) insert_translation(t,(ptr_psi_term)TRUE,FALSE);
else *infoptr = FALSE; /* sanjay */
t->status=4;
t->flags=QUOTED_TRUE; /* 14.9 */
mark_quote_tree_new(t->attr_list);
}
}
void mark_quote_tree_new(n)
ptr_node n;
{
if (n) {
mark_quote_tree_new(n->left);
mark_quote_new((ptr_psi_term) (n->data));
mark_quote_tree_new(n->right);
}
}
/****************************************************************************/
/* A more efficient version of mark_quote */
/* This version avoids using the translation table by setting a 'visited' */
/* in the status field. */
extern void mark_quote_tree(); /* A forward declaration */
/* Mark a psi-term as completely evaluated. */
void mark_quote(t)
ptr_psi_term t;
{
ptr_list l;
if (t && !(t->status&RMASK)) {
t->status = 4;
t->flags=QUOTED_TRUE; /* 14.9 */
t->status |= RMASK;
mark_quote(t->coref);
mark_quote_tree(t->attr_list);
t->status &= ~RMASK;
}
}
void mark_quote_tree(t)
ptr_node t;
{
if (t) {
mark_quote_tree(t->left);
mark_quote((ptr_psi_term) (t->data));
mark_quote_tree(t->right);
}
}
/* Back-trackably mark a psi-term as completely evaluated. */
void bk_mark_quote_tree();
void bk_mark_quote(t)
ptr_psi_term t;
{
ptr_list l;
if (t && !(t->status&RMASK)) {
if(t->status!=4 && (GENERIC)t<heap_pointer)/* RM: Jul 16 1993 */
push_ptr_value(int_ptr,&(t->status));
t->status = 4;
t->flags=QUOTED_TRUE; /* 14.9 */
t->status |= RMASK;
bk_mark_quote(t->coref);
bk_mark_quote_tree(t->attr_list);
t->status &= ~RMASK;
}
}
void bk_mark_quote_tree(t)
ptr_node t;
{
if (t) {
bk_mark_quote_tree(t->left);
bk_mark_quote((ptr_psi_term) (t->data));
bk_mark_quote_tree(t->right);
}
}
/****************************************************************************/