Back to index

radiance  4R0+20100331
caldefn.c
Go to the documentation of this file.
00001 #ifndef lint
00002 static const char    RCSid[] = "$Id: caldefn.c,v 2.24 2010/03/05 17:28:46 greg Exp $";
00003 #endif
00004 /*
00005  *  Store variable definitions.
00006  *
00007  *  7/1/85  Greg Ward
00008  *
00009  *  11/11/85  Added conditional compiles (OUTCHAN) for control output.
00010  *
00011  *  4/2/86  Added conditional compiles for function definitions (FUNCTION).
00012  *
00013  *  1/15/88  Added clock for caching of variable values.
00014  *
00015  *  11/16/88  Added VARDEF structure for hard linking.
00016  *
00017  *  5/31/90  Added conditional compile (REDEFW) for redefinition warning.
00018  *
00019  *  4/23/91  Added ':' assignment for constant expressions
00020  *
00021  *  8/7/91  Added optional context path to append to variable names
00022  *
00023  *  5/17/2001  Fixed clock counter wrapping behavior
00024  *
00025  *  2/19/03   Eliminated conditional compiles in favor of esupport extern.
00026  */
00027 
00028 #include "copyright.h"
00029 
00030 #include  <stdio.h>
00031 #include  <string.h>
00032 #include  <ctype.h>
00033 
00034 #include  "rterror.h"
00035 #include  "rtio.h"
00036 #include  "rtmisc.h"
00037 #include  "calcomp.h"
00038 
00039 #ifndef        NHASH
00040 #define        NHASH        521           /* hash size (a prime!) */
00041 #endif
00042 
00043 #define  hash(s)     (shash(s)%NHASH)
00044 
00045 #define        newnode()    (EPNODE *)ecalloc(1, sizeof(EPNODE))
00046 
00047 static double  dvalue(char  *name, EPNODE *d);
00048 
00049 #define  MAXCLOCK    (1L<<31)      /* clock wrap value */
00050 
00051 unsigned long  eclock = 0;         /* value storage timer */
00052 
00053 #define  MAXCNTX     1023          /* maximum context length */
00054 
00055 static char  context[MAXCNTX+1];   /* current context path */
00056 
00057 static VARDEF  *hashtbl[NHASH];           /* definition list */
00058 static int  htndx;                 /* index for */             
00059 static VARDEF  *htpos;                    /* ...dfirst() and */
00060 static EPNODE  *ochpos;                   /* ...dnext */
00061 static EPNODE  *outchan;
00062 
00063 EPNODE *curfunc = NULL;
00064 #define        dname(ep)    ((ep)->v.kid->type == SYM ? \
00065                      (ep)->v.kid->v.name : \
00066                      (ep)->v.kid->v.kid->v.name)
00067 
00068 
00069 void
00070 fcompile(                   /* get definitions from a file */
00071        char  *fname
00072 )
00073 {
00074     FILE  *fp;
00075 
00076     if (fname == NULL)
00077        fp = stdin;
00078     else if ((fp = fopen(fname, "r")) == NULL) {
00079        eputs(fname);
00080        eputs(": cannot open\n");
00081        quit(1);
00082     }
00083     initfile(fp, fname, 0);
00084     while (nextc != EOF)
00085        getstatement();
00086     if (fname != NULL)
00087        fclose(fp);
00088 }
00089 
00090 
00091 void
00092 scompile(            /* get definitions from a string */
00093        char  *str,
00094        char  *fn,
00095        int  ln
00096 )
00097 {
00098     initstr(str, fn, ln);
00099     while (nextc != EOF)
00100        getstatement();
00101 }
00102 
00103 
00104 double
00105 varvalue(                   /* return a variable's value */
00106        char  *vname
00107 )
00108 {
00109     return(dvalue(vname, dlookup(vname)));
00110 }
00111 
00112 
00113 double
00114 evariable(                  /* evaluate a variable */
00115        EPNODE *ep
00116 )
00117 {
00118     register VARDEF  *dp = ep->v.ln;
00119 
00120     return(dvalue(dp->name, dp->def));
00121 }
00122 
00123 
00124 void
00125 varset(              /* set a variable's value */
00126        char  *vname,
00127        int  assign,
00128        double val
00129 )
00130 {
00131     char  *qname;
00132     register EPNODE  *ep1, *ep2;
00133                                    /* get qualified name */
00134     qname = qualname(vname, 0);
00135                                    /* check for quick set */
00136     if ((ep1 = dlookup(qname)) != NULL && ep1->v.kid->type == SYM) {
00137        ep2 = ep1->v.kid->sibling;
00138        if (ep2->type == NUM) {
00139            ep2->v.num = val;
00140            ep1->type = assign;
00141            return;
00142        }
00143     }
00144                                    /* hand build definition */
00145     ep1 = newnode();
00146     ep1->type = assign;
00147     ep2 = newnode();
00148     ep2->type = SYM;
00149     ep2->v.name = savestr(vname);
00150     addekid(ep1, ep2);
00151     ep2 = newnode();
00152     ep2->type = NUM;
00153     ep2->v.num = val;
00154     addekid(ep1, ep2);
00155     dremove(qname);
00156     dpush(qname, ep1);
00157 }
00158 
00159 
00160 void
00161 dclear(                     /* delete variable definitions of name */
00162        char  *name
00163 )
00164 {
00165     register EPNODE  *ep;
00166 
00167     while ((ep = dpop(name)) != NULL) {
00168        if (ep->type == ':') {
00169            dpush(name, ep);        /* don't clear constants */
00170            return;
00171        }
00172        epfree(ep);
00173     }
00174 }
00175 
00176 
00177 void
00178 dremove(                    /* delete all definitions of name */
00179        char  *name
00180 )
00181 {
00182     register EPNODE  *ep;
00183 
00184     while ((ep = dpop(name)) != NULL)
00185        epfree(ep);
00186 }
00187 
00188 
00189 int
00190 vardefined(   /* return non-zero if variable defined */
00191        char  *name
00192 )
00193 {
00194     register EPNODE  *dp;
00195 
00196     return((dp = dlookup(name)) != NULL && dp->v.kid->type == SYM);
00197 }
00198 
00199 
00200 char *
00201 setcontext(                 /* set a new context path */
00202        register char  *ctx
00203 )
00204 {
00205     register char  *cpp;
00206 
00207     if (ctx == NULL)
00208        return(context);            /* just asking */
00209     while (*ctx == CNTXMARK)
00210        ctx++;                      /* skip past marks */
00211     if (!*ctx) {
00212        context[0] = '\0';          /* empty means clear context */
00213        return(context);
00214     }
00215     cpp = context;                 /* start context with mark */
00216     *cpp++ = CNTXMARK;
00217     do {                           /* carefully copy new context */
00218        if (cpp >= context+MAXCNTX)
00219            break;                  /* just copy what we can */
00220        if (isid(*ctx))
00221            *cpp++ = *ctx++;
00222        else {
00223            *cpp++ = '_'; ctx++;
00224        }
00225     } while (*ctx);
00226     while (cpp[-1] == CNTXMARK)           /* cannot end in context mark */
00227        cpp--;
00228     *cpp = '\0';
00229     return(context);
00230 }
00231 
00232 
00233 char *
00234 pushcontext(         /* push on another context */
00235        char  *ctx
00236 )
00237 {
00238     char  oldcontext[MAXCNTX+1];
00239     register int  n;
00240 
00241     strcpy(oldcontext, context);   /* save old context */
00242     setcontext(ctx);               /* set new context */
00243     n = strlen(context);           /* tack on old */
00244     if (n+strlen(oldcontext) > MAXCNTX) {
00245        strncpy(context+n, oldcontext, MAXCNTX-n);
00246        context[MAXCNTX] = '\0';
00247     } else
00248        strcpy(context+n, oldcontext);
00249     return(context);
00250 }
00251 
00252 
00253 char *
00254 popcontext(void)                   /* pop off top context */
00255 {
00256     register char  *cp1, *cp2;
00257 
00258     if (!context[0])               /* nothing left to pop */
00259        return(context);
00260     cp2 = context;                 /* find mark */
00261     while (*++cp2 && *cp2 != CNTXMARK)
00262        ;
00263     cp1 = context;                 /* copy tail to front */
00264     while ( (*cp1++ = *cp2++) )
00265        ;
00266     return(context);
00267 }
00268 
00269 
00270 char *
00271 qualname(            /* get qualified name */
00272        register char  *nam,
00273        int  lvl
00274 )
00275 {
00276     static char       nambuf[RMAXWORD+1];
00277     register char  *cp = nambuf, *cpp;
00278                             /* check for explicit local */
00279     if (*nam == CNTXMARK)
00280        if (lvl > 0)         /* only action is to refuse search */
00281            return(NULL);
00282        else
00283            nam++;
00284     else if (nam == nambuf) /* check for repeat call */
00285        return(lvl > 0 ? NULL : nam);
00286                             /* copy name to static buffer */
00287     while (*nam) {
00288        if (cp >= nambuf+RMAXWORD)
00289               goto toolong;
00290        *cp++ = *nam++;
00291     }
00292                             /* check for explicit global */
00293     if (cp > nambuf && cp[-1] == CNTXMARK) {
00294        if (lvl > 0)
00295            return(NULL);
00296        *--cp = '\0';
00297        return(nambuf);             /* already qualified */
00298     }
00299     cpp = context;          /* else skip the requested levels */
00300     while (lvl-- > 0) {
00301        if (!*cpp)
00302            return(NULL);    /* return NULL if past global level */
00303        while (*++cpp && *cpp != CNTXMARK)
00304            ;
00305     }
00306     while (*cpp) {          /* copy context to static buffer */
00307        if (cp >= nambuf+RMAXWORD)
00308            goto toolong;
00309        *cp++ = *cpp++;
00310     }
00311 toolong:
00312     *cp = '\0';
00313     return(nambuf);         /* return qualified name */
00314 }
00315 
00316 
00317 int
00318 incontext(                  /* is qualified name in current context? */
00319        register char  *qn
00320 )
00321 {
00322     if (!context[0])               /* global context accepts all */
00323        return(1);
00324     while (*qn && *qn != CNTXMARK) /* find context mark */
00325        qn++;
00326     return(!strcmp(qn, context));
00327 }
00328 
00329 
00330 void
00331 chanout(                    /* set output channels */
00332        void  (*cs)(int n, double v)
00333 )
00334 {
00335     register EPNODE  *ep;
00336 
00337     for (ep = outchan; ep != NULL; ep = ep->sibling)
00338        (*cs)(ep->v.kid->v.chan, evalue(ep->v.kid->sibling));
00339 
00340 }
00341 
00342 
00343 void
00344 dcleanup(            /* clear definitions (0->vars,1->output,2->consts) */
00345        int  lvl
00346 )
00347 {
00348     register int  i;
00349     register VARDEF  *vp;
00350     register EPNODE  *ep;
00351                             /* if context is global, clear all */
00352     for (i = 0; i < NHASH; i++)
00353        for (vp = hashtbl[i]; vp != NULL; vp = vp->next)
00354            if (incontext(vp->name)) {
00355               if (lvl >= 2)
00356                   dremove(vp->name);
00357               else
00358                   dclear(vp->name);
00359            }
00360     if (lvl >= 1) {
00361        for (ep = outchan; ep != NULL; ep = ep->sibling)
00362            epfree(ep);
00363        outchan = NULL;
00364     }
00365 }
00366 
00367 
00368 EPNODE *
00369 dlookup(                    /* look up a definition */
00370        char  *name
00371 )
00372 {
00373     register VARDEF  *vp;
00374     
00375     if ((vp = varlookup(name)) == NULL)
00376        return(NULL);
00377     return(vp->def);
00378 }
00379 
00380 
00381 VARDEF *
00382 varlookup(                  /* look up a variable */
00383        char  *name
00384 )
00385 {
00386     int        lvl = 0;
00387     register char  *qname;
00388     register VARDEF  *vp;
00389                             /* find most qualified match */
00390     while ((qname = qualname(name, lvl++)) != NULL)
00391        for (vp = hashtbl[hash(qname)]; vp != NULL; vp = vp->next)
00392            if (!strcmp(vp->name, qname))
00393               return(vp);
00394     return(NULL);
00395 }
00396 
00397 
00398 VARDEF *
00399 varinsert(                  /* get a link to a variable */
00400        char  *name
00401 )
00402 {
00403     register VARDEF  *vp;
00404     int        hv;
00405     
00406     if ((vp = varlookup(name)) != NULL) {
00407        vp->nlinks++;
00408        return(vp);
00409     }
00410     vp = (VARDEF *)emalloc(sizeof(VARDEF));
00411     vp->lib = liblookup(name);
00412     if (vp->lib == NULL)           /* if name not in library */
00413        name = qualname(name, 0);   /* use fully qualified version */
00414     hv = hash(name);
00415     vp->name = savestr(name);
00416     vp->nlinks = 1;
00417     vp->def = NULL;
00418     vp->next = hashtbl[hv];
00419     hashtbl[hv] = vp;
00420     return(vp);
00421 }
00422 
00423 
00424 void
00425 libupdate(                  /* update library links */
00426        char  *fn
00427 )
00428 {
00429     register int  i;
00430     register VARDEF  *vp;
00431                                    /* if fn is NULL then relink all */
00432     for (i = 0; i < NHASH; i++)
00433        for (vp = hashtbl[i]; vp != NULL; vp = vp->next)
00434            if (vp->lib != NULL || fn == NULL || !strcmp(fn, vp->name))
00435               vp->lib = liblookup(vp->name);
00436 }
00437 
00438 
00439 void
00440 varfree(                           /* release link to variable */
00441        register VARDEF       *ln
00442 )
00443 {
00444     register VARDEF  *vp;
00445     int        hv;
00446 
00447     if (--ln->nlinks > 0)
00448        return;                            /* still active */
00449 
00450     hv = hash(ln->name);
00451     vp = hashtbl[hv];
00452     if (vp == ln)
00453        hashtbl[hv] = vp->next;
00454     else {
00455        while (vp->next != ln)             /* must be in list */
00456               vp = vp->next;
00457        vp->next = ln->next;
00458     }
00459     freestr(ln->name);
00460     efree((char *)ln);
00461 }
00462 
00463 
00464 EPNODE *
00465 dfirst(void)                /* return pointer to first definition */
00466 {
00467     htndx = 0;
00468     htpos = NULL;
00469     ochpos = outchan;
00470     return(dnext());
00471 }
00472 
00473 
00474 EPNODE *
00475 dnext(void)                        /* return pointer to next definition */
00476 {
00477     register EPNODE  *ep;
00478     register char  *nm;
00479 
00480     while (htndx < NHASH) {
00481        if (htpos == NULL)
00482               htpos = hashtbl[htndx++];
00483        while (htpos != NULL) {
00484            ep = htpos->def;
00485            nm = htpos->name;
00486            htpos = htpos->next;
00487            if (ep != NULL && incontext(nm))
00488               return(ep);
00489        }
00490     }
00491     if ((ep = ochpos) != NULL)
00492        ochpos = ep->sibling;
00493     return(ep);
00494 }
00495 
00496 
00497 EPNODE *
00498 dpop(                /* pop a definition */
00499        char  *name
00500 )
00501 {
00502     register VARDEF  *vp;
00503     register EPNODE  *dp;
00504     
00505     if ((vp = varlookup(name)) == NULL || vp->def == NULL)
00506        return(NULL);
00507     dp = vp->def;
00508     vp->def = dp->sibling;
00509     varfree(vp);
00510     return(dp);
00511 }
00512 
00513 
00514 void
00515 dpush(               /* push on a definition */
00516        char  *nm,
00517        register EPNODE       *ep
00518 )
00519 {
00520     register VARDEF  *vp;
00521 
00522     vp = varinsert(nm);
00523     ep->sibling = vp->def;
00524     vp->def = ep;
00525 }
00526 
00527 
00528 void
00529 addchan(                    /* add an output channel assignment */
00530        EPNODE *sp
00531 )
00532 {
00533     int        ch = sp->v.kid->v.chan;
00534     register EPNODE  *ep, *epl;
00535 
00536     for (epl = NULL, ep = outchan; ep != NULL; epl = ep, ep = ep->sibling)
00537        if (ep->v.kid->v.chan >= ch) {
00538            if (epl != NULL)
00539               epl->sibling = sp;
00540            else
00541               outchan = sp;
00542            if (ep->v.kid->v.chan > ch)
00543               sp->sibling = ep;
00544            else {
00545               sp->sibling = ep->sibling;
00546               epfree(ep);
00547            }
00548            return;
00549        }
00550     if (epl != NULL)
00551        epl->sibling = sp;
00552     else
00553        outchan = sp;
00554     sp->sibling = NULL;
00555 
00556 }
00557 
00558 
00559 void
00560 getstatement(void)                 /* get next statement */
00561 {
00562     register EPNODE  *ep;
00563     char  *qname;
00564     register VARDEF  *vdef;
00565 
00566     if (nextc == ';') {            /* empty statement */
00567        scan();
00568        return;
00569     }
00570     if (esupport&E_OUTCHAN &&
00571               nextc == '$') {             /* channel assignment */
00572        ep = getchan();
00573        addchan(ep);
00574     } else {                       /* ordinary definition */
00575        ep = getdefn();
00576        qname = qualname(dname(ep), 0);
00577        if (esupport&E_REDEFW && (vdef = varlookup(qname)) != NULL) {
00578            if (vdef->def != NULL && epcmp(ep, vdef->def)) {
00579               wputs(qname);
00580               if (vdef->def->type == ':')
00581                   wputs(": redefined constant expression\n");
00582               else
00583                   wputs(": redefined\n");
00584            } else if (ep->v.kid->type == FUNC && vdef->lib != NULL) {
00585               wputs(qname);
00586               wputs(": definition hides library function\n");
00587            }
00588        }
00589        if (ep->type == ':')
00590            dremove(qname);
00591        else
00592            dclear(qname);
00593        dpush(qname, ep);
00594     }
00595     if (nextc != EOF) {
00596        if (nextc != ';')
00597            syntax("';' expected");
00598        scan();
00599     }
00600 }
00601 
00602 
00603 EPNODE *
00604 getdefn(void)
00605        /* A -> SYM = E1 */
00606        /*     SYM : E1 */
00607        /*     FUNC(SYM,..) = E1 */
00608        /*     FUNC(SYM,..) : E1 */
00609 {
00610     register EPNODE  *ep1, *ep2;
00611 
00612     if (!isalpha(nextc) && nextc != CNTXMARK)
00613        syntax("illegal variable name");
00614 
00615     ep1 = newnode();
00616     ep1->type = SYM;
00617     ep1->v.name = savestr(getname());
00618 
00619     if (esupport&E_FUNCTION && nextc == '(') {
00620        ep2 = newnode();
00621        ep2->type = FUNC;
00622        addekid(ep2, ep1);
00623        ep1 = ep2;
00624        do {
00625            scan();
00626            if (!isalpha(nextc))
00627               syntax("illegal parameter name");
00628            ep2 = newnode();
00629            ep2->type = SYM;
00630            ep2->v.name = savestr(getname());
00631            addekid(ep1, ep2);
00632        } while (nextc == ',');
00633        if (nextc != ')')
00634            syntax("')' expected");
00635        scan();
00636        curfunc = ep1;
00637     }
00638 
00639     if (nextc != '=' && nextc != ':')
00640        syntax("'=' or ':' expected");
00641 
00642     ep2 = newnode();
00643     ep2->type = nextc;
00644     scan();
00645     addekid(ep2, ep1);
00646     addekid(ep2, getE1());
00647 
00648     if (ep1->type == SYM && ep1->sibling->type != NUM) {
00649        ep1 = newnode();
00650        ep1->type = CLKT;
00651        ep1->v.tick = 0;
00652        addekid(ep2, ep1);
00653        ep1 = newnode();
00654        ep1->type = NUM;
00655        addekid(ep2, ep1);
00656     }
00657     curfunc = NULL;
00658 
00659     return(ep2);
00660 }
00661 
00662 
00663 EPNODE *
00664 getchan(void)               /* A -> $N = E1 */
00665 {
00666     register EPNODE  *ep1, *ep2;
00667 
00668     if (nextc != '$')
00669        syntax("missing '$'");
00670     scan();
00671 
00672     ep1 = newnode();
00673     ep1->type = CHAN;
00674     ep1->v.chan = getinum();
00675 
00676     if (nextc != '=')
00677        syntax("'=' expected");
00678     scan();
00679 
00680     ep2 = newnode();
00681     ep2->type = '=';
00682     addekid(ep2, ep1);
00683     addekid(ep2, getE1());
00684 
00685     return(ep2);
00686 }
00687 
00688 
00689 
00690 /*
00691  *  The following routines are for internal use only:
00692  */
00693 
00694 
00695 static double               /* evaluate a variable */
00696 dvalue(char  *name, EPNODE  *d)
00697 {
00698     register EPNODE  *ep1, *ep2;
00699     
00700     if (d == NULL || d->v.kid->type != SYM) {
00701        eputs(name);
00702        eputs(": undefined variable\n");
00703        quit(1);
00704     }
00705     ep1 = d->v.kid->sibling;                     /* get expression */
00706     if (ep1->type == NUM)
00707        return(ep1->v.num);                /* return if number */
00708     ep2 = ep1->sibling;                          /* check time */
00709     if (eclock >= MAXCLOCK)
00710        eclock = 1;                        /* wrap clock counter */
00711     if (ep2->v.tick < MAXCLOCK &&
00712               (ep2->v.tick == 0) | (ep2->v.tick != eclock)) {
00713        ep2->v.tick = d->type == ':' ? MAXCLOCK : eclock;
00714        ep2 = ep2->sibling;
00715        ep2->v.num = evalue(ep1);          /* needs new value */
00716     } else
00717        ep2 = ep2->sibling;                /* else reuse old value */
00718 
00719     return(ep2->v.num);
00720 }