/* expr.c - expression evaluator routines */

/* SimpleScalar(TM) Tool Suite
 * Copyright (C) 1994-2003 by Todd M. Austin, Ph.D. and SimpleScalar, LLC.
 * All Rights Reserved. 
 * 
 * THIS IS A LEGAL DOCUMENT, BY USING SIMPLESCALAR,
 * YOU ARE AGREEING TO THESE TERMS AND CONDITIONS.
 * 
 * No portion of this work may be used by any commercial entity, or for any
 * commercial purpose, without the prior, written permission of SimpleScalar,
 * LLC (info@simplescalar.com). Nonprofit and noncommercial use is permitted
 * as described below.
 * 
 * 1. SimpleScalar is provided AS IS, with no warranty of any kind, express
 * or implied. The user of the program accepts full responsibility for the
 * application of the program and the use of any results.
 * 
 * 2. Nonprofit and noncommercial use is encouraged. SimpleScalar may be
 * downloaded, compiled, executed, copied, and modified solely for nonprofit,
 * educational, noncommercial research, and noncommercial scholarship
 * purposes provided that this notice in its entirety accompanies all copies.
 * Copies of the modified software can be delivered to persons who use it
 * solely for nonprofit, educational, noncommercial research, and
 * noncommercial scholarship purposes provided that this notice in its
 * entirety accompanies all copies.
 * 
 * 3. ALL COMMERCIAL USE, AND ALL USE BY FOR PROFIT ENTITIES, IS EXPRESSLY
 * PROHIBITED WITHOUT A LICENSE FROM SIMPLESCALAR, LLC (info@simplescalar.com).
 * 
 * 4. No nonprofit user may place any restrictions on the use of this software,
 * including as modified by the user, by any other authorized user.
 * 
 * 5. Noncommercial and nonprofit users may distribute copies of SimpleScalar
 * in compiled or executable form as set forth in Section 2, provided that
 * either: (A) it is accompanied by the corresponding machine-readable source
 * code, or (B) it is accompanied by a written offer, with no time limit, to
 * give anyone a machine-readable copy of the corresponding source code in
 * return for reimbursement of the cost of distribution. This written offer
 * must permit verbatim duplication by anyone, or (C) it is distributed by
 * someone who received only the executable form, and is accompanied by a
 * copy of the written offer of source code.
 * 
 * 6. SimpleScalar was developed by Todd M. Austin, Ph.D. The tool suite is
 * currently maintained by SimpleScalar LLC (info@simplescalar.com). US Mail:
 * 2395 Timbercrest Court, Ann Arbor, MI 48105.
 * 
 * Copyright (C) 1994-2003 by Todd M. Austin, Ph.D. and SimpleScalar, LLC.
 */


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>

#include "host.h"
#include "misc.h"
#include "eval.h"

#if defined(sparc) && !defined(__svr4__)
#define strtoul strtol
#endif /* sparc */

/* expression evaluation error, this must be a global */
enum eval_err_t eval_error = ERR_NOERR;

/* enum eval_err_t -> error description string map */
char *eval_err_str[ERR_NUM] = {
  /* ERR_NOERR */	"!! no error!!",
  /* ERR_UPAREN */	"unmatched parenthesis",
  /* ERR_NOTERM */	"expression term is missing",
  /* ERR_DIV0 */	"divide by zero",
  /* ERR_BADCONST */	"badly formed constant",
  /* ERR_BADEXPR */	"badly formed expression",
  /* ERR_UNDEFVAR */	"variable is undefined",
  /* ERR_EXTRA */	"extra characters at end of expression"
};

/* *first* token character -> enum eval_token_t map */
static enum eval_token_t tok_map[256];
static int tok_map_initialized = FALSE;

/* builds the first token map */
static void
init_tok_map(void)
{
  int i;

  for (i=0; i<256; i++)
    tok_map[i] = tok_invalid;

  /* identifier characters */
  for (i='a'; i<='z'; i++)
    tok_map[i] = tok_ident;
  for (i='A'; i<='Z'; i++)
    tok_map[i] = tok_ident;
  tok_map[(int)'_'] = tok_ident;
  tok_map[(int)'$'] = tok_ident;

  /* numeric characters */
  for (i='0'; i<='9'; i++)
    tok_map[i] = tok_const;
  tok_map[(int)'.'] = tok_const;

  /* operator characters */
  tok_map[(int)'+'] = tok_plus;
  tok_map[(int)'-'] = tok_minus;
  tok_map[(int)'*'] = tok_mult;
  tok_map[(int)'/'] = tok_div;
  tok_map[(int)'('] = tok_oparen;
  tok_map[(int)')'] = tok_cparen;

  /* whitespace characers */
  tok_map[(int)' '] = tok_whitespace;
  tok_map[(int)'\t'] = tok_whitespace;
}

