/*
 * File expr.c - expression handling for Wine internal debugger.
 *
 * Copyright (C) 1997, Eric Youngdale.
 *
 */

#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "winbase.h"
#include "wine/winbase16.h"
#include "debugger.h"
#include "expr.h"

#include <stdarg.h>

struct expr
{
  unsigned int	perm;
  unsigned int	type:31;
  union
  {
    struct
    {
      int value;
    } constant;

    struct
    {
      const char * str;
    } string;

    struct
    {
      unsigned int value;
    } u_const;

    struct
    {
      const char * name;
    } symbol;

    struct
    {
      const char * name;
    } intvar;

    struct
    {
      int unop_type;
      struct expr * exp1;
      int	      result;
    } unop;

    struct
    {
      int binop_type;
      int result;
      struct expr * exp1;
      struct expr * exp2;
    } binop;

    struct
    {
      struct datatype * cast;
      struct expr     * expr;
    } cast;

    struct
    {
      struct expr * exp1;
      const char * element_name;
      int result;
    } structure;

    struct
    {
      struct expr * base;
      struct expr * index;
    } array;

    struct
    {
      const char  * funcname;
      int	    nargs;
      int	    result;
      struct expr * arg[5];
    } call;

  } un;
};

#define EXPR_TYPE_CONST		0
#define EXPR_TYPE_US_CONST	1
#define EXPR_TYPE_SYMBOL	2
#define EXPR_TYPE_INTVAR	3
#define EXPR_TYPE_BINOP		4
#define EXPR_TYPE_UNOP		5
#define EXPR_TYPE_STRUCT	6
#define EXPR_TYPE_PSTRUCT	7
#define EXPR_TYPE_ARRAY		8
#define EXPR_TYPE_CALL		9
#define EXPR_TYPE_STRING	10
#define EXPR_TYPE_CAST		11

static char expr_list[4096];
static unsigned int next_expr_free = 0;

/*
 * This is how we turn an expression address into the actual value.
 * This works well in the 32 bit domain - not sure at all about the
 * 16 bit world.
 */
#define VAL(_exp)	DEBUG_GetExprValue(&_exp, NULL)

static
struct expr *
DEBUG_GetFreeExpr(void)
{
  struct expr * rtn;

  rtn =  (struct expr *) &expr_list[next_expr_free];

  next_expr_free += sizeof(struct expr);
  assert(next_expr_free < sizeof(expr_list));

  return rtn;
}

void
DEBUG_FreeExprMem(void)
{
  next_expr_free = 0;
}

struct expr *
DEBUG_TypeCastExpr(struct datatype * dt, struct expr * exp)
{
  struct expr * ex;

  ex = DEBUG_GetFreeExpr();

  ex->type        = EXPR_TYPE_CAST;
  ex->un.cast.cast = dt;
  ex->un.cast.expr = exp;
  return ex;
}

struct expr *
DEBUG_IntVarExpr(const char* name)
{
  struct expr * ex;

  ex = DEBUG_GetFreeExpr();

  ex->type        = EXPR_TYPE_INTVAR;
  ex->un.intvar.name = name;
  return ex;
}

struct expr *
DEBUG_SymbolExpr(const char * name)
{
  struct expr * ex;

  ex = DEBUG_GetFreeExpr();

  ex->type        = EXPR_TYPE_SYMBOL;
  ex->un.symbol.name = name;
  return ex;
}

struct expr *
DEBUG_ConstExpr(int value)
{
  struct expr * ex;

  ex = DEBUG_GetFreeExpr();

  ex->type        = EXPR_TYPE_CONST;
  ex->un.constant.value = value;
  return ex;
}

struct expr *
DEBUG_StringExpr(const char * str)
{
  struct expr * ex;
  char * pnt;
  ex = DEBUG_GetFreeExpr();

  ex->type        = EXPR_TYPE_STRING;
  ex->un.string.str = str+1;
  pnt = strrchr(ex->un.string.str, '"');
  if( pnt != NULL )
    {
      *pnt =  '\0';
    }
  return ex;
}

