--- ray/src/common/caldefn.c 1990/07/19 11:15:31 1.5
+++ ray/src/common/caldefn.c 2003/02/22 02:07:21 2.12
@@ -1,9 +1,6 @@
-/* Copyright (c) 1986 Regents of the University of California */
-
#ifndef lint
-static char SCCSid[] = "$SunId$ LBL";
+static const char RCSid[] = "$Id: caldefn.c,v 2.12 2003/02/22 02:07:21 greg Exp $";
#endif
-
/*
* Store variable definitions.
*
@@ -18,44 +15,112 @@ static char SCCSid[] = "$SunId$ LBL";
* 11/16/88 Added VARDEF structure for hard linking.
*
* 5/31/90 Added conditional compile (REDEFW) for redefinition warning.
+ *
+ * 4/23/91 Added ':' assignment for constant expressions
+ *
+ * 8/7/91 Added optional context path to append to variable names
+ *
+ * 5/17/2001 Fixed clock counter wrapping behavior
+ *
+ * 2/19/03 Eliminated conditional compiles in favor of esupport extern.
*/
+/* ====================================================================
+ * The Radiance Software License, Version 1.0
+ *
+ * Copyright (c) 1990 - 2002 The Regents of the University of California,
+ * through Lawrence Berkeley National Laboratory. 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. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes Radiance software
+ * (http://radsite.lbl.gov/)
+ * developed by the Lawrence Berkeley National Laboratory
+ * (http://www.lbl.gov/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Radiance," "Lawrence Berkeley National Laboratory"
+ * and "The Regents of the University of California" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact radiance@radsite.lbl.gov.
+ *
+ * 5. Products derived from this software may not be called "Radiance",
+ * nor may "Radiance" appear in their name, without prior written
+ * permission of Lawrence Berkeley National Laboratory.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 Lawrence Berkeley National Laboratory OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of Lawrence Berkeley National Laboratory. For more
+ * information on Lawrence Berkeley National Laboratory, please see
+ * .
+ */
+
#include
+#include
+
#include
#include "calcomp.h"
-#ifndef NHASH
-#define NHASH 521 /* hash size (a prime!) */
+#ifndef NHASH
+#define NHASH 521 /* hash size (a prime!) */
#endif
-#define newnode() (EPNODE *)ecalloc(1, sizeof(EPNODE))
+#define hash(s) (shash(s)%NHASH)
-extern char *ecalloc(), *savestr();
+#define newnode() (EPNODE *)ecalloc(1, sizeof(EPNODE))
static double dvalue();
-long eclock = -1; /* value storage timer */
+#define MAXCLOCK (1L<<31) /* clock wrap value */
+unsigned long eclock = 0; /* value storage timer */
+
+#define MAXCNTX 1023 /* maximum context length */
+
+static char context[MAXCNTX+1]; /* current context path */
+
static VARDEF *hashtbl[NHASH]; /* definition list */
static int htndx; /* index for */
static VARDEF *htpos; /* ...dfirst() and */
-#ifdef OUTCHAN
static EPNODE *ochpos; /* ...dnext */
static EPNODE *outchan;
-#endif
-#ifdef FUNCTION
-EPNODE *curfunc;
-#define dname(ep) ((ep)->v.kid->type == SYM ? \
+EPNODE *curfunc = NULL;
+#define dname(ep) ((ep)->v.kid->type == SYM ? \
(ep)->v.kid->v.name : \
(ep)->v.kid->v.kid->v.name)
-#else
-#define dname(ep) ((ep)->v.kid->v.name)
-#endif
+void
fcompile(fname) /* get definitions from a file */
char *fname;
{
@@ -70,12 +135,13 @@ char *fname;
}
initfile(fp, fname, 0);
while (nextc != EOF)
- loaddefn();
+ getstatement();
if (fname != NULL)
fclose(fp);
}
+void
scompile(str, fn, ln) /* get definitions from a string */
char *str;
char *fn;
@@ -83,7 +149,7 @@ int ln;
{
initstr(str, fn, ln);
while (nextc != EOF)
- loaddefn();
+ getstatement();
}
@@ -97,7 +163,7 @@ char *vname;
double
evariable(ep) /* evaluate a variable */
-EPNODE *ep;
+EPNODE *ep;
{
register VARDEF *dp = ep->v.ln;
@@ -105,22 +171,28 @@ EPNODE *ep;
}
-varset(vname, val) /* set a variable's value */
+void
+varset(vname, assign, val) /* set a variable's value */
char *vname;
-double val;
+int assign;
+double val;
{
+ char *qname;
register EPNODE *ep1, *ep2;
+ /* get qualified name */
+ qname = qualname(vname, 0);
/* check for quick set */
- if ((ep1 = dlookup(vname)) != NULL && ep1->v.kid->type == SYM) {
+ if ((ep1 = dlookup(qname)) != NULL && ep1->v.kid->type == SYM) {
ep2 = ep1->v.kid->sibling;
if (ep2->type == NUM) {
ep2->v.num = val;
+ ep1->type = assign;
return;
}
}
/* hand build definition */
ep1 = newnode();
- ep1->type = '=';
+ ep1->type = assign;
ep2 = newnode();
ep2->type = SYM;
ep2->v.name = savestr(vname);
@@ -129,21 +201,39 @@ double val;
ep2->type = NUM;
ep2->v.num = val;
addekid(ep1, ep2);
- dclear(vname);
- dpush(ep1);
+ dremove(qname);
+ dpush(qname, ep1);
}
-dclear(name) /* delete all definitions of name */
+void
+dclear(name) /* delete variable definitions of name */
char *name;
{
register EPNODE *ep;
+ while ((ep = dpop(name)) != NULL) {
+ if (ep->type == ':') {
+ dpush(name, ep); /* don't clear constants */
+ return;
+ }
+ epfree(ep);
+ }
+}
+
+
+void
+dremove(name) /* delete all definitions of name */
+char *name;
+{
+ register EPNODE *ep;
+
while ((ep = dpop(name)) != NULL)
epfree(ep);
}
+int
vardefined(name) /* return non-zero if variable defined */
char *name;
{
@@ -153,32 +243,165 @@ char *name;
}
-#ifdef OUTCHAN
-chanout() /* set output channels */
+char *
+setcontext(ctx) /* set a new context path */
+register char *ctx;
{
+ register char *cpp;
+
+ if (ctx == NULL)
+ return(context); /* just asking */
+ while (*ctx == CNTXMARK)
+ ctx++; /* skip past marks */
+ if (!*ctx) {
+ context[0] = '\0'; /* empty means clear context */
+ return(context);
+ }
+ cpp = context; /* start context with mark */
+ *cpp++ = CNTXMARK;
+ do { /* carefully copy new context */
+ if (cpp >= context+MAXCNTX)
+ break; /* just copy what we can */
+ if (isid(*ctx))
+ *cpp++ = *ctx++;
+ else {
+ *cpp++ = '_'; ctx++;
+ }
+ } while (*ctx);
+ while (cpp[-1] == CNTXMARK) /* cannot end in context mark */
+ cpp--;
+ *cpp = '\0';
+ return(context);
+}
+
+
+char *
+pushcontext(ctx) /* push on another context */
+char *ctx;
+{
+ extern char *strncpy(), *strcpy();
+ char oldcontext[MAXCNTX+1];
+ register int n;
+
+ strcpy(oldcontext, context); /* save old context */
+ setcontext(ctx); /* set new context */
+ n = strlen(context); /* tack on old */
+ if (n+strlen(oldcontext) > MAXCNTX) {
+ strncpy(context+n, oldcontext, MAXCNTX-n);
+ context[MAXCNTX] = '\0';
+ } else
+ strcpy(context+n, oldcontext);
+ return(context);
+}
+
+
+char *
+popcontext() /* pop off top context */
+{
+ register char *cp1, *cp2;
+
+ if (!context[0]) /* nothing left to pop */
+ return(context);
+ cp2 = context; /* find mark */
+ while (*++cp2 && *cp2 != CNTXMARK)
+ ;
+ cp1 = context; /* copy tail to front */
+ while (*cp1++ = *cp2++)
+ ;
+ return(context);
+}
+
+
+char *
+qualname(nam, lvl) /* get qualified name */
+register char *nam;
+int lvl;
+{
+ static char nambuf[MAXWORD+1];
+ register char *cp = nambuf, *cpp;
+ /* check for explicit local */
+ if (*nam == CNTXMARK)
+ if (lvl > 0) /* only action is to refuse search */
+ return(NULL);
+ else
+ nam++;
+ else if (nam == nambuf) /* check for repeat call */
+ return(lvl > 0 ? NULL : nam);
+ /* copy name to static buffer */
+ while (*nam) {
+ if (cp >= nambuf+MAXWORD)
+ goto toolong;
+ *cp++ = *nam++;
+ }
+ /* check for explicit global */
+ if (cp > nambuf && cp[-1] == CNTXMARK) {
+ if (lvl > 0)
+ return(NULL);
+ *--cp = '\0';
+ return(nambuf); /* already qualified */
+ }
+ cpp = context; /* else skip the requested levels */
+ while (lvl-- > 0) {
+ if (!*cpp)
+ return(NULL); /* return NULL if past global level */
+ while (*++cpp && *cpp != CNTXMARK)
+ ;
+ }
+ while (*cpp) { /* copy context to static buffer */
+ if (cp >= nambuf+MAXWORD)
+ goto toolong;
+ *cp++ = *cpp++;
+ }
+toolong:
+ *cp = '\0';
+ return(nambuf); /* return qualified name */
+}
+
+
+int
+incontext(qn) /* is qualified name in current context? */
+register char *qn;
+{
+ if (!context[0]) /* global context accepts all */
+ return(1);
+ while (*qn && *qn != CNTXMARK) /* find context mark */
+ qn++;
+ return(!strcmp(qn, context));
+}
+
+
+void
+chanout(cs) /* set output channels */
+int (*cs)();
+{
register EPNODE *ep;
for (ep = outchan; ep != NULL; ep = ep->sibling)
- chanset(ep->v.kid->v.chan, evalue(ep->v.kid->sibling));
+ (*cs)(ep->v.kid->v.chan, evalue(ep->v.kid->sibling));
}
-#endif
-dclearall() /* clear all definitions */
+void
+dcleanup(lvl) /* clear definitions (0->vars,1->output,2->consts) */
+int lvl;
{
register int i;
register VARDEF *vp;
register EPNODE *ep;
-
+ /* if context is global, clear all */
for (i = 0; i < NHASH; i++)
for (vp = hashtbl[i]; vp != NULL; vp = vp->next)
- dclear(vp->name);
-#ifdef OUTCHAN
- for (ep = outchan; ep != NULL; ep = ep->sibling)
- epfree(ep);
- outchan = NULL;
-#endif
+ if (incontext(vp->name))
+ if (lvl >= 2)
+ dremove(vp->name);
+ else
+ dclear(vp->name);
+ if (lvl >= 1) {
+ for (ep = outchan; ep != NULL; ep = ep->sibling)
+ epfree(ep);
+ outchan = NULL;
+ }
}
@@ -189,7 +412,7 @@ char *name;
register VARDEF *vp;
if ((vp = varlookup(name)) == NULL)
- return(NULL);
+ return(NULL);
return(vp->def);
}
@@ -198,11 +421,14 @@ VARDEF *
varlookup(name) /* look up a variable */
char *name;
{
+ int lvl = 0;
+ register char *qname;
register VARDEF *vp;
-
- for (vp = hashtbl[hash(name)]; vp != NULL; vp = vp->next)
- if (!strcmp(vp->name, name))
- return(vp);
+ /* find most qualified match */
+ while ((qname = qualname(name, lvl++)) != NULL)
+ for (vp = hashtbl[hash(qname)]; vp != NULL; vp = vp->next)
+ if (!strcmp(vp->name, qname))
+ return(vp);
return(NULL);
}
@@ -212,42 +438,58 @@ varinsert(name) /* get a link to a variable */
char *name;
{
register VARDEF *vp;
- int hv;
+ int hv;
- hv = hash(name);
- for (vp = hashtbl[hv]; vp != NULL; vp = vp->next)
- if (!strcmp(vp->name, name)) {
- vp->nlinks++;
- return(vp);
- }
+ if ((vp = varlookup(name)) != NULL) {
+ vp->nlinks++;
+ return(vp);
+ }
vp = (VARDEF *)emalloc(sizeof(VARDEF));
+ vp->lib = liblookup(name);
+ if (vp->lib == NULL) /* if name not in library */
+ name = qualname(name, 0); /* use fully qualified version */
+ hv = hash(name);
vp->name = savestr(name);
vp->nlinks = 1;
vp->def = NULL;
- vp->lib = NULL;
vp->next = hashtbl[hv];
hashtbl[hv] = vp;
return(vp);
}
+void
+libupdate(fn) /* update library links */
+char *fn;
+{
+ register int i;
+ register VARDEF *vp;
+ /* if fn is NULL then relink all */
+ for (i = 0; i < NHASH; i++)
+ for (vp = hashtbl[i]; vp != NULL; vp = vp->next)
+ if (vp->lib != NULL || fn == NULL || !strcmp(fn, vp->name))
+ vp->lib = liblookup(vp->name);
+}
+
+
+void
varfree(ln) /* release link to variable */
-register VARDEF *ln;
+register VARDEF *ln;
{
register VARDEF *vp;
- int hv;
+ int hv;
if (--ln->nlinks > 0)
- return; /* still active */
+ return; /* still active */
hv = hash(ln->name);
vp = hashtbl[hv];
if (vp == ln)
- hashtbl[hv] = vp->next;
+ hashtbl[hv] = vp->next;
else {
- while (vp->next != ln) /* must be in list */
- vp = vp->next;
- vp->next = ln->next;
+ while (vp->next != ln) /* must be in list */
+ vp = vp->next;
+ vp->next = ln->next;
}
freestr(ln->name);
efree((char *)ln);
@@ -259,9 +501,7 @@ dfirst() /* return pointer to first definition */
{
htndx = 0;
htpos = NULL;
-#ifdef OUTCHAN
ochpos = outchan;
-#endif
return(dnext());
}
@@ -270,24 +510,22 @@ EPNODE *
dnext() /* return pointer to next definition */
{
register EPNODE *ep;
+ register char *nm;
while (htndx < NHASH) {
- if (htpos == NULL)
- htpos = hashtbl[htndx++];
- while (htpos != NULL) {
- ep = htpos->def;
- htpos = htpos->next;
- if (ep != NULL)
- return(ep);
- }
+ if (htpos == NULL)
+ htpos = hashtbl[htndx++];
+ while (htpos != NULL) {
+ ep = htpos->def;
+ nm = htpos->name;
+ htpos = htpos->next;
+ if (ep != NULL && incontext(nm))
+ return(ep);
+ }
}
-#ifdef OUTCHAN
if ((ep = ochpos) != NULL)
- ochpos = ep->sibling;
+ ochpos = ep->sibling;
return(ep);
-#else
- return(NULL);
-#endif
}
@@ -299,7 +537,7 @@ char *name;
register EPNODE *dp;
if ((vp = varlookup(name)) == NULL || vp->def == NULL)
- return(NULL);
+ return(NULL);
dp = vp->def;
vp->def = dp->sibling;
varfree(vp);
@@ -307,22 +545,24 @@ char *name;
}
-dpush(ep) /* push on a definition */
-register EPNODE *ep;
+void
+dpush(nm, ep) /* push on a definition */
+char *nm;
+register EPNODE *ep;
{
register VARDEF *vp;
- vp = varinsert(dname(ep));
+ vp = varinsert(nm);
ep->sibling = vp->def;
vp->def = ep;
}
-#ifdef OUTCHAN
+void
addchan(sp) /* add an output channel assignment */
-EPNODE *sp;
+EPNODE *sp;
{
- int ch = sp->v.kid->v.chan;
+ int ch = sp->v.kid->v.chan;
register EPNODE *ep, *epl;
for (epl = NULL, ep = outchan; ep != NULL; epl = ep, ep = ep->sibling)
@@ -346,35 +586,42 @@ EPNODE *sp;
sp->sibling = NULL;
}
-#endif
-loaddefn() /* load next definition */
+void
+getstatement() /* get next statement */
{
register EPNODE *ep;
+ char *qname;
+ register VARDEF *vdef;
if (nextc == ';') { /* empty statement */
scan();
return;
}
-#ifdef OUTCHAN
- if (nextc == '$') { /* channel assignment */
+ if (esupport&E_OUTCHAN &&
+ nextc == '$') { /* channel assignment */
ep = getchan();
addchan(ep);
- } else
-#endif
- { /* ordinary definition */
+ } else { /* ordinary definition */
ep = getdefn();
-#ifdef REDEFW
- if (dlookup(dname(ep)) != NULL) {
- dclear(dname(ep));
- wputs(dname(ep));
- wputs(": redefined\n");
- }
-#else
- dclear(dname(ep));
-#endif
- dpush(ep);
+ qname = qualname(dname(ep), 0);
+ if (esupport&E_REDEFW && (vdef = varlookup(qname)) != NULL)
+ if (vdef->def != NULL && epcmp(ep, vdef->def)) {
+ wputs(qname);
+ if (vdef->def->type == ':')
+ wputs(": redefined constant expression\n");
+ else
+ wputs(": redefined\n");
+ } else if (ep->v.kid->type == FUNC && vdef->lib != NULL) {
+ wputs(qname);
+ wputs(": definition hides library function\n");
+ }
+ if (ep->type == ':')
+ dremove(qname);
+ else
+ dclear(qname);
+ dpush(qname, ep);
}
if (nextc != EOF) {
if (nextc != ';')
@@ -386,19 +633,20 @@ loaddefn() /* load next definition */
EPNODE *
getdefn() /* A -> SYM = E1 */
- /* FUNC(SYM,..) = E1 */
+ /* SYM : E1 */
+ /* FUNC(SYM,..) = E1 */
+ /* FUNC(SYM,..) : E1 */
{
register EPNODE *ep1, *ep2;
- if (!isalpha(nextc))
+ if (!isalpha(nextc) && nextc != CNTXMARK)
syntax("illegal variable name");
ep1 = newnode();
ep1->type = SYM;
ep1->v.name = savestr(getname());
-#ifdef FUNCTION
- if (nextc == '(') {
+ if (esupport&E_FUNCTION && nextc == '(') {
ep2 = newnode();
ep2->type = FUNC;
addekid(ep2, ep1);
@@ -416,37 +664,32 @@ getdefn() /* A -> SYM = E1 */
syntax("')' expected");
scan();
curfunc = ep1;
- } else
- curfunc = NULL;
-#endif
+ }
- if (nextc != '=')
- syntax("'=' expected");
- scan();
+ if (nextc != '=' && nextc != ':')
+ syntax("'=' or ':' expected");
ep2 = newnode();
- ep2->type = '=';
+ ep2->type = nextc;
+ scan();
addekid(ep2, ep1);
addekid(ep2, getE1());
-#ifdef FUNCTION
- if (ep1->type == SYM)
-#endif
- {
+ if (ep1->type == SYM && ep1->sibling->type != NUM) {
ep1 = newnode();
ep1->type = TICK;
- ep1->v.tick = -1;
+ ep1->v.tick = 0;
addekid(ep2, ep1);
ep1 = newnode();
ep1->type = NUM;
addekid(ep2, ep1);
}
+ curfunc = NULL;
return(ep2);
}
-#ifdef OUTCHAN
EPNODE *
getchan() /* A -> $N = E1 */
{
@@ -471,7 +714,6 @@ getchan() /* A -> $N = E1 */
return(ep2);
}
-#endif
@@ -483,7 +725,7 @@ getchan() /* A -> $N = E1 */
static double
dvalue(name, d) /* evaluate a variable */
char *name;
-EPNODE *d;
+EPNODE *d;
{
register EPNODE *ep1, *ep2;
@@ -496,25 +738,15 @@ EPNODE *d;
if (ep1->type == NUM)
return(ep1->v.num); /* return if number */
ep2 = ep1->sibling; /* check time */
- if (ep2->v.tick < 0 || ep2->v.tick < eclock) {
- ep2->v.tick = eclock;
+ if (eclock >= MAXCLOCK)
+ eclock = 1; /* wrap clock counter */
+ if (ep2->v.tick < MAXCLOCK &&
+ ep2->v.tick == 0 | ep2->v.tick != eclock) {
+ ep2->v.tick = d->type == ':' ? MAXCLOCK : eclock;
ep2 = ep2->sibling;
ep2->v.num = evalue(ep1); /* needs new value */
} else
ep2 = ep2->sibling; /* else reuse old value */
return(ep2->v.num);
-}
-
-
-static int
-hash(s) /* hash a string */
-register char *s;
-{
- register int rval = 0;
-
- while (*s)
- rval += *s++;
-
- return(rval % NHASH);
}