/* get next token from the expression string */
static enum eval_token_t		/* token parsed */
get_next_token(struct eval_state_t *es)	/* expression evaluator */
{
  int allow_hex;
  enum eval_token_t tok;
  char *ptok_buf, last_char;

  /* initialize the token map, if needed */
  if (!tok_map_initialized)
    {
      init_tok_map();
      tok_map_initialized = TRUE;
    }

  /* use the peek'ed token, if available, tok_buf should still be valid */
  if (es->peek_tok != tok_invalid)
    {
      tok = es->peek_tok;
      es->peek_tok = tok_invalid;
      return tok;
    }

  /* set up the token string space */
  ptok_buf = es->tok_buf;
  *ptok_buf = '\0';

  /* skip whitespace */
  while (*es->p && tok_map[(int)*es->p] == tok_whitespace)
    es->p++;

  /* end of token stream? */
  if (*es->p == '\0')
    return tok_eof;

  *ptok_buf++ = *es->p;
  tok = tok_map[(int)*es->p++];
  switch (tok)
    {
    case tok_ident:
      /* parse off next identifier */
      while (*es->p
	     && (tok_map[(int)*es->p] == tok_ident
		 || tok_map[(int)*es->p] == tok_const))
	{
	  *ptok_buf++ = *es->p++;
	}
      break;
    case tok_const:
      /* parse off next numeric literal */
      last_char = '\0';
      allow_hex = FALSE;
      while (*es->p &&
	     (tok_map[(int)*es->p] == tok_const
	      || (*es->p == '-' && last_char == 'e')
	      || (*es->p == '+' && last_char == 'e')
	      || tolower(*es->p) == 'e'
	      || tolower(*es->p) == 'x'
	      || (tolower(*es->p) == 'a' && allow_hex)
	      || (tolower(*es->p) == 'b' && allow_hex)
	      || (tolower(*es->p) == 'c' && allow_hex)
	      || (tolower(*es->p) == 'd' && allow_hex)
	      || (tolower(*es->p) == 'e' && allow_hex)
	      || (tolower(*es->p) == 'f' && allow_hex)))
	{
	  last_char = tolower(*es->p);
	  if (*es->p == 'x' || *es->p == 'X')
	    allow_hex = TRUE;
	  *ptok_buf++ = *es->p++;
	}
      break;
    case tok_plus:
    case tok_minus:
    case tok_mult:
    case tok_div:
    case tok_oparen:
    case tok_cparen:
      /* just pass on the token */
      break;
    default:
      tok = tok_invalid;
      break;
    }

  /* terminate the token string buffer */
  *ptok_buf = '\0';

  return tok;
}

/* peek ahead at the next token from the expression stream, currently
   only the next token can be peek'ed at */
static enum eval_token_t		 /* next token in expression */
peek_next_token(struct eval_state_t *es) /* expression evalutor */
{
  /* if there is no peek ahead token, get one */
  if (es->peek_tok == tok_invalid)
    {
      es->lastp = es->p;
      es->peek_tok = get_next_token(es);
    }

  /* return peek ahead token */
  return es->peek_tok;
}

/* forward declaration */
static struct eval_value_t expr(struct eval_state_t *es);

/* default expression error value, eval_err is also set */
static struct eval_value_t err_value = { et_int, { 0 } };

/* expression type strings */
char *eval_type_str[et_NUM] = {
  /* et_int */		"int",
  /* et_uint */		"unsigned int",
  /* et_addr */		"md_addr_t",
#ifdef HOST_HAS_QWORD
  /* et_qword */	"qword_t",
  /* et_sqword */	"sqword_t",
#endif /* HOST_HAS_QWORD */
  /* et_float */	"float",
  /* et_double */	"double",
  /* et_symbol */	"symbol"
};

/* determine necessary arithmetic conversion on T1 <op> T2 */
static enum eval_type_t			/* type of expression result */
result_type(enum eval_type_t t1,	/* left operand type */
	    enum eval_type_t t2)	/* right operand type */
{
  /* sanity check, symbols should not show up in arithmetic exprs */
  if (t1 == et_symbol || t2 == et_symbol)
    panic("symbol used in expression");

