/*
 * symbols.c  -  Symbol maintenance for MGL compiler
 *
 * Copyright (C) 2003-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  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
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: symbols.c,v 1.10 2007/01/06 18:31:29 gkminix Exp $
 */

#include "mknbi.h"
#include "mgl.h"



/*
 *****************************************************************************
 *
 * Global symbol table. It can only be accessed by using the public routines
 * within this module, and doesn't get exported.
 *
 *****************************************************************************
 */

/*
 * Symbol table for the MGL compiler
 */
static struct sym *symtab = NULL;



/*
 * Internal symbol names which should not be accessible by the user are
 * constructed using a '$I' followed by a number. The following variable
 * counts up this number. Note that temporary variables get '$L' as defined
 * in file utils.c.
 */
static unsigned long intcount = 0;

#define MAX_INTCOUNT	65535L		/* maximum no. of internal symbols */



/*
 * In order to find symbols fast we use a hash table. Each entry in the
 * hash table represents a symbol list.
 */
#define HASH_SIZE	256		/* size of hash table */

static struct symlist *hash_table[HASH_SIZE];




/*
 *****************************************************************************
 *
 * Predefined symbols
 *
 *****************************************************************************
 */

/*
 * Definition of internal functions
 */
static struct vardef intargs = { &int_type, CLASS_LOCAL };

static struct {
	char           *name;
	struct funcdef  f;
} functab[] = {
	/* This is a general scalar operation - int types are just dummies */
	{ "pred",	{ 0, 0, 0, CMD_PRED, 1, NULL, &int_type, &intargs } },
	/* This is a general scalar operation - int types are just dummies */
	{ "succ",	{ 0, 0, 0, CMD_SUCC, 1, NULL, &int_type, &intargs } },
	/* This is a general scalar operation - int types are just dummies */
	{ "ord",	{ 0, 0, 0, CMD_ORD,  1, NULL, &int_type, &intargs } },
	{ "odd",	{ 0, 0, 0, CMD_ODD,  1, NULL, &bool_type, &intargs } },
	{ "chr",	{ 0, 0, 0, CMD_CHR,  1, NULL, &char_type, &intargs } },
	{ "abs",	{ 0, 0, 0, CMD_ABS,  1, NULL, &int_type, &intargs } },
	{ "sqr",	{ 0, 0, 0, CMD_SQR,  1, NULL, &int_type, &intargs } },
	{ NULL,		{ 0, 0, 0, CMD_NONE, 0, NULL, NULL, NULL } }
};



/*
 * Symbol names of predefined types
 */
static struct {
	char            *name;
	struct typesdef *t;
} predeftypes[] = {
	{ "integer",	&int_type    },
	{ "string",	&string_type },
	{ "char",	&char_type   },
	{ "boolean",	&bool_type   },
	{ "ipaddr",	&ipaddr_type },
	{ NULL,		NULL         }
};



/*
 * Internally defined constants
 */
static struct {
	char            *name;
	int              numval;
	struct typesdef *t;
} predefconsts[] = {
	{ "maxint",		MAX_INT,	&int_type     },
	{ "true",		1,		&bool_type    },
	{ "false",		0,		&bool_type    },
	{ "nil",		0,		&pointer_type },
	{ NULL,			0,		NULL          }
};




/*
 *****************************************************************************
 *
 * Handling of individual symbols
 *
 *****************************************************************************
 */

/*
 * Compute the hash value of a name
 */
__attribute__((pure)) static int gethash __F((name), const char *name)
{
  int hashval = 0;

#ifdef PARANOID
  if (name == NULL || *name == '\0')
	interror(121, "invalid symbol name");
#endif

  while (*name) {
	hashval = (hashval + (int)(*name)) % HASH_SIZE;
	name++;
  }
  return(hashval);
}



/*
 * Find a symbol
 */
struct sym *findsym __F((name, ref), const char *name AND ref_t ref)
{
  struct symlist *slp;

  if (name == NULL)
	return(NULL);