struct expr *
DEBUG_USConstExpr(unsigned int value)
{
  struct expr * ex;

  ex = DEBUG_GetFreeExpr();

  ex->type           = EXPR_TYPE_CONST;
  ex->un.u_const.value = value;
  return ex;
}

struct expr *
DEBUG_BinopExpr(int operator_type, struct expr * exp1, struct expr * exp2)
{
  struct expr * ex;

  ex = DEBUG_GetFreeExpr();

  ex->type           = EXPR_TYPE_BINOP;
  ex->un.binop.binop_type = operator_type;
  ex->un.binop.exp1     = exp1;
  ex->un.binop.exp2     = exp2;
  return ex;
}

struct expr *
DEBUG_UnopExpr(int operator_type, struct expr * exp1)
{
  struct expr * ex;

  ex = DEBUG_GetFreeExpr();

  ex->type           = EXPR_TYPE_UNOP;
  ex->un.unop.unop_type = operator_type;
  ex->un.unop.exp1      = exp1;
  return ex;
}

struct expr *
DEBUG_StructExpr(struct expr * exp, const char * element)
{
  struct expr * ex;

  ex = DEBUG_GetFreeExpr();

  ex->type           = EXPR_TYPE_STRUCT;
  ex->un.structure.exp1 = exp;
  ex->un.structure.element_name = element;
  return ex;
}

struct expr *
DEBUG_StructPExpr(struct expr * exp, const char * element)
{
  struct expr * ex;

  ex = DEBUG_GetFreeExpr();

  ex->type           = EXPR_TYPE_PSTRUCT;
  ex->un.structure.exp1 = exp;
  ex->un.structure.element_name = element;
  return ex;
}

struct expr *
DEBUG_CallExpr(const char * funcname, int nargs, ...)
{
  struct expr * ex;
  va_list ap;
  int i;

  ex = DEBUG_GetFreeExpr();

  ex->type           = EXPR_TYPE_CALL;
  ex->un.call.funcname = funcname;
  ex->un.call.nargs = nargs;

  va_start(ap, nargs);
  for(i=0; i < nargs; i++)
    {
      ex->un.call.arg[i] = va_arg(ap, struct expr *);
    }
  va_end(ap);
  return ex;
}