  /* using C rules, i.e., A6.5 */
  if (t1 == et_double || t2 == et_double)
    return et_double;
  else if (t1 == et_float || t2 == et_float)
    return et_float;
#ifdef HOST_HAS_QWORD
  else if (t1 == et_qword || t2 == et_qword)
    return et_qword;
  else if (t1 == et_sqword || t2 == et_sqword)
    return et_sqword;
#endif /* HOST_HAS_QWORD */
  else if (t1 == et_addr || t2 == et_addr)
    return et_addr;
  else if (t1 == et_uint || t2 == et_uint)
    return et_uint;
  else
    return et_int;
}

/*
 * expression value arithmetic conversions
 */

/* eval_value_t (any numeric type) -> double */
double
eval_as_double(struct eval_value_t val)
{
  switch (val.type)
    {
    case et_double:
      return val.value.as_double;
    case et_float:
      return (double)val.value.as_float;
#ifdef HOST_HAS_QWORD
    case et_qword:
#ifdef _MSC_VER /* FIXME: MSC does not implement qword_t to dbl conversion */
      return (double)(sqword_t)val.value.as_qword;
#else /* !_MSC_VER */
      return (double)val.value.as_qword;
#endif /* _MSC_VER */
    case et_sqword:
      return (double)val.value.as_sqword;
#endif /* HOST_HAS_QWORD */
    case et_addr:
#if defined(_MSC_VER) && defined(TARGET_ALPHA)
      /* FIXME: MSC does not implement qword_t to double conversion */
      return (double)(sqword_t)val.value.as_addr;
#else
      return (double)val.value.as_addr;
#endif
    case et_uint:
      return (double)val.value.as_uint;
    case et_int:
      return (double)val.value.as_int;
    case et_symbol:
      panic("symbol used in expression");
    default:
      panic("illegal arithmetic expression conversion");
    }
}

/* eval_value_t (any numeric type) -> float */
float
eval_as_float(struct eval_value_t val)
{
  switch (val.type)
    {
    case et_double:
      return (float)val.value.as_double;
    case et_float:
      return val.value.as_float;
#ifdef HOST_HAS_QWORD
    case et_qword:
#ifdef _MSC_VER /* FIXME: MSC does not implement qword_t to dbl conversion */
      return (float)(sqword_t)val.value.as_qword;
#else /* !_MSC_VER */
      return (float)val.value.as_qword;
#endif /* _MSC_VER */
    case et_sqword:
      return (float)val.value.as_sqword;
#endif /* HOST_HAS_QWORD */
    case et_addr:
#if defined(_MSC_VER) && defined(TARGET_ALPHA)
      /* FIXME: MSC does not implement qword_t to double conversion */
      return (float)(sqword_t)val.value.as_addr;
#else
      return (float)val.value.as_addr;
#endif
    case et_uint:
      return (float)val.value.as_uint;
    case et_int:
      return (float)val.value.as_int;
    case et_symbol:
      panic("symbol used in expression");
    default:
      panic("illegal arithmetic expression conversion");
    }
}

#ifdef HOST_HAS_QWORD
/* eval_value_t (any numeric type) -> qword_t */
qword_t
eval_as_qword(struct eval_value_t val)
{
  switch (val.type)
    {
    case et_double:
      return (qword_t)val.value.as_double;
    case et_float:
      return (qword_t)val.value.as_float;
    case et_qword:
      return val.value.as_qword;
    case et_sqword:
      return (qword_t)val.value.as_sqword;
    case et_addr:
      return (qword_t)val.value.as_addr;
    case et_uint:
      return (qword_t)val.value.as_uint;
    case et_int:
      return (qword_t)val.value.as_int;
    case et_symbol:
      panic("symbol used in expression");
    default:
      panic("illegal arithmetic expression conversion");
    }
}

/* eval_value_t (any numeric type) -> sqword_t */
sqword_t
eval_as_sqword(struct eval_value_t val)
{
  switch (val.type)
    {
    case et_double:
      return (sqword_t)val.value.as_double;
    case et_float:
      return (sqword_t)val.value.as_float;
    case et_qword:
      return (sqword_t)val.value.as_qword;
    case et_sqword:
      return val.value.as_sqword;
    case et_addr:
      return (sqword_t)val.value.as_addr;
    case et_uint:
      return (sqword_t)val.value.as_uint;
    case et_int:
      return (sqword_t)val.value.as_int;
    case et_symbol:
      panic("symbol used in expression");
    default:
      panic("illegal arithmetic expression conversion");
    }
}
#endif /* HOST_HAS_QWORD */

