#include "syn.hpp"


//------------------- SYNTAX ANALYZER Module --------------------//
//						  v 09 . 04 .  2007						 //
//																 //
//								MT								 //
//---------------------------------------------------------------//



//------------------------------ HELPER ---------------------------
void  IsCorrectExpress(Lexeme *current){
	if (current->id == lx_assign ||
		current->id == lx_label ||
		current->id == lx_func ||
		current->id == lx_str_const ||
		current->id == lx_keyword )
			throw "Using wrong operand in expression. \0";
};


type  CSyntax :: sx_Identifier()
{
	if (current -> id == lx_var)
		if (next->s[0] == '[') {
		
			NextLex();  sx_Expression();  NextLex();

			if (current->s[0] != ']')  throw "Missed ']' in array (lx_var). \0" ;
			return t_var_array;

		} else return t_var;

	else return t_not_var;
};


type  CSyntax :: sx_RobotData()
//  <lx_robotdata> ::= .<name_data>{.<name_data>}
//  <name_data> ::= <char>{<char>}[ '['<expression>']' ]
{
	type res = t_not_var;

	do {
		if (current -> id == lx_robotdata) {
			res = t_var;
			//---------------------------
			if (next->s[0] == '[') {
				NextLex();  sx_Expression();  NextLex();
				if (current->s[0] != ']')  throw "Missed ']' in array (robot_data). \0" ;
				res = t_var_array;
			};
			//===========================
		} else break;

		if (next->id == lx_robotdata) NextLex(); 
		else break;
	} while (1);
	
	return res;
};



void  CSyntax :: NextLex() 
{   
	if (current->next == NULL) throw LEX_OVER;

	if (prev == NULL) prev = begin; 
	else { 
		prev=current; 
		current = next; 
		if (current->next != NULL) 
			next=current->next; 
	}
};


bool  CSyntax :: Comp(char *s) 
{   
	return (strcmp(current->s, s) == 0);
};


bool  CSyntax :: IsO1HigherO2(char o1, char o2)
{	// + -   * / %
	char o1p = 0, o2p = 0;

	if (IsStr(o1,"-+"))
		o1p = 1;
	if (IsStr(o2,"-+"))
		o2p = 1;

	if (IsStr(o1,"*/%"))
		o1p = 2;
	if (IsStr(o2,"*/%"))
		o2p = 2;

	if (IsStr(o1,"<>="))
		o1p = 3;
	if (IsStr(o2,"<>="))
		o2p = 3;

	if (IsStr(o1,"&|"))
		o1p = 4;
	if (IsStr(o2,"&|"))
		o2p = 4;


	if (o2p==0 || o1p==0) 
		throw "Wrong operand in IsO1HighO2 call. ";

	return o1p > o2p;
};

//============================== HELPER ===========================

//------------------------------- SX ------------------------------

void  CSyntax :: sx_Expression_Primary()
{	type t;
	NextLex();	IsCorrectExpress(current);
	//--- Int const
	if (current->id == lx_int_const) 
		{ 
			PolizConstInt *tmp = new PolizConstInt(atoi(current->s));
			exe->AddElem(tmp);
			return; 
		};
	//--- Var
	if ((t = sx_Identifier()) != t_not_var) 
	{ 
		if (t == t_var) {
			PolizFuncVarWrite *tmp 
				= new PolizFuncVarWrite(current->s);
			exe->Debug(current->s);
			exe->AddElem(tmp);	
		}
		if (t == t_var_array) {/*!!! ARRAY !!!*/ }
		return; 
	};
	//--- Robot Data
	if (sx_RobotData() != t_not_var) { return; };

	//--- ( Expression )
	if ( current->s[0] == '(' ) {
		sx_Expression();
		NextLex();
		if (current->s[0] != ')') throw "Missed ')'. \0" ;
		return;
	};
	//--- -<primary>
	if ( current->s[0] == '-' ) {	
		sx_Expression_Primary();
		return;
	};
	//--- !<primary>
	if ( current->s[0] == '!' ) {	
		sx_Expression_Primary();
		return;
	};

	throw "Not match expression, wrong operand. \0"; 
};


void  CSyntax :: sx_Expression_Term()
{	//	<term> ::= <primary> {[ *| /| %] <primary>}
	sx_Expression_Primary();

	do {	
		if (next->s[0] == '*') NextLex(); else 
		if (next->s[0] == '/') NextLex(); else 
		if (next->s[0] == '%') NextLex(); else break;
		sx_Expression_Primary(); 
	} while (1);	
};


void  CSyntax :: sx_Expression_Bool()
{	//	<bool> ::= <term> { [ +| - ] <term> }
	sx_Expression_Term();

	do {	
		if (next->s[0] == '+') NextLex(); else 
		if (next->s[0] == '-') NextLex(); else break;
		sx_Expression_Term(); 
	} while (1);
};


void  CSyntax :: sx_Expression_Or()
{	//  <or> ::= <bool> [ > |< |= ] <bool> ]

	sx_Expression_Bool();

	switch (next->s[0]) {
		case '>' : NextLex(); sx_Expression_Bool(); break;
		case '<' : NextLex(); sx_Expression_Bool(); break;
		case '=' : NextLex(); sx_Expression_Bool(); break;
	};
};


