/*

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/ 
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "globals.h"
#include "types.h"
#include "errors.h"
#include "ast_util.h"
#include "parse_making_ast.h"
#include "string_cache.h"
#include "ast_optimizations.h"
#include "verilog_bison_user_defined.h"
#include "verilog_preprocessor.h"
#include "util.h"
#include "hard_blocks.h"

extern int yylineno;

STRING_CACHE *defines_for_file_sc;
STRING_CACHE **defines_for_module_sc;
STRING_CACHE *modules_inputs_sc;
STRING_CACHE *modules_outputs_sc;

STRING_CACHE *module_names_to_idx;

ast_node_t **block_instantiations_instance;
int size_block_instantiations;

ast_node_t **module_instantiations_instance;
int size_module_instantiations;

int num_modules;
ast_node_t **ast_modules;

ast_node_t **all_file_items_list;
int size_all_file_items_list;

short to_view_parse;

void graphVizOutputPreproc(FILE *yyin, char* path, char *file)
{
	char line[MaxLine];
	FILE *fp;
	char *tmp;

	// strip the ".v" from file
	tmp = strrchr(file, '.');
	oassert(tmp);
	oassert(*(tmp+1) == 'v');
	*tmp = '\0';
	
	// strip the path from file
	tmp = strrchr(file, '/');
	if (tmp) file = tmp;

	sprintf(line, "%s/%s_preproc.v", path, file);
	fp = fopen(line, "w");
	oassert(fp);
	while (fgets(line, MaxLine, yyin))
		fprintf(fp, "%s", line);
	fclose(fp);
	rewind(yyin);
}


/*---------------------------------------------------------------------------------------------
 * (function: parse_to_ast)
 *-------------------------------------------------------------------------------------------*/
void parse_to_ast()
{
	int i;
	extern FILE *yyin;
	extern int yylineno;
	
	/* hooks into macro at the top of verilog_flex.l that shows the tokens as they're parsed.  Set to true if you want to see it go...*/
	to_view_parse = configuration.print_parse_tokens;

	/* initialize the parser */
	init_parser();

	/* open files for parsing */
	if (global_args.verilog_file != NULL)
	{
		/* make a consitant file list so we can access in compiler ... replicating what read config does for the filenames */
		configuration.list_of_file_names = (char**)malloc(sizeof(char*));
		configuration.num_list_of_file_names = 1;
		configuration.list_of_file_names[0] = global_args.verilog_file;

		yyin = fopen(global_args.verilog_file, "r");
		if (yyin == NULL)
		{
			error_message(-1, -1, -1, "cannot open file: %s", global_args.verilog_file);
		}
	
		/*Testing preprocessor - Paddy O'Brien*/
		init_veri_preproc();
		yyin = veri_preproc(yyin);
		cleanup_veri_preproc();

		/* write out the pre-processed file */
		if (configuration.output_preproc_source)
			graphVizOutputPreproc(yyin, configuration.debug_output_path, configuration.list_of_file_names[0]) ;
			
		/* set the file name */	
		current_parse_file = 0;

		/* setup the local parser structures for a file */
		init_parser_for_file();
		/* parse */
		yyparse();
		/* cleanup parser */
		clean_up_parser_for_file();

		fclose(yyin);
	}
	else if (global_args.config_file != NULL)
	{
		/* read all the files in the configuration file */
		for (i = 0; i < configuration.num_list_of_file_names; i++)
		{
			yyin = fopen(configuration.list_of_file_names[i], "r");
			if (yyin == NULL)
			{
				error_message(-1, -1, -1, "cannot open file: %s\n", configuration.list_of_file_names[i]);
			}
		
			/*Testing preprocessor - Paddy O'Brien*/
			init_veri_preproc();
			yyin = veri_preproc(yyin);
			cleanup_veri_preproc();
			
			/* write out the pre-processed file */
			if (configuration.output_preproc_source)
				graphVizOutputPreproc(yyin, configuration.debug_output_path, configuration.list_of_file_names[i]) ;
 			
			/* set the file name */	
			current_parse_file = i;

			/* reset the line count */
			yylineno = 0;

			/* setup the local parser structures for a file */
			init_parser_for_file();
			/* parse next file */
			yyparse();
			/* cleanup parser */
			clean_up_parser_for_file();

			fclose(yyin);
		}
	}

	/* clean up all the structures in the parser */
	cleanup_parser();

	/* for error messages - this is in case we use any of the parser functions after parsing (i.e. create_case_control_signals()) */
	current_parse_file = -1;
}

/* --------------------------------------------------------------------------------------------
 --------------------------------------------------------------------------------------------
 --------------------------------------------------------------------------------------------
BASIC PARSING FUNCTIONS
 Assume that all `defines are constants so we can change the constant into a number (see def_reduct by performing a search in this file)
 --------------------------------------------------------------------------------------------
 --------------------------------------------------------------------------------------------
 --------------------------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
 * (function: cleanup_hard_blocks)
 *   This function will correctly label nodes in the AST as being part of a
 *   hard block and not a module. This needs to be done post parsing since
 *   there are no language differences between hard blocks and modules.
 *--------------------------------------------------------------------------*/
