1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*3e14f97fSRoger A. Faulkner * Copyright (c) 1985-2010 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6da2e3ebdSchin * Common Public License, Version 1.0 *
77c2fbfb3SApril Chin * by AT&T Intellectual Property *
8da2e3ebdSchin * *
9da2e3ebdSchin * A copy of the License is available at *
10da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt *
11da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12da2e3ebdSchin * *
13da2e3ebdSchin * Information and Software Systems Research *
14da2e3ebdSchin * AT&T Research *
15da2e3ebdSchin * Florham Park NJ *
16da2e3ebdSchin * *
17da2e3ebdSchin * Glenn Fowler <gsf@research.att.com> *
18da2e3ebdSchin * David Korn <dgk@research.att.com> *
19da2e3ebdSchin * Phong Vo <kpv@research.att.com> *
20da2e3ebdSchin * *
21da2e3ebdSchin ***********************************************************************/
22da2e3ebdSchin #pragma prototyped
23da2e3ebdSchin /*
24da2e3ebdSchin * G. S. Fowler
25da2e3ebdSchin * D. G. Korn
26da2e3ebdSchin * AT&T Bell Laboratories
27da2e3ebdSchin *
28da2e3ebdSchin * long integer arithmetic expression evaluator
29da2e3ebdSchin * long constants may be represented as:
30da2e3ebdSchin *
31da2e3ebdSchin * 0ooo octal
32da2e3ebdSchin * 0[xX]hhh hexadecimal
33da2e3ebdSchin * ddd decimal
34da2e3ebdSchin * n#ccc base n, 2 <= b <= 36
35da2e3ebdSchin *
36da2e3ebdSchin * NOTE: all operands are evaluated as both the parse
37da2e3ebdSchin * and evaluation are done on the fly
38da2e3ebdSchin */
39da2e3ebdSchin
40da2e3ebdSchin #include <ast.h>
41da2e3ebdSchin #include <ctype.h>
42da2e3ebdSchin
43da2e3ebdSchin #define getchr(ex) (*(ex)->nextchr++)
44da2e3ebdSchin #define peekchr(ex) (*(ex)->nextchr)
45da2e3ebdSchin #define ungetchr(ex) ((ex)->nextchr--)
46da2e3ebdSchin
47da2e3ebdSchin #define error(ex,msg) return(seterror(ex,msg))
48da2e3ebdSchin
49da2e3ebdSchin typedef struct /* expression handle */
50da2e3ebdSchin {
51da2e3ebdSchin char* nextchr; /* next expression char */
52da2e3ebdSchin char* errchr; /* next char after error */
53da2e3ebdSchin char* errmsg; /* error message text */
54da2e3ebdSchin long (*convert)(const char*, char**, void*);
55da2e3ebdSchin void* handle; /* user convert handle */
56da2e3ebdSchin } Expr_t;
57da2e3ebdSchin
58da2e3ebdSchin /*
59da2e3ebdSchin * set error message string
60da2e3ebdSchin */
61da2e3ebdSchin
62da2e3ebdSchin static long
seterror(register Expr_t * ex,char * msg)63da2e3ebdSchin seterror(register Expr_t* ex, char* msg)
64da2e3ebdSchin {
65da2e3ebdSchin if (!ex->errmsg) ex->errmsg = msg;
66da2e3ebdSchin ex->errchr = ex->nextchr;
67da2e3ebdSchin ex->nextchr = "";
68da2e3ebdSchin return(0);
69da2e3ebdSchin }
70da2e3ebdSchin
71da2e3ebdSchin /*
72da2e3ebdSchin * evaluate a subexpression with precedence
73da2e3ebdSchin */
74da2e3ebdSchin
75da2e3ebdSchin static long
expr(register Expr_t * ex,register int precedence)76da2e3ebdSchin expr(register Expr_t* ex, register int precedence)
77da2e3ebdSchin {
78da2e3ebdSchin register int c;
79da2e3ebdSchin register long n;
80da2e3ebdSchin register long x;
81da2e3ebdSchin char* pos;
82da2e3ebdSchin int operand = 1;
83da2e3ebdSchin
84da2e3ebdSchin while (c = getchr(ex), isspace(c));
85da2e3ebdSchin switch (c)
86da2e3ebdSchin {
87da2e3ebdSchin case 0:
88da2e3ebdSchin ungetchr(ex);
89da2e3ebdSchin if (!precedence) return(0);
90da2e3ebdSchin error(ex, "more tokens expected");
91da2e3ebdSchin case '-':
92da2e3ebdSchin n = -expr(ex, 13);
93da2e3ebdSchin break;
94da2e3ebdSchin case '+':
95da2e3ebdSchin n = expr(ex, 13);
96da2e3ebdSchin break;
97da2e3ebdSchin case '!':
98da2e3ebdSchin n = !expr(ex, 13);
99da2e3ebdSchin break;
100da2e3ebdSchin case '~':
101da2e3ebdSchin n = ~expr(ex, 13);
102da2e3ebdSchin break;
103da2e3ebdSchin default:
104da2e3ebdSchin ungetchr(ex);
105da2e3ebdSchin n = 0;
106da2e3ebdSchin operand = 0;
107da2e3ebdSchin break;
108da2e3ebdSchin }
109da2e3ebdSchin for (;;)
110da2e3ebdSchin {
111da2e3ebdSchin switch (c = getchr(ex))
112da2e3ebdSchin {
113da2e3ebdSchin case 0:
114da2e3ebdSchin goto done;
115da2e3ebdSchin case ')':
116da2e3ebdSchin if (!precedence) error(ex, "too many )'s");
117da2e3ebdSchin goto done;
118da2e3ebdSchin case '(':
119da2e3ebdSchin n = expr(ex, 1);
120da2e3ebdSchin if (getchr(ex) != ')')
121da2e3ebdSchin {
122da2e3ebdSchin ungetchr(ex);
123da2e3ebdSchin error(ex, "closing ) expected");
124da2e3ebdSchin }
125da2e3ebdSchin gotoperand:
126da2e3ebdSchin if (operand) error(ex, "operator expected");
127da2e3ebdSchin operand = 1;
128da2e3ebdSchin continue;
129da2e3ebdSchin case '?':
130da2e3ebdSchin if (precedence > 1) goto done;
131da2e3ebdSchin if (peekchr(ex) == ':')
132da2e3ebdSchin {
133da2e3ebdSchin getchr(ex);
134da2e3ebdSchin x = expr(ex, 2);
135da2e3ebdSchin if (!n) n = x;
136da2e3ebdSchin }
137da2e3ebdSchin else
138da2e3ebdSchin {
139da2e3ebdSchin x = expr(ex, 2);
140da2e3ebdSchin if (getchr(ex) != ':')
141da2e3ebdSchin {
142da2e3ebdSchin ungetchr(ex);
143da2e3ebdSchin error(ex, ": expected for ? operator");
144da2e3ebdSchin }
145da2e3ebdSchin if (n)
146da2e3ebdSchin {
147da2e3ebdSchin n = x;
148da2e3ebdSchin expr(ex, 2);
149da2e3ebdSchin }
150da2e3ebdSchin else n = expr(ex, 2);
151da2e3ebdSchin }
152da2e3ebdSchin break;
153da2e3ebdSchin case ':':
154da2e3ebdSchin goto done;
155da2e3ebdSchin case '|':
156da2e3ebdSchin if (peekchr(ex) == '|')
157da2e3ebdSchin {
158da2e3ebdSchin if (precedence > 2) goto done;
159da2e3ebdSchin getchr(ex);
160da2e3ebdSchin x = expr(ex, 3);
161da2e3ebdSchin n = n || x;
162da2e3ebdSchin }
163da2e3ebdSchin else
164da2e3ebdSchin {
165da2e3ebdSchin if (precedence > 4) goto done;
166da2e3ebdSchin x = expr(ex, 5);
167da2e3ebdSchin n |= x;
168da2e3ebdSchin }
169da2e3ebdSchin break;
170da2e3ebdSchin case '^':
171da2e3ebdSchin if (precedence > 5) goto done;
172da2e3ebdSchin x = expr(ex, 6);
173da2e3ebdSchin n ^= x;
174da2e3ebdSchin break;
175da2e3ebdSchin case '&':
176da2e3ebdSchin if (peekchr(ex) == '&')
177da2e3ebdSchin {
178da2e3ebdSchin if (precedence > 3) goto done;
179da2e3ebdSchin getchr(ex);
180da2e3ebdSchin x = expr(ex, 4);
181da2e3ebdSchin n = n && x;
182da2e3ebdSchin }
183da2e3ebdSchin else
184da2e3ebdSchin {
185da2e3ebdSchin if (precedence > 6) goto done;
186da2e3ebdSchin x = expr(ex, 7);
187da2e3ebdSchin n &= x;
188da2e3ebdSchin }
189da2e3ebdSchin break;
190da2e3ebdSchin case '=':
191da2e3ebdSchin case '!':
192da2e3ebdSchin if (peekchr(ex) != '=') error(ex, "operator syntax error");
193da2e3ebdSchin if (precedence > 7) goto done;
194da2e3ebdSchin getchr(ex);
195da2e3ebdSchin x = expr(ex, 8);
196da2e3ebdSchin if (c == '=') n = n == x;
197da2e3ebdSchin else n = n != x;
198da2e3ebdSchin break;
199da2e3ebdSchin case '<':
200da2e3ebdSchin case '>':
201da2e3ebdSchin if (peekchr(ex) == c)
202da2e3ebdSchin {
203da2e3ebdSchin if (precedence > 9) goto done;
204da2e3ebdSchin getchr(ex);
205da2e3ebdSchin x = expr(ex, 10);
206da2e3ebdSchin if (c == '<') n <<= x;
207da2e3ebdSchin else n >>= x;
208da2e3ebdSchin }
209da2e3ebdSchin else
210da2e3ebdSchin {
211da2e3ebdSchin if (precedence > 8) goto done;
212da2e3ebdSchin if (peekchr(ex) == '=')
213da2e3ebdSchin {
214da2e3ebdSchin getchr(ex);
215da2e3ebdSchin x = expr(ex, 9);
216da2e3ebdSchin if (c == '<') n = n <= x;
217da2e3ebdSchin else n = n >= x;
218da2e3ebdSchin }
219da2e3ebdSchin else
220da2e3ebdSchin {
221da2e3ebdSchin x = expr(ex, 9);
222da2e3ebdSchin if (c == '<') n = n < x;
223da2e3ebdSchin else n = n > x;
224da2e3ebdSchin }
225da2e3ebdSchin }
226da2e3ebdSchin break;
227da2e3ebdSchin case '+':
228da2e3ebdSchin case '-':
229da2e3ebdSchin if (precedence > 10) goto done;
230da2e3ebdSchin x = expr(ex, 11);
231da2e3ebdSchin if (c == '+') n += x;
232da2e3ebdSchin else n -= x;
233da2e3ebdSchin break;
234da2e3ebdSchin case '*':
235da2e3ebdSchin case '/':
236da2e3ebdSchin case '%':
237da2e3ebdSchin if (precedence > 11) goto done;
238da2e3ebdSchin x = expr(ex, 12);
239da2e3ebdSchin if (c == '*') n *= x;
240da2e3ebdSchin else if (x == 0) error(ex, "divide by zero");
241da2e3ebdSchin else if (c == '/') n /= x;
242da2e3ebdSchin else n %= x;
243da2e3ebdSchin break;
244da2e3ebdSchin default:
245da2e3ebdSchin if (isspace(c)) continue;
246da2e3ebdSchin pos = --ex->nextchr;
247da2e3ebdSchin if (isdigit(c)) n = strton(ex->nextchr, &ex->nextchr, NiL, 0);
248da2e3ebdSchin else if (ex->convert) n = (*ex->convert)(ex->nextchr, &ex->nextchr, ex->handle);
249da2e3ebdSchin if (ex->nextchr == pos) error(ex, "syntax error");
250da2e3ebdSchin goto gotoperand;
251da2e3ebdSchin }
252da2e3ebdSchin if (ex->errmsg) return(0);
253da2e3ebdSchin if (!operand) error(ex, "operand expected");
254da2e3ebdSchin }
255da2e3ebdSchin done:
256da2e3ebdSchin ungetchr(ex);
257da2e3ebdSchin if (!operand) error(ex, "operand expected");
258da2e3ebdSchin return(n);
259da2e3ebdSchin }
260da2e3ebdSchin
261da2e3ebdSchin /*
262da2e3ebdSchin * evaluate an integer arithmetic expression in s
263da2e3ebdSchin *
264da2e3ebdSchin * (long)(*convert)(const char* string, char** end, void* handle)
265da2e3ebdSchin * is a user supplied conversion routine that is called when unknown
266da2e3ebdSchin * chars are encountered; string points to the part to be
267da2e3ebdSchin * converted and end is adjusted to point to the next non-converted
268da2e3ebdSchin * character; if string is 0 then end points to an error message string
269da2e3ebdSchin *
270da2e3ebdSchin * NOTE: (*convert)() may call strexpr(ex, )
271da2e3ebdSchin */
272da2e3ebdSchin
273da2e3ebdSchin long
strexpr(const char * s,char ** end,long (* convert)(const char *,char **,void *),void * handle)274da2e3ebdSchin strexpr(const char* s, char** end, long(*convert)(const char*, char**, void*), void* handle)
275da2e3ebdSchin {
276da2e3ebdSchin long n;
277da2e3ebdSchin Expr_t ex;
278da2e3ebdSchin
279da2e3ebdSchin ex.nextchr = (char*)s;
280da2e3ebdSchin ex.errmsg = 0;
281da2e3ebdSchin ex.convert = convert;
282da2e3ebdSchin ex.handle = handle;
283da2e3ebdSchin n = expr(&ex, 0);
284da2e3ebdSchin if (peekchr(&ex) == ':')
285da2e3ebdSchin seterror(&ex, "invalid use of :");
286da2e3ebdSchin if (ex.errmsg)
287da2e3ebdSchin {
288da2e3ebdSchin if (convert) (*convert)(NiL, &ex.errmsg, handle);
289da2e3ebdSchin ex.nextchr = ex.errchr;
290da2e3ebdSchin n = 0;
291da2e3ebdSchin }
292da2e3ebdSchin if (end) *end = ex.nextchr;
293da2e3ebdSchin return(n);
294da2e3ebdSchin }
295