/* Copyright (C) 1986 Free Software Foundation, Inc.

		       NO WARRANTY

  BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
CORRECTION.

 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.

		GENERAL PUBLIC LICENSE TO COPY

  1. You may copy and distribute verbatim copies of this source file
as you receive it, in any medium, provided that you conspicuously
and appropriately publish on each copy a valid copyright notice
"Copyright (C) 1986 Free Software Foundation, Inc"; and include following the
copyright notice a verbatim copy of the above disclaimer of warranty
and of this License.

  2. You may modify your copy or copies of this source file or
any portion of it, and copy and distribute such modifications under
the terms of Paragraph 1 above, provided that you also do the following:

    a) cause the modified files to carry prominent notices stating
    that you changed the files and the date of any change; and

    b) cause the whole of any work that you distribute or publish,
    that in whole or in part contains or is a derivative of this
    program or any part thereof, to be freely distributed
    and licensed to all third parties on terms identical to those
    contained in this License Agreement (except that you may choose
    to grant more extensive warranty protection to third parties,
    at your option).

  3. You may copy and distribute this program or any portion of it in
compiled, executable or object code form under the terms of Paragraphs
1 and 2 above provided that you do the following:

    a) cause each such copy to be accompanied by the
    corresponding machine-readable source code, which must
    be distributed under the terms of Paragraphs 1 and 2 above; or,

    b) cause each such copy to be accompanied by a
    written offer, with no time limit, to give any third party
    free (except for a nominal shipping charge) a machine readable
    copy of the corresponding source code, to be distributed
    under the terms of Paragraphs 1 and 2 above; or,

    c) in the case of a recipient of this program in compiled, executable
    or object code form (without the corresponding source code) you
    shall cause copies you distribute to be accompanied by a copy
    of the written offer of source code which you received along
    with the copy you received.

  4. You may not copy, sublicense, distribute or transfer this program
except as expressly provided under this License Agreement.  Any attempt
otherwise to copy, sublicense, distribute or transfer this program is void and
your rights to use the program under this License agreement shall be
automatically terminated.  However, parties who have received computer
software programs from you with this License Agreement will not have
their licenses terminated so long as such parties remain in full compliance.

*/

/*
 * Author: Mike Parker.
 * Expr.  This program evaluates expressions.  Each token (operator, operand,
 *  parenthesis) of the expression must be a seperate argument.  The parser
 *  used is a reasonably general one, though any incarnation of it is
 *  language-specific.  It is especially nice for expressions.
 *
 * The way it works is (parse_expr is special, parse_expr_1 is typical)
 *  follows.  Note that when the summary below speaks of a new node in this
 *  implementation no parse tree is needed; the node is evaluated
 *  immediately.  Note that one function can handle multiple operators all of
 *  equal precedence, provided they all associate ((x op x) op x).
 *
 *	global token-list
 *
 *	parse_expr ()
 *	while true
 *		lhs = parse_expr_1 ()
 *		if next-token = lowest-precendence-operator then
 *			advance token-list
 *			rhs = parse_expr_1 ()
 *			lhs = new-node (lhs, operator, rhs)
 *			continue while
 *		else if no more tokens then
 *			return lhs
 *		else
 *			syntax error (unrecognized operator)
 *		endif
 *	end while
 *
 *	parse_expr_1 ()
 *	while true
 *		lhs = parse_expr_2 ()
 *		if next-token = second-lowest-precedence-operator then
 *			advance token-list
 *			rhs = parse_expr_1 ()
 *			lhs = new-node (lhs, operator, rhs)
 *			continue while
 *		else
 *			return lhs
 *		endif
 *	end while
 *
 *	parse_expr_2 () similar, with a different operator
 *	parse_expr_3 () similar, with a different operator
 *	etc, until finally
 *
 *	parse_expr_N ()
 *	if next-token is a left paren then
 *		advance token-list
 *		node = parse_expr () (the top parse_expr)
 *		check that next token is a right paren (syntax error if not)
 *		advance token-list
 *		return node
 *	else if next-token is a legal operand then
 *		advance token-list
 *		return new-leaf-node (operand)
 *	else
 *		syntax error (invalid operand)
 *	endif
 *
 * The regular expression parser is very limited in the regular expressions
 *  it accepts.  Only one \(...\) pair may be specified, there is no \|
 *  operator, only some things may be starred, etc.  \1 is legal in the
 *  expression anywhere after the \(...\) pair and matches only the exact
 *  string which the \(...\) matched.
 */