/* eval_value_t (any numeric type) -> unsigned int */
md_addr_t
eval_as_addr(struct eval_value_t val)
{
  switch (val.type)
    {
    case et_double:
      return (md_addr_t)val.value.as_double;
    case et_float:
      return (md_addr_t)val.value.as_float;
#ifdef HOST_HAS_QWORD
    case et_qword:
      return (md_addr_t)val.value.as_qword;
    case et_sqword:
      return (md_addr_t)val.value.as_sqword;
#endif /* HOST_HAS_QWORD */
    case et_addr:
      return val.value.as_addr;
    case et_uint:
      return (md_addr_t)val.value.as_uint;
    case et_int:
      return (md_addr_t)val.value.as_int;
    case et_symbol:
      panic("symbol used in expression");
    default:
      panic("illegal arithmetic expression conversion");
    }
}

/* eval_value_t (any numeric type) -> unsigned int */
unsigned int
eval_as_uint(struct eval_value_t val)
{
  switch (val.type)
    {
    case et_double:
      return (unsigned int)val.value.as_double;
    case et_float:
      return (unsigned int)val.value.as_float;
#ifdef HOST_HAS_QWORD
    case et_qword:
      return (unsigned int)val.value.as_qword;
    case et_sqword:
      return (unsigned int)val.value.as_sqword;
#endif /* HOST_HAS_QWORD */
    case et_addr:
      return (unsigned int)val.value.as_addr;
    case et_uint:
      return val.value.as_uint;
    case et_int:
      return (unsigned int)val.value.as_int;
    case et_symbol:
      panic("symbol used in expression");
    default:
      panic("illegal arithmetic expression conversion");
    }
}

/* eval_value_t (any numeric type) -> int */
int
eval_as_int(struct eval_value_t val)
{
  switch (val.type)
    {
    case et_double:
      return (int)val.value.as_double;
    case et_float:
      return (int)val.value.as_float;
#ifdef HOST_HAS_QWORD
    case et_qword:
      return (int)val.value.as_qword;
    case et_sqword:
      return (int)val.value.as_sqword;
#endif /* HOST_HAS_QWORD */
    case et_addr:
      return (int)val.value.as_addr;
    case et_uint:
      return (int)val.value.as_uint;
    case et_int:
      return val.value.as_int;
    case et_symbol:
      panic("symbol used in expression");
    default:
      panic("illegal arithmetic expression conversion");
    }
}

/*
 * arithmetic intrinsics operations, used during expression evaluation
 */

/* compute <val1> + <val2> */
static struct eval_value_t
f_add(struct eval_value_t val1, struct eval_value_t val2)
{
  enum eval_type_t et;
  struct eval_value_t val;

  /* symbols are not allowed in arithmetic expressions */
  if (val1.type == et_symbol || val2.type == et_symbol)
    {
      eval_error = ERR_BADEXPR;
      return err_value;
    }

  /* get result type, and perform operation in that type */
  et = result_type(val1.type, val2.type);
  switch (et)
    {
    case et_double:
      val.type = et_double;
      val.value.as_double = eval_as_double(val1) + eval_as_double(val2);
      break;
    case et_float:
      val.type = et_float;
      val.value.as_float = eval_as_float(val1) + eval_as_float(val2);
      break;
#ifdef HOST_HAS_QWORD
    case et_qword:
      val.type = et_qword;
      val.value.as_qword = eval_as_qword(val1) + eval_as_qword(val2);
      break;
    case et_sqword:
      val.type = et_sqword;
      val.value.as_sqword = eval_as_sqword(val1) + eval_as_sqword(val2);
      break;
#endif /* HOST_HAS_QWORD */
    case et_addr:
      val.type = et_addr;
      val.value.as_addr = eval_as_addr(val1) + eval_as_addr(val2);
      break;
    case et_uint:
      val.type = et_uint;
      val.value.as_uint = eval_as_uint(val1) + eval_as_uint(val2);
      break;
    case et_int:
      val.type = et_int;
      val.value.as_int = eval_as_int(val1) + eval_as_int(val2);
      break;
    default:
      panic("bogus expression type");
    }

  return val;
}

