/*
 * Simple PASCAL compiler
 * File name: table.c 
 * ----------------------
 * Implements the variable table. 
 *    Chakma Junan
 *    e993178@edu.tut.ac.jp
 *    Information and Computer Science
 *    Toyohashi University of Technology
 */
#include <stdio.h>
#include <stdlib.h>
#include "table.h"

static	int	address;
/*
 * Function: init_table();
 * Usage: return_value = init_table();
 * -----------------------------------
 * This function initializes the variable 
 * table.
 */
struct list *init_table()
{
        struct  list *local_table;

	local_table = (struct list *) malloc(sizeof(struct list));
	local_table->head = NULL;
	local_table->tail = NULL;
	address = 0;
	return local_table;
}

/*
 * Function: newItem
 * Usage: item = newItem(int address, char *s);
 * -----------------------------------------
 * Creates a new table_item(Please notice
 * that a table_item declared like this in
 * the header file: table.h,
 * struct listItem
 *    {
 *     int address; 
 *     char variable_name[16];
 *      struct listItem *successor;
 *    };
 */
struct listItem *newItem(int address, char *s)
{
	struct listItem *newitem;

	newitem = (struct listItem *)malloc(sizeof(struct listItem));
	newitem->address = address;
	strcpy(newitem->variable_name, s);
	newitem->successor = NULL;
	return newitem;
}

/*
 * Function: clean_table
 * Usage: (void)clean_table(struct list *local_table);
 * ---------------------------------------------------
 * This function was originally designed to clean a
 * table from its garbage. But it isn't necessary from
 * anywhere. So, why does it stands here? It stands here
 * cause I want to use it for future purpose.
 */
void clean_table(struct list *local_table)
{   
    struct listItem *tmp;
	
    tmp = local_table->head;
    while(tmp != NULL)
    {
       if(strncmp("TEMP", tmp->variable_name, 4) == 0)
       {
          local_table->head = tmp->successor;
       }
    tmp = tmp->successor;
    }
}   

/*
 * Function: find_address
 * Usage: i = find_address(char *s);
 * ---------------------------------
 * Searches the table for a variable name 's'.
 * If it exists, it returns it's address, 
 * otherwise it returns -1.
 */
int find_address(char *s, struct list *local_table)
{
    struct listItem *tmp;

    tmp = local_table->head;
    while(tmp != NULL)
    {
         if(strcmp(s, tmp->variable_name) == 0)
		return tmp->address;
	 tmp = tmp->successor;
    }
    return -1;
}

/*
 * Function: append
 * Usage: i = append(struct listItem *item);
 * -----------------------------------------
 * This function appends a table! It shouldn't be used
 * from anywhere.
 *
 * I wasn't sure which should I use: push or append for a
 * variable table. So, I created both to check which one
 * works better. At the last, I decided to use push without
 * any proven reason though :(
 */
int append(struct listItem *item, struct list *local_table)
{
	if(!local_table) init_table();
	if(!local_table->tail)
	{
		local_table->head = item;
		local_table->tail = item;
	}
	else
	{
		local_table->tail->successor = item;
		local_table->tail = item;
	}
	item->successor = NULL;
	return 0;
}

/*
 * Function: printData
 * Usage: (void) printData();
 * --------------------------
 * Just for debugging purpose. Prints the variable
 * table. Please make sure that the variable table
 * has the appropiate variables.
 */
void printData(struct list *local_table)
{
	struct listItem *temp;

	if(local_table != NULL)
		temp = local_table->head;
	else
	        temp = NULL;	
	while(temp != NULL)
	{
		printf("%d %s \n", temp->address, temp->variable_name);
		temp = temp->successor;
	}
}
/*
 * Function: countData
 * Usage: int data = countData(struct list *local_table);
 * -------------------------------------------------------
 * This function counts data presently registered in the
 * variable table 'local_table'.
 */
int countData(struct list *local_table)
{
	struct listItem *temp;
	int    counter = 0;

	if(local_table != NULL)
		temp = local_table->head;
	else
		temp = NULL;
	while(temp != NULL)
	{
		temp = temp->successor;
		counter++;
	}
	return counter;
}

/*
 * Function: push
 * Usage: push(struct listItem *table_item);
 * -----------------------------------------
 * This function pushes(actually 'add' should be the appropiate
 * word) a variable in the table. Please refer to the 'table.h'
 * file for the definition of 'struct listItem'.
 */
void push(struct listItem *table_item, struct list *local_table)
{
	table_item->successor = local_table->head;
	local_table->head = table_item;
}

/*
 * Function: table();
 * Usage: table(char *s);
 * ----------------------
 * This function accepts a 'char *s', considers it to be a variable
 * name and if there's no variables with the same name in the table,
 * adds the variable in the table.
 */
void table(char *s, struct list *local_table)
{
	struct listItem *table_item;

	if(find_address(s, local_table) == -1)
	{
	        table_item = newItem(address++, s);
		push(table_item, local_table);
	}
	else
	{
		error("Double declaration of variable: %s \n", s);
	}
}