/*
 * This code assumes the following:
 *
 *	setjmp / longjmp as in 4.2bsd
 *	no char in a regular expression will have bit 0x80 set (this bit is
 *	  used internally to designate a special R.E. operator).  If this is
 *	  not the case, or if characters are less than 8 bits wide, the
 *	  #defines below for RE_LITERAL, RE_DOT, etc (don't miss RE_special)
 *	  must be changed to any characters (except the null) which cannot
 *	  appear.  RE_STAR, |ed with any of the RE_XXXX symbols, must also be
 *	  something something which cannot appear in a regular expression.
 *	  RE_STAR is not used except by |ing it into one of the RE_XXXX
 *	  symbols, so it can be a valid character when standing alone.
 *	support for enum types (though this could be fixed to use ints and
 *	  #defined constants)
 *	Heavy recursion is OK (in the regular expression match routine)
 */

#include <stdio.h>
#include <setjmp.h>
#include <ctype.h>

/*#define EVAL_TRACE /* define this to get evaluation trace */
/*#define RE_TRACE /* define this to get regular expression trace */

char *malloc ();
char *realloc ();
char *copyofstr (); /* defined below */
#define NEW(type) ((type *) Malloc (sizeof (type)))
#define OLD(x) free ((char *) x)

/* the kinds of value we can have */
typedef enum {
	  integer,
	  string } TYPE;

/* a value is.... */
typedef struct {
	  TYPE type;			/* which kind */
	  union {			/* the value itself */
	    int i;
	    char *s; } u; } VALUE;

char **args;
jmp_buf err;
char *re_str;
char *re_pat;
char *lmark;
char *rmark;
char *re_comp;
int hasmark;
/* See the comments above for the assumptions wired into these constants. */
#define RE_LITERAL ((char)0x80)
#define RE_DOT ((char)0x81)
#define RE_ANYOF ((char)0x82)
#define RE_ENDANY ((char)0x83)
#define RE_MARK ((char)0x84)
#define RE_ENDMARK ((char)0x85)
#define RE_COPYMARK ((char)0x86)
#define RE_ANYRANGE ((char)0x87)
#define RE_ANYOFNOT ((char)0x88)
#define RE_STAR ((char)0x40)
#define RE_special(c) ((c)&0x80)	/* tells whether a char is one of the
					   special things above */

/* called to bomb out when an error is noticed */
error (msg)
     char *msg;
{
  fprintf (stderr, "%s\n", msg);
  longjmp (err, 1);
}

/* malloc with checking */
char *
Malloc (nb)
     int nb;
{
  char *cp;

  cp = malloc (nb);
  if (cp == 0)
    {
      error ("out of memory");
    }
  return (cp);
}

/* return a copy of s in malloc ()ed memory */
char *
copyofstr (s)
     char *s;
{
  int i;
  char *cp;

  i = strlen (s);
  cp = Malloc (i+1);
  strcpy (cp, s);
  return (cp);
}

/* return a VALUE for this integer */
VALUE *
int_value (i)
     int i;
{
  VALUE *v;

  v = NEW (VALUE);
  v->type = integer;
  v->u.i = i;
  return (v);
}

/* return a VALUE for this string */
VALUE *
str_value (s)
     char *s;
{
  VALUE *v;

  v = NEW (VALUE);
  v->type = string;
  v->u.s = copyofstr (s);
  return (v);
}