/* compute <val1> - <val2> */
static struct eval_value_t
f_sub(struct eval_value_t val1, struct eval_value_t val2)
{
  enum eval_type_t et;
  struct eval_value_t val;

  /* symbols are not allowed in arithmetic expressions */
  if (val1.type == et_symbol || val2.type == et_symbol)
    {
      eval_error = ERR_BADEXPR;
      return err_value;
    }

  /* get result type, and perform operation in that type */
  et = result_type(val1.type, val2.type);
  switch (et)
    {
    case et_double:
      val.type = et_double;
      val.value.as_double = eval_as_double(val1) - eval_as_double(val2);
      break;
    case et_float:
      val.type = et_float;
      val.value.as_float = eval_as_float(val1) - eval_as_float(val2);
      break;
#ifdef HOST_HAS_QWORD
    case et_qword:
      val.type = et_qword;
      val.value.as_qword = eval_as_qword(val1) - eval_as_qword(val2);
      break;
    case et_sqword:
      val.type = et_sqword;
      val.value.as_sqword = eval_as_sqword(val1) - eval_as_sqword(val2);
      break;
#endif /* HOST_HAS_QWORD */
    case et_addr:
      val.type = et_addr;
      val.value.as_addr = eval_as_addr(val1) - eval_as_addr(val2);
      break;
    case et_uint:
      val.type = et_uint;
      val.value.as_uint = eval_as_uint(val1) - eval_as_uint(val2);
      break;
    case et_int:
      val.type = et_int;
      val.value.as_int = eval_as_int(val1) - eval_as_int(val2);
      break;
    default:
      panic("bogus expression type");
    }

  return val;
}

/* compute <val1> * <val2> */
static struct eval_value_t
f_mult(struct eval_value_t val1, struct eval_value_t val2)
{
  enum eval_type_t et;
  struct eval_value_t val;

  /* symbols are not allowed in arithmetic expressions */
  if (val1.type == et_symbol || val2.type == et_symbol)
    {
      eval_error = ERR_BADEXPR;
      return err_value;
    }

  /* get result type, and perform operation in that type */
  et = result_type(val1.type, val2.type);
  switch (et)
    {
    case et_double:
      val.type = et_double;
      val.value.as_double = eval_as_double(val1) * eval_as_double(val2);
      break;
    case et_float:
      val.type = et_float;
      val.value.as_float = eval_as_float(val1) * eval_as_float(val2);
      break;
#ifdef HOST_HAS_QWORD
    case et_qword:
      val.type = et_qword;
      val.value.as_qword = eval_as_qword(val1) * eval_as_qword(val2);
      break;
    case et_sqword:
      val.type = et_sqword;
      val.value.as_sqword = eval_as_sqword(val1) * eval_as_sqword(val2);
      break;
#endif /* HOST_HAS_QWORD */
    case et_addr:
      val.type = et_addr;
      val.value.as_addr = eval_as_addr(val1) * eval_as_addr(val2);
      break;
    case et_uint:
      val.type = et_uint;
      val.value.as_uint = eval_as_uint(val1) * eval_as_uint(val2);
      break;
    case et_int:
      val.type = et_int;
      val.value.as_int = eval_as_int(val1) * eval_as_int(val2);
      break;
    default:
      panic("bogus expression type");
    }

  return val;
}

/* compute <val1> / <val2> */
static struct eval_value_t
f_div(struct eval_value_t val1, struct eval_value_t val2)
{
  enum eval_type_t et;
  struct eval_value_t val;

  /* symbols are not allowed in arithmetic expressions */
  if (val1.type == et_symbol || val2.type == et_symbol)
    {
      eval_error = ERR_BADEXPR;
      return err_value;
    }

  /* get result type, and perform operation in that type */
  et = result_type(val1.type, val2.type);
  switch (et)
    {
    case et_double:
      val.type = et_double;
      val.value.as_double = eval_as_double(val1) / eval_as_double(val2);
      break;
    case et_float:
      val.type = et_float;
      val.value.as_float = eval_as_float(val1) / eval_as_float(val2);
      break;
#ifdef HOST_HAS_QWORD
    case et_qword:
      val.type = et_qword;
      val.value.as_qword = eval_as_qword(val1) / eval_as_qword(val2);
      break;
    case et_sqword:
      val.type = et_sqword;
      val.value.as_sqword = eval_as_sqword(val1) / eval_as_sqword(val2);
      break;
#endif /* HOST_HAS_QWORD */
    case et_addr:
      val.type = et_addr;
      val.value.as_addr = eval_as_addr(val1) / eval_as_addr(val2);
      break;
    case et_uint:
      val.type = et_uint;
      val.value.as_uint = eval_as_uint(val1) / eval_as_uint(val2);
      break;
    case et_int:
      val.type = et_int;
      val.value.as_int = eval_as_int(val1) / eval_as_int(val2);
      break;
    default:
      panic("bogus expression type");
    }

  return val;
}

