/*
    c99tree - scope and typedef handling
    Copyright (C) 2010  Tibor Palinkas

    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.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "tree.h"
#include "c99tree.h"
#include "scope.h"
#include "genht/hash.h"

scope_t *scope = NULL;

/* --- hash --- */
static int keyeq(char *a, char *b)
{
	return !strcmp(a, b);
}

static unsigned int hash(char *key)
{
	return murmurhash(key, strlen(key));
}

/* --- scope --- */
void scope_enter()
{
	scope_t *s;

	s = malloc(sizeof(scope_t));

	s->typedefs = htsp_alloc(hash, keyeq);
	s->negative_typedefs = htsp_alloc(hash, keyeq);
	s->compound = NULL;

	s->child = NULL;
	s->parent = scope;
	if (scope != NULL)
		scope->child = s;
	scope = s;
}

void scope_leave()
{
	htsp_entry_t *e;
	scope_t *s = scope;

	assert(scope->child == NULL);
	scope = s->parent;
	if (scope != NULL)
		scope->child = NULL;
	for (e = htsp_first(s->typedefs); e; e = htsp_next(s->typedefs, e)) {
		free(e->key);
		Nfree(e->value);
	}
	for (e = htsp_first(s->negative_typedefs); e; e = htsp_next(s->negative_typedefs, e)) {
		free(e->key);
		Nfree(e->value);
	}
	htsp_free(s->typedefs);
	htsp_free(s->negative_typedefs);
	free(s);
}

/* --- typedef --- */

void scope_add_typedef(const char *old, node_t *new)
{
	htsp_set(scope->typedefs, (htsp_key_t)strdup(old), (htsp_value_t)new);
}


const node_t *scope_get_typedef(const char *type)
{
	scope_t *s;
	const node_t *ret;

	/* walk trough all parents as well, return first hit... */
	for(s = scope; s != NULL; s = s->parent) {
		/* if we have a negative typedef, that overrides any later real typedef */
		ret = (const node_t *)htsp_get(s->negative_typedefs, (htsp_key_t)type);
		if (ret != NULL)
			return NULL;

		/* return the first typedef found in any of the current or parent scopes */
		ret = (const node_t *)htsp_get(s->typedefs, (htsp_key_t)type);
		if (ret != NULL)
			return ret;
	}

	/* ...or return NULL if nothing is found (not ever typedef'd) */
	return NULL;
}

void scope_typedef_add(node_t *type, node_t *ids)
{
	node_t *n;

	assert(type->type == T_TYPE);

	for(n = ids; n != NULL; n = n->next) {
		assert(n->type == T_ID);
		scope_add_typedef(n->name, Nalloc(T_TYPECUSTOM, strdup(n->name), verbose_options[VO_TYPEDEF] ? Nclone(type, INV_LOC_NONE) : NULL));
	}
}

void scope_negative_typedef_add(const char *name)
{
	htsp_set(scope->negative_typedefs, (htsp_key_t)strdup(name), Nalloc(T_INVALID, NULL, NULL));
}
