/**********************************************************************/
/*   This  file  contains  the  generic code generator routines. The  */
/*   actual  syntax  of  the  output is handled in the files crbin.c  */
/*   and crasc.c depending on the command line.			      */
/**********************************************************************/
# include "crunch.h"

int generate_ascii = TRUE;
gen_t gen_ascii = {
	gena_macro,
	gena_list,
	gena_id,
	gena_lit,
	gena_end_list,
	gena_int,
	gena_string,
	gena_float,
	gena_token,
	gena_finish,
	gena_null
	};
gen_t gen_binary = {
	genb_macro,
	genb_list,
	genb_id,
	genb_lit,
	genb_end_list,
	genb_int,
	genb_string,
	genb_float,
	genb_token,
	genb_finish,
	genb_null
	};
int	initialisor_level = 0;


extern Head_p	hd_syms;
extern Head_p	hd_globals;
void	open_bracket PROTO((node_t *));
void	gen_bracket PROTO((node_t *, int));
void	init_ascii PROTO((void));
void	init_binary PROTO((void));
void	gen_keywd PROTO((char *));

/**********************************************************************/
/*   Function called to initialise the code generator.		      */
/**********************************************************************/
void
init_codegen()
{
	init_ascii();
	init_binary();
}

void
compile_main(main_tree)
node_t	*main_tree;
{

	if (main_tree)
		compile_func("_init", (node_t *) NULL, main_tree);
	else if (hd_syms && ll_first(hd_syms)) {
		gen_macro();
		gen_keywd("macro");
		gen_lit("_init");
		if (compile_globals(FALSE))
			gen_end_list();
		gen_end_list();
		}
	gen_finish();
		
}
void
compile_func(function, arglist, stmts)
char	*function;
node_t	*arglist;
node_t	*stmts;
{	int	do_end = FALSE;

	gen_macro();
	/***********************************************/
	/*   Generate  the  macro  header  and  macro  */
	/*   name part.				       */
	/***********************************************/
	gen_keywd("macro");
	gen_lit(function);
	
	/***********************************************/
	/*   If  theres  no  code  in  the  function,  */
	/*   just terminate the macro definition.      */
	/***********************************************/
	if (stmts == NULL) {
		gen_end_list();
		return;
		}
	if (strcmp(function, "_init") == 0)
		do_end = compile_globals(stmts != NULL);
	else
		do_end = compile_arglist((Head_p) arglist, stmts != NULL);
	if (stmts) {
		/***********************************************/
		/*   If  we  got  a  multi-line  macro,  dont  */
		/*   enclose  the  whole  lot  in yet another  */
		/*   layer of list brackets.		       */
		/***********************************************/
		if (stmts->type == node_keywd && stmts->atom.ival == K_BLOCK) {
			compile_list((Head_p) stmts->right, 1, do_end, FALSE);
			}
		else {
			if (!do_end && stmts->left && stmts->right)
				gen_list();
			compile_node(stmts, 1);
			if (!do_end && stmts->left && stmts->right)
				gen_end_list();
			}
		if (do_end)
			gen_end_list();
		}
	gen_end_list();
	if (do_end)
		gen_end_list();
}

