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