
/*
   Statement parsing, part 1
*/

#include <stdio.h>
#include "cc65.h"
#include "cclex.h"

int * readwhile();

/*
   Statement parser
   called whenever syntax requires
   a statement.
   this routine performs that
   statement
   and returns 1 if it is a branch, 0 otherwise
*/
statement()
{

#ifdef M6502
  abtchk();
#endif
  if (curtok == IDENT)
    {
      if (nxttok == COLON)
	{
	  dolabel();
	}
      else
	{
	  expression();
	  ns();
	  return (0);
	}
    }
  switch (curtok)
    {
/*    case ASM:	 -- jrd
    	doasm();
	break;		*/
    case LCURLY:
      compound();
      break;
    case IF:
      doif();
      break;
    case WHILE:
      dowhile('w');
      break;
    case DO:
      dowhile('d');
      break;
    case SWITCH:
      doswitch();
      break;
    case RETURN:
      doreturn();
      ns();
      return (1);
    case BREAK:
      dobreak();
      ns();
      return (1);
    case CONTINUE:
      docont();
      ns();
      return (1);
    case FOR:
      dofor();
      break;
    case GOTO:
      dogoto();
      return (1);
    case SEMI:
      /*
       * ignore it.
       */
      gettok();
      break;
    default:
      expression();
      ns();
    }
  return (0);
}

/*
   Semicolon enforcer called whenever syntax requires a semicolon
*/
ns()
{
  needtok(SEMI, "semicolon");
}

/*
	compound()
	Compound statement.  Allow any number of statements, inside
	braces.
*/
compound()
{
int isbrk;
char * locptr;
int oldsp;

  /*
   * gobble LCURLY
   */
  gettok();

  /*
   * parse local variable declarations if any
   */
  oldsp = oursp;
  locptr = lsptr;
  declloc();

  isbrk = 0;
  ++ncmp;
  while (curtok != RCURLY)
    {
      if (curtok == CEOF)
	break;
      else
	{
	  isbrk = statement();
	}
    }
  if (curtok == RCURLY)
    {
      gettok();
      --ncmp; /* close current level */
    }
  lsptr = locptr;
  if (isbrk)
    {
      oursp = oldsp;
    }
  else
    {
      oursp = modstk(oldsp);
    }
  return (isbrk);
}

/*
   Handle 'if' statement here
*/
doif()
{
int flab1;
int flab2;

  gettok();
  flab1 = getlabel();
  test(flab1);
  statement();
  if (curtok != ELSE)
    {
      outcdf(flab1);
      return;
    }
  gettok();
  jump(flab2 = getlabel());
  outcdf(flab1);
  statement();
  outcdf(flab2);
}

/*
   Handle 'while' statement here
*/
dowhile(wtype)
char wtype;
{
int loop;
int lab;

  gettok();
  loop = getlabel();
  lab = getlabel();
  addwhile(oursp, loop, lab, 0, 0);
  outcdf(loop);
  if (wtype == 'w')
    {
      test(lab);
      statement();
    }
  else
    {
      statement();
      if (curtok == WHILE)
	{
	  gettok();
	}
      else
	{
	  Error("do with no while");
	}
      test(lab);
      ns();
    }
  jump(loop);
  outcdf(lab);
  delwhile();
}

/*
   Handle 'return' statement here
*/
doreturn()
{
  gettok();
  if (curtok != SEMI)
    expression();
  modstk(0);
  ret();
}

/*
   Handle 'break' statement here
*/
dobreak()
{
int * ptr;

  gettok();
  if ((ptr = readwhile()) == 0)
    return;
  modstk((ptr[wqsp]));
  jump(ptr[wqlab]);
}

/*
   Handle 'continue' statement here
*/
docont()
{
int * ptr;

  gettok();
  if ((ptr = readwhile()) == 0)
    return;
  while (ptr >= wq)
    {
      if (ptr[wqloop])
	break;
      ptr -= wqsiz;
    }
  if (ptr < wq)
    {
      Error("no active whiles");
      return;
    }

  modstk((ptr[wqsp]));
  if (ptr[wqinc])
    {
      jump(ptr[wqinc]);
    }
  else
    {
      jump(ptr[wqloop]);
    }
}

addwhile(sp, loop, lab, linc, lstat)
int sp;
int loop;
int lab;
int linc;
int lstat;
{
  if (wqptr == wqmax)
    {
      Error("too many active whiles");
      return;
    }

  wqptr[wqsp] = sp;
  wqptr[wqloop] = loop;
  wqptr[wqlab] = lab;
  wqptr[wqinc] = linc;
  wqptr[wqstat] = lstat;
  wqptr += wqsiz;
}

delwhile()
{
  wqptr -= wqsiz;
}

int *
    readwhile()
{
  if (wqptr == wq)
    {
      Error("no active whiles");
      return (0);
    }
  else
    return (wqptr - wqsiz);
}

/*
	dogoto()
	Process a goto statement.
*/
dogoto()
{
struct hashent * psym;

  gettok();
  if (curtok != IDENT)
    {
      Need("goto label");
      return;
    }

  if ((psym = (struct hashent *) curval)->h_loc == 0)
    {
      addloc(psym, type_int, SC_INMEM, getlabel());
    }
  modstk(0);
  jump(psym->h_ldata);
  gettok();
}

/*
	dolabel()
	Define a label.
*/
dolabel()
{
int lab;

  if (((struct hashent *) curval)->h_loc == 0)
    {
      addloc((struct hashent *) curval, type_int, SC_INMEM, getlabel());
    }
  if (oursp)
    {
      jump(lab = getlabel());
    }
  outcdf(((struct hashent *) curval)->h_ldata);
  if (oursp)
    {
/*
   this modstk is here to get stack right 'cause anybody who
   jumps here will have reset the stack to zero relative to
   this frame before jumping.  Therefore, we modstk to twice
   the 'current' stack depth, thus causing a real modification
   by the current depth.
*/
      modstk(oursp * 2);	/* put the stack back */
      outcdf(lab);		/* define the label for code to jump around to */
      nl();
    }
  gettok();
  gettok();
}