/* free a VALUE, including structure components */
freev (v)
     VALUE *v;
{
  if (v->type == string)
    {
      free (v->u.s);
    }
  OLD (v);
}

/* print VALUE v */
printv (v)
     VALUE *v;
{
  switch (v->type)
    {
    case integer:
      printf ("%d\n", v->u.i);
      break;
    case string:
      printf ("%s\n", v->u.s);
      break;
    }
}

/* is it null-string / zero-number ? */
int
null (v)
     VALUE *v;
{
  switch (v->type)
    {
    case integer:
      return (v->u.i == 0);
      break;
    case string:
      return (v->u.s[0] == '\0');
      break;
    }
}

/* is it a string value? */
int
isstring (v)
     VALUE *v;
{
  return (v->type == string);
}

/* coerce it to a string value (can't fail) */
tostring (v)
     VALUE *v;
{
  char *temp;

  switch (v->type)
    {
    case integer:
      temp = Malloc (4*(sizeof (int)/sizeof (char)));
      sprintf (temp, "%d", v->u.i);
      v->u.s = temp;
      v->type = string;
      break;
    case string:
      break;
    }
}

/* coerce it to an integer value (return 1 on success, 0 on failure) */
toarith (v)
     VALUE *v;
{
  int i;
  int neg;
  char *cp;

  switch (v->type)
    {
    case integer:
      return (1);
      break;
    case string:
      i = 0;
      cp = v->u.s;
      if (neg = (*cp == '-'))
	{
	  cp ++;
	}
      for (; *cp; cp++)
	{
	  if (isdigit (*cp))
	    {
	      i = (i * 10) + (*cp - '0');
	    }
	  else
	    {
	      return (0);
	    }
	}
      free (v->u.s);
      v->u.i = i * (neg ? -1 : 1);
      v->type = integer;
      return (1);
      break;
    }
}

/* does the next token match this exactly? */
int
nextarg (str)
     char *str;
{
  return (strcmp (*args, str) == 0);
}

/* are there no more tokens? */
int
nomoreargs ()
{
  return (*args == 0);
}

/* the comparison operator handling functions */
#define cmpf (name, rel)					\
	name (l, r) VALUE *l; VALUE *r;			\
	{						\
	 if (isstring (l) || isstring (r))		\
	  { tostring (l);				\
	    tostring (r);				\
	    return (strcmp (l->u.s, r->u.s) rel 0);	\
	  }						\
	 else						\
	  { return (l->u.i rel r->u.i);			\
	  }						\
	}

cmpf (less_than, <)
cmpf (less_equal, <=)
cmpf (equal, ==)
cmpf (not_equal, !=)
cmpf (greater_equal, >=)
cmpf (greater_than, >)

#undef cmpf

/* the arithmetic operator handling functions */
#define arithf (name, op)					\
	name (l, r) VALUE *l; VALUE *r;			\
	{						\
	 if (!toarith (l) || !toarith (r))		\
	  { error ("non-numeric argument");		\
	  }						\
	 return (l->u.i op r->u.i);			\
	}

arithf (plus, +)
arithf (minus, -)
arithf (times, *)
arithf (divide, /)
arithf (mod, %)

#undef arithf

#ifdef EVAL_TRACE
/* function to print evaluation trace and args remaining */
trace (fxn)
     char *fxn;
{
  char **a;

  printf ("%s:", fxn);
  for (a=args; *a; a++)
    {
      printf (" %s", *a);
    }
  putchar ('\n');
}
#endif

/* compile a regular expression into internal form */
/* The internal form is purely prefix, otherwise is very nearly a copy of the
    input expression.  For example, * operators are moved up and |ed with the
    thing they apply to.  - signs in []s are moved to before the characters
    they apply to.  Things like that.  All special characters are replaced
    with the corresponding ``magic'' character from the #defines at the top.
    Note that the compiled form never has more characters than the
    non-compiled form (this is assumed in the malloc call at the very
    beginning).  If an ordinary character is starred, it is turned into a
    RE_LITERAL operator (as if it had been backslashed) and that gets
    starred. */
