#include        <stdio.h>
#include        <string.h>
#include        <ctype.h>
#include        <math.h>
#include <setjmp.h>
#include        "layout.h"

#define         Boolean         int
#define         True            1
#define         False           0

/* $Header: /afs/athena.mit.edu/astaff/project/forms/src/prototype/RCS/evaluator.c,v 2.4 93/06/10 17:38:04 eddietwo Exp $ */

/* internal #define's used by Eval() */
#define PRECEDENCE(x) ((x) & 0x7F)
#define ASSOCRIGHT(x) ((x) & 0x80)

/* the sizes of the operation and number stacks */
#define OPSTACK 30
#define NUMSTACK 60

/******************************************************************************
  Error handling. */

#define EvalErrorFine 1
#define EvalErrorSyntaxError 2
#define EvalErrorParentheses 3
#define EvalErrorWrongNumArgs 4
#define EvalErrorTwoNumsInARow 5
#define EvalErrorStrangeOperator 6
#define EvalErrorStackOverflow 7
#define EvalErrorStack EvalErrorStackOverflow
#define EvalErrorType 8
#define EvalErrorNoSuchFunction 9
#define EvalErrorNoSuchError 10
#define EvalErrorNoSuchField 11
#define EvalErrorNoFieldValue 12
#define EvalErrorSpecialError 90

#define EvalError(m) longjmp(ES->evalerrorjmp, (m))
#define EvalErrorSpecial(s) {evalspecialerror = (s); EvalError(EvalErrorSpecialError);}

static char *evalspecialerror;
static int evalerrorresult;
static char *evalerrorparsepos;
static char *evalerrordescriptions[] = {
  "",
  "Everything's fine! Wow, that's weird.",
  "Syntax error",
  "You miscounted parentheses",
  "Wrong number of arguments to an @ function",
  "You can't enter two numbers in a row",
  "I don't recognize this operator",
  "Stack overflow (your expression is too complex)",
  "Type conversion error",
  "I don't recognize the name of that function",
  "",
  "This field does not exist",
  "This field has no value",
};

/*****************************************************************************
  What is THIS holy crapola? Well, i wanted to modularize Eval() -- break some
  of its pieces out into other places -- but that meant having a big structure
  for everything to hang out in, or a bunch of static globals. Why not static
  globals? So the evaluator can be reentrant. */

typedef struct {
  char opstack[OPSTACK];	/* operator stack, one character per op. */
  int precedence[OPSTACK];	/* operator precedence stack, contains
				   information on precedence and
				   associativity */

  Value numstack[NUMSTACK];	/* number stack */
  int oppos, numpos;	        /* stack pointers */

  char lastop;                  /* lastop is 1 iff the last thing parsed
				   was not a number. It's only used to decide
				   whether + and - are unary or not. */

  char *s, *originals;		/* this is the actual evaluation string. */

  jmp_buf evalerrorjmp;	        /* used to report errors. */
  
  FormclassPtr formclass;
  ForminstancePtr forminst;
  
} EvalState;


/******************************************************************************
  And now, the magic functions! */

typedef void (*EvalFunctionFunction)(EvalState *ES, int argc, Value *argv);
typedef struct {
  char *name;
  EvalFunctionFunction f;
  int zeroplace;
} EvalFunction;

void EvalStringFunction(EvalState *ES, int argc, Value *argv);
void EvalChoicesFunction(EvalState *ES, int argc, Value *argv);
void EvalNodenumberFunction(EvalState *ES, int argc, Value *argv);
void EvalTrueFunction(EvalState *ES, int argc, Value *argv);
void EvalFalseFunction(EvalState *ES, int argc, Value *argv);

static EvalFunction evalfunctions[] = {
  {"string", EvalStringFunction, 0},
  {"choices", EvalChoicesFunction, 0},
  {"nodenumber", EvalNodenumberFunction, 1},
  {"true", EvalTrueFunction, 1},
  {"false", EvalFalseFunction, 1},
  {"on", EvalTrueFunction, 1},
  {"off", EvalFalseFunction, 1},
  {NULL, NULL, 0}
  };

