
/*
   Expression parsing, part 2
*/

#include <stdio.h>
#include "cc65.h"
#include "cclex.h"

int hie8();
int hie9();
int hie10();

int increm();
int decrem();
int inc();
int dec();

/*
  hie7( lval )
  Parse << and >>.
*/

extern asl();
extern asr();
struct op_alist hie7_ops[] =
{
  {ASL, asl},
  {ASR, asr},
  {0, 0}
};

hie7(lval)
struct expent * lval;
{
  return (hie_internal(hie7_ops, lval, hie8));
}

/*
  hie8( lval )
  Process + and - binary operators.
*/
hie8(lval)
struct expent * lval;
{
  int k;

  k = hie9(lval);
  if ((curtok != PLUS) && (curtok != MINUS))
    {
      return (k);
    }
  else
    {
      struct expent lval2;
      char * svptr;
      char * tptr1;
      char * tptr2;

      svptr = outqi;
      exprhs(k, lval);
      while (1)
	{
	  if (curtok == PLUS)
	    {
	      getoprnd(hie9, &lval2);
	      tptr1 = lval->e_tptr;
	      tptr2 = lval2.e_tptr;
	      if ((tptr1[0] & T_POINTER) &&
		  (tptr2[0] & T_INTEGER))
		{
		  cscale(&lval2, PSizeOf(tptr1));
		}
	      else if ((tptr1[0] & T_INTEGER) &&
		       (tptr2[0] & T_POINTER))
		{
		  swapstk();
		  scale(PSizeOf(tptr2));
		  lval->e_tptr = lval2.e_tptr;
		}
	      if (isconst(lval, &lval2, svptr))
		{
		  liconst(lval->e_const += lval2.e_const);
		}
	      else
		{
		  add();
		  lval->e_flags = E_MEXPR;
		}
	    }
	  else if (curtok == MINUS)
	    {
	      getoprnd(hie9, &lval2);
	      tptr1 = lval->e_tptr;
	      tptr2 = lval2.e_tptr;
	      if ((tptr1[0] & T_POINTER) && (tptr2[0] & T_INTEGER))
		{
		  cscale(&lval2, PSizeOf(tptr1));
		}
	      sub();
	      if ((tptr1[0] & tptr2[0] & T_POINTER) &&
		  (strcmp(tptr1, tptr2) == 0))
		{
		  k = PSizeOf(tptr1);
		  if (k > 1)
		    {
		      push();
		      /*
			immed();
			outdec(k);
		      */
		      konst1(1);

		      div();
		      lval->e_flags = E_MEXPR;
		    }
		  lval->e_tptr = type_int;
		}
	      if (lval->e_flags == E_MCONST && lval2.e_flags == E_MCONST)
		{
		  outqi = svptr;
		  liconst(lval->e_const -= lval2.e_const);
		}
	      else
		{
		  lval->e_flags = E_MEXPR;
		}
	    }
	  else
	    {
	      if (lval->e_flags == E_MCONST)
		{
		  outqi = svptr;
		}
	      else
		{
		  lval->e_flags = E_MEXPR;
		}
	      return (0);
	    }
	}
    }
}

/*
  hie9( lval )
  Process * and / operators.
*/

extern mult();
extern div();
extern mod();
struct op_alist hie9_ops[] =
{
  {STAR, mult},
  {DIV, div},
  {MOD, mod},
  {0, 0}
};

hie9(lval)
struct expent * lval;
{
  return (hie_internal(hie9_ops, lval, hie10));
}

/*
  hie10( lval )
  Parse unary operators.
*/

/* various internal functions */
static int hie10a(lval, incptr, incint)	/* guts of inc, dec */
struct expent * lval;
int (*incptr) ();	/* for inc'ing ptrs */
int (*incint) ();	/* for inc'ing ints */
{
int k;

  gettok();
  if ((k = hie10(lval)) == 0)
    {
      needlval();
      return (0);
    }
  if (lval->e_flags & E_MEXPR)
    push1();
  exprhs(k, lval);
  if (lval->e_tptr[0] == T_PTR)
    {
      (*incptr) (SizeOf(lval->e_tptr + 1));
    }
  else
    {
      (*incint) ();
    }
  store(lval);
  lval->e_flags = E_MEXPR;
}

