
/*
   expression parser, part 1
*/

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

extern char * lsptr;

int hie0();
int hie1();
int hie2();
int hie3();
int hie4();
int hie5();
int hie6();
int add();
int sub();
int mult();
int div();
int mod();
int asl();
int asr();
int and();
int xor();
int or();
int lt();
/* int	ult(); */
int gt();
/* int	ugt(); */
int le();
/* int	ule(); */
int ge();
/* int	uge(); */
int eq();
int ne();

/*
  needtok( token, msg )
  Token is expected next.  Print "missing <msg>" if not found.
*/
needtok(token, msg)
int token;
char * msg;
{
  if (curtok == token)
    {
      gettok();
      return;
    }
  else
    {
      Missing(msg);
    }
}

/*
  needcol()
  Colon enforcer.
*/
needcol()
{
  needtok(COLON, "colon");
}

/*
  getoprnd( func, lval )
  Subroutine of commonly executed sequence of statements;
*/
getoprnd(func, lval)
int (*func) ();
struct expent * lval;
{
  gettok();
  push();
  exprhs((*func) (lval), lval);
}

/*
  isconst( lval1, lval2 )
  Test to see if both lval1 and lval2 are constant expressions.
*/
isconst(lval1, lval2, svptr)
struct expent * lval1;
struct expent * lval2;
char * svptr;
{
  if (lval1->e_flags == E_MCONST && lval2->e_flags == E_MCONST)
    {
      popsp();
      outqi = svptr;
      return (1);
    }
  else
    {
      return (0);
    }
}

/* added by jrd.  return T if value describes constant 0 */
const0(lval)
struct expent * lval;
{
  return ((lval->e_flags == E_MCONST) && (lval->e_const == 0));
}

/*
  expression()
  P = value of expression.
*/
expression()
{
struct expent lval;

#ifndef M6502
  bzero(&lval, sizeof(lval));
#endif
  exprhs(expr(hie0, &lval), &lval);
}

/*
  expr( func, lval )
  Expression parser; func is either hie0 or hie1.
*/
expr(func, lval)
int (*func) ();
struct expent * lval;
{
int k;
char * locptr;
int savsp;

  savsp = oursp;
  locptr = lsptr;

/* #ifdef M6502  -- jrd */

  flushout();

/* #endif */

  k = (*func) (lval);
  lsptr = locptr;
#ifndef M6502
  if (savsp != oursp)
    {
      char errbuf[100];

      sprintf(errbuf, "oursp [%d] != savsp [%d]", oursp, savsp);
      Error(errbuf);
    }
#endif
  return (k);
}

/*
  hie0( lval )
  Parse comma operator.
*/
hie0(lval)
struct expent * lval;
{
int k;

  k = hie1(lval);
  while (curtok == COMMA)
    {
      gettok();
      k = hie1(lval);
    }
  return (k);
}

/*
  hie1( lval )
  Parse first level of expression hierarchy.
*/
hie1(lval)
struct expent * lval;
{
int (*func) ();
int k;

  k = hieQuest(lval);
  switch (curtok)
    {
    case RPAREN:
    case SEMI:
      return (k);
    case ASGN:
      {
	struct expent lval2;

	gettok();
	if (k == 0)
	  {
	    needlval();
	    return (0);
	  }
	if (lval->e_flags & E_MEXPR)
	  push();
	exprhs(hie1(&lval2), &lval2);
	store(lval);
	lval->e_flags = E_MEXPR;

/* if either the source expression of the target generated condition
   codes, set the cond-codes bit in the returned expr */
	lval->e_test |= (lval2.e_test & E_CC);

	return (0);
      }
    case PASGN:
      func = add;
      break;
    case SASGN:
      func = sub;
      break;
    case MASGN:
      func = mult;
      break;
    case DASGN:
      func = div;
      break;
    case MOASGN:
      func = mod;
      break;
    case SLASGN:
      func = asl;
      break;
    case SRASGN:
      func = asr;
      break;
    case AASGN:
      func = and;
      break;
    case XOASGN:
      func = xor;
      break;
    case OASGN:
      func = or;
      break;
    default:
      return (k);
    }
  opeq(func, lval, k);
  return (0);
}

/*
  opeq( func, lval, k)
  Process "op=" operators.
*/
opeq(func, lval, k)
int (*func) ();
struct expent * lval;
int k;
{
char * ptr;
struct expent lval2;

  gettok();
  if (k == 0)
    {
      needlval();
      return;
    }
  if (lval->e_flags & E_MEXPR)
    push1();
  exprhs(k, lval);
  push();
  exprhs(hie1(&lval2), &lval2);
  if (func == add || func == sub)
    {
      if (lval->e_tptr[0] == T_PTR)
	{
	  scale(SizeOf(lval->e_tptr + 1));
	}
    }
  (*func) ();
  store(lval);
  lval->e_flags = E_MEXPR;
}