void cleanup_hard_blocks()
{
	int i, j;
	ast_node_t *block_node, *instance_node, *connect_list_node;

	for (i = 0; i < size_block_instantiations; i++)
	{
		block_node = block_instantiations_instance[i];
		instance_node = block_node->children[1];
		instance_node->type = HARD_BLOCK_NAMED_INSTANCE;
		connect_list_node = instance_node->children[1];
		connect_list_node->type = HARD_BLOCK_CONNECT_LIST;

		for (j = 0; j < connect_list_node->num_children; j++)
		{
			connect_list_node->children[j]->type = HARD_BLOCK_CONNECT;
		}
	}
	return;
}

/*---------------------------------------------------------------------------------------------
 * (function: init_parser)
 *-------------------------------------------------------------------------------------------*/
void init_parser()
{
	defines_for_file_sc = sc_new_string_cache();

	defines_for_module_sc = NULL;

	/* record of each of the individual modules */
	num_modules = 0; // we're going to record all the modules in a list so we can build a tree of them later
	ast_modules = NULL;
	module_names_to_idx = sc_new_string_cache();
	module_instantiations_instance = NULL;
	size_module_instantiations = 0;
	block_instantiations_instance = NULL;
	size_block_instantiations = 0;

	/* keeps track of all the ast roots */
	all_file_items_list = NULL;
	size_all_file_items_list = 0;
}

/*---------------------------------------------------------------------------------------------
 * (function: cleanup_parser)
 *-------------------------------------------------------------------------------------------*/
void cleanup_parser()
{
	int i;
	
	/* frees all the defines for module string caches (used for parameters) */
	if (num_modules > 0)
	{
		for (i = 0; i < num_modules; i++)
		{
			sc_free_string_cache(defines_for_module_sc[i]);
		}
		
		free(defines_for_module_sc);
	}
}

/*---------------------------------------------------------------------------------------------
 * (function: init_parser_for_file)
 *-------------------------------------------------------------------------------------------*/
void init_parser_for_file()
{
	/* crrate a hash for defines so we can look them up when we find them */
	defines_for_module_sc = (STRING_CACHE**)realloc(defines_for_module_sc, sizeof(STRING_CACHE*)*(num_modules+1));
	defines_for_module_sc[num_modules] = sc_new_string_cache();

	/* create string caches to hookup PORTS with INPUT and OUTPUTs.  This is made per module and will be cleaned and remade at next_module */
	modules_inputs_sc = sc_new_string_cache();
	modules_outputs_sc = sc_new_string_cache();
}

/*---------------------------------------------------------------------------------------------
 * (function: clean_up_parser_for_file)
 *-------------------------------------------------------------------------------------------*/
void clean_up_parser_for_file()
{
	/* cleanup the defines hash */
	sc_free_string_cache(defines_for_file_sc);
}

/*---------------------------------------------------------------------------------------------
 * (function: next_parsed_verilog_file)
 *-------------------------------------------------------------------------------------------*/
void next_parsed_verilog_file(ast_node_t *file_items_list)
{
	int i;
	/* optimization entry point */
	printf("Optimizing module by AST based optimizations\n");
	optimizations_on_AST(file_items_list);

	if (configuration.output_ast_graphs == 1)
	{
		/* IF - we want outputs for the graphViz files of each module */
		for (i = 0; i < file_items_list->num_children; i++)
		{
			assert(file_items_list->children[i]);
			graphVizOutputAst(configuration.debug_output_path, file_items_list->children[i]);
		}
	}

	/* store the root of this files ast */
	all_file_items_list = (ast_node_t**)realloc(all_file_items_list, sizeof(ast_node_t*)*(size_all_file_items_list+1));
	all_file_items_list[size_all_file_items_list] = file_items_list;
	size_all_file_items_list ++;
}

/*---------------------------------------------------------------------------------------------
 * (function: newSymbolNode)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newSymbolNode(char *id, int line_number)
{
	long sc_spot;
	ast_node_t *current_node;

	if (id[0] == '`')
	{
		/* IF - this is a define replace with number constant */ 
		/* def_reduct */

		/* get the define symbol from the string cache */
		if ((sc_spot = sc_lookup_string(defines_for_file_sc, (id+1))) == -1)
		{
			error_message(PARSE_ERROR, line_number, current_parse_file, "Define \"%s\" used but not declared\n", id);
		}

		/* return the number node */
		return (ast_node_t*)defines_for_file_sc->data[sc_spot];
	}
	else
	{
		/* create node */
		current_node = create_tree_node_id(id, line_number, current_parse_file);
	}

	return current_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newNumberNode)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newNumberNode(char *num, int line_number)
{
	ast_node_t *current_node = create_tree_node_number(num, line_number, current_parse_file);
	return current_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newList)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newList(ids node_type, ast_node_t *child)
{
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(node_type, yylineno, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 1, child);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newList_entry)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newList_entry(ast_node_t *list, ast_node_t *child)
{
	/* allocate child nodes to this node */
	add_child_to_node(list, child);

	return list;
}

