
/*
   Expression parsing, part 3
*/

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

int hie0();

/*
  hie11( lval )
*/
hie11(lval)
struct expent * lval;
{
  int k;

  k = primary(lval);
  if ((curtok < LBRACK) || (curtok > PREF))
    {
      return (k);
    }
  else
    {
      struct expent lval2;
      char * svptr;
      char * tptr;
      char * tptr2;

      while (1)
	{
	  if (curtok == LBRACK)
	    {
	      gettok();
	      exprhs(k, lval);
	      svptr = outqi;
	      push();
	      /*
	        TOS now contains ptr to array elements.
	      */
	      exprhs(hie0(&lval2), &lval2);
	      needbrack(RBRACK);
	      if ((tptr = lval->e_tptr)[0] & T_POINTER)
		{
		  if (tptr[0] == T_ARRAY)
		    lval->e_tptr += 3;
		  cscale(&lval2, SizeOf(++lval->e_tptr));
		  if ((lval2.e_flags == E_MCONST) &&
		      (lval2.e_const == 0))
		    {
		      popsp();
		      outqi = svptr;
		      goto end_array;
		    }
		}
	      else if ((tptr2 = lval2.e_tptr)[0] & T_POINTER)
		{
		  if (tptr2[0] == T_ARRAY)
		    lval2.e_tptr += 3;
		  swapstk();
		  scale(SizeOf(++lval2.e_tptr));
		  lval->e_tptr = lval2.e_tptr;
		}
	      else
		{
		  Error("cannot subscript");
		}
	      add();
	    end_array:
	      lval->e_flags = E_MEXPR;
	      k = lval->e_tptr[0] != T_ARRAY;
	    }
	  else if (curtok == LPAREN)
	    {
	      gettok();
	      if (lval->e_tptr[0] != T_FUNC)
		{
		  Illegal("function call");
		}
	      callfunction(k, lval);
	      lval->e_test = E_CC; /* funs always return cond codes */
	      lval->e_flags = E_MEXPR;
	      ++lval->e_tptr;
	      k = 0;
	    }
	  else if (curtok == DOT)
	    {
	      k = opref(k, lval);
	    }
	  else if (curtok == PREF)
	    {
	      k = oppref(k, lval);
	    }
	  else
	    return (k);
	}
    }
}

/*
  primary( lval )
  This is the lowest level of the expression parser.
*/
int primary(lval)
struct expent * lval;
{
  int k;
  struct hashent * psym;
  char * tptr;
  int type;

  lval->e_test = 0;		/* not a test at all, yet */
  if (curtok == IDENT)
    {
      psym = (struct hashent *) curval;
      gettok();
      /*
        check local symbol table.
      */
      if (psym->h_loc != 0)
	{
	  lval->e_tptr = tptr = psym->h_ltptr;
	  if (psym->h_loc & SC_STACK)
	    {
#ifdef oldM6502
	      getladr(psym->h_ldata);
	      lval->e_flags = E_MEXPR;
#else
	      lval->e_flags = E_MLOCAL | E_TLOFFS;
#endif
	    }
	  else
	    {
	      lval->e_flags = E_MGLOBAL | E_TGLAB;
	    }
	  lval->e_const = psym->h_ldata;
	  if ((tptr[0] == T_FUNC) || (tptr[0] == T_ARRAY))
	    {
	      return (0);
	    }
	  return (1);
	}
      /*
        check global symbol table.
      */
      if (psym->h_glb != 0)
	{
	  if ((type = psym->h_glb) & SC_STRUCT)
	    {
	      Error("struct tag/fld cannot be primary");
	      return (0);
	    }
	  lval->e_tptr = tptr = psym->h_gtptr;
	  lval->e_const = psym->h_gdata;
#ifndef M6502
	  /* don't do enums in the native compiler, to save space */
	  if (type == SC_ENUM)
	    {
	      lval->e_flags = E_MCONST;
	      return (0);
	    }
	  else
#endif
	    {
	      lval->e_flags = E_MGLOBAL | E_MCONST | E_TGLAB;
	    }
	  if ((tptr[0] == T_FUNC) || (tptr[0] == T_ARRAY))
	    {
	      return (0);
	    }
	  return (1);
	}
      /*
        IDENT is either an auto-declared function or an undefined
        variable.
      */
      if (curtok == LPAREN)
	{
	  /*
	    declare function returning int.
	  */
	  addglb(psym, type_ifunc, SC_EXTERN);
	  lval->e_tptr = Lmalloc(3);
	  strcpy(lval->e_tptr, type_ifunc);
	  lval->e_flags = E_MGLOBAL | E_TGLAB;
	  lval->e_const = psym->h_gdata;
	  return (0);
	}
      addloc(psym, type_int, SC_STACK, 0);
      lval->e_flags = E_MLOCAL | E_TLOFFS;
      lval->e_tptr = type_int;
      lval->e_const = 0;
      Error("undefined symbol");
      return (1);
    }

