/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 */
/* $Id$ */
/*	DUMP ROUTINES	*/


#include	"parameters.h"
#ifdef	DEBUG
#include	<ack_string.h>
#include	<alloc.h>
#include	<flt_arith.h>
#include	"arith.h"
#include	"stack.h"
#include	"def.h"
#include	"idf.h"
#include	"type.h"
#include	"proto.h"
#include	"struct.h"
#include	"field.h"
#include	"print.h"
#include	"Lpars.h"
#include	"label.h"
#include	"expr.h"
/*#include	"static.h"*/
#include	"declar.h"

/*	Some routines (symbol2str, type2str, qual2str) which should have
 *	yielded strings are written to yield a pointer to a transient piece
 *	of memory, containing the string, since this is the only reasonable
 *	thing to do in C. `Transient' means that the result may soon
 *	disappear, which is generally not a problem, since normally it is
 *	consumed immediately. Sometimes we need more than one of them, and
 *	MAXTRANS is the maximum number we will need simultaneously.
 */
#define	MAXTRANS	6

extern char options[];


extern struct idf *idf_hashtable[];
extern char *symbol2str();

enum sdef_kind {selector, field};		/* parameter for dumpsdefs */

static int dumplevel;

/* Forward declarations */
static void dumpstack(void);
static char *next_transient(void);
static char *qual2str(int);
static char *type2str(register struct type *);
static void p1_indent(register int);
static void dumpdefs(register struct def *, int);
void dumpidf(register struct idf *, int);
void dumptags(register struct tag *);
void dumptype(register struct type *);
void dumpsdefs(register struct sdef *, enum sdef_kind);
static void p1_expr(int, register struct expr *);

void newline(void)
{
	register int dl = dumplevel;
	
	print("\n");
	while (dl >= 2)	{
		print("\t");
		dl -= 2;
	}
	if (dl)
		print("    ");
}



void dumpidftab(char msg[], int opt)
{
	/*	Dumps the identifier table in readable form (but in
		arbitrary order).
		Unless opt & 1, macros are not dumped.
		Unless opt & 2, reserved identifiers are not dumped.
		Unless opt & 4, universal identifiers are not dumped.
	*/

	print(">>> DUMPIDF, %s (start)", msg);
	dumpstack();
	idfappfun(dumpidf, opt);
	newline();
	print(">>> DUMPIDF, %s (end)\n", msg);
}

static void dumpstack(void)
{
	/*	Dumps the identifier stack, starting at the top.
	*/
	register struct stack_level *stl = local_level;
	
	while (stl)	{
		register struct stack_entry *se = stl->sl_entry;
		
		newline();
		print("%3d: ", stl->sl_level);
		while (se)	{
			print("%s ", se->se_idf->id_text);
			se = se->next;
		}
		stl = stl->sl_previous;
	}
	print("\n");
}

void dumpidf(register struct idf *idf, int opt)
{
	/*	All information about the identifier idf is divulged in a
		hopefully readable format.
	*/
	int started = 0;
	
	if (!idf)
		return;
	if ((opt&2) && idf->id_reserved)	{
		if (!started++)	{
			newline();
			print("%s:", idf->id_text);
		}
		print(" reserved: %d;", idf->id_reserved);
	}
	if (idf->id_def && ((opt&4) || idf->id_def->df_level))	{
		if (!started++)	{
			newline();
			print("%s:", idf->id_text);
		}
		dumpdefs(idf->id_def, opt);
	}
	if (idf->id_sdef)	{
		if (!started++)	{
			newline();
			print("%s:", idf->id_text);
		}
		dumpsdefs(idf->id_sdef, selector);
	}
	if (idf->id_tag)	{
		if (!started++)	{
			newline();
			print("%s:", idf->id_text);
		}
		dumptags(idf->id_tag);
	}
}

void dumpdefs(register struct def *def, int opt)
{
	dumplevel++;
	while (def && ((opt&4) || def->df_level))	{
		newline();
		print("L%d: %s %s%stype%s %lo; ",
			def->df_level,
			symbol2str(def->df_sc),
			def->df_initialized ? "init'd " : "",
			def->df_used ? "used " : "",
			def->df_sc == ENUM ? ", =" : " at",
			def->df_address
		);
		print("%s, line %u",
			def->df_file ? def->df_file : "NO_FILE", def->df_line);
		dumptype(def->df_type);
		def = def->next;
	}
	dumplevel--;
}