/*
  hieQuest( lval )
  Parse "lvalue ? exp : exp"
*/
hieQuest(lval)
struct expent * lval;
{
  int k;

  k = hieOr(lval);
  if (curtok != QUEST)
    {
      return (k);
    }
  else
    {
      int labf;
      int labt;

      gettok();
      exprhs(k, lval);
      labf = getlabel();
      falsejump(labf, 0);
      expression();
      labt = getlabel();
      needcol();
      jump(labt);
      outcdf(labf);
      expression();
      outcdf(labt);
      lval->e_flags = E_MEXPR;
      return (0);
    }
}

/*
  hieOr( lval )
  Process "exp || exp".
*/
hieOr(lval)
struct expent * lval;
{
  int k;

  k = hieAnd(lval);		/* do higher-precedence exprs */
  if (curtok != DBAR)		/* if not ||, return */
    {
      return (k);
    }
  else
    {
      int lab1;			/* labl to exit expr w/ 1 */
      int lab2;			/* labl to exit expr w/ 0 */
      struct expent lval2;
      int loadk;		/* flag if load 1 at end */

      loadk = ((lval->e_test & E_LOGL) == 0); /* need to load 1 if not already */
      exprhs(k, lval);		/* get first expr */
      lab2 = getlabel();	/* get internal label.  for
				   tests, this is the only
				   label */
      truejump(lab1 = getlabel(), 0); /* get lab and gen 1st bne to it */
      while (curtok == DBAR)	/* while there's more expr */
	{
	  gettok();		/* skip the || */
	  exprhs(hieAnd(&lval2), &lval2); /* get a subexpr */
	  loadk |= ((lval2.e_test & E_LOGL) == 0); /* need to load 1 if not already */
	  if (curtok == DBAR)	/* if still more, ... */
	    {
	      truejump(lab1, lval->e_test & E_XINV); /* bne out */
	    }
	  else
	    {
	      if (loadk)	/* only generate branch around
				   load if we're loading */
		falsejump(lab2, lval->e_test & E_XINV);	/* else beq out */
	    }
	}
      outcdf(lab1);		/* define lab1 */
      if (loadk)		/* if need a load-const 1 ... */
	{
	  konst1(1);		/*  ... do it */
	}
      outcdf(lab2);		/* define lab2 */
      lval->e_flags = E_MEXPR;
      return (0);
    }
}

/*
  hieAnd( lval )
  Process "exp && exp"
*/
hieAnd(lval)
struct expent * lval;
{
  int k;

  k = hie2(lval);
  if (curtok != DAMP)
    {
      return (k);
    }
  else
    {
      int lab;
      struct expent lval2;

      exprhs(k, lval);
      falsejump(lab = getlabel(), 0);
      while (curtok == DAMP)
	{
	  gettok();
	  exprhs(hie2(&lval2), &lval2);
	  falsejump(lab, 0);	/* zzz finish later */
	}
      /*	immed();
		outdec(1);	*/
      konst1(1);

      outcdf(lab);
      lval->e_flags = E_MEXPR;
      return (0);
    }
}

/* utils used by hie2, 3, and 4.  it's the guts of those 3 guys,
   extracted and parameterized */

static int kcalc(tok, val1, val2)
int tok, val1, val2;
{
#ifdef M6502
/* hand bummed version of what this compiles into, to avoid extraneous
   pushes. */
#asm
	ldy	#6		; get 'tok'
	jsr	ldaysp		; this is safe cause theyre all byte-sized
	sta	tmp4		; save the value here
; common preamble
	ldy	#4		; offset for val1
	jsr	pushwysp	; push it
	ldy	#4		; offset for val2
	jsr	ldaxysp		; load it
	ldy	tmp4		; get token number back

	cpy	#78		; BAR
	bne	*+6		; nope, try next
	jsr	ortos		; do an OR
	jmp	exitfun		; and return the value

	cpy	#79		; EQ
	bne	*+6		; nope, try next
	jsr	toseqax		; do an ==
	jmp	exitfun		; and return the value

	cpy	#90		; XOR
	bne	*+6		; nope, try next
	jsr	xortos		; do an XOR
	jmp	exitfun		; and return the value

	cpy	#73		; AMP
	bne	*+6		; nope, try next
	jsr	andtos		; do an AND
	jmp	exitfun		; and return the value

	cpy	#88		; ASR
	bne	*+6		; nope, try next
	jsr	asrtos		; do an ASR
	jmp	exitfun		; and return the value

	cpy	#82		; ASL
	bne	*+6		; nope, try next
	jsr	asltos		; do an ASL
	jmp	exitfun		; and return the value

	cpy	#68		; STAR
	bne	*+6		; nope, try next
	jsr	multos		; do a multiply
	jmp	exitfun		; and return the value

	cpy	#70		; DIV
	bne	*+6		; nope, try next
	jsr	divtos		; do a divide
	jmp	exitfun		; and return the value

	cpy	#92		; MOD
	bne	*+6		; nope, try next
	jsr	modtos		; do a MOD
	jmp	exitfun		; and return the value
; if we get here its an internal error.  ignore for now
	jsr	popax		; flush dead value on stack
#endasm
#else
  switch (tok)
    {
    case BAR:
      return (val1 | val2);
    case EQ:
      return (val1 == val2);
    case XOR:
      return (val1 ^ val2);
    case AMP:
      return (val1 & val2);
    case ASR:
      return (val1 >> val2);
    case ASL:
      return (val1 << val2);
    case STAR:
      return (val1 * val2);
    case DIV:
      return (val1 / val2);
    case MOD:
      return (val1 % val2);
#ifndef M6502
    default:
      printf("Internal error: kcalc: got token %X\n", tok);
#endif
    }
#endif
}