static int hie10b(lval, k, incptr, incint)	/* guts of second inc, dec */
struct expent * lval;
int k;
int (*incptr) ();	/* for inc'ing ptrs */
int (*incint) ();	/* for inc'ing ints */
{
  gettok();
  if (k == 0)
    {
      needlval();
      return (0);
    }
  if (lval->e_flags & E_MEXPR)
    push1();
  exprhs(1, lval);
  save();
  if (lval->e_tptr[0] == T_PTR)
    {
      (*incptr) (SizeOf(lval->e_tptr + 1));
    }
  else
    {
      (*incint) ();
    }
  store(lval);
  rstr();
  lval->e_flags = E_MEXPR;
}

static hie10c(tok, lval)	/* guts of minus, comp */
int tok;
struct expent * lval;
{
  int k;

  gettok();
  k = hie10(lval);
  if (lval->e_flags == E_MCONST)
    {
      lval->e_const = (tok == MINUS ? -lval->e_const : ~lval->e_const);
      return (0);
    }
  exprhs(k, lval);
  if (tok == MINUS)
    neg();
  else
    com();
  lval->e_flags = E_MEXPR;
}

hie10(lval)
struct expent * lval;
{
  int k;
  char * ptr;
  struct hashent * sadr;
  int type;

  if (curtok != IDENT)
    {
      switch (curtok)
	{
	case INC:
	  {
	    hie10a(lval, increm, inc);
	    return (0);
	  }
	case DEC:
	  {
	    hie10a(lval, decrem, dec);
	    return (0);
	  }
	case MINUS:
	case COMP:
	  {
	    hie10c(curtok, lval);
	    return (0);
	  }
	case BANG:
	  {
	    gettok();
	    k = hie10(lval);	/* decode the expr */
	    exprhs(k, lval);	/* load the value */

#ifdef busted
	    if (lval->e_test)	/* somebody doing a test of this exp? */
	      {
		lval->e_test ^= E_XINV;	/* flip sense of test */
	      }
	    else
	      /* normal expr */
	      {
#ifdef old_cruft
		push();
		konst1(0);
		eq();
#else
		lneg();
#endif
	      }
#else
	    lneg();		/* sheesh */
#endif
	    lval->e_flags = E_MEXPR; /* say it's an expr */
	    return (0);		/* expr not storable */
	  }
	case STAR:
	  {
	    gettok();
	    exprhs(hie10(lval), lval);
	    if (((ptr = lval->e_tptr)[0] & T_POINTER) == 0)
	      {
		Illegal("indirection");
	      }
	    else
	      {
		if (ptr[0] == T_ARRAY)
		  {
		    ptr += 4;
		  }
		else
		  {
		    ++ptr;
		  }
		lval->e_tptr = ptr;
	      }
	    lval->e_flags = E_MEXPR;
	    return (1);
	  }
	case AMP:
	  {
	    gettok();
	    k = hie10(lval);
	    if (k == 0)
	      {
		Illegal("address");
		return (0);
	      }
	    ptr = Lmalloc(strlen(lval->e_tptr) + 2);
	    ptr[0] = T_PTR;
	    strcpy(ptr + 1, lval->e_tptr);
	    lval->e_tptr = ptr;
	    return (0);
	  }
	case SIZEOF:
	  {
	    gettok();
	    if ((curtok == LPAREN) && (nxttok >= 20) && (nxttok <= 29))
	      {
		char tarray1[MAXTYPELEN];

		gettok();
		type = gettype(-1, &sadr);
		absdecl = 1;
		declare(tarray1, type, sadr);
		needbrack(RPAREN);
		lval->e_const = SizeOf(tarray1);
	      }
	    else
	      {
		hie10(lval);
		lval->e_const = SizeOf(lval->e_tptr);
	      }
	    lval->e_flags = E_MCONST | E_TCONST;
	    lval->e_tptr = type_int;
	    return (0);
	  }
	default:
	  if ((curtok == LPAREN) && (nxttok >= 20) && (nxttok <= 29))
	    {
	      char tarray2[MAXTYPELEN];

	      gettok();
	      type = gettype(-1, &sadr);
	      absdecl = 1;
	      declare(tarray2, type, sadr);
	      needbrack(RPAREN);
	      k = hie10(lval);
	      strcpy(lval->e_tptr = Lmalloc(strlen(tarray2) + 1), tarray2);
	      return (k);
	    }
	}			/* end switch */
    }				/* end curtok != ident */
  k = hie11(lval);
  switch (curtok)
    {
    case INC:
      {
	hie10b(lval, k, increm, inc);
	return (0);
      }
    case DEC:
      {
	hie10b(lval, k, decrem, dec);
	return (0);
      }
    default:
      return (k);
    }
}