void dumptags(register struct tag *tag)
{
	dumplevel++;
	while (tag)	{
		register struct type *tp = tag->tg_type;
		register int fund = tp->tp_fund;

		newline();
		print("L%d: %s %s",
			tag->tg_level,
			fund == STRUCT ? "struct" :
			fund == UNION ? "union" :
			fund == ENUM ? "enum" : "<UNKNOWN>",
			tp->tp_idf->id_text
		);
		if (is_struct_or_union(fund))	{
			print(" {");
			dumpsdefs(tp->tp_sdef, field);
			newline();
			print("}");
		}
		print(";");
		tag = tag->next;
	}
	dumplevel--;
}

void dumpsdefs(register struct sdef *sdef, enum sdef_kind sdk)
{
	/*	Since sdef's are members of two chains, there are actually
		two dumpsdefs's, one following the chain of all selectors
		belonging to the same idf, starting at idf->id_sdef;
		and the other following the chain of all selectors belonging
		to the same struct, starting at stp->tp_sdef.
	*/

	dumplevel++;
	while (sdef)	{
		newline();
		print("L%d: ", sdef->sd_level);
#ifndef NOBITFIELD
		if (sdk == selector)
#endif /* NOBITFIELD */
			print("selector %s at offset %lu in %s;",
				type2str(sdef->sd_type),
				sdef->sd_offset, type2str(sdef->sd_stype)
			);
#ifndef NOBITFIELD
		else	print("field %s at offset %lu;",
				type2str(sdef->sd_type), sdef->sd_offset
			);
#endif /* NOBITFIELD */
		sdef = (sdk == selector ? sdef->next : sdef->sd_sdef);
	}
	dumplevel--;
}

void dumpproto(register struct proto *pl)
{
	register struct type *type;
	register int argcnt = 0;

	newline();
	print("dump proto type list (start)");
	newline();
	while (pl) {
		print("%d: %s", argcnt++,
			pl->pl_flag & PL_FORMAL ?
			(pl->pl_flag & PL_VOID ? "void" : "formal")
			: (pl->pl_flag & PL_ELLIPSIS
				? "ellipsis" : "unknown" ));
		newline();
		if ( (type = pl->pl_type) ){
			dumptype(type);
			newline();
		}
		if (pl->pl_idf) {
			dumplevel++;
			print("idf:");
			dumpidf(pl->pl_idf, 7);
			dumplevel--;
		}
		newline();
		pl = pl->next;
	}
	print("dump proto type list (end)\n");
}

void dumptype(register struct type *tp)
{
	int ops = 1;

	dumplevel++;
	newline();
	if (!tp) {
		print("<NILTYPE>");
		newline();
		dumplevel--;
		return;
	}

	print("(@%lx, #%ld, &%d) ", tp, (long)tp->tp_size, tp->tp_align);

	while (ops)	{
		print("%s", qual2str(tp->tp_typequal));
		switch (tp->tp_fund)	{
		case POINTER:
			print("pointer to ");
			break;
		case ARRAY:
			print("array [%ld] of ", tp->tp_size);
			break;
		case FUNCTION:
			print("function ");
			if (tp->tp_proto) {
				print("with prototype");
				dumplevel++;
				dumpproto(tp->tp_proto);
				dumplevel--;
				newline();
			}
			print("yielding ");
			break;
		default:
			print("%s%s ", tp->tp_unsigned ? "unsigned " : "",
				       symbol2str(tp->tp_fund));
			if (tp->tp_idf)
				print("%s ", tp->tp_idf->id_text);
#ifndef NOBITFIELD
			if (tp->tp_fund == FIELD && tp->tp_field)	{
				struct field *fd = tp->tp_field;
				
				print("[s=%ld,w=%ld] of ",
					fd->fd_shift, fd->fd_width);
			}
			else
#endif /* NOBITFIELD */
			ops = 0;
			break;
		}
		if (ops) tp = tp->tp_up;
	}
	dumplevel--;
}