/**********************************************************************/
/*   The  following  function  is  used to generate code for all the  */
/*   global symbols we've seen.					      */
/**********************************************************************/
int
compile_globals(flag)
int	flag;
{	register List_p	lp;
	register symbol_t *sp;
	int	j, t;
	int	got_decl = FALSE;
	
	/***********************************************/
	/*   Dont  generate  anything  if  no symbols  */
	/*   defined.				       */
	/***********************************************/
	if (hd_syms == NULL || ll_first(hd_syms) == NULL)
		return FALSE;

	/***********************************************/
	/*   We  must  have  at  least one non-extern  */
	/*   declaration  before  we start outputting  */
	/*   any  code.  So  we  need to have a quick  */
	/*   peek at the delcarations list.	       */
	/***********************************************/
	for (lp = ll_first(hd_syms); !got_decl && lp; lp = ll_next(lp)) {
		sp = (symbol_t *) ll_elem(lp);
		if ((sp->s_type & SC_MASK) == (((long) SC_EXTERN) << SC_SHIFT))
			continue;
		t = sp->s_type & TY_MASK;
		switch (t) {
		  case TY_INT:
		  case TY_STRING:
		  case TY_LIST:
		  case TY_DECLARE:
		  case TY_FLOAT:
		  case TY_DOUBLE:
		  	got_decl = TRUE;
			break;
		  }
		}

	if (!got_decl)
		return FALSE;

	if (flag)
		gen_list();
	gen_list();
        /***********************************************/
        /*   Output   declarations,   but  merge  all  */
        /*   declarations of each type.		       */
        /***********************************************/
	for (j = 0; j < 5; j++) {
		int	done_keywd = FALSE;
		char	*keywd= "";
		for (lp = ll_first(hd_syms); lp; lp = ll_next(lp)) {
			sp = (symbol_t *) ll_elem(lp);
			if ((sp->s_type & SC_MASK) == (((long) SC_EXTERN) << SC_SHIFT))
				continue;
			t = sp->s_type & TY_MASK;
			switch (j) {
			  case 0:
			  	if (t != TY_INT)
					continue;
				keywd = "int";
				break;
			  case 1:
			  	if (t != TY_STRING)
					continue;
				keywd = "string";
				break;
			  case 2:
			  	if (t != TY_LIST)
					continue;
				keywd = "list";
				break;
			  case 3:
			  	if (t != TY_DECLARE)
					continue;
				keywd = "declare";
				break;
			  case 4:
			  	if (t != TY_FLOAT && t != TY_DOUBLE)
					continue;
				keywd = "float";
				break;
			  }
			if (!done_keywd) {
				gen_keywd(keywd);
				done_keywd = TRUE;
				}
			gen_lit(sp->name);
			}
		if (done_keywd) {
			gen_end_list();
			}
		}
		
	/***********************************************/
	/*   Now  output  the  (global  ...) stuff so  */
	/*   that  symbols  get  stored in the global  */
	/*   symbol table.			       */
	/***********************************************/
	gen_keywd("global");
	while (lp = ll_first(hd_syms)) {
		sp = (symbol_t *) ll_elem(lp);
		ll_delete(lp);
		if ((sp->s_type & SC_MASK) != (((long) SC_EXTERN) << SC_SHIFT)) {
			switch ((int) (sp->s_type & TY_MASK)) {
			  case TY_INT:
			  case TY_STRING:
			  case TY_DECLARE:
			  case TY_LIST:
			  case TY_FLOAT:
			  case TY_DOUBLE:
			  	gen_lit(sp->name);
				break;
			  }
			}
		chk_free((char *) sp);
		}
	gen_end_list();
	
        /***********************************************/
        /*   Output   any   initialisation  code  for  */
        /*   each global			       */
        /***********************************************/
	if (hd_globals) {
		for (lp = ll_first(hd_globals); lp; lp = ll_next(lp)) {
			compile_node((node_t *) ll_elem(lp), 1);
			}
		}
	return TRUE;
}

