74bcd16425
was written by Aaron Lehmann <aaronl@vitelus.com> for busybox. This patch makes a few trivial changes to Aaron's code so that it can be used (in theory) by the other shells as well... -Erik
251 lines
6.5 KiB
C
251 lines
6.5 KiB
C
/* 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;
|
|
}
|