  slp = hash_table[gethash(name)];
  while (slp != NULL) {
	if (issymname(slp->sym, name) &&
	    (ref == SYMBOL_ANYREF || ref == slp->sym->ref))
		break;
	slp = slp->next;
  }
  return(slp == NULL ? NULL : slp->sym);
}



/*
 * Add a symbol to the symbol table
 */
struct sym *addsym __F((name, ref), const char *name AND ref_t ref)
{
  struct symlist *slp;
  char namebuf[8];
  int i;

  /* Check if we have to create a new internal symbol */
  if (name == NULL) {
	if (intcount >= MAX_INTCOUNT) {
		prnerr("overflow in internal symbol name space");
		nbexit(EXIT_MGL_OVERFLOW);
	}
	sprintf(namebuf, "$I%05lu", intcount++);
  }

  /* Create new hash table entry */
  i = gethash(name == NULL ? namebuf : name);
  slp = (struct symlist *)nbmalloc(sizeof(struct symlist));
  slp->next = hash_table[i];
  hash_table[i] = slp;

  /* Create new symbol table entry */
  slp->sym = (struct sym *)nbmalloc(sizeof(struct sym));
  slp->sym->ref = ref;
  slp->sym->type = nosym;
  slp->sym->name = NULL;
  slp->sym->level = curlevel;
  slp->sym->next = symtab;
  symtab = slp->sym;
  copystr(&slp->sym->name, (name == NULL ? namebuf : name));
  return(slp->sym);
}



/*
 * Check if the symbol is already defined within the current level. If it
 * has been defined in a lower level, we can assign the name again, and have
 * to create a new symbol table entry for it.
 */
struct sym *newsym __F((sp, ref), struct sym *sp AND ref_t ref)
{
  /* Just for safety */
  if (sp == NULL)
	return(NULL);

  /* If it's not defined at all, we can return it unchanged */
  if (isnosym(sp)) {
	sp->ref = ref;
	return(sp);
  }

  /* Check if symbol is defined within the current level and context */
  if (sp->level >= curlevel && sp->ref == ref)
	return(NULL);

  /* Create new entry in symbol table */
  return(addsym(sp->name, ref));
}



/*
 * Delete a single symbol
 */
static void delsym __F((sp, prevsp), struct sym *sp AND struct sym *prevsp)
{
  struct symlist *slp1, *slp2;
  struct typesdef *tp;
  int i;

#ifdef PARANOID
  if (sp == NULL)
	interror(119, "missing symbol pointer in delsym()");
#endif

  /* Don't delete any global symbol */
  if (sp->level < 0)
	return;

  /* Check if the symbol is referenced by an unknown pointer type */
  if (istypesym(sp) && sp->def.t != NULL) {
	tp = typetab;
	while (tp != NULL) {
		if (ispointer(tp) &&
		    tp->def.p.basetype == NULL &&
		    tp->def.p.unknownsym == sp) {
			tp->def.p.basetype = sp->def.t;
			tp->def.p.unknownsym = NULL;
		}
		tp = tp->next;
	}
  }

  /* Find the symbol preceding the current one and remove it from the list */
  if (sp == symtab)
	symtab = sp->next;
  else {
	if (prevsp == NULL) {
		for (prevsp = symtab; prevsp != NULL; prevsp = prevsp->next)
			if (prevsp->next == sp)
				break;
	}
#ifdef PARANOID
	if (prevsp == NULL || prevsp->next != sp)
		interror(120, "invalid symbol pointer in delsym()");
#endif
	prevsp->next = sp->next;
  }

  /* Find the symbol in the hash table, and remove it there as well */
  i = gethash(sp->name);
  slp1 = NULL;
  slp2 = hash_table[i];
  while (slp2 != NULL) {
	if (slp2->sym == sp)
		break;
	slp1 = slp2;
	slp2 = slp2->next;
  }
  if (slp2 != NULL) {
	if (slp1 == NULL)
		hash_table[i] = slp2->next;
	else
		slp1->next = slp2->next;
	free(slp2);
  }

  /* Delete any string in a constant */
  if (isconstsym(sp) &&
      sp->def.c.t->type == EXPR_STRING &&
      sp->def.c.val.s != NULL)
	free(sp->def.c.val.s);

  /* Delete any argument list in a function definition */
  if (isfuncsym(sp) && sp->def.f.args != NULL)
	free(sp->def.f.args);

  /* Now delete the symbol name and the symbol itself */
  free(sp->name);
  free(sp);
}