  /*
    Character and integer constants.
  */
  if ((curtok == ICONST) || (curtok == CCONST))
    {
      lval->e_flags = E_MCONST | E_TCONST;
      lval->e_tptr = type_int;
      lval->e_const = curval;
      gettok();
      return (0);
    }

  /*
    Process parenthesized subexpression by calling the whole parser
    recursively.
  */
  if (curtok == LPAREN)
    {
      gettok();
      k = hie0(lval);
      needbrack(RPAREN);
      return (k);
    }

  /*
    String literals.
  */
  if (curtok == SCONST)
    {
      char tarray[6];

      lval->e_flags = E_MCONST | E_TLIT;
      tarray[0] = T_ARRAY;
      encode(tarray + 1, strlen(litq + curval) + 1);
      tarray[4] = T_CHAR;
      tarray[5] = '\0';
      lval->e_tptr = Lmalloc(strlen(tarray) + 1);
      strcpy(lval->e_tptr, tarray);
      lval->e_const = curval;
      gettok();
      return (0);
    }

  /*
    Illegal primary.
  */
  Error("invalid expression");
  lval->e_flags = E_MCONST;
  lval->e_tptr = type_int;
  return (0);
}


/*
   true if val1 -> int pointer or int array and val2 not ptr or array.
   used to decide whether to shift value left 1 when indexing.
*/
scale(n)
int n;
{
  if (n == 2)
    {
      doublereg();
    }
  else if (n != 1)
    {
      push();
      immed();
#ifndef M6502
      if (n == 4)
	{
	  outdecnl(2);
	  asl();
	  return;
	}
      else if (n == 8)
	{
	  outdecnl(3);
	  asl();
	  return;
	}
#endif
      outdecnl(n);
      mult();
    }
}

cscale(lval, n)
struct expent * lval;
int n;
{
/*
    if (lval->e_flags == E_MCONST) {
	lval->e_const *= n;
	rmvbyte(4);
	lconst(E_TCONST, lval->e_const);
	}
    else {
*/
  scale(n);
/*	}	*/
}

decrem(n)
int n;
{
#ifdef old_cruft
  if (n <= 4)
    {
      while (n--)
	dec();
    }
  else
    {
      push();
      immed();
      outdecnl(n);
      sub();
    }
#else
  if (n <= 2)
    {
      oljsr((n == 1) ? "decax1" : "decax2");
    }
  else
    {
      push();
      immed();
      outdecnl(n);
      sub();
    }
#endif
}

increm(n)
int n;
{
#ifdef old_cruft
  if (n <= 4)
    {
      while (n--)
	inc();
    }
  else
    {
      push();
      immed();
      outdecnl(n);
      add();
    }
#else
  if (n <= 8)
    {
      ot("\tjsr\tincax");	/* use the canned incax1..8 ops */
      outdecnl(n);
    }
  else
    {
      konst3(n);		/* load Y with number */
      oljsr("indexax");		/* use indexing op */
    }
#endif
}

