# include	"crunch.h"

extern Head_p hd_stmt;
extern Head_p	hd_syms;
extern Head_p	hd_funcs;
extern Head_p hd_init;
extern int	block_level;
extern int	inside_struct;
extern int	struct_flag;

Head_p	hd_block;	/* List containing chains of statements in a block. */
Head_p	hd_undef;	/* List of symbols which have been reported */
			/* as being undefined so we dont keep 	    */
			/* printing same error message.		    */
char *strdup();
void	dump_syms PROTO((void));
int	nt PROTO((node_t *, int));
int	print_sym PROTO((symbol_t *, int));
int	get_size PROTO((symbol_t *));
int	get_alignment PROTO((symbol_t *));
void	pr_symtab PROTO((void));

/**********************************************************************/
/*   Structure  giving  the  size  of  a  basic type and the storage  */
/*   alignments. These are correct for the 386/486.		      */
/**********************************************************************/
struct ty_info {
	int	ty_size;
	int	ty_align;
	} ty_info[] = {
	{4,	4},	/* TY_INT 	*/
	{1,	1},	/* TY_CHAR	*/
	{2,	2},	/* TY_SHORT	*/
	{4,	4},	/* TY_VOID	*/
	{4,	4},	/* TY_LONG 	*/
	{8,	4},	/* TY_FLOAT	*/
	{8,	4},	/* TY_DOUBLE	*/
	{4,	4},	/* TY_UNSIGNED 	*/
	{4,	4},	/* TY_SIGNED 	*/
	{4,	4},	/* TY_STRUCT	*/
	{4,	4},	/* TY_UNION	*/
	{4,	4},	/* TY_ENUM	*/
	{4,	4},	/* TY_STRING	*/
	{4,	4},	/* TY_DECLARE	*/
	{4,	4},	/* TY_LIST	*/
	{4,	4},	/* TY_STRUCTI	*/
	{4,	4},	/* TY_UNIONI	*/
	};
	
/**********************************************************************/
/*   Function  to  initialise  symbol  table. May be called multiple  */
/*   times.  Each  time  means free up data structures from previous  */
/*   usage.							      */
/**********************************************************************/
void
init_symtab()
{	register List_p	lp;
	register symbol_t *sp;

	if (hd_syms == NULL)
		return;
	while ((lp = ll_first(hd_syms)) != NULL) {
		sp = (symbol_t *) ll_elem(lp);
		chk_free((void *) sp->name);
		chk_free((void *) sp);
		ll_delete(lp);
		}
}
/************************************************************************
 *    Function  to  add  a symbol to symbol table taking into account	*
 *    current   block   nesting   level.   Also   detects   duplicate	*
 *    definition errors.						*
 ************************************************************************/
symbol_t *
add_symbol(sym, np, type)
char *sym;
node_t	*np;
long	type;
{
	return add_symbol_to_table(&hd_syms, INSERT_AT_FRONT, 
		sym, np, type, (symbol_t *) NULL);
}

