blob: 04c45ec3d83daa6028d41533607035e7cbfbd20d [file] [log] [blame]
Eric Andersen74bcd162001-07-30 21:41:37 +00001/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
2
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:
10
11 The above copyright notice and this permission notice shall be
12 included in all copies or substantial portions of the Software.
13
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.
21*/
22
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. */
28
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. */
33
34#include <stdlib.h>
35#include <string.h>
36#include "libbb.h"
37
38typedef char operator;
39
40#define tok_decl(prec,id) (((id)<<5)|(prec))
41#define PREC(op) ((op)&0x1F)
42
43#define TOK_LPAREN tok_decl(0,0)
44
45#define TOK_OR tok_decl(1,0)
46
47#define TOK_AND tok_decl(2,0)
48
49#define TOK_BOR tok_decl(3,0)
50
51#define TOK_BXOR tok_decl(4,0)
52
53#define TOK_BAND tok_decl(5,0)
54
55#define TOK_EQ tok_decl(6,0)
56#define TOK_NE tok_decl(6,1)
57
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)
62
63#define TOK_LSHIFT tok_decl(8,0)
64#define TOK_RSHIFT tok_decl(8,1)
65
66#define TOK_ADD tok_decl(9,0)
67#define TOK_SUB tok_decl(9,1)
68
69#define TOK_MUL tok_decl(10,0)
70#define TOK_DIV tok_decl(10,1)
71#define TOK_REM tok_decl(10,2)
72
73#define UNARYPREC 14
74#define TOK_BNOT tok_decl(UNARYPREC,0)
75#define TOK_NOT tok_decl(UNARYPREC,1)
76#define TOK_UMINUS tok_decl(UNARYPREC,2)
77
78#define TOK_NUM tok_decl(15,0)
79
80#define ARITH_APPLY(op) arith_apply(op, numstack, &numstackptr)
81#define NUMPTR (*numstackptr)
82static short arith_apply(operator op, long *numstack, long **numstackptr)
83{
84 if (NUMPTR == numstack) goto err;
85 if (op == TOK_UMINUS)
86 NUMPTR[-1] *= -1;
87 else if (op == TOK_NOT)
88 NUMPTR[-1] = !(NUMPTR[-1]);
89 else if (op == TOK_BNOT)
90 NUMPTR[-1] = ~(NUMPTR[-1]);
91
92 /* Binary operators */
93 else {
94 if (NUMPTR-1 == numstack) goto err;
95 --NUMPTR;
96 if (op == TOK_BOR)
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;
Eric Andersen34506362001-08-02 05:02:46 +0000122 else if (op == TOK_DIV) {
123 if(*NUMPTR==0)
124 return -2;
Eric Andersen74bcd162001-07-30 21:41:37 +0000125 NUMPTR[-1] /= *NUMPTR;
Eric Andersen34506362001-08-02 05:02:46 +0000126 }
127 else if (op == TOK_REM) {
128 if(*NUMPTR==0)
129 return -2;
Eric Andersen74bcd162001-07-30 21:41:37 +0000130 NUMPTR[-1] %= *NUMPTR;
Eric Andersen34506362001-08-02 05:02:46 +0000131 }
Eric Andersen74bcd162001-07-30 21:41:37 +0000132 else if (op == TOK_ADD)
133 NUMPTR[-1] += *NUMPTR;
134 else if (op == TOK_SUB)
135 NUMPTR[-1] -= *NUMPTR;
136 }
137 return 0;
Eric Andersen34506362001-08-02 05:02:46 +0000138err: return(-1);
Eric Andersen74bcd162001-07-30 21:41:37 +0000139}
140
Eric Andersen34506362001-08-02 05:02:46 +0000141extern long arith (const char *startbuf, int *errcode)
Eric Andersen74bcd162001-07-30 21:41:37 +0000142{
143 register char arithval;
144 const char *expr = startbuf;
145
146 operator lasttok = TOK_MUL, op;
147 size_t datasizes = strlen(startbuf);
148 unsigned char prec;
149
150 long *numstack, *numstackptr;
Eric Andersen74bcd162001-07-30 21:41:37 +0000151 operator *stack = alloca(datasizes * sizeof(operator)), *stackptr = stack;
Eric Andersen34506362001-08-02 05:02:46 +0000152
153 *errcode = 0;
Eric Andersen74bcd162001-07-30 21:41:37 +0000154 numstack = alloca((datasizes/2+1)*sizeof(long)), numstackptr = numstack;
155
156 while ((arithval = *expr)) {
157 if (arithval == ' ' || arithval == '\n' || arithval == '\t')
158 goto prologue;
159 if ((unsigned)arithval-'0' <= 9) /* isdigit */ {
160 *numstackptr++ = strtol(expr, (char **) &expr, 10);
161 lasttok = TOK_NUM;
162 continue;
163 } if (arithval == '(') {
164 *stackptr++ = TOK_LPAREN;
165 lasttok = TOK_LPAREN;
166 goto prologue;
167 } if (arithval == ')') {
168 lasttok = TOK_NUM;
169 while (stackptr != stack) {
170 op = *--stackptr;
171 if (op == TOK_LPAREN)
172 goto prologue;
Eric Andersen34506362001-08-02 05:02:46 +0000173 *errcode = ARITH_APPLY(op);
174 if(*errcode) return *errcode;
Eric Andersen74bcd162001-07-30 21:41:37 +0000175 }
176 goto err; /* Mismatched parens */
177 } if (arithval == '|') {
178 if (*++expr == '|')
179 op = TOK_OR;
180 else {
181 --expr;
182 op = TOK_BOR;
183 }
184 } else if (arithval == '&') {
185 if (*++expr == '&')
186 op = TOK_AND;
187 else {
188 --expr;
189 op = TOK_BAND;
190 }
191 } else if (arithval == '=') {
192 if (*++expr != '=') goto err; /* Unknown token */
193 op = TOK_EQ;
194 } else if (arithval == '!') {
195 if (*++expr == '=')
196 op = TOK_NE;
197 else {
198 --expr;
199 op = TOK_NOT;
200 }
201 } else if (arithval == '>') {
202 switch (*++expr) {
203 case '=':
204 op = TOK_GE;
205 break;
206 case '>':
207 op = TOK_RSHIFT;
208 break;
209 default:
210 --expr;
211 op = TOK_GT;
212 }
213 } else if (arithval == '<') {
214 switch (*++expr) {
215 case '=':
216 op = TOK_LE;
217 break;
218 case '<':
219 op = TOK_LSHIFT;
220 break;
221 default:
222 --expr;
223 op = TOK_LT;
224 }
225 } else if (arithval == '*')
226 op = TOK_MUL;
227 else if (arithval == '/')
228 op = TOK_DIV;
229 else if (arithval == '%')
230 op = TOK_REM;
231 else if (arithval == '+') {
232 if (lasttok != TOK_NUM) goto prologue; /* Unary plus */
233 op = TOK_ADD;
234 } else if (arithval == '-')
235 op = (lasttok == TOK_NUM) ? TOK_SUB : TOK_UMINUS;
236 else if (arithval == '~')
237 op = TOK_BNOT;
238 else goto err; /* Unknown token */
239
240 prec = PREC(op);
241 if (prec != UNARYPREC)
Eric Andersen34506362001-08-02 05:02:46 +0000242 while (stackptr != stack && PREC(stackptr[-1]) >= prec) {
243 *errcode = ARITH_APPLY(*--stackptr);
244 if(*errcode) return *errcode;
245 }
Eric Andersen74bcd162001-07-30 21:41:37 +0000246 *stackptr++ = op;
247 lasttok = op;
248prologue: ++expr;
249 } /* yay */
250
Eric Andersen34506362001-08-02 05:02:46 +0000251 while (stackptr != stack) {
252 *errcode = ARITH_APPLY(*--stackptr);
253 if(*errcode) return *errcode;
254 }
Eric Andersen74bcd162001-07-30 21:41:37 +0000255 if (numstackptr != numstack+1) {
256err:
Eric Andersen34506362001-08-02 05:02:46 +0000257 *errcode = -1;
Eric Andersen74bcd162001-07-30 21:41:37 +0000258 return -1;
259 /* NOTREACHED */
260 }
261
262 return *numstack;
263}