re_compile ()
{
  char *pat;
  char *to;
  char *justdid;
  char *doing;
  int hadmark;
  int hadendmark;
  char last;

  re_comp = Malloc (strlen (re_pat)+1);
  to = re_comp;
  pat = re_pat;
  doing = 0;
  hadmark = 0;
  hadendmark = 0;
  hasmark = 0;
  for (; *pat; pat++)
    {
      justdid = doing;
      doing = to;
      switch (*pat)
	{
	case '.':
	  *to++ = RE_DOT;
	  break;
	case '\\':
	  pat ++;
	  if (*pat == '1')
	    {
	      *to++ = RE_COPYMARK;
	    }
	  else if (isdigit (*pat))
	    {
	      error ("\\ digit too large");
	    }
	  else if (*pat == '(')
	    {
	      if (hadmark)
		{
		  error ("too many `\\('s");
		}
	      *to++ = RE_MARK;
	      hadmark = 1;
	      hasmark = 1;
	    }
	  else if (*pat == ')')
	    {
	      if (hadendmark || !hadmark)
		{
		  error ("RE syntax error");
		}
	      *to++ = RE_ENDMARK;
	      hadendmark = 1;
	    }
	  else
	    {
	      *to++ = RE_LITERAL;
	      *to++ = *pat;
	    }
	  break;
	case '[':
	  *to++ = RE_ANYOF;
	  pat ++;
	  if (*pat == '^')
	    {
	      to[-1] = RE_ANYOFNOT;
	      pat ++;
	    }
	  last = 0;
	  if (*pat == ']')
	    {
	      *to++ = *pat++;
	      last = ']';
	    }
	  for (; *pat; pat++)
	    {
	      if (*pat == '-')
		{
		  if (last && (last < pat[1]))
		    {
		      to[-1] = RE_ANYRANGE;
		      *to++ = last;
		      *to++ = *++pat;
		      last = 0;
		    }
		  else
		    {
		      *to++ = '-';
		      last = '-';
		    }
		}
	      else if (*pat == ']')
		{
		  *to++ = RE_ENDANY;
		  break;
		}
	      else
		{
		  last = *pat;
		  *to++ = last;
		}
	    }
	  if (! *pat)
	    {
	      error ("RE syntax error -- no closing ]");
	    }
	  break;
	case '*':
	  if (justdid)
	    {
	      if (! RE_special (*justdid))
		{
		  if (justdid != to-1) /* just bugproofing */
		    {
		      error ("INTERNAL RE compile bug: justdid != to-1");
		    }
		  if ((justdid > re_comp) && (justdid[-1] == RE_LITERAL))
		    {
		      justdid[-1] = RE_LITERAL | RE_STAR;
		    }
		  else
		    {
		      *to++ = *justdid;
		      *justdid = RE_LITERAL | RE_STAR;
		    }
		}
	      else
		{
		  *justdid |= RE_STAR;
		}
	    }
	  break;
	default:
	  *to++ = *pat;
	  break;
	}
    }
  *to++ = '\0';
}

int
belongs (c, neg)
     char c;
     int neg;
{
  char *cp;

  if (c == '\0')
    {
      return (0); /* null never belongs, even if the []s are ^ed */
    }
  for (cp=re_comp; (*cp)!=RE_ENDANY; cp++)
    {
      if (*cp == c)
	{
	  return (!neg);
	}
      if (*cp == RE_ANYRANGE)
	{
	  cp += 2;
	  if ((cp[-1] <= c) && (c <= cp[0]))
	    {
	      return (!neg);
	    }
	}
    }
  return (neg);
}