DBG_VALUE DEBUG_EvalExpr(struct expr * exp)
{
  DBG_VALUE	rtn;
  int		i;
  DBG_VALUE	exp1;
  DBG_VALUE	exp2;
  unsigned int	cexp[5];
  int		    scale1;
  int		    scale2;
  int		    scale3;
  struct datatype * type1;
  struct datatype * type2;

  rtn.type = NULL;
  rtn.cookie = DV_INVALID;
  rtn.addr.off = 0;
  rtn.addr.seg = 0;

  switch(exp->type)
    {
    case EXPR_TYPE_CAST:
      if (!exp->un.cast.cast)
      {
	  DEBUG_Printf(DBG_CHN_MESG, "Can't cast to unknown type\n");
	  RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL);
      }
      rtn = DEBUG_EvalExpr(exp->un.cast.expr);
      rtn.type = exp->un.cast.cast;
      break;
    case EXPR_TYPE_STRING:
      rtn.type = DEBUG_GetBasicType(DT_BASIC_STRING);
      rtn.cookie = DV_HOST;
      rtn.addr.off = (unsigned int) &exp->un.string.str;
      rtn.addr.seg = 0;
      break;
    case EXPR_TYPE_CONST:
      rtn.type = DEBUG_GetBasicType(DT_BASIC_CONST_INT);
      rtn.cookie = DV_HOST;
      rtn.addr.off = (unsigned int) &exp->un.constant.value;
      rtn.addr.seg = 0;
      break;
    case EXPR_TYPE_US_CONST:
      rtn.type = DEBUG_GetBasicType(DT_BASIC_USHORTINT);
      rtn.cookie = DV_HOST;
      rtn.addr.off = (unsigned int) &exp->un.u_const.value;
      rtn.addr.seg = 0;
      break;
    case EXPR_TYPE_SYMBOL:
      switch (DEBUG_GetSymbolValue(exp->un.symbol.name, -1, &rtn, FALSE))
      {
      case gsv_found:
          break;
      case gsv_unknown:
	  RaiseException(DEBUG_STATUS_NO_SYMBOL, 0, 0, NULL);
          /* should never be here */
      case gsv_aborted:
          RaiseException(DEBUG_STATUS_ABORT, 0, 0, NULL);
          /* should never be here */
      }
      break;
    case EXPR_TYPE_PSTRUCT:
      exp1 =  DEBUG_EvalExpr(exp->un.structure.exp1);
      if( exp1.type == NULL )
	{
	   RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL);
	}
      rtn.cookie = DV_TARGET;
      rtn.addr.off = DEBUG_TypeDerefPointer(&exp1, &rtn.type);
      if( rtn.type == NULL )
	{
	  RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL);
	}
      if (!DEBUG_FindStructElement(&rtn, exp->un.structure.element_name,
				   &exp->un.structure.result))
      {
	  DEBUG_Printf(DBG_CHN_MESG, "%s\n", exp->un.structure.element_name);
	  RaiseException(DEBUG_STATUS_NO_FIELD, 0, 0, NULL);
      }

      break;
    case EXPR_TYPE_STRUCT:
      exp1 =  DEBUG_EvalExpr(exp->un.structure.exp1);
      if( exp1.type == NULL )
      {
	  RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL);
      }
      rtn = exp1;
      if (!DEBUG_FindStructElement(&rtn, exp->un.structure.element_name,
				   &exp->un.structure.result))
      {
	  DEBUG_Printf(DBG_CHN_MESG, "%s\n", exp->un.structure.element_name);
	  RaiseException(DEBUG_STATUS_NO_FIELD, 0, 0, NULL);
      }
      break;
    case EXPR_TYPE_CALL:
      /*
       * First, evaluate all of the arguments.  If any of them are not
       * evaluable, then bail.
       */
      for(i=0; i < exp->un.call.nargs; i++)
	{
	  exp1  = DEBUG_EvalExpr(exp->un.call.arg[i]);
	  if( exp1.type == NULL )
	    {
	      return rtn;
	    }
	  cexp[i] = DEBUG_GetExprValue(&exp1, NULL);
	}

      /*
       * Now look up the address of the function itself.
       */
      switch (DEBUG_GetSymbolValue(exp->un.call.funcname, -1, &rtn, FALSE ))
      {
      case gsv_found:
          break;
      case gsv_unknown:
	  RaiseException(DEBUG_STATUS_NO_SYMBOL, 0, 0, NULL);
          /* should never be here */
      case gsv_aborted:
          RaiseException(DEBUG_STATUS_ABORT, 0, 0, NULL);
          /* should never be here */
      }

#if 0
      /* FIXME: NEWDBG NIY */
      /* Anyway, I wonder how this could work depending on the calling order of
       * the function (cdecl vs pascal for example)
       */
      int		(*fptr)();

      fptr = (int (*)()) rtn.addr.off;
      switch(exp->un.call.nargs)
	{
	case 0:
	  exp->un.call.result = (*fptr)();
	  break;
	case 1:
	  exp->un.call.result = (*fptr)(cexp[0]);
	  break;
	case 2:
	  exp->un.call.result = (*fptr)(cexp[0], cexp[1]);
	  break;
	case 3:
	  exp->un.call.result = (*fptr)(cexp[0], cexp[1], cexp[2]);
	  break;
	case 4:
	  exp->un.call.result = (*fptr)(cexp[0], cexp[1], cexp[2], cexp[3]);
	  break;
	case 5:
	  exp->un.call.result = (*fptr)(cexp[0], cexp[1], cexp[2], cexp[3], cexp[4]);
	  break;
	}
