diff options
Diffstat (limited to 'libbb/arith.c')
-rw-r--r-- | libbb/arith.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/libbb/arith.c b/libbb/arith.c new file mode 100644 index 000000000..c7a3cf98b --- /dev/null +++ b/libbb/arith.c @@ -0,0 +1,250 @@ +/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* This is my infix parser/evaluator. It is optimized for size, intended + * as a replacement for yacc-based parsers. However, it may well be faster + * than a comparable parser writen in yacc. The supported operators are + * listed in #defines below. Parens, order of operations, and error handling + * are supported. This code is threadsafe. */ + +/* To use the routine, call it with an expression string. It returns an + * integer result. You will also need to define an "error" function + * that takes printf arguments and _does not return_, or modify the code + * to use another error mechanism. */ + +#include <stdlib.h> +#include <string.h> +#include "libbb.h" + +typedef char operator; + +#define tok_decl(prec,id) (((id)<<5)|(prec)) +#define PREC(op) ((op)&0x1F) + +#define TOK_LPAREN tok_decl(0,0) + +#define TOK_OR tok_decl(1,0) + +#define TOK_AND tok_decl(2,0) + +#define TOK_BOR tok_decl(3,0) + +#define TOK_BXOR tok_decl(4,0) + +#define TOK_BAND tok_decl(5,0) + +#define TOK_EQ tok_decl(6,0) +#define TOK_NE tok_decl(6,1) + +#define TOK_LT tok_decl(7,0) +#define TOK_GT tok_decl(7,1) +#define TOK_GE tok_decl(7,2) +#define TOK_LE tok_decl(7,3) + +#define TOK_LSHIFT tok_decl(8,0) +#define TOK_RSHIFT tok_decl(8,1) + +#define TOK_ADD tok_decl(9,0) +#define TOK_SUB tok_decl(9,1) + +#define TOK_MUL tok_decl(10,0) +#define TOK_DIV tok_decl(10,1) +#define TOK_REM tok_decl(10,2) + +#define UNARYPREC 14 +#define TOK_BNOT tok_decl(UNARYPREC,0) +#define TOK_NOT tok_decl(UNARYPREC,1) +#define TOK_UMINUS tok_decl(UNARYPREC,2) + +#define TOK_NUM tok_decl(15,0) + +#define ARITH_APPLY(op) arith_apply(op, numstack, &numstackptr) +#define NUMPTR (*numstackptr) +static short arith_apply(operator op, long *numstack, long **numstackptr) +{ + if (NUMPTR == numstack) goto err; + if (op == TOK_UMINUS) + NUMPTR[-1] *= -1; + else if (op == TOK_NOT) + NUMPTR[-1] = !(NUMPTR[-1]); + else if (op == TOK_BNOT) + NUMPTR[-1] = ~(NUMPTR[-1]); + + /* Binary operators */ + else { + if (NUMPTR-1 == numstack) goto err; + --NUMPTR; + if (op == TOK_BOR) + NUMPTR[-1] |= *NUMPTR; + else if (op == TOK_OR) + NUMPTR[-1] = *NUMPTR || NUMPTR[-1]; + else if (op == TOK_BAND) + NUMPTR[-1] &= *NUMPTR; + else if (op == TOK_AND) + NUMPTR[-1] = NUMPTR[-1] && *NUMPTR; + else if (op == TOK_EQ) + NUMPTR[-1] = (NUMPTR[-1] == *NUMPTR); + else if (op == TOK_NE) + NUMPTR[-1] = (NUMPTR[-1] != *NUMPTR); + else if (op == TOK_GE) + NUMPTR[-1] = (NUMPTR[-1] >= *NUMPTR); + else if (op == TOK_RSHIFT) + NUMPTR[-1] >>= *NUMPTR; + else if (op == TOK_LSHIFT) + NUMPTR[-1] <<= *NUMPTR; + else if (op == TOK_GT) + NUMPTR[-1] = (NUMPTR[-1] > *NUMPTR); + else if (op == TOK_LT) + NUMPTR[-1] = (NUMPTR[-1] < *NUMPTR); + else if (op == TOK_LE) + NUMPTR[-1] = (NUMPTR[-1] <= *NUMPTR); + else if (op == TOK_MUL) + NUMPTR[-1] *= *NUMPTR; + else if (op == TOK_DIV) + NUMPTR[-1] /= *NUMPTR; + else if (op == TOK_REM) + NUMPTR[-1] %= *NUMPTR; + else if (op == TOK_ADD) + NUMPTR[-1] += *NUMPTR; + else if (op == TOK_SUB) + NUMPTR[-1] -= *NUMPTR; + } + return 0; +err: return(1); +} + +extern long arith (const char *startbuf) +{ + register char arithval; + const char *expr = startbuf; + + operator lasttok = TOK_MUL, op; + size_t datasizes = strlen(startbuf); + unsigned char prec; + + long *numstack, *numstackptr; + + operator *stack = alloca(datasizes * sizeof(operator)), *stackptr = stack; + numstack = alloca((datasizes/2+1)*sizeof(long)), numstackptr = numstack; + + while ((arithval = *expr)) { + if (arithval == ' ' || arithval == '\n' || arithval == '\t') + goto prologue; + if ((unsigned)arithval-'0' <= 9) /* isdigit */ { + *numstackptr++ = strtol(expr, (char **) &expr, 10); + lasttok = TOK_NUM; + continue; + } if (arithval == '(') { + *stackptr++ = TOK_LPAREN; + lasttok = TOK_LPAREN; + goto prologue; + } if (arithval == ')') { + lasttok = TOK_NUM; + while (stackptr != stack) { + op = *--stackptr; + if (op == TOK_LPAREN) + goto prologue; + if(ARITH_APPLY(op)) goto err; + } + goto err; /* Mismatched parens */ + } if (arithval == '|') { + if (*++expr == '|') + op = TOK_OR; + else { + --expr; + op = TOK_BOR; + } + } else if (arithval == '&') { + if (*++expr == '&') + op = TOK_AND; + else { + --expr; + op = TOK_BAND; + } + } else if (arithval == '=') { + if (*++expr != '=') goto err; /* Unknown token */ + op = TOK_EQ; + } else if (arithval == '!') { + if (*++expr == '=') + op = TOK_NE; + else { + --expr; + op = TOK_NOT; + } + } else if (arithval == '>') { + switch (*++expr) { + case '=': + op = TOK_GE; + break; + case '>': + op = TOK_RSHIFT; + break; + default: + --expr; + op = TOK_GT; + } + } else if (arithval == '<') { + switch (*++expr) { + case '=': + op = TOK_LE; + break; + case '<': + op = TOK_LSHIFT; + break; + default: + --expr; + op = TOK_LT; + } + } else if (arithval == '*') + op = TOK_MUL; + else if (arithval == '/') + op = TOK_DIV; + else if (arithval == '%') + op = TOK_REM; + else if (arithval == '+') { + if (lasttok != TOK_NUM) goto prologue; /* Unary plus */ + op = TOK_ADD; + } else if (arithval == '-') + op = (lasttok == TOK_NUM) ? TOK_SUB : TOK_UMINUS; + else if (arithval == '~') + op = TOK_BNOT; + else goto err; /* Unknown token */ + + prec = PREC(op); + if (prec != UNARYPREC) + while (stackptr != stack && PREC(stackptr[-1]) >= prec) + if(ARITH_APPLY(*--stackptr)) goto err; + *stackptr++ = op; + lasttok = op; +prologue: ++expr; + } /* yay */ + + while (stackptr != stack) + if(ARITH_APPLY(*--stackptr)) goto err; + if (numstackptr != numstack+1) { +err: + return -1; + /* NOTREACHED */ + } + + return *numstack; +} |