#ifdef RE_TRACE
/* function to dump a regular expression compiled form in readable format */
dump_re (str)
     char *str;
{
  char *cp;

  for (cp=str; *cp; cp++)
    {
      int isstar;
      if (! RE_special (*cp))
	{
	  putchar (*cp);
	}
      else
	{
	  isstar = *cp & RE_STAR;
	  switch ((*cp) & ~ RE_STAR)
	    {
	    case RE_LITERAL:
	      putchar ('\\');
	      putchar (*++cp);
	      break;
	    case RE_DOT:
	      putchar ('.');
	      break;
	    case RE_ANYOF:
	    case RE_ANYOFNOT:
	      putchar ('[');
	      if ((*cp&~RE_STAR) == RE_ANYOFNOT)
		{
		  putchar ('^');
		}
	      for (cp++; *cp!=RE_ENDANY; cp++)
		{
		  if (*cp == RE_ANYRANGE)
		    {
		      putchar (*++cp);
		      putchar ('-');
		      putchar (*++cp);
		    }
		  else
		    {
		      putchar (*cp);
		    }
		}
	      putchar (']');
	      break;
	    case RE_MARK:
	      printf ("\\(");
	      break;
	    case RE_ENDMARK:
	      printf ("\\)");
	      break;
	    case RE_COPYMARK:
	      printf ("\\1");
	      break;
	    }
	  if (isstar)
	    {
	      putchar ('*');
	      isstar = 0;
	    }
	}
    }
}
#endif

/* compare the (compiled) regular expression in re_comp with the string in
    re_str.  Return 1 on match, 0 on failure.  Leaves lmark, rmark, re_str
    set according to \(...\)s and how much was matched. */
int
re_execute ()
{
  register char *s_save;
  register char *p_save;
  register int n;

#ifdef RE_TRACE
  printf ("re_execute, str=%s, pat=", re_str);
  dump_re (re_comp);
  putchar ('\n');
#endif
  if (*re_comp == '\0') /* empty pattern matches, right here */
    {
      return (1);
    }
  if (! RE_special (*re_comp)) /* ordinary character */
    {
      if (*re_str == *re_comp)
	{
	  re_str ++;
	  re_comp ++;
	  return (re_execute ());
	}
      else
	{
	  return (0);
	}
    }
  switch (*re_comp)
    {
    case RE_LITERAL:		/* backslashed ordinary character */
      re_comp ++;
      if (*re_str == *re_comp)
	{
	  re_str ++;
	  re_comp ++;
	  return (re_execute ());
	}
      else
	{
	  return (0);
	}
      break;
    case RE_LITERAL | RE_STAR:	/* single character, starred */
      s_save = re_str;
      p_save = re_comp;
      if (*re_str == re_comp[1])
	{
	  re_str ++;
	  if (re_execute ())
	    {
	      return (1);
	    }
	  else
	    {
	      re_str = s_save + 1;
	      re_comp = p_save + 2;
	      if (re_execute ())
		{
		  return (1);
		}
	    }
	}
      re_str = s_save;
      re_comp = p_save + 2;
      return (re_execute ());
      break;
    case RE_DOT:		/* any character */
      re_comp ++;
      if (*re_str)
	{
	  re_str ++;
	  return (re_execute ());
	}
      return (0);
      break;
    case RE_DOT | RE_STAR:	/* .* -> any string of characters */
      s_save = re_str;
      p_save = re_comp;
      if (*re_str)
	{
	  re_str ++;
	  if (re_execute ())
	    {
	      return (1);
	    }
	  else
	    {
	      re_str = s_save + 1;
	      re_comp = p_save + 1;
	      if (re_execute ())
		{
		  return (1);
		}
	    }
	}
      re_str = s_save;
      re_comp = p_save + 1;
      return (re_execute ());
      break;
    case RE_ANYOF:		/* [  ] */
    case RE_ANYOFNOT:		/* [^ ] */
      re_comp ++;
      if (belongs (*re_str, (re_comp[-1]==RE_ANYOFNOT)))
	{
	  re_str ++;
	  for (; *re_comp!=RE_ENDANY; re_comp++) ;
	  re_comp ++;
	  return (re_execute ());
	}
      else
	{
	  return (0);
	}
      break;
    case RE_ANYOF | RE_STAR:	/* [  ]* */
    case RE_ANYOFNOT | RE_STAR:	/* [^ ]* */
      s_save = re_str;
      p_save = ++ re_comp;
      if (belongs (*re_str, (re_comp[-1]==(RE_ANYOFNOT|RE_STAR))))
	{
	  re_str ++;
	  re_comp --;
	  if (re_execute ())
	    {
	      return (1);
	    }
	  else
	    {
	      re_str = s_save + 1;
	      for (re_comp=p_save; *re_comp!=RE_ENDANY; re_comp++) ;
	      re_comp ++;
	      if (re_execute ())
		{
		  return (1);
		}
	    }
	}
      re_str = s_save;
      for (re_comp=p_save; *re_comp!=RE_ENDANY; re_comp++) ;
      re_comp ++;
      return (re_execute ());
      break;
    case RE_MARK:
      re_comp ++;
      lmark = re_str;
      return (re_execute ());
      break;
    case RE_ENDMARK:
      re_comp ++;
      rmark = re_str;
      return (re_execute ());
      break;
    case RE_COPYMARK:
      re_comp ++;
      if (!lmark)
	{
	  error ("\\1 before \\(...\\)");
	}
      if (!rmark)
	{
	  error ("\\1 inside \\(...\\)");
	}
      n = rmark - lmark;
      if (strncmp (re_str, lmark, n) == 0)
	{
	  re_str += n;
	  return (re_execute ());
	}
      return (0);
      break;
    default:
      error ("RE syntax error");
      break;
    }
}