#else
      DEBUG_Printf(DBG_CHN_MESG, "Function call no longer implemented\n");
      /* would need to set up a call to this function, and then restore the current
       * context afterwards...
       */
      exp->un.call.result = 0;
#endif
      rtn.type = DEBUG_GetBasicType(DT_BASIC_INT);
      rtn.cookie = DV_HOST;
      rtn.addr.off = (unsigned int) &exp->un.call.result;

      break;
    case EXPR_TYPE_INTVAR:
      {

	 DBG_INTVAR*	div = DEBUG_GetIntVar(exp->un.intvar.name);

	 if (!div) RaiseException(DEBUG_STATUS_NO_SYMBOL, 0, 0, NULL);
	 rtn.cookie = DV_HOST;
	 rtn.type = div->type;
	 rtn.addr.off = (unsigned int)div->pval;
	 /* EPP FIXME rtn.addr.seg = ?? */
      }
      break;
    case EXPR_TYPE_BINOP:
      exp1 = DEBUG_EvalExpr(exp->un.binop.exp1);
      exp2 = DEBUG_EvalExpr(exp->un.binop.exp2);
      rtn.cookie = DV_HOST;
      if( exp1.type == NULL || exp2.type == NULL )
	{
	  RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL);
	}
      if( exp1.type == DEBUG_GetBasicType(DT_BASIC_CONST_INT) &&
          exp2.type == DEBUG_GetBasicType(DT_BASIC_CONST_INT) )
	{
	  rtn.type = exp1.type;
	}
      else
	{
          rtn.type = DEBUG_GetBasicType(DT_BASIC_INT);
	}
      rtn.addr.seg = 0;
      rtn.addr.off = (unsigned int) &exp->un.binop.result;
      switch(exp->un.binop.binop_type)
	{
	case EXP_OP_ADD:
	  type1 = DEBUG_GetPointerType(exp1.type);
	  type2 = DEBUG_GetPointerType(exp2.type);
	  scale1 = 1;
	  scale2 = 1;
	  if( type1 != NULL && type2 != NULL )
	    {
	      RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL);
	    }
	  else if( type1 != NULL )
	    {
	      scale2 = DEBUG_GetObjectSize(type1);
	      rtn.type = exp1.type;
	    }
	  else if( type2 != NULL )
	    {
	      scale1 = DEBUG_GetObjectSize(type2);
	      rtn.type = exp2.type;
	    }
	  exp->un.binop.result = (VAL(exp1) * scale1  + scale2 * VAL(exp2));
	  break;
	case EXP_OP_SUB:
	  type1 = DEBUG_GetPointerType(exp1.type);
	  type2 = DEBUG_GetPointerType(exp2.type);
	  scale1 = 1;
	  scale2 = 1;
	  scale3 = 1;
	  if( type1 != NULL && type2 != NULL )
	    {
	      if( type1 != type2 )
		{
		  RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL);
		}
	      scale3 = DEBUG_GetObjectSize(type1);
	    }
	  else if( type1 != NULL )
	    {
	      scale2 = DEBUG_GetObjectSize(type1);
	      rtn.type = exp1.type;
	    }

	  else if( type2 != NULL )
	    {
	      scale1 = DEBUG_GetObjectSize(type2);
	      rtn.type = exp2.type;
	    }
	  exp->un.binop.result = (VAL(exp1) - VAL(exp2)) / scale3;
	  break;
	case EXP_OP_SEG:
	  rtn.cookie = DV_TARGET;
	  rtn.type = NULL;
	  rtn.addr.seg = VAL(exp1);
	  rtn.addr.off = VAL(exp2);
	  break;
	case EXP_OP_LOR:
	  exp->un.binop.result = (VAL(exp1) || VAL(exp2));
	  break;
	case EXP_OP_LAND:
	  exp->un.binop.result = (VAL(exp1) && VAL(exp2));
	  break;
	case EXP_OP_OR:
	  exp->un.binop.result = (VAL(exp1) | VAL(exp2));
	  break;
	case EXP_OP_AND:
	  exp->un.binop.result = (VAL(exp1) & VAL(exp2));
	  break;
	case EXP_OP_XOR:
	  exp->un.binop.result = (VAL(exp1) ^ VAL(exp2));
 	  break;
	case EXP_OP_EQ:
	  exp->un.binop.result = (VAL(exp1) == VAL(exp2));
	  break;
	case EXP_OP_GT:
	  exp->un.binop.result = (VAL(exp1) > VAL(exp2));
	  break;
	case EXP_OP_LT:
	  exp->un.binop.result = (VAL(exp1) < VAL(exp2));
	  break;
	case EXP_OP_GE:
	  exp->un.binop.result = (VAL(exp1) >= VAL(exp2));
	  break;
	case EXP_OP_LE:
	  exp->un.binop.result = (VAL(exp1) <= VAL(exp2));
	  break;
	case EXP_OP_NE:
	  exp->un.binop.result = (VAL(exp1) != VAL(exp2));
	  break;
	case EXP_OP_SHL:
	  exp->un.binop.result = ((unsigned) VAL(exp1) << VAL(exp2));
	  break;
	case EXP_OP_SHR:
	  exp->un.binop.result = ((unsigned) VAL(exp1) >> VAL(exp2));
	  break;
	case EXP_OP_MUL:
	  exp->un.binop.result = (VAL(exp1) * VAL(exp2));
	  break;
	case EXP_OP_DIV:
	  if( VAL(exp2) == 0 )
	    {
	       RaiseException(DEBUG_STATUS_DIV_BY_ZERO, 0, 0, NULL);
	    }
	  exp->un.binop.result = (VAL(exp1) / VAL(exp2));
	  break;
	case EXP_OP_REM:
	  if( VAL(exp2) == 0 )
	    {
	       RaiseException(DEBUG_STATUS_DIV_BY_ZERO, 0, 0, NULL);
	    }
	  exp->un.binop.result = (VAL(exp1) % VAL(exp2));
	  break;
	case EXP_OP_ARR:
	  DEBUG_ArrayIndex(&exp1, &rtn, VAL(exp2));
	  break;
	default:
	  RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL);
	  break;
	}
      break;
    case EXPR_TYPE_UNOP:
      exp1 = DEBUG_EvalExpr(exp->un.unop.exp1);
      rtn.cookie = DV_HOST;
      if( exp1.type == NULL )
	{
	  RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL);
	}
      rtn.addr.seg = 0;
      rtn.addr.off = (unsigned int) &exp->un.unop.result;
      if( exp1.type == DEBUG_GetBasicType(DT_BASIC_CONST_INT) )
	{
	  rtn.type = exp1.type;
	}
      else
	{
	  rtn.type = DEBUG_GetBasicType(DT_BASIC_INT);
	}
      switch(exp->un.unop.unop_type)
	{
	case EXP_OP_NEG:
	  exp->un.unop.result = -VAL(exp1);
	  break;
	case EXP_OP_NOT:
	  exp->un.unop.result = !VAL(exp1);
	  break;
	case EXP_OP_LNOT:
	  exp->un.unop.result = ~VAL(exp1);
	  break;
	case EXP_OP_DEREF:
	  /* FIXME: this is currently buggy.
	   * there is no way to tell were the deref:ed value is...
	   * for example:
	   *	x is a pointer to struct s, x being on the stack
	   *		=> exp1 is target, result is target
	   *	x is a pointer to struct s, x being optimized into a reg
	   *		=> exp1 is host, result is target
	   *	x is a pointer to internal variable x
	   *	       	=> exp1 is host, result is host
	   * so we force DV_TARGET, because dereferencing pointers to
	   * internal variables is very unlikely. a correct fix would be
	   * rather large.
	   */
	  rtn.cookie = DV_TARGET;
	  rtn.addr.off = (unsigned int) DEBUG_TypeDerefPointer(&exp1, &rtn.type);
	  if (!rtn.type)
	    {
	      RaiseException(DEBUG_STATUS_BAD_TYPE, 0, 0, NULL);
	    }
	  break;
	case EXP_OP_FORCE_DEREF:
	  rtn.cookie = exp1.cookie;
	  rtn.addr.seg = exp1.addr.seg;
	  if (exp1.cookie == DV_TARGET)
	     DEBUG_READ_MEM((void*)exp1.addr.off, &rtn.addr.off, sizeof(rtn.addr.off));
	  else
	     memcpy(&rtn.addr.off, (void*)exp1.addr.off, sizeof(rtn.addr.off));
	  break;
	case EXP_OP_ADDR:
          /* FIXME: even for a 16 bit entity ? */
	  rtn.type = DEBUG_FindOrMakePointerType(exp1.type);
	  exp->un.unop.result = exp1.addr.off;
	  break;
	default:
	   RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL);
	}
      break;
    default:
      DEBUG_Printf(DBG_CHN_MESG,"Unexpected expression (%d).\n", exp->type);
      RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL);
      break;
    }

  assert(rtn.cookie == DV_TARGET || rtn.cookie == DV_HOST);

  return rtn;
}


