/* macstuff.c macro definition stuff */

#include "picpar.tab.h"
#include "macstuff.h"

static MAC_NODE  *root_macro;
static TOKEN     *root_token;
static TOKEN    **current_token;
static TOKEN	**exploded_list;

/* false (NULL) if macro does not exist else address of MAC_NODE */

MAC_NODE *maclookup(char *idname)
{
	MAC_NODE *ptr;

	ptr = root_macro;

	while(ptr != NULL)
	{
		if (strcmp(ptr->name, idname) == 0)
			return ptr;
		ptr = ptr->next;
	}
	return NULL;
}

void macinit()
{
	root_macro = NULL;
}

void macdefine(char *idname)
{
	int i;
	MAC_NODE *ptr;

	ptr = (MAC_NODE *)malloc(sizeof(MAC_NODE));

	if (ptr==NULL)
	{
		printf("Malloc failed, out of memory in macdefine\n");
		exit(1);
	}

	ptr->name     = strdup(idname);

	if(ptr->name == NULL)
	{
		printf("Malloc failed, out of memory in macdefine\n");
		exit(1);
	}

	ptr->tokens   = NULL;
	ptr->next     = root_macro;

	root_macro    = ptr;

	current_token = &(root_macro->tokens);
}

/* create token */

TOKEN *createtoken(int type, int intval, char *strval)
{
	TOKEN *ptr;

	ptr = (TOKEN *)malloc(sizeof(TOKEN));

	if (ptr == NULL)
	{
		printf("Malloc failed, out of memory in createtoken\n");
		exit(1);
	}

	ptr->type   = type;
	ptr->intval = intval;

	if ((strcmp(strval,"") == 0) || (strval == NULL))
		ptr->strval = NULL;
	else
	{
		ptr->strval = strdup(strval);
		if(ptr->strval == NULL)
		{
			printf("Malloc failed, out of memory in createtoken\n");
			exit(1);
		}
	}

	ptr->next = NULL;

	return ptr;
}

/* delete token */

void deletetoken(TOKEN *ptr)
{
	if (ptr == NULL)
	{
		printf("Error Trying to delete token with NULL pointer\n");
		exit(1);
	}

	if (ptr->strval != NULL)	/* delete string if it exists */
		free(ptr->strval);

	free(ptr);
}

/* delete token list */

void deletetokenlist(TOKEN *root)
{
	TOKEN *ptr, *tbd;

	ptr = root;

	while(ptr != NULL)
	{
		tbd = ptr;
		ptr = ptr->next;
		deletetoken(tbd);
	}
}

/* add a token onto current macro */

void macaddtoken(int type, int intval, char *strval)
{
	TOKEN *ptr;

	ptr = createtoken(type, intval, strval);

	*current_token = ptr;
	current_token = &(ptr->next);
}

/* create a copy of the token passed to the function */
/* returns address of copy */

TOKEN *copytoken(TOKEN *token)
{
	return createtoken(token->type, token->intval, token->strval);
}

/* addtoexplodedlist adds a token to the exploded token list */

void addtoexplodedlist(TOKEN *token)
{
	TOKEN *ptr;

	ptr = copytoken(token);

	*exploded_list = ptr;
	exploded_list  = &(ptr->next);
}

/* explode a token list run through exploding macros */

void explodetokenlist(TOKEN *root)
{
	TOKEN    *args[MAX_ARGS];
	TOKEN   **arg_ptr;
	TOKEN    *ptr;
	TOKEN    *ins_ptr;
	TOKEN    *ins_arg_ptr;
	int       inmacro;	/* 0 if not in macro else the argument this is */
	MAC_NODE *macro_included;
	int       i;

	ptr        = root;
	inmacro    = 0;

	for(i=0;i<MAX_ARGS;i++)
		args[i] = NULL;

	while(ptr != NULL)
	{
		if(ptr->type == MAC_USE)
		{
			inmacro    = 1;
			macro_included = maclookup(ptr->strval);

			/* prepare arguments */

			for(i=0;i<MAX_ARGS;i++)
				deletetokenlist(args[i]);

			arg_ptr = &args[0];
		}
		else
		{
			if(inmacro)
			{
				switch(ptr->type)
				{
					case COMMA:	inmacro++;
							if (inmacro > MAX_ARGS)
							{
								printf("Too many arguments to macro\n");
								exit(1);
							}
							arg_ptr = &args[inmacro-1];
							break;

					case NEWLINE:	inmacro = 0;
							ins_ptr = macro_included->tokens;

							while(ins_ptr != NULL)
							{
								if(ins_ptr->type != DOLLAR)
								{
									addtoexplodedlist(ins_ptr);
								}
								else
								{
									ins_arg_ptr = args[(ins_ptr->intval) - 1];
									while(ins_arg_ptr != NULL)
									{
										addtoexplodedlist(ins_arg_ptr);
										ins_arg_ptr = ins_arg_ptr->next;
									}
								}
								ins_ptr = ins_ptr->next;
							}
							addtoexplodedlist(ptr);
							break;

					default:	*arg_ptr = copytoken(ptr);
							arg_ptr = &((*arg_ptr)->next);
							break;
				}
			}
			else
			{
				addtoexplodedlist(ptr);
			}
		}
		ptr = ptr->next;	/* move onto next token */
	}
}

/* explode a macro */

void explodemacro(char *name)
{
	MAC_NODE *macptr;

	TOKEN *explode_result;

	macptr = maclookup(name);

	if(macptr == NULL)
	{
		printf("Macro %s doesn't exist\n",name);
		exit(1);
	}

	explode_result = NULL;
	exploded_list  = &explode_result;	/* set up the explode */

	explodetokenlist(macptr->tokens);	/* explode onto explode_result */

	deletetokenlist(macptr->tokens);	/* delete the old */

	macptr->tokens = explode_result;	/* link in the new */
}

void macuseddefine()
{
	root_token = NULL;
	current_token = &root_token;
}

void macusedoutput(FILE *ofile)
{
	TOKEN *ptr;
	TOKEN *explode_result;

	explode_result = NULL;
	exploded_list = &explode_result;

	explodetokenlist(root_token);
	deletetokenlist(root_token);

	ptr = explode_result;

	while(ptr != NULL)
	{
		switch(ptr->type)
		{
			case NUM:	fprintf(ofile,"%d ",ptr->intval);
					break;
			case SYMBOL:	
			case ID:	
			case STRING:	fprintf(ofile,"%s ",ptr->strval);
					break;

			case NEWLINE:	fprintf(ofile,"#%d\n",ptr->intval);
					break;

			default:	printf("MAC_USED Expansion error (%d)\n", ptr->type);
					exit(1);
					break;
		}
		ptr = ptr->next;
	}

	deletetokenlist(explode_result);
}


/* ... The End ... */