void  CSyntax :: sx_Expression_And()
{	//  <and> ::= <or> {'|' <or>}
	do {
		sx_Expression_Or(); 
		if (next->s[0] == '|') NextLex(); 
		else break;
	} while (1);
};


void  CSyntax :: sx_Expression()
{	//  <expression> ::= <and> {'&' <and>}

	do {
		try { sx_Expression_And(); }
		catch (const char *message) { ShowError(message); };
		
		if (next->s[0] == '&') NextLex(); 
		else break;
	} while (1);
};


//--------------------------------------------- xs_Statement ------

void  CSyntax :: sx_Statement_Robot_Print()
{
	NextLex();
	if (current->id != lx_str_const) 
		throw "String in '?print' missed. \0";
					
	NextLex();
	while (current->s[0] == ',') {
		sx_Expression(); 				
		NextLex();
	};

	BackLex();
};


void  CSyntax :: sx_Statement_Robot()
{
/*	<robot_commands> ::= ? [turn | auto | prod <expression> | 
						sell <expression>, <expression> | buy <expression>, <expression>
						print "<string>", <expression> {,<expression>}  ]  */
	//--- Turn
	if (Comp("?turn")) return;
	//--- Auto
	if (Comp("?auto")) return;
	//--- Prod
	if (Comp("?prod"))
	{ sx_Expression(); return; };
	//--- Sell
	if (Comp("?sell")) {
		sx_Expression(); NextLex();
		if (current->s[0] != ',') 
			throw "Missed ',' in function '?sell'. \0";
		sx_Expression(); 
		return; 
	};
	//--- Buy
	if (Comp("?buy")) {
		sx_Expression(); NextLex();
		if (current->s[0] != ',') 
			throw "Missed ',' in function '?buy'. \0";
		sx_Expression(); 
		return; 
	};
	//--- Print
	if (Comp("?print")) {
		sx_Statement_Robot_Print();
		return; 
	}
	throw "Such robot function not found.";
};


void  CSyntax :: sx_Statement_Indetifier()
{
	//--- lx_assign :=
	NextLex();
	if (current->id == lx_assign) 
		sx_Expression();
	else throw "Expect ':='. \0";
};


void  CSyntax :: sx_Statement_IfElse()
{
	NextLex();
	 if (current->s[0] != '(') throw "Missed '('. \0";
	sx_Expression();
	NextLex();
	 if (current->s[0] != ')') throw "Missed ')' in 'if' statement. \0";
	sx_Statement();
		
	NextLex();
	if ( strcmp(current->s, "else")==0 || strcmp(next->s, "else")==0 ) {
		if (strcmp(next->s, "else") == 0 && strcmp(current->s, "else") == 0) throw "Double 'else'. \0";
		if (strcmp(next->s, "else") == 0 || strcmp(current->s, "else") != 0) NextLex();
				
		sx_Statement();
	}
	else BackLex();
};


void  CSyntax :: sx_Statement_Cycle()
{
	try {
		do {  sx_Program();  NextLex();  }
		while (current->s[0] == ';');
	}
	catch (const char *s) { 
		if ( strcmp(s, LEX_OVER)==0 ) 
			throw "Lexems are over. May be missed '}'. \0";  
		else throw; 
	};

	if (current->s[0] != '}') throw "Missed '}'. \0";
};


void  CSyntax :: sx_Statement_Goto()
{
	//--- Goto 
	NextLex();
	 if (current->id != lx_label) 
		 throw "Goto parametr is incorrect. \0";
};


void  CSyntax :: sx_Statement()
{
	if (next->s[0] == ';' || next->s[0] == '}') return;
	else NextLex();

	switch (current->id) {
		//--- Spacer
		case lx_spacer : 
			if (Comp("{")) {
				sx_Statement_Cycle(); 
				return; }
			break;
		//--- Label
		case lx_label : return; break;
		//--- Robot Function
		case lx_func : 
			sx_Statement_Robot(); return;
			break;
		//--- Variable or array
		case lx_var : 
			if (sx_Identifier() != t_not_var) {
				sx_Statement_Indetifier(); 
				return; }
			break;
		//--- Keyword
		case lx_keyword : 
			if (Comp("if")) { 
				sx_Statement_IfElse();  
				return; }
			if (Comp("goto")) { 
				sx_Statement_Goto(); 
				return; }
			break;
		
		//--- Not match  
		default : throw "Not match statement. \0"; break;
	};
};

//============================================= xs_Statement ======

void CSyntax :: sx_Program()
{
	try { sx_Statement(); }
	catch (const char *message) {
		ShowError(message);
	}
};


void CSyntax :: ShowError(const char *message)
{
	Error.ClearLastText();
	  Error.Gum("Syntax :: Line \0");
	   char tmp[INT_SIZE] = {0};
	   snprintf (tmp, INT_SIZE, "%d", current->line);
	  Error.Gum(tmp);
	  Error.Gum(" --> \0");
	  Error.Gum(message);
	  Error.Gum("~ '");
	  Error.Gum(current->s);
	  Error.Gum("'");
	  Error.OnErrorSpit(-550, true);

	passed = passed & false;
};


bool CSyntax :: MakeAnalysis()
{
	sx_Program(); 
	return passed;
};


