/*	$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