xref: /freebsd/usr.sbin/pmcstudy/eval_expr.c (revision 463a577b274c9523cb72f534d42e7efa4d8e1c3d)
1d95b3509SRandall Stewart /*-
2d95b3509SRandall Stewart  * Copyright (c) 2015 Netflix Inc.
3d95b3509SRandall Stewart  * All rights reserved.
4d95b3509SRandall Stewart  *
5d95b3509SRandall Stewart  * Redistribution and use in source and binary forms, with or without
6d95b3509SRandall Stewart  * modification, are permitted provided that the following conditions
7d95b3509SRandall Stewart  * are met:
8d95b3509SRandall Stewart  * 1. Redistributions of source code must retain the above copyright
9d95b3509SRandall Stewart  *    notice, this list of conditions and the following disclaimer,
10d95b3509SRandall Stewart  *    in this position and unchanged.
11d95b3509SRandall Stewart  * 2. Redistributions in binary form must reproduce the above copyright
12d95b3509SRandall Stewart  *    notice, this list of conditions and the following disclaimer in the
13d95b3509SRandall Stewart  *    documentation and/or other materials provided with the distribution.
14d95b3509SRandall Stewart  * 3. The name of the author may not be used to endorse or promote products
15d95b3509SRandall Stewart  *    derived from this software without specific prior written permission
16d95b3509SRandall Stewart  *
17d95b3509SRandall Stewart  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18d95b3509SRandall Stewart  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19d95b3509SRandall Stewart  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20d95b3509SRandall Stewart  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21d95b3509SRandall Stewart  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22d95b3509SRandall Stewart  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23d95b3509SRandall Stewart  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24d95b3509SRandall Stewart  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25d95b3509SRandall Stewart  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26d95b3509SRandall Stewart  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27d95b3509SRandall Stewart  */
28d95b3509SRandall Stewart #include <sys/types.h>
29d95b3509SRandall Stewart #include <stdio.h>
30d95b3509SRandall Stewart #include <stdlib.h>
31d95b3509SRandall Stewart #include <unistd.h>
32d95b3509SRandall Stewart #include <string.h>
33d95b3509SRandall Stewart #include <strings.h>
34d95b3509SRandall Stewart #include <ctype.h>
35d95b3509SRandall Stewart #include "eval_expr.h"
36d95b3509SRandall Stewart __FBSDID("$FreeBSD$");
37d95b3509SRandall Stewart 
38d95b3509SRandall Stewart static struct expression *
39d95b3509SRandall Stewart alloc_and_hook_expr(struct expression **exp_p, struct expression **last_p)
40d95b3509SRandall Stewart {
41d95b3509SRandall Stewart 	struct expression *ex, *at;
42d95b3509SRandall Stewart 
43d95b3509SRandall Stewart 	ex = malloc(sizeof(struct expression));
44d95b3509SRandall Stewart 	if (ex == NULL) {
45d95b3509SRandall Stewart 		printf("Out of memory in exp allocation\n");
46d95b3509SRandall Stewart 		exit(-2);
47d95b3509SRandall Stewart 	}
48d95b3509SRandall Stewart 	memset(ex, 0, sizeof(struct expression));
49d95b3509SRandall Stewart 	if (*exp_p == NULL) {
50d95b3509SRandall Stewart 		*exp_p = ex;
51d95b3509SRandall Stewart 	}
52d95b3509SRandall Stewart 	at = *last_p;
53d95b3509SRandall Stewart 	if (at == NULL) {
54d95b3509SRandall Stewart 		/* First one, its last */
55d95b3509SRandall Stewart 		*last_p = ex;
56d95b3509SRandall Stewart 	} else {
57d95b3509SRandall Stewart 		/* Chain it to the end and update last */
58d95b3509SRandall Stewart 		at->next = ex;
59d95b3509SRandall Stewart 		ex->prev = at;
60d95b3509SRandall Stewart 		*last_p = ex;
61d95b3509SRandall Stewart 	}
62d95b3509SRandall Stewart 	return (ex);
63d95b3509SRandall Stewart }
64d95b3509SRandall Stewart 
65d95b3509SRandall Stewart 
66d95b3509SRandall Stewart static int
67d95b3509SRandall Stewart validate_expr(struct expression *exp, int val1_is_set, int op_is_set, int val2_is_set,
68d95b3509SRandall Stewart 	      int *op_cnt)
69d95b3509SRandall Stewart {
70d95b3509SRandall Stewart 	int val1, op, val2;
71d95b3509SRandall Stewart 	int open_cnt;
72d95b3509SRandall Stewart 	val1 = op = val2 = 0;
73d95b3509SRandall Stewart 	if (val1_is_set) {
74d95b3509SRandall Stewart 		val1 = 1;
75d95b3509SRandall Stewart 	}
76d95b3509SRandall Stewart 	if (op_is_set) {
77d95b3509SRandall Stewart 		op = 1;
78d95b3509SRandall Stewart 	}
79d95b3509SRandall Stewart 	if (val2_is_set) {
80d95b3509SRandall Stewart 		val2 = 1;
81d95b3509SRandall Stewart 	}
82d95b3509SRandall Stewart 	open_cnt = *op_cnt;
83d95b3509SRandall Stewart 	if (exp == NULL) {
84d95b3509SRandall Stewart 		/* End of the road */
85d95b3509SRandall Stewart 		if (val1 && op && val2 && (open_cnt == 0)) {
86d95b3509SRandall Stewart 			return(0);
87d95b3509SRandall Stewart 		} else {
88d95b3509SRandall Stewart 			return(1);
89d95b3509SRandall Stewart 		}
90d95b3509SRandall Stewart 	}
91d95b3509SRandall Stewart 	switch(exp->type) {
92d95b3509SRandall Stewart 	case TYPE_OP_PLUS:
93d95b3509SRandall Stewart 	case TYPE_OP_MINUS:
94d95b3509SRandall Stewart 	case TYPE_OP_MULT:
95d95b3509SRandall Stewart 	case TYPE_OP_DIVIDE:
96d95b3509SRandall Stewart 		if (val1 && op && val2) {
97d95b3509SRandall Stewart 			/* We are at x + y +
98d95b3509SRandall Stewart 			 * collapse back to val/op
99d95b3509SRandall Stewart 			 */
100d95b3509SRandall Stewart 			val1 = 1;
101d95b3509SRandall Stewart 			op = 1;
102d95b3509SRandall Stewart 			val2 = 0;
103d95b3509SRandall Stewart 		} else if ((op == 0) && (val1)) {
104d95b3509SRandall Stewart 			op = 1;
105d95b3509SRandall Stewart 		} else {
106d95b3509SRandall Stewart 			printf("Op but no val1 set\n");
107d95b3509SRandall Stewart 			return(-1);
108d95b3509SRandall Stewart 		}
109d95b3509SRandall Stewart 		break;
110d95b3509SRandall Stewart 	case TYPE_PARN_OPEN:
111d95b3509SRandall Stewart 		if (exp->next == NULL) {
112d95b3509SRandall Stewart 			printf("NULL after open paren\n");
113d95b3509SRandall Stewart 			exit(-1);
114d95b3509SRandall Stewart 		}
115d95b3509SRandall Stewart 		if ((exp->next->type == TYPE_OP_PLUS) ||
116d95b3509SRandall Stewart 		    (exp->next->type == TYPE_OP_MINUS) ||
117d95b3509SRandall Stewart 		    (exp->next->type == TYPE_OP_DIVIDE) ||
118d95b3509SRandall Stewart 		    (exp->next->type == TYPE_OP_MULT)) {
119d95b3509SRandall Stewart 			printf("'( OP' -- not allowed\n");
120d95b3509SRandall Stewart 			return(-1);
121d95b3509SRandall Stewart 		}
122d95b3509SRandall Stewart 		if (val1 && (op == 0)) {
123d95b3509SRandall Stewart 			printf("'Val (' -- not allowed\n");
124d95b3509SRandall Stewart 			return(-1);
125d95b3509SRandall Stewart 		}
126d95b3509SRandall Stewart 		if (val1 && op && val2) {
127d95b3509SRandall Stewart 			printf("'Val OP Val (' -- not allowed\n");
128d95b3509SRandall Stewart 			return(-1);
129d95b3509SRandall Stewart 		}
130d95b3509SRandall Stewart 		open_cnt++;
131d95b3509SRandall Stewart 		*op_cnt = open_cnt;
132d95b3509SRandall Stewart 		if (val1) {
133d95b3509SRandall Stewart 			if (validate_expr(exp->next, 0, 0, 0, op_cnt) == 0) {
134d95b3509SRandall Stewart 				val2 = 1;
135d95b3509SRandall Stewart 			} else {
136d95b3509SRandall Stewart 				return(-1);
137d95b3509SRandall Stewart 			}
138d95b3509SRandall Stewart 		} else {
139d95b3509SRandall Stewart 			return(validate_expr(exp->next, 0, 0, 0, op_cnt));
140d95b3509SRandall Stewart 		}
141d95b3509SRandall Stewart 		break;
142d95b3509SRandall Stewart 	case TYPE_PARN_CLOSE:
143d95b3509SRandall Stewart 		open_cnt--;
144d95b3509SRandall Stewart 		*op_cnt = open_cnt;
145d95b3509SRandall Stewart 		if (val1 && op && val2) {
146d95b3509SRandall Stewart 			return(0);
147d95b3509SRandall Stewart 		} else {
148d95b3509SRandall Stewart 			printf("Found close paren and not complete\n");
149d95b3509SRandall Stewart 			return(-1);
150d95b3509SRandall Stewart 		}
151d95b3509SRandall Stewart 		break;
152d95b3509SRandall Stewart 	case TYPE_VALUE_CON:
153d95b3509SRandall Stewart 	case TYPE_VALUE_PMC:
154d95b3509SRandall Stewart 		if (val1 == 0) {
155d95b3509SRandall Stewart 			val1 = 1;
156d95b3509SRandall Stewart 		} else if (val1 && op) {
157d95b3509SRandall Stewart 			val2 = 1;
158d95b3509SRandall Stewart 		} else {
159d95b3509SRandall Stewart 			printf("val1 set, val2 about to be set op empty\n");
160d95b3509SRandall Stewart 			return(-1);
161d95b3509SRandall Stewart 		}
162d95b3509SRandall Stewart 		break;
163d95b3509SRandall Stewart 	default:
164d95b3509SRandall Stewart 		printf("unknown type %d\n", exp->type);
165d95b3509SRandall Stewart 		exit(-5);
166d95b3509SRandall Stewart 		break;
167d95b3509SRandall Stewart 	}
168d95b3509SRandall Stewart 	return(validate_expr(exp->next, val1, op, val2, op_cnt));
169d95b3509SRandall Stewart }
170d95b3509SRandall Stewart 
171d95b3509SRandall Stewart void
172d95b3509SRandall Stewart print_exp(struct expression *exp)
173d95b3509SRandall Stewart {
174d95b3509SRandall Stewart 	if (exp == NULL) {
175d95b3509SRandall Stewart 		printf("\n");
176d95b3509SRandall Stewart 		return;
177d95b3509SRandall Stewart 	}
178d95b3509SRandall Stewart 	switch(exp->type) {
179d95b3509SRandall Stewart 	case TYPE_OP_PLUS:
180d95b3509SRandall Stewart 		printf(" + ");
181d95b3509SRandall Stewart 		break;
182d95b3509SRandall Stewart 	case TYPE_OP_MINUS:
183d95b3509SRandall Stewart 		printf(" - ");
184d95b3509SRandall Stewart 		break;
185d95b3509SRandall Stewart 	case TYPE_OP_MULT:
186d95b3509SRandall Stewart 		printf(" * ");
187d95b3509SRandall Stewart 		break;
188d95b3509SRandall Stewart 	case TYPE_OP_DIVIDE:
189d95b3509SRandall Stewart 		printf(" / ");
190d95b3509SRandall Stewart 		break;
191d95b3509SRandall Stewart 	case TYPE_PARN_OPEN:
192d95b3509SRandall Stewart 		printf(" ( ");
193d95b3509SRandall Stewart 		break;
194d95b3509SRandall Stewart 	case TYPE_PARN_CLOSE:
195d95b3509SRandall Stewart 		printf(" ) ");
196d95b3509SRandall Stewart 		break;
197d95b3509SRandall Stewart 	case TYPE_VALUE_CON:
198d95b3509SRandall Stewart 		printf("%f", exp->value);
199d95b3509SRandall Stewart 		break;
200d95b3509SRandall Stewart 	case TYPE_VALUE_PMC:
201d95b3509SRandall Stewart 		printf("%s", exp->name);
202d95b3509SRandall Stewart 		break;
203d95b3509SRandall Stewart 	default:
204d95b3509SRandall Stewart 		printf("Unknown op %d\n", exp->type);
205d95b3509SRandall Stewart 		break;
206d95b3509SRandall Stewart 	}
207d95b3509SRandall Stewart 	print_exp(exp->next);
208d95b3509SRandall Stewart }
209d95b3509SRandall Stewart 
210d95b3509SRandall Stewart static void
211d95b3509SRandall Stewart walk_back_and_insert_paren(struct expression **beg, struct expression *frm)
212d95b3509SRandall Stewart {
213d95b3509SRandall Stewart 	struct expression *at, *ex;
214d95b3509SRandall Stewart 
215d95b3509SRandall Stewart 	/* Setup our new open paren */
216d95b3509SRandall Stewart 	ex = malloc(sizeof(struct expression));
217d95b3509SRandall Stewart 	if (ex == NULL) {
218d95b3509SRandall Stewart 		printf("Out of memory in exp allocation\n");
219d95b3509SRandall Stewart 		exit(-2);
220d95b3509SRandall Stewart 	}
221d95b3509SRandall Stewart 	memset(ex, 0, sizeof(struct expression));
222d95b3509SRandall Stewart 	ex->type = TYPE_PARN_OPEN;
223d95b3509SRandall Stewart 	/* Now lets place it */
224d95b3509SRandall Stewart 	at = frm->prev;
225d95b3509SRandall Stewart 	if (at == *beg) {
226d95b3509SRandall Stewart 		/* We are inserting at the head of the list */
227d95b3509SRandall Stewart 	in_beg:
228d95b3509SRandall Stewart 		ex->next = at;
229d95b3509SRandall Stewart 		at->prev = ex;
230d95b3509SRandall Stewart 		*beg = ex;
231d95b3509SRandall Stewart 		return;
232d95b3509SRandall Stewart 	} else if ((at->type == TYPE_VALUE_CON) ||
233d95b3509SRandall Stewart 	    (at->type == TYPE_VALUE_PMC)) {
234d95b3509SRandall Stewart 		/* Simple case we have a value in the previous position */
235d95b3509SRandall Stewart 	in_mid:
236d95b3509SRandall Stewart 		ex->prev = at->prev;
237d95b3509SRandall Stewart 		ex->prev->next = ex;
238d95b3509SRandall Stewart 		ex->next = at;
239d95b3509SRandall Stewart 		at->prev = ex;
240d95b3509SRandall Stewart 		return;
241d95b3509SRandall Stewart 	} else if (at->type == TYPE_PARN_CLOSE) {
242d95b3509SRandall Stewart 		/* Skip through until we reach beg or all ( closes */
243d95b3509SRandall Stewart 		int par_cnt=1;
244d95b3509SRandall Stewart 
245d95b3509SRandall Stewart 		at = at->prev;
246d95b3509SRandall Stewart 		while(par_cnt) {
247d95b3509SRandall Stewart 			if (at->type == TYPE_PARN_CLOSE) {
248d95b3509SRandall Stewart 				par_cnt++;
249d95b3509SRandall Stewart 			} else if (at->type == TYPE_PARN_OPEN) {
250d95b3509SRandall Stewart 				par_cnt--;
251d95b3509SRandall Stewart 				if (par_cnt == 0) {
252d95b3509SRandall Stewart 					break;
253d95b3509SRandall Stewart 				}
254d95b3509SRandall Stewart 			}
255d95b3509SRandall Stewart 			at = at->prev;
256d95b3509SRandall Stewart 		}
257d95b3509SRandall Stewart 		if (at == *beg) {
258d95b3509SRandall Stewart 			/* At beginning we insert */
259d95b3509SRandall Stewart 			goto in_beg;
260d95b3509SRandall Stewart 		} else {
261d95b3509SRandall Stewart 			goto in_mid;
262d95b3509SRandall Stewart 		}
263d95b3509SRandall Stewart 	} else {
264d95b3509SRandall Stewart 		printf("%s:Unexpected type:%d?\n",
265d95b3509SRandall Stewart 		       __FUNCTION__, at->type);
266d95b3509SRandall Stewart 		exit(-1);
267d95b3509SRandall Stewart 	}
268d95b3509SRandall Stewart }
269d95b3509SRandall Stewart 
270d95b3509SRandall Stewart static void
271d95b3509SRandall Stewart walk_fwd_and_insert_paren(struct expression *frm, struct expression **added)
272d95b3509SRandall Stewart {
273d95b3509SRandall Stewart 	struct expression *at, *ex;
274d95b3509SRandall Stewart 	/* Setup our new close paren */
275d95b3509SRandall Stewart 	ex = malloc(sizeof(struct expression));
276d95b3509SRandall Stewart 	if (ex == NULL) {
277d95b3509SRandall Stewart 		printf("Out of memory in exp allocation\n");
278d95b3509SRandall Stewart 		exit(-2);
279d95b3509SRandall Stewart 	}
280d95b3509SRandall Stewart 	memset(ex, 0, sizeof(struct expression));
281d95b3509SRandall Stewart 	ex->type = TYPE_PARN_CLOSE;
282d95b3509SRandall Stewart 	*added = ex;
283d95b3509SRandall Stewart 	/* Now lets place it */
284d95b3509SRandall Stewart 	at = frm->next;
285d95b3509SRandall Stewart 	if ((at->type == TYPE_VALUE_CON) ||
286d95b3509SRandall Stewart 	    (at->type == TYPE_VALUE_PMC)) {
287d95b3509SRandall Stewart 		/* Simple case we have a value in the previous position */
288d95b3509SRandall Stewart 	insertit:
289d95b3509SRandall Stewart 		ex->next = at->next;
290d95b3509SRandall Stewart 		ex->prev = at;
291d95b3509SRandall Stewart 		at->next = ex;
292d95b3509SRandall Stewart 		return;
293d95b3509SRandall Stewart 	} else if (at->type == TYPE_PARN_OPEN) {
294d95b3509SRandall Stewart 		int par_cnt=1;
295d95b3509SRandall Stewart 		at = at->next;
296d95b3509SRandall Stewart 		while(par_cnt) {
297d95b3509SRandall Stewart 			if (at->type == TYPE_PARN_OPEN) {
298d95b3509SRandall Stewart 				par_cnt++;
299d95b3509SRandall Stewart 			} else if (at->type == TYPE_PARN_CLOSE) {
300d95b3509SRandall Stewart 				par_cnt--;
301d95b3509SRandall Stewart 				if (par_cnt == 0) {
302d95b3509SRandall Stewart 					break;
303d95b3509SRandall Stewart 				}
304d95b3509SRandall Stewart 			}
305d95b3509SRandall Stewart 			at = at->next;
306d95b3509SRandall Stewart 		}
307d95b3509SRandall Stewart 		goto insertit;
308d95b3509SRandall Stewart 	} else {
309d95b3509SRandall Stewart 		printf("%s:Unexpected type:%d?\n",
310d95b3509SRandall Stewart 		       __FUNCTION__,
311d95b3509SRandall Stewart 		       at->type);
312d95b3509SRandall Stewart 		exit(-1);
313d95b3509SRandall Stewart 	}
314d95b3509SRandall Stewart }
315d95b3509SRandall Stewart 
316d95b3509SRandall Stewart 
317d95b3509SRandall Stewart static void
318d95b3509SRandall Stewart add_precendence(struct expression **beg, struct expression *start, struct expression *end)
319d95b3509SRandall Stewart {
320d95b3509SRandall Stewart 	/*
321d95b3509SRandall Stewart 	 * Between start and end add () around any * or /. This
322d95b3509SRandall Stewart 	 * is quite tricky since if there is a () set inside the
323d95b3509SRandall Stewart 	 * list we need to skip over everything in the ()'s considering
324d95b3509SRandall Stewart 	 * that just a value.
325d95b3509SRandall Stewart 	 */
326d95b3509SRandall Stewart 	struct expression *at, *newone;
327d95b3509SRandall Stewart 	int open_cnt;
328d95b3509SRandall Stewart 
329d95b3509SRandall Stewart 	at = start;
330d95b3509SRandall Stewart 	open_cnt = 0;
331d95b3509SRandall Stewart 	while(at != end) {
332d95b3509SRandall Stewart 		if (at->type == TYPE_PARN_OPEN) {
333d95b3509SRandall Stewart 			open_cnt++;
334d95b3509SRandall Stewart 		}
335d95b3509SRandall Stewart 		if (at->type == TYPE_PARN_CLOSE) {
336d95b3509SRandall Stewart 			open_cnt--;
337d95b3509SRandall Stewart 		}
338d95b3509SRandall Stewart 		if (open_cnt == 0) {
339d95b3509SRandall Stewart 			if ((at->type == TYPE_OP_MULT) ||
340d95b3509SRandall Stewart 			    (at->type == TYPE_OP_DIVIDE)) {
341d95b3509SRandall Stewart 				walk_back_and_insert_paren(beg, at);
342d95b3509SRandall Stewart 				walk_fwd_and_insert_paren(at, &newone);
343d95b3509SRandall Stewart 				at = newone->next;
344d95b3509SRandall Stewart 				continue;
345d95b3509SRandall Stewart 			}
346d95b3509SRandall Stewart 		}
347d95b3509SRandall Stewart 		at = at->next;
348d95b3509SRandall Stewart 	}
349d95b3509SRandall Stewart 
350d95b3509SRandall Stewart }
351d95b3509SRandall Stewart 
352d95b3509SRandall Stewart static void
353d95b3509SRandall Stewart set_math_precidence(struct expression **beg, struct expression *exp, struct expression **stopped)
354d95b3509SRandall Stewart {
355d95b3509SRandall Stewart 	struct expression *at, *start, *end;
356d95b3509SRandall Stewart 	int cnt_lower, cnt_upper;
357d95b3509SRandall Stewart 	/*
358d95b3509SRandall Stewart 	 * Walk through and set any math precedence to
359d95b3509SRandall Stewart 	 * get proper precedence we insert () around * / over + -
360d95b3509SRandall Stewart 	 */
361d95b3509SRandall Stewart 	end = NULL;
362d95b3509SRandall Stewart 	start = at = exp;
363d95b3509SRandall Stewart 	cnt_lower = cnt_upper = 0;
364d95b3509SRandall Stewart 	while(at) {
365d95b3509SRandall Stewart 		if (at->type == TYPE_PARN_CLOSE) {
366d95b3509SRandall Stewart 			/* Done with that paren */
367d95b3509SRandall Stewart 			if (stopped) {
368d95b3509SRandall Stewart 				*stopped = at;
369d95b3509SRandall Stewart 			}
370d95b3509SRandall Stewart 			if (cnt_lower && cnt_upper) {
371d95b3509SRandall Stewart 				/* We have a mixed set ... add precedence between start/end */
372d95b3509SRandall Stewart 				add_precendence(beg, start, end);
373d95b3509SRandall Stewart 			}
374d95b3509SRandall Stewart 			return;
375d95b3509SRandall Stewart 		}
376d95b3509SRandall Stewart 		if (at->type == TYPE_PARN_OPEN) {
377d95b3509SRandall Stewart 			set_math_precidence(beg, at->next, &end);
378d95b3509SRandall Stewart 			at = end;
379d95b3509SRandall Stewart 			continue;
380d95b3509SRandall Stewart 		} else if ((at->type == TYPE_OP_PLUS) ||
381d95b3509SRandall Stewart 			   (at->type == TYPE_OP_MINUS)) {
382d95b3509SRandall Stewart 			cnt_lower++;
383d95b3509SRandall Stewart 		} else if ((at->type == TYPE_OP_DIVIDE) ||
384d95b3509SRandall Stewart 			   (at->type == TYPE_OP_MULT)) {
385d95b3509SRandall Stewart 			cnt_upper++;
386d95b3509SRandall Stewart 		}
387d95b3509SRandall Stewart 		at = at->next;
388d95b3509SRandall Stewart 	}
389d95b3509SRandall Stewart 	if (cnt_lower && cnt_upper) {
390d95b3509SRandall Stewart 		add_precendence(beg, start, NULL);
391d95b3509SRandall Stewart 	}
392d95b3509SRandall Stewart }
393d95b3509SRandall Stewart 
394d95b3509SRandall Stewart extern char **valid_pmcs;
395d95b3509SRandall Stewart extern int valid_pmc_cnt;
396d95b3509SRandall Stewart 
397d95b3509SRandall Stewart static void
398d95b3509SRandall Stewart pmc_name_set(struct expression *at)
399d95b3509SRandall Stewart {
400d95b3509SRandall Stewart 	int i, idx, fnd;
401d95b3509SRandall Stewart 
402d95b3509SRandall Stewart 	if (at->name[0] == '%') {
403d95b3509SRandall Stewart 		/* Special number after $ gives index */
404d95b3509SRandall Stewart 		idx = strtol(&at->name[1], NULL, 0);
405d95b3509SRandall Stewart 		if (idx >= valid_pmc_cnt) {
406d95b3509SRandall Stewart 			printf("Unknown PMC %s -- largest we have is $%d -- can't run your expression\n",
407d95b3509SRandall Stewart 			       at->name, valid_pmc_cnt);
408d95b3509SRandall Stewart 			exit(-1);
409d95b3509SRandall Stewart 		}
410d95b3509SRandall Stewart 		strcpy(at->name, valid_pmcs[idx]);
411d95b3509SRandall Stewart 	} else {
412d95b3509SRandall Stewart 		for(i=0, fnd=0; i<valid_pmc_cnt; i++) {
413d95b3509SRandall Stewart 			if (strcmp(valid_pmcs[i], at->name) == 0) {
414d95b3509SRandall Stewart 				fnd = 1;
415d95b3509SRandall Stewart 				break;
416d95b3509SRandall Stewart 			}
417d95b3509SRandall Stewart 		}
418d95b3509SRandall Stewart 		if (!fnd) {
419d95b3509SRandall Stewart 			printf("PMC %s does not exist on this machine -- can't run your expression\n",
420d95b3509SRandall Stewart 			       at->name);
421d95b3509SRandall Stewart 			exit(-1);
422d95b3509SRandall Stewart 		}
423d95b3509SRandall Stewart 	}
424d95b3509SRandall Stewart }
425d95b3509SRandall Stewart 
426d95b3509SRandall Stewart struct expression *
427d95b3509SRandall Stewart parse_expression(char *str)
428d95b3509SRandall Stewart {
429d95b3509SRandall Stewart 	struct expression *exp=NULL, *last=NULL, *at;
430d95b3509SRandall Stewart 	int open_par, close_par;
431d95b3509SRandall Stewart 	int op_cnt=0;
432d95b3509SRandall Stewart 	size_t siz, i, x;
433d95b3509SRandall Stewart 	/*
434d95b3509SRandall Stewart 	 * Walk through a string expression and convert
435d95b3509SRandall Stewart 	 * it to a linked list of actions. We do this by:
436d95b3509SRandall Stewart 	 * a) Counting the open/close paren's, there must
437d95b3509SRandall Stewart 	 *    be a matching number.
438d95b3509SRandall Stewart 	 * b) If we have balanced paren's then create a linked list
439d95b3509SRandall Stewart 	 *    of the operators, then we validate that expression further.
440d95b3509SRandall Stewart 	 * c) Validating that we have:
441d95b3509SRandall Stewart 	 *      val OP val <or>
442d95b3509SRandall Stewart 	 *      val OP (  <and>
443d95b3509SRandall Stewart 	 *    inside every paran you have a:
444d95b3509SRandall Stewart 	 *      val OP val <or>
445d95b3509SRandall Stewart 	 *      val OP (   <recursively>
446d95b3509SRandall Stewart 	 * d) A final optional step (not implemented yet) would be
447*463a577bSEitan Adler 	 *    to insert the mathematical precedence paran's. For
448d95b3509SRandall Stewart 	 *    the start we will just do the left to right evaluation and
449d95b3509SRandall Stewart 	 *    then later we can add this guy to add paran's to make it
450d95b3509SRandall Stewart 	 *    mathimatically correct... i.e instead of 1 + 2 * 3 we
451d95b3509SRandall Stewart 	 *    would translate it into 1 + ( 2 * 3).
452d95b3509SRandall Stewart 	 */
453d95b3509SRandall Stewart 	open_par = close_par = 0;
454d95b3509SRandall Stewart 	siz = strlen(str);
455d95b3509SRandall Stewart 	/* No trailing newline please */
456d95b3509SRandall Stewart 	if (str[(siz-1)] == '\n') {
457d95b3509SRandall Stewart 		str[(siz-1)] = 0;
458d95b3509SRandall Stewart 		siz--;
459d95b3509SRandall Stewart 	}
460d95b3509SRandall Stewart 	for(i=0; i<siz; i++) {
461d95b3509SRandall Stewart 		if (str[i] == '(') {
462d95b3509SRandall Stewart 			open_par++;
463d95b3509SRandall Stewart 		} else if (str[i] == ')') {
464d95b3509SRandall Stewart 			close_par++;
465d95b3509SRandall Stewart 		}
466d95b3509SRandall Stewart 	}
467d95b3509SRandall Stewart 	if (open_par != close_par) {
468d95b3509SRandall Stewart 		printf("Invalid expression '%s' %d open paren's and %d close?\n",
469d95b3509SRandall Stewart 		       str, open_par, close_par);
470d95b3509SRandall Stewart 		exit(-1);
471d95b3509SRandall Stewart 	}
472d95b3509SRandall Stewart 	for(i=0; i<siz; i++) {
473d95b3509SRandall Stewart 		if (str[i] == '(') {
474d95b3509SRandall Stewart 			at = alloc_and_hook_expr(&exp, &last);
475d95b3509SRandall Stewart 			at->type = TYPE_PARN_OPEN;
476d95b3509SRandall Stewart 		} else if (str[i] == ')') {
477d95b3509SRandall Stewart 			at = alloc_and_hook_expr(&exp, &last);
478d95b3509SRandall Stewart 			at->type = TYPE_PARN_CLOSE;
479d95b3509SRandall Stewart 		} else if (str[i] == ' ') {
480d95b3509SRandall Stewart 			/* Extra blank */
481d95b3509SRandall Stewart 			continue;
482d95b3509SRandall Stewart 		} else if (str[i] == '\t') {
483d95b3509SRandall Stewart 			/* Extra tab */
484d95b3509SRandall Stewart 			continue;
485d95b3509SRandall Stewart 		} else if (str[i] == '+') {
486d95b3509SRandall Stewart 			at = alloc_and_hook_expr(&exp, &last);
487d95b3509SRandall Stewart 			at->type = TYPE_OP_PLUS;
488d95b3509SRandall Stewart 		} else if (str[i] == '-') {
489d95b3509SRandall Stewart 			at = alloc_and_hook_expr(&exp, &last);
490d95b3509SRandall Stewart 			at->type = TYPE_OP_MINUS;
491d95b3509SRandall Stewart 		} else if (str[i] == '/') {
492d95b3509SRandall Stewart 			at = alloc_and_hook_expr(&exp, &last);
493d95b3509SRandall Stewart 			at->type = TYPE_OP_DIVIDE;
494d95b3509SRandall Stewart 		} else if (str[i] == '*') {
495d95b3509SRandall Stewart 			at = alloc_and_hook_expr(&exp, &last);
496d95b3509SRandall Stewart 			at->type = TYPE_OP_MULT;
497d95b3509SRandall Stewart 		} else {
498d95b3509SRandall Stewart 			/* Its a value or PMC constant */
499d95b3509SRandall Stewart 			at = alloc_and_hook_expr(&exp, &last);
500d95b3509SRandall Stewart 			if (isdigit(str[i]) || (str[i] == '.')) {
501d95b3509SRandall Stewart 				at->type = TYPE_VALUE_CON;
502d95b3509SRandall Stewart 			} else {
503d95b3509SRandall Stewart 				at->type = TYPE_VALUE_PMC;
504d95b3509SRandall Stewart 			}
505d95b3509SRandall Stewart 			x = 0;
506d95b3509SRandall Stewart 			while ((str[i] != ' ') &&
507d95b3509SRandall Stewart 			       (str[i] != '\t') &&
508d95b3509SRandall Stewart 			       (str[i] != 0) &&
509d95b3509SRandall Stewart 			       (str[i] != ')') &&
510d95b3509SRandall Stewart 			       (str[i] != '(')) {
511d95b3509SRandall Stewart 				/* We collect the constant until a space or tab */
512d95b3509SRandall Stewart 				at->name[x] = str[i];
513d95b3509SRandall Stewart 				i++;
514d95b3509SRandall Stewart 				x++;
515d95b3509SRandall Stewart 				if (x >=(sizeof(at->name)-1)) {
516d95b3509SRandall Stewart 					printf("Value/Constant too long %d max:%d\n",
517d95b3509SRandall Stewart 					       (int)x, (int)(sizeof(at->name)-1));
518d95b3509SRandall Stewart 					exit(-3);
519d95b3509SRandall Stewart 				}
520d95b3509SRandall Stewart 			}
521d95b3509SRandall Stewart 			if (str[i] != 0) {
522d95b3509SRandall Stewart 				/* Need to back up and see the last char since
523d95b3509SRandall Stewart 				 * the for will increment the loop.
524d95b3509SRandall Stewart 				 */
525d95b3509SRandall Stewart 				i--;
526d95b3509SRandall Stewart 			}
527d95b3509SRandall Stewart 			/* Now we have pulled the string, set it up */
528d95b3509SRandall Stewart 			if (at->type == TYPE_VALUE_CON) {
529d95b3509SRandall Stewart 				at->state = STATE_FILLED;
530d95b3509SRandall Stewart 				at->value = strtod(at->name, NULL);
531d95b3509SRandall Stewart 			} else {
532d95b3509SRandall Stewart 				pmc_name_set(at);
533d95b3509SRandall Stewart 			}
534d95b3509SRandall Stewart 		}
535d95b3509SRandall Stewart 	}
536d95b3509SRandall Stewart 	/* Now lets validate its a workable expression */
537d95b3509SRandall Stewart 	if (validate_expr(exp, 0, 0, 0, &op_cnt)) {
538d95b3509SRandall Stewart 		printf("Invalid expression\n");
539d95b3509SRandall Stewart 		exit(-4);
540d95b3509SRandall Stewart 	}
541d95b3509SRandall Stewart 	set_math_precidence(&exp, exp, NULL);
542d95b3509SRandall Stewart 	return (exp);
543d95b3509SRandall Stewart }
544d95b3509SRandall Stewart 
545d95b3509SRandall Stewart 
546d95b3509SRandall Stewart 
547d95b3509SRandall Stewart static struct expression *
548d95b3509SRandall Stewart gather_exp_to_paren_close(struct expression *exp, double *val_fill)
549d95b3509SRandall Stewart {
550d95b3509SRandall Stewart 	/*
551d95b3509SRandall Stewart 	 * I have been given ( ???
552d95b3509SRandall Stewart 	 * so I could see either
553d95b3509SRandall Stewart 	 * (
554d95b3509SRandall Stewart 	 * or
555d95b3509SRandall Stewart 	 * Val Op
556d95b3509SRandall Stewart 	 *
557d95b3509SRandall Stewart 	 */
558d95b3509SRandall Stewart 	struct expression *lastproc;
559d95b3509SRandall Stewart 	double val;
560d95b3509SRandall Stewart 
561d95b3509SRandall Stewart 	if (exp->type == TYPE_PARN_OPEN) {
562d95b3509SRandall Stewart 		lastproc = gather_exp_to_paren_close(exp->next, &val);
563d95b3509SRandall Stewart 		*val_fill = val;
564d95b3509SRandall Stewart 	} else {
565d95b3509SRandall Stewart 		*val_fill = run_expr(exp, 0, &lastproc);
566d95b3509SRandall Stewart 	}
567d95b3509SRandall Stewart 	return(lastproc);
568d95b3509SRandall Stewart }
569d95b3509SRandall Stewart 
570d95b3509SRandall Stewart 
571d95b3509SRandall Stewart double
572d95b3509SRandall Stewart run_expr(struct expression *exp, int initial_call, struct expression **lastone)
573d95b3509SRandall Stewart {
574d95b3509SRandall Stewart 	/*
575d95b3509SRandall Stewart 	 * We expect to find either
576d95b3509SRandall Stewart 	 * a) A Open Paren
577d95b3509SRandall Stewart 	 * or
578d95b3509SRandall Stewart 	 * b) Val-> Op -> Val
579d95b3509SRandall Stewart 	 * or
580d95b3509SRandall Stewart 	 * c) Val-> Op -> Open Paren
581d95b3509SRandall Stewart 	 */
582d95b3509SRandall Stewart 	double val1, val2, res;
583d95b3509SRandall Stewart 	struct expression *op, *other_half, *rest;
584d95b3509SRandall Stewart 
585d95b3509SRandall Stewart 	if (exp->type == TYPE_PARN_OPEN) {
586d95b3509SRandall Stewart 		op = gather_exp_to_paren_close(exp->next, &val1);
587d95b3509SRandall Stewart 	} else if(exp->type == TYPE_VALUE_CON) {
588d95b3509SRandall Stewart 		val1 = exp->value;
589d95b3509SRandall Stewart 		op = exp->next;
590d95b3509SRandall Stewart 	} else if (exp->type ==  TYPE_VALUE_PMC) {
591d95b3509SRandall Stewart 		val1 = exp->value;
592d95b3509SRandall Stewart 		op = exp->next;
593d95b3509SRandall Stewart 	} else {
594d95b3509SRandall Stewart 		printf("Illegal value in %s huh?\n", __FUNCTION__);
595d95b3509SRandall Stewart 		exit(-1);
596d95b3509SRandall Stewart 	}
597d95b3509SRandall Stewart 	if (op == NULL) {
598d95b3509SRandall Stewart 		return (val1);
599d95b3509SRandall Stewart 	}
600d95b3509SRandall Stewart more_to_do:
601d95b3509SRandall Stewart 	other_half = op->next;
602d95b3509SRandall Stewart 	if (other_half->type == TYPE_PARN_OPEN) {
603d95b3509SRandall Stewart 		rest = gather_exp_to_paren_close(other_half->next, &val2);
604d95b3509SRandall Stewart 	} else if(other_half->type == TYPE_VALUE_CON) {
605d95b3509SRandall Stewart 		val2 = other_half->value;
606d95b3509SRandall Stewart 		rest = other_half->next;
607d95b3509SRandall Stewart 	} else if (other_half->type ==  TYPE_VALUE_PMC) {
608d95b3509SRandall Stewart 		val2 = other_half->value;
609d95b3509SRandall Stewart 		rest = other_half->next;
610d95b3509SRandall Stewart 	} else {
611d95b3509SRandall Stewart 		printf("Illegal2 value in %s huh?\n", __FUNCTION__);
612d95b3509SRandall Stewart 		exit(-1);
613d95b3509SRandall Stewart 	}
614d95b3509SRandall Stewart 	switch(op->type) {
615d95b3509SRandall Stewart 	case TYPE_OP_PLUS:
616d95b3509SRandall Stewart 		res = val1 + val2;
617d95b3509SRandall Stewart 		break;
618d95b3509SRandall Stewart 	case TYPE_OP_MINUS:
619d95b3509SRandall Stewart 		res = val1 - val2;
620d95b3509SRandall Stewart 		break;
621d95b3509SRandall Stewart 	case TYPE_OP_MULT:
622d95b3509SRandall Stewart 		res = val1 * val2;
623d95b3509SRandall Stewart 		break;
624d95b3509SRandall Stewart 	case TYPE_OP_DIVIDE:
625d95b3509SRandall Stewart 		if (val2 != 0.0)
626d95b3509SRandall Stewart 			res = val1 / val2;
627d95b3509SRandall Stewart 		else {
628d95b3509SRandall Stewart 			printf("Division by zero averted\n");
629d95b3509SRandall Stewart 			res = 1.0;
630d95b3509SRandall Stewart 		}
631d95b3509SRandall Stewart 		break;
632d95b3509SRandall Stewart 	default:
633d95b3509SRandall Stewart 		printf("Op is not an operator -- its %d\n",
634d95b3509SRandall Stewart 		       op->type);
635d95b3509SRandall Stewart 		exit(-1);
636d95b3509SRandall Stewart 		break;
637d95b3509SRandall Stewart 	}
638d95b3509SRandall Stewart 	if (rest == NULL) {
639d95b3509SRandall Stewart 		if (lastone) {
640d95b3509SRandall Stewart 			*lastone = NULL;
641d95b3509SRandall Stewart 		}
642d95b3509SRandall Stewart 		return (res);
643d95b3509SRandall Stewart 	}
644d95b3509SRandall Stewart 	if ((rest->type == TYPE_PARN_CLOSE) && (initial_call == 0)) {
645d95b3509SRandall Stewart 		if (lastone) {
646d95b3509SRandall Stewart 			*lastone = rest->next;
647d95b3509SRandall Stewart 		}
648d95b3509SRandall Stewart 		return(res);
649d95b3509SRandall Stewart 	}
650d95b3509SRandall Stewart 	/* There is more, as in
651d95b3509SRandall Stewart 	 * a + b + c
652d95b3509SRandall Stewart 	 * where we just did a + b
653d95b3509SRandall Stewart 	 * so now it becomes val1 is set to res and
654d95b3509SRandall Stewart 	 * we need to proceed with the rest of it.
655d95b3509SRandall Stewart 	 */
656d95b3509SRandall Stewart 	val1 = res;
657d95b3509SRandall Stewart 	op = rest;
658d95b3509SRandall Stewart 	if ((op->type != TYPE_OP_PLUS) &&
659d95b3509SRandall Stewart 	    (op->type != TYPE_OP_MULT) &&
660d95b3509SRandall Stewart 	    (op->type != TYPE_OP_MINUS) &&
661d95b3509SRandall Stewart 	    (op->type != TYPE_OP_DIVIDE)) {
662d95b3509SRandall Stewart 		printf("%s ending on type:%d not an op??\n", __FUNCTION__, op->type);
663d95b3509SRandall Stewart 		return(res);
664d95b3509SRandall Stewart 	}
665d95b3509SRandall Stewart 	if (op)
666d95b3509SRandall Stewart 		goto more_to_do;
667d95b3509SRandall Stewart 	return (res);
668d95b3509SRandall Stewart }
669d95b3509SRandall Stewart 
670d95b3509SRandall Stewart #ifdef STAND_ALONE_TESTING
671d95b3509SRandall Stewart 
672d95b3509SRandall Stewart static double
673d95b3509SRandall Stewart calc_expr(struct expression *exp)
674d95b3509SRandall Stewart {
675d95b3509SRandall Stewart 	struct expression *at;
676d95b3509SRandall Stewart 	double xx;
677d95b3509SRandall Stewart 
678d95b3509SRandall Stewart 	/* First clear PMC's setting */
679d95b3509SRandall Stewart 	for(at = exp; at != NULL; at = at->next) {
680d95b3509SRandall Stewart 		if (at->type == TYPE_VALUE_PMC) {
681d95b3509SRandall Stewart 			at->state = STATE_UNSET;
682d95b3509SRandall Stewart 		}
683d95b3509SRandall Stewart 	}
684d95b3509SRandall Stewart 	/* Now for all pmc's make up values .. here is where I would pull them */
685d95b3509SRandall Stewart 	for(at = exp; at != NULL; at = at->next) {
686d95b3509SRandall Stewart 		if (at->type == TYPE_VALUE_PMC) {
687d95b3509SRandall Stewart 			at->value = (random() * 1.0);
688d95b3509SRandall Stewart 			at->state = STATE_FILLED;
689d95b3509SRandall Stewart 			if (at->value == 0.0) {
690d95b3509SRandall Stewart 				/* So we don't have div by 0 */
691d95b3509SRandall Stewart 				at->value = 1.0;
692d95b3509SRandall Stewart 			}
693d95b3509SRandall Stewart 		}
694d95b3509SRandall Stewart 	}
695d95b3509SRandall Stewart 	/* Now lets calculate the expression */
696d95b3509SRandall Stewart 	print_exp(exp);
697d95b3509SRandall Stewart 	xx = run_expr(exp, 1, NULL);
698d95b3509SRandall Stewart 	printf("Answer is %f\n", xx);
699d95b3509SRandall Stewart 	return(xx);
700d95b3509SRandall Stewart }
701d95b3509SRandall Stewart 
702d95b3509SRandall Stewart 
703d95b3509SRandall Stewart int
704d95b3509SRandall Stewart main(int argc, char **argv)
705d95b3509SRandall Stewart {
706d95b3509SRandall Stewart 	struct expression *exp;
707d95b3509SRandall Stewart 	if (argc < 2) {
708d95b3509SRandall Stewart 		printf("Use %s expression\n", argv[0]);
709d95b3509SRandall Stewart 		return(-1);
710d95b3509SRandall Stewart 	}
711d95b3509SRandall Stewart 	exp = parse_expression(argv[1]);
712d95b3509SRandall Stewart 	printf("Now the calc\n");
713d95b3509SRandall Stewart 	calc_expr(exp);
714d95b3509SRandall Stewart 	return(0);
715d95b3509SRandall Stewart }
716d95b3509SRandall Stewart 
717d95b3509SRandall Stewart #endif
718