/* do the : operator.  sv is the VALUE for the lhs (the string), pv is the
    VALUE for the rhs (the pattern). */
VALUE *
docolon (sv, pv)
     VALUE *sv;
     VALUE *pv;
{
  char *cp;
  int n;
  VALUE *v;

  tostring (sv);
  re_str = sv->u.s;
  tostring (pv);
  re_pat = pv->u.s;
#ifdef RE_TRACE
  printf ("RE: %s\n", re_pat);
#endif
  re_compile ();
#ifdef RE_TRACE
  printf ("Compiled: ");
  dump_re (re_comp);
  putchar ('\n');
#endif
  lmark = 0;
  rmark = 0;
  if (re_execute ())		/* if we match.... */
    {
      if (hasmark)		/* \(...\) used? */
	{
	  *rmark = '\0';
	  v = str_value (lmark);	/* make a VALUE of it */
	}
      else
	{
	  v = int_value (re_str - sv->u.s); /* # chars matched */
	}
    }
  else			/* match failed -- return right kind of null */
    {
      if (hasmark)
	{
	  v = str_value ("");
	}
      else
	{
	  v = int_value (0);
	}
    }
  return (v);
}

/* eval6 -- handles bare operands and ( expr ) syntax */
VALUE *
eval6 ()
{
  VALUE *v;
  VALUE *eval ();

#ifdef EVAL_TRACE
  trace ("eval6");
#endif
  if (nextarg ("("))
    {
      args ++;
      v = eval ();
      if (! nextarg (")"))
	{
	  error ("syntax error");
	}
      args ++;
      return (v);
    }
  else if (nextarg (")"))
    {
      error ("syntax error");
    }
  else
    {
      v = str_value (*args++);
      (void) toarith (v);
      return (v);
    }
}

/* eval5 -- handles : operator (pattern matching).  Calls ``docolon'' to do
    the real work. */
VALUE *
eval5 ()
{
  VALUE *l;
  VALUE *r;
  VALUE *v;

#ifdef EVAL_TRACE
  trace ("eval5");
#endif
  l = eval6 ();
  while (1)
    {
      if (nextarg (":"))
	{
	  args ++;
	  r = eval6 ();
	  v = docolon (l, r);
	  freev (l);
	  freev (r);
	  l = v;
	}
      else
	{
	  return (l);
	}
    }
}