/**********************************************************************/
/*   Generate code for the declaration of arguments to a function.    */
/**********************************************************************/
int
compile_arglist(hd_arglist, flag)
Head_p	hd_arglist;
int	flag;
{	register List_p lp;
	register node_t *np;
	int	arg;
	int	dots_seen = FALSE;
	long	old_type = -1;
	
	if (hd_arglist == NULL)
		return FALSE;
	if (flag)
		gen_list();
	gen_list();
	for (lp = ll_first(hd_arglist); lp; lp = ll_next(lp)) {
		np = (node_t *) ll_elem(lp);
		if (dots_seen)
			fprintf(errfp, "Declarations after an ellipsis detected.\n");
		if (np == NULL)
			dots_seen = TRUE;
		else if (np->right) {
			if (np->atom.ival != old_type) {
				if (old_type >= 0) {
					gen_end_list();
					}
				gen_list();
				gen_lit(type_to_str(np->atom.ival));
				}
			gen_lit(np->right->atom.sval);
			old_type = np->atom.ival;
			}
		}
	if (old_type >= 0) {
		gen_end_list();
		}
		
	for (arg = 0, lp = ll_first(hd_arglist); lp; lp = ll_next(lp)) {
		np = (node_t *) ll_elem(lp);
		if (np && np->right) {
			gen_keywd("get_parm");
			gen_int((long) arg);
			gen_lit(np->right->atom.sval);
			gen_end_list();
			}
		arg++;
		}
	return TRUE;
}
void
compile_list(hd, level, flag, sep_stmt)
Head_p hd;
int	level;
int	flag;
int	sep_stmt;
{	register List_p	lp;
	int	complex_list = FALSE;
	node_t	*np;
	
	if (hd == NULL)
		return;
	lp = ll_first(hd);
	if (lp) {
		if (ll_next(lp))
			complex_list = TRUE;
		else {
			np = (node_t *) ll_elem(lp);
			if (np && np->type == node_keywd && np->atom.ival == K_NOOP)
				complex_list = TRUE;
			}
		}
	if (complex_list && !flag) {
		gen_list();
		}
	while ((lp = ll_first(hd)) != NULL) {
		np = (node_t *) ll_elem(lp);
		/***********************************************/
		/*   Make  sure  code  like:  '1;2;3;' causes  */
		/*   each  constant  to be inside its own set  */
		/*   of brackets.			       */
		/***********************************************/
		if (np) {
			if (sep_stmt) {
				switch (np->type) {
				  case node_string:
				  case node_integer:
				  case node_float:
				  	gen_list();
					break;
				  default:
				  	break;
				  }
				}
			compile_node(np, level);
			if (sep_stmt) {
				switch (np->type) {
				  case node_string:
				  case node_integer:
				  case node_float:
				  	gen_end_list();
					break;
				  default:
				  	break;
				  }
				}
			}
		ll_delete(lp);
		}
	ll_free(hd);
	if (complex_list && !flag) {
		gen_end_list();
		}
}

