/* expr has the grammar:   expr ::= expr operator expr | "(" expr ")" ;
   with operator priority taken care of.  Resulting value goes to stdout.
   Note that ":" operations are not implemented.   This is basically the
   v7expr.c at uunet */

#define MAXPRIO	6
#define OPERAND	20
enum opnum {EOI,OR,AND,LT,LE,EQ,NE,GE,GT,ADD,SUB,MUL,DIV,MOD,LPAREN,RPAREN};

struct E_sop { char *text; enum opnum num; short prio;
}E_ops[]={{"|",  OR,    6}, {"&",  AND,   5}, {"<",  LT,    4}, {"<=", LE,   4},
          {"=",  EQ,    4}, {"!=", NE,    4}, {">=", GE,    4}, {">",  GT,   4},
          {"+",  ADD,   3}, {"-",  SUB,   3}, {"*",  MUL,   2}, {"/",  DIV,  2},
          {"%",  MOD,   2}, {"(",  LPAREN,0}, {")",  RPAREN,0}, {0,    0,   0}};

char **E_ip;
struct E_sop *E_op;
int E_error=0;

/* set global E_op to the op and return the opnum or the operand */
E_lex(char* s) {
	struct E_sop *op=E_ops;
	if (s==0) {
        E_op=0;
        return EOI;
    }
	while (op->text) {
		if (strcmp(s,op->text)==0) {
			E_op=op;
			return op->num;
		}
		op++;
	}
	E_op=0;
	return OPERAND;
}

int E_expr(int n, short prio) {
	int res;
	if (n==EOI) { E_error=1; return 1; }
	if (n==LPAREN) {
		res=E_expr(E_lex(*++E_ip), MAXPRIO);
		if (E_lex(*++E_ip) != RPAREN) { E_error=1; return 1; }
		return res;
	}
	if (n != OPERAND) { E_error=1; return 1; }
	if (prio == 0) return atoi(*E_ip);
	res = E_expr(n, prio - 1);
	while ((n = E_lex(*++E_ip)) && E_op && E_op->prio == prio)
		res = E_eval(res, n, E_expr(E_lex(*++E_ip), prio - 1));
	E_ip--;
	return res;
}

int E_eval(int l1, int op, int 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 ADD: return l1 + l2;
        case SUB: return l1 - l2;
        case MUL: return l1 * l2;
        case DIV: return l1 / l2;
        case MOD: return l1 % l2;
    }
    E_error=1;
    return 1;
}

#ifdef ADDON

#include "addon.h"
typedef enum bool { FALSE, TRUE } bool;
extern void set(bool);
#define good_bye { set(TRUE); return; }
#define bad_bye { set(FALSE); return; }

void b_expr(char** argv) {

#else

#define good_bye return 0;
#define bad_bye return E_error;

int main(int argc, char** argv) {

#endif /* ADDON */

	int res;

	E_ip=&argv[1];
	res=E_expr(E_lex(*E_ip), MAXPRIO);

	if (*++E_ip != 0)
        bad_bye
	printf("%ld\n", res);
	if (E_error==1)
        bad_bye
    else
        good_bye
}