/**********************************************************************/
/*   Add a symbol to the specified symbol table.		      */
/**********************************************************************/
symbol_t *
add_symbol_to_table(hd, order, sym, np, type, owner)
Head_p	*hd;
int	order;
char	*sym;
node_t	*np;
long	type;
symbol_t	*owner;	/* Symbol table entry for struct members. */
{	register List_p	lp;
	register symbol_t *sp;
	extern int line_no;
	int	is_struct = (int) (type & TY_MASK);
	long	t;
	int	redef = FALSE;
	static symbol_t null_symbol;
	
	is_struct = type == TY_STRUCT || type == TY_UNION;
	
	if (*hd == NULL)
		*hd = ll_init();
		
	for (lp = ll_first(*hd); lp; lp = ll_next(lp)) {
		sp = (symbol_t *) ll_elem(lp);
		t = sp->s_type & TY_MASK;
		t = t == TY_STRUCT || type == TY_UNION;
		if (sp->name[0] != *sym || strcmp(sp->name, sym) != 0 || 
		    sp->level != block_level)
			continue;
		if (t == is_struct) {
		    	char	buf[BUFSIZ];
		    	if (sp->s_flags & SF_FORWARD) {
				sp->s_flags &= ~SF_FORWARD;
				redef = TRUE;
				goto setup;
				}
# if 0
			if (
			    ((sp->s_type & (TY_MASK | TQ_MASK)) ==
			    (type & (TY_MASK | TQ_MASK))))
			    	return sp;
# endif
		    	yyerror1("Symbol '%s' redefinition error", sym);
			if (sp->s_line_no == line_no)
				return NULL;
			sprintf(buf, " (original definition in file %s line %d)",
				sp->s_filename, sp->s_line_no);
			yyerror1(buf, (char *) NULL);
			return NULL;
			}
		else {
		    	yyerror1("Symbol '%s' redefinition error", sym);
			return NULL;
			}
		}
	sp = (symbol_t *) chk_alloc(sizeof (symbol_t));
	*sp = null_symbol;
	sp->name = strdup(sym);
setup:
	sp->level = block_level;
	sp->s_tree = np;
	sp->s_owner = owner;
	sp->s_type = type;
	sp->s_line_no = line_no;
	sp->s_filename = get_filename(filename);
	if (!redef) {
		if (order == INSERT_AT_END)
			ll_append(*hd, (char *) sp);
		else
			ll_push(*hd, (char *) sp);
		}
	return sp;
}
/************************************************************************
 *    Function  to  check  that a symbol has been predefined. If rsvd	*
 *    is TRUE then allow symbol to be a reserved into macro name.	*
 ************************************************************************/
symbol_t *
lookup_sym(sym, rsvd)
char *sym;
int	rsvd;
{	register List_p lp;
	register symbol_t *sp;
	int	ch = *sym;
	

	/***********************************************/
	/*   First  symbol  in file wont have hd_syms  */
	/*   set  up.  This is ok, since it must be a  */
	/*   function name.			       */
	/***********************************************/
	if (hd_syms == NULL) {
		if (!rsvd && strcmp(sym, "NULL") != 0)
			goto undef_ref;
		return NULL;
		}

	/***********************************************/
	/*   Search   symbol  table  inner  block  to  */
	/*   outer block.			       */
	/***********************************************/
	for (lp = ll_first(hd_syms); lp; lp = ll_next(lp)) {
		sp = (symbol_t *) ll_elem(lp);
		if (sp->name[0] == (char) ch && strcmp(sp->name, sym) == 0) {
			sp->s_flags |= SF_REF;
			return sp;
			}
		}
	if (rsvd)
		return NULL;

	/***********************************************/
	/*   Special case the NULL keywd.	       */
	/***********************************************/
	if (strcmp(sym, "NULL") == 0)
		return NULL;

	/***********************************************/
	/*   Check   list   of  undefined  referenced  */
	/*   symbols  so  we  dont keep printing same  */
	/*   error.				       */
	/***********************************************/
undef_ref:
	if (hd_undef == NULL)
		hd_undef = ll_init();
	for (lp = ll_first(hd_undef); lp; lp = ll_next(lp)) {
		if (strcmp((char *) ll_elem(lp), sym) == 0)
			return NULL;
		}
	yyerror1("Undefined symbol '%s'", sym);
	ll_append(hd_undef, strdup(sym));
	return NULL;
}
void
enter_block()
{	block_t	*bp = (block_t *) chk_alloc(sizeof(block_t));

	block_level++;
	if (hd_block == NULL)
		hd_block = ll_init();
	bp->b_stmt = hd_stmt;
	bp->b_init = hd_init;
	ll_push(hd_block, (char *) bp);
	hd_stmt = NULL;
	hd_init = NULL;
	
}
/************************************************************************
 *    Code  to  remove  symbols  from  symbol  table  from  currently	*
 *    nested block.							*
 ************************************************************************/