/**********************************************************************/
/*   Generate code for a parse tree.				      */
/**********************************************************************/
void
compile_node(tree, level)
node_t	*tree;
int	level;
{
	if (tree == NULL)
		return;
	if (tree->type == node_keywd) {
		switch ((int) tree->atom.ival) {
		  case K_INIT: {
		  	compile_node(tree->right, level);
		  	compile_list((Head_p) tree->left, level, FALSE, FALSE);
		  	break;
			}
		  case K_INITLIST: {
		  	gen_list();
		  	if (initialisor_level++ == 0)
				gen_id("quote_list");
		  	compile_node(tree->right, level);
			if (tree->left) {
				gen_keywd("quote_list");
			  	compile_node(tree->left, level);
				}
			gen_end_list();
			initialisor_level--;
		  	break;
			}
		  case K_FUNCALL:
		  	open_bracket(tree);
		  	gen_lit(tree->left->atom.sval);
			compile_node(tree->right, level);
			gen_end_list();
		  	break;
		  case K_BLOCK:
			compile_list((Head_p) tree->right, level, FALSE, TRUE);
			break;
		  case K_DEFAULT:
		  	gen_null();
			compile_node(tree->right, level);
			break;
		  case K_CASE:
		  	gen_bracket(tree->left, level);
			if (tree->right == NULL)
			  	gen_null();
			else {
			  	if (tree->right->left && tree->right->right)
					open_bracket(tree);
				compile_list((Head_p) tree->right, level, FALSE, TRUE);
			  	if (tree->right->left && tree->right->right)
					gen_end_list();
				}
			break;
		  case K_BREAKSW:
		  	gen_keywd("breaksw");
			gen_end_list();
		  	break;
		  case K_IF:
			gen_keywd("if");
			compile_node(tree->left, level);
			compile_list((Head_p) tree->right, level, FALSE, FALSE);
			gen_end_list();
			break;
		  case K_ELSE: {
			gen_keywd("if");
			compile_node(tree->left->left, level);
			compile_list((Head_p) tree->left->right, level, FALSE, FALSE);
			compile_list((Head_p) tree->right, level, FALSE, FALSE);
			gen_end_list();
			break;
			}
		  case K_WHILE:
			gen_keywd("while");
			compile_node(tree->left, level);
			compile_list((Head_p) tree->right, level, FALSE, TRUE);
			gen_end_list();
			break;
		  case K_FOR:
			gen_keywd("for");
			gen_bracket(tree->left, level);
			gen_bracket(tree->right->left, level);
			gen_bracket(tree->right->right->left, level);
			if (tree->right->right->right)
				compile_list((Head_p) tree->right->right->right, level, FALSE, TRUE);
			gen_end_list();
			break;
		  case OSQUARE:
			gen_keywd("nth");
			compile_node(tree->right, level);
			compile_node(tree->left, level);
			gen_end_list();
			break;
		  case K_NOOP:
		  	if (tree->left) {
				compile_node(tree->left, level);
				}
			if (tree->right) {
				compile_node(tree->right, level);
				}
			break;
		  case K_SWITCH:
			compile_switch(tree, level);
			break;
		  case	K_FLOAT:
		  case	K_DOUBLE:
		  case	K_INT:
		  case	K_STRING:
		  case	K_LIST:
		  case	K_DECLARE:
			goto DEFAULT;
			
			/***********************************************/
			/*   Handle    post-inc/decrement   operators  */
			/*   here   because  there  arent  any  valid  */
			/*   tokens  for  these  on  input. (They are  */
			/*   positional).			       */
			/***********************************************/
		  case POST_PLUS_PLUS:
		  case POST_MINUS_MINUS:
			gen_keywd(tree->atom.ival == POST_PLUS_PLUS ? 
				"post++" : "post--");
			compile_node(tree->left, level);
			gen_end_list();
			break;
		  DEFAULT:
		  default:
			print_node(tree, level);
		  }
		}
	else
		print_node(tree, level);
}
void
print_node(tree, level)
node_t	*tree;
int	level;
{

	switch (tree->type) {
	  case node_string:
		gen_string(tree->atom.sval);
		break;
	  case node_symbol:
	  	if (tree->atom.sval[0] == 'N' && strcmp(tree->atom.sval, "NULL") == 0)
			gen_null();
		else
			gen_lit(tree->atom.sval);
		break;
	  case node_integer:
		gen_int(tree->atom.ival);
		break;
	  case node_float:
		gen_float(tree->atom.floatval);
		break;
	  case node_keywd:
		open_bracket(tree);
		if (tree->atom.ival != COMMA)
			gen_token((int) tree->atom.ival);
		break;
	  }
		
	if (tree->left)
		compile_node(tree->left, level+1);
	if (tree->right) {
		if (tree->type == node_keywd && tree->atom.ival == K_WHILE &&
		   (tree->right->type == node_keywd && tree->right->atom.ival == K_NOOP)) {
		   	gen_list();
			compile_node(tree->right, level+1);
			gen_end_list();
			}
		else
			compile_node(tree->right, level+1);
		}
	if (tree->type == node_keywd)
		gen_end_list();
}
void
compile_switch(tree, level)
node_t	*tree;
int	level;
{	node_t	*casep;

	gen_keywd("switch");
	compile_node(tree->left, level);
	casep = tree->right;
	if (casep) {
		compile_node(casep->left, level+1);
		compile_node(casep->right, level+1);
		}
	gen_end_list();
}

/**********************************************************************/
/*   Start  a  new list, and if the debug flag is set, add a call to  */
/*   the debugger so we can see whats going on.			      */
/**********************************************************************/
void
open_bracket(np)
node_t *np;
{

	gen_list();
	if (g_flag) {
		extern char *current_fn;
		gen_list();
		gen_lit("__dbg_trace__");
		gen_int((long) np->lineno);
		gen_string(filename);
		gen_string(current_fn);
		gen_end_list();
		}
}
/**********************************************************************/
/*   Generate  code  for a tree, and enclose it in parenthesis if we  */
/*   have a complex tree (i.e. more than one statement).	      */
/**********************************************************************/
void
gen_bracket(np, level)
node_t	*np;
int	level;
{
	if (np == NULL)
		gen_null();
	else if (np->type == node_keywd && np->atom.ival == K_NOOP) {
		gen_list();
		compile_node(np, level);
		gen_end_list();
		}
	else
		compile_node(np, level);
}
void
gen_keywd(str)
char	*str;
{
	gen_list();
	gen_id(str);
}