/*****************************************************************************/

extern FieldPtr FindFieldByName(char *, FormclassPtr);
static char RealEval(char *s, FormclassPtr, ForminstancePtr,
		     Value *valueret);


char Fieldval(FieldPtr field, Value *v) {
  InputFieldPtr	myinput;
  
  if (field->field_type != INPUT)
    return (0);
  
  myinput = field->detail->inputptr;
  switch (myinput->input_type) {
  case TEXTINPUT:
    v->t = ValueString;
    v->v.s = (char *)malloctrace(strlen(myinput->inputsource->text->data)+1);
    strcpy(v->v.s, myinput->inputsource->text->data);
    return 1;
    
  case NUMERICINPUT:
    v->t = ValueInt;
    v->v.i = myinput->inputsource->numeric->data;
    return 1;
    
  case BOOLEANINPUT:
    v->t = ValueBool;
    v->v.b = myinput->inputsource->boolean->data;
    return 1;
    
  case SELECTIONINPUT:
    v->t = ValueIntList;
    {
      int siz = 0, *p = myinput->inputsource->selection->data;
      while (*p > -1) p++, siz++;
      v->v.il = (int *)malloctrace(sizeof(int) * (siz + 1));
      memcpy(v->v.il, myinput->inputsource->selection->data,
	     sizeof(int) * (siz + 1));
    }
    return 1;
    
  }
  
  return (0);
}


/* getprecedence  Returns the precedence and associativity of the operator
     beginning at *ES->s. Moves s ahead by a couple characters, depending, and
     stores that value in ES->s. Stores the precedence in *precd, and returns
     the actual operator as a character. */
static int getprecedence(EvalState *ES, int *precd) {
  int prec;
  char *s = ES->s;
  char op = *s;
  switch (op) {
  case ')':
    prec = 100;
    break;
  case '*': case '/': case '%':
    prec = 1;
    break;
  case '+': case '-':
    if (ES->lastop) prec = 0, op = op == '+' ? 'P' : 'M';
    /* If the last thing was an operator, we are dealing with unary + or -. */
    else prec = 2;
    break;
  case '!':
    if (s[1] != '=') {
      prec = 0, op = 'N';	/* unary !. */
      break;
    }
    /* fall through to != ... */
  case '=':
    if (*++s != '=') EvalError(EvalErrorStrangeOperator);
    /* only != and == are possible, not =9 or =[foo] or ![x] */
    prec = 3;
    break;
  case '>': case '<':
    if (s[1] == '=') s++, op = op == '<' ? 'L' : 'G';
    prec = 4;
    break;
  case '&': case '|':
    if (*++s != op) EvalError(EvalErrorStrangeOperator);
    /* only && and || are possible, not |* or &q or whatever */
    prec = 5;
    break;
  case '?': case ':':
    prec = 6 | 0x80;		/* ?: associates right-to-left */
    break;
  case ',':
    prec = 7;
    break;
  case 0:
    /* end-of-string is a special operator with VERY low precedence.
     * Its precedence is lower than anything else, so it'll cause
     * the entire expression to be evaluated. */
    prec = 1000;
    break;
  default:
    /* A mystery character is an error. */
    EvalError(EvalErrorStrangeOperator);
  }
  if (*s) s++;
  ES->s = s;
  if (precd) *precd = prec;
  return op;
}


/* EvalStringFunction  performs an @string() function call. */
void EvalStringFunction(EvalState *ES, int argc, Value *argv) {
  if (argc != 1) EvalError(EvalErrorWrongNumArgs);
  if (ES->numpos >= NUMSTACK) EvalError(EvalErrorStackOverflow);
  if (!ES->lastop) EvalError(EvalErrorTwoNumsInARow);
  if (!valconvtoString(argv)) EvalError(EvalErrorType);
  valuecpy(&ES->numstack[ES->numpos], argv);
  argv->v.s = NULL;
  ES->numpos++;
  ES->lastop = 0;
}