static char *type2str(register struct type *tp)
{
	/*	Yields a pointer to a one-line description of the type tp.
	*/
	char *buf = next_transient();
	int ops = 1;

	buf[0] = '\0';
	if (!tp)	{
		sprint(buf, "<NILTYPE>");
		return buf;
	}
	sprint(buf, "%s(@%lx, #%ld, &%d) ",
			buf, tp, (long)tp->tp_size, tp->tp_align);

	while (ops)	{
		sprint(buf, "%s%s", buf, qual2str(tp->tp_typequal));
		switch (tp->tp_fund)	{
		case POINTER:
			sprint(buf, "%spointer to ", buf);
			break;
		case ARRAY:
			sprint(buf, "%sarray [%ld] of ", buf, tp->tp_size);
			break;
		case FUNCTION:
			sprint(buf, "%sfunction yielding ", buf);
			break;
		default:
			sprint(buf, "%s%s%s ", buf,
					tp->tp_unsigned ? "unsigned " : "",
					symbol2str(tp->tp_fund)
			);
			if (tp->tp_idf)
				sprint(buf, "%s %s ", buf,
					tp->tp_idf->id_text);
#ifndef NOBITFIELD
			if (tp->tp_fund == FIELD && tp->tp_field)	{
				struct field *fd = tp->tp_field;
				
				sprint(buf, "%s [s=%ld,w=%ld] of ", buf,
					fd->fd_shift, fd->fd_width);
			}
			else
#endif /* NOBITFIELD */
			ops = 0;
			break;
		}
		if (ops) tp = tp->tp_up;
	}
	return buf;
}

static char *qual2str(int qual)
{
	char *buf = next_transient();

	*buf = '\0';
	if (qual == 0)
		sprint(buf, "(none)");
	if (qual & TQ_CONST)
		sprint(buf, "%sconst ", buf);
	if (qual & TQ_VOLATILE)
		sprint(buf, "%svolatile ", buf);

	return qual == 0 ? "" : buf;
}

GSTATIC char trans_buf[MAXTRANS][300];

static char *		/* the ultimate transient buffer supplier */
next_transient(void)
{
	static int bnum;

	if (++bnum == MAXTRANS)
		bnum = 0;
	return trans_buf[bnum];
}

void print_expr(char msg[], struct expr *expr)
{
	/*	Provisional routine to print an expression preceded by a
		message msg.
	*/
	if (options['x'])	{
		print("\n%s: ", msg);
		print("(L=line, T=type, r/lV=r/lvalue, F=flags, D=depth)\n");
		p1_expr(0, expr);
	}
}

static void p1_expr(int lvl, register struct expr *expr)
{
	p1_indent(lvl);
	if (!expr)	{
		print("NILEXPR\n");
		return;
	}
	print("expr: L=%u, T=%s, %cV, F=%03o, D=%d, %s: ",
		expr->ex_line,
		type2str(expr->ex_type),
		expr->ex_lvalue ? 'l' : 'r',
		expr->ex_flags & 0xFF,
		expr->ex_depth,
		expr->ex_class == Value ? "Value" :
		expr->ex_class == String ? "String" :
		expr->ex_class == Float ? "Float" :
		expr->ex_class == Oper ? "Oper" :
		expr->ex_class == Type ? "Type" : "UNKNOWN CLASS"
	);
	switch (expr->ex_class)	{
		struct oper *o;
	case Value:
		switch (expr->VL_CLASS) {
		case Const:
			print("(Const) ");
			break;
		case Name:
			print("(Name) %s + ", expr->VL_IDF->id_text);
			break;
		case Label:
			print("(Label) .%lu + ", expr->VL_LBL);
			break;
		default:
			print("(Unknown) ");
			break;
		}
		print("%s\n", writh2str(expr->VL_VALUE,
					expr->ex_type->tp_unsigned));
		break;
	case String:
	{
		print(
			"\"%s\"\n",
			bts2str(expr->SG_VALUE, expr->SG_LEN-1,
							next_transient())
		);
		break;
	}
	case Float:
	{
		char buf[FLT_STRLEN];

		flt_flt2str(&(expr->FL_ARITH), buf, FLT_STRLEN);
		print("%s\n", buf);
		break;
	}
	case Oper:
		o = &expr->ex_object.ex_oper;
		print("\n");
		p1_expr(lvl+1, o->op_left);
		p1_indent(lvl);
		print("%s <%s>\n", symbol2str(o->op_oper),
			type2str(o->op_type)
		);
		p1_expr(lvl+1, o->op_right);
		break;
	case Type:
		print("\n");
		break;
	default:
		print("UNKNOWN CLASS\n");
		break;
	}
}

static void p1_indent(register int lvl)
{
	while (lvl--)
		print("  ");
}
#endif	/* DEBUG */
