[comp.os.minix] expr

erikb@botter.UUCP (04/03/87)

Here's the source for expr(1). Like test(1), this program was not part
of the Minix distribution. Unfortunately, I didn't have time to implement
the ':' operator. Maybe there's someone else out there who will do the job...

Erik Baalbergen
-- cut here --
/* expr(1)  --  author Erik Baalbergen */

/* expr accepts the following grammar:
	expr ::= expr operator expr | "(" expr ")" ;
  where the priority of the operators is taken care of.
  The resulting value is printed on stdout.
  Note that the ":"-operator is not implemented.
*/

#define EOI	0
#define OR	1
#define AND	2
#define LT	3
#define LE	4
#define EQ	5
#define NE	6
#define GE	7
#define GT	8
#define PLUS	9
#define MINUS	10
#define TIMES	11
#define DIV	12
#define MOD	13
#define COLON	14
#define LPAREN	15
#define RPAREN	16
#define OPERAND	20

#define MAXPRIO	6

struct op {
	char *op_text;
	short op_num, op_prio;
} ops[] = {
	{"|",	OR,	6},
	{"&",	AND,	5},
	{"<",	LT,	4},
	{"<=",	LE,	4},
	{"=",	EQ,	4},
	{"!=",	NE,	4},
	{">=",	GE,	4},
	{">",	GT,	4},
	{"+",	PLUS,	3},
	{"-",	MINUS,	3},
	{"*",	TIMES,	2},
	{"/",	DIV,	2},
	{"%",	MOD,	2},
	/* {":",	COLON,	1}, */
	{"(",	LPAREN,	0},
	{")",	RPAREN,	0},
	{0, 0, 0}
};

long eval(), expr();
char *prog;
char **ip;
struct op *ip_op;

main(argc, argv)
	char *argv[];
{
	long res;

	prog = argv[0];
	ip = &argv[1];
	res = expr(lex(*ip), MAXPRIO);
	if (*++ip != 0)
		syntax();
	printf("%ld\n", res);
	exit(0);
}

lex(s)
	register char *s;
{
	register struct op *op = ops;

	if (s == 0) {
		ip_op = 0;
		return EOI;
	}
	while (op->op_text) {
		if (strcmp(s, op->op_text) == 0) {
			ip_op = op;
			return op->op_num;
		}
		op++;
	}
	ip_op = 0;
	return OPERAND;
}

long
num(s)
	register char *s;
{
	long l = 0;
	long sign = 1;

	if (*s == '\0')
		syntax();
	if (*s == '-') {
		sign = -1;
		s++;
	}
	while (*s >= '0' && *s <= '9')
		l = l * 10 + *s++ - '0';
	if (*s != '\0')
		syntax();
	return sign * l;
}

syntax()
{
	write(2, prog, strlen(prog));
	write(2, ": syntax error\n", 15);
	exit(1);
}

long
expr(n, prio)
{
	long res;

	if (n == EOI)
		syntax();
	if (n == LPAREN) {
		res = expr(lex(*++ip), MAXPRIO);
		if (lex(*++ip) != RPAREN)
			syntax();
		return res;
	}
	if (n != OPERAND)
		syntax();
	if (prio == 0)
		return num(*ip);
	res = expr(n, prio - 1);
	while ((n = lex(*++ip)) && ip_op && ip_op->op_prio == prio)
		res = eval(res, n, expr(lex(*++ip), prio - 1));
	ip--;
	return res;
}

long
eval(l1, op, l2)
	long l1, l2;
{
	switch (op) {
	case OR:
		return l1 ? l1 : l2;
	case AND:
		return (l1 && l2) ? l1 : 0;
	case LT:
		return l1 < l2;
	case LE:
		return l1 <= l2;
	case EQ:
		return l1 == l2;
	case NE:
		return l1 != l2;
	case GE:
		return l1 >= l2;
	case GT:
		return l1 > l2;
	case PLUS:
		return l1 + l2;
	case MINUS:
		return l1 - l2;
	case TIMES:
		return l1 * l2;
	case DIV:
		return l1 / l2;
	case MOD:
		return l1 % l2;
	}
	fatal();
}

fatal()
{
	write(2, "fatal\n", 6);
	exit(1);
}

brachman@ubc-cs.UUCP (04/11/87)

The version of expr.c posted by erikb@cs.vu.nl (Erik Baalbergen) has a bug.
Try:
	expr '(' 1 ')' + 1

You'll get "syntax error".

I'm about to post a version of expr(1) to net.sources that is almost
compatible with the 4.[23] BSD version.
The posting will include Henry Spencer's regular expression routines which
are very useful even if you don't want my version of expr.

By the way, the 4.[23] BSD expr has a bug.  Try:
	expr 5 - 6 - 1
See my recent posting to comp.bugs.4bsd for the fix.

-----
Barry Brachman		 | {ihnp4!alberta,uw-beaver,seismo}!
Dept. of Computer Science|	ubc-vision!ubc-cs!brachman
Univ. of British Columbia| brachman@cs.ubc.cdn
Vancouver, B.C. V6T 1W5  | brachman%ubc.csnet@csnet-relay.arpa
(604) 228-4327		 | brachman@ubc.csnet

erikb@botter.UUCP (04/13/87)

The following is a fix in reply to the bug (expr '(' 1 ')' + 1) as reported
by Barry Brachman. The '<' lines refer to the bug version, the '>' to the
fixed version.

Erik Baalbergen
-- cut here --
4c4,5
< 	expr ::= expr operator expr | "(" expr ")" ;
---
> 	expr ::= primary | primary operator expr ;
> 	primary ::= '(' expr ')' | signed-integer ;
49,50c50,51
< 	{"(",	LPAREN,	0},
< 	{")",	RPAREN,	0},
---
> 	{"(",	LPAREN,	-1},
> 	{")",	RPAREN,	-1},
128,131c129,135
< 		res = expr(lex(*++ip), MAXPRIO);
< 		if (lex(*++ip) != RPAREN)
< 			syntax();
< 		return res;
---
> 		if (prio == 0) {
> 			res = expr(lex(*++ip), MAXPRIO);
> 			if (lex(*++ip) != RPAREN)
> 				syntax();
> 		}
> 		else
> 			res = expr(n, prio - 1);
133c137,143
< 	if (n != OPERAND)
---
> 	else
> 	if (n == OPERAND) {
> 		if (prio == 0)
> 			return num(*ip);
> 		res = expr(n, prio - 1);
> 	}
> 	else
135,137d144
< 	if (prio == 0)
< 		return num(*ip);
< 	res = expr(n, prio - 1);