/* EvalChoicesFunction  performs an @choices() function call. */
void EvalChoicesFunction(EvalState *ES, int argc, Value *argv) {
  if (argc == 0) EvalError(EvalErrorWrongNumArgs);
  if (ES->numpos >= NUMSTACK) EvalError(EvalErrorStackOverflow);
  if (!ES->lastop) EvalError(EvalErrorTwoNumsInARow);
  if (!valconvtoInt(argv)) EvalError(EvalErrorType);
  if (argv[0].v.i >= argc-1 || argv[0].v.i < 0)
    EvalErrorSpecial("The argument to choices was out of range");
  valuecpy(&ES->numstack[ES->numpos], &argv[argv[0].v.i + 1]);
  argv[argv[0].v.i + 1].v.s = NULL;
  ES->numpos++;
  ES->lastop = 0;
}


/* EvalNodenumberFunction  performs an @nodenumber() function call. */
void EvalNodenumberFunction(EvalState *ES, int argc, Value *argv) {
  ES->numstack[ES->numpos].t = ValueInt;
  if (ES->forminst)
    ES->numstack[ES->numpos].v.i = ES->forminst->currentnode->nodenumber;
  else ES->numstack[ES->numpos].v.i = 0;
  ES->numpos++;
  ES->lastop = 0;
}


/* EvalTrueFunction  performs an @true() function call. */
void EvalTrueFunction(EvalState *ES, int argc, Value *argv) {
  ES->numstack[ES->numpos].t = ValueBool;
  ES->numstack[ES->numpos].v.i = 1;
  ES->numpos++;
  ES->lastop = 0;
}


/* EvalFalseFunction  performs an @false() function call. */
void EvalFalseFunction(EvalState *ES, int argc, Value *argv) {
  ES->numstack[ES->numpos].t = ValueBool;
  ES->numstack[ES->numpos].v.i = 0;
  ES->numpos++;
  ES->lastop = 0;
}


/* EvalDoFunction  performs a function call. */
void EvalDoFunction(EvalState *ES) {
  char buf[BUFSIZ], *t = buf, *s = ES->s;
  Value *argv, retarg;
  int argc = 0;
  char NoArgs = 0;
  EvalFunction *ef;
  jmp_buf oldjmpbuf;
  int errorjmpval;

  s++;
  while (isalnum(*s) || *s == '-' || *s == '_') *t++ = *s++;
  *t++ = 0;
  if (*s != '(') NoArgs = 1;
  else s++;
  for (ef = evalfunctions; ef->name && strcmp(ef->name, buf); ef++);
  if (!ef->name) EvalError(EvalErrorNoSuchFunction);
  argv = (Value *)malloctrace(1);

  /* We need our own error handler to get rid of the argv[] array. */
  memcpy(oldjmpbuf, ES->evalerrorjmp, sizeof(jmp_buf));
  if (errorjmpval = setjmp(ES->evalerrorjmp)) {
    Value *v;
    for (v = argv; argc; argc--, v++) freeinternalvaluestorage(v);
    freetrace(argv);
    memcpy(ES->evalerrorjmp, oldjmpbuf, sizeof(jmp_buf));
    if (errorjmpval == EvalErrorFine) {
      ES->s = s;
      return;
    } else EvalError(errorjmpval);
  }
  
  while (!NoArgs && *s != ')') {
    char *t = s, savecrap, level = 0, ok;
    while ((level || *t != ')' && *t != ',') && *t) {
      if (*t == '(') level++;
      if (*t == ')') level--;
      t++;
    }
    if (!*t) EvalError(EvalErrorSyntaxError);
    savecrap = *t;
    *t = 0;
    ok = RealEval(s, ES->formclass, ES->forminst, &retarg);
    *t = savecrap;
    if (!ok) EvalError(EvalErrorNoSuchError);
    argv = (Value *)realloctrace(argv, ++argc * sizeof(Value));
    valuecpy(&argv[argc-1], &retarg);
    *t = savecrap;
    s = t;
    if (*s == ',') s++;
  }
  if (!NoArgs) s++;

  if (ef->zeroplace) {
    if (argc != 0) EvalError(EvalErrorWrongNumArgs);
    if (ES->numpos >= NUMSTACK) EvalError(EvalErrorStackOverflow);
    if (!ES->lastop) EvalError(EvalErrorTwoNumsInARow);
  }
  
  (*ef->f)(ES, argc, argv);

  /* Let's just use the error mechanism we already have set up for getting rid
     of the argv[] array. */
  EvalError(EvalErrorFine);
}


