/************************************************************************/
/*                                                                      */
/* Copyright (c) 1988 -                                                 */
/*     The Rector and Visitors of the University of Virginia            */
/*                                                                      */
/* This code may not be distributed further without permission          */
/*      from the University of Virginia.                                */
/* This code is distributed WITHOUT ANY WARRANTY.  No claims are        */
/*      made as to whether it serves any particular purpose or even     */
/*      works at all.                                                   */
/*                                                                      */
/************************************************************************/

/*
 * This file has the code that stores and retrieves identifiers
 * from the name table.  The name table is an array of linked
 * lists. A simple hash function on the identfier string yields
 * an index to the proper linked list.  Although one identifier may
 * appear in multiple scopes, it is stored only once in the name table
 * and never removed.  Note that Pascal reserved words are also stored
 * in the name table for quick lookup.  Each name table entry is an
 * IDENT struct, comprised of these fields:
 *
 * id_name (char*) the name of the identifier.  Always lower case.  This
 *     also contains the digit-sequence of a declared Pascal label.
 *
 * id_next (IDENT*) pointer to next name table entry on linked list.
 *
 * id_symbol (int) symbolic value of this name.  Most names are
 *     identifiers with id_symbol == SY_IDENT.  Reserved words have
 *     other values, eg "else" has SY_ELSE.
 *
 * id_object (OBJECT*) points to the most recent binding of this name
 *     to an OBJECT in the symbol table.  This speeds the search for
 *     the current symbol table entry associated with this name.
 *
 * id_cacherev (int) This field must match extern int ob_cacherev
 *     in order to use id_object.  Whenever the static scope level
 *     changes, ob_cacherev is incremented.  This forces the next
 *     search for a symbol with this name to be linear, but id_cacherev
 *     and id_object get updated so later searches are direct.
 */

#include <string.h>
#include "types.h"
#include "ident.h"
#include "scan.h"
#include "error.h"

#define ID_TABSIZ 631
#define ID_CHUNK  50

/* id_tab is the name table array */

static IDENT
	*id_tab[ID_TABSIZ],
	nonamebuf = { "", SY_IDENT, ID_NULL, OB_NULL, 0 }
;

IDENT
	*ident_in,
	*id_noname = &nonamebuf
;


/*
 * id_alloc() allocates space for a name table entry
 */

static IDENT *
id_alloc()
{
	static unsigned count=0;
	static IDENT *next;
	char *calloc();

	if (count == 0) {
		next = (IDENT *) calloc(count=ID_CHUNK, sizeof(IDENT));
		if (next == ID_NULL) {
			sprintf(err_text, err_nomem, "idalloc()");
			err_msg(E_INTERNAL, E_CURLINE);
		}
	}
	count--;
	return(next++);
}

/*
 * strsave() saves a string in memory obtained from malloc()
 * Gets memory in chunks to reduce calls to malloc().
 */

#define STR_CHUNK 2048
char *
strsave (name)
	char *name;
{
	static char *start = (char*)0, *limit = (char*)0;
	register char *s, *p;
	char *malloc();

	for(;;) {
		s = name;
		p = start;
		while (*s && p < limit) {
			*p++ = *s++;
		}
		if (*s || !p) {  /* string doesn't fit */
			if (!(start = malloc(STR_CHUNK))) {
				sprintf(err_text, err_nomem, "strsave()");
				err_msg(E_INTERNAL, E_CURLINE);
			}
			limit = start + STR_CHUNK - 1;
			continue;
		}
		*p++ = '\0';
		s = start;
		start = p;
		return(s);
	}
}

/*
 * id_hash() hashes an identifier to a name table index
 */

static int
id_hash(s)
	register char *s;
{
	unsigned register h = 0;
	while(*s) {
		h += *s++;
	}
	return (h % ID_TABSIZ);
}

/*
 * id_get() looks up an identifier in the name table and builds
 * a new entry if not found.  Returns a pointer to the entry.
 */
IDENT *
id_get(name, symbol)
	char *name;
	int symbol;
{
	register IDENT *p, **t;
	register int cmp;

	t = id_tab + id_hash(name);
	if (*t) {
		for ( p = *t; (cmp = strcmp(p->id_name, name)) && p->id_next;
			p = p->id_next)
		{
		}
		if (cmp)
			p = p->id_next = id_alloc();
		else
			return (p);		/* a hit */
	}
	else {
		*t = p = id_alloc();
	}
	p->id_name = (symbol==SY_IDENT) ? strsave(name) : name;
	p->id_symbol = symbol;
	p->id_cacherev = 0;
	p->id_next = ID_NULL;
	return(p);
}
