#include <stdio.h>
#include <string.h>
#include "types.h"
#include "tword.h"
#include "node.h"
#include "object.h"
#include "peep.h"

extern void as_emit1(), as_emit2();
extern linelist *t;
extern char *fullname();

/*
 * The following array keeps track of which record types have been
 * given .def entries.  The array index uniquely identifies each
 * record type.
 */

#define MAXRECID 50

static OBJECT * rectab[MAXRECID];
static int recidx = 0;
static int setdefined = 0;


/*
 * defsym() emits .def entries for variables, parameters, and
 * record types.
 */

static int
defsym(ob)
	register OBJECT *ob;
{
	register OBJECT *type, *field, *start;
	register int sclass, id;
	register TWORD tword;


	tword = ob->ob_tword;

	/* get storage class (.scl) */

	switch(ob->ob_kind) {

	case OB_VARIABLE:
		sclass = ob->ob_level > 1 ? SC_AUTO : SC_EXTERN;
		break;

	case OB_VALPARM:
	case OB_VARPARM:
	case OB_BOUNDID:
		sclass = SC_PARAM;

		/* treat conformant array as pointer to base type */

		for (type = ob->ob_type; type->ob_kind == OB_CONFARRAY;
			type = type->ob_type);
		{
		}
		if (type != ob->ob_type)
			tword = TW_INCREF(type->ob_tword);
		break;

	case OB_FIELD:
		sclass = SC_MOS;
		break;

	case OB_RECORD:
		sclass = SC_STNAME;
		break;
	}

	/* emit .def information */

	switch(ob->ob_kind) {

	case OB_VARIABLE:
	case OB_VARPARM:
	case OB_VALPARM:
	case OB_BOUNDID:
	case OB_FIELD:

		/* skip anything without a name */

		if (*ob->ob_name == '\0')
			return(-1);

		/* pre-define base type if it is a record  or set */

		for (start = type = ob->ob_type;
			(type->ob_kind & (OB_POINTER|OB_ARRAY|OB_CONFARRAY)) &&
				type->ob_type != start;  /* watch out for cycles */
			type = type->ob_type)
		{
		}

		switch (type->ob_kind) {

		case OB_RECORD:
			id = defsym(type);
			break;

		case OB_SET:
			if (setdefined)
				break;
			setdefined = 1;
			strcpy(t->line, "\t.def\t.setst;\t.scl\t10;\t.type\t010");
			as_emit2();
			sprintf(t->line, "\t.size\t%d;\t.endef", type->ob_size);
			as_emit2();
			sprintf(t->line, "\t.def\t.eos;\t.val\t%d;\t.scl\t102",
				type->ob_size);
			as_emit2();
			sprintf(t->line, "\t.tag\t.setst;\t.size\t%d;\t.endef",
				type->ob_size);
			as_emit2();
			break;
		}

		/* emit first part of .def */

		switch (sclass) {

		case SC_AUTO:
		case SC_PARAM:
			sprintf(t->line,
				"\t.def\t%s;\t.val\t%s.%d;\t.scl\t%d;\t.type\t%#o",
				ob->ob_name, ob->ob_name, ob->ob_level, sclass, tword);
			break;
		
		case SC_EXTERN:
			sprintf(t->line,
				"\t.def\t%s;\t.val\t%s;\t.scl\t%d;\t.type\t%#o",
				ob->ob_name, ob->ob_name, sclass, tword);
			break;
		
		case SC_MOS:
			sprintf(t->line,
				"\t.def\t%s;\t.val\t%d;\t.scl\t%d;\t.type\t%#o",
				ob->ob_name, ob->ob.field.offset, sclass, tword);
			break;
		}
		as_emit2();

		/* emit .tag info if base type is a record or set */

		switch(type->ob_kind) {

		case OB_RECORD:
			if (id != -1) {
				sprintf(t->line, "\t.tag\t.%dfake;\t.size\t%d",
					id, type->ob_size);
				as_emit2();
			}
			break;

		case OB_SET:
			sprintf(t->line, "\t.tag\t.setst;\t.size\t%d",
				type->ob_size);
			as_emit2();
			break;
		}

		/* emit .dim info if this is an array or pointer to array */

		for (start = type = ob->ob_type;
			(type->ob_kind & (OB_POINTER|OB_CONFARRAY)) &&
				type->ob_type != start;  /* watch out for cycles */
			type = type->ob_type)
		{
		}
		if (type->ob_kind == OB_ARRAY) {
			register char *p;
			int size = type->ob_size;
			strcpy(t->line, "\t.dim\t");
			p = t->line + 6;
			while (type->ob_kind == OB_ARRAY) {
				p += sprintf(p, "%d,",
					type->ob.array.upper - type->ob.array.lower + 1);
				type = type->ob_type;
			}
			sprintf(p-1, ";\t.size\t%d", size);
			as_emit2();
		}
		strcpy(t->line, "\t.endef");
		as_emit2();
		return(-1);
	
	case OB_RECORD:

		/* see if we already have .def entry for record */

		for (id = 0; id < recidx; id++) {
			if (rectab[id] == ob)
				return(id);
		}

		if (ob->ob_flags & OB_SEEN)  /* stop infinite recursion */
			return(-1);

		/* if any fields have record types, define them first */

		ob->ob_flags |= OB_SEEN;
		for (field = ob->ob.record.field; field;
			field = field->ob_prev)
		{
			for (start = type = field->ob_type;
				(type->ob_kind & (OB_ARRAY|OB_POINTER)) &&
					type->ob_type != start;
				type = type->ob_type)
			{
			}
			if (type->ob_kind == OB_RECORD)
				(void) defsym(type);
		}
		ob->ob_flags &= ~OB_SEEN;

		/* add to table of defined record types */

		if (recidx >= MAXRECID)
			return(-1);
		rectab[id = recidx++] = ob;

		/* emit .def for record and its fields */

		sprintf(t->line,
			"\t.def\t.%dfake;\t.scl\t%d;\t.type\t%#o;\t.size\t%d;\t.endef",
			id, sclass, ob->ob_tword, ob->ob_size);
		as_emit2();

		for (field = ob->ob.record.field; field;
			field = field->ob_prev)
		{
			if (field->ob_kind == OB_FIELD)
				(void) defsym(field);
		}

		sprintf(t->line, "\t.def\t.eos;\t.val\t%d;\t.scl\t102",
			ob->ob_size);
		as_emit2();
		sprintf(t->line, "\t.tag\t.%dfake;\t.size\t%d;\t.endef",
			id, ob->ob_size);
		as_emit2();
		return(id);
	}
	return(-1);
}