/* eval4 -- handles *, /, % operators */
VALUE *
eval4 ()
{
  VALUE *l;
  VALUE *r;
  int (*fxn)();
  int val;

#ifdef EVAL_TRACE
  trace ("eval4");
#endif
  l = eval5 ();
  while (1)
    {
      fxn = 0;
      if (nextarg ("*"))
	{
	  fxn = times;
	}
      else if (nextarg ("/"))
	{
	  fxn = divide;
	}
      else if (nextarg ("%"))
	{
	  fxn = mod;
	}
      if (fxn)
	{
	  args ++;
	  r = eval5 ();
	  val = (*fxn)(l, r);
	  freev (l);
	  freev (r);
	  l = int_value (val);
	}
      else
	{
	  return (l);
	}
    }
}

/* eval3 -- handles +, - operators */
VALUE *
eval3 ()
{
  VALUE *l;
  VALUE *r;
  int (*fxn)();
  int val;

#ifdef EVAL_TRACE
  trace ("eval3");
#endif
  l = eval4 ();
  while (1)
    {
      fxn = 0;
      if (nextarg ("+"))
	{
	  fxn = plus;
	}
      else if (nextarg ("-"))
	{
	  fxn = minus;
	}
      if (fxn)
	{
	  args ++;
	  r = eval4 ();
	  val = (*fxn)(l, r);
	  freev (l);
	  freev (r);
	  l = int_value (val);
	}
      else
	{
	  return (l);
	}
    }
}

/* eval2 -- handles comparisons */
VALUE *
eval2 ()
{
  VALUE *l;
  VALUE *r;
  int (*fxn)();
  int val;

#ifdef EVAL_TRACE
  trace ("eval2");
#endif
  l = eval3 ();
  while (1)
    {
      fxn = 0;
      if (nextarg ("<"))
	{
	  fxn = less_than;
	}
      else if (nextarg ("<="))
	{
	  fxn = less_equal;
	}
      else if (nextarg ("="))
	{
	  fxn = equal;
	}
      else if (nextarg ("!="))
	{
	  fxn = not_equal;
	}
      else if (nextarg (">="))
	{
	  fxn = greater_equal;
	}
      else if (nextarg (">"))
	{
	  fxn = greater_than;
	}
      if (fxn)
	{
	  args ++;
	  r = eval3 ();
	  val = (*fxn)(l, r);
	  freev (l);
	  freev (r);
	  l = int_value (val);
	}
      else
	{
	  return (l);
	}
    }
}

/* eval1 -- handles & */
VALUE *
eval1 ()
{
  VALUE *l;
  VALUE *r;

#ifdef EVAL_TRACE
  trace ("eval1");
#endif
  l = eval2 ();
  while (1)
    {
      if (nextarg ("&"))
	{
	  args ++;
	  r = eval2 ();
	  if (null (l) || null (r))
	    {
	      freev (l);
	      freev (r);
	      l = int_value (0);
	    }
	  else
	    {
	      freev (r);
	    }
	}
      else
	{
	  return (l);
	}
    }
}

/* eval -- handles | */
VALUE *
eval ()
{
  VALUE *l;
  VALUE *r;

#ifdef EVAL_TRACE
  trace ("eval");
#endif
  l = eval1 ();
  while (1)
    {
      if (nextarg ("|"))
	{
	  args ++;
	  r = eval1 ();
	  if (null (l))
	    {
	      freev (l);
	      l = r;
	    }
	  else
	    {
	      freev (r);
	    }
	}
      else
	{
	  return (l);
	}
    }
}

main (ac, av)
     int ac;
     char **av;
{
  VALUE *v;

  if (setjmp (err))
    {
      exit (2);
    }
  args = av + 1;
  v = eval ();
  if (! nomoreargs ())
    {
      error ("syntax error");
    }
  printv (v);
  exit (!null (v));
}