int
DEBUG_DisplayExpr(const struct expr * exp)
{
  int		i;

  switch(exp->type)
    {
    case EXPR_TYPE_CAST:
      DEBUG_Printf(DBG_CHN_MESG, "((");
      DEBUG_PrintTypeCast(exp->un.cast.cast);
      DEBUG_Printf(DBG_CHN_MESG, ")");
      DEBUG_DisplayExpr(exp->un.cast.expr);
      DEBUG_Printf(DBG_CHN_MESG, ")");
      break;
    case EXPR_TYPE_INTVAR:
      DEBUG_Printf(DBG_CHN_MESG, "$%s", exp->un.intvar.name);
      break;
    case EXPR_TYPE_US_CONST:
      DEBUG_Printf(DBG_CHN_MESG, "%ud", exp->un.u_const.value);
      break;
    case EXPR_TYPE_CONST:
      DEBUG_Printf(DBG_CHN_MESG, "%d", exp->un.u_const.value);
      break;
    case EXPR_TYPE_STRING:
      DEBUG_Printf(DBG_CHN_MESG, "\"%s\"", exp->un.string.str);
      break;
    case EXPR_TYPE_SYMBOL:
      DEBUG_Printf(DBG_CHN_MESG, "%s" , exp->un.symbol.name);
      break;
    case EXPR_TYPE_PSTRUCT:
      DEBUG_DisplayExpr(exp->un.structure.exp1);
      DEBUG_Printf(DBG_CHN_MESG, "->%s", exp->un.structure.element_name);
      break;
    case EXPR_TYPE_STRUCT:
      DEBUG_DisplayExpr(exp->un.structure.exp1);
      DEBUG_Printf(DBG_CHN_MESG, ".%s", exp->un.structure.element_name);
      break;
    case EXPR_TYPE_CALL:
      DEBUG_Printf(DBG_CHN_MESG, "%s(",exp->un.call.funcname);
      for(i=0; i < exp->un.call.nargs; i++)
	{
	  DEBUG_DisplayExpr(exp->un.call.arg[i]);
	  if( i != exp->un.call.nargs - 1 )
	    {
	      DEBUG_Printf(DBG_CHN_MESG, ", ");
	    }
	}
      DEBUG_Printf(DBG_CHN_MESG, ")");
      break;
    case EXPR_TYPE_BINOP:
      DEBUG_Printf(DBG_CHN_MESG, "( ");
      DEBUG_DisplayExpr(exp->un.binop.exp1);
      switch(exp->un.binop.binop_type)
	{
	case EXP_OP_ADD:
 	  DEBUG_Printf(DBG_CHN_MESG, " + ");
	  break;
	case EXP_OP_SUB:
 	  DEBUG_Printf(DBG_CHN_MESG, " - ");
	  break;
	case EXP_OP_SEG:
 	  DEBUG_Printf(DBG_CHN_MESG, ":");
	  break;
	case EXP_OP_LOR:
 	  DEBUG_Printf(DBG_CHN_MESG, " || ");
	  break;
	case EXP_OP_LAND:
 	  DEBUG_Printf(DBG_CHN_MESG, " && ");
	  break;
	case EXP_OP_OR:
 	  DEBUG_Printf(DBG_CHN_MESG, " | ");
	  break;
	case EXP_OP_AND:
 	  DEBUG_Printf(DBG_CHN_MESG, " & ");
	  break;
	case EXP_OP_XOR:
 	  DEBUG_Printf(DBG_CHN_MESG, " ^ ");
 	  break;
	case EXP_OP_EQ:
 	  DEBUG_Printf(DBG_CHN_MESG, " == ");
	  break;
	case EXP_OP_GT:
 	  DEBUG_Printf(DBG_CHN_MESG, " > ");
	  break;
	case EXP_OP_LT:
 	  DEBUG_Printf(DBG_CHN_MESG, " < ");
	  break;
	case EXP_OP_GE:
 	  DEBUG_Printf(DBG_CHN_MESG, " >= ");
	  break;
	case EXP_OP_LE:
 	  DEBUG_Printf(DBG_CHN_MESG, " <= ");
	  break;
	case EXP_OP_NE:
 	  DEBUG_Printf(DBG_CHN_MESG, " != ");
	  break;
	case EXP_OP_SHL:
 	  DEBUG_Printf(DBG_CHN_MESG, " << ");
	  break;
	case EXP_OP_SHR:
 	  DEBUG_Printf(DBG_CHN_MESG, " >> ");
	  break;
	case EXP_OP_MUL:
 	  DEBUG_Printf(DBG_CHN_MESG, " * ");
	  break;
	case EXP_OP_DIV:
 	  DEBUG_Printf(DBG_CHN_MESG, " / ");
	  break;
	case EXP_OP_REM:
 	  DEBUG_Printf(DBG_CHN_MESG, " %% ");
	  break;
	case EXP_OP_ARR:
 	  DEBUG_Printf(DBG_CHN_MESG, "[");
	  break;
	default:
	  break;
	}
      DEBUG_DisplayExpr(exp->un.binop.exp2);
      if( exp->un.binop.binop_type == EXP_OP_ARR )
	{
 	  DEBUG_Printf(DBG_CHN_MESG, "]");
	}
      DEBUG_Printf(DBG_CHN_MESG, " )");
      break;
    case EXPR_TYPE_UNOP:
      switch(exp->un.unop.unop_type)
	{
	case EXP_OP_NEG:
 	  DEBUG_Printf(DBG_CHN_MESG, "-");
	  break;
	case EXP_OP_NOT:
	  DEBUG_Printf(DBG_CHN_MESG, "!");
	  break;
	case EXP_OP_LNOT:
	  DEBUG_Printf(DBG_CHN_MESG, "~");
 	  break;
	case EXP_OP_DEREF:
	  DEBUG_Printf(DBG_CHN_MESG, "*");
	  break;
	case EXP_OP_ADDR:
	  DEBUG_Printf(DBG_CHN_MESG, "&");
	  break;
	}
      DEBUG_DisplayExpr(exp->un.unop.exp1);
      break;
    default:
      DEBUG_Printf(DBG_CHN_MESG,"Unexpected expression.\n");
      RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL);
      break;
    }

  return TRUE;
}

