ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/caldefn.c
Revision: 2.40
Committed: Mon Sep 16 17:31:14 2024 UTC (4 days, 8 hours ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.39: +31 -31 lines
Log Message:
refactor: Changed too-generic symbol names in calcomp.h

File Contents

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