/* $NetBSD: expr.c,v 1.3 1995/04/28 23:27:15 jtc Exp $ */
/*
* Written by J.T. Conklin <jtc@netbsd.org>.
* Public domain.
* converted to a library function by D'Arcy J.M. Cain
* DJMC: I'd like to make this reentrant some day
*/
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
/* type used by xstrtok function */
typedef struct {
char *scanpoint; /* filled in by xstrtok */
char *str2parse; /* string to parse - set for first call */
const char *delim; /* string of delimiters */
int quote; /* respect quoting if set */
} XSTRTOK;
extern char *xstrtok(XSTRTOK *xinfo);
/* The calling program is expected to have a fatal function */
void fatal(const char *s,...);
enum token {
OR, AND, EQ, LT, GT, ADD, SUB, MUL, DIV, MOD, MATCH, RP, LP,
NE, LE, GE, OPERAND, EOI
};
static int eval0(void);
static enum token token;
static int tokval;
static XSTRTOK x;
int expr(const char *str);
static void
nexttoken(void)
{
static char *p = NULL;
static const char *opstr = "|&=<>+-*/%:()";
const char *i;
/* just in case */
if (p)
while (isspace((int) *p))
p++;
if ((!p || !*p) && (p = xstrtok(&x)) == NULL)
{
token = EOI;
return;
}
if ((*p == '-' && isdigit((int) p[1])) || isdigit((int) *p))
{
tokval = strtol(p, &p, 0);
token = OPERAND;
return;
}
if ((i = strchr(opstr, *p)) == NULL)
fatal("Invalid operator %s", p);
if (p[1] == '=')
{
switch (*i)
{
case '<':
token = LE;
p += 2;
return;
case '>':
token = GE;
p += 2;
return;
case '!':
token = NE;
p += 2;
return;
}
}
token = i - opstr;
p++;
return;
}
static int
eval5(void)
{
int v;
if (token != OPERAND)
{
if (token == RP)
{
nexttoken();
v = eval0();
if (token != LP)
fatal("Syntax error - token != LP");
nexttoken();
return v;
}
else
fatal("Syntax error - token != RP");
}
nexttoken();
return tokval;
}
/* Parse and evaluate multiplication and division expressions */
static int
eval4(void)
{
enum token op;
int l = eval5(), r;
while ((op = token) == MUL || op == DIV || op == MOD)
{
nexttoken();
r = eval5();
if (op == MUL)
l *= r;
else
{
if (r == 0)
fatal("division by zero");
if (op == DIV)
l /= r;
else
l %= r;
}
}
return l;
}
/* Parse and evaluate addition and subtraction expressions */
static int
eval3(void)
{
int l = eval4(), r;
enum token op;
while ((op = token) == ADD || op == SUB)
{
nexttoken();
r = eval4();
if (op == ADD)
l += r;
else
l -= r;
}
return l;
}
/* Parse and evaluate comparison expressions */
static int
eval2(void)
{
int l, r;
enum token op;
l = eval3();
while ((op = token) == EQ || op == NE || op == LT || op == GT || op == LE || op == GE)
{
nexttoken();
r = eval3();
switch (op)
{
case GT:
l = (l > r);
break;
case GE:
l = (l >= r);
break;
case LT:
l = (l < r);
break;
case LE:
l = (l <= r);
break;
case EQ:
l = (l == r);
break;
case NE:
l = (l != r);
break;
default:
fatal("Internal error");
}
}
return l;
}
/* Parse and evaluate & expressions */
static int
eval1(void)
{
int l = eval2();
while (token == AND)
{
nexttoken();
l = (eval1() && l);
}
return l;
}
/* Parse and evaluate | expressions */
static int
eval0(void)
{
int l = eval1();
while (token == OR)
{
nexttoken();
l = (eval1() || l);
}
return l;
}
int
expr(const char *str)
{
int v;
x.str2parse = strdup(str);
x.delim = " ";
x.quote = 0;
nexttoken();
v = eval0();
if (token != EOI)
{
fatal("Syntax error - token != EOI", token);
/* NOTREACHED */
}
return v;
}
syntax highlighted by Code2HTML, v. 0.9.1