/* compute - <val1> */
static struct eval_value_t
f_neg(struct eval_value_t val1)
{
  struct eval_value_t val;

  /* symbols are not allowed in arithmetic expressions */
  if (val1.type == et_symbol)
    {
      eval_error = ERR_BADEXPR;
      return err_value;
    }

  /* result type is the same as the operand type */
  switch (val1.type)
    {
    case et_double:
      val.type = et_double;
      val.value.as_double = - val1.value.as_double;
      break;
    case et_float:
      val.type = et_float;
      val.value.as_float = - val1.value.as_float;
      break;
#ifdef HOST_HAS_QWORD
    case et_qword:
      val.type = et_sqword;
      val.value.as_qword = - (sqword_t)val1.value.as_qword;
      break;
    case et_sqword:
      val.type = et_sqword;
      val.value.as_sqword = - val1.value.as_sqword;
      break;
#endif /* HOST_HAS_QWORD */
    case et_addr:
      val.type = et_addr;
      val.value.as_addr = - val1.value.as_addr;
      break;
    case et_uint:
      if ((unsigned int)val1.value.as_uint > 2147483648U)
	{
	  /* promote type */
#ifdef HOST_HAS_QWORD
	  val.type = et_sqword;
	  val.value.as_sqword = - ((sqword_t)val1.value.as_uint);
#else /* !HOST_HAS_QWORD */
	  val.type = et_double;
	  val.value.as_double = - ((double)val1.value.as_uint);
#endif /* HOST_HAS_QWORD */
	}
      else
	{
	  /* don't promote type */
	  val.type = et_int;
	  val.value.as_int = - ((int)val1.value.as_uint);
	}
      break;
    case et_int:
      if ((unsigned int)val1.value.as_int == 0x80000000U)
	{
	  /* promote type */
	  val.type = et_uint;
	  val.value.as_uint = 2147483648U;
	}
      else
	{
	  /* don't promote type */
	  val.type = et_int;
	  val.value.as_int = - val1.value.as_int;
	}
      break;
    default:
      panic("bogus expression type");
    }

  return val;
}

/* compute val1 == 0 */
static int
f_eq_zero(struct eval_value_t val1)
{
  int val;

  /* symbols are not allowed in arithmetic expressions */
  if (val1.type == et_symbol)
    {
      eval_error = ERR_BADEXPR;
      return FALSE;
    }

  switch (val1.type)
    {
    case et_double:
      val = val1.value.as_double == 0.0;
      break;
    case et_float:
      val = val1.value.as_float == 0.0;
      break;
#ifdef HOST_HAS_QWORD
    case et_qword:
      val = val1.value.as_qword == 0;
      break;
    case et_sqword:
      val = val1.value.as_sqword == 0;
      break;
#endif /* HOST_HAS_QWORD */
    case et_addr:
      val = val1.value.as_addr == 0;
      break;
    case et_uint:
      val = val1.value.as_uint == 0;
      break;
    case et_int:
      val = val1.value.as_int == 0;
      break;
    default:
      panic("bogus expression type");
    }

  return val;
}

/* evaluate the value of the numeric literal constant in ES->TOK_BUF,
   eval_err is set to a value other than ERR_NOERR if the constant cannot
   be parsed and converted to an expression value */