/*---------------------------------------------------------------------------------------------
 * (function: newListReplicate)
 * Basically this functions emulates verilog replication: {5{1'b0}} by concatenating that many
 * children together -- certainly not the most elegant solution, but was the easiest
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newListReplicate(ast_node_t *exp, ast_node_t *child )
{
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(CONCATENATE, yylineno, current_parse_file);
	
	new_node->types.concat.num_bit_strings = -1;

	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 1, child);
	
	int i;
	for (i = 1; i < exp->types.number.value; i++) 
	{
		add_child_to_node(new_node, child);
	}

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: markAndProcessSymbolListWith)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *markAndProcessSymbolListWith(ids id, ast_node_t *symbol_list)
{
	int i;
	long sc_spot;
	ast_node_t *range_min = 0;
	ast_node_t *range_max = 0;

	for (i = 0; i < symbol_list->num_children; i++)
	{
		/* checks range is legal */
		get_range(symbol_list->children[i]);

		if ((i == 0) && (symbol_list->children[0]->children[1] != NULL)&& (symbol_list->children[0]->children[2] != NULL) && (symbol_list->children[0]->children[1]->type == NUMBERS) && (symbol_list->children[0]->children[2]->type == NUMBERS))
		{
			range_max = symbol_list->children[0]->children[1];
			range_min = symbol_list->children[0]->children[2];
		}

		if ((symbol_list->children[i]->children[1] == NULL) && (symbol_list->children[i]->children[2] == NULL))
		{
			symbol_list->children[i]->children[1] = range_max;
			symbol_list->children[i]->children[2] = range_min;
		}

		switch(id)
		{
			case PORT:
			{
				short found_match = FALSE;

				symbol_list->children[i]->types.variable.is_port = TRUE;
				
				/* find the related INPUT or OUTPUT definition and store that instead */
				if ((sc_spot = sc_lookup_string(modules_inputs_sc, symbol_list->children[i]->children[0]->types.identifier)) != -1)
				{
					symbol_list->children[i]->types.variable.is_input = TRUE;
					symbol_list->children[i]->children[0] = (ast_node_t*)modules_inputs_sc->data[sc_spot];
					found_match = TRUE;
				}
				if ((found_match == FALSE) && ((sc_spot = sc_lookup_string(modules_outputs_sc, symbol_list->children[i]->children[0]->types.identifier)) != -1))
				{
					symbol_list->children[i]->types.variable.is_output = TRUE;
					symbol_list->children[i]->children[0] = (ast_node_t*)modules_outputs_sc->data[sc_spot];
					found_match = TRUE;
				}
				
				if (found_match == FALSE)
				{
					error_message(PARSE_ERROR, symbol_list->children[i]->line_number, current_parse_file, "No matching input declaration for port %s\n", symbol_list->children[i]->children[0]->types.identifier);
				}
				break;
			}
			case PARAMETER:
			{
				int binary_range = -1;
				if (i == 0)
				{
					binary_range = get_range(symbol_list->children[i]);
				}
				
				/* fifth spot in the children list holds a parameter value */
				if (binary_range != -1)
				{
					/* check that the parameter size matches the number included */
					 if((symbol_list->children[i]->children[5]->types.number.size != 0) 
						&& (symbol_list->children[i]->children[5]->types.number.base == BIN) 
						&& (symbol_list->children[i]->children[5]->types.number.size != binary_range)) 
					{
						error_message(PARSE_ERROR, symbol_list->children[i]->children[5]->line_number, current_parse_file, "parameter %s and range %d don't match\n", symbol_list->children[i]->children[0]->types.identifier, binary_range);
					}
					else
					{
						symbol_list->children[i]->children[5]->types.number.size = binary_range; // assign the binary range 
					}
				}

				/* create an entry in the symbol table for this parameter */
				if ((sc_spot = sc_add_string(defines_for_module_sc[num_modules], symbol_list->children[i]->children[0]->types.identifier)) == -1)
				{
					error_message(PARSE_ERROR, symbol_list->children[i]->children[5]->line_number, current_parse_file, "define has same name (%s).  Other define migh be in another file.  Odin considers a define as global.\n", 
						symbol_list->children[i]->children[0]->types.identifier, 
						((ast_node_t*)(defines_for_module_sc[num_modules]->data[sc_spot]))->line_number);
				}

				defines_for_module_sc[num_modules]->data[sc_spot] = (void*)symbol_list->children[i]->children[5];
				/* mark the node as shared so we don't delete it */
				symbol_list->children[i]->children[5]->shared_node = TRUE;
				/* now do the mark */
				symbol_list->children[i]->types.variable.is_parameter = TRUE;
				break;
			}
			case INPUT:
				symbol_list->children[i]->types.variable.is_input = TRUE;
				/* add this input to the modules string cache */
				if ((sc_spot = sc_add_string(modules_inputs_sc, symbol_list->children[i]->children[0]->types.identifier)) == -1)
				{
					error_message(PARSE_ERROR, symbol_list->children[i]->children[0]->line_number, current_parse_file, "Module already has input with this name %s\n", symbol_list->children[i]->children[0]->types.identifier);
				}
				/* store the data which is an idx here */
				modules_inputs_sc->data[sc_spot] = (void*)symbol_list->children[i];
				break;
			case OUTPUT:
				symbol_list->children[i]->types.variable.is_output = TRUE;
				/* add this output to the modules string cache */
				if ((sc_spot = sc_add_string(modules_outputs_sc, symbol_list->children[i]->children[0]->types.identifier)) == -1)
				{
					error_message(PARSE_ERROR, symbol_list->children[i]->children[0]->line_number, current_parse_file, "Module already has input with this name %s\n", symbol_list->children[i]->children[0]->types.identifier);
				}
				/* store the data which is an idx here */
				modules_outputs_sc->data[sc_spot] = (void*)symbol_list->children[i];
				break;
			case INOUT:
				symbol_list->children[i]->types.variable.is_inout = TRUE;
				error_message(PARSE_ERROR, symbol_list->children[i]->children[0]->line_number, current_parse_file, "Odin does not handle inouts (%s)\n", symbol_list->children[i]->children[0]->types.identifier);
				break;
			case WIRE:
				symbol_list->children[i]->types.variable.is_wire = TRUE;
				break;
			case REG:
				symbol_list->children[i]->types.variable.is_reg = TRUE;
				break;
			default:
				oassert(FALSE);
		}
	}

	return symbol_list;
}
/*---------------------------------------------------------------------------------------------
 * (function: newArrayRef)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newArrayRef(char *id, ast_node_t *expression, int line_number)
{
	/* allocate or check if there's a node for this */
	ast_node_t *symbol_node = newSymbolNode(id, line_number);
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(ARRAY_REF, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 2, symbol_node, expression);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newRangeRef)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newRangeRef(char *id, ast_node_t *expression1, ast_node_t *expression2, int line_number)
{
	/* allocate or check if there's a node for this */
	ast_node_t *symbol_node = newSymbolNode(id, line_number);
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(RANGE_REF, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 3, symbol_node, expression1, expression2);
	/* swap the direction so in form [MSB:LSB] */
	get_range(new_node);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newBinaryOperation)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newBinaryOperation(operation_list op_id, ast_node_t *expression1, ast_node_t *expression2, int line_number)
{
	info_ast_visit_t *node_details = NULL;
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(BINARY_OPERATION, line_number, current_parse_file);
	/* store the operation type */
	new_node->types.operation.op = op_id;
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 2, expression1, expression2);

	/* see if this binary expression can have some constant folding */
	node_details = constantFold(new_node);
	if ((node_details != NULL) && (node_details->is_constant_folded == TRUE))
	{
		new_node = node_details->from;
		free(node_details);
	}

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newUnaryOperation)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newUnaryOperation(operation_list op_id, ast_node_t *expression, int line_number)
{
	info_ast_visit_t *node_details = NULL;
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(UNARY_OPERATION, line_number, current_parse_file);
	/* store the operation type */
	new_node->types.operation.op = op_id;
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 1, expression);

	/* see if this binary expression can have some constant folding */
	node_details = constantFold(new_node);
	if ((node_details != NULL) && (node_details->is_constant_folded == TRUE))
	{
		new_node = node_details->from;
		free(node_details);
	}

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newNegedgeSymbol)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newNegedgeSymbol(char *symbol, int line_number)
{
	/* get the symbol node */
	ast_node_t *symbol_node = newSymbolNode(symbol, line_number);
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(NEGEDGE, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 1, symbol_node);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newPosedgeSymbol)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newPosedgeSymbol(char *symbol, int line_number)
{
	/* get the symbol node */
	ast_node_t *symbol_node = newSymbolNode(symbol, line_number);
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(POSEDGE, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 1, symbol_node);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newCaseItem)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newCaseItem(ast_node_t *expression, ast_node_t *statement, int line_number)
{
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(CASE_ITEM, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 2, expression, statement);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newDefaultCase)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newDefaultCase(ast_node_t *statement, int line_number)
{
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(CASE_DEFAULT, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 1, statement);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newNonBlocking)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newNonBlocking(ast_node_t *expression1, ast_node_t *expression2, int line_number)
{
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(NON_BLOCKING_STATEMENT, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 2, expression1, expression2);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newBlocking)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newBlocking(ast_node_t *expression1, ast_node_t *expression2, int line_number)
{
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(BLOCKING_STATEMENT, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 2, expression1, expression2);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newIf)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newIf(ast_node_t *compare_expression, ast_node_t *true_expression, ast_node_t *false_expression, int line_number)
{
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(IF, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 3, compare_expression, true_expression, false_expression);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newIfQuestion) for f = a ? b : c;
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newIfQuestion(ast_node_t *compare_expression, ast_node_t *true_expression, ast_node_t *false_expression, int line_number)
{
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(IF_Q, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 3, compare_expression, true_expression, false_expression);

	return new_node;
}
/*---------------------------------------------------------------------------------------------
 * (function: newCase)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newCase(ast_node_t *compare_expression, ast_node_t *case_list, int line_number)
{
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(CASE, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 2, compare_expression, case_list);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newAlways)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newAlways(ast_node_t *delay_control, ast_node_t *statement, int line_number)
{
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(ALWAYS, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 2, delay_control, statement);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newModuleConnection)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newModuleConnection(char* id, ast_node_t *expression, int line_number)
{
	ast_node_t *symbol_node;
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(MODULE_CONNECT, line_number, current_parse_file);
	if (id != NULL)
	{
		symbol_node = newSymbolNode(id, line_number);
	}
	else
	{
		symbol_node = NULL;
	}

	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 2, symbol_node, expression);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newModuleParameter)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newModuleParameter(char* id, ast_node_t *expression, int line_number)
{
	ast_node_t *symbol_node;
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(MODULE_PARAMETER, line_number, current_parse_file);
	if (id != NULL)
	{
		error_message(PARSE_ERROR, line_number, current_parse_file, "Specifying parameters by name not currently supported!\n");
		symbol_node = newSymbolNode(id, line_number);
	}
	else
	{
		symbol_node = NULL;
	}
	
	if (expression->type != NUMBERS)
	{
		error_message(PARSE_ERROR, line_number, current_parse_file, "Parameter value must be of type NUMBERS!\n");
	}

	/* allocate child nodes to this node */
	// leave 4 blank nodes so that expression is the 6th node to behave just like  
	// a default var_declare parameter (see create_symbol_table_for_module)
	allocate_children_to_node(new_node, 6, symbol_node, NULL, NULL, NULL, NULL, expression);

	// set is_parameter for the same reason
	new_node->types.variable.is_parameter = TRUE;

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newModuleNamedInstance)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newModuleNamedInstance(char* unique_name, ast_node_t *module_connect_list, ast_node_t *module_parameter_list, int line_number)
{
	ast_node_t *symbol_node = newSymbolNode(unique_name, line_number);

	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(MODULE_NAMED_INSTANCE, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 3, symbol_node, module_connect_list, module_parameter_list);

	return new_node;
}

