Eric Andersen | 74bcd16 | 2001-07-30 21:41:37 +0000 | [diff] [blame] | 1 | /* 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 | |
| 38 | typedef 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) |
| 82 | static 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 Andersen | 3450636 | 2001-08-02 05:02:46 +0000 | [diff] [blame] | 122 | else if (op == TOK_DIV) { |
| 123 | if(*NUMPTR==0) |
| 124 | return -2; |
Eric Andersen | 74bcd16 | 2001-07-30 21:41:37 +0000 | [diff] [blame] | 125 | NUMPTR[-1] /= *NUMPTR; |
Eric Andersen | 3450636 | 2001-08-02 05:02:46 +0000 | [diff] [blame] | 126 | } |
| 127 | else if (op == TOK_REM) { |
| 128 | if(*NUMPTR==0) |
| 129 | return -2; |
Eric Andersen | 74bcd16 | 2001-07-30 21:41:37 +0000 | [diff] [blame] | 130 | NUMPTR[-1] %= *NUMPTR; |
Eric Andersen | 3450636 | 2001-08-02 05:02:46 +0000 | [diff] [blame] | 131 | } |
Eric Andersen | 74bcd16 | 2001-07-30 21:41:37 +0000 | [diff] [blame] | 132 | else if (op == TOK_ADD) |
| 133 | NUMPTR[-1] += *NUMPTR; |
| 134 | else if (op == TOK_SUB) |
| 135 | NUMPTR[-1] -= *NUMPTR; |
| 136 | } |
| 137 | return 0; |
Eric Andersen | 3450636 | 2001-08-02 05:02:46 +0000 | [diff] [blame] | 138 | err: return(-1); |
Eric Andersen | 74bcd16 | 2001-07-30 21:41:37 +0000 | [diff] [blame] | 139 | } |
| 140 | |
Eric Andersen | 3450636 | 2001-08-02 05:02:46 +0000 | [diff] [blame] | 141 | extern long arith (const char *startbuf, int *errcode) |
Eric Andersen | 74bcd16 | 2001-07-30 21:41:37 +0000 | [diff] [blame] | 142 | { |
| 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 Andersen | 74bcd16 | 2001-07-30 21:41:37 +0000 | [diff] [blame] | 151 | operator *stack = alloca(datasizes * sizeof(operator)), *stackptr = stack; |
Eric Andersen | 3450636 | 2001-08-02 05:02:46 +0000 | [diff] [blame] | 152 | |
| 153 | *errcode = 0; |
Eric Andersen | 74bcd16 | 2001-07-30 21:41:37 +0000 | [diff] [blame] | 154 | 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 Andersen | 3450636 | 2001-08-02 05:02:46 +0000 | [diff] [blame] | 173 | *errcode = ARITH_APPLY(op); |
| 174 | if(*errcode) return *errcode; |
Eric Andersen | 74bcd16 | 2001-07-30 21:41:37 +0000 | [diff] [blame] | 175 | } |
| 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 Andersen | 3450636 | 2001-08-02 05:02:46 +0000 | [diff] [blame] | 242 | while (stackptr != stack && PREC(stackptr[-1]) >= prec) { |
| 243 | *errcode = ARITH_APPLY(*--stackptr); |
| 244 | if(*errcode) return *errcode; |
| 245 | } |
Eric Andersen | 74bcd16 | 2001-07-30 21:41:37 +0000 | [diff] [blame] | 246 | *stackptr++ = op; |
| 247 | lasttok = op; |
| 248 | prologue: ++expr; |
| 249 | } /* yay */ |
| 250 | |
Eric Andersen | 3450636 | 2001-08-02 05:02:46 +0000 | [diff] [blame] | 251 | while (stackptr != stack) { |
| 252 | *errcode = ARITH_APPLY(*--stackptr); |
| 253 | if(*errcode) return *errcode; |
| 254 | } |
Eric Andersen | 74bcd16 | 2001-07-30 21:41:37 +0000 | [diff] [blame] | 255 | if (numstackptr != numstack+1) { |
| 256 | err: |
Eric Andersen | 3450636 | 2001-08-02 05:02:46 +0000 | [diff] [blame] | 257 | *errcode = -1; |
Eric Andersen | 74bcd16 | 2001-07-30 21:41:37 +0000 | [diff] [blame] | 258 | return -1; |
| 259 | /* NOTREACHED */ |
| 260 | } |
| 261 | |
| 262 | return *numstack; |
| 263 | } |