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.csneterikb@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);