/*-------------------------------------------------------------------------
 * (function: newHardBlockInstance)
 *-----------------------------------------------------------------------*/
ast_node_t *newHardBlockInstance(char* module_ref_name, ast_node_t *module_named_instance, int line_number)
{
	ast_node_t *symbol_node = newSymbolNode(module_ref_name, line_number);

	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(HARD_BLOCK, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 2, symbol_node, module_named_instance);

	/* store the hard block symbol name that this calls in a list that will at the end be asociated with the hard block node */
	block_instantiations_instance = (ast_node_t **)realloc(block_instantiations_instance, sizeof(ast_node_t*)*(size_block_instantiations+1));
	block_instantiations_instance[size_block_instantiations] = new_node;
	size_block_instantiations++;

	return new_node;
}

/*-------------------------------------------------------------------------
 * (function: newModuleInstance)
 *-----------------------------------------------------------------------*/
ast_node_t *newModuleInstance(char* module_ref_name, ast_node_t *module_named_instance, int line_number)
{
#ifdef VPR6
	if (sc_lookup_string(hard_block_names, module_ref_name) != -1)
	{
		return newHardBlockInstance(module_ref_name, module_named_instance, line_number);
	}
#endif

	// make a unique module name based on its parameter list
	ast_node_t *module_param_list = module_named_instance->children[2];
	char *module_param_name = make_module_param_name(module_param_list, module_ref_name);
	ast_node_t *symbol_node = newSymbolNode(module_param_name, line_number);

	// if this is a parameterised instantiation
	if (module_param_list)
	{
		// which doesn't exist in ast_modules yet
		long sc_spot;
		if ((sc_spot = sc_lookup_string(module_names_to_idx, module_param_name)) == -1)
		{
			// then add it, but set it to the symbol_node, because the 
			// module in question may not have been parsed yet
			// later, we convert this symbol node back into a module node
			ast_modules = (ast_node_t **)realloc(ast_modules, sizeof(ast_node_t*)*(num_modules+1));
			ast_modules[num_modules] = symbol_node;
			num_modules++;
			sc_spot = sc_add_string(module_names_to_idx, module_param_name);
			module_names_to_idx->data[sc_spot] = symbol_node;
			defines_for_module_sc = (STRING_CACHE**)realloc(defines_for_module_sc, sizeof(STRING_CACHE*)*(num_modules+1));
			defines_for_module_sc[num_modules] = NULL;
		}
	}

	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(MODULE_INSTANCE, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 2, symbol_node, module_named_instance);

	/* store the module symbol name that this calls in a list that will at the end be asociated with the module node */
	module_instantiations_instance = (ast_node_t **)realloc(module_instantiations_instance, sizeof(ast_node_t*)*(size_module_instantiations+1));
	module_instantiations_instance[size_module_instantiations] = new_node;
	size_module_instantiations++;

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newGateInstance)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newGateInstance(char* gate_instance_name, ast_node_t *expression1, ast_node_t *expression2, ast_node_t *expression3, int line_number)
{
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(GATE_INSTANCE, line_number, current_parse_file);
	ast_node_t *symbol_node = NULL;

	if (gate_instance_name != NULL)
	{
		symbol_node = newSymbolNode(gate_instance_name, line_number);
	}

	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 4, symbol_node, expression1, expression2, expression3);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newGate)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newGate(operation_list op_id, ast_node_t *gate_instance, int line_number)
{
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(GATE, line_number, current_parse_file);
	/* store the operation type */
	new_node->types.operation.op = op_id;
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 1, gate_instance);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newAssign)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newAssign(ast_node_t *statement, int line_number)
{
	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(ASSIGN, line_number, current_parse_file);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 1, statement);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newVarDeclare)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newVarDeclare(char* symbol, ast_node_t *expression1, ast_node_t *expression2, ast_node_t *expression3, ast_node_t *expression4, ast_node_t *value, int line_number)
{
	ast_node_t *symbol_node = newSymbolNode(symbol, line_number);

	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(VAR_DECLARE, line_number, current_parse_file);

	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 6, symbol_node, expression1, expression2, expression3, expression4, value);

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: newModule)
 *-------------------------------------------------------------------------------------------*/
