/**********************************************************************/
/*   This  file  is  the back end for the code generator and is used  */
/*   to  generate  a  binary version of the parse tree (i.e. the .cm  */
/*   language directly).					      */
/**********************************************************************/
# include	"crunch.h"
# include	<ctype.h>
# include	"../def.h"
# include	"../cm.h"
# include	"../refstr.h"

typedef	unsigned char	LIST;
/**********************************************************************/
/*   Header for the .cm file.					      */
/**********************************************************************/
CM	cm_header;

/**********************************************************************/
/*   Initial size of object code buffer.			      */
/**********************************************************************/
# define	OBJECT_SIZE	4096

/**********************************************************************/
/*   Size  to  increase  object  code buffer size when we run out of  */
/*   room.							      */
/**********************************************************************/
# define	OBJECT_INCR	2048

/**********************************************************************/
/*   Stack  of  where  we the F_LIST entry goes so we can back-patch  */
/*   in the end address of the list when its been fully built.	      */
/**********************************************************************/
Head_p	hd_list_stack;

/**********************************************************************/
/*   Pointer to next byte to be added to object file.		      */
/**********************************************************************/
unsigned char	*byte_ptr;

/**********************************************************************/
/*   Pointer to memory allocated for object code buffer.	      */
/**********************************************************************/
unsigned char	*object_code;

/**********************************************************************/
/*   Size of memory chunk currently allocated to object code buffer.  */
/**********************************************************************/
long	object_allocated;

/**********************************************************************/
/*   Splay tree of allocated strings.				      */
/**********************************************************************/
SPTREE	*str_tree;

typedef struct string_t {
	int	str_index;	/* Index into the string table.		*/
	char	*str_str;
	} string_t;
int	str_index;

/**********************************************************************/
/*   Following  is  an array of pointers to the strings in the splay  */
/*   tree.							      */
/**********************************************************************/
ref_t	*str_table;
/**********************************************************************/
/*   List  of  indices  to  where  the  macros start. Needed for the  */
/*   header so we can locate the start of each macro easily.	      */
/**********************************************************************/
static ref_t	*macro_offsets;

static int	list_level;

extern int	cm_version;
extern int	sizeof_atoms[];
void	gen_byte PROTO((int));
int	builtin_index PROTO((char *));
void	genb_str PROTO((char *, int));
void	swap_cm_header PROTO((CM *));
void	free_binary PROTO((void));