struct expr *
DEBUG_CloneExpr(const struct expr * exp)
{
  int		i;
  struct expr * rtn;

  rtn = (struct expr *) DBG_alloc(sizeof(struct expr));

  /*
   * First copy the contents of the expression itself.
   */
  *rtn = *exp;


  switch(exp->type)
    {
    case EXPR_TYPE_CAST:
      rtn->un.cast.expr = DEBUG_CloneExpr(exp->un.cast.expr);
      break;
    case EXPR_TYPE_INTVAR:
      rtn->un.intvar.name = DBG_strdup(exp->un.intvar.name);
      break;
    case EXPR_TYPE_US_CONST:
    case EXPR_TYPE_CONST:
      break;
    case EXPR_TYPE_STRING:
      rtn->un.string.str = DBG_strdup(exp->un.string.str);
      break;
    case EXPR_TYPE_SYMBOL:
      rtn->un.symbol.name = DBG_strdup(exp->un.symbol.name);
      break;
    case EXPR_TYPE_PSTRUCT:
    case EXPR_TYPE_STRUCT:
      rtn->un.structure.exp1 = DEBUG_CloneExpr(exp->un.structure.exp1);
      rtn->un.structure.element_name = DBG_strdup(exp->un.structure.element_name);
      break;
    case EXPR_TYPE_CALL:
      for(i=0; i < exp->un.call.nargs; i++)
	{
	  rtn->un.call.arg[i]  = DEBUG_CloneExpr(exp->un.call.arg[i]);
	}
      rtn->un.call.funcname = DBG_strdup(exp->un.call.funcname);
      break;
    case EXPR_TYPE_BINOP:
      rtn->un.binop.exp1 = DEBUG_CloneExpr(exp->un.binop.exp1);
      rtn->un.binop.exp2 = DEBUG_CloneExpr(exp->un.binop.exp2);
      break;
    case EXPR_TYPE_UNOP:
      rtn->un.unop.exp1 = DEBUG_CloneExpr(exp->un.unop.exp1);
      break;
    default:
      DEBUG_Printf(DBG_CHN_MESG,"Unexpected expression.\n");
      RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL);
      break;
    }

  return rtn;
}