ast_node_t *newModule(char* module_name, ast_node_t *list_of_ports, ast_node_t *list_of_module_items, int line_number)
{
	long sc_spot;
	ast_node_t *symbol_node = newSymbolNode(module_name, line_number);

	/* create a node for this array reference */
	ast_node_t* new_node = create_node_w_type(MODULE, line_number, current_parse_file);
	/* mark all the ports symbols as ports */
	markAndProcessSymbolListWith(PORT, list_of_ports);
	/* allocate child nodes to this node */
	allocate_children_to_node(new_node, 3, symbol_node, list_of_ports, list_of_module_items);

	/* store the list of modules this module instantiates */
	new_node->types.module.module_instantiations_instance = module_instantiations_instance;
	new_node->types.module.size_module_instantiations = size_module_instantiations;
	new_node->types.module.is_instantiated = FALSE;
	new_node->types.module.index = num_modules;

	/* record this module in the list of modules (for evaluation later in terms of just nodes) */
	ast_modules = (ast_node_t **)realloc(ast_modules, sizeof(ast_node_t*)*(num_modules+1));
	ast_modules[num_modules] = new_node;

	if ((sc_spot = sc_add_string(module_names_to_idx, module_name)) == -1)
	{
		error_message(PARSE_ERROR, line_number, current_parse_file, "module names with the same name -> %s\n", module_name);
	}
	/* store the data which is an idx here */
	module_names_to_idx->data[sc_spot] = (void*)new_node;

	/* now that we've bottom up built the parse tree for this module, go to the next module */
	next_module();

	return new_node;
}