/* char Eval0(char *s, FormclassPtr form)
   takes in a string which is an expression, containing the following
   operators, in this order of precedence:
   ( )
   unary-plus unary-minus
   * / %
   + -
   == !=
   <= >= < >
   &&
   ||
   ?:				... ?: associates from right to left.
                                    all others associate from left to right.
   ,
   and floating-point numbers, and field names.
   Returns the answer if the expression is well-formed, or NULL if it's not. */

char EvalFormclass(char *s, FormclassPtr form, Value *valueret) {
  char c = RealEval(s, form, NULL, valueret);
  if (!c) {
    fprintf(stderr, "Arrgh! %s while evaluating:\n",
	    evalerrorresult == EvalErrorSpecialError ?
	    evalspecialerror :
	    evalerrordescriptions[evalerrorresult]);
    fprintf(stderr, "    %s\n", s);
    fprintf(stderr, "    ");
    while (s != evalerrorparsepos) fprintf(stderr, "-"), s++;
    fprintf(stderr, "^\n");
  }
  return c;
}

char Eval(char *s, ForminstancePtr form, Value *valueret) {
  char c = RealEval(s, form->class, form, valueret);
  if (!c) {
    fprintf(stderr, "Arrgh! %s while evaluating:\n",
	    evalerrorresult == EvalErrorSpecialError ?
	    evalspecialerror :
	    evalerrordescriptions[evalerrorresult]);
    fprintf(stderr, "    %s\n", s);
    fprintf(stderr, "    ");
    while (s != evalerrorparsepos) fprintf(stderr, "-"), s++;
    fprintf(stderr, "^\n");
  }
  return c;
}

