ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/caldefn.c
Revision: 2.36
Committed: Tue Sep 26 18:33:14 2023 UTC (7 months, 2 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.35: +5 -9 lines
Log Message:
style: minor code clean-ups

File Contents

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