static struct eval_value_t		/* value of the literal constant */
constant(struct eval_state_t *es)	/* expression evaluator */
{
  struct eval_value_t val;
  int int_val;
  unsigned int uint_val;
  double double_val;
  char *endp;
#ifdef HOST_HAS_QWORD
  sqword_t sqword_val;
  qword_t qword_val;
#endif /* HOST_HAS_QWORD */

#if 0 /* no longer needed... */
#if defined(sparc) && !defined(__svr4__)
  extern long strtol(char *, char **, int);
  extern double strtod(char *, char **);
#endif /* sparc */
#endif

  /*
   * attempt multiple conversions, from least to most precise, using
   * the value returned when the conversion is successful
   */

  /* attempt integer conversion */
  errno = 0;
  int_val = strtol(es->tok_buf, &endp, /* parse base */0);
  if (!errno && !*endp)
    {
      /* good conversion */
      val.type = et_int;
      val.value.as_int = int_val;
      return val;
    }

  /* else, not an integer, attempt unsigned int conversion */
  errno = 0;
  uint_val = strtoul(es->tok_buf, &endp, /* parse base */0);
  if (!errno && !*endp)
    {
      /* good conversion */
      val.type = et_uint;
      val.value.as_uint = uint_val;
      return val;
    }

#ifdef HOST_HAS_QWORD
  /* else, not an int/uint, attempt sqword_t conversion */
  errno = 0;
  sqword_val = myatosq(es->tok_buf, &endp, /* parse base */0);
  if (!errno && !*endp)
    {
      /* good conversion */
      val.type = et_sqword;
      val.value.as_sqword = sqword_val;
      return val;
    }

  /* else, not an sqword_t, attempt qword_t conversion */
  errno = 0;
  qword_val = myatoq(es->tok_buf, &endp, /* parse base */0);
  if (!errno && !*endp)
    {
      /* good conversion */
      val.type = et_qword;
      val.value.as_qword = qword_val;
      return val;
    }
#endif /* HOST_HAS_QWORD */

  /* else, not any type of integer, attempt double conversion (NOTE: no
     reliable float conversion is available on all machines) */
  errno = 0;
  double_val = strtod(es->tok_buf, &endp);
  if (!errno && !*endp)
    {
      /* good conversion */
      val.type = et_double;
      val.value.as_double = double_val;
      return val;
    }

  /* else, not a double value, therefore, could not convert constant,
     declare an error */
  eval_error = ERR_BADCONST;
  return err_value;
}

/* evaluate an expression factor, eval_err will indicate it any
   expression evaluation occurs */
static struct eval_value_t		/* value of factor */
factor(struct eval_state_t *es)		/* expression evaluator */
{
  enum eval_token_t tok;
  struct eval_value_t val;

  tok = peek_next_token(es);
  switch (tok)
    {
    case tok_oparen:
      (void)get_next_token(es);
      val = expr(es);
      if (eval_error)
	return err_value;

      tok = peek_next_token(es);
      if (tok != tok_cparen)
	{
	  eval_error = ERR_UPAREN;
	  return err_value;
	}
      (void)get_next_token(es);
      break;

    case tok_minus:
      /* negation operator */
      (void)get_next_token(es);
      val = factor(es);
      if (eval_error)
	return err_value;
      val = f_neg(val);
      break;

    case tok_ident:
      (void)get_next_token(es);
      /* evaluate the identifier in TOK_BUF */
      val = es->f_eval_ident(es);
      if (eval_error)
	return err_value;
      break;

    case tok_const:
      (void)get_next_token(es);
      val = constant(es);
      if (eval_error)
	return err_value;
      break;

    default:
      eval_error = ERR_NOTERM;
      return err_value;
    }

  return val;
}

/* evaluate an expression term, eval_err will indicate it any
   expression evaluation occurs */
static struct eval_value_t		/* value to expression term */
term(struct eval_state_t *es)		/* expression evaluator */
{
  enum eval_token_t tok;
  struct eval_value_t val, val1;

  val = factor(es);
  if (eval_error)
    return err_value;

  tok = peek_next_token(es);
  switch (tok)
    {
    case tok_mult:
      (void)get_next_token(es);
      val = f_mult(val, term(es));
      if (eval_error)
	return err_value;
      break;

    case tok_div:
      (void)get_next_token(es);
      val1 = term(es);
      if (eval_error)
	return err_value;
      if (f_eq_zero(val1))
	{
	  eval_error = ERR_DIV0;
	  return err_value;
	}
      val = f_div(val, val1);
      break;

    default:;
    }

  return val;
}

/* evaluate an expression, eval_err will indicate it any expression
   evaluation occurs */
static struct eval_value_t		/* value of the expression */
expr(struct eval_state_t *es)		/* expression evaluator */
{
  enum eval_token_t tok;
  struct eval_value_t val;

  val = term(es);
  if (eval_error)
    return err_value;

  tok = peek_next_token(es);
  switch (tok)
    {
    case tok_plus:
      (void)get_next_token(es);
      val = f_add(val, expr(es));
      if (eval_error)
	return err_value;
      break;

    case tok_minus:
      (void)get_next_token(es);
      val = f_sub(val, expr(es));
      if (eval_error)
	return err_value;
      break;

    default:;
    }

  return val;
}

/* create an evaluator */
struct eval_state_t *			/* expression evaluator */
eval_new(eval_ident_t f_eval_ident,	/* user ident evaluator */
	 void *user_ptr)		/* user ptr passed to ident fn */
{
  struct eval_state_t *es;

  es = calloc(1, sizeof(struct eval_state_t));
  if (!es)
    fatal("out of virtual memory");

  es->f_eval_ident = f_eval_ident;
  es->user_ptr = user_ptr;

  return es;
}

