/*
 * 68K/386 32-bit C compiler.
 *
 * copyright (c) 1997, David Lindauer
 * 
 * This compiler is intended for educational use.  It may not be used
 * for profit without the express written consent of the author.
 *
 * It may be freely redistributed, as long as this notice remains intact
 * and either the original sources or derived sources 
 * are distributed along with any executables derived from the originals.
 *
 * The author is not responsible for any damages that may arise from use
 * of this software, either idirect or consequential.
 *
 * V2.05 June 2002
 * David Lindauer, camille@bluegrass.net
 *
 * Credits to Mathew Brandt for original K&R C compiler
 *
 */
/* Handles symbol tables 
 */
#include        <stdio.h>
#include				<malloc.h>
#include				<string.h>
#include        "lists.h"
#include        "expr.h"
#include        "c.h"
#include 				"errors.h"
#include				"lists.h"
#define ROTR(x,bits) (((x << (16 - bits)) | (x >> bits)) & 0xffff)
#define ROTL(x,bits) (((x << bits) | (x >> (16 - bits))) & 0xffff)
#define HASHTABLESIZE 1023

extern TABLE oldlsym;
extern int prm_cplusplus,prm_cmangle;
extern SYM *declclass, *typequal;
HASHREC **defhash=0;

LIST *orderednamespaces;
NAMESPACE *thisnamespace;
NAMESPACE globalnamespace;
HASHREC **gsyms; /*everywhere else this is a table so I don't have to typecast */
TABLE lsyms,defsyms;

void symini(void)
{
	lsyms.head = lsyms.tail = defsyms.head = defsyms.tail = 0;
	if (!defhash) {
  	gsyms = (HASHREC **)malloc(HASHTABLESIZE * sizeof(HASHREC *));
  	defhash = (HASHREC **)malloc(HASHTABLESIZE * sizeof(HASHREC *));
	}
  memset(defhash,0,HASHTABLESIZE * sizeof(HASHREC *));
  memset(gsyms,0,HASHTABLESIZE * sizeof(HASHREC *));
	thisnamespace = &globalnamespace;
	globalnamespace.below = 0;
	orderednamespaces = 0;
/*	switchtonamespace("std"); */
}
static void pushnamespace(NAMESPACE *q)
{
	LIST *list = xalloc(sizeof(LIST));
	list->link = orderednamespaces;
	list->data = q;
	orderednamespaces = list;
	thisnamespace = q;
	gsyms = &q->table;
}
static void popnamespace(void)
{
	if (orderednamespaces->link) {
		thisnamespace = orderednamespaces->link;
		gsyms = &thisnamespace->table;
	}
	
}
void switchtonamespace(char *name)
{
		NAMESPACE *q = thisnamespace->below;
		while (q) {
			if (!strcmp(q->name,name)) {
				pushnamespace(q);
				return;
			}
			q = q->next;
		}
		q = xalloc(sizeof(NAMESPACE));
		q->name = litlate(name);
		if (thisnamespace->below) {
			q->next = thisnamespace->below->next;
			thisnamespace->below->next = q;
		}
		else 
			thisnamespace->below = q;
		pushnamespace(q);
}
void switchfromnamespace(void)
{
	popnamespace();
}



/* Sym tab hash function */	
static unsigned int ComputeHash(char *string,int size)
{
  unsigned int len = strlen(string), rv;
  char *pe = len + string;
  unsigned char blank = ' ';

  rv = len | blank;
  while(len--) {
    unsigned char cback = (unsigned char)(*--pe) | blank;
    rv = ROTR(rv,2) ^ cback;
  }
  return(rv % size);
}
/* Add a hash item to the table */
HASHREC *AddHash(HASHREC *item,HASHREC **table,int size)
{
  int index = ComputeHash(item->key,size);
  HASHREC **p;

  item->link = 0;

  if (*(p = &table[index])) {
    HASHREC *q = *p,*r = *p;
    while (q) {
			r = q;
      if (!strcmp(r->key,item->key))
				return(r);
			q = q->link;
		}
		r->link = item;
  }
  else
    *p = item;
  return(0);
}
/*
 * Find something in the hash table
 */