/*
 * Delete all symbols with higher or equal nesting level than the current
 * level.
 */
void delsymbols __F((ref), ref_t ref)
{
  struct sym *sp1, *sp2, *sp3;

  sp1 = NULL;
  sp2 = symtab;
  while (sp2 != NULL) {
	sp3 = sp2->next;
	if (sp2->level >= curlevel &&
	    (ref == SYMBOL_ANYREF || sp2->ref == ref))
		delsym(sp2, sp1);
	else
		sp1 = sp2;
	sp2 = sp3;
  }
}




/*
 *****************************************************************************
 *
 * Handling of symbol lists
 *
 *****************************************************************************
 */

/*
 * Add a symbol to a symbol list
 */
void addsymlist __F((slp, sp), struct symlist **slp AND struct sym *sp)
{
  struct symlist *tmpslp;

  assert(slp != NULL);
  tmpslp = (struct symlist *)nbmalloc(sizeof(struct symlist));
  tmpslp->sym = sp;
  tmpslp->next = *slp;
  if (*slp != NULL)
	tmpslp->num = (*slp)->num + 1;
  else
	tmpslp->num = 0;
  *slp = tmpslp;
}



/*
 * Create a new symbol list
 */
struct symlist *createsymlist __F((ref), ref_t ref)
{
  struct sym *sp;
  struct symlist *slp = NULL;

  for (sp = symtab; sp != NULL; sp = sp->next)
	if (sp->ref == ref)
		addsymlist(&slp, sp);
  return(slp);
}



/*
 * Delete a symbol list
 */
void delsymlist __F((slist, force), struct symlist *slist AND int force)
{
  struct symlist *slp, *slptmp;

  slp = slist;
  while (slp != NULL) {
	if (force || isnosym(slp->sym))
		delsym(slp->sym, NULL);
	slptmp = slp->next;
	free(slp);
	slp = slptmp;
  }
}




/*
 *****************************************************************************
 *
 * Initialization
 *
 *****************************************************************************
 */

/*
 * Initialize the symbol table processing
 */
void syminit __F_NOARGS
{
  struct sym *sp;
  int i;

  /* Initialize the hash table */
  for (i = 0; i < HASH_SIZE; i++)
	hash_table[i] = NULL;

  /* Preset the list of known types with builtin static type list */
  for (i = 0; predeftypes[i].name != NULL; i++) {
	sp = addsym(predeftypes[i].name, SYMBOL_SYSREF);
	sp->type = typesym;
	sp->def.t = predeftypes[i].t;
  }

  /* Preset the list of internal functions */
  for (i = 0; functab[i].name != NULL; i++) {
	sp = addsym(functab[i].name, SYMBOL_SYSREF);
	sp->type = funcsym;
	sp->def.f = functab[i].f;
  }

  /* Preset the list of internal constants */
  for (i = 0; predefconsts[i].name != NULL; i++) {
	sp = addsym(predefconsts[i].name, SYMBOL_SYSREF);
	sp->type = constsym;
	sp->def.c.t = predefconsts[i].t;
	switch (predefconsts[i].t->type) {
		case EXPR_NUM:
			sp->def.c.val.i = predefconsts[i].numval;
			break;
		case EXPR_BOOL:
			sp->def.c.val.b = predefconsts[i].numval;
			break;
		case EXPR_CHAR:
			sp->def.c.val.c = predefconsts[i].numval;
			break;
		case EXPR_POINTER:
			sp->def.c.val.p = predefconsts[i].numval;
			break;
		default:
			break;
	}
  }
}

