2 This file is part of the software library CADLIB written by Conrad Ziesler
3 Copyright 2003, Conrad Ziesler, all rights reserved.
5 *************************
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 /* eqn.c, equation evaluation and parsing
33 extern int __debug_eqn__
;
35 /* int __debug_eqn__=1; */
37 #ifndef STRIP_DEBUGGING
38 #define D(level,a) do { if(__debug_eqn__>(level)) a; } while(0)
44 #define uchar unsigned char
50 #define MAX_STACK 1024
52 static memory_t eqn_mem_default
=MEMORY_INIT
;
53 static memory_t
*eqn_mem_stack
[MAX_STACK
]={ &eqn_mem_default
, NULL
, NULL
};
54 static int eqn_mem_stackp
=0;
55 static int eqn_mem_qalloc
=0;
57 void eqn_mem_free(void *p
)
67 /* make and return a new eqn memory object */
68 void *eqn_mem_new(void)
72 p
=malloc(sizeof(memory_t
));
78 void eqn_mem_push(void *p
)
81 assert(eqn_mem_stackp
<MAX_STACK
);
82 eqn_mem_stack
[eqn_mem_stackp
]=p
;
86 /* return and pop stack */
87 void *eqn_mem_pop(void)
90 assert(eqn_mem_stackp
>0);
91 p
=eqn_mem_stack
[eqn_mem_stackp
];
98 void eqn_free(enq_t e
)
99 { } /* static equations are not mallocated */
102 static void *eqn_malloc(int size
)
104 void *p
=memory_alloc(eqn_mem_stack
[eqn_mem_stackp
],size
);
105 if(p
==((void *)0x81dd014))
113 void eqn_free(eqn_t e
)
114 { memory_free(eqn_mem_stack
[eqn_mem_stackp
],e
.eqn
); }
120 eqntoken_t
*eqntoken_next(eqntoken_t
*p
)
125 case OPeolist
: case OPeol_const
: case OPeol_valp
: case OPeol_qty
:
127 case OPval
: case OPvalm
: r
= (void *) (((char *)p
)+(sizeof(itemop_t
)+sizeof(eqntokenval_t
) )) ; break;
128 case OPlit
: case OPlitm
:
129 case OPlitv
: case OPlitvm
: r
= (void *) (((char *)p
)+(sizeof(itemop_t
)+sizeof(eqntokenlit_t
) )) ; break;
130 default: r
= (void *) (((char *)p
)+(sizeof(itemop_t
))) ; break;
135 eqntoken_t
*eqntoken_last(eqntoken_t
*p
)
139 while(!OP_ISEND(p
->op
))p
=eqntoken_next(p
);
144 r
= (void *) (((char *)p
)+(sizeof(itemop_t
)+sizeof(eqntokenval_t
))) ; break;
146 r
= (void *) (((char *)p
)+(sizeof(itemop_t
)+sizeof(eqntokenpval_t
))) ; break;
148 r
= (void *) (((char *)p
)+(sizeof(itemop_t
))) ; break;
158 static float float_parse(uchar
*val
, int *count
)
160 float v
=0.0,sign
=1,sig
=0,rest
=0;
162 uchar
*ps
=NULL
,*pa
=NULL
,*pb
=NULL
,*tmpc
;
165 for(ps
=val
;(isspace(ps
[0]))&&(ps
[0]!=0);ps
++);
166 if(ps
[(unsigned)0]=='-') { sign
=-1; pa
=ps
+1; }
167 else if(ps
[(unsigned)0]=='+') { sign
=1; pa
=ps
+1; }
170 if(pa
[(unsigned)0]=='.'){ sig
=0; pb
=pa
+1; }
172 if(isdigit(pa
[(unsigned)0]))
175 for(pb
=pa
;pb
[0]!=0;pb
++)
176 if(!isdigit(pb
[0])) break;
179 if(pb
==NULL
){ return -1e99
; }
188 for(i
=0;pb
[i
]!=0;i
++)
190 if(!isdigit(pb
[i
]))break;
201 case 'f': v
*=1e-15; break;
202 case 'p': v
*=1e-12; break;
203 case 'n': v
*=1e-9; break;
204 case 'u': v
*=1e-6; break;
205 case 'm': v
*=1e-3; break;
206 case 'x': v
*=1e6
; break;
207 case 'e': v
*=pow(10.0,(float)atoi(pb
+i
+1));
208 for(tmpc
=pb
+i
+1;isdigit(tmpc
[0]);tmpc
++);
211 case 0: offset
--; break;
212 case ' ': offset
--; break;
213 default: offset
--; break; /* dont eat this char */
216 /* this is for the case of '5fF' where we have some trailing alpha chars for units which
217 must be discarded so the parser doesn't get confused
219 while((a
=val
[offset
])!=0)
221 if(isalpha(a
))offset
++;
225 D(10,fprintf(stderr
,"%s = %e\n",val
,v
));
226 if(count
!=NULL
)*count
=offset
;
231 static int eqntoken_parse(eqntop_t
*top
)
240 if(isspace(top
->last
)||(top
->last
==0))uselast
=0;
244 for(p
=top
->str
;(isspace(p
[0]))&&(p
[0]!=0);p
++);
250 if(top
->nodep
>=MAXEQNSTACK
)return 0;
252 node
=top
->nodes
+top
->nodep
++;
253 node
->z
.lit
.lit
=NULL
;
256 case EQN_DELIM
: if(!uselast
)top
->str
++; /* fall through */
257 case 0: node
->op
=OPeolist
; r
=0; break;
258 case '+': node
->op
=OPadd
; if(!uselast
)top
->str
++; top
->opc
++; break;
259 case '-': node
->op
=OPsub
; if(!uselast
)top
->str
++; top
->opc
++; break;
260 case '*': node
->op
=OPmul
; if(!uselast
)top
->str
++; top
->opc
++; break;
261 case '/': node
->op
=OPdiv
; if(!uselast
)top
->str
++; top
->opc
++; break;
262 case '^': node
->op
=OPexp
; if(!uselast
)top
->str
++; top
->opc
++; break;
263 case '(': node
->op
=OPopen
; if(!uselast
)top
->str
++; top
->opc
++; break;
264 case ')': node
->op
=OPclose
; if(!uselast
)top
->str
++; top
->opc
++; break;
270 node
->z
.val
.x
=float_parse(p
,&count
);
277 if(strchr("\'+-/*^\n\r()",p
[i
])!=NULL
)break;
280 if( (top
->last
==0) || (top
->last
==EQN_DELIM
)) { r
=0; top
->str
=p
+i
; }
284 /* can never have alphanumeral followed by alphanumeral without some delimiter,
285 since it would just become one big identifer
287 node
->z
.lit
.ref
=eqn_litref_INIT
;
288 node
->z
.lit
.cache
=0.0;
301 /* this code assumes dependencies have already been cached, see eqnlist.c */
302 static int token_value(eqntoken_t
*token
, float *v
)
309 if(token
->z
.pval
.xp
!=NULL
)
310 a
=*(token
->z
.pval
.xp
);
312 D(3,fprintf(stderr
,"loaded pval=(%p->%g)\n",token
->z
.pval
.xp
,a
));
314 case OPeol_const
: a
=token
->z
.val
.x
; break;
315 case OPlit
: a
= 0.0; r
++; break;
316 case OPlitm
: a
=-0.0; r
++; break;
317 case OPlitv
: a
= token
->z
.lit
.cache
; break;
318 case OPlitvm
: a
=-(token
->z
.lit
.cache
); break;
319 case OPval
: a
= token
->z
.val
.x
; break;
320 case OPvalm
: a
=-(token
->z
.val
.x
); break;
326 #define PUSH(a) do { if(data->stackp<MAXSTACK) data->stack[data->stackp++]=(a); \
327 D(5,fprintf(stderr,"Push(%g) new stackp=%i\n",(a),data->stackp)); } while (0)
328 #define POP(a) do { if(data->stackp>0) (a)=data->stack[--(data->stackp)]; \
329 D(5,fprintf(stderr,"Pop(%g) new stackp=%i\n",(a),data->stackp)); } while (0)
331 static int eqntoken_eval(eqneval_t
*data
)
336 eqntoken_t
*token
,*lp
;
339 static const int precidence
[]=OPprecidence
;
340 static const int type
[]=OPtype
;
342 int types
[4]={ -10 , -10 , -10 , -10 };
343 int precs
[4]={ -10, -10 , -10, -10 };
344 int stack_entry
=data
->stackp
;
349 itemop_t ops
[4]={OPeolist
, OPeolist
, OPeolist
, OPeolist
};
350 eqntoken_t
*tokens
[4]={ NULL
, NULL
, NULL
, NULL
};
354 data
->list
=eqntoken_next(data
->list
);
355 if(data
->list
==token
)
356 toend
++; /* catch endings */
358 for(lp
=token
,i
=0;(i
<4);i
++,lp
=eqntoken_next(lp
)) /* lookahead */
363 assert((ops
[i
]*sizeof(int))<sizeof(precidence
));
364 assert((ops
[i
]*sizeof(int))<sizeof(type
));
365 types
[i
]=type
[ops
[i
]];
366 precs
[i
]=precidence
[ops
[i
]];
367 if(OP_ISEND(lp
->op
)) /* we did last token, stop */
371 D(8,fprintf(stderr
,"Eval: lookahead = "));
375 D(8,fprintf(stderr
," %s,t%i,p%i" ,syms
[ops
[i
]],types
[i
],precs
[i
]));
377 D(8,fprintf(stderr
,"\n"));
379 if(OP_ISANYV(token
->op
))
381 r
=token_value(token
,&a
);
382 D(5,fprintf(stderr
,"Value: %g\n",a
));
388 if(types
[0]==2) /* binary op */
393 if( /* literal and higher precidence operator after literal not () */
394 (OP_ISV(ops
[1]) && (precs
[2] > precs
[0]) && (types
[2]!=4) ) ||
396 /* operator (ie open/close) which has a higher precidence */
397 ((types
[1]>=2) && (precs
[1] > precs
[0]))
401 D(6,fprintf(stderr
,"Shift: recursion\n"));
410 D(6,fprintf(stderr
,"Reduce: \n"));
411 if( OP_ISANYV(ops
[1]) )
413 data
->list
=eqntoken_next(data
->list
);
414 r
+=token_value(tokens
[1],&b
);
417 else { /* error, need literal */ assert(0); }
420 D(6,fprintf(stderr
,"Doing Binary Op, %g op %g \n",a
,b
));
423 case OPadd
: a
=a
+b
; break;
424 case OPsub
: a
=a
-b
; break;
425 case OPmul
: a
=a
*b
; break;
431 case OPexp
: a
=pow(a
,b
); break;
439 if(types
[0]==4) /* open/close */
441 if(token
->op
==OPopen
)
443 D(6,fprintf(stderr
,"open { : \n"));
446 else if(token
->op
==OPclose
)
448 D(6,fprintf(stderr
,"close } : returning \n"));
453 else if(token
->op
== OPeolist
);
455 if(toend
) { D(6,fprintf(stderr
,"Ending: \n")); return 0; }
458 D(6,fprintf(stderr
,"Stack: entry=%i now=%i %s \n",stack_entry
,data
->stackp
,
459 ((stack_entry
<data
->stackp
)||loop
)?"looping":"returning"));
462 while((stack_entry
<data
->stackp
)||loop
);
470 eqntoken_t
*eqntok_parse(char *str
)
473 int i
,j
,qlit
,qval
,qvalp
;
476 eqntoken_t
*list
,*lp
,*lpn
;
485 D(2,fprintf(stderr
,"Parsing [%s]\n",str
));
486 while(eqntoken_parse(&data
));
488 for(i
=0,qval
=0,qlit
=0,qvalp
=0,j
=0;i
<data
.nodep
;i
++)
490 switch(data
.nodes
[i
].op
)
506 if((qval
==1) && (qlit
==0) && (qvalp
==0) ) /* optimization for single constant, don't store eolist */
508 eq
=eqn_const(data
.nodes
[0].z
.val
.x
);
511 else if((qvalp
==1) && (qlit
==0) && (qval
==0) )
513 eq
=eqn_valp(data
.nodes
[0].z
.pval
.xp
);
516 else if((qvalp
==0) && (qlit
==0) && (qval
==0) )
518 /* it appears we have a parse error, no equation is possible */
520 fprintf(stderr
,"Invalid Equation \n");
525 lp
=list
=eqn_malloc((sizeof(itemop_t
)*(j
+1)) + (sizeof(eqntokenlit_t
)*(qlit
)) + (sizeof(eqntokenval_t
)*(qval
)));
527 for(last_was_op
=1,i
=j
=0;i
<data
.nodep
;i
++)
533 if( (last_was_op
) && (cp
->op
==OPsub
) && (type
[np
->op
]==1) )
535 np
->op
++; /* go from OPxxx to OPxxxm */
545 lpn
=eqntoken_next(lp
);
546 memcpy(lp
,cp
,((char *)lpn
)-((char *)lp
));
549 /* list[j++]=cp[0]; */
551 /*memcpy(list,data.nodes,data->nodep*sizeof(eqntok_t));*/
553 /* list[j].op=OPeolist; */
559 fprintf(stderr
, "eqnparse: %i tokens %p ", data
.nodep
,lp
);
560 for(lp
=list
;!OP_ISEND(lp
->op
);lp
=eqntoken_next(lp
))
561 fprintf(stderr
, "%s ",syms
[lp
->op
]);
562 fprintf(stderr
, "%s ",syms
[lp
->op
]);
563 fprintf(stderr
,"\n");
571 /* returns 0 if equation can be evaluated */
572 int eqntok_depend(eqntoken_t
*list
, plookup_t
*lookup
)
578 if(list
->op
==OPeol_const
)return 0; /*optimization */
579 assert(lookup
!=NULL
);
581 for(lp
=list
;!OP_ISEND(lp
->op
);lp
=eqntoken_next(lp
))
583 if((lp
->op
==OPlit
)||(lp
->op
==OPlitm
))
585 D(3,fprintf(stderr
,"eqntok_depend: Looking up %s\n",lp
->z
.lit
.lit
));
586 if((vp
=lookup
->lookup(lookup
,lp
->z
.lit
.lit
))!=eqn_litref_INIT
)
589 lp
->op
=(lp
->op
==OPlit
)?OPlitv
:OPlitvm
;
597 /* only evaluate after eqntok_depend, and
598 call eqntok_eval IN PROPER ORDER
599 returns 0 if eval ok, 1 if broken dependency, 2 on parse error
602 int eqntok_eval(float *res
, eqntoken_t
*list
)
606 memset(&data
,0,sizeof(data
));
607 data
.stack
[(data
.stackp
=0)]=0.0;
613 D(3,debug_eqn(stderr
,"Evaluating ",&v
));
614 D(3,fprintf(stderr
,"\n"));
616 for(lp
=list
;!OP_ISEND(lp
->op
);lp
=eqntoken_next(lp
))
618 if(lp
->op
==OPlit
)return 1;
621 eqntoken_eval(&data
);
626 if(data
.stackp
==2)return 0;
632 eqntoken_t
*eqntok_copy(eqntoken_t
*p
)
637 q
=((char *)np
)-((char *)p
);
645 eqntoken_t
*eqntok_copy_m(eqntoken_t
*p
, float m
)
650 if(p
->op
==OPeol_const
) /* common case */
652 np
=eqn_malloc(sizeof(itemop_t
)+sizeof(eqntokenval_t
));
654 np
->z
.val
.x
=m
*p
->z
.val
.x
;
656 else /* build np = m*(old_equation)EOL */
659 q
=((char *)np
)-((char *)p
);
661 np
=eqn_malloc(q
+(sizeof(itemop_t
)*4)+sizeof(eqntokenval_t
));
665 nnp
=eqntoken_next(nnp
);
667 nnp
=eqntoken_next(nnp
);
669 nnp
=eqntoken_next(nnp
);
671 nnp
=(void *) (((char *)nnp
)+q
-sizeof(itemop_t
));
673 nnp
=eqntoken_next(nnp
);
683 void debug_eqn(void *dbg_fp
, char *str
, eqn_t
*eqn
)
688 fprintf(dbg
,"%g",eqn
->val
);
692 if(0)fprintf(dbg
," %s=[%p %p]",str
,eqn
,eqn
->eqn
);
693 else fprintf(dbg
," %s=",str
);
697 for(eqt
=eqn
->eqn
;!OP_ISEND(eqt
->op
);eqt
=eqntoken_next(eqt
))
704 fprintf(dbg
,"(%c%s)",s
,eqt
->z
.lit
.lit
); break;
708 fprintf(dbg
,"(%c%g)",s
,eqt
->z
.val
.x
); break;
712 fprintf(dbg
,"(%c%s=%i)",s
,eqt
->z
.lit
.lit
,eqt
->z
.lit
.ref
); break;
714 case OPadd
: fprintf(dbg
,"+"); break;
715 case OPsub
: fprintf(dbg
,"-"); break;
716 case OPmul
: fprintf(dbg
,"*"); break;
717 case OPdiv
: fprintf(dbg
,"/"); break;
718 case OPexp
: fprintf(dbg
,"^"); break;
719 case OPopen
: fprintf(dbg
,"{"); break;
720 case OPclose
: fprintf(dbg
,"}"); break;
724 if(eqt
->op
==OPeol_const
)
725 fprintf(dbg
,"%g",eqt
->z
.val
.x
);
726 if(eqt
->op
==OPeol_valp
)
727 fprintf(dbg
,"%p->%g",eqt
->z
.pval
.xp
, (eqt
->z
.pval
.xp
==NULL
)?0.0:*(eqt
->z
.pval
.xp
));
740 eqn_t
eqn_const(float val
)
746 eqn
.eqn
=eqn_malloc(sizeof(itemop_t
)+sizeof(eqntokenval_t
));
747 eqn
.eqn
->op
=OPeol_const
;
748 eqn
.eqn
->z
.val
.x
=val
;
753 eqn_t
eqn_valp(float *valp
)
759 r
.eqn
=eqn_malloc(sizeof(itemop_t
)+sizeof(eqntokenpval_t
));
760 r
.eqn
->z
.pval
.xp
=valp
;
761 r
.eqn
->op
=OPeol_valp
;
767 eqn_t
eqn_undefined(void)
769 return eqn_valp(NULL
);
772 int eqn_is_undefined(eqn_t e
)
779 if(e
.eqn
->op
==OPeol_valp
)
786 eqn_t
eqn_copy(eqn_t e
)
792 e
.eqn
=eqntok_copy(e
.eqn
);
797 eqn_t
eqn_copy_m(eqn_t e
, float m
)
805 if(m
==1.0) /* multiplication by 1 is a nop */
806 e
.eqn
=eqntok_copy(e
.eqn
);
808 e
.eqn
=eqntok_copy_m(e
.eqn
,m
);
814 eqn_t
eqn_parse(uchar
*val
)
818 eqn
.val
=float_parse(val
,NULL
);
820 eqn
.eqn
=eqntok_parse(val
);
825 float parse_float(uchar
*val
)
827 return float_parse(val
,NULL
);
831 /* this is valid ONLY if we have no literals, ie (6+3*5) */
832 /* use eqnl_eval for equations like (lambda*5+delta) */
834 float eqn_getval_(eqn_t
*eqn
)
842 assert(eqn
->eqn
!=NULL
);
843 if(eqn
->eqn
->op
==OPeol_const
)
844 return eqn
->eqn
->z
.val
.x
;
846 eqntok_eval(&f
,eqn
->eqn
);
851 eqn_t
eqn_empty(void)
862 float eqn_setval(eqn_t
*eqn
, float val
)
866 return (eqn
->val
=val
);
871 if(eqn
->eqn
->op
==OPeol_const
)
872 eqn
->eqn
->z
.val
.x
=val
;
875 D(1,debug_eqn(stderr
, "cannot change eqn",eqn
));
876 D(1,fprintf(stderr
,"\n"));
883 float eqn_setvalp(eqn_t
*eqn
, float *valp
)
888 return (eqn
->val
=*valp
);
893 if(eqn
->eqn
->op
==OPeol_valp
)
894 eqn
->eqn
->z
.pval
.xp
=valp
;
897 D(1,debug_eqn(stderr
, "cannot set valp",eqn
));
898 D(1,fprintf(stderr
,"\n"));