#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_str_const ||
		current->id == lx_keyword 
		)
			throw "Using wrong operand in expression. \0";
};


type CSyntax :: sx_Identifier(bool adr)
{
	if (current -> id == lx_var) {
		char *var_name = current->s;
		if (next->s[0] == '[') {
			NextLex();  sx_Expression();  NextLex();
			if (current->s[0] != ']')  throw "Missed ']' in array. \0" ;

			if (!adr) {			
				PolizFuncVarArrayWrite *tmp = new PolizFuncVarArrayWrite(var_name);
				exe->AddElem(tmp);
			}	
			return t_var_array;
		} 
		else {	
			if (!adr) {
				PolizFuncVarWrite *tmp = new PolizFuncVarWrite(var_name	);
				exe->AddElem(tmp);	
			}
			return t_var;
		};
	}
	else return t_not_var;
};


void CSyntax :: sx_RobotData()
//  <lx_robotdata> ::= .<name_data>{.<name_data>}
//  <name_data> ::= <char>{<char>}[ '['<expression>']' ]
{
	char rt = 0, *s1 = NULL, *s2 = NULL;
	
	if (current -> id == lx_robotdata) {
		rt = 1;
		s1 = current->s;
		
		if (next->s[0] == '[') {
			NextLex();  sx_Expression();  NextLex();
			if (current->s[0] != ']')  throw "Missed ']' in array (robot_data). " ;
			
			if (next->id == lx_robotdata) {
				NextLex();
				s2 = current->s;
				rt = 3;
			} else throw "An Inadmissible robodata.";

		} else

		if (next->id == lx_robotdata) {
			NextLex();
			s2 = current->s;
			rt = 2;
		} 
			
		PolizFuncRoboWrite *tmp = new PolizFuncRoboWrite(s1, s2, rt);
		exe->AddElem(tmp);
		
	}; 
};



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);
};

//============================== HELPER ===========================


//------------------------------ POLIZ ----------------------------
bool CSyntax :: IsO1HigherO2(char o1, char o2)
{	
	int 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 (o2=='(' || o1=='(') 
		return false;
	if (o2p==0 || o1p==0) 
		return false;

	return o1p >= o2p;
};

void CSyntax :: StackToPOLIZ() 
{
	char *c = strdup(" ");
	do {
		free(c);
		if (stack.IsEmpty()) 
			throw " Poliz_Expression Bracks Error.";
		
		c = stack.Pop();
		if (c[0]!=')') AddToPOLIZ(c[0]);
				
	} while (c[0] != '(');
	free(c);
};


PolizElem* CSyntax :: AddToPOLIZ(char c) 
{
	PolizElem *tmp;
	switch (c) {
		case '+' : tmp = new PolizFuncPlus(); break; 
		case '*' : tmp = new PolizFuncMult(); break;
		case '/' : tmp = new PolizFuncDiv(); break; 
		case '%' : tmp = new PolizFuncMod(); break; 
		case '-' : tmp = new PolizFuncMinus(); break; 
		case '!' : tmp = new PolizFuncWow(); break; 

		case '&' : tmp = new PolizFuncAnd(); break;
		case '|' : tmp = new PolizFuncOr(); break;
		case '<' : tmp = new PolizFuncLower(); break;
		case '>' : tmp = new PolizFuncHigher(); break;
		case '=' : tmp = new PolizFuncEq(); break;
		default  : tmp = NULL; break;
		}
	if (tmp!=NULL) exe->AddElem(tmp);

	return tmp;
};

void CSyntax :: AddOperPOLIZ(char o2, bool gonext)
{
	char *o1 = stack.Pop();
	
	if (IsO1HigherO2(o1[0], o2)) {
		if (o1[0]=='(') Push("("); 
		else AddToPOLIZ(o1[0]);
		AddOperPOLIZ(o2,false);
	}
	else { 
		Push(o1[0]); 
		Push(o2);
	}
	
	delete[] o1;
	if (gonext) NextLex();
}


void CSyntax :: AddLabelPOLIZ(char *s)
{
	PolizElem *tmp = new PolizFuncLabelRead(s);
	exe->AddElem(tmp);
};