/*---------------------------------------------------------------------------------------------
 * (function: next_module)
 *-------------------------------------------------------------------------------------------*/
void next_module()
{
	num_modules ++;

	/* define the string cache for the next module */
	defines_for_module_sc = (STRING_CACHE**)realloc(defines_for_module_sc, sizeof(STRING_CACHE*)*(num_modules+1));
	defines_for_module_sc[num_modules] = sc_new_string_cache();

	/* create a new list for the instantiations list */
	module_instantiations_instance = NULL;
	size_module_instantiations = 0;

	/* old ones are done so clean */
	sc_free_string_cache(modules_inputs_sc);
	sc_free_string_cache(modules_outputs_sc);
	/* make for next module */
	modules_inputs_sc = sc_new_string_cache();
	modules_outputs_sc = sc_new_string_cache();
}

/*--------------------------------------------------------------------------
 * (function: newDefparam)
 *------------------------------------------------------------------------*/
void newDefparam(char *inst, char *param, char *val, int line_number)
{
	ast_node_t *sym_node = newSymbolNode(val, line_number);
	sym_node->shared_node = TRUE;
}

/*--------------------------------------------------------------------------
 * (function: newConstant)
 *------------------------------------------------------------------------*/
void newConstant(char *id, char *number, int line_number)
{
	long sc_spot;
	ast_node_t *number_node = newNumberNode(number, line_number);

	/* add the define character string to the parser and maintain the number around */
	/* def_reduct */
	if ((sc_spot = sc_add_string(defines_for_file_sc, id)) == -1)
	{
		error_message(PARSE_ERROR, current_parse_file, line_number, "define with same name (%s) on line %d\n", id, ((ast_node_t*)(defines_for_file_sc->data[sc_spot]))->line_number);
	}
	/* store the data */
	defines_for_file_sc->data[sc_spot] = (void*)number_node;
	/* mark node as shared */
	number_node->shared_node = TRUE;
}

/* --------------------------------------------------------------------------------------------
 --------------------------------------------------------------------------------------------
 --------------------------------------------------------------------------------------------
AST VIEWING FUNCTIONS
 --------------------------------------------------------------------------------------------
 --------------------------------------------------------------------------------------------
 --------------------------------------------------------------------------------------------*/

int unique_label_count;
/*---------------------------------------------------------------------------
 * (function: graphVizOutputAst)
 *-------------------------------------------------------------------------*/
void graphVizOutputAst(char* path, ast_node_t *top)
{
	char path_and_file[4096];
	FILE *fp;
	static int file_num = 0;

	/* open the file */
	sprintf(path_and_file, "%s/%s_ast.dot", path, top->children[0]->types.identifier);
	fp = fopen(path_and_file, "w");
	file_num++;

        /* open graph */
        fprintf(fp, "digraph G {\t\nranksep=.25;\n");
	unique_label_count = 0;
	
	graphVizOutputAst_traverse_node(fp, top, NULL, -1);

        /* close graph */
        fprintf(fp, "}\n");
	fclose(fp);
}

/*---------------------------------------------------------------------------------------------
 * (function: graphVizOutputAst_traverse_node)
 *-------------------------------------------------------------------------------------------*/
