ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/caldefn.c
Revision: 2.28
Committed: Thu Apr 2 18:00:34 2020 UTC (4 years ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R3
Changes since 2.27: +6 -2 lines
Log Message:
Added warning about assigning non-constant to constant variable

File Contents

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