static char RealEval(char *s, FormclassPtr form,
		     ForminstancePtr forminst, Value *valueret) {
  
  EvalState es, *ES = &es;
  int eerr;

  ES->oppos = ES->numpos = 0;
  ES->lastop = 1;
  ES->s = s;
  ES->formclass = form;
  ES->forminst = forminst;

  if (eerr = setjmp(ES->evalerrorjmp)) {
    while (ES->numpos) {
      freeinternalvaluestorage(&ES->numstack[ES->numpos-1]);
      ES->numpos--;
    }
    if (eerr != EvalErrorNoSuchError) {
      evalerrorresult = eerr;
      evalerrorparsepos = ES->s;
    }
    return 0;
  }

  /* Here we go! */
  while (1) {
    if (isdigit(*ES->s) || *ES->s == '.') {                     /* numbers */
      if (ES->numpos >= NUMSTACK) EvalError(EvalErrorStack);
      if (!ES->lastop) EvalError(EvalErrorTwoNumsInARow);
                                /* if we've read two numbers in a row,
				   with no intervening operator,
				   that's an error. */
      ES->numstack[ES->numpos].t = ValueDouble;
      ES->numstack[ES->numpos].v.d = (float)strtod(ES->s, &ES->s);
      ES->numpos++;
      ES->lastop = 0;
    } else if (*ES->s == '"') {	                        /* strings */
      char buf[BUFSIZ], *t = buf;
      if (ES->numpos >= NUMSTACK) EvalError(EvalErrorStack);
      if (!ES->lastop) EvalError(EvalErrorTwoNumsInARow);
      ES->s++;
      while (*ES->s != '"' && *ES->s) {
	if (*ES->s == '\\') {
	  ES->s++;
	  if (*ES->s == 'n') *t = '\n';
	  else if (*ES->s == 't') *t = '\t';
	  else *t = *ES->s;
	} else *t = *ES->s;
	t++, ES->s++;
      }
      *t = 0;
      if (!*ES->s) EvalError(EvalErrorSyntaxError);
                                    /* a string must be terminated with a " */
      ES->numstack[ES->numpos].t = ValueString;
      ES->numstack[ES->numpos].v.s = (char *)malloctrace((t - buf) + 1);
      strcpy(ES->numstack[ES->numpos].v.s, buf);
      ES->numpos++;
      ES->lastop = 0;
      ES->s++;
    } else if (isspace(*ES->s)) ES->s++;                    /* skip spaces */
    else if (*ES->s == '@') EvalDoFunction(ES);
    else if (*ES->s == '(') {
      /* A left paren is a special operator in that it should not cause
       * any evaluation to occur, yet it has a very low precedence. Thus we
       * deal with it separately. */
      if (ES->oppos >= OPSTACK) EvalError(EvalErrorStack);
      ES->opstack[ES->oppos] = '(';
      ES->precedence[ES->oppos++] = 101;
      ES->s++;
      ES->lastop = 1;
    } else if (isalpha(*ES->s) || *ES->s == '_') {       /* field names */
      char *q = ES->s, crap;
      FieldPtr field;
      if (ES->numpos >= NUMSTACK) EvalError(EvalErrorStack);
      if (!ES->lastop) EvalError(EvalErrorTwoNumsInARow);
      while (isalnum(*ES->s) || *ES->s == '_') ES->s++;
      crap = *ES->s;
      *ES->s = 0;
      field = FindFieldByName(q, form);
      if (!field) {
	/* If it's not a field, we assume it's the name of a zero-place
	   function. */
	EvalFunction *f = evalfunctions;
	while (f->f && (!f->zeroplace || strcmp(f->name, q))) f++;
	*ES->s = crap;
	if (!f->f) EvalError(EvalErrorNoSuchField);
	else (*f->f)(ES, 0, NULL);
      } else {
	*ES->s = crap;
	if (!Fieldval(field, &ES->numstack[ES->numpos]))
	  EvalError(EvalErrorNoFieldValue);
	ES->numpos++;
	ES->lastop = 0;
      }
    } else {		                          	/* operators */
      char op;
      int prec;

      /* get the precedence of the operator in question... */
      op = getprecedence(ES, &prec);
      
      /* Here comes the important part: the evaluation loop.
       * We loop, at each step comparing the precedence of the operator we
       * just parsed to the precedence of the operator on the top of the stack.
       * If the NEW operator has LOWER precedence, we evaluate the OLD
       * operator, taking numbers off the top of the number stack and
       * sticking the result back on. (As, once you've parsed, say,
       * "2*6+" you know that this will be completely equivalent to "12+";
       * nothing that comes after the + can affect the 2*6, as + has lower
       * precedence than *.) If the two operators have the
       * same precedence, but associate from left to right, we also evaluate
       * the old operator. Once we finish with that operator, we loop again,
       * going down the stack until we hit an operator with lower precedence
       * than the one we just parsed, or the bottom of the stack. */
      
      while (ES->oppos &&
	     (PRECEDENCE(prec) > PRECEDENCE(ES->precedence[ES->oppos-1]) ||
	      PRECEDENCE(prec) == PRECEDENCE(ES->precedence[ES->oppos-1]) &&
	      !ASSOCRIGHT(prec))) {
	Value *num = &ES->numstack[ES->numpos-2];
	Bool worked;
	if (ES->numpos - 2 - (ES->opstack[ES->oppos-1] == ':') +
	    (ES->opstack[ES->oppos-1] == 'M' ||
	     ES->opstack[ES->oppos-1] == 'N' ||
	     ES->opstack[ES->oppos-1] == 'P') < 0)
	  EvalError(EvalErrorSyntaxError);
	                        /* If we don't have enough operands,
				   that's an error. : (as in ?:)
				   requires 3 operands, where M and P
				   (the representations for unary minus
				   and plus) require only 1. */
	ES->numpos--;
	switch (ES->opstack[ES->oppos-1]) {
	case 'M':
	  if (!valconvtoDouble(&num[1])) EvalError(EvalErrorType);
	  num[1].v.d = -num[1].v.d;
	case 'P':
	  if (!valconvtoDouble(&num[1])) EvalError(EvalErrorType);
	  ES->numpos++;
	  break;
	case 'N':
	  if (!valconvtoBool(&num[1])) EvalError(EvalErrorType);
	  num[1].v.b = !num[1].v.b;
	  ES->numpos++;
	  break;
	case '+':
	  if (num[0].t == ValueString && num[1].t == ValueString) {
	    char *xx = (char *)malloctrace(strlen(num[0].v.s) +
					   strlen(num[1].v.s) + 1);
	    sprintf(xx, "%s%s", num[0].v.s, num[1].v.s);
	    freeinternalvaluestorage(&num[0]);
	    freeinternalvaluestorage(&num[1]);
	    num[0].t = ValueString;
	    num[0].v.s = xx;
	  } else {
	    if (!valconvtoDouble(&num[0])) EvalError(EvalErrorType);
	    if (!valconvtoDouble(&num[1])) EvalError(EvalErrorType);
	    num[0].v.d += num[1].v.d;
	  }
	  break;
	case '-':
	  if (!valconvtoDouble(&num[0])) EvalError(EvalErrorType);
	  if (!valconvtoDouble(&num[1])) EvalError(EvalErrorType);
	  num[0].v.d -= num[1].v.d;
	  break;
	case '*':
	  if (!valconvtoDouble(&num[0])) EvalError(EvalErrorType);
	  if (!valconvtoDouble(&num[1])) EvalError(EvalErrorType);
	  num[0].v.d *= num[1].v.d;
	  break;
	case '/':
	  if (!valconvtoDouble(&num[0])) EvalError(EvalErrorType);
	  if (!valconvtoDouble(&num[1])) EvalError(EvalErrorType);
	  num[0].v.d /= num[1].v.d;
	  break;
	case '%':
	  if (!valconvtoInt(&num[0])) EvalError(EvalErrorType);
	  if (!valconvtoInt(&num[1])) EvalError(EvalErrorType);
	  num[0].v.i %= num[1].v.i;
	  break;
	case '=':
	  if (num[0].t == ValueString && num[1].t == ValueString) {
	    worked = !strcmp(num[0].v.s, num[1].v.s);
	    freeinternalvaluestorage(&num[0]);
	    freeinternalvaluestorage(&num[1]);
	  } else {
	    if (!valconvtoDouble(&num[0])) EvalError(EvalErrorType);
	    if (!valconvtoDouble(&num[1])) EvalError(EvalErrorType);
	    worked = num[0].v.d == num[1].v.d;
	  }
	  num[0].t = ValueBool;
	  num[0].v.b = worked;
	  break;
	case '!':
	  if (num[0].t == ValueString && num[1].t == ValueString) {
	    worked = strcmp(num[0].v.s, num[1].v.s);
	    freetrace(num[0].v.s);
	    freetrace(num[1].v.s);
	  } else {
	    if (!valconvtoDouble(&num[0])) EvalError(EvalErrorType);
	    if (!valconvtoDouble(&num[1])) EvalError(EvalErrorType);
	    worked = num[0].v.d != num[1].v.d;
	  }
	  num[0].t = ValueBool;
	  num[0].v.b = worked;
	  break;
	case '<':
	  if (!valconvtoDouble(&num[0])) EvalError(EvalErrorType);
	  if (!valconvtoDouble(&num[1])) EvalError(EvalErrorType);
	  num[0].v.b = num[0].v.d < num[1].v.d;
	  num[0].t = ValueBool;
	  break;
	case '>':
	  if (!valconvtoDouble(&num[0])) EvalError(EvalErrorType);
	  if (!valconvtoDouble(&num[1])) EvalError(EvalErrorType);
	  num[0].v.b = num[0].v.d > num[1].v.d;
	  num[0].t = ValueBool;
	  break;
	case 'L':
	  if (!valconvtoDouble(&num[0])) EvalError(EvalErrorType);
	  if (!valconvtoDouble(&num[1])) EvalError(EvalErrorType);
	  num[0].v.b = num[0].v.d <= num[1].v.d;
	  num[0].t = ValueBool;
	  break;
	case 'G':
	  if (!valconvtoDouble(&num[0])) EvalError(EvalErrorType);
	  if (!valconvtoDouble(&num[1])) EvalError(EvalErrorType);
	  num[0].v.b = num[0].v.d >= num[1].v.d;
	  num[0].t = ValueBool;
	  break;
	case '&':
	  if (!valconvtoBool(&num[0])) EvalError(EvalErrorType);
	  if (!valconvtoBool(&num[1])) EvalError(EvalErrorType);
	  num[0].v.b = num[0].v.b && num[1].v.b;
	  break;
	case '|':
	  if (!valconvtoBool(&num[0])) EvalError(EvalErrorType);
	  if (!valconvtoBool(&num[1])) EvalError(EvalErrorType);
	  num[0].v.b = num[0].v.b || num[1].v.b;
	  break;
	case ':':
	  /* The placement of the : on the stack should have caused everything
	     down to its matching ? to be evaluated. Thus, the ? should be
	     right underneath the :. If it's not, there's been an error. */
	  if (ES->opstack[ES->oppos-2] != '?') EvalError(EvalErrorSyntaxError);
	  num--, ES->numpos--, ES->oppos--;
	  if (!valconvtoBool(&num[0])) EvalError(EvalErrorType);
	  if (!num[0].v.b) {
	    valuecpy(&num[0], &num[2]);
	    freeinternalvaluestorage(&num[1]);
	  } else {
	    valuecpy(&num[0], &num[1]);
	    freeinternalvaluestorage(&num[2]);
	  }
	  break;
	case '?':
	  /* We should NEVER explicitly try and evaluate the ?.
	     If we do, it's a ? without a matching :. */
	  EvalError(EvalErrorSyntaxError);
	case ',':
	  if (!valconvtoIntList(&num[0])) EvalError(EvalErrorType);
	  if (!valconvtoInt(&num[1])) EvalError(EvalErrorType);
	  intlistConcat(&num[0], num[1].v.i);
	  break;
	}
	ES->oppos--;
      } /* end evaluation loop */
      
      if (op == ')') {
	if (!ES->oppos || ES->opstack[ES->oppos-1] != '(')
	  EvalError(EvalErrorParentheses);
	/* A right paren without a matching left paren is broken. */
	ES->oppos--;
	ES->lastop = 0;
	/* We don't want to stick a right parenthesis on the op stack. And a
	   parenthesis doesn't really count as an operator; in (1)+0, the +
	   should be BINARY not unary. */
      } else if (op == 0) break;
      else {
	if (ES->oppos >= OPSTACK) EvalError(EvalErrorStack);
	ES->opstack[ES->oppos] = op;
	ES->precedence[ES->oppos++] = prec;
	ES->lastop = 1;
      }
    } /* end else clause for operators */
  } /* end while (1) */
  
  if (ES->numpos != 1) EvalError(EvalErrorSyntaxError);
  /* If we've evaluated only operators, or nothing at all, it was broken. */

  if (valueret) valuecpy(valueret, &ES->numstack[0]);
  else freeinternalvaluestorage(&ES->numstack[0]);
  return 1;
}
