/*
 * picasm -- symtab.c
 *
 * Copyright 1995-2004 Timo Rossi, <trossi@iki.fi>
 * See the file LICENSE for license terms.
 *
 * symbol table handling
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "picasm.h"
#include "symtab.h"

#define HASH_TABLE_SIZE 127

typedef struct symbol *symtable[HASH_TABLE_SIZE];

/* structure for list of local symbol tables */
struct localtab {
    struct localtab *next;
    struct patch *patch_list;
    symtable table;
};

static symtable global_symbol_table;
static struct localtab *local_table_list;

/*
 * Compute a hash value from a string
 */
static unsigned int
hash(char *str)
{
    unsigned int h, a;

    h = 0;
    while((a = *str++) != '\0')
    {
	h = 17*h + a;
    }
    return h % HASH_TABLE_SIZE;
}

/*
 * Initialize global and local symbol tables
 */
void
init_symtab(void)
{
    struct symbol **sym;
    int n;

    for(sym = global_symbol_table, n = HASH_TABLE_SIZE;
	n-- > 0; *sym++ = NULL)
	;

    local_table_list = NULL;
}

/*
 * Add a new local symbol table
 */
void add_local_symtab(void)
{
    struct localtab *tab;
    struct symbol **symp;
    int n;

    tab = mem_alloc(sizeof(struct localtab));

    for(symp = &tab->table[0], n = HASH_TABLE_SIZE;
	n-- > 0; *symp++ = NULL)
	;

    tab->patch_list = NULL;
    local_patch_list_ptr = &tab->patch_list;
    tab->next = local_table_list;
    local_table_list = tab;
    local_level++;
}

/*
 * Remove a local symbol table.
 * The caller should check that local_level > 0 before calling this
 */
void remove_local_symtab(void)
{
    int i;
    struct localtab *tab;
    struct symbol *sym, *sym2;

    tab = local_table_list;

    for(i = 0; i < HASH_TABLE_SIZE; i++)
    {
	for(sym = tab->table[i]; sym != NULL; sym = sym2)
	{
	    sym2 = sym->next;
	    mem_free(sym);
	}
    }

    local_table_list = tab->next;
    if(local_table_list != NULL)
	local_patch_list_ptr = &local_table_list->patch_list;

    mem_free(tab);
    local_level--;
}

/*
 * Add a new symbol to the symbol table
 */
struct symbol *
add_symbol(char *name, int tab)
{
    struct symbol *sym;
    symtable *table;
    int i;

    table = (tab == SYMTAB_LOCAL ? &local_table_list->table :
	     &global_symbol_table);

    if((sym = mem_alloc(sizeof(struct symbol) + strlen(name))) == NULL)
	return NULL;

    i = hash(name);
    sym->next = (*table)[i];
    (*table)[i] = sym;

    sym->type2 = SYMT_UNKNOWN;
    strcpy(sym->name, name);

/* the caller must fill the value, type and flags fields */

    return sym;
}

/*
 * Try to find a symbol from the symbol table
 */
struct symbol *
lookup_symbol(char *name, int tab)
{
    symtable *table;
    struct symbol *sym;
    int i;

    table = (tab == SYMTAB_LOCAL ? &local_table_list->table :
	     &global_symbol_table);

    i = hash(name);

    for(sym = (*table)[i]; sym != NULL; sym = sym->next)
    {
	if(strcmp(sym->name, name) == 0)
	    return sym;
    }

    return NULL;
}

/*
 * symbol table output for listing (global symbols only)
 *
 * if level >= 2, all symbols are displayed, else symbols beginning
 * with '__' are omitted.
 *
 */
void dump_symtab(FILE *fp, int level)
{
    int i;
    struct symbol *sym, *nsym, *s0, *s1;
    struct symbol *syms = NULL;
  
    for(i = 0; i < HASH_TABLE_SIZE; i++)
    {
	for(sym = global_symbol_table[i]; sym != NULL; sym = nsym)
	{
	    nsym = sym->next;

	    for(s0 = NULL, s1 = syms; s1 != NULL; s0 = s1, s1 = s1->next)
	    {
		if(strcmp(s1->name, sym->name) > 0)
		    break;
	    }
      
	    if(s0 == NULL)
	    {
		sym->next = syms;
		syms = sym;
	    }
	    else
	    {
		sym->next = s0->next;
		s0->next = sym;
	    }
	}
    }

    fputs("\n\nSymbol Table:\nname                 decimal    hex\n", fp);

    for(sym = syms; sym != NULL; sym = sym->next)
    {
	if(level < 2 && sym->name[0] == '_' && sym->name[1] == '_')
	    continue;

	switch(sym->type)
	{
	case SYM_MACRO:
	    fprintf(fp, "%-20s   MACRO\n", sym->name);
	    break;

	case SYM_FORWARD:
	    fprintf(fp, "%-20s  UNDEFINED\n", sym->name);
	    break;

	case SYM_SET:
	    fprintf(fp, "%-20s  %6ld  0x%04lx (set)\n",
		    sym->name, sym->v.value, sym->v.value);
	    break;

	case SYM_DEFINED:
	    fprintf(fp, "%-20s  %6ld  0x%04lx",
		    sym->name, sym->v.value, sym->v.value);
	    switch(sym->type2)
	    {
	    case SYMT_LABEL:
		fputs(" (label)", fp);
		break;

	    case SYMT_REGFILE:
		fputs(" (regfile)", fp);
		break;
	    }
	    fputc('\n', fp);
	    break;

	default:
	    fprintf(fp, "%-20s   (type 0x%x ??" "?)\n", sym->name, sym->type);
	    break;
	}
    }
}


syntax highlighted by Code2HTML, v. 0.9.1