/*
 * Simple PASCAL compiler
 * File name: statement.c
 * ----------------------
 * Statement part was implemented by
 *    Chakma Junan
 *    e993178@edu.tut.ac.jp
 *    Information and Computer Science
 *    Toyohashi University of Technology
 *
 * Rest was originally implemented by 
 *    ohno@edu.tut.ac.jp
 */
#include <stdio.h>
#include "getsym.h"

#define	DEBUG	1

extern	TOKEN	tok;
extern	FILE	*infile;
extern	FILE	*outfile;
extern	int	label;
extern	int	procedure_flag;
extern	struct	list	*Table;
extern	struct	list	*procedure_table;

extern	TOKEN	stack[100];
extern	int	top;

void	statement();
void	condition();
void	write_to_display();
void	expression();
void	term();
void	factor();


void statement()
{
        char tmp[MAXIDLEN+1];
	int  local_label;
        int   br_counter = 0;

	switch(tok.attr)
	{
	/* Just a dummy case */
	case NUMBER:
		printf( "got a NUMBER: %d!!! \n", tok.value);
		code_generator();
		getsym();
		break;
	/* Does a 'getsym()' and nothing else */
	case SYMBOL:
		if(tok.value == SEMICOLON)
		{
			getsym();
			return;
		}
		break;   /* Just breaking it....*/
	case IDENTIFIER:
		strcpy(tmp, tok.charvalue);
		getsym();
		if(tok.attr == SYMBOL && tok.value == BECOMES)
		{
			expression();

		        #if DEBUG == 1
		        gen_load("r3");
			gen_store(tmp);
		        #endif

                        /******* Cleaning TEMP variables ****/
			if(procedure_flag == 1)
			   clean_table(procedure_table);
			else
			   clean_table(Table);
                        /******* Cleaning TEMP variables ****/

                } else if(tok.attr == SYMBOL && tok.value == LPAREN)
		{

		  while(tok.value != RPAREN)
		  {
		     expression();
		     gen_load("r3");

                     /******* Cleaning TEMP variables ****/
		     if(procedure_flag == 1)
		        clean_table(procedure_table);
		     else
		        clean_table(Table);
                     /******* Cleaning TEMP variables ****/

		     if(procedure_flag == 1)
		      gen_store_param(br_counter+countData(procedure_table)+1);
		     else
		      gen_store_param(br_counter+countData(Table)+1);
		     br_counter++;

		     if(tok.value == COMMA) 
		     {
			continue;
		     }
		     if(tok.value == EOF)
		     {
			error("line: %d: ')' required. \n", tok.sline);
		     }
		  }
		  #if DEBUG == 1
		  if(procedure_flag == 1)
		     gen_call_before(procedure_table);
                  else
		     gen_call_before(Table);
		  #endif

		  #if DEBUG == 1
		  if(procedure_flag == 1)
		     gen_call_after(procedure_table, tmp);
                  else
		     gen_call_after(Table, tmp);
		  #endif

		  getsym();
		}
		break;
	case RWORD:
		switch(tok.value)
		{
		case BEGIN:
			do 
			{
				getsym();
				statement();
				if(tok.attr == ENDFILE)
				{
					error( "line: %d: 'end' is 
						required. \n", tok.sline);
				}
			} while(tok.attr != RWORD && tok.value != END);
			getsym();
			break;
		case WRITE:
			getsym();
			write_to_display();
			break;
		case WHILE:
			/* writing label */
	                local_label = label;

			#if DEBUG == 1
			gen_label(local_label);
			#endif

			condition();
			label++;
			if(tok.attr == RWORD && tok.value == DO)
			{
				getsym();
				statement();
			} else error("'do' is required!");

			#if DEBUG == 1
			gen_condition("jmp", local_label);
			gen_label(local_label + 1);
			#endif

			break;
		case IF:
			/* writing label */
	                local_label = label;
			condition();
			label++;
			if(tok.attr == RWORD && tok.value == THEN)
			{
				getsym();
				statement();
			} else error("line: %d: 'then' is required!", tok.sline);
			#if DEBUG == 1
			gen_condition("jmp", local_label + 2);
			gen_label(local_label + 1);
		        #endif

			if(tok.attr == RWORD && tok.value == ELSE)
			{
				getsym();
				statement();
			}

			#if DEBUG == 1
			gen_label(local_label + 2);
			#endif
			label++;

			break;
		}
	}
}

/*
 * Function: condition();
 * Usage: i = condition();
 * -----------------------
 * This function implements the 'condition' part.
 * Point to remember that, whenever this function
 * is called it should store the successive token
 * in the 'tok' variable.
 */
void condition()
{
        int  symbol;

	expression();

	#if DEBUG == 1
	gen_load("r3");
	#endif

	#if DEBUG == 1
	gen_loadr("r1", "r3");
	#endif

	symbol = tok.value;

	expression();

        #if DEBUG == 1
	gen_load("r3");
	#endif

        /* Generating code according to the symbol */
	#if DEBUG == 1
	gen_cmpr("r1", "r3");
	if(symbol == LESSEQL)  gen_condition("jgt", label + 1);
	if(symbol == GRTREQL)  gen_condition("jlt", label + 1);
	if(symbol == GRTRTHAN) gen_condition("jle", label + 1);
	if(symbol == LESSTHAN) gen_condition("jge", label + 1);
	if(symbol == NOTEQL)   gen_condition("jz ", label + 1);
	if(symbol == EQL)      gen_condition("jnz", label + 1);
	#endif

        /******* Cleaning TEMP variables ****/
	if(procedure_flag == 1)
	   clean_table(procedure_table);
	else
	   clean_table(Table);
        /******* Cleaning TEMP variables ****/

	label = label + 2;
}

/*
 * Funtion: write_to_display()
 * Usage: write_to_display();
 * ---------------------------
 * Writes the variables to the stderr(this happens
 * to be the display, or the terminal you should
 * be using).
 */
void write_to_display()
{
	#if DEBUG == 1
	gen_write(tok.charvalue);
	#endif

	getsym();
	if(tok.attr == SYMBOL && tok.value == COMMA)
	{
		getsym();
		write_to_display();
	}
}

/*
 * Function: expression
 * Usage: i = expression();
 * ------------------------
 * Implements the expression part. Point to remember
 * that this function reads the successive token and
 * stores it in 'tok'. So, whenever you call this 
 * function, you should take appropiate action. 
 * Important point to remember:
 *    It uses two registers 'r2' and 'r3' and finally
 *    it stores the result in the register 'r3'.
 */
void expression()
{
    int sign; /* for sign */

    term();
    sign = tok.value;
    while(tok.value == PLUS || tok.value == MINUS)
    {
	term();
	gen_code(sign);
	sign = tok.value;
    }
    /* 
     * you can add some codes here to perform some error
     * checking, like, what can come after expression!
     */
}
/*
 * Function: term
 * Usage: term();
 * --------------
 * Implements the 'term' part. It is called from the
 * function 'expression' immediately and muliplication
 * and division is performed before addition and 
 * subtraction. Here how it is implemented:
 * For example: 
 *             5 div 4 *  4;
 * is to be calculated. When term is called from 'expression'
 * above equation is marked in the following way:
 *             5 div 4 *  4;
 *                ^    ^
 *                |    |
 *               sign tok.value
 * So, when it enters the 'while' loop it performs the 'sign'
 * part before performing the 'tok.value' part. After the 'while'
 * loop is performed for one time the equation gets marked:
 *             5 div 4 *  4;
 *                     ^   ^
 *                     |   |
 *                    sign tok.value
 */
void term()
{
   int sign; /* for sign */

   factor();
   sign = tok.value;
   while(tok.value == TIMES || tok.value == DIV)
   {
       factor();
       gen_code(sign);
       sign = tok.value;
   }
}
/*
 * Function: factor
 * Usage: factor();
 * ----------------
 * Implements the 'factor' part. When it reads an IDETIFIER
 * it loads the contents of the IDENTIFIER's address. If it
 * reads a NUMBER, it loads it using 'loadi'(load immediate).
 * If it gets an '(', it looks for an ')' until it gets one.
 */
void factor()
{
    getsym();
    if(tok.attr == SYMBOL && tok.value == MINUS)
    {
       getsym();
       if(tok.attr == NUMBER || tok.attr == IDENTIFIER)
	  tok.sline = tok.sline * (-1);
       else
	  error("line: %d: IDENTIFIER or NUMBER expected. \n", tok.sline);
    }
    if(tok.attr == IDENTIFIER) 
    {
       /* Ok, producing code */
       stack[top++] = tok;
    } else if(tok.attr == NUMBER)
    {
       /* Ok, producing code */
       stack[top++] = tok;
    } else if(tok.value == LPAREN)
    while(tok.value != RPAREN)
    {
	expression();
	if(tok.attr == ENDFILE)
	{
	   error( "line: %d: ')' is required. \n", tok.sline);
	}
    }
    getsym();
}

showstack()
{
    int index;

    if(top == 0) return; /* Empty stack */
    index = top - 1;
    while(index >= 0)
    {
	if(stack[index].attr == IDENTIFIER)
	   printf( "Identifier: %s \n", stack[index].charvalue);
        else if(stack[index].attr == NUMBER)
	   printf( "Number: %d \n", stack[index].value);
        index--;
    }
}
