ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/calexpr.c
Revision: 2.38
Committed: Sat Dec 7 16:38:08 2019 UTC (4 years, 5 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.37: +4 -1 lines
Log Message:
Slight efficiency improvement in eval()

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: calexpr.c,v 2.37 2019/03/04 18:16:18 greg Exp $";
3 #endif
4 /*
5 * Compute data values using expression parser
6 *
7 * 7/1/85 Greg Ward
8 *
9 * 11/11/85 Made channel input conditional with (INCHAN) compiles.
10 *
11 * 4/2/86 Added conditional compiles for function definitions (FUNCTION).
12 *
13 * 1/29/87 Made variables conditional (VARIABLE)
14 *
15 * 5/19/88 Added constant subexpression elimination (RCONST)
16 *
17 * 2/19/03 Eliminated conditional compiles in favor of esupport extern.
18 */
19
20 #include "copyright.h"
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <math.h>
27 #include <stdlib.h>
28
29 #include "rtmisc.h"
30 #include "rtio.h"
31 #include "rterror.h"
32 #include "calcomp.h"
33
34 #define MAXLINE 256 /* maximum line length */
35
36 #define newnode() (EPNODE *)ecalloc(1, sizeof(EPNODE))
37
38 #define isdecimal(c) (isdigit(c) || (c) == '.')
39
40 static double euminus(EPNODE *), eargument(EPNODE *), enumber(EPNODE *);
41 static double echannel(EPNODE *);
42 static double eadd(EPNODE *), esubtr(EPNODE *),
43 emult(EPNODE *), edivi(EPNODE *),
44 epow(EPNODE *);
45 static double ebotch(EPNODE *);
46
47 unsigned int esupport = /* what to support */
48 E_VARIABLE | E_FUNCTION ;
49
50 int eofc = 0; /* optional end-of-file character */
51 int nextc; /* lookahead character */
52
53 double (*eoper[])(EPNODE *) = { /* expression operations */
54 ebotch,
55 evariable,
56 enumber,
57 euminus,
58 echannel,
59 efunc,
60 eargument,
61 ebotch,
62 ebotch,
63 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
64 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
65 emult,
66 eadd,
67 0,
68 esubtr,
69 0,
70 edivi,
71 0,0,0,0,0,0,0,0,0,0,
72 ebotch,
73 0,0,
74 ebotch,
75 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
76 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
77 epow,
78 };
79
80 static FILE *infp; /* input file pointer */
81 static char *linbuf; /* line buffer */
82 static char *infile; /* input file name */
83 static int lineno; /* input line number */
84 static int linepos; /* position in buffer */
85
86
87 EPNODE *
88 eparse( /* parse an expression string */
89 char *expr
90 )
91 {
92 EPNODE *ep;
93
94 initstr(expr, NULL, 0);
95 curfunc = NULL;
96 ep = getE1();
97 if (nextc != EOF)
98 syntax("unexpected character");
99 return(ep);
100 }
101
102
103 double
104 eval( /* evaluate an expression string */
105 char *expr
106 )
107 {
108 int prev_support = esupport;
109 EPNODE *ep;
110 double rval;
111
112 esupport &= ~E_RCONST; /* don't bother reducing constant expr */
113 ep = eparse(expr);
114 esupport = prev_support; /* as you were */
115 rval = evalue(ep);
116 epfree(ep);
117 return(rval);
118 }
119
120
121 int
122 epcmp( /* compare two expressions for equivalence */
123 EPNODE *ep1,
124 EPNODE *ep2
125 )
126 {
127 double d;
128
129 if (ep1->type != ep2->type)
130 return(1);
131
132 switch (ep1->type) {
133
134 case VAR:
135 return(ep1->v.ln != ep2->v.ln);
136
137 case NUM:
138 if (ep2->v.num == 0)
139 return(ep1->v.num != 0);
140 d = ep1->v.num / ep2->v.num;
141 return((d > 1.000000000001) | (d < 0.999999999999));
142
143 case CHAN:
144 case ARG:
145 return(ep1->v.chan != ep2->v.chan);
146
147 case '=':
148 case ':':
149 return(epcmp(ep1->v.kid->sibling, ep2->v.kid->sibling));
150
151 case CLKT:
152 case SYM: /* should never get this one */
153 return(0);
154
155 default:
156 ep1 = ep1->v.kid;
157 ep2 = ep2->v.kid;
158 while (ep1 != NULL) {
159 if (ep2 == NULL)
160 return(1);
161 if (epcmp(ep1, ep2))
162 return(1);
163 ep1 = ep1->sibling;
164 ep2 = ep2->sibling;
165 }
166 return(ep2 != NULL);
167 }
168 }
169
170
171 void
172 epfree( /* free a parse tree */
173 EPNODE *epar
174 )
175 {
176 EPNODE *ep;
177
178 switch (epar->type) {
179
180 case VAR:
181 varfree(epar->v.ln);
182 break;
183
184 case SYM:
185 freestr(epar->v.name);
186 break;
187
188 case NUM:
189 case CHAN:
190 case ARG:
191 case CLKT:
192 break;
193
194 default:
195 while ((ep = epar->v.kid) != NULL) {
196 epar->v.kid = ep->sibling;
197 epfree(ep);
198 }
199 break;
200
201 }
202
203 efree((char *)epar);
204 }
205
206 /* the following used to be a switch */
207 static double
208 eargument(
209 EPNODE *ep
210 )
211 {
212 return(argument(ep->v.chan));
213 }
214
215 static double
216 enumber(
217 EPNODE *ep
218 )
219 {
220 return(ep->v.num);
221 }
222
223 static double
224 euminus(
225 EPNODE *ep
226 )
227 {
228 EPNODE *ep1 = ep->v.kid;
229
230 return(-evalue(ep1));
231 }
232
233 static double
234 echannel(
235 EPNODE *ep
236 )
237 {
238 return(chanvalue(ep->v.chan));
239 }
240
241 static double
242 eadd(
243 EPNODE *ep
244 )
245 {
246 EPNODE *ep1 = ep->v.kid;
247
248 return(evalue(ep1) + evalue(ep1->sibling));
249 }
250
251 static double
252 esubtr(
253 EPNODE *ep
254 )
255 {
256 EPNODE *ep1 = ep->v.kid;
257
258 return(evalue(ep1) - evalue(ep1->sibling));
259 }
260
261 static double
262 emult(
263 EPNODE *ep
264 )
265 {
266 EPNODE *ep1 = ep->v.kid;
267
268 return(evalue(ep1) * evalue(ep1->sibling));
269 }
270
271 static double
272 edivi(
273 EPNODE *ep
274 )
275 {
276 EPNODE *ep1 = ep->v.kid;
277 double d;
278
279 d = evalue(ep1->sibling);
280 if (d == 0.0) {
281 wputs("Division by zero\n");
282 errno = ERANGE;
283 return(0.0);
284 }
285 return(evalue(ep1) / d);
286 }
287
288 static double
289 epow(
290 EPNODE *ep
291 )
292 {
293 EPNODE *ep1 = ep->v.kid;
294 double d;
295 int lasterrno;
296
297 lasterrno = errno;
298 errno = 0;
299 d = pow(evalue(ep1), evalue(ep1->sibling));
300 #ifdef isnan
301 if (errno == 0) {
302 if (isnan(d))
303 errno = EDOM;
304 else if (isinf(d))
305 errno = ERANGE;
306 }
307 #endif
308 if (errno == EDOM || errno == ERANGE) {
309 wputs("Illegal power\n");
310 return(0.0);
311 }
312 errno = lasterrno;
313 return(d);
314 }
315
316 static double
317 ebotch(
318 EPNODE *ep
319 )
320 {
321 eputs("Bad expression!\n");
322 quit(1);
323 return 0.0; /* pro forma return */
324 }
325
326
327 EPNODE *
328 ekid( /* return pointer to a node's nth kid */
329 EPNODE *ep,
330 int n
331 )
332 {
333
334 for (ep = ep->v.kid; ep != NULL; ep = ep->sibling)
335 if (--n < 0)
336 break;
337
338 return(ep);
339 }
340
341
342 int
343 nekids( /* return # of kids for node ep */
344 EPNODE *ep
345 )
346 {
347 int n = 0;
348
349 for (ep = ep->v.kid; ep != NULL; ep = ep->sibling)
350 n++;
351
352 return(n);
353 }
354
355
356 void
357 initfile( /* prepare input file */
358 FILE *fp,
359 char *fn,
360 int ln
361 )
362 {
363 static char inpbuf[MAXLINE];
364
365 infp = fp;
366 linbuf = inpbuf;
367 infile = fn;
368 lineno = ln;
369 linepos = 0;
370 inpbuf[0] = '\0';
371 scan();
372 }
373
374
375 void
376 initstr( /* prepare input string */
377 char *s,
378 char *fn,
379 int ln
380 )
381 {
382 infp = NULL;
383 infile = fn;
384 lineno = ln;
385 linbuf = s;
386 linepos = 0;
387 scan();
388 }
389
390
391 void
392 getscanpos( /* return current scan position */
393 char **fnp,
394 int *lnp,
395 char **spp,
396 FILE **fpp
397 )
398 {
399 if (fnp != NULL) *fnp = infile;
400 if (lnp != NULL) *lnp = lineno;
401 if (spp != NULL) *spp = linbuf+linepos;
402 if (fpp != NULL) *fpp = infp;
403 }
404
405
406 int
407 scan(void) /* scan next character, return literal next */
408 {
409 int lnext = 0;
410
411 do {
412 if (linbuf[linepos] == '\0')
413 if (infp == NULL || fgets(linbuf, MAXLINE, infp) == NULL)
414 nextc = EOF;
415 else {
416 nextc = linbuf[0];
417 lineno++;
418 linepos = 1;
419 }
420 else
421 nextc = linbuf[linepos++];
422 if (!lnext)
423 lnext = nextc;
424 if (nextc == eofc) {
425 nextc = EOF;
426 break;
427 }
428 if (nextc == '{') {
429 scan();
430 while (nextc != '}')
431 if (nextc == EOF)
432 syntax("'}' expected");
433 else
434 scan();
435 scan();
436 }
437 } while (isspace(nextc));
438 return(lnext);
439 }
440
441
442 char *
443 long2ascii( /* convert long to ascii */
444 long l
445 )
446 {
447 static char buf[16];
448 char *cp;
449 int neg = 0;
450
451 if (l == 0)
452 return("0");
453 if (l < 0) {
454 l = -l;
455 neg++;
456 }
457 cp = buf + sizeof(buf);
458 *--cp = '\0';
459 while (l) {
460 *--cp = l % 10 + '0';
461 l /= 10;
462 }
463 if (neg)
464 *--cp = '-';
465 return(cp);
466 }
467
468
469 void
470 syntax( /* report syntax error and quit */
471 char *err
472 )
473 {
474 int i;
475
476 if (infile != NULL || lineno != 0) {
477 if (infile != NULL) eputs(infile);
478 if (lineno != 0) {
479 eputs(infile != NULL ? ", line " : "line ");
480 eputs(long2ascii((long)lineno));
481 }
482 eputs(":\n");
483 }
484 eputs(linbuf);
485 if (linbuf[strlen(linbuf)-1] != '\n')
486 eputs("\n");
487 for (i = 0; i < linepos-1; i++)
488 eputs(linbuf[i] == '\t' ? "\t" : " ");
489 eputs("^ ");
490 eputs(err);
491 eputs("\n");
492 quit(1);
493 }
494
495
496 void
497 addekid( /* add a child to ep */
498 EPNODE *ep,
499 EPNODE *ekid
500 )
501 {
502 if (ep->v.kid == NULL)
503 ep->v.kid = ekid;
504 else {
505 for (ep = ep->v.kid; ep->sibling != NULL; ep = ep->sibling)
506 ;
507 ep->sibling = ekid;
508 }
509 ekid->sibling = NULL;
510 }
511
512
513 char *
514 getname(void) /* scan an identifier */
515 {
516 static char str[RMAXWORD+1];
517 int i, lnext;
518
519 lnext = nextc;
520 for (i = 0; i < RMAXWORD && isid(lnext); i++, lnext = scan())
521 str[i] = lnext;
522 str[i] = '\0';
523 while (isid(lnext)) /* skip rest of name */
524 lnext = scan();
525
526 return(str);
527 }
528
529
530 int
531 getinum(void) /* scan a positive integer */
532 {
533 int n, lnext;
534
535 n = 0;
536 lnext = nextc;
537 while (isdigit(lnext)) {
538 n = n * 10 + lnext - '0';
539 lnext = scan();
540 }
541 return(n);
542 }
543
544
545 double
546 getnum(void) /* scan a positive float */
547 {
548 int i, lnext;
549 char str[RMAXWORD+1];
550
551 i = 0;
552 lnext = nextc;
553 while (isdigit(lnext) && i < RMAXWORD) {
554 str[i++] = lnext;
555 lnext = scan();
556 }
557 if (lnext == '.' && i < RMAXWORD) {
558 str[i++] = lnext;
559 lnext = scan();
560 if (i == 1 && !isdigit(lnext))
561 syntax("badly formed number");
562 while (isdigit(lnext) && i < RMAXWORD) {
563 str[i++] = lnext;
564 lnext = scan();
565 }
566 }
567 if ((lnext == 'e') | (lnext == 'E') && i < RMAXWORD) {
568 str[i++] = lnext;
569 lnext = scan();
570 if ((lnext == '-') | (lnext == '+') && i < RMAXWORD) {
571 str[i++] = lnext;
572 lnext = scan();
573 }
574 if (!isdigit(lnext))
575 syntax("missing exponent");
576 while (isdigit(lnext) && i < RMAXWORD) {
577 str[i++] = lnext;
578 lnext = scan();
579 }
580 }
581 str[i] = '\0';
582
583 return(atof(str));
584 }
585
586
587 EPNODE *
588 getE1(void) /* E1 -> E1 ADDOP E2 */
589 /* E2 */
590 {
591 EPNODE *ep1, *ep2;
592
593 ep1 = getE2();
594 while (nextc == '+' || nextc == '-') {
595 ep2 = newnode();
596 ep2->type = nextc;
597 scan();
598 addekid(ep2, ep1);
599 addekid(ep2, getE2());
600 if (esupport&E_RCONST &&
601 ep1->type == NUM && ep1->sibling->type == NUM)
602 ep2 = rconst(ep2);
603 ep1 = ep2;
604 }
605 return(ep1);
606 }
607
608
609 EPNODE *
610 getE2(void) /* E2 -> E2 MULOP E3 */
611 /* E3 */
612 {
613 EPNODE *ep1, *ep2;
614
615 ep1 = getE3();
616 while (nextc == '*' || nextc == '/') {
617 ep2 = newnode();
618 ep2->type = nextc;
619 scan();
620 addekid(ep2, ep1);
621 addekid(ep2, getE3());
622 if (esupport&E_RCONST) {
623 EPNODE *ep3 = ep1->sibling;
624 if (ep1->type == NUM && ep3->type == NUM) {
625 ep2 = rconst(ep2);
626 } else if (ep3->type == NUM) {
627 if (ep2->type == '/') {
628 if (ep3->v.num == 0)
629 syntax("divide by zero constant");
630 ep2->type = '*'; /* for speed */
631 ep3->v.num = 1./ep3->v.num;
632 } else if (ep3->v.num == 0) {
633 ep1->sibling = NULL; /* (E2 * 0) */
634 epfree(ep2);
635 ep2 = ep3;
636 }
637 } else if (ep1->type == NUM && ep1->v.num == 0) {
638 epfree(ep3); /* (0 * E3) or (0 / E3) */
639 ep1->sibling = NULL;
640 efree((char *)ep2);
641 ep2 = ep1;
642 }
643 }
644 ep1 = ep2;
645 }
646 return(ep1);
647 }
648
649
650 EPNODE *
651 getE3(void) /* E3 -> E4 ^ E3 */
652 /* E4 */
653 {
654 EPNODE *ep1, *ep2;
655
656 ep1 = getE4();
657 if (nextc != '^')
658 return(ep1);
659 ep2 = newnode();
660 ep2->type = nextc;
661 scan();
662 addekid(ep2, ep1);
663 addekid(ep2, getE3());
664 if (esupport&E_RCONST) {
665 EPNODE *ep3 = ep1->sibling;
666 if (ep1->type == NUM && ep3->type == NUM) {
667 ep2 = rconst(ep2);
668 } else if (ep1->type == NUM && ep1->v.num == 0) {
669 epfree(ep3); /* (0 ^ E3) */
670 ep1->sibling = NULL;
671 efree((char *)ep2);
672 ep2 = ep1;
673 } else if ((ep3->type == NUM && ep3->v.num == 0) ||
674 (ep1->type == NUM && ep1->v.num == 1)) {
675 epfree(ep2); /* (E4 ^ 0) or (1 ^ E3) */
676 ep2 = newnode();
677 ep2->type = NUM;
678 ep2->v.num = 1;
679 }
680 }
681 return(ep2);
682 }
683
684
685 EPNODE *
686 getE4(void) /* E4 -> ADDOP E5 */
687 /* E5 */
688 {
689 EPNODE *ep1, *ep2;
690
691 if (nextc == '-') {
692 scan();
693 ep2 = getE5();
694 if (ep2->type == NUM) {
695 ep2->v.num = -ep2->v.num;
696 return(ep2);
697 }
698 if (ep2->type == UMINUS) { /* don't generate -(-E5) */
699 ep1 = ep2->v.kid;
700 efree((char *)ep2);
701 return(ep1);
702 }
703 ep1 = newnode();
704 ep1->type = UMINUS;
705 addekid(ep1, ep2);
706 return(ep1);
707 }
708 if (nextc == '+')
709 scan();
710 return(getE5());
711 }
712
713
714 EPNODE *
715 getE5(void) /* E5 -> (E1) */
716 /* VAR */
717 /* NUM */
718 /* $N */
719 /* FUNC(E1,..) */
720 /* ARG */
721 {
722 int i;
723 char *nam;
724 EPNODE *ep1, *ep2;
725
726 if (nextc == '(') {
727 scan();
728 ep1 = getE1();
729 if (nextc != ')')
730 syntax("')' expected");
731 scan();
732 return(ep1);
733 }
734
735 if (esupport&E_INCHAN && nextc == '$') {
736 scan();
737 ep1 = newnode();
738 ep1->type = CHAN;
739 ep1->v.chan = getinum();
740 return(ep1);
741 }
742
743 if (esupport&(E_VARIABLE|E_FUNCTION) &&
744 (isalpha(nextc) || nextc == CNTXMARK)) {
745 nam = getname();
746 ep1 = NULL;
747 if ((esupport&(E_VARIABLE|E_FUNCTION)) == (E_VARIABLE|E_FUNCTION)
748 && curfunc != NULL)
749 for (i = 1, ep2 = curfunc->v.kid->sibling;
750 ep2 != NULL; i++, ep2 = ep2->sibling)
751 if (!strcmp(ep2->v.name, nam)) {
752 ep1 = newnode();
753 ep1->type = ARG;
754 ep1->v.chan = i;
755 break;
756 }
757 if (ep1 == NULL) {
758 ep1 = newnode();
759 ep1->type = VAR;
760 ep1->v.ln = varinsert(nam);
761 }
762 if (esupport&E_FUNCTION && nextc == '(') {
763 ep2 = newnode();
764 ep2->type = FUNC;
765 addekid(ep2, ep1);
766 ep1 = ep2;
767 do {
768 scan();
769 addekid(ep1, getE1());
770 } while (nextc == ',');
771 if (nextc != ')')
772 syntax("')' expected");
773 scan();
774 } else if (!(esupport&E_VARIABLE))
775 syntax("'(' expected");
776 if (esupport&E_RCONST && isconstvar(ep1))
777 ep1 = rconst(ep1);
778 return(ep1);
779 }
780
781 if (isdecimal(nextc)) {
782 ep1 = newnode();
783 ep1->type = NUM;
784 ep1->v.num = getnum();
785 return(ep1);
786 }
787 syntax("unexpected character");
788 return NULL; /* pro forma return */
789 }
790
791
792 EPNODE *
793 rconst( /* reduce a constant expression */
794 EPNODE *epar
795 )
796 {
797 EPNODE *ep;
798
799 ep = newnode();
800 ep->type = NUM;
801 errno = 0;
802 ep->v.num = evalue(epar);
803 if (errno == EDOM || errno == ERANGE)
804 syntax("bad constant expression");
805 epfree(epar);
806
807 return(ep);
808 }
809
810
811 int
812 isconstvar( /* is ep linked to a constant expression? */
813 EPNODE *ep
814 )
815 {
816 EPNODE *ep1;
817
818 if (esupport&E_FUNCTION && ep->type == FUNC) {
819 if (!isconstfun(ep->v.kid))
820 return(0);
821 for (ep1 = ep->v.kid->sibling; ep1 != NULL; ep1 = ep1->sibling)
822 if (ep1->type != NUM && !isconstfun(ep1))
823 return(0);
824 return(1);
825 }
826 if (ep->type != VAR)
827 return(0);
828 ep1 = ep->v.ln->def;
829 if (ep1 == NULL || ep1->type != ':')
830 return(0);
831 if (esupport&E_FUNCTION && ep1->v.kid->type != SYM)
832 return(0);
833 return(1);
834 }
835
836
837 int
838 isconstfun( /* is ep linked to a constant function? */
839 EPNODE *ep
840 )
841 {
842 EPNODE *dp;
843 LIBR *lp;
844
845 if (ep->type != VAR)
846 return(0);
847 if ((dp = ep->v.ln->def) != NULL) {
848 if (dp->v.kid->type == FUNC)
849 return(dp->type == ':');
850 else
851 return(0); /* don't identify masked library functions */
852 }
853 if ((lp = ep->v.ln->lib) != NULL)
854 return(lp->atyp == ':');
855 return(0);
856 }