#ifdef old_cruft
/*
   determine type of binary operation
*/
result(lval, lval2)
struct expent * lval;
struct expent * lval2;
{
}
#endif

/*
	store( lval )
	Store primary reg into this reference
*/
store(lval)
struct expent * lval;
{
int f;

  f = lval->e_flags;
#ifndef M6502
  if (f == 0)
    {
      fatal("e_flags == 0");
    }
#endif
  if (f & E_MGLOBAL)
    {
      putmem(lval->e_const, lval->e_tptr);
    }
  else if (f & E_MLOCAL)
    {
      putloc(lval->e_const, lval->e_tptr);
    }
  else if (f & E_MEXPR)
    {
      putstk(lval);
    }
  else
    {
#ifdef M6502
      fatal("err 1");
#else
      fatal("trying to store into constant");
#endif
    }
}

/*
  exprhs( k, lval )
*/
exprhs(k, lval)
int k;
struct expent * lval;
{
  int f;
  int immed();

  f = lval->e_flags;
  if (k)			/* reference storable-p */
    {
      if (f & E_MGLOBAL)	/* ref to globalvar */
	{
	  /*	    getmem(lval->e_const, lval->e_tptr);	-- jrd */
	  getmem(lval->e_const, lval->e_tptr, lval->e_test);
	}
      else if (f & E_MLOCAL)	/* ref to localvar */
	{
	  getloc(lval->e_const, lval->e_tptr);
	}
      else if (f & E_MCONST)	/* ref to constant */
	{
#ifdef M6502
	  fatal("err 2");
#else
	  fatal("Constant with k = 1");
#endif
	}
      else
	{
	  indirect(lval);	/* else must be locative */
	}
      /*	lval->e_test |= E_CC;		.. say we set cc (all those do...) */
    }
  else
    /* reference not storable */
    if (f == E_MEOFFS)
      {
	push();
	immed();
	/*	    outdec(lval->e_const);	*/
	outdecnl(lval->e_const); /* -- jrd */
	add();
      }
    else if (f != E_MEXPR)
      {
	lconst(f & E_MCTYPE, lval->e_const);
      }

  /*	lval->e_test &= ~E_CC;		/* say cc not set right */
  if (lval->e_test & E_TEST)	/* we testing this value? */
    {
#ifndef M6502
      /* debug... */
      ol(";;; force test");
#endif
      tst();			/* yes, force a test */
    }
}


/*
	liconst( const )
	Load primary reg with integer constant.
*/
liconst(cnst)
int cnst;
{
  immed();
  outdecnl(cnst);
}

/*
	lconst( func, ctype, const )
	Load primary reg with some constant value.
*/
lconst(ctype, cnst)
int ctype;
int cnst;
{
  ctype &= E_MCTYPE;
  if (ctype == E_TLOFFS)
    {
      getladr(cnst);
    }
  else
    {
      immed();
      outconst(ctype, cnst, 0);
    }
}

/*
	outconst(ctype, const, ischar)
	Output psuedo-op appropriate to type of constant.
*/
outconst(ctype, cnst, ischar)
int ctype;
int cnst;
int ischar;
{
  switch (ctype & E_MCTYPE)
    {
    case E_TCONST:		/* fixnum constant */
      outdecnl(cnst);
      return;
    case E_TGLAB:		/* some kind of global */
      outgbl(cnst);
      nl();
      return;
    case E_TLIT:		/* a literal of some kind */
      outslt(cnst);
      return;
    default:
#ifdef M6502
      fatal("err 3");
#else
      printf("unknown constant: %d\n", ctype & E_MCTYPE);
      fatal("compiler error: unknown constant type");
#endif
    }
}