void
init_binary()
{	static CM	null_cm;

	free_binary();
	
	str_tree = spinit();
	str_index = 0;
	hd_list_stack = ll_init();
	macro_offsets = r_init(F_STR, (char *) NULL, 32 * sizeof(long));
	str_table = r_init(F_STR, (char *) NULL, 256 * sizeof (char *));
	
	if (object_code == NULL)
		object_code = (unsigned char *) chk_alloc(OBJECT_SIZE);
	byte_ptr = object_code;
	cm_header = null_cm;
	list_level = 0;	
}
void
free_binary()
{
	free_list(&hd_list_stack);
	r_dec(macro_offsets);
	r_dec(str_table);
	if (str_tree) {
		string_t *strp;
		SPBLK	**array;
		int	i;
		array = sp_flatten(str_tree);
		for (i = 0; array[i]; i++) {
			strp = (string_t *) (array[i]->data);
			chk_free(array[i]->data);
			chk_free(strp->str_str);
			spfreeblk(array[i]);
			}
		chk_free((void *) array);
		spfree(str_tree);
		}
}
void
genb_list()
{	long	offset;

	if (list_level++ == 0)
		return;
	gen_byte(F_LIST);
	offset = byte_ptr - object_code;
	ll_push(hd_list_stack, (char *) offset);
	gen_byte(0);
	gen_byte(0);
}
void
genb_end_list()
{	int	offset;

	if (--list_level == 0) {
		gen_byte(F_HALT);
		return;
		}

	offset = (int) ll_elem(ll_first(hd_list_stack));
	gen_byte(F_HALT);
	ll_pop(hd_list_stack);
	LPUT16((char *) object_code + offset - 1, 
		(byte_ptr - object_code) - offset + 1);
}
void
genb_int(n)
long	n;
{	char	buf[5];
	int	i;
	gen_byte(F_INT);
	LPUT32(buf, n);
	for (i = 1; i < sizeof buf; i++)
		gen_byte(buf[i]);
}
void
genb_float(v)
double	v;
{	char	buf[1 + sizeof (double)];
	int	i;
	gen_byte(F_FLOAT);
	LPUT_FLOAT(buf, v);
	for (i = 1; i < sizeof buf; i++)
		gen_byte(buf[i]);

}
void
genb_string(str)
char	*str;
{
	genb_str(str, F_LIT);
}
void
genb_token(val)
int	val;
{

	genb_str(map(val), F_ID);
}
void
genb_id(str)
char *str;
{
	genb_str(str, F_ID);
}
void
genb_str(str, type)
char	*str;
int	type;
{	int	i = builtin_index(str);
	SPBLK		*sp;
	string_t	*strp;
	register char *cp;
	char	*alloc_str;
	
	if (i >= 0) {
		gen_byte(F_ID);
		gen_byte(0);
		gen_byte(0);
		LPUT16((char *) (byte_ptr - 3), i);
		return;
		}
	alloc_str = (char *) strdup(str);
	/***********************************************/
	/*   Convert   string  losing  the  backslash  */
	/*   sequences.				       */
	/***********************************************/
	for (cp = alloc_str; *cp; ) {
		if (*cp++ != '\\')
			continue;
		switch (cp[0]) {
		  case 'n':
		  	cp[-1] = '\n';
			strcpy(cp, cp+1);
			break;
		  case 'r':
		  	cp[-1] = '\r';
			strcpy(cp, cp+1);
			break;
		  case 't':
		  	cp[-1] = '\t';
			strcpy(cp, cp+1);
			break;
		  case 'f':
		  	cp[-1] = '\f';
			strcpy(cp, cp+1);
			break;
		  case 'b':
		  	cp[-1] = '\b';
			strcpy(cp, cp+1);
			break;
		  case 'a':
		  	cp[-1] = 7;
			strcpy(cp, cp+1);
			break;
		  case '\\':
			strcpy(cp, cp+1);
			break;
		  case '0': case '1': case '2': case '3': case '4':
		  case '5': case '6': case '7': {
		  	int	len = 0;
			int	n = 0;
			while (len < 3) {
				if (!isdigit(cp[len]))
					break;
				n = 8 * n + cp[len++] - '0';
				}
			cp[-1] = (char) n;
			strcpy(cp, cp + len);
			break;
		  	}
		  case 'x': {
		  	int len = 1;
			int n = 0;
			while (len < 3) {
				if (!isxdigit(cp[len]))
					break;
				n <<= 4;
				if (isdigit(cp[len]))
					n += cp[len] - '0';
				else if (isupper(cp[len]))
					n += cp[len] - 'A' + 10;
				else
					n += cp[len] - 'a' + 10;
				len++;
				}
			cp[-1] = (char) n;
			strcpy(cp, cp + len);
			break;
		  	}
		  default:
		  	strcpy(cp-1, cp);
			break;
			
		  }
		}
	if ((sp = splookup(alloc_str, str_tree)) != NULL) {
		strp = (string_t *) sp->data;
		chk_free(alloc_str);
		}
	else {
		sp = spblk(sizeof (string_t));
		strp = (string_t *) sp->data;
		sp->key = alloc_str;
		r_append(str_table, (char *) &alloc_str, sizeof alloc_str, 128 * sizeof (char *));
		strp->str_index = str_index++;
		strp->str_str = alloc_str;
		cm_header.cm_num_strings++;
		spenq(sp, str_tree);
		}
	if (type == F_ID)
		type = F_STR;
	gen_byte(type);
	gen_byte(0);
	gen_byte(0);
	gen_byte(0);
	gen_byte(0);
	LPUT32((char *) (byte_ptr - 5), (long) strp->str_index);
}
void
genb_lit(str)
char	*str;
{
	genb_str(str, F_STR);
}
void
genb_null()
{
	gen_byte(F_NULL);
}
void
genb_macro()
{
	long	offset = byte_ptr - object_code;

	cm_header.cm_num_macros++;
	r_append(macro_offsets, (char *) &offset, sizeof(long), 64 * sizeof(long));
}
void
gen_byte(b)
int	b;
{	int	offset;

	if (byte_ptr > object_code + object_allocated - 10) {
		offset = byte_ptr - object_code;
		object_allocated += OBJECT_INCR;
		object_code = (unsigned char *) chk_realloc((void *) object_code, 
			(int) object_allocated);
		byte_ptr = object_code + offset;
		}
	*byte_ptr++ = (unsigned char) b;
}
/**********************************************************************/
/*   Now  that  compilation  is finished, we can output the compiled  */
/*   code.							      */
/**********************************************************************/
void
genb_finish()
{	long	*maclp;
	long	str_offset;
	char	**strp;
	long	offset;
	long	j;
	int	code_size;

	gen_byte(F_END);	
	code_size  = byte_ptr - object_code;

	/***********************************************/
	/*   Make   sure   code  is  on  a  quad-byte  */
	/*   boundary.				       */
	/***********************************************/
	if (code_size & 3)
		code_size = (code_size | 3) + 1;
	cm_header.cm_magic = CM_MAGIC;
	cm_header.cm_version = cm_version;
	cm_header.cm_num_atoms = byte_ptr - object_code;
	cm_header.cm_globals = 0;
	cm_header.cm_num_globals = 0;

	swap_cm_header(&cm_header);
	
	fwrite(&cm_header, 1, sizeof cm_header, ofp);
	
	maclp = (long *) macro_offsets->r_ptr;
	while (maclp < (long *) &macro_offsets->r_ptr[macro_offsets->r_used]) {
		long	index = *maclp++;
		index = WGET32(index);
		fwrite(&index, sizeof index, 1, ofp);
		}
	str_offset = WGET32((long) code_size);
	fwrite(&str_offset, sizeof str_offset, 1, ofp);
	j = cm_header.cm_num_strings;
	fwrite(&j, sizeof j, 1, ofp);
	
	fwrite(object_code, code_size, 1, ofp);
	
	/***********************************************/
	/*   Write out the table of string offset.     */
	/***********************************************/
	strp = (char **) str_table->r_ptr;
	for (offset = 0, j = 0; j++ < str_index; ) {
		char	*cp = *strp++;
		long	o = WGET32(offset);
		fwrite(&o, sizeof o, 1, ofp);
		offset += strlen(cp) + 1;
		}
	/***********************************************/
	/*   Write out the strings themselves.	       */
	/***********************************************/
	strp = (char **) str_table->r_ptr;
	for (offset = 0, j = 0; j++ < str_index; ) {
		char	*cp = *strp++;
		fwrite(cp, strlen(cp) + 1, 1, ofp);
		}
}
void
swap_cm_header(cm)
CM *cm;
{	
	cm->cm_magic = WGET16(cm->cm_magic);
	cm->cm_version = WGET16(cm->cm_version);
	cm->cm_num_macros = WGET16(cm->cm_num_macros);
	cm->cm_num_atoms = WGET16(cm->cm_num_atoms);
	cm->cm_globals = WGET32(cm->cm_globals);
	cm->cm_num_globals = WGET16(cm->cm_num_globals);
	cm->cm_num_strings = WGET16(cm->cm_num_strings);
}

