ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/caldefn.c
Revision: 2.26
Committed: Thu Jul 24 15:41:13 2014 UTC (9 years, 8 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R2, rad4R2P2, rad5R0, rad5R1, rad4R2, rad4R2P1
Changes since 2.25: +8 -1 lines
Log Message:
Added warning to varset() for resetting non-numeric definitions

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: caldefn.c,v 2.25 2012/07/29 22:10:45 greg Exp $";
3 #endif
4 /*
5 * Store variable definitions.
6 *
7 * 7/1/85 Greg Ward
8 *
9 * 11/11/85 Added conditional compiles (OUTCHAN) for control output.
10 *
11 * 4/2/86 Added conditional compiles for function definitions (FUNCTION).
12 *
13 * 1/15/88 Added clock for caching of variable values.
14 *
15 * 11/16/88 Added VARDEF structure for hard linking.
16 *
17 * 5/31/90 Added conditional compile (REDEFW) for redefinition warning.
18 *
19 * 4/23/91 Added ':' assignment for constant expressions
20 *
21 * 8/7/91 Added optional context path to append to variable names
22 *
23 * 5/17/2001 Fixed clock counter wrapping behavior
24 *
25 * 2/19/03 Eliminated conditional compiles in favor of esupport extern.
26 */
27
28 #include "copyright.h"
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
33
34 #include "rterror.h"
35 #include "rtio.h"
36 #include "rtmisc.h"
37 #include "calcomp.h"
38
39 #ifndef NHASH
40 #define NHASH 521 /* hash size (a prime!) */
41 #endif
42
43 #define hash(s) (shash(s)%NHASH)
44
45 #define newnode() (EPNODE *)ecalloc(1, sizeof(EPNODE))
46
47 static double dvalue(char *name, EPNODE *d);
48
49 #define MAXCLOCK (1L<<31) /* clock wrap value */
50
51 unsigned long eclock = 0; /* value storage timer */
52
53 #define MAXCNTX 1023 /* maximum context length */
54
55 static char context[MAXCNTX+1]; /* current context path */
56
57 static VARDEF *hashtbl[NHASH]; /* definition list */
58 static int htndx; /* index for */
59 static VARDEF *htpos; /* ...dfirst() and */
60 static EPNODE *ochpos; /* ...dnext */
61 static EPNODE *outchan;
62
63 EPNODE *curfunc = NULL;
64 #define dname(ep) ((ep)->v.kid->type == SYM ? \
65 (ep)->v.kid->v.name : \
66 (ep)->v.kid->v.kid->v.name)
67
68
69 void
70 fcompile( /* get definitions from a file */
71 char *fname
72 )
73 {
74 FILE *fp;
75
76 if (fname == NULL)
77 fp = stdin;
78 else if ((fp = fopen(fname, "r")) == NULL) {
79 eputs(fname);
80 eputs(": cannot open\n");
81 quit(1);
82 }
83 initfile(fp, fname, 0);
84 while (nextc != EOF)
85 getstatement();
86 if (fname != NULL)
87 fclose(fp);
88 }
89
90
91 void
92 scompile( /* get definitions from a string */
93 char *str,
94 char *fn,
95 int ln
96 )
97 {
98 initstr(str, fn, ln);
99 while (nextc != EOF)
100 getstatement();
101 }
102
103
104 double
105 varvalue( /* return a variable's value */
106 char *vname
107 )
108 {
109 return(dvalue(vname, dlookup(vname)));
110 }
111
112
113 double
114 evariable( /* evaluate a variable */
115 EPNODE *ep
116 )
117 {
118 VARDEF *dp = ep->v.ln;
119
120 return(dvalue(dp->name, dp->def));
121 }
122
123
124 void
125 varset( /* set a variable's value */
126 char *vname,
127 int assign,
128 double val
129 )
130 {
131 char *qname;
132 EPNODE *ep1, *ep2;
133 /* get qualified name */
134 qname = qualname(vname, 0);
135 /* check for quick set */
136 if ((ep1 = dlookup(qname)) != NULL && ep1->v.kid->type == SYM &&
137 (ep1->type == ':') <= (assign == ':')) {
138 ep2 = ep1->v.kid->sibling;
139 if (ep2->type == NUM) {
140 ep2->v.num = val;
141 ep1->type = assign;
142 return;
143 }
144 }
145 if (ep1 != NULL && esupport&E_REDEFW) {
146 wputs(qname);
147 if (ep1->type == ':')
148 wputs(": reset constant expression\n");
149 else
150 wputs(": reset expression\n");
151 }
152 /* hand build definition */
153 ep1 = newnode();
154 ep1->type = assign;
155 ep2 = newnode();
156 ep2->type = SYM;
157 ep2->v.name = savestr(vname);
158 addekid(ep1, ep2);
159 ep2 = newnode();
160 ep2->type = NUM;
161 ep2->v.num = val;
162 addekid(ep1, ep2);
163 if (assign == ':')
164 dremove(qname);
165 else
166 dclear(qname);
167 dpush(qname, ep1);
168 }
169
170
171 void
172 dclear( /* delete variable definitions of name */
173 char *name
174 )
175 {
176 EPNODE *ep;
177
178 while ((ep = dpop(name)) != NULL) {
179 if (ep->type == ':') {
180 dpush(name, ep); /* don't clear constants */
181 return;
182 }
183 epfree(ep);
184 }
185 }
186
187
188 void
189 dremove( /* delete all definitions of name */
190 char *name
191 )
192 {
193 EPNODE *ep;
194
195 while ((ep = dpop(name)) != NULL)
196 epfree(ep);
197 }
198
199
200 int
201 vardefined( /* return non-zero if variable defined */
202 char *name
203 )
204 {
205 EPNODE *dp;
206
207 return((dp = dlookup(name)) != NULL && dp->v.kid->type == SYM);
208 }
209
210
211 char *
212 setcontext( /* set a new context path */
213 char *ctx
214 )
215 {
216 char *cpp;
217
218 if (ctx == NULL)
219 return(context); /* just asking */
220 while (*ctx == CNTXMARK)
221 ctx++; /* skip past marks */
222 if (!*ctx) {
223 context[0] = '\0'; /* empty means clear context */
224 return(context);
225 }
226 cpp = context; /* start context with mark */
227 *cpp++ = CNTXMARK;
228 do { /* carefully copy new context */
229 if (cpp >= context+MAXCNTX)
230 break; /* just copy what we can */
231 if (isid(*ctx))
232 *cpp++ = *ctx++;
233 else {
234 *cpp++ = '_'; ctx++;
235 }
236 } while (*ctx);
237 while (cpp[-1] == CNTXMARK) /* cannot end in context mark */
238 cpp--;
239 *cpp = '\0';
240 return(context);
241 }
242
243
244 char *
245 pushcontext( /* push on another context */
246 char *ctx
247 )
248 {
249 char oldcontext[MAXCNTX+1];
250 int n;
251
252 strcpy(oldcontext, context); /* save old context */
253 setcontext(ctx); /* set new context */
254 n = strlen(context); /* tack on old */
255 if (n+strlen(oldcontext) > MAXCNTX) {
256 strncpy(context+n, oldcontext, MAXCNTX-n);
257 context[MAXCNTX] = '\0';
258 } else
259 strcpy(context+n, oldcontext);
260 return(context);
261 }
262
263
264 char *
265 popcontext(void) /* pop off top context */
266 {
267 char *cp1, *cp2;
268
269 if (!context[0]) /* nothing left to pop */
270 return(context);
271 cp2 = context; /* find mark */
272 while (*++cp2 && *cp2 != CNTXMARK)
273 ;
274 cp1 = context; /* copy tail to front */
275 while ( (*cp1++ = *cp2++) )
276 ;
277 return(context);
278 }
279
280
281 char *
282 qualname( /* get qualified name */
283 char *nam,
284 int lvl
285 )
286 {
287 static char nambuf[RMAXWORD+1];
288 char *cp = nambuf, *cpp;
289 /* check for explicit local */
290 if (*nam == CNTXMARK)
291 if (lvl > 0) /* only action is to refuse search */
292 return(NULL);
293 else
294 nam++;
295 else if (nam == nambuf) /* check for repeat call */
296 return(lvl > 0 ? NULL : nam);
297 /* copy name to static buffer */
298 while (*nam) {
299 if (cp >= nambuf+RMAXWORD)
300 goto toolong;
301 *cp++ = *nam++;
302 }
303 /* check for explicit global */
304 if (cp > nambuf && cp[-1] == CNTXMARK) {
305 if (lvl > 0)
306 return(NULL);
307 *--cp = '\0';
308 return(nambuf); /* already qualified */
309 }
310 cpp = context; /* else skip the requested levels */
311 while (lvl-- > 0) {
312 if (!*cpp)
313 return(NULL); /* return NULL if past global level */
314 while (*++cpp && *cpp != CNTXMARK)
315 ;
316 }
317 while (*cpp) { /* copy context to static buffer */
318 if (cp >= nambuf+RMAXWORD)
319 goto toolong;
320 *cp++ = *cpp++;
321 }
322 toolong:
323 *cp = '\0';
324 return(nambuf); /* return qualified name */
325 }
326
327
328 int
329 incontext( /* is qualified name in current context? */
330 char *qn
331 )
332 {
333 if (!context[0]) /* global context accepts all */
334 return(1);
335 while (*qn && *qn != CNTXMARK) /* find context mark */
336 qn++;
337 return(!strcmp(qn, context));
338 }
339
340
341 void
342 chanout( /* set output channels */
343 void (*cs)(int n, double v)
344 )
345 {
346 EPNODE *ep;
347
348 for (ep = outchan; ep != NULL; ep = ep->sibling)
349 (*cs)(ep->v.kid->v.chan, evalue(ep->v.kid->sibling));
350
351 }
352
353
354 void
355 dcleanup( /* clear definitions (0->vars,1->output,2->consts) */
356 int lvl
357 )
358 {
359 int i;
360 VARDEF *vp;
361 EPNODE *ep;
362 /* if context is global, clear all */
363 for (i = 0; i < NHASH; i++)
364 for (vp = hashtbl[i]; vp != NULL; vp = vp->next)
365 if (incontext(vp->name)) {
366 if (lvl >= 2)
367 dremove(vp->name);
368 else
369 dclear(vp->name);
370 }
371 if (lvl >= 1) {
372 for (ep = outchan; ep != NULL; ep = ep->sibling)
373 epfree(ep);
374 outchan = NULL;
375 }
376 }
377
378
379 EPNODE *
380 dlookup( /* look up a definition */
381 char *name
382 )
383 {
384 VARDEF *vp;
385
386 if ((vp = varlookup(name)) == NULL)
387 return(NULL);
388 return(vp->def);
389 }
390
391
392 VARDEF *
393 varlookup( /* look up a variable */
394 char *name
395 )
396 {
397 int lvl = 0;
398 char *qname;
399 VARDEF *vp;
400 /* find most qualified match */
401 while ((qname = qualname(name, lvl++)) != NULL)
402 for (vp = hashtbl[hash(qname)]; vp != NULL; vp = vp->next)
403 if (!strcmp(vp->name, qname))
404 return(vp);
405 return(NULL);
406 }
407
408
409 VARDEF *
410 varinsert( /* get a link to a variable */
411 char *name
412 )
413 {
414 VARDEF *vp;
415 int hv;
416
417 if ((vp = varlookup(name)) != NULL) {
418 vp->nlinks++;
419 return(vp);
420 }
421 vp = (VARDEF *)emalloc(sizeof(VARDEF));
422 vp->lib = liblookup(name);
423 if (vp->lib == NULL) /* if name not in library */
424 name = qualname(name, 0); /* use fully qualified version */
425 hv = hash(name);
426 vp->name = savestr(name);
427 vp->nlinks = 1;
428 vp->def = NULL;
429 vp->next = hashtbl[hv];
430 hashtbl[hv] = vp;
431 return(vp);
432 }
433
434
435 void
436 libupdate( /* update library links */
437 char *fn
438 )
439 {
440 int i;
441 VARDEF *vp;
442 /* if fn is NULL then relink all */
443 for (i = 0; i < NHASH; i++)
444 for (vp = hashtbl[i]; vp != NULL; vp = vp->next)
445 if (vp->lib != NULL || fn == NULL || !strcmp(fn, vp->name))
446 vp->lib = liblookup(vp->name);
447 }
448
449
450 void
451 varfree( /* release link to variable */
452 VARDEF *ln
453 )
454 {
455 VARDEF *vp;
456 int hv;
457
458 if (--ln->nlinks > 0)
459 return; /* still active */
460
461 hv = hash(ln->name);
462 vp = hashtbl[hv];
463 if (vp == ln)
464 hashtbl[hv] = vp->next;
465 else {
466 while (vp->next != ln) /* must be in list */
467 vp = vp->next;
468 vp->next = ln->next;
469 }
470 freestr(ln->name);
471 efree((char *)ln);
472 }
473
474
475 EPNODE *
476 dfirst(void) /* return pointer to first definition */
477 {
478 htndx = 0;
479 htpos = NULL;
480 ochpos = outchan;
481 return(dnext());
482 }
483
484
485 EPNODE *
486 dnext(void) /* return pointer to next definition */
487 {
488 EPNODE *ep;
489 char *nm;
490
491 while (htndx < NHASH) {
492 if (htpos == NULL)
493 htpos = hashtbl[htndx++];
494 while (htpos != NULL) {
495 ep = htpos->def;
496 nm = htpos->name;
497 htpos = htpos->next;
498 if (ep != NULL && incontext(nm))
499 return(ep);
500 }
501 }
502 if ((ep = ochpos) != NULL)
503 ochpos = ep->sibling;
504 return(ep);
505 }
506
507
508 EPNODE *
509 dpop( /* pop a definition */
510 char *name
511 )
512 {
513 VARDEF *vp;
514 EPNODE *dp;
515
516 if ((vp = varlookup(name)) == NULL || vp->def == NULL)
517 return(NULL);
518 dp = vp->def;
519 vp->def = dp->sibling;
520 varfree(vp);
521 return(dp);
522 }
523
524
525 void
526 dpush( /* push on a definition */
527 char *nm,
528 EPNODE *ep
529 )
530 {
531 VARDEF *vp;
532
533 vp = varinsert(nm);
534 ep->sibling = vp->def;
535 vp->def = ep;
536 }
537
538
539 void
540 addchan( /* add an output channel assignment */
541 EPNODE *sp
542 )
543 {
544 int ch = sp->v.kid->v.chan;
545 EPNODE *ep, *epl;
546
547 for (epl = NULL, ep = outchan; ep != NULL; epl = ep, ep = ep->sibling)
548 if (ep->v.kid->v.chan >= ch) {
549 if (epl != NULL)
550 epl->sibling = sp;
551 else
552 outchan = sp;
553 if (ep->v.kid->v.chan > ch)
554 sp->sibling = ep;
555 else {
556 sp->sibling = ep->sibling;
557 epfree(ep);
558 }
559 return;
560 }
561 if (epl != NULL)
562 epl->sibling = sp;
563 else
564 outchan = sp;
565 sp->sibling = NULL;
566
567 }
568
569
570 void
571 getstatement(void) /* get next statement */
572 {
573 EPNODE *ep;
574 char *qname;
575 VARDEF *vdef;
576
577 if (nextc == ';') { /* empty statement */
578 scan();
579 return;
580 }
581 if (esupport&E_OUTCHAN &&
582 nextc == '$') { /* channel assignment */
583 ep = getchan();
584 addchan(ep);
585 } else { /* ordinary definition */
586 ep = getdefn();
587 qname = qualname(dname(ep), 0);
588 if (esupport&E_REDEFW && (vdef = varlookup(qname)) != NULL) {
589 if (vdef->def != NULL && epcmp(ep, vdef->def)) {
590 wputs(qname);
591 if (vdef->def->type == ':')
592 wputs(": redefined constant expression\n");
593 else
594 wputs(": redefined\n");
595 } else if (ep->v.kid->type == FUNC && vdef->lib != NULL) {
596 wputs(qname);
597 wputs(": definition hides library function\n");
598 }
599 }
600 if (ep->type == ':')
601 dremove(qname);
602 else
603 dclear(qname);
604 dpush(qname, ep);
605 }
606 if (nextc != EOF) {
607 if (nextc != ';')
608 syntax("';' expected");
609 scan();
610 }
611 }
612
613
614 EPNODE *
615 getdefn(void)
616 /* A -> SYM = E1 */
617 /* SYM : E1 */
618 /* FUNC(SYM,..) = E1 */
619 /* FUNC(SYM,..) : E1 */
620 {
621 EPNODE *ep1, *ep2;
622
623 if (!isalpha(nextc) && nextc != CNTXMARK)
624 syntax("illegal variable name");
625
626 ep1 = newnode();
627 ep1->type = SYM;
628 ep1->v.name = savestr(getname());
629
630 if (esupport&E_FUNCTION && nextc == '(') {
631 ep2 = newnode();
632 ep2->type = FUNC;
633 addekid(ep2, ep1);
634 ep1 = ep2;
635 do {
636 scan();
637 if (!isalpha(nextc))
638 syntax("illegal parameter name");
639 ep2 = newnode();
640 ep2->type = SYM;
641 ep2->v.name = savestr(getname());
642 addekid(ep1, ep2);
643 } while (nextc == ',');
644 if (nextc != ')')
645 syntax("')' expected");
646 scan();
647 curfunc = ep1;
648 }
649
650 if (nextc != '=' && nextc != ':')
651 syntax("'=' or ':' expected");
652
653 ep2 = newnode();
654 ep2->type = nextc;
655 scan();
656 addekid(ep2, ep1);
657 addekid(ep2, getE1());
658
659 if (ep1->type == SYM && ep1->sibling->type != NUM) {
660 ep1 = newnode();
661 ep1->type = CLKT;
662 ep1->v.tick = 0;
663 addekid(ep2, ep1);
664 ep1 = newnode();
665 ep1->type = NUM;
666 addekid(ep2, ep1);
667 }
668 curfunc = NULL;
669
670 return(ep2);
671 }
672
673
674 EPNODE *
675 getchan(void) /* A -> $N = E1 */
676 {
677 EPNODE *ep1, *ep2;
678
679 if (nextc != '$')
680 syntax("missing '$'");
681 scan();
682
683 ep1 = newnode();
684 ep1->type = CHAN;
685 ep1->v.chan = getinum();
686
687 if (nextc != '=')
688 syntax("'=' expected");
689 scan();
690
691 ep2 = newnode();
692 ep2->type = '=';
693 addekid(ep2, ep1);
694 addekid(ep2, getE1());
695
696 return(ep2);
697 }
698
699
700
701 /*
702 * The following routines are for internal use only:
703 */
704
705
706 static double /* evaluate a variable */
707 dvalue(char *name, EPNODE *d)
708 {
709 EPNODE *ep1, *ep2;
710
711 if (d == NULL || d->v.kid->type != SYM) {
712 eputs(name);
713 eputs(": undefined variable\n");
714 quit(1);
715 }
716 ep1 = d->v.kid->sibling; /* get expression */
717 if (ep1->type == NUM)
718 return(ep1->v.num); /* return if number */
719 ep2 = ep1->sibling; /* check time */
720 if (eclock >= MAXCLOCK)
721 eclock = 1; /* wrap clock counter */
722 if (ep2->v.tick < MAXCLOCK &&
723 (ep2->v.tick == 0) | (ep2->v.tick != eclock)) {
724 ep2->v.tick = d->type == ':' ? MAXCLOCK : eclock;
725 ep2 = ep2->sibling;
726 ep2->v.num = evalue(ep1); /* needs new value */
727 } else
728 ep2 = ep2->sibling; /* else reuse old value */
729
730 return(ep2->v.num);
731 }