ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/caldefn.c
Revision: 2.35
Committed: Tue Sep 26 00:14:02 2023 UTC (7 months, 3 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.34: +8 -6 lines
Log Message:
style: corrected efree() typing and channel free loop

File Contents

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