#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()
//<identifier> ::= $<char>{<char>} 
{
	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;
};


//============================== HELPER ====================================+





void CSyntax :: sx_Expression_Primary()
{	
	NextLex();	IsCorrectExpress(current);
		
	//--- Int const
	if (current->id == lx_int_const) { return; };

	//--- Var
	if (sx_Identifier() != t_not_var) { 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 ---+
bool CSyntax :: sx_Statement_Robot()
{
/*	<robot_commands> ::= ? [turn | auto | prod <expression> | 
						sell <expression>, <expression> | buy <expression>, <expression>
						print "<string>", <expression> {,<expression>}  ]
*/
	if (current->id == lx_func) {
		//--- Turn
		if (strcmp(current->s, "?turn\0") == 0) return true;
		//--- Auto
		if (strcmp(current->s, "?auto\0") == 0) return true;
		//--- Prod
		if (strcmp(current->s, "?prod\0") == 0){ sx_Expression(); return true; };
		//--- Sell
		if (strcmp(current->s, "?sell\0") == 0)
			{	sx_Expression(); NextLex();
				if (current->s[0] != ',') throw "Missed ',' in function '?sell'. \0";
				sx_Expression(); 
			 return true; 
			};
		//--- Buy
		if (strcmp(current->s, "?buy\0") == 0)
			{	sx_Expression(); NextLex();
				if (current->s[0] != ',') throw "Missed ',' in function '?buy'. \0";
				sx_Expression(); 
			 return true; 
			};
		//--- Print
		if (strcmp(current->s, "?print\0") == 0)
			{	
				NextLex();
					if (current->id != lx_str_const) throw "String in '?print' missed. \0";
					
				NextLex();
				while (current->s[0] == ',') 
				{
					sx_Expression(); 				
					NextLex();
				};
				BackLex();
				
			 return true; 
			};

		throw "Such robot function not found.";
	}
  return false;
};


bool CSyntax :: sx_Statement_Indetifier()
{
	
	if ( sx_Identifier() != t_not_var)
	 {
		//--- lx_assign :=
		NextLex();
		if (current->id == lx_assign) 
			sx_Expression();
		else throw "Expect ':='. \0";

	  return true;
	};
 return false;
};


bool CSyntax :: sx_Statement_IfElse()
{
	if (strcmp(current->s, "if\0") == 0) {
	
		NextLex();
			if (current->s[0] != '(') throw "Missed '('. \0";
		sx_Expression();
		NextLex();
			if (current->s[0] != ')') throw "Missed ')'. \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();
		
	  return true;
	}
  return false;
};


bool CSyntax :: sx_Statement_Cycle()
{
	if (current->s[0] == '{') 
	{
		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";

	 return true;
	};
	 
 return false;
};


bool CSyntax :: sx_Statement_Goto()
{
	//--- Goto 
	if (strcmp(current->s, "goto\0") == 0) {
		NextLex();
			if (current->id != lx_label) throw "Goto parametr is incorrect. \0";
		return true;
	}

	//--- lx_label declaration
	if (current->id == lx_label) {
		
		return true;
	}
 
 return false;
};


void CSyntax :: sx_Statement()
{
	bool res = false;

// <statement> ::= 	
	//--- Nothing 	
	if (next->s[0] == ';') res = true;
	if (next->s[0] == '}') res = true;
	if (!res) NextLex();

	//--- <identifier> {'['<expression>']'} = <expression> | 
	if (sx_Statement_Indetifier()) res = true;
	//--- if (<expression>) <statement> [else <statement>] |
	if (sx_Statement_IfElse())  res = true;
	//--- '{' <statement> {;<statement>} '}' |
	if (sx_Statement_Goto()) res = true;
	//--- <robot_commands>
	if (sx_Statement_Robot()) res = true;

	//--- goto <lx_label> | <lx_label>
	if (sx_Statement_Cycle()) res = true;

		//--- NOT MATCH	  
		if (!res) throw "Not match statement. \0";
};

//============================================================ xs_Statement ===+


bool CSyntax :: sx_Program()
{
	try {  sx_Statement();  }
	catch (const char *message) {
		ShowError(message);
	  return false; 
	}

  return true;
};


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;
};