void CSyntax :: AddVarPOLIZ(char *var_name, type tmp_type)
{
	PolizElem *tmp;
	if (tmp_type==t_var) 
		tmp = new PolizFuncVarRead(var_name);
	else 
		tmp = new PolizFuncVarArrayRead(var_name);
	exe->AddElem(tmp);
};

//============================== POLIZ ============================


//------------------------------- SX ------------------------------
void  CSyntax :: sx_Expression_Primary()
{	
	exe->Debug("-PRI.");
	NextLex();	IsCorrectExpress(current);

	//--- Int const
	if (current->id == lx_int_const) 
	{		PolizElem *tmp = new PolizConstInt(atoi(current->s));
			exe->AddElem(tmp);		
			return; 
	};

	//--- Var
	if (current->id==lx_var) { sx_Identifier(false); return; }

	//--- Robot Data
	if (current->id == lx_robotdata) { sx_RobotData(); return; }


	//--- ( Expression )
	if ( current->s[0] == '(' ) {
		sx_Expression();
		NextLex();
		if (current->s[0] != ')') throw "Missed ')'. " ;
		return;
	};

	//--- -<primary>
	if ( current->s[0] == '-' ) {	
		sx_Expression_Primary();
		PolizElem *tmp = new PolizFuncPriMinus();
		exe->AddElem(tmp);	
		return;
	};

	//--- !<primary>
	if ( current->s[0] == '!' ) {	
		sx_Expression_Primary();
		PolizElem *tmp = new PolizFuncWow();
		exe->AddElem(tmp);	
		return;
	};

	//--- Macros
	if (Comp("?macro")) { 
		NextLex(); 
		if (current->id!=lx_str_const)
			throw "Missed macros name. ";
		PolizElem *tmp = new PolizFuncRobMacros(current->s);
		exe->AddElem(tmp);
		return;
	}; 
	
	throw "Not match expression, wrong operand. "; 
};


void  CSyntax :: sx_Expression_Term()
{	//	<term> ::= <primary> {[ *| /| %] <primary>}
	exe->Debug("TERM-");
	sx_Expression_Primary();

	do {	
		if (next->s[0] == '*') AddOperPOLIZ(next->s[0]);  else 
		if (next->s[0] == '/') AddOperPOLIZ(next->s[0]);  else 
		if (next->s[0] == '%') AddOperPOLIZ(next->s[0]);  else break;
		sx_Expression_Primary(); 
	} while (1);	
};


void  CSyntax :: sx_Expression_Bool()
{	//	<bool> ::= <term> { [ +| - ] <term> }
	exe->Debug("BOOL-");
	sx_Expression_Term();

	do {	
		if (next->s[0] == '+') { AddOperPOLIZ(next->s[0]); }
		else 
		if (next->s[0] == '-') { AddOperPOLIZ(next->s[0]); }
		else break;
		sx_Expression_Term(); 
	} while (1);
};


void  CSyntax :: sx_Expression_Or()
{	//  <or> ::= <bool> [ > |< |= ] <bool> ]
	exe->Debug("OR-");
	sx_Expression_Bool();

	switch (next->s[0]) {
		case '>':AddOperPOLIZ(next->s[0]); sx_Expression_Bool();break;
		case '<':AddOperPOLIZ(next->s[0]); sx_Expression_Bool();break;
		case '=':AddOperPOLIZ(next->s[0]); sx_Expression_Bool();break;
	};
};


void  CSyntax :: sx_Expression_And()
{	//  <and> ::= <or> {'|' <or>}
	exe->Debug("AND-");
	do {
		sx_Expression_Or(); 
		if (next->s[0] == '|') AddOperPOLIZ(next->s[0]); 
		else break;
	} while (1);
};


void  CSyntax :: sx_Expression()
{	//  <expression> ::= <and> {'&' <and>}
	Push("(");
	exe->Debug("\n| MAIN_EXPRESS-");
	do {
		try { sx_Expression_And(); }
		catch (const char *message) { ShowError(message); };
		
		if (next->s[0] == '&') {
			AddOperPOLIZ(next->s[0]);
		}
		else break;
	} while (1);
		
	StackToPOLIZ();
};