void graphVizOutputAst_traverse_node(FILE *fp, ast_node_t *node, ast_node_t *from, int from_num)
{
	int i;
	int my_label = unique_label_count;

	/* increase the unique count for other nodes since ours is recorded */
	unique_label_count++;

	if (node == NULL)
	{
		/* print out the node and label details */
	}
	else
	{
		switch(node->type)
		{
			case FILE_ITEMS:
				fprintf(fp, "\t%d [label=\"FILE_ITEMS\"];\n", my_label);
				break;
			case MODULE: 
				fprintf(fp, "\t%d [label=\"MODULE\"];\n", my_label);
				break;
			case MODULE_ITEMS:
				fprintf(fp, "\t%d [label=\"MODULE_ITEMS\"];\n", my_label);
				break;
			case VAR_DECLARE:
				{
					char temp[4096] = "";
					if(node->types.variable.is_input)
					{
						sprintf(temp, "%s INPUT", temp);
					}
					if(node->types.variable.is_output)
					{
						sprintf(temp, "%s OUTPUT", temp);
					}
					if(node->types.variable.is_inout)
					{
						sprintf(temp, "%s INOUT", temp);
					}
					if(node->types.variable.is_port)
					{
						sprintf(temp, "%s PORT", temp);
					}
					if(node->types.variable.is_parameter)
					{
						sprintf(temp, "%s PARAMETER", temp);
					}
					if(node->types.variable.is_wire)
					{
						sprintf(temp, "%s WIRE", temp);
					}
					if(node->types.variable.is_reg)
					{
						sprintf(temp, "%s REG", temp);
					}
					fprintf(fp, "\t%d [label=\"VAR_DECLARE %s\"];\n", my_label, temp);
				}
				break;
			case VAR_DECLARE_LIST:
				fprintf(fp, "\t%d [label=\"VAR_DECLARE_LIST\"];\n", my_label);
				break;
			case ASSIGN:
				fprintf(fp, "\t%d [label=\"ASSIGN\"];\n", my_label);
				break;
			case GATE:
				fprintf(fp, "\t%d [label=\"GATE\"];\n", my_label);
				break;
			case GATE_INSTANCE:
				fprintf(fp, "\t%d [label=\"GATE_INSTANCE\"];\n", my_label);
				break;
			case MODULE_CONNECT_LIST:
				fprintf(fp, "\t%d [label=\"MODULE_CONNECT_LIST\"];\n", my_label);
				break;
			case MODULE_CONNECT:
				fprintf(fp, "\t%d [label=\"MODULE_CONNECT\"];\n", my_label);
				break;
			case MODULE_PARAMETER_LIST:
				fprintf(fp, "\t%d [label=\"MODULE_PARAMETER_LIST\"];\n", my_label);
				break;
			case MODULE_PARAMETER:
				fprintf(fp, "\t%d [label=\"MODULE_PARAMETER\"];\n", my_label);
				break;
			case MODULE_NAMED_INSTANCE:
				fprintf(fp, "\t%d [label=\"MODULE_NAMED_INSTANCE\"];\n", my_label);
				break;
			case MODULE_INSTANCE:
				fprintf(fp, "\t%d [label=\"MODULE_INSTANCE\"];\n", my_label);
				break;
			case HARD_BLOCK:
				fprintf(fp, "\t%d [label=\"HARD_BLOCK\"];\n", my_label);
				break;
			case HARD_BLOCK_NAMED_INSTANCE:
				fprintf(fp, "\t%d [label=\"HARD_BLOCK_NAMED_INSTANCE\"];\n", my_label);
				break;
			case HARD_BLOCK_CONNECT:
				fprintf(fp, "\t%d [label=\"HARD_BLOCK_CONNECT\"];\n", my_label);
				break;
			case HARD_BLOCK_CONNECT_LIST:
				fprintf(fp, "\t%d [label=\"HARD_BLOCK_CONNECT_LIST\"];\n", my_label);
				break;
			case BLOCK:
				fprintf(fp, "\t%d [label=\"BLOCK\"];\n", my_label);
				break;
			case NON_BLOCKING_STATEMENT:
				fprintf(fp, "\t%d [label=\"NON_BLOCKING_STATEMENT\"];\n", my_label);
				break;
			case BLOCKING_STATEMENT:
				fprintf(fp, "\t%d [label=\"BLOCKING_STATEMENT\"];\n", my_label);
				break;
			case CASE:
				fprintf(fp, "\t%d [label=\"CASE\"];\n", my_label);
				break;
			case CASE_LIST:
				fprintf(fp, "\t%d [label=\"CASE_LIST\"];\n", my_label);
				break;
			case CASE_ITEM:
				fprintf(fp, "\t%d [label=\"CASE_ITEM\"];\n", my_label);
				break;
			case CASE_DEFAULT:
				fprintf(fp, "\t%d [label=\"CASE_DEFAULT\"];\n", my_label);
				break;
			case ALWAYS:
				fprintf(fp, "\t%d [label=\"ALWAYS\"];\n", my_label);
				break;
			case DELAY_CONTROL:
				fprintf(fp, "\t%d [label=\"DELAY_CONTROL\"];\n", my_label);
				break;
			case POSEDGE:
				fprintf(fp, "\t%d [label=\"POSEDGE\"];\n", my_label);
				break;
			case NEGEDGE:
				fprintf(fp, "\t%d [label=\"NEGEDGE\"];\n", my_label);
				break;
			case IF:
				fprintf(fp, "\t%d [label=\"IF\"];\n", my_label);
				break;
			case IF_Q:
				fprintf(fp, "\t%d [label=\"IF_Q\"];\n", my_label);
				break;
			case BINARY_OPERATION:
				switch (node->types.operation.op)
				{
					case ADD:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION ADD\"];\n", my_label);
						break;
					case MINUS:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION MINUS\"];\n", my_label);
						break;
					case BITWISE_NOT:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION BITWISE_NOT\"];\n", my_label);
						break;
					case BITWISE_AND:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION BITWISE_AND\"];\n", my_label);
						break;
					case BITWISE_OR:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION BITWISE_OR\"];\n", my_label);
						break;
					case BITWISE_NAND:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION BITWISE_NAND\"];\n", my_label);
						break;
					case BITWISE_NOR:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION BITWISE_NOR\"];\n", my_label);
						break;
					case BITWISE_XNOR:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION BITWISE_XNOR\"];\n", my_label);
						break;
					case BITWISE_XOR:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION BITWISE_XOR\"];\n", my_label);
						break;
					case LOGICAL_NOT:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION LOGICAL_NOT\"];\n", my_label);
						break;
					case LOGICAL_OR:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION LOGICAL_OR\"];\n", my_label);
						break;
					case LOGICAL_AND:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION LOGICAL_AND\"];\n", my_label);
						break;
					case MULTIPLY:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION MULTIPLY\"];\n", my_label);
						break;
					case DIVIDE:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION DIVIDE\"];\n", my_label);
						break;
					case MODULO:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION MODULO\"];\n", my_label);
						break;
					case LT:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION LT\"];\n", my_label);
						break;
					case GT:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION GT\"];\n", my_label);
						break;
					case LOGICAL_EQUAL:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION LOGICAL_EQUAL\"];\n", my_label);
						break;
					case NOT_EQUAL:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION NOT_EQUAL\"];\n", my_label);
						break;
					case LTE:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION LTE\"];\n", my_label);
						break;
					case GTE:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION GTE\"];\n", my_label);
						break;
					case SR:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION SR\"];\n", my_label);
						break;
					case SL:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION SL\"];\n", my_label);
						break;
					case CASE_EQUAL:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION CASE_EQUAL\"];\n", my_label);
						break;
					case CASE_NOT_EQUAL:
						fprintf(fp, "\t%d [label=\"BINARY_OPERATION\"];\n", my_label);
						break;
					default:
						break;
				}
				break;
			case UNARY_OPERATION:
				switch (node->types.operation.op)
				{
					case ADD:
						fprintf(fp, "\t%d [label=\"UNARY_OPERATION ADD\"];\n", my_label);
						break;
					case MINUS:
						fprintf(fp, "\t%d [label=\"UNARY_OPERATION MINUS\"];\n", my_label);
						break;
					case BITWISE_NOT:
						fprintf(fp, "\t%d [label=\"UNARY_OPERATION BITWISE_NOT\"];\n", my_label);
						break;
					case BITWISE_AND:
						fprintf(fp, "\t%d [label=\"UNARY_OPERATION BITWISE_AND\"];\n", my_label);
						break;
					case BITWISE_OR:
						fprintf(fp, "\t%d [label=\"UNARY_OPERATION BITWISE_OR\"];\n", my_label);
						break;
					case BITWISE_NAND:
						fprintf(fp, "\t%d [label=\"UNARY_OPERATION BITWISE_NAND\"];\n", my_label);
						break;
					case BITWISE_NOR:
						fprintf(fp, "\t%d [label=\"UNARY_OPERATION BITWISE_NOR\"];\n", my_label);
						break;
					case BITWISE_XNOR:
						fprintf(fp, "\t%d [label=\"UNARY_OPERATION BITWISE_XNOR\"];\n", my_label);
						break;
					case BITWISE_XOR:
						fprintf(fp, "\t%d [label=\"UNARY_OPERATION BITWISE_XOR\"];\n", my_label);
						break;
					case LOGICAL_NOT:
						fprintf(fp, "\t%d [label=\"UNARY_OPERATION LOGICAL_NOT\"];\n", my_label);
						break;
					default:
						break;
				}
				break;
			case ARRAY_REF:
				fprintf(fp, "\t%d [label=\"ARRAY_REF\"];\n", my_label);
				break;
			case RANGE_REF:
				fprintf(fp, "\t%d [label=\"RANGE_REF\"];\n", my_label);
				break;
			case CONCATENATE:
				fprintf(fp, "\t%d [label=\"CONCATENATE\"];\n", my_label);
				break;
			case IDENTIFIERS:
				fprintf(fp, "\t%d [label=\"IDENTIFIERS:%s\"];\n", my_label, node->types.identifier);
				break;
			case NUMBERS:
				switch (node->types.number.base)
				{
					case(DEC):
						fprintf(fp, "\t%d [label=\"NUMBERS DEC:%s\"];\n", my_label, node->types.number.number);
						break;
					case(HEX):
						fprintf(fp, "\t%d [label=\"NUMBERS HEX:%s\"];\n", my_label, node->types.number.number);
						break;
					case(OCT):
						fprintf(fp, "\t%d [label=\"NUMBERS OCT:%s\"];\n", my_label, node->types.number.number);
						break;
					case(BIN):
						fprintf(fp, "\t%d [label=\"NUMBERS BIN:%s\"];\n", my_label, node->types.number.number);
						break;
					case(LONG_LONG):
						fprintf(fp, "\t%d [label=\"NUMBERS LONG_LONG:%lld\"];\n", my_label, node->types.number.value);
						break;
				}
				break;
			default:
				oassert(FALSE);
		}	
	}

	if (node != NULL)
	{
		/* print out the connection with the previous node */
		if (from != NULL)
			fprintf(fp, "\t%d -> %d;\n", from_num, my_label);

		for (i = 0; i < node->num_children; i++)
		{
			graphVizOutputAst_traverse_node(fp, node->children[i], node, my_label);
		}
	}
}
