xref: /onnv-gate/usr/src/lib/libast/common/string/strexpr.c (revision 12068:08a39a083754)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*12068SRoger.Faulkner@Oracle.COM *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
184887Schin *                  David Korn <dgk@research.att.com>                   *
194887Schin *                   Phong Vo <kpv@research.att.com>                    *
204887Schin *                                                                      *
214887Schin ***********************************************************************/
224887Schin #pragma prototyped
234887Schin /*
244887Schin  * G. S. Fowler
254887Schin  * D. G. Korn
264887Schin  * AT&T Bell Laboratories
274887Schin  *
284887Schin  * long integer arithmetic expression evaluator
294887Schin  * long constants may be represented as:
304887Schin  *
314887Schin  *	0ooo		octal
324887Schin  *	0[xX]hhh	hexadecimal
334887Schin  *	ddd		decimal
344887Schin  *	n#ccc		base n, 2 <= b <= 36
354887Schin  *
364887Schin  * NOTE: all operands are evaluated as both the parse
374887Schin  *	 and evaluation are done on the fly
384887Schin  */
394887Schin 
404887Schin #include <ast.h>
414887Schin #include <ctype.h>
424887Schin 
434887Schin #define getchr(ex)	(*(ex)->nextchr++)
444887Schin #define peekchr(ex)	(*(ex)->nextchr)
454887Schin #define ungetchr(ex)	((ex)->nextchr--)
464887Schin 
474887Schin #define error(ex,msg)	return(seterror(ex,msg))
484887Schin 
494887Schin typedef struct				/* expression handle		*/
504887Schin {
514887Schin 	char*		nextchr;	/* next expression char		*/
524887Schin 	char*		errchr;		/* next char after error	*/
534887Schin 	char*		errmsg;		/* error message text		*/
544887Schin 	long		(*convert)(const char*, char**, void*);
554887Schin 	void*		handle;		/* user convert handle		*/
564887Schin } Expr_t;
574887Schin 
584887Schin /*
594887Schin  * set error message string
604887Schin  */
614887Schin 
624887Schin static long
seterror(register Expr_t * ex,char * msg)634887Schin seterror(register Expr_t* ex, char* msg)
644887Schin {
654887Schin 	if (!ex->errmsg) ex->errmsg = msg;
664887Schin 	ex->errchr = ex->nextchr;
674887Schin 	ex->nextchr = "";
684887Schin 	return(0);
694887Schin }
704887Schin 
714887Schin /*
724887Schin  * evaluate a subexpression with precedence
734887Schin  */
744887Schin 
754887Schin static long
expr(register Expr_t * ex,register int precedence)764887Schin expr(register Expr_t* ex, register int precedence)
774887Schin {
784887Schin 	register int	c;
794887Schin 	register long	n;
804887Schin 	register long	x;
814887Schin 	char*		pos;
824887Schin 	int		operand = 1;
834887Schin 
844887Schin 	while (c = getchr(ex), isspace(c));
854887Schin 	switch (c)
864887Schin 	{
874887Schin 	case 0:
884887Schin 		ungetchr(ex);
894887Schin 		if (!precedence) return(0);
904887Schin 		error(ex, "more tokens expected");
914887Schin 	case '-':
924887Schin 		n = -expr(ex, 13);
934887Schin 		break;
944887Schin 	case '+':
954887Schin 		n = expr(ex, 13);
964887Schin 		break;
974887Schin 	case '!':
984887Schin 		n = !expr(ex, 13);
994887Schin 		break;
1004887Schin 	case '~':
1014887Schin 		n = ~expr(ex, 13);
1024887Schin 		break;
1034887Schin 	default:
1044887Schin 		ungetchr(ex);
1054887Schin 		n = 0;
1064887Schin 		operand = 0;
1074887Schin 		break;
1084887Schin 	}
1094887Schin 	for (;;)
1104887Schin 	{
1114887Schin 		switch (c = getchr(ex))
1124887Schin 		{
1134887Schin 		case 0:
1144887Schin 			goto done;
1154887Schin 		case ')':
1164887Schin 			if (!precedence) error(ex, "too many )'s");
1174887Schin 			goto done;
1184887Schin 		case '(':
1194887Schin 			n = expr(ex, 1);
1204887Schin 			if (getchr(ex) != ')')
1214887Schin 			{
1224887Schin 				ungetchr(ex);
1234887Schin 				error(ex, "closing ) expected");
1244887Schin 			}
1254887Schin 		gotoperand:
1264887Schin 			if (operand) error(ex, "operator expected");
1274887Schin 			operand = 1;
1284887Schin 			continue;
1294887Schin 		case '?':
1304887Schin 			if (precedence > 1) goto done;
1314887Schin 			if (peekchr(ex) == ':')
1324887Schin 			{
1334887Schin 				getchr(ex);
1344887Schin 				x = expr(ex, 2);
1354887Schin 				if (!n) n = x;
1364887Schin 			}
1374887Schin 			else
1384887Schin 			{
1394887Schin 				x = expr(ex, 2);
1404887Schin 				if (getchr(ex) != ':')
1414887Schin 				{
1424887Schin 					ungetchr(ex);
1434887Schin 					error(ex, ": expected for ? operator");
1444887Schin 				}
1454887Schin 				if (n)
1464887Schin 				{
1474887Schin 					n = x;
1484887Schin 					expr(ex, 2);
1494887Schin 				}
1504887Schin 				else n = expr(ex, 2);
1514887Schin 			}
1524887Schin 			break;
1534887Schin 		case ':':
1544887Schin 			goto done;
1554887Schin 		case '|':
1564887Schin 			if (peekchr(ex) == '|')
1574887Schin 			{
1584887Schin 				if (precedence > 2) goto done;
1594887Schin 				getchr(ex);
1604887Schin 				x = expr(ex, 3);
1614887Schin 				n = n || x;
1624887Schin 			}
1634887Schin 			else
1644887Schin 			{
1654887Schin 				if (precedence > 4) goto done;
1664887Schin 				x = expr(ex, 5);
1674887Schin 				n |= x;
1684887Schin 			}
1694887Schin 			break;
1704887Schin 		case '^':
1714887Schin 			if (precedence > 5) goto done;
1724887Schin 			x = expr(ex, 6);
1734887Schin 			n ^= x;
1744887Schin 			break;
1754887Schin 		case '&':
1764887Schin 			if (peekchr(ex) == '&')
1774887Schin 			{
1784887Schin 				if (precedence > 3) goto done;
1794887Schin 				getchr(ex);
1804887Schin 				x = expr(ex, 4);
1814887Schin 				n = n && x;
1824887Schin 			}
1834887Schin 			else
1844887Schin 			{
1854887Schin 				if (precedence > 6) goto done;
1864887Schin 				x = expr(ex, 7);
1874887Schin 				n &= x;
1884887Schin 			}
1894887Schin 			break;
1904887Schin 		case '=':
1914887Schin 		case '!':
1924887Schin 			if (peekchr(ex) != '=') error(ex, "operator syntax error");
1934887Schin 			if (precedence > 7) goto done;
1944887Schin 			getchr(ex);
1954887Schin 			x = expr(ex, 8);
1964887Schin 			if (c == '=') n = n == x;
1974887Schin 			else n = n != x;
1984887Schin 			break;
1994887Schin 		case '<':
2004887Schin 		case '>':
2014887Schin 			if (peekchr(ex) == c)
2024887Schin 			{
2034887Schin 				if (precedence > 9) goto done;
2044887Schin 				getchr(ex);
2054887Schin 				x = expr(ex, 10);
2064887Schin 				if (c == '<') n <<= x;
2074887Schin 				else n >>= x;
2084887Schin 			}
2094887Schin 			else
2104887Schin 			{
2114887Schin 				if (precedence > 8) goto done;
2124887Schin 				if (peekchr(ex) == '=')
2134887Schin 				{
2144887Schin 					getchr(ex);
2154887Schin 					x = expr(ex, 9);
2164887Schin 					if (c == '<') n = n <= x;
2174887Schin 					else n = n >= x;
2184887Schin 				}
2194887Schin 				else
2204887Schin 				{
2214887Schin 					x = expr(ex, 9);
2224887Schin 					if (c == '<') n = n < x;
2234887Schin 					else n = n > x;
2244887Schin 				}
2254887Schin 			}
2264887Schin 			break;
2274887Schin 		case '+':
2284887Schin 		case '-':
2294887Schin 			if (precedence > 10) goto done;
2304887Schin 			x = expr(ex, 11);
2314887Schin 			if (c == '+') n +=  x;
2324887Schin 			else n -= x;
2334887Schin 			break;
2344887Schin 		case '*':
2354887Schin 		case '/':
2364887Schin 		case '%':
2374887Schin 			if (precedence > 11) goto done;
2384887Schin 			x = expr(ex, 12);
2394887Schin 			if (c == '*') n *= x;
2404887Schin 			else if (x == 0) error(ex, "divide by zero");
2414887Schin 			else if (c == '/') n /= x;
2424887Schin 			else n %= x;
2434887Schin 			break;
2444887Schin 		default:
2454887Schin 			if (isspace(c)) continue;
2464887Schin 			pos = --ex->nextchr;
2474887Schin 			if (isdigit(c)) n = strton(ex->nextchr, &ex->nextchr, NiL, 0);
2484887Schin 			else if (ex->convert) n = (*ex->convert)(ex->nextchr, &ex->nextchr, ex->handle);
2494887Schin 			if (ex->nextchr == pos) error(ex, "syntax error");
2504887Schin 			goto gotoperand;
2514887Schin 		}
2524887Schin 		if (ex->errmsg) return(0);
2534887Schin 		if (!operand) error(ex, "operand expected");
2544887Schin 	}
2554887Schin  done:
2564887Schin 	ungetchr(ex);
2574887Schin 	if (!operand) error(ex, "operand expected");
2584887Schin 	return(n);
2594887Schin }
2604887Schin 
2614887Schin /*
2624887Schin  * evaluate an integer arithmetic expression in s
2634887Schin  *
2644887Schin  * (long)(*convert)(const char* string, char** end, void* handle)
2654887Schin  * is a user supplied conversion routine that is called when unknown
2664887Schin  * chars are encountered; string points to the part to be
2674887Schin  * converted and end is adjusted to point to the next non-converted
2684887Schin  * character; if string is 0 then end points to an error message string
2694887Schin  *
2704887Schin  * NOTE: (*convert)() may call strexpr(ex, )
2714887Schin  */
2724887Schin 
2734887Schin long
strexpr(const char * s,char ** end,long (* convert)(const char *,char **,void *),void * handle)2744887Schin strexpr(const char* s, char** end, long(*convert)(const char*, char**, void*), void* handle)
2754887Schin {
2764887Schin 	long	n;
2774887Schin 	Expr_t	ex;
2784887Schin 
2794887Schin 	ex.nextchr = (char*)s;
2804887Schin 	ex.errmsg = 0;
2814887Schin 	ex.convert = convert;
2824887Schin 	ex.handle = handle;
2834887Schin 	n = expr(&ex, 0);
2844887Schin 	if (peekchr(&ex) == ':')
2854887Schin 		seterror(&ex, "invalid use of :");
2864887Schin 	if (ex.errmsg)
2874887Schin 	{
2884887Schin 		if (convert) (*convert)(NiL, &ex.errmsg, handle);
2894887Schin 		ex.nextchr = ex.errchr;
2904887Schin 		n = 0;
2914887Schin 	}
2924887Schin 	if (end) *end = ex.nextchr;
2934887Schin 	return(n);
2944887Schin }
295