xref: /titanic_51/usr/src/lib/libast/common/string/strexpr.c (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
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