/*
 * Recursively go through an expression tree and free all memory associated
 * with it.
 */
int
DEBUG_FreeExpr(struct expr * exp)
{
  int i;

  switch(exp->type)
    {
    case EXPR_TYPE_CAST:
      DEBUG_FreeExpr(exp->un.cast.expr);
      break;
    case EXPR_TYPE_INTVAR:
      DBG_free((char *) exp->un.intvar.name);
      break;
    case EXPR_TYPE_US_CONST:
    case EXPR_TYPE_CONST:
      break;
    case EXPR_TYPE_STRING:
      DBG_free((char *) exp->un.string.str);
      break;
    case EXPR_TYPE_SYMBOL:
      DBG_free((char *) exp->un.symbol.name);
      break;
    case EXPR_TYPE_PSTRUCT:
    case EXPR_TYPE_STRUCT:
      DEBUG_FreeExpr(exp->un.structure.exp1);
      DBG_free((char *) exp->un.structure.element_name);
      break;
    case EXPR_TYPE_CALL:
      for(i=0; i < exp->un.call.nargs; i++)
	{
	  DEBUG_FreeExpr(exp->un.call.arg[i]);
	}
      DBG_free((char *) exp->un.call.funcname);
      break;
    case EXPR_TYPE_BINOP:
      DEBUG_FreeExpr(exp->un.binop.exp1);
      DEBUG_FreeExpr(exp->un.binop.exp2);
      break;
    case EXPR_TYPE_UNOP:
      DEBUG_FreeExpr(exp->un.unop.exp1);
      break;
    default:
      DEBUG_Printf(DBG_CHN_MESG,"Unexpected expression.\n");
      RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL);
      break;
    }

  DBG_free(exp);
  return TRUE;
}