/* delete an evaluator */
void
eval_delete(struct eval_state_t *es)	/* evaluator to delete */
{
  free(es);
}

/* evaluate an expression, if an error occurs during evaluation, the
   global variable eval_error will be set to a value other than ERR_NOERR */
struct eval_value_t			/* value of the expression */
eval_expr(struct eval_state_t *es,	/* expression evaluator */
	  char *p,			/* ptr to expression string */
	  char **endp)			/* returns ptr to 1st unused char */
{
  struct eval_value_t val;

  /* initialize the evaluator state */
  eval_error = ERR_NOERR;
  es->p = p;
  *es->tok_buf = '\0';
  es->peek_tok = tok_invalid;

  /* evaluate the expression */
  val = expr(es);

  /* return a pointer to the first character not used in the expression */
  if (endp)
    {
      if (es->peek_tok != tok_invalid)
	{
	  /* did not consume peek'ed token, so return last p */
	  *endp = es->lastp;
	}
      else
	*endp = es->p;
    }

  return val;
}

/* print an expression value */
void
eval_print(FILE *stream,		/* output stream */
	   struct eval_value_t val)	/* expression value to print */
{
  switch (val.type)
    {
    case et_double:
      fprintf(stream, "%f [double]", val.value.as_double);
      break;
    case et_float:
      fprintf(stream, "%f [float]", (double)val.value.as_float);
      break;
#ifdef HOST_HAS_QWORD
    case et_qword:
      myfprintf(stream, "%lu [qword_t]", val.value.as_qword);
      break;
    case et_sqword:
      myfprintf(stream, "%ld [sqword_t]", val.value.as_sqword);
      break;
#endif /* HOST_HAS_QWORD */
    case et_addr:
      myfprintf(stream, "0x%p [md_addr_t]", val.value.as_addr);
      break;
    case et_uint:
      fprintf(stream, "%u [uint]", val.value.as_uint);
      break;
    case et_int:
      fprintf(stream, "%d [int]", val.value.as_int);
      break;
    case et_symbol:
      fprintf(stream, "\"%s\" [symbol]", val.value.as_symbol);
      break;
    default:
      panic("bogus expression type");
    }
}


#ifdef TEST

static struct eval_value_t an_int;
static struct eval_value_t a_uint;
static struct eval_value_t a_float;
static struct eval_value_t a_double;
static struct eval_value_t a_symbol;

struct sym_map_t {
  char *symbol;
  struct eval_value_t *value;
};

static struct sym_map_t sym_map[] = {
  { "an_int", &an_int },
  { "a_uint", &a_uint },
  { "a_float", &a_float },
  { "a_double", &a_double },
  { "a_symbol", &a_symbol },
  { NULL, NULL },
};

struct eval_value_t
my_eval_ident(struct eval_state_t *es)
{
  struct sym_map_t *sym;

  for (sym=sym_map; sym->symbol != NULL; sym++)
    {
      if (!strcmp(sym->symbol, es->tok_buf))
	return *sym->value;
    }

  eval_error = ERR_UNDEFVAR;
  return err_value;
}

void
main(void)
{
  struct eval_state_t *es;

  /* set up test variables */
  an_int.type = et_int; an_int.value.as_int = 1;
  a_uint.type = et_uint; a_uint.value.as_uint = 2;
  a_float.type = et_float; a_float.value.as_float = 3.0f;
  a_double.type = et_double; a_double.value.as_double = 4.0;
  a_symbol.type = et_symbol; a_symbol.value.as_symbol = "testing 1 2 3...";

  /* instantiate an evaluator */
  es = eval_new(my_eval_ident, NULL);

  while (1)
    {
      struct eval_value_t val;
      char expr_buf[1024];

      fgets(expr_buf, 1024, stdin);

      /* chop */
      if (expr_buf[strlen(expr_buf)-1] == '\n')
	expr_buf[strlen(expr_buf)-1] = '\0';

      if (expr_buf[0] == '\0')
	exit(0);

      val = eval_expr(es, expr_buf, NULL);
      if (eval_error)
	fprintf(stdout, "eval error: %s\n", eval_err_str[eval_error]);
      else
	{
	  fprintf(stdout, "%s == ", expr_buf);
	  eval_print(stdout, val);
	  fprintf(stdout, "\n");
	}
    }
}

#endif /* TEST */