node_t	*
exit_block()
{	register List_p lp;
	register symbol_t *sp;
	block_t	*bp;
	node_t	*tree = NULL;
# define	NUM_ELEM	(sizeof types / sizeof types[0])
	static int types[] = {K_INT, K_STRING, K_LIST, K_DECLARE, K_FLOAT};
	node_t	*ty_trees[NUM_ELEM];
	register int j;
	long	ty;
	char	buf[BUFSIZ];
	
	for (j = 0; j < NUM_ELEM; j++)
		ty_trees[j] = NULL;
	if (hd_syms)
		while (lp = ll_first(hd_syms)) {
			sp = (symbol_t *) ll_elem(lp);
			if (sp->level != block_level)
				break;
			if ((sp->s_flags & SF_REF) == 0) {
				sprintf(buf, "unused %s %s", 
					sp->level == 1 ? "parameter" : "variable",
					sp->name);
				yywarn_with_line_no(buf, sp->s_line_no);
				}
			ty = sp->s_type;
			j = ty == TY_INT ? 0 :
			    ty == TY_STRING ? 1 :
			    ty == TY_LIST ? 2 :
			    ty == TY_DECLARE ? 3 :
			    (ty == TY_FLOAT || ty == TY_DOUBLE) ? 4 : -1;
			if ((sp->s_type & SC_MASK) == 0 && j >= 0) {
				/***********************************************/
				/*   Gather  declarations  into  those of the  */
				/*   same type.				       */
				/***********************************************/
				if (ty_trees[j] == NULL)
					ty_trees[j] = string_node(sp->name);
				else
					ty_trees[j] = node((long) K_NOOP, ty_trees[j],
						string_node(sp->name));
				}
			chk_free(sp->name);
			chk_free((char *) sp);
			ll_delete(lp);
			}
			
	/***********************************************/
	/*   Build   a   tree   containing   all  the  */
	/*   symbols  to  be  defined on entry to the  */
	/*   enclosing block.			       */
	/***********************************************/
	for (j = 0; j < NUM_ELEM; j++) {
		if (ty_trees[j] == NULL)
			continue;
		if (tree == NULL)
			tree = node((long) types[j], ty_trees[j], (node_t *) NULL);
		else
			tree = node((long) K_NOOP, tree, 
				node((long) types[j], ty_trees[j], (node_t *) NULL));
		}
		
	/***********************************************/
	/*   Now  append  the  list  of  initializers  */
	/*   for  these  local  variables  (if  there  */
	/*   are any).				       */
	/***********************************************/
	if (hd_init) {
		tree = node((long) K_NOOP, tree,
			node((long) K_INIT, (node_t *) hd_init, (node_t *) NULL));
		hd_init = NULL;
		}
	block_level--;
	bp = (block_t *) ll_elem(ll_first(hd_block));
	hd_stmt = bp->b_stmt;
	hd_init = bp->b_init;
	chk_free((void *) bp);
	ll_pop(hd_block);
	return tree;
}

