# include	<stdio.h>
# include	"constants.h"
# include	"globals.h"
# include	<sccs.h>

SCCSID(@(#)cvar.c	8.1	12/31/84)

/*
**  CVAR -- routines to manipulate the c variable trees
**
**	C variable trees are binary trees of cvar structs,
**	with the c_left < c_right with respect to c_id.
*/
/*
**  DECL_CVAR -- Declare a C variable
**
**	Parameters:
**		name -- identifier string (makes its own copy for the tree)
**		type 
**		indir_level -- level of indirection of declaration
**			(- 1 if string)
**		block_level -- 0 - global, else local var
**
**	Returns:
**		none
**
**	Side Effects:
**		allocates a cvar node, and a copy of name, may put a node
**		in a cvar tree (if not previously declared).
**
**	Called By:
**		the c_variable productions of the parser [grammar.y]
*/


decl_cvar(name, type, indir_level, block_level)
char		*name;
int		type;
int		indir_level;
int		block_level;
{
	register struct cvar		*bad_node;
	struct cvar			*dec_var();

	if (bad_node = dec_var(name, type, indir_level, block_level,
				&C_locals, &C_globals))
	{
		yysemerr("re-declared identifier", bad_node->c_id);
		xfree(bad_node->c_id);
		xfree(bad_node);
	}
}

/*
**  DECL_FIELD -- Declare a structures field
**
**	Same as decl_cvar() for fields within C records (structs).
**	NOTE : if a !0 is returned from dec_var() (i.e. the field
**	was already declared) the storage for that node is freed
**	but no error has been comitted, as fields may be re-declared.
*/

decl_field(name, type, indir_level, block_level)
char		*name;
int		type;
int		indir_level;
int		block_level;
{
	register struct cvar		*bad_node;
	struct cvar			*dec_var();

	if (bad_node = dec_var(name, type, indir_level, block_level,
				&F_locals, &F_globals))
	{
		xfree(bad_node->c_id);
		xfree(bad_node);
	}
}
/*
**  DEC_VAR -- declare a C var or field.
**
**	Parameters:
**		same as decl_cvar() & decl_field plus
**		the local and global tree variables.
**
**	Returns:
**		0 -- successful
**		other -- cvar node pointer that couldn't be entered
**			to tree
*/

struct cvar *
dec_var(name, type, indir_level, block_level, local_tree, global_tree)
char		*name;
int		type, indir_level, block_level;
struct cvar	**local_tree, **global_tree;
{
	register struct cvar		*cvarp;
	register			i;
	char				*salloc();

	cvarp = (struct cvar *)nalloc(sizeof *cvarp);
	if (!cvarp)
	{
		yysemerr("unable to allocate space for a variable", name);
		return (0);
	}
	if (!(cvarp->c_id = salloc(name)))
	{
		yysemerr("no space for variable name", name);
		xfree(cvarp);
		return (0);
	}
	cvarp->c_type = type;
	cvarp->c_indir = indir_level;
	cvarp->c_left = cvarp->c_right = 0;
	i = c_enter(cvarp, block_level > 0 ? local_tree : global_tree);
	return (i ? 0 : cvarp);
}
/*
**   C_ENTER -- Enter a cvar node in a cvar tree
**
**	Parameters:
**		node -- the cvar node to insert
**		root -- a pointer to the root pointer
**
**	Returns:
**		1 -- if successful
**		0 -- otherwise (node of same name existed
**
**	Side Effects:
**		If a node of that name didn't exist one is inserted
**
**	Called By:
**		dec_var()
*/

c_enter(node, root)
struct cvar	*node;
struct cvar	**root;
{
	register char		*name;
	register struct cvar	*n, *r;

	r = *root;
	n = node;
	name = n->c_id;
	if (!r)
	{
		*root = n;
		return (1);
	}
	for (;;)
	{
		switch (scompare(name, 0, r->c_id, 0))
		{
			
		  case -1 :
			if (!r->c_left)
			{
				r->c_left = n;
				return (1);
			}
			r = r->c_left;
			break;

		  case 0 :
			yysemerr("identifier re-declared", name);
			xfree(name);
			xfree(n);
			return (0);

		  case 1 :
			if (!r->c_right)
			{
				r->c_right = n;
				return (1);
			}
			r = r->c_right;
			break;
		}
	}
}


/*
**  GET_VAR -- get a cvar node from a local_tree, global_tree pair
**	searching first through the local then the global.
**
**	Parameters:
**		id -- c_id key
**		local_tree -- first tree
**		global_tree -- secomd tree to search
**
**	Returns:
**		0 -- if no node by that name
**		otherwise -- pointer to the node
*/


struct cvar *get_var(id, local_tree, global_tree)
char		*id;
struct cvar	*local_tree, *global_tree;
{
	register char		*name;
	register struct cvar	*tree, *node;
	char			flag;

	flag = 0;
	name = id;
	tree = local_tree;
	for ( ; ; )
	{
		for (node = tree; node; )
		{
			switch (scompare(name, 0, node->c_id, 0))
			{
			  
			  case -1 :
				if (!node->c_left)
					break;
				else
					node = node->c_left;
				continue;

			  case 0 :
				return (node);

			  case 1 :
				if (!node->c_right)
					break;
				else
					node = node->c_right;
				continue;
			}
			break;
		}
		if (!flag)
		{
			flag += 1;
			tree = global_tree;
		}
		else
			return (0);
	}
}
/*
**  GETCVAR -- get the cvar node for a given identifier
**	Looks first in C_locals, then in C_globals.
**
**	Parameters:
**		id -- name of cvar to look for
**
**	Returns:
**		adress of cvar node if found
**		0 -- otherwise
**
**	Requires:
**		C_locals & C_globals -- to search them
*/

struct cvar	*
getcvar(id)
char		*id;
{
	return (get_var(id, C_locals, C_globals));
}

/*
**  GETFIELD -- Same as getcvar() for structure fields
*/

struct cvar	*
getfield(id)
char		*id;
{
	return (get_var(id, F_locals, F_globals));
}


/*
**  FREECVAR & F_CVAR -- Free up storage in a cvar tree
**
**	Freecvar calls f_cvar to free storage for a tree, then
**	0's out the root pointer passed it.
*/

freecvar(rootp)
struct cvar	**rootp;
{
	f_cvar(*rootp);
	*rootp = 0;
}

f_cvar(root)
struct cvar	*root;
{
	if (root)
	{
		f_cvar(root->c_left);
		f_cvar(root->c_right);
		xfree(root->c_id);
		xfree(root);
	}
}
