
#include <stdio.h>
#include <string.h>
#include "parser.h"

extern int yylex();	    /* global variables to communicate with the lexer */
extern char * yytext;	/*	generated by LEX */
extern int yylineno;

char *function_names[100] = 
{"COND","ADD","SUB","MULT","DIV","MOD","EQ","LT","GT","AND","OR","MINUS","NOT"};

char *param_names[25];
short function_indx = 13;
short param_indx = 0;
short param_max = 0;
short recurse = 0;

char comma;
short goal_bool = 0;
short param_bool = 0;

int define_cnt = 0;
int token; 	    /* current token */
int stmtno = 1;	/* stmt counter */

void print_read_number()
{
		 printf("int read_number() {\n");
     printf("    int number;\n");
     printf("    printf(\"Please input a number: \");\n");
     printf("    scanf(\"\%s\", &number);\n", "%d");
     printf("    return number;\n");
     printf("}\n");
}	

void program()
{				    
    printf("#include <stdio.h>\n#include \"compiler.h\"\n\n");
		print_read_number();

    advance();

		function_definitions();
		if (match(QUESTION)) {
			advance();
			goal_expression();
		}
		else {
			printf("\nError line %d: missing '?'\n", yylineno);
			exit(1);
		}

    printf("\b\b);\n    exit(0);\n}\n\n/* # of Defines: %d */\n", define_cnt);
}

void function_definitions()
{
	function_definition();

	if (match(EOF)) {
		printf("\nError line %d: missing '?'\n", yylineno);
		exit(1);
	}
	else if (!match(QUESTION)) {
		function_definitions();
	}
	else if (match(QUESTION))
		printf(");\n}\n");
}

void goal_expression()
{
	printf("\nint main() {\n");
	printf("    printf(\"The goal expression yields: %s\\n\", %s\n          ",
		  	"%d", function_names[function_indx-1]);
  goal_bool = 1;
	expression();	
}


void expression()
{
	if (match (NUMBER)) {
		printf("%s", yytext);
		advance();
	}
	else if (match(IDENTIFIER)) {
		parameter_name(' ');
	}
	else if (match(LP)) {
		advance();
		simple_expression();
		if (match(RP)) {
			advance();
		}
		else {
			printf("\nError line %d: missing right paren (expression)\n", yylineno);
			exit(1);
		}
	}
}


void function_definition()
{
	if (match(LP)) {
		advance();
		if (match(DEFINE_T)) {
			define_cnt++;
			advance();
			if (match(LP)) {
				advance();
				function_name();
				parameter_names();
				if (match(RP)) {
					advance();
					expression();
					if (match(RP))
						advance();
				}
			}
		}
	}
	else {
		printf("\nError: Missing Left Paren!\n");
		exit(1);
	}
}

void function_name()
{
	if (match(IDENTIFIER)) {
		if (goal_bool == 0) {
			param_bool = 1;
			function_names[function_indx] = (char*) malloc (sizeof(yytext));
			strcpy (function_names[function_indx],  yytext);
			function_indx++;
			if (recurse == 0)
 				printf("\nint %s (", function_names[function_indx-1]);
			else 
				printf("(%s)", function_names[function_indx-1]);
			recurse = 1;
		}
		advance();
	}
}

void parameter_names()
{
	//param_indx = 0;
	param_max = 0;
//	comma = ',';
	parameter_name();
//	comma = ' ';
	if (!match(RP)) {
		parameter_names();
	}
	else if (match(RP)) {
		param_bool = 0;
		printf("\b\b) {\n    return (");
	}
}

void parameter_name()
{
	if (match(IDENTIFIER)) {
		param_names[param_indx] = (char*) malloc (sizeof(yytext));
	  strcpy (param_names[param_indx], yytext);
		param_indx++;
		param_max++;	
		if ((param_bool == 1) && (recurse == 0)) 
			printf("%cint %s, ", comma, yytext);
		else	
			printf("%s", yytext);
		advance();
	}
}


void simple_expression()
{
	if (match(ADD_T) || match(SUB_T) || match(MULT_T) || match(DIV_T) ||
		match(MOD_T) || match(EQ_T) || match(LT_T) || match(GT_T) ||
		match(AND_T) || match(OR_T)) 
	{
		bin_operation();
		//expression();
		//expression();
	}
	else if (match(MINUS_T) || match (NOT_T)) {
		un_operation();
		//expression();
	}
	else if (match(IDENTIFIER)) {
		function_name();
		expression_list();
	}
	else if (match(COND_T)) {
		advance();
		branch();
		branches();
	}
	else if (match(READ_NUMBER_T)) {
		printf("read_number(), ");
		advance();
	}	
}


void branches()
{
	branch();
	if (match(EOF)) {
		printf("\nError line %d: unexpected End Of File!");
		exit(1);
	}
	if (!match(RP))
		branches();
}


void branch()
{	
	if (match(LP)) {
		advance();
    expression();
		printf(" ?");
		printf(" : ");
		expression();
		printf(" ");
		if (match(RP)) {
			advance();
		}
		else {
			printf("\nError line %d: missing right paren (branch)");
			exit(1);
		}
	}
}

void expression_list()
{
	expression();
	if (match(EOF)) {
		printf("\nError line %d: unexpected End Of File!");
		exit(1);
	}
	if (!match(RP))
		expression_list();
}

void bin_operation()
{
	if (match(ADD_T)) {
		advance();
		printf("(");
		expression();
		printf(" + ");
		expression();
		printf(")");
	}
	else if (match(SUB_T)) {
    advance();
	  printf("(");
	  expression();
	  printf(" - ");
	  expression();
	  printf(")");
	}
	else if (match(MULT_T)) {
	  advance();
		printf("(");
		expression();
		printf(" * ");
		expression();
		printf(")");
	}
	else if (match(DIV_T)) {
	  advance();
		printf("(");
		expression();
		printf(" / ");
		expression();
		printf(")");
	}
	else if (match(MOD_T)) {
	  advance();
		printf("(");
		expression();
		printf(" % ");
		expression();
		printf(")");
	}
	else if (match(EQ_T)) {
	  advance();
		printf("(");
		expression();
		printf(" == ");
		expression();
		printf(")");
	}
	else if (match(LT_T)) {
	  advance();
		printf("(");
		expression();
		printf(" < ");
		expression();
		printf(")");
	}
	else if (match(GT_T)) {
	  advance();
		printf("(");
		expression();
		printf(" > ");
		expression();
		printf(")");
	}
	else if (match(AND_T)) {
	  advance();
		printf("(");
		expression();
		printf(" && ");
		expression();
		printf(")");
	}
	else if (match(OR_T)) {
	  advance();
		printf("(");
		expression();
		printf(" || ");
		expression();
		printf(")");
	}
}

void un_operation()
{
	if (match(NOT_T)) {
		printf("!(", yytext);
		advance();
		expression();
		printf(")");
	}
	else if (match(MINUS_T)) {
		printf("-", yytext);
		advance();
		expression();
		printf(" ");
	}
}


int match(int t)
{
	return (token == t);
}

void advance()	/* calls yylex to get next token */
{
	token = yylex();
}