//--------------------------------------------- xs_Statement ------
void  CSyntax :: sx_Statement_Robot_Print()
{
	NextLex();
	if (current->id != lx_str_const) 
		throw "String in '?print' missed. ";
	
	char *s = current->s;
	
	NextLex();
	while (current->s[0] == ',') {
		sx_Expression(); 				
		NextLex();
	};

	PolizElem *p = new PolizConstStr(s);
	exe->AddElem(p);
	
	PolizElem *pr = new PolizFuncRobPrint();
	exe->AddElem(pr);

	BackLex();
};


void  CSyntax :: sx_Statement_Robot()
{
	PolizElem *tmp = NULL;
	//--- Turn
	if (Comp("?turn")) tmp = new PolizFuncRobTurn();
	//--- Upgrade
	if (Comp("?upgrade")) tmp = new PolizFuncRobUpgrade();
	//--- Auto
	if (Comp("?auto")) tmp = new PolizFuncRobAuto();
	//--- New Plant
	if (Comp("?build")) tmp = new PolizFuncRobPlant();
	//--- Auto Build
	if (Comp("?abuild")) tmp = new PolizFuncRobABuild();
	//--- Prod
	if (Comp("?prod"))
	{ sx_Expression(); tmp = new PolizFuncRobProd(); };
	//--- Sell
	if (Comp("?sell")) {
		sx_Expression(); NextLex();
		if (current->s[0] != ',') 
			throw "Missed ',' in function '?sell'. ";
		sx_Expression(); 
		tmp = new PolizFuncRobSell();
	};
	//--- Buy
	if (Comp("?buy")) {
		sx_Expression(); NextLex();
		if (current->s[0] != ',') 
			throw "Missed ',' in function '?buy'. ";
		sx_Expression(); 
		tmp = new PolizFuncRobBuy();
	};
	//--- Print
	if (Comp("?print")) {
		sx_Statement_Robot_Print();
		return; 
	};

	if (tmp!=NULL) { exe->AddElem(tmp); 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 ':='. ";
};


void  CSyntax :: sx_Statement_IfElse_Else(char *label_else)
{
	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();
				
		AddLabelPOLIZ(label_else);

		sx_Statement();
	}
	else BackLex();
};


void  CSyntax :: sx_Statement_IfElse()
{
	//--- (
	NextLex();
	if (current->s[0] != '(') throw "Missed '('. ";
	sx_Expression();
	//--- )
	NextLex();
	 if (current->s[0] != ')') throw "Missed ')' in 'if' statement. ";
	
	ifelse++;
	//--- Labels names
	char *label = new char[256];
	sprintf(label, "@@%d", ifelse);
	char *label_else = new char[256];
	sprintf(label_else, "@@else%d", ifelse);

	//--- if poliz insert
	PolizElem *tmp = new PolizFuncPassIf(label, label_else);
	exe->AddElem(tmp);

	sx_Statement();
	//--- goto poliz (to Default)
	tmp = new PolizFuncPassGo(label);
	exe->AddElem(tmp);

	sx_Statement_IfElse_Else(label_else);
	
	//--- Insert Label DEFAULT
	AddLabelPOLIZ(label);
};


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 '}'. ";  
		else throw; 
	};

	if (current->s[0] != '}') throw "Missed '}'. ";
};


void  CSyntax :: sx_Statement_Goto()
{
	//--- Goto 
	NextLex();
	if (current->id != lx_label) 
		throw "Goto parameter is incorrect. ";

	PolizElem *tmp = new PolizFuncPassGo(current->s);
	exe->AddElem(tmp);
};


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 : AddLabelPOLIZ(current->s); return;
		
		//--- Robot Function
		case lx_func : sx_Statement_Robot(); return;
			break;

		//--- Variable or array
		case lx_var : 
			if (current->id == lx_var) 
			{
				char *var_name = current -> s;
				type tmp_type = sx_Identifier(true);
				sx_Statement_Indetifier(); 
				AddVarPOLIZ(var_name, tmp_type);				
				return; 
			}
			break;

		//--- Keyword
		case lx_keyword : 
			if (Comp("if")) { sx_Statement_IfElse(); return; }
			if (Comp("goto")) { sx_Statement_Goto(); return; }
			break;

		//--- Like a comment 
		case lx_str_const : return; 
		
		//--- Not match  
		default : throw "Not match statement. "; break;
	};
};

//=============================== SX ==============================

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;
};


