ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/caldefn.c
Revision: 2.26
Committed: Thu Jul 24 15:41:13 2014 UTC (9 years, 9 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R2, rad4R2P2, rad5R0, rad5R1, rad4R2, rad4R2P1
Changes since 2.25: +8 -1 lines
Log Message:
Added warning to varset() for resetting non-numeric definitions

File Contents

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