/* stw.c -- Implementation File (module.c template V1.0)
   Copyright (C) 1995 Free Software Foundation, Inc.
   Contributed by James Craig Burley (burley@gnu.ai.mit.edu).

This file is part of GNU Fortran.

GNU Fortran is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Fortran is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Fortran; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

   Related Modules:
      None (despite the name, it doesn't really depend on ffest*)

   Description:
      Provides abstraction and stack mechanism to track the block structure
      of a Fortran program.

   Modifications:
*/

/* Include files. */

#include "proj.h"
#include "stw.h"
#include "bld.h"
#include "com.h"
#include "info.h"
#include "lab.h"
#include "lex.h"
#include "malloc.h"
#include "sta.h"
#include "stv.h"
#include "symbol.h"
#include "where.h"

/* Externals defined here. */

ffestw ffestw_stack_top_ = NULL;

/* Simple definitions and enumerations. */


/* Internal typedefs. */


/* Private include files. */


/* Internal structure definitions. */


/* Static objects accessed by functions in this module. */


/* Static functions (internal). */


/* Internal macros. */


/* ffestw_display_state -- DEBUGGING; display current block state

   ffestw_display_state();  */

void
ffestw_display_state ()
{
  assert (ffestw_stack_top_ != NULL);

  if (!ffe_is_ffedebug ())
    return;

  fprintf (stdout, "; block %lu, state ", ffestw_stack_top_->blocknum_);
  switch (ffestw_stack_top_->state_)
    {
    case FFESTV_stateNIL:
      fputs ("NIL", stdout);
      break;

    case FFESTV_statePROGRAM0:
      fputs ("PROGRAM0", stdout);
      break;

    case FFESTV_statePROGRAM1:
      fputs ("PROGRAM1", stdout);
      break;

    case FFESTV_statePROGRAM2:
      fputs ("PROGRAM2", stdout);
      break;

    case FFESTV_statePROGRAM3:
      fputs ("PROGRAM3", stdout);
      break;

    case FFESTV_statePROGRAM4:
      fputs ("PROGRAM4", stdout);
      break;

    case FFESTV_statePROGRAM5:
      fputs ("PROGRAM5", stdout);
      break;

    case FFESTV_stateSUBROUTINE0:
      fputs ("SUBROUTINE0", stdout);
      break;

    case FFESTV_stateSUBROUTINE1:
      fputs ("SUBROUTINE1", stdout);
      break;

    case FFESTV_stateSUBROUTINE2:
      fputs ("SUBROUTINE2", stdout);
      break;

    case FFESTV_stateSUBROUTINE3:
      fputs ("SUBROUTINE3", stdout);
      break;

    case FFESTV_stateSUBROUTINE4:
      fputs ("SUBROUTINE4", stdout);
      break;

    case FFESTV_stateSUBROUTINE5:
      fputs ("SUBROUTINE5", stdout);
      break;

    case FFESTV_stateFUNCTION0:
      fputs ("FUNCTION0", stdout);
      break;

    case FFESTV_stateFUNCTION1:
      fputs ("FUNCTION1", stdout);
      break;

    case FFESTV_stateFUNCTION2:
      fputs ("FUNCTION2", stdout);
      break;

    case FFESTV_stateFUNCTION3:
      fputs ("FUNCTION3", stdout);
      break;

    case FFESTV_stateFUNCTION4:
      fputs ("FUNCTION4", stdout);
      break;

    case FFESTV_stateFUNCTION5:
      fputs ("FUNCTION5", stdout);
      break;

    case FFESTV_stateMODULE0:
      fputs ("MODULE0", stdout);
      break;

    case FFESTV_stateMODULE1:
      fputs ("MODULE1", stdout);
      break;

    case FFESTV_stateMODULE2:
      fputs ("MODULE2", stdout);
      break;

    case FFESTV_stateMODULE3:
      fputs ("MODULE3", stdout);
      break;

    case FFESTV_stateMODULE4:
      fputs ("MODULE4", stdout);
      break;

    case FFESTV_stateMODULE5:
      fputs ("MODULE5", stdout);
      break;

    case FFESTV_stateBLOCKDATA0:
      fputs ("BLOCKDATA0", stdout);
      break;

    case FFESTV_stateBLOCKDATA1:
      fputs ("BLOCKDATA1", stdout);
      break;

    case FFESTV_stateBLOCKDATA2:
      fputs ("BLOCKDATA2", stdout);
      break;

    case FFESTV_stateBLOCKDATA3:
      fputs ("BLOCKDATA3", stdout);
      break;

    case FFESTV_stateBLOCKDATA4:
      fputs ("BLOCKDATA4", stdout);
      break;

    case FFESTV_stateBLOCKDATA5:
      fputs ("BLOCKDATA5", stdout);
      break;

    case FFESTV_stateUSE:
      fputs ("USE", stdout);
      break;

    case FFESTV_stateTYPE:
      fputs ("TYPE", stdout);
      break;

    case FFESTV_stateINTERFACE0:
      fputs ("INTERFACE0", stdout);
      break;

    case FFESTV_stateINTERFACE1:
      fputs ("INTERFACE1", stdout);
      break;

    case FFESTV_stateSTRUCTURE:
      fputs ("STRUCTURE", stdout);
      break;

    case FFESTV_stateUNION:
      fputs ("UNION", stdout);
      break;

    case FFESTV_stateMAP:
      fputs ("MAP", stdout);
      break;

    case FFESTV_stateWHERETHEN:
      fputs ("WHERETHEN", stdout);
      break;

    case FFESTV_stateWHERE:
      fputs ("WHERE", stdout);
      break;

    case FFESTV_stateIFTHEN:
      fputs ("IFTHEN", stdout);
      break;

    case FFESTV_stateIF:
      fputs ("IF", stdout);
      break;

    case FFESTV_stateDO:
      fputs ("DO", stdout);
      break;

    case FFESTV_stateSELECT0:
      fputs ("SELECT0", stdout);
      break;

    case FFESTV_stateSELECT1:
      fputs ("SELECT1", stdout);
      break;

    default:
      assert ("bad state" == NULL);
      break;
    }
  if (ffestw_stack_top_->top_do_ != NULL)
    fputs (" (within DO)", stdout);
  fputc ('\n', stdout);
}

