1 /* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
3 Permission is hereby granted, free of charge, to any person obtaining
4 a copy of this software and associated documentation files (the
5 "Software"), to deal in the Software without restriction, including
6 without limitation the rights to use, copy, modify, merge, publish,
7 distribute, sublicense, and/or sell copies of the Software, and to
8 permit persons to whom the Software is furnished to do so, subject to
9 the following conditions:
11 The above copyright notice and this permission notice shall be
12 included in all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 /* This is my infix parser/evaluator. It is optimized for size, intended
24 * as a replacement for yacc-based parsers. However, it may well be faster
25 * than a comparable parser writen in yacc. The supported operators are
26 * listed in #defines below. Parens, order of operations, and error handling
27 * are supported. This code is threadsafe. */
29 /* To use the routine, call it with an expression string. It returns an
30 * integer result. You will also need to define an "error" function
31 * that takes printf arguments and _does not return_, or modify the code
32 * to use another error mechanism. */
38 typedef char operator;
40 #define tok_decl(prec,id) (((id)<<5)|(prec))
41 #define PREC(op) ((op)&0x1F)
43 #define TOK_LPAREN tok_decl(0,0)
45 #define TOK_OR tok_decl(1,0)
47 #define TOK_AND tok_decl(2,0)
49 #define TOK_BOR tok_decl(3,0)
51 #define TOK_BXOR tok_decl(4,0)
53 #define TOK_BAND tok_decl(5,0)
55 #define TOK_EQ tok_decl(6,0)
56 #define TOK_NE tok_decl(6,1)
58 #define TOK_LT tok_decl(7,0)
59 #define TOK_GT tok_decl(7,1)
60 #define TOK_GE tok_decl(7,2)
61 #define TOK_LE tok_decl(7,3)
63 #define TOK_LSHIFT tok_decl(8,0)
64 #define TOK_RSHIFT tok_decl(8,1)
66 #define TOK_ADD tok_decl(9,0)
67 #define TOK_SUB tok_decl(9,1)
69 #define TOK_MUL tok_decl(10,0)
70 #define TOK_DIV tok_decl(10,1)
71 #define TOK_REM tok_decl(10,2)
74 #define TOK_BNOT tok_decl(UNARYPREC,0)
75 #define TOK_NOT tok_decl(UNARYPREC,1)
76 #define TOK_UMINUS tok_decl(UNARYPREC,2)
78 #define TOK_NUM tok_decl(15,0)
80 #define ARITH_APPLY(op) arith_apply(op, numstack, &numstackptr)
81 #define NUMPTR (*numstackptr)
82 static short arith_apply(operator op, long *numstack, long **numstackptr)
84 if (NUMPTR == numstack) goto err;
87 else if (op == TOK_NOT)
88 NUMPTR[-1] = !(NUMPTR[-1]);
89 else if (op == TOK_BNOT)
90 NUMPTR[-1] = ~(NUMPTR[-1]);
92 /* Binary operators */
94 if (NUMPTR-1 == numstack) goto err;
97 NUMPTR[-1] |= *NUMPTR;
98 else if (op == TOK_OR)
99 NUMPTR[-1] = *NUMPTR || NUMPTR[-1];
100 else if (op == TOK_BAND)
101 NUMPTR[-1] &= *NUMPTR;
102 else if (op == TOK_AND)
103 NUMPTR[-1] = NUMPTR[-1] && *NUMPTR;
104 else if (op == TOK_EQ)
105 NUMPTR[-1] = (NUMPTR[-1] == *NUMPTR);
106 else if (op == TOK_NE)
107 NUMPTR[-1] = (NUMPTR[-1] != *NUMPTR);
108 else if (op == TOK_GE)
109 NUMPTR[-1] = (NUMPTR[-1] >= *NUMPTR);
110 else if (op == TOK_RSHIFT)
111 NUMPTR[-1] >>= *NUMPTR;
112 else if (op == TOK_LSHIFT)
113 NUMPTR[-1] <<= *NUMPTR;
114 else if (op == TOK_GT)
115 NUMPTR[-1] = (NUMPTR[-1] > *NUMPTR);
116 else if (op == TOK_LT)
117 NUMPTR[-1] = (NUMPTR[-1] < *NUMPTR);
118 else if (op == TOK_LE)
119 NUMPTR[-1] = (NUMPTR[-1] <= *NUMPTR);
120 else if (op == TOK_MUL)
121 NUMPTR[-1] *= *NUMPTR;
122 else if (op == TOK_DIV)
123 NUMPTR[-1] /= *NUMPTR;
124 else if (op == TOK_REM)
125 NUMPTR[-1] %= *NUMPTR;
126 else if (op == TOK_ADD)
127 NUMPTR[-1] += *NUMPTR;
128 else if (op == TOK_SUB)
129 NUMPTR[-1] -= *NUMPTR;
135 extern long arith (const char *startbuf)
137 register char arithval;
138 const char *expr = startbuf;
140 operator lasttok = TOK_MUL, op;
141 size_t datasizes = strlen(startbuf);
144 long *numstack, *numstackptr;
146 operator *stack = alloca(datasizes * sizeof(operator)), *stackptr = stack;
147 numstack = alloca((datasizes/2+1)*sizeof(long)), numstackptr = numstack;
149 while ((arithval = *expr)) {
150 if (arithval == ' ' || arithval == '\n' || arithval == '\t')
152 if ((unsigned)arithval-'0' <= 9) /* isdigit */ {
153 *numstackptr++ = strtol(expr, (char **) &expr, 10);
156 } if (arithval == '(') {
157 *stackptr++ = TOK_LPAREN;
158 lasttok = TOK_LPAREN;
160 } if (arithval == ')') {
162 while (stackptr != stack) {
164 if (op == TOK_LPAREN)
166 if(ARITH_APPLY(op)) goto err;
168 goto err; /* Mismatched parens */
169 } if (arithval == '|') {
176 } else if (arithval == '&') {
183 } else if (arithval == '=') {
184 if (*++expr != '=') goto err; /* Unknown token */
186 } else if (arithval == '!') {
193 } else if (arithval == '>') {
205 } else if (arithval == '<') {
217 } else if (arithval == '*')
219 else if (arithval == '/')
221 else if (arithval == '%')
223 else if (arithval == '+') {
224 if (lasttok != TOK_NUM) goto prologue; /* Unary plus */
226 } else if (arithval == '-')
227 op = (lasttok == TOK_NUM) ? TOK_SUB : TOK_UMINUS;
228 else if (arithval == '~')
230 else goto err; /* Unknown token */
233 if (prec != UNARYPREC)
234 while (stackptr != stack && PREC(stackptr[-1]) >= prec)
235 if(ARITH_APPLY(*--stackptr)) goto err;
241 while (stackptr != stack)
242 if(ARITH_APPLY(*--stackptr)) goto err;
243 if (numstackptr != numstack+1) {