/*
 * funcsym emits .def entries for a function, its parameters, and
 * its local variables.
 */
	
funcsym(name, typeid, line)
	char *name;
	TWORD typeid;
	int line;
{
	register OBJECT *ob;

	sprintf(t->line,
		"\t.def\t%s;\t.val\t%s;\t.scl\t%d;\t.type\t%#o;\t.endef",
		name, name, SC_EXTERN, typeid);
	as_emit2();

	sprintf(t->line,
		"\t.def\t.bf;\t.val\t.;\t.scl\t101;\t.line\t%d;\t.endef",
		line);
	as_emit2();

	c_sseg(SG_DATA);
	for (ob = ob_block[ob_level]; ob; ob = ob->ob_prev) {
		if (ob->ob_kind & (OB_VARIABLE|OB_VARPARM|OB_VALPARM|OB_BOUNDID))
			(void) defsym(ob);
	}
	c_sseg(SG_PROG);
}


/*
 * efuncsym emits .def entries at end of function
 */

efuncsym(name, line)
	char *name;
	int line;
{
	sprintf(t->line,
		"\t.def\t.ef;\t.val\t.;\t.scl\t101;\t.line\t%d;\t.endef",
		line);
	as_emit2();
	sprintf(t->line, "\t.def\t%s;\t.val\t.;\t.scl\t-1;\t.endef",
		name);
	as_emit2();
}
