ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/caldefn.c
Revision: 2.22
Committed: Sun Mar 28 20:33:12 2004 UTC (20 years ago) by schorsch
Content type: text/plain
Branch: MAIN
CVS Tags: rad3R7P2, rad3R7P1, rad3R6, rad3R6P1, rad3R8, rad3R9
Changes since 2.21: +2 -1 lines
Log Message:
Continued ANSIfication, and other fixes and clarifications.

File Contents

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