/*
	test( label )
	Generate code to perform test and jump if false.
*/
test(label)
int label;
{
int k;
struct expent lval;

#ifndef M6502
  bzero(&lval, sizeof(lval));
#endif
  needbrack(LPAREN);
  k = expr(hie0, &lval); /* generate code to eval the expr */
/* #ifndef M6502 */
  if (lval.e_flags == E_MCONST)
    {
      if (lval.e_const == 0)
	{
	  jump(label);
	  Warning("unreachable code");
	}
      needbrack(RPAREN);
      return;
    }
/* #endif */
  /* if the expr hasn't set condition
     codes, set the force-test flag */
  if (!(lval.e_test & E_CC))
    lval.e_test |= E_TEST;
  exprhs(k, &lval);		/* load the value as apropriate */
  needbrack(RPAREN);
#ifndef M6502
  if (lval.e_test & E_XINV)
    ol(";;; test inverted");
#endif
  falsejump(label, lval.e_test & E_XINV);
}

/*
	needbrack( btype )
	Enforce closing bracket type.
*/
needbrack(btype)
int btype;
{
  needtok(btype, "bracket");
}

/*
	callfunction(ptr)
	Perform a function call.  Called from hie11, this routine will
	either call the named function, or if the supplied ptr is zero,
	will call the contents of P.
*/
callfunction(k, lval)
int k;
struct expent * lval;
{
char * dptr;
struct expent lval2;
int nargs;

  nargs = 0;
  if (lval->e_flags & E_MEXPR)
    {
      save();
    }
  while (curtok != RPAREN)
    {
      exprhs(hie1(&lval2), &lval2);
      push();
      nargs += 2;
      if (curtok != COMMA)
	break;
      gettok();
    }
  needbrack(RPAREN);
  if (lval->e_flags & E_MEXPR)
    {
      rstr();
/*	push();		not needed -- jrd.  we call (AX) */
      callstk(nargs);
    }
  else
    {
      call(lval->e_const, nargs);
    }
}

/*
	opref( lval )
	Process . operator.
*/
int opref(k, lval)
int k;
struct expent * lval;
{
  if (!(lval->e_tptr[0] & T_STRUCT))
    {
      Need("struct");
    }
  if (!(lval->e_flags & E_MEXPR))
    {
      lconst(lval->e_flags & E_MCTYPE, lval->e_const);
    }
  return (structref(lval));
}

/*
	oppref( k, lval )
	Process -> operator.
*/
int oppref(k, lval)
int k;
struct expent * lval;
{
char * tptr;

  tptr = (char *) lval->e_tptr;
  if ((tptr[0] != T_PTR) || !(tptr[1] & T_STRUCT))
    {
      Need("struct pointer");
    }
  exprhs(k, lval);
  return (structref(lval));
}

/*
	structref( lval )
	Process struct field after . or ->.
*/
int structref(lval)
struct expent * lval;
{
struct hashent * psym;

  gettok();
  if (curtok != IDENT)
    {
      Syntax();
      lval->e_tptr = type_int;
      return (0);
    }
  psym = (struct hashent *) curval;
  gettok();
#ifdef M6502
/*
   until such time as we fix cc65 to generate fetches of unsigned
   chars correctly, must mask here...
  if (psym->h_glb != SC_SFLD)
*/
  if ((psym->h_glb & 0xFF) != SC_SFLD)
#else
  if ((psym->h_glb & 0xFF) != SC_SFLD)
#endif
    {
      Need("struct field");
      lval->e_tptr = type_int;
      return (0);
    }

  lval->e_const = psym->h_gdata;
  lval->e_tptr = Lmalloc(strlen(psym->h_gtptr) + 1);
  strcpy(lval->e_tptr, psym->h_gtptr);
  lval->e_flags = E_MEOFFS;
  if (psym->h_gtptr[0] == T_ARRAY)
    {
      return (0);
    }
  return (1);
}

/*
	getlabel()
	Get next unused label.
*/
getlabel()
{
  return (++nxtlab);
}