/**********************************************************************/
/*   Following  function  used  to dump the symbol table when the -S  */
/*   switch  is  used. This is used so that we can get the structure  */
/*   offset  information.  This  isn't strictly a part of the crunch  */
/*   language,  but  since  we  more  or  less  have a full C syntax  */
/*   interpreter,  we  can  use  crunch  for  things  not  otherwise  */
/*   related to crisp/crunch.					      */
/**********************************************************************/
void
dump_syms()
{	register List_p	lp, lp1;
	register symbol_t *sp, *sp1;
	long	type;
	Head_p	hd_str;
	int	offset;
	int	max;

	if (hd_syms == NULL || ll_first(hd_syms) == NULL)
		return;
	hd_str = ll_init();
	
	/***********************************************/
	/*   Make  a  list  of  all  structures -- in  */
	/*   the order they were defined.	       */
	/***********************************************/
	for (lp = ll_first(hd_syms); lp; lp = ll_next(lp)) {
		sp = (symbol_t *) ll_elem(lp);
		type = sp->s_type & TY_MASK;
		if (type != TY_STRUCT && type != TY_UNION)
			continue;
		if (sp->s_owner)
			continue;
		ll_push(hd_str, (char *) sp);
		}
	
	/***********************************************/
	/*   Print   out  all  structure  definitions  */
	/*   first.				       */
	/***********************************************/
	for (lp = ll_first(hd_str); lp; lp = ll_next(lp)) {
		char	*cp;
		sp = (symbol_t *) ll_elem(lp);
		type = sp->s_type & TY_MASK;
		if (sp->s_type == TY_STRUCT)
			cp = "struct";
		else
			cp = "union";
		if (struct_flag)
			printf("\ndefine %s\n", sp->name);
		else
			printf("%s %s {\n", cp, sp->name);
			
		/***********************************************/
		/*   Calculate   base   alignment   of   this  */
		/*   structure  from  first element inside of  */
		/*   it.				       */
		/***********************************************/
		sp->s_align = 0;
		if (sp->s_members && ll_first(sp->s_members)) {
			sp1 = (symbol_t *) ll_elem(ll_first(sp->s_members));
			if (sp1) {
				sp->s_align = get_alignment(sp1);
				}
			else
				sp->s_align = 0;
			}
		offset = 0;
		max = 0;
		for (lp1 = sp->s_members ? ll_first(sp->s_members) : NULL; lp1; ) {
			int	align;
			sp1 = (symbol_t *) ll_elem(lp1);
			if ((sp->s_type & TY_MASK) == TY_UNION) {
				if (offset > max)
					max = offset;
				offset = 0;
				}
			if (struct_flag)
				printf("0x%x=", offset);
			else
				printf("/* %3x */", offset);
			offset += print_sym(sp1, 1);
			if ((lp1 = ll_next(lp1)) == NULL)
				break;
			sp1 = (symbol_t *) ll_elem(lp1);
			type = sp1->s_type & TY_MASK;
			align = get_alignment(sp1);
			if (sp1->s_tree && sp1->s_tree->type == node_keywd &&
			    sp1->s_tree->atom.ival == TO_PTR)
			    	align = 4;
			if (align && offset & (align - 1))
				offset = (offset | (align - 1)) + 1;
			}
		if ((sp->s_type & TY_MASK) == TY_UNION && offset < max)
			offset = max;
		sp->s_size = offset;
		if (!struct_flag)
			printf("\t}; /* size=%x align=%x */\n", offset, sp->s_align);
		}
		
	/***********************************************/
	/*   Print  all  non-structures and structure  */
	/*   members.				       */
	/***********************************************/
	printf("\n");
	for (lp = ll_first(hd_syms); lp; lp = ll_next(lp)) {
		sp = (symbol_t *) ll_elem(lp);
		type = sp->s_type & TY_MASK;
		if (sp->s_owner)
			continue;
		if (type == TY_STRUCT || type == TY_UNION)
			continue;
		print_sym(sp, 0);
		}
}
int
print_sym(sp, tab)
symbol_t	*sp;
int	tab;
{	long	type;
	long	class;
	long	qual;
	int	size;
	char	*tstr;
	
	if (!struct_flag && tab)
		printf("\t");
	type = sp->s_type & TY_MASK;
	class = ((sp->s_type & SC_MASK) >> SC_SHIFT);
	qual = ((sp->s_type & TQ_MASK) >> TQ_SHIFT);
	
	if (struct_flag && !tab) {
		/***********************************************/
		/*   Dont print typedef's in +struct mode.     */
		/***********************************************/
		if (class == SC_TYPEDEF)
			return 0;
		}
	if (!struct_flag && class) {
		char *cp;
		cp = class == SC_AUTO ? "auto " :
		     class == SC_REGISTER ? "register " :
		     class == SC_TYPEDEF ? "typedef " :
		     class == SC_EXTERN ? "extern " :
		     class == SC_STATIC ? "static " : "<class?>";
		printf(cp);
		}
	if (qual) {
		qual += TQ_CONST - 1;
		printf(qual == TQ_CONST ? "const " :
			qual == TQ_VOLATILE ? "volatile " : 
				"<qual?>");
		}
	tstr = type_to_str(type);
	if (struct_flag) {
		/***********************************************/
		/*   Print   struct/union  types  for  global  */
		/*   variables if in +struct mode.	       */
		/***********************************************/
		if (!tab) {
			if (type != TY_STRUCTI && type != TY_UNIONI)
				return 0;
			if (sp->s_susym)
				printf("struct %s ", sp->s_susym->name);
			}
		}
	else {
		printf("%s ", tstr);
		if (type == TY_STRUCTI || type == TY_UNIONI) {
			if (sp->s_susym == NULL)
				printf("<dont-know> ");
			else
				printf("%s ", sp->s_susym->name);
			}
		}
	size = get_size(sp);
	if (sp->s_tree)
		size = nt(sp->s_tree, size);
	if (struct_flag)
		printf("%s\n", sp->name);
	else
		printf("%s;\n", sp->name);
	return size;
}
int
get_size(sp)
symbol_t	*sp;
{
	register long type = sp->s_type & TY_LO_MASK;
	if (type == TY_UNSIGNED || type == TY_SIGNED)
		type = (sp->s_type >> TY_SHIFT) & TY_LO_MASK;
	if (type != TY_STRUCTI && type != TY_UNIONI)
		return ty_info[type].ty_size;
	return sp->s_susym->s_size;
}
int
get_alignment(sp)
symbol_t	*sp;
{
	register long type = sp->s_type & TY_LO_MASK;
	if (type == TY_UNSIGNED || type == TY_SIGNED)
		type = (sp->s_type >> TY_SHIFT) & TY_LO_MASK;
	if (type != TY_STRUCTI && type != TY_UNIONI)
		return ty_info[type].ty_align;
	return sp->s_susym->s_align;
}
char *
type_to_str(type)
long	type;
{	static char *types[] = {
		"<undefined>",
		"int",
		"char", "short", "void", "long", "float", "double",
		"signed", "unsigned", "struct", "union", "enum", 
		"string", "declare", "list", "struct", "union", 
		};
	static char	buf[64];
	long	t;

	type &= TY_MASK;
	t = type & TY_LO_MASK;
	if (t >= sizeof types / sizeof types[0])
		strcpy(buf, "<type?>");
	else {
		strcpy(buf, types[t]);
		}
	t = (type >> TY_SHIFT) & TY_LO_MASK;
	if (t) {
		if (t >= sizeof types / sizeof types[0])
			strcat(buf, " <type?>");
		else {
			strcat(buf, " ");
			strcat(buf, types[t]);
			}
		}
	return buf;
}
int
get_typedef_type(name)
char	*name;
{
	symbol_t *sp = lookup_sym(name, FALSE);
	return sp->s_type & (TQ_MASK | TY_MASK);
}
symbol_t *
lookup_struct(name)
char *name;
{
	symbol_t *sp = lookup_sym(name, TRUE);
	if (sp)
		return sp;
		
	/***********************************************/
	/*   Add forward reference.		       */
	/***********************************************/
	sp = add_symbol(name, (node_t *) NULL, (long) TY_STRUCT);
	sp->s_flags |= SF_FORWARD;
	return sp;
}
/**********************************************************************/
/*   Function called from debugger only.			      */
/**********************************************************************/
void
pr_symtab()
{	register List_p lp;
	register symbol_t *sp;

	if (hd_syms == NULL)
		return;
	for (lp = ll_first(hd_syms); lp; lp = ll_next(lp)) {
		sp = (symbol_t *) ll_elem(lp);
		printf("%d:  %s\n", sp->level, sp->name);
		}
}