/* ffestw_init_0 -- Initialize ffestw structures

   ffestw_init_0();  */

void
ffestw_init_0 ()
{
  ffestw b;

  ffestw_stack_top_ = b = (ffestw) malloc_new_kp (malloc_pool_image (),
					  "FFESTW stack base", sizeof (*b));
  b->uses_ = 0;			/* catch if anyone uses, kills, &c this
				   block. */
  b->next_ = NULL;
  b->previous_ = NULL;
  b->top_do_ = NULL;
  b->blocknum_ = 0;
  b->shriek_ = NULL;
  b->state_ = FFESTV_stateNIL;
  b->line_ = ffewhere_line_unknown ();
  b->col_ = ffewhere_column_unknown ();
}

/* ffestw_kill -- Kill block

   ffestw b;
   ffestw_kill(b);  */

void
ffestw_kill (ffestw b)
{
  assert (b != NULL);
  assert (b->uses_ > 0);

  if (--b->uses_ != 0)
    return;

  ffewhere_line_kill (b->line_);
  ffewhere_column_kill (b->col_);
}

/* ffestw_new -- Create block

   ffestw b;
   b = ffestw_new();  */

ffestw
ffestw_new ()
{
  ffestw b;

  b = (ffestw) malloc_new_kp (malloc_pool_image (), "FFESTW", sizeof (*b));
  b->uses_ = 1;

  return b;
}

/* ffestw_pop -- Pop block off stack

   ffestw_pop();  */

ffestw
ffestw_pop ()
{
  ffestw b;
  ffestw oldb = ffestw_stack_top_;

  assert (oldb != NULL);
  ffestw_stack_top_ = b = ffestw_stack_top_->previous_;
  assert (b != NULL);
  if ((ffewhere_line_is_unknown (b->line_) || ffewhere_column_is_unknown (b->col_))
      && (ffesta_tokens[0] != NULL))
    {
      assert (b->state_ == FFESTV_stateNIL);
      if (ffewhere_line_is_unknown (b->line_))
	b->line_
	  = ffewhere_line_use (ffelex_token_where_line (ffesta_tokens[0]));
      if (ffewhere_column_is_unknown (b->col_))
	b->col_
	  = ffewhere_column_use (ffelex_token_where_column (ffesta_tokens[0]));
    }

  return oldb;
}

/* ffestw_push -- Push block onto stack, return its address

   ffestw b;  // NULL if new block to be obtained first.
   ffestw_push(b);

   Returns address of block if desired, also updates ffestw_stack_top_
   to point to it.

   30-Oct-91  JCB  2.0
      Takes block as arg, or NULL if new block needed.	*/

ffestw
ffestw_push (ffestw b)
{
  if (b == NULL)
    b = ffestw_new ();

  b->next_ = NULL;
  b->previous_ = ffestw_stack_top_;
  b->line_ = ffewhere_line_unknown ();
  b->col_ = ffewhere_column_unknown ();
  ffestw_stack_top_ = b;
  return b;
}

/* ffestw_update -- Update current block line/col info

   ffestw_update();

   Updates block to point to current statement.	 */

ffestw
ffestw_update (ffestw b)
{
  if (b == NULL)
    {
      b = ffestw_stack_top_;
      assert (b != NULL);
    }

  if (ffesta_tokens[0] == NULL)
    return b;

  if (!ffewhere_line_is_unknown (b->line_))
    ffewhere_line_kill (b->line_);
  if (!ffewhere_column_is_unknown (b->col_))
    ffewhere_column_kill (b->col_);
  b->line_ = ffewhere_line_use (ffelex_token_where_line (ffesta_tokens[0]));
  b->col_ = ffewhere_column_use (ffelex_token_where_column (ffesta_tokens[0]));

  return b;
}

/* ffestw_use -- Mark extra use of block

   ffestw b;
   b = ffestw_use(b);  // will always return original copy of b

   Increments use counter for b.  */

ffestw
ffestw_use (ffestw b)
{
  assert (b != NULL);
  assert (b->uses_ != 0);

  ++b->uses_;

  return b;
}