HASHREC **LookupHash(char *key, HASHREC **table, int size)
{
  int index = ComputeHash(key,size);
  HASHREC **p;

  if (*(p = &table[index])) {
    HASHREC *q= *p;
    while (q) {
      if (!strcmp(q->key, key))
				return(p);
			p = *p;
			q=q->link;
		}
	}
	return(0);
}
void isaccessible(SYM *sp)
{
#ifdef CPLUSPLUS
	if ((sp->value.classdata.cppflags & PF_MEMBER) && sp->parentclass) {
		if (sp->value.classdata.cppflags & PF_UNREACHABLE)
			genclasserror(ERR_NOTACCESSIBLE,sp->name);
		if (sp->parentclass != declclass) {
			if (declclass){
				if (!(sp->value.classdata.cppflags & (PF_PUBLIC | PF_PROTECTED)))
					genclasserror(ERR_NOTACCESSIBLE,sp->name);
			}else if (!(sp->value.classdata.cppflags & PF_PUBLIC))
				genclasserror(ERR_NOTACCESSIBLE,sp->name);
		}
	}
#endif
}
/*
 * Some tables use hash tables and some use linked lists
 * This is the global symbol search routine
 */
SYM     *basesearch(char *na,TABLE *table, int checkaccess)
{
	SYM *thead = table->head;
	SYM **p;
	if (table == gsyms) {
		p=((SYM **)LookupHash(na,gsyms,HASHTABLESIZE));
		if (p) {
			p = *p;
			if (checkaccess)
				isaccessible(p);
		}
		return (SYM *) p;
	}
	else if (table == &defsyms) {
		p=((SYM **)LookupHash(na,defhash,HASHTABLESIZE));
		if (p) {
			p = *p;
			if (checkaccess)
				isaccessible(p);
		}
		return (SYM *) p;
	}
	else
	       while( thead != 0) {
                if(strcmp(thead->name,na) == 0) {
											if (checkaccess)
												isaccessible(thead);
                      return thead;
								}
                thead = thead->next;
                }
        return 0;
}
SYM *search(char *na, TABLE *table)
{
	return basesearch(na,table,TRUE);
}
SYM *losearch(char *na)
{
	SYM *sp;
		sp = search(na,&lsyms);
	if (!sp && declclass)
		 sp = search(na,&declclass->tp->lst);
	return sp;
}
SYM *tcsearch(char *na, int checkaccess)
{
	SYM *sp=0;
	if (!typequal || !(sp = basesearch(na,&typequal->tp->lst,checkaccess))) ;
	return sp;
}
SYM     *gsearch(char *na)
{       SYM     *sp;
        if( (sp = losearch(na)) == 0)
                sp = search(na,gsyms);
        return sp;
}
/* The global symbol insert routine */
void insert(SYM *sp,TABLE *table)

{
	if (table == gsyms) {
		if (AddHash(sp,gsyms,HASHTABLESIZE))
			gensymerror(ERR_DUPSYM,sp->name);
	}
  else if (table == &defsyms) {
		AddHash(sp,defhash,HASHTABLESIZE);
	}
	else if (table == &lsyms) {
		SYM *thead = table->head,*qhead = 0;
		/* Only check the current local block... */
	  while( thead != oldlsym.head) {
        if(strcmp(thead->name,sp->name) == 0) {
              qhead = thead;
					 		break;
					}	
        thead = thead->next;
        }
		if (qhead) 
       gensymerror(ERR_DUPSYM,sp->name);
		else {
		/* Putting local symbols in backwards */
      if( table->head == 0) {
        table->head = table->tail = sp;
      	sp->next = 0;
			}
      else    {
				sp->next = table->head;
				table->head = sp;
      }
		}
	}
	else if( search(sp->name,table) == 0) {
                if( table->head == 0)
                        table->head = table->tail = sp;
                else    {
                        table->tail->next = sp;
                        table->tail = sp;
                        }
                sp->next = 0;
                }
        else
                gensymerror(ERR_DUPSYM,sp->name);
}
