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 /* equations.c, equation evaluation and parsing
32 #include "equations.h"
36 #ifndef STRIP_DEBUGGING
37 #define D(level,a) do { if(__debug_eqn__>(level)) a; } while(0)
43 #define uchar unsigned char
47 static eqntoken_t
*eqntoken_next(eqntoken_t
*p
);
50 static void debug_eqn(FILE *fp
, char *str
, eqn_t
*eqn
)
54 fprintf(fp
," %s=",str
);
58 for(eqt
=eqn
->eqn
;!OP_ISEND(eqt
->op
);eqt
=eqntoken_next(eqt
))
65 fprintf(fp
,"(%c%s)",s
,eqt
->z
.lit
.lit
); break;
69 fprintf(fp
,"(%c%g)",s
,eqt
->z
.val
.x
); break;
73 fprintf(fp
,"(%c%p->%g)",s
,eqt
->z
.pval
.xp
,eqt
->z
.pval
.xp
[0]); break;
75 case OPadd
: fprintf(fp
,"+"); break;
76 case OPsub
: fprintf(fp
,"-"); break;
77 case OPmul
: fprintf(fp
,"*"); break;
78 case OPdiv
: fprintf(fp
,"/"); break;
79 case OPexp
: fprintf(fp
,"^"); break;
80 case OPopen
: fprintf(fp
,"{"); break;
81 case OPclose
: fprintf(fp
,"}"); break;
82 case OPnorm
: fprintf(fp
,":norm:"); break; /* A norm B == norm_B(A) */
86 if(eqt
->op
==OPeol_const
)
87 fprintf(fp
,"%g",eqt
->z
.val
.x
);
88 if(eqt
->op
==OPeol_valp
)
89 fprintf(fp
,"%p->%g",eqt
->z
.pval
.xp
, (eqt
->z
.pval
.xp
==NULL
)?0.0:*(eqt
->z
.pval
.xp
));
96 static void *eqn_malloc(int size
)
98 void *p
=calloc(1,size
);
101 static char *eqn_strdup(char *p
)
106 static void eqn_free(void *p
)
107 { if(p
!=NULL
) free(p
); }
111 static eqntoken_t
*eqntoken_next(eqntoken_t
*p
)
116 case OPeolist
: case OPeol_const
: case OPeol_valp
: case OPeol_qty
:
118 case OPval
: case OPvalm
: r
= (void *) (((char *)p
)+(sizeof(itemop_t
)+sizeof(eqntokenval_t
) )) ; break;
119 case OPlit
: case OPlitm
:
120 case OPlitv
: case OPlitvm
: r
= (void *) (((char *)p
)+(sizeof(itemop_t
)+sizeof(eqntokenlit_t
) )) ; break;
121 default: r
= (void *) (((char *)p
)+(sizeof(itemop_t
))) ; break;
128 static eqntoken_t
*eqntoken_last(eqntoken_t
*p
)
132 while(!OP_ISEND(p
->op
))p
=eqntoken_next(p
);
137 r
= (void *) (((char *)p
)+(sizeof(itemop_t
)+sizeof(eqntokenval_t
))) ; break;
139 r
= (void *) (((char *)p
)+(sizeof(itemop_t
)+sizeof(eqntokenpval_t
))) ; break;
141 r
= (void *) (((char *)p
)+(sizeof(itemop_t
))) ; break;
154 static inline char t_peek(eqntop_t
*top
)
156 if(top
->n_unget
>0) { return top
->unget
[top
->n_unget
-1]; }
160 static inline char t_eat(eqntop_t
*top
)
164 if(top
->n_unget
>0) top
->n_unget
++;
169 static inline void t_uneat(eqntop_t
*top
, char a
)
171 if(top
->n_unget
>(sizeof(top
->unget
)-1)) assert(0);
174 top
->unget
[top
->n_unget
]=a
;
182 static float float_parse(eqntop_t
*top
)
184 float v
=0.0,sign
=1,sig
=0,rest
=0,digit
,place
,exp
;
187 while(isspace(t_peek(top
))) t_eat(top
);
189 if(a
=='-') { sign
=-1; t_eat(top
); }
190 else if(a
=='+') { sign
=1; t_eat(top
); }
192 if(t_peek(top
)=='.') { sig
=0; t_eat(top
); }
196 while(isdigit(t_peek(top
)))
197 { sig
*=10; sig
+=(t_eat(top
)-'0'); }
200 if(t_peek(top
)=='.') t_eat(top
);
201 if(isdigit(t_peek(top
)))
204 while(isdigit(t_peek(top
)))
205 { digit
=(t_eat(top
)-'0'); rest
+=(digit
*place
); place
*=0.1; }
214 case 'f': t_eat(top
); v
*=1e-15; break;
215 case 'p': t_eat(top
); v
*=1e-12; break;
216 case 'n': t_eat(top
); v
*=1e-9; break;
217 case 'u': t_eat(top
); v
*=1e-6; break;
218 case 'm': t_eat(top
); v
*=1e-3; break;
219 case 'x': t_eat(top
); v
*=1e6
; break;
220 case 'e': t_eat(top
);
222 while(isdigit(t_peek(top
)))
223 { exp
*=10; exp
+=(t_eat(top
)-'0'); }
227 if(isalpha(t_peek(top
))) /* eat trailing alphanumerics (ie. 15fF <- F is extra alpha) */
230 D(10,fprintf(stdout
,"NUM[%e]\n",v
));
237 static int eqntoken_parse(eqntop_t
*top
)
244 if(top
->nodep
>=MAXEQNSTACK
)return 0;
246 node
=top
->nodes
+top
->nodep
++;
247 node
->z
.lit
.lit
=NULL
;
250 case EQN_DELIM
: t_eat(top
); /* fall through */
251 case 0: node
->op
=OPeolist
; return 0;
252 case '+': node
->op
=OPadd
; t_eat(top
); top
->opc
++; break;
253 case '-': node
->op
=OPsub
; t_eat(top
); top
->opc
++; break;
254 case '*': node
->op
=OPmul
; t_eat(top
); top
->opc
++; break;
255 case '/': node
->op
=OPdiv
; t_eat(top
); top
->opc
++; break;
256 case '^': node
->op
=OPexp
; t_eat(top
); top
->opc
++; break;
257 case '(': node
->op
=OPopen
; t_eat(top
); top
->opc
++; break;
258 case ')': node
->op
=OPclose
; t_eat(top
); top
->opc
++; break;
259 case '$': node
->op
=OPnorm
; t_eat(top
); top
->opc
++; break;
265 node
->z
.val
.x
=float_parse(top
);
271 p
=alloca(strlen(top
->str
)+sizeof(top
->unget
)+16);
273 if(c
=='{') { inquote
++; t_eat(top
); c
=t_peek(top
); }
275 for(i
=0;c
!=0;c
=t_peek(top
),i
++)
277 if(!inquote
) { if(strchr("\'+-/*^\n\r()",c
)!=NULL
)break; }
278 if(inquote
&& (c
=='{')) inquote
++;
279 if(inquote
&& (c
=='}')) { inquote
--; if(inquote
==0) { t_eat(top
); break; }}
284 D(10,fprintf(stdout
,"LIT[%s]\n",p
));
285 node
->z
.lit
.lit
=eqn_strdup(p
);
295 /* this code assumes dependencies have already been cached, see eqnlist.c */
296 static int token_value(eqntoken_t
*token
, float *v
)
303 if(token
->z
.pval
.xp
!=NULL
)
304 a
=*(token
->z
.pval
.xp
);
306 D(3,fprintf(stdout
,"loaded pval=(%p->%g)\n",token
->z
.pval
.xp
,a
));
308 case OPeol_const
: a
=token
->z
.val
.x
; break;
309 case OPlit
: a
= 0.0; r
++; break;
310 case OPlitm
: a
=-0.0; r
++; break;
311 case OPlitv
: a
= *(token
->z
.pval
.xp
); break;
312 case OPlitvm
: a
=-(*(token
->z
.pval
.xp
)); break;
313 case OPval
: a
= token
->z
.val
.x
; break;
314 case OPvalm
: a
=-(token
->z
.val
.x
); break;
320 #define PUSH(a) do { if(data->stackp<MAXSTACK) data->stack[data->stackp++]=(a); \
321 D(5,fprintf(stdout,"Push(%g) new stackp=%i\n",(a),data->stackp)); } while (0)
322 #define POP(a) do { if(data->stackp>0) (a)=data->stack[--(data->stackp)]; \
323 D(5,fprintf(stdout,"Pop(%g) new stackp=%i\n",(a),data->stackp)); } while (0)
325 static int eqntoken_eval(eqneval_t
*data
)
330 eqntoken_t
*token
,*lp
;
333 static const int precidence
[]=OPprecidence
;
334 static const int type
[]=OPtype
;
336 int types
[4]={ -10 , -10 , -10 , -10 };
337 int precs
[4]={ -10, -10 , -10, -10 };
338 int stack_entry
=data
->stackp
;
343 itemop_t ops
[4]={OPeolist
, OPeolist
, OPeolist
, OPeolist
};
344 eqntoken_t
*tokens
[4]={ NULL
, NULL
, NULL
, NULL
};
348 data
->list
=eqntoken_next(data
->list
);
349 if(data
->list
==token
)
350 toend
++; /* catch endings */
352 for(lp
=token
,i
=0;(i
<4);i
++,lp
=eqntoken_next(lp
)) /* lookahead */
357 assert((ops
[i
]*sizeof(int))<sizeof(precidence
));
358 assert((ops
[i
]*sizeof(int))<sizeof(type
));
359 types
[i
]=type
[ops
[i
]];
360 precs
[i
]=precidence
[ops
[i
]];
361 if(OP_ISEND(lp
->op
)) /* we did last token, stop */
365 D(8,fprintf(stdout
,"Eval: lookahead = "));
369 D(8,fprintf(stdout
," %s,t%i,p%i" ,syms
[ops
[i
]],types
[i
],precs
[i
]));
371 D(8,fprintf(stdout
,"\n"));
373 if(OP_ISANYV(token
->op
))
375 r
=token_value(token
,&a
);
376 D(5,fprintf(stdout
,"Value: %g\n",a
));
382 if(types
[0]==2) /* binary op */
387 if( /* literal and higher precidence operator after literal not () */
388 (OP_ISV(ops
[1]) && (precs
[2] > precs
[0]) && (types
[2]!=4) ) ||
390 /* operator (ie open/close) which has a higher precidence */
391 ((types
[1]>=2) && (precs
[1] > precs
[0]))
395 D(6,fprintf(stdout
,"Shift: recursion\n"));
404 D(6,fprintf(stdout
,"Reduce: \n"));
405 if( OP_ISANYV(ops
[1]) )
407 data
->list
=eqntoken_next(data
->list
);
408 r
+=token_value(tokens
[1],&b
);
410 else { /* error, need literal */ assert(0); }
413 D(6,fprintf(stdout
,"Doing Binary Op, %g op %g \n",a
,b
));
416 case OPadd
: a
=a
+b
; break;
417 case OPsub
: a
=a
-b
; break;
418 case OPmul
: a
=a
*b
; break;
424 case OPexp
: a
=pow(a
,b
); break;
425 case OPnorm
: if(b
!=0) a
=pow(pow(a
,b
),1/b
); else a
=0; break;
433 if(types
[0]==4) /* open/close */
435 if(token
->op
==OPopen
)
437 D(6,fprintf(stdout
,"open { : \n"));
440 else if(token
->op
==OPclose
)
442 D(6,fprintf(stdout
,"close } : returning \n"));
447 else if(token
->op
== OPeolist
);
449 if(toend
) { D(6,fprintf(stdout
,"Ending: \n")); return 0; }
452 D(6,fprintf(stdout
,"Stack: entry=%i now=%i %s \n",stack_entry
,data
->stackp
,
453 ((stack_entry
<data
->stackp
)||loop
)?"looping":"returning"));
456 while((stack_entry
<data
->stackp
)||loop
);
464 static eqntoken_t
*eqntok_parse(char *str
)
466 int i
,j
,qlit
,qval
,qvalp
;
469 eqntoken_t
*list
,*lp
,*lpn
;
472 memset(&data
,0,sizeof(data
));
479 D(2,fprintf(stdout
,"Parsing [%s]\n",str
));
481 if(data
.str
[0]==EQN_DELIM
) data
.str
++; /* eat equation prefix delimiter */
482 while(eqntoken_parse(&data
));
484 for(i
=0,qval
=0,qlit
=0,qvalp
=0,j
=0;i
<data
.nodep
;i
++)
486 switch(data
.nodes
[i
].op
)
502 /* fixme. undo bitrot */
504 if((qval
==1) && (qlit
==0) && (qvalp
==0) ) /* optimization for single constant, don't store eolist */
506 eq
=eqn_const(data
.nodes
[0].z
.val
.x
);
509 else if((qvalp
==1) && (qlit
==0) && (qval
==0) )
511 eq
=eqn_valp(data
.nodes
[0].z
.pval
.xp
);
514 else if((qvalp
==0) && (qlit
==0) && (qval
==0) )
516 /* it appears we have a parse error, no equation is possible */
518 fprintf(stdout
,"Invalid Equation \n");
524 lp
=list
=eqn_malloc((sizeof(itemop_t
)*(j
+1)) + (sizeof(eqntokenlit_t
)*(qlit
)) + (sizeof(eqntokenval_t
)*(qval
)));
526 for(last_was_op
=1,i
=j
=0;i
<data
.nodep
;i
++)
532 if( (last_was_op
) && (cp
->op
==OPsub
) && (type
[np
->op
]==1) )
534 np
->op
++; /* go from OPxxx to OPxxxm */
544 lpn
=eqntoken_next(lp
);
545 memcpy(lp
,cp
,((char *)lpn
)-((char *)lp
));
554 fprintf(stdout
, "eqnparse: %i tokens %p ", data
.nodep
,lp
);
555 for(lp
=list
;!OP_ISEND(lp
->op
);lp
=eqntoken_next(lp
))
556 fprintf(stdout
, "%s ",syms
[lp
->op
]);
557 fprintf(stdout
, "%s ",syms
[lp
->op
]);
558 fprintf(stdout
,"\n");
569 /* only evaluate after eqntok_depend, and
570 call eqntok_eval IN PROPER ORDER
571 returns 0 if eval ok, 1 if broken dependency, 2 on parse error
574 static int eqntok_eval(float *res
, eqntoken_t
*list
)
578 memset(&data
,0,sizeof(data
));
579 data
.stack
[(data
.stackp
=0)]=0.0;
585 D(3,debug_eqn(stdout
,"Evaluating \n",&v
));
588 /* check that all dependencies have been taken care of */
589 for(lp
=list
;!OP_ISEND(lp
->op
);lp
=eqntoken_next(lp
))
591 if((lp
->op
==OPlit
)||(lp
->op
==OPlitm
))return 1;
594 eqntoken_eval(&data
);
599 if(data
.stackp
==2)return 0;
605 static eqntoken_t
*eqntok_copy(eqntoken_t
*p
)
610 q
=((char *)np
)-((char *)p
);
618 static eqntoken_t
*eqntok_copy_m(eqntoken_t
*p
, float m
)
623 if(p
->op
==OPeol_const
) /* common case */
625 np
=eqn_malloc(sizeof(itemop_t
)+sizeof(eqntokenval_t
));
627 np
->z
.val
.x
=m
*p
->z
.val
.x
;
629 else /* build np = m*(old_equation)EOL */
632 q
=((char *)np
)-((char *)p
);
634 np
=eqn_malloc(q
+(sizeof(itemop_t
)*4)+sizeof(eqntokenval_t
));
638 nnp
=eqntoken_next(nnp
);
640 nnp
=eqntoken_next(nnp
);
642 nnp
=eqntoken_next(nnp
);
644 nnp
=(void *) (((char *)nnp
)+q
-sizeof(itemop_t
));
646 nnp
=eqntoken_next(nnp
);
661 eqn_t
equation_parse(uchar
*str
)
665 eqn
.eqn
=eqntok_parse(str
);
673 /* returns 0 if equation can be evaluated */
674 int equation_depend(eqn_t eqn
, float *(lookup
)(void *user
, char *str
), void *user
)
678 eqntoken_t
*lp
,*list
;
680 if(list
==NULL
)return -1;
682 if(list
->op
==OPeol_const
)return 0; /*optimization */
684 for(lp
=list
;!OP_ISEND(lp
->op
);lp
=eqntoken_next(lp
))
686 if((lp
->op
==OPlit
)||(lp
->op
==OPlitm
))
688 D(50,fprintf(stdout
,"eqntok_depend: Looking up %s\n",lp
->z
.lit
.lit
));
689 if((vp
=lookup(user
,lp
->z
.lit
.lit
))!=NULL
)
691 eqn_free(lp
->z
.lit
.lit
);
693 lp
->op
=(lp
->op
==OPlit
)?OPlitv
:OPlitvm
;
702 int equation_eval(eqn_t
*peqn
)
707 r
=eqntok_eval(&(peqn
->val
), peqn
->eqn
);
708 D(1,fprintf(stdout
,"Result: %g\n",peqn
->val
));
713 eqn_t
equation_empty(eqn_t eqn
)
716 /* should traverse equations first to free any literal strings */
718 for(eqt
=eqn
.eqn
;(eqt
!=NULL
);eqt
=eqntoken_next(eqt
))
724 eqn_free(eqt
->z
.lit
.lit
);
728 if(OP_ISEND(eqt
->op
)) break;
737 void equation_debug(eqn_t eqn
, void *fp
)
740 sprintf(buf
,"eqn=%g \n ",eqn
.val
);
741 debug_eqn(fp
, buf
, &eqn
);