#include "SynAnalyzer.h"

#include "Stack.h"
#include "Map.h"
#include <cstdlib>

SynAnalyzer::SynAnalyzer(FILE *source,  Interpreter *interpreter): lex(source)
{
	this->interpreter = interpreter;
}

SynAnalyzer::~SynAnalyzer() {}

void SynAnalyzer::nextLexem()
{
	current = lex.GetNext();
}

void SynAnalyzer::readStatement()
{
	switch (current.GetType())
	{
		case LexAnalyzer::Lexem::l_var:
		{
			nextLexem();
			readVar();
			
			if (current.GetType() != LexAnalyzer::Lexem::l_assign)
				throw SyntaxError("Assignment expected.", current.GetLine());
				
			nextLexem();			
			readExpression();
			append(new PolizAssign);
			
			break;
		}
		
		case LexAnalyzer::Lexem::l_identifier:
		{
			String name = current.GetInfo();
			nextLexem();
			readCall();
			append(new PolizFunction(name));
			
			break;
		}
		
		case LexAnalyzer::Lexem::l_if:
		{
			nextLexem();
			if (current.GetType() != LexAnalyzer::Lexem::l_eopen)
				throw SyntaxError("'(' expected after 'if' keyword.", current.GetLine());
			nextLexem();
			readExpression();			
			if (current.GetType() != LexAnalyzer::Lexem::l_eclose)
				throw SyntaxError("')' expected after expression in 'if' operator.", current.GetLine());
			
			nextLexem();
			readStatement();
			if (current.GetType() == LexAnalyzer::Lexem::l_else)
			{
				nextLexem();
				readStatement();
			}
				
			break;
		}
		
		case LexAnalyzer::Lexem::l_while:
		{
			nextLexem();
			if (current.GetType() != LexAnalyzer::Lexem::l_eopen)
				throw SyntaxError("'(' expected after 'while' keyword.", current.GetLine());
			nextLexem();
			readExpression();			
			if (current.GetType() != LexAnalyzer::Lexem::l_eclose)
				throw SyntaxError("')' expected after expression in 'while' operator.", current.GetLine());
			
			nextLexem();
			readStatement();
										
			break;
		}
		
		case LexAnalyzer::Lexem::l_bopen:
		{
			nextLexem();
			readStatement();
			
			while (current.GetType() == LexAnalyzer::Lexem::l_semi)
			{
				nextLexem();
				readStatement();
			}
			
			if (current.GetType() != LexAnalyzer::Lexem::l_bclose)
				throw SyntaxError("'}' expected.", current.GetLine());
				
			nextLexem();
			
			break;
		}
		
		default:
			throw SyntaxError("Unknown statement.", current.GetLine());
	}
}

void SynAnalyzer::readCall()
{
	if (current.GetType() == LexAnalyzer::Lexem::l_eopen)
	{
		nextLexem();
		readArgs();
		if (current.GetType() != LexAnalyzer::Lexem::l_eclose)
			throw SyntaxError("')' expected after the fuction arguments.", current.GetLine());
		nextLexem();
	}
}

void SynAnalyzer::readArgs()
{
	unsigned count = 1;
	readExpression();
	while (current.GetType() == LexAnalyzer::Lexem::l_fix)
	{
		nextLexem();
		readExpression();
		count++;
	}
	append(new PolizInt(count));
}

void SynAnalyzer::readExpression()
{
	readAndOp();
	while (current.GetType() == LexAnalyzer::Lexem::l_or)
	{
		nextLexem();
		readAndOp();
		append(new PolizOr());
	}
}

void SynAnalyzer::readAndOp()
{
	readBoolOp();
	while (current.GetType() == LexAnalyzer::Lexem::l_and)
	{
		nextLexem();
		readBoolOp();
		append(new PolizOr());
	}
}

void SynAnalyzer::readBoolOp()
{
	readSumOp();
	PolizElement *tmp;
	switch (current.GetType())
	{
		case LexAnalyzer::Lexem::l_more: tmp = new PolizMore(); break;
		case LexAnalyzer::Lexem::l_less: tmp = new PolizLess(); break;
		case LexAnalyzer::Lexem::l_eq: tmp = new PolizEq(); break;
		case LexAnalyzer::Lexem::l_noteq: tmp = new PolizNotEq(); break;
		case LexAnalyzer::Lexem::l_lesseq: tmp = new PolizLessEq(); break;
		case LexAnalyzer::Lexem::l_moreeq: tmp = new PolizMoreEq(); break;
		
		default: return;
	}
	
	nextLexem();
	readSumOp();
	append(tmp);
}

void SynAnalyzer::readSumOp()
{
	readTerm();
	PolizElement *tmp;	
	do
	{	
		switch (current.GetType())	
		{
			case LexAnalyzer::Lexem::l_plus: tmp = new PolizPlus(); break;
			case LexAnalyzer::Lexem::l_minus: tmp = new PolizMinus(); break;
			
			default: return;
		}
		
		nextLexem();
		readTerm();
		append(tmp);
	} while (true);
}

void SynAnalyzer::readTerm()
{
	readPrimary();	
	PolizElement *tmp;
	do	
	{
		switch (current.GetType())	
		{
			case LexAnalyzer::Lexem::l_mul: tmp = new PolizMul(); break;
			case LexAnalyzer::Lexem::l_div: tmp = new PolizDiv(); break;
			case LexAnalyzer::Lexem::l_mod: tmp = new PolizMod(); break;
			
			default: return;
		}
		
		nextLexem();
		readPrimary();
		append(tmp);
	} while (true);
}

void SynAnalyzer::readPrimary()
{
	switch (current.GetType())
	{
		case LexAnalyzer::Lexem::l_var:
		{
			nextLexem();
			readVar();
			
			break;
		}
		
		case LexAnalyzer::Lexem::l_identifier:
		{
			nextLexem();
			readCall();
			
			break;
		}
		
		case LexAnalyzer::Lexem::l_number:
		{
			append(new PolizInt(atoi(current.GetInfo().CStr())));
			nextLexem();
			break;
		}
		
		case LexAnalyzer::Lexem::l_string:
		{
			append(new PolizString(current.GetInfo()));
			nextLexem();
			break;
		}
		
		case LexAnalyzer::Lexem::l_eopen:
		{
			nextLexem();
			readExpression();
			if (current.GetType() != LexAnalyzer::Lexem::l_eclose)
				throw SyntaxError("')' expected after expression.", current.GetLine());
			nextLexem();
			
			break;
		}
		
		case LexAnalyzer::Lexem::l_minus:
		{
			nextLexem();
			readPrimary();
			append(new PolizUnar);
			
			break;
		}
		
		case LexAnalyzer::Lexem::l_not:
		{
			nextLexem();
			readPrimary();
			append(new PolizNot);
			
			break;
		}
		
		default:
			throw SyntaxError("Unknown primary in expression.", current.GetLine());
	}
}

void SynAnalyzer::readVar()
{
	if (current.GetType() != LexAnalyzer::Lexem::l_identifier)
		throw SyntaxError("identifier expected.", current.GetLine());
	String name = current.GetInfo();

	unsigned count = 0;
	nextLexem();		
	while (current.GetType() == LexAnalyzer::Lexem::l_iopen)
	{
		nextLexem();
		readExpression();
		if (current.GetType() != LexAnalyzer::Lexem::l_iclose)
			throw SyntaxError("']' expected.", current.GetLine());
		nextLexem();
		count++;
	}
	
	append(new PolizVariable(name));	
	if (count > 0) append(new PolizArray(count));
}

void SynAnalyzer::Analyze()
{	
	nextLexem();
	readStatement();
}
