/* << ACE >>

   -- Amiga BASIC compiler --

   ** Symbol Table Management & Code Generation **
   ** Copyright (C) 1998 David Benn
   ** 
   ** This program 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
   ** of the License, or (at your option) any later version.
   **
   ** This program 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 this program; if not, write to the Free Software
   ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

   Author: David J Benn
   Date: 26th October-25th November, 2nd-13th December 1991,
   27th January 1992, 8th-9th, 25th February 1992,
   21st April 1992,
   8th,9th,13th,14th,28th June 1992,
   2nd,3rd,4th,5th,17th,26th,28th,29th July 1992,
   1st-3rd,8th,9th August 1992,
   6th,22nd,29th December 1992,
   13th January 1993,
   2nd,28th February 1993,
   12th,20th April 1993,
   15th-18th,28th December 1993,
   7th January 1994,
   26th February 1994,
   12th June 1994,
   20th August 1994,
   10th September 1994
 */

#include <string.h>
#include "symvar.h"

/* -- general functions -- */
void kill_all_lists (void)
{
  lev = ONE;
  while (lev >= ZERO)
    {
      kill_symtab ();
      --lev;
    }
  puts ("freeing code list...");
  kill_code ();			/* all other lists are freed by cleanup() */
}

/* -- symbol table functions -- */
void new_symtab (void)
{
/* create a new symbol table
   at current level.
 */

  if ((tab_head[lev] = (SYM *) sym_alloc (sizeof (SYM), MEMF_ANY)) == NULL)
    {
      printf ("Can't allocate memory for symbol table!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }
  tab_head[lev]->next = NULL;
}

void kill_symtab (void)
{
/* free memory held by 
   symbol table. 
 */

  free_sym_alloc ();
}

void find_tab_tail (void)
{
/* find the end of the 
   symbol table at current
   level.
 */

  tab_tail = tab_head[lev];
  while (tab_tail->next != NULL)
    tab_tail = tab_tail->next;
}

BOOL exist (char *name, int obj)
{
  int oldlevel;

  oldlevel = lev;

  if ((obj == subprogram) || (obj == function) ||
      (obj == definedfunc) || (obj == extfunc) || (obj == extvar) ||
      (obj == constant) || (obj == structdef))
    lev = ZERO;

  curr_item = tab_head[lev]->next;
  while (curr_item != NULL)
    {
      if ((strcmp (curr_item->name, name) == 0) && (curr_item->object == obj))
	{
	  lev = oldlevel;
	  return (TRUE);
	}
      else
	curr_item = curr_item->next;
    }

  lev = oldlevel;
  return (FALSE);
}

void enter (char *name, int typ, int obj, int dims)
/* enter a symbol into symbol table */
{
  int i;

  /* allocate memory for info' */
  if ((new_item = (SYM *) sym_alloc (sizeof (SYM), MEMF_ANY)) == NULL)
    {
      printf ("Can't allocate memory for symbol table item!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }

  if ((new_item->name = (char *) sym_alloc (strlen (name) + 1, MEMF_ANY)) == NULL)
    {
      printf ("Can't allocate memory for symbol table item!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }

  /* fill the node with info' */
  strcpy (new_item->name, name);
  new_item->type = typ;
  new_item->object = obj;
  new_item->dims = dims;

  /* string defaults */
  if ((typ == stringtype) && (obj != array))
    {
      new_item->new_string_var = TRUE;
      new_item->decl = undeclared;
      new_item->size = MAXSTRLEN;
    }

  if ((obj != label) && (obj != function) && (obj != constant) &&
      (obj != extvar) && (obj != extfunc) && (obj != structdef))
    {
      /* how many bytes to reserve? */
      if ((typ == shorttype) && (obj != array))
	addr[lev] += 2;
      else
	addr[lev] += 4;		/* long, single, string, array, sub, structure */

      new_item->address = addr[lev];
    }
  else
    new_item->address = 0;

  new_item->level = lev;
  new_item->shared = FALSE;	/* shared may be explicitly SET later */

  /* array? -> store index maxima */
  if (obj == array)
    {
      if ((new_item->index = (SHORT *) sym_alloc ((dims + 1) * 2, MEMF_ANY)) == NULL)
	{
	  printf ("Array index storage allocation error.\n");
	  early_exit = TRUE;
	  kill_all_lists ();
	  cleanup ();
	}
      else
	for (i = 0; i <= dims; i++)
	  new_item->index[i] = dimsize[i];
    }

  /* setup for structure definition */
  if (obj == structdef)
    {
      if ((new_item->structmem =
	   (STRUCM *) sym_alloc (sizeof (STRUCM), MEMF_ANY)) == NULL)
	{
	  printf ("Can't allocate memory for an initial structdef node!\n");
	  early_exit = TRUE;
	  kill_all_lists ();
	  cleanup ();
	}
      else
	{
	  new_item->structmem->next = NULL;
	  new_item->size = 0;
	}
    }

  /* link item into symbol table */

  find_tab_tail ();
/* find_tab_tail(tab_head[lev]); */

  tab_tail->next = new_item;
  new_item->next = NULL;
  curr_item = new_item;
}

/*
   void show_table_item(SYM *item)
   {
   printf("%10s\t",item->name);
   showtyp(item->type);
   showobj(item->object);
   printf("%5d\t%5d\t%5d\n",item->dims,item->address,item->level);
   }
 */

/* --code generator functions-- */

void create_lists (void)
{
/* create code, DATA, BSS, XREF and BASIC DATA lists */

  data = (DATA *) alloc (sizeof (DATA), MEMF_ANY);
  if (data == NULL)
    {
      cleanup ();
    }
  else
    {
      curr_data = data;
      data->next = NULL;
    }

  bss = (BSS *) alloc (sizeof (BSS), MEMF_ANY);
  if (bss == NULL)
    {
      cleanup ();
    }
  else
    {
      curr_bss = bss;
      bss->next = NULL;
    }

  xref = (XREF *) alloc (sizeof (XREF), MEMF_ANY);
  if (xref == NULL)
    {
      cleanup ();
    }
  else
    {
      curr_xref = xref;
      xref->next = NULL;
    }

  basdata = (BASDATA *) alloc (sizeof (BASDATA), MEMF_ANY);
  if (basdata == NULL)
    {
      cleanup ();
    }
  else
    {
      curr_basdata = basdata;
      basdata->next = NULL;
    }

  code = (CODE *) alloc_code ("  ", "  ", "  ");	/* first node is a dummy */
  if (code == NULL)
    {
      cleanup ();
    }
  else
    {
      curr_code = code;
      code->next = NULL;
    }
}

BOOL is_a_label (char *opc)
{
  int cc = 0;

  while (opc[cc] != '\0')
    cc++;
  if (opc[cc - 1] == ':')
    return (TRUE);
  else
    return (FALSE);
}

void write_code (CODE * line)
{
  if (strcmp (line->opcode, "nop") != 0)
    {
      if (!is_a_label (line->opcode))
	{
	  fprintf (dest, "\t%s", line->opcode);
	  fprintf (dest, "\t%s", line->srcopr);
	  if (line->destopr[0] != ' ')
	    fprintf (dest, ",%s\n", line->destopr);	/* comma & destopr */
	  else
	    fprintf (dest, "\n");	/* no destopr, so just LF */
	}
      else
	fprintf (dest, "%s\n", line->opcode);	/* label starts in 1st column */
    }
}

BOOL label_undef (CODE * node)
{
  char buf[50];

  if ((strcmp (node->opcode, "jmp") == 0) ||
      (strcmp (node->opcode, "jsr") == 0))
    {
      if (strcmp (node->destopr, "* ") == 0)
	{
	  /* undefined label at time of jmp/jsr */
	  strcpy (buf, node->srcopr);
	  strcat (buf, ":\0");
	  /* has it been defined since jmp/jsr? */
	  if (exist (buf, label))
	    {
	      strcpy (node->destopr, "  ");
	      return (FALSE);
	    }			/* not UNdefined */
	  else
	    {
	      early_exit = TRUE;
	      return (TRUE);	/* undefined */
	    }
	}
      else
	return (FALSE);		/* not UNdefined */
    }
  else
    return (FALSE);		/* not a branch instruction */
}

void undef_label_check (void)
{
  BOOL past_head = FALSE;

  /* check for undefined labels */
  curr_code = code;
  while (curr_code->next != NULL)
    {
      if (past_head)
	{
	  if (label_undef (curr_code))
	    {
	      _error (8);
	      printf ("'%s'\n", curr_code->srcopr);
	    }
	}
      curr_code = curr_code->next;
      if (!past_head)
	past_head = TRUE;
    }
  if (label_undef (curr_code))
    {
      _error (8);
      printf ("'%s'\n", curr_code->srcopr);
    }
}

void kill_code (void)
{
  BOOL past_head = FALSE;
  CODE *curr, *temp;

  curr = code;

  do
    {
      temp = curr;
      curr = curr->next;
      if (past_head)
	{
	  if (!early_exit)
	    write_code (temp);

	  /* free all the struct's memory */
	  free_code (temp);
	}

      if (!past_head)
	past_head = TRUE;
    }
  while (curr != NULL);

  /* free the dummy head node's memory */
  free_code (code);
}

void gen (char *opcode, char *srcopr, char *destopr)
{
  /* allocate memory for a new node & each field */
  if ((new_code = (CODE *) alloc_code (opcode, srcopr, destopr)) == NULL)
    {
      printf ("Can't allocate memory for code node!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }

  /* fill code struct */
  strcpy (new_code->opcode, opcode);
  strcpy (new_code->srcopr, srcopr);
  strcpy (new_code->destopr, destopr);

  new_code->next = NULL;
  curr_code->next = new_code;
  curr_code = curr_code->next;
}

void change (CODE * cx, char *opcode, char *srcopr, char *destopr)
{
  /* free the old fields */
  free_code_members (cx);

  /* allocate memory & insert new instruction & operands */
  if ((BOOL) alloc_code_members (cx, opcode, srcopr, destopr))
    {
      strcpy (cx->opcode, opcode);
      strcpy (cx->srcopr, srcopr);
      strcpy (cx->destopr, destopr);
    }
  else
    {
      printf ("Can't allocate memory for CODE node fields!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }
}

/* --DATA list functions-- */

BOOL exist_DATA (char *name)
{
  DATA *curr;
  curr = data->next;
  while (curr != NULL)
    {
      if (strcmp (curr->name, name) == 0)
	return (TRUE);
      else
	curr = curr->next;
    }
  return (FALSE);
}

void enter_DATA (char *name, char *literal)
{
  if (exist_DATA (name))
    return;			/* already exists */

  /* allocate memory for a new node & each field */
  if ((new_data = (DATA *) alloc (sizeof (DATA), MEMF_ANY)) == NULL)
    {
      printf ("Can't allocate memory for DATA node!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }

  if ((new_data->name = (char *) alloc (strlen (name) + 1, MEMF_ANY)) == NULL)
    {
      printf ("Can't allocate memory for DATA node name field!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }

  if ((new_data->literal = (char *) alloc (strlen (literal) + 1, MEMF_ANY)) == NULL)
    {
      printf ("Can't allocate memory for DATA node literal field!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }

  /* fill DATA struct */
  strcpy (new_data->name, name);
  strcpy (new_data->literal, literal);

  new_data->next = NULL;
  curr_data->next = new_data;
  curr_data = curr_data->next;
}

void write_data (void)
{
  BOOL past_head = FALSE;
  DATA *curr, *temp;

  curr = data;
  if (curr->next != NULL)
    {
      fprintf (dest, "\n\tSECTION data,DATA\n\n");
    }

  do
    {
      temp = curr;
      curr = curr->next;
      if (past_head)
	{
	  fprintf (dest, "%s\t%s\n", temp->name, temp->literal);
	}
      if (!past_head)
	past_head = TRUE;
    }
  while (curr != NULL);
}

/* --BSS list functions-- */

BOOL exist_BSS (char *name)
{
  BSS *curr;
  curr = bss->next;
  while (curr != NULL)
    {
      if (strcmp (curr->name, name) == 0)
	return (TRUE);
      else
	curr = curr->next;
    }
  return (FALSE);
}

void enter_BSS (char *name, char *store)
{
  /* ignore if already exists, except if name is "  " 
     which is used for structure declarations */
  if ((exist_BSS (name)) && (strcmp (name, "  ") != 0))
    return;

  /* allocate memory for a new node & each field */
  if ((new_bss = (BSS *) alloc (sizeof (BSS), MEMF_ANY)) == NULL)
    {
      printf ("Can't allocate memory for BSS node!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }

  if ((new_bss->name = (char *) alloc (strlen (name) + 1, MEMF_ANY)) == NULL)
    {
      printf ("Can't allocate memory for BSS node name field!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }

  if ((new_bss->store = (char *) alloc (strlen (store) + 1, MEMF_ANY)) == NULL)
    {
      printf ("Can't allocate memory for BSS node literal field!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }

  /* fill BSS struct */
  strcpy (new_bss->name, name);
  strcpy (new_bss->store, store);

  new_bss->next = NULL;
  curr_bss->next = new_bss;
  curr_bss = curr_bss->next;
}

void write_bss (void)
{
  BOOL past_head = FALSE;
  BSS *curr, *temp;

  curr = bss;
  if (curr->next != NULL)
    {
      fprintf (dest, "\n\tSECTION mem,BSS\n\n");
    }

  do
    {
      temp = curr;
      curr = curr->next;
      if (past_head)
	{
	  fprintf (dest, "%s\t%s\n", temp->name, temp->store);
	}
      if (!past_head)
	past_head = TRUE;
    }
  while (curr != NULL);
}

/* --XREF list functions-- */

BOOL exist_XREF (char *name)
{
  XREF *curr;

  curr = xref->next;
  while (curr != NULL)
    {
      if (strcmp (curr->name, name) == 0)
	return (TRUE);
      else
	curr = curr->next;
    }
  return (FALSE);
}

void enter_XREF (char *name)
{
  if (exist_XREF (name))
    return;			/* already exists */

  if (strcmp (name, "_DOSBase") == 0)
    dosused = TRUE;
  if (strcmp (name, "_MathBase") == 0)
    mathffpused = TRUE;
  if (strcmp (name, "_MathTransBase") == 0)
    mathtransused = TRUE;
  if (strcmp (name, "_GfxBase") == 0)
    gfxused = TRUE;
  if (strcmp (name, "_IntuitionBase") == 0)
    intuitionused = TRUE;
  if (strcmp (name, "_TransBase") == 0)
    translateused = TRUE;

  /* allocate memory for a new node & name field */
  if ((new_xref = (XREF *) alloc (sizeof (XREF), MEMF_ANY)) == NULL)
    {
      printf ("Can't allocate memory for XREF node!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }

  if ((new_xref->name = (char *) alloc (strlen (name) + 1, MEMF_ANY)) == NULL)
    {
      printf ("Can't allocate memory for XREF node name field!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }

  /* fill XREF struct */
  strcpy (new_xref->name, name);

  new_xref->next = NULL;
  curr_xref->next = new_xref;
  curr_xref = curr_xref->next;
}

void write_xrefs (void)
{
  BOOL past_head = FALSE;
  XREF *curr, *temp;

  fprintf (dest, "\n");

  curr = xref;
  do
    {
      temp = curr;
      curr = curr->next;
      if (past_head)
	{
	  /* xdef or xref? */
	  if (temp->name[0] != '*')
	    {
	      /* XREF */
	      fprintf (dest, "\txref %s\n", temp->name);
	    }
	  else
	    {
	      /* XDEF -> first replace '*' with '_' */
	      temp->name[0] = '_';
	      fprintf (dest, "\txdef %s\n", temp->name);
	    }
	}
      if (!past_head)
	past_head = TRUE;
    }
  while (curr != NULL);
}

/* --BASIC DATA list functions-- */

void enter_BASDATA (char *literal)
{
  basdatapresent = TRUE;

  /* allocate memory for a new node & data */
  if ((new_basdata = (BASDATA *) alloc (sizeof (BASDATA), MEMF_ANY)) == NULL)
    {
      printf ("Can't allocate memory for BASIC DATA node!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }

  if ((new_basdata->literal = (char *) alloc (strlen (literal) + 1, MEMF_ANY)) == NULL)
    {
      printf ("Can't allocate memory for BASIC DATA node literal field!\n");
      early_exit = TRUE;
      kill_all_lists ();
      cleanup ();
    }

  /* fill BASDATA struct */
  strcpy (new_basdata->literal, literal);

  new_basdata->next = NULL;
  curr_basdata->next = new_basdata;
  curr_basdata = curr_basdata->next;
}

void write_basdata (void)
{
  BOOL past_head = FALSE;
  BASDATA *curr, *temp;

  curr = basdata;

  if (curr->next != NULL)
    {
      fprintf (dest, "\n_BASICdata:\n");
    }

  do
    {
      temp = curr;
      curr = curr->next;
      if (past_head)
	{
	  fprintf (dest, "\t%s\n", temp->literal);
	}
      if (!past_head)
	past_head = TRUE;
    }
  while (curr != NULL);
}


/* --structure functions-- */

void find_structmem_tail (SYM * symtabitem)
{
/* find end of structdef member list. */
  tail_structmem = symtabitem->structmem;
  while (tail_structmem->next != NULL)
    tail_structmem = tail_structmem->next;
}

BOOL structmem_exist (SYM * symtabitem, char *name)
{
/* seek a structure 
   member. 
 */

  curr_structmem = symtabitem->structmem->next;		/* head has no member data */

  while (curr_structmem != NULL)
    {
      if (strcmp (curr_structmem->name, name) == 0)
	return (TRUE);
      else
	curr_structmem = curr_structmem->next;
    }

  return (FALSE);
}

void add_struct_member (SYM * symtabitem, char *name, int mtype, SYM * structtype)
{
/* add a member to a
   structure definition.
 */

  if (structmem_exist (symtabitem, name))
    _error (61);
  else
    {
      /* add a unique member */
      if ((new_structmem = (STRUCM *) alloc (sizeof (STRUCM), MEMF_ANY)) == NULL)
	{
	  printf ("Can't allocate memory for structdef node!\n");
	  early_exit = TRUE;
	  kill_all_lists ();
	  cleanup ();
	}
      else
	{
	  /* enter member data */
	  strcpy (new_structmem->name, name);
	  new_structmem->type = mtype;
	  new_structmem->offset = symtabitem->size;

	  /* link member into list */
	  find_structmem_tail (symtabitem);

	  new_structmem->next = NULL;
	  tail_structmem->next = new_structmem;
	  curr_structmem = new_structmem;

	  /* increment size of structure 
	     and hence, find next offset. 
	   */
	  switch (mtype)
	    {
	    case bytetype:
	      symtabitem->size += 1;
	      break;
	    case shorttype:
	      symtabitem->size += 2;
	      break;
	    case longtype:
	      symtabitem->size += 4;
	      break;
	    case singletype:
	      symtabitem->size += 4;
	      break;
	    case stringtype:
	      symtabitem->size += MAXSTRLEN;
	      curr_structmem->strsize = MAXSTRLEN;
	      break;
	    case structure:
	      symtabitem->size += structtype->size;
	      curr_structmem->strsize = structtype->size;
	      break;
	    }
	}
    }
}
