ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/caldefn.c
Revision: 2.25
Committed: Sun Jul 29 22:10:45 2012 UTC (11 years, 9 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.24: +43 -39 lines
Log Message:
Made varset() behavior identical to loading definition

File Contents

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