int op_assoc(tok, ops)
int tok;
struct op_alist * ops;
{
  while (ops->tok && (ops->tok != tok))
    ++ops;
  return (ops->gen);
}

/* helper fun for various hie frobs */

int hie_internal(ops, lval, hienext)
struct op_alist * ops;		/* alist of ops we'll take here */
struct expent * lval;		/* parent expr's lval */
int (*hienext) ();		/* next higher level parser */
{
  int k;

  k = (*hienext) (lval);
  if (!op_assoc(curtok, ops))	/* sufficient to test if it's there... */
    {
      return (k);
    }
  else
    {
      struct expent lval2;
      char * svptr;
      int (*gen) ();
      int this_tok;

      svptr = outqi;
      exprhs(k, lval);
      while (1)
	{
	  if (gen = op_assoc((this_tok = curtok), ops))
	    {
	      getoprnd(hienext, &lval2);
	      if (isconst(lval, &lval2, svptr))
		{
		  liconst(lval->e_const =
			  kcalc(this_tok, lval->e_const, lval2.e_const));
		  lval->e_test &= ~E_CC; /* cc not set right */
		}
	      else
		{
		  (*gen) ();
		  lval->e_flags = E_MEXPR;
		  lval->e_test |= E_CC;	/* say we set cc */
		}
	    }
	  else
	    {
	      if (lval->e_flags == E_MCONST)
		{
		  outqi = svptr;
		}
	      return (0);
	    }
	}
    }
}

/*
  hie2( lval )
*/
struct op_alist hie2_ops[] =
{
  {BAR, or},
  {0, 0}
};

hie2(lval)
struct expent * lval;
{
  return (hie_internal(hie2_ops, lval, hie3));
}

/*
  hie3( lval )
*/
struct op_alist hie3_ops[] =
{
  {XOR, xor},
  {0, 0}
};

hie3(lval)
struct expent * lval;
{
  return (hie_internal(hie3_ops, lval, hie4));
}

/*
  hie4( lval )
*/
struct op_alist hie4_ops[] =
{
  {AMP, and},
  {0, 0}
};

hie4(lval)
struct expent * lval;
{
  return (hie_internal(hie4_ops, lval, hie5));
}

/*
  hie5( lval )
*/
struct op_alist hie5_ops[] =
{
  {EQ, eq},
  {NE, ne},
  {0, 0}
};

hie5(lval)
struct expent * lval;
{
  return (hie_internal(hie5_ops, lval, hie6));
}

/*
  hie6( lval )
  process greater-than type comparators
*/

/* helper fun for hie6.  Where's FLET when you need it? */
static hie6_internal(isptr1, lval, gen)
int isptr1;			/* other value is ptr */
struct expent * lval;
int (*gen) ();			/* code generator fun */
{
  gettok();
  push();
  exprhs(hie7(lval), lval);
  (*gen) ((isptr1 || (lval->e_tptr[0] & T_UNSIGNED)));
}

hie6(lval)
struct expent * lval;
{
  int k;

  k = hie7(lval);
  if ((curtok < LE) || (curtok > GT))
    {
      return (k);
    }
  else
    {
      struct expent lval2;
      int isptr1;

      exprhs(k, lval);
      isptr1 = lval->e_tptr[0] & T_UNSIGNED;
      lval->e_test = E_LOGL | E_CC; /* say we returned a logl val, and set cc */
      while (1)
	{
	  switch (curtok)
	    {
	    case LE:
	      hie6_internal(isptr1, &lval2, le);
	      break;
	    case GE:
	      hie6_internal(isptr1, &lval2, ge);
	      break;
	    case LT:
	      hie6_internal(isptr1, &lval2, lt);
	      break;
	    case GT:
	      hie6_internal(isptr1, &lval2, gt);
	      break;
	    default:
	      lval->e_flags = E_MEXPR;
	      return (0);
	    }
	}
    }
}
