#include "lex.hpp"


//------------------------- LEXIC Module ------------------------//
//						  v 08 . 04 .  2007						 //
//																 //
//								MT								 //
//---------------------------------------------------------------//


//============================ HELPER ===================================

//--- Is <str> ---'
bool IsStr(char c, char *str)
{
	for (char* tmp = str; (*tmp) != '\0'; tmp++)
		if ( (*tmp) == c )	return true;

	return false;
};


//--- Is Digit ---
bool IsDigit(int c) { return isdigit(c); };


//--- Is Symbol ---
bool IsSymbol(int c) 
{
	if (
		c >= 'A' && c <= 'Z' ||
		c >= 'a' && c <= 'z' ||
		c == '_'
		) 
		return true;

	return false;
};


//--- Is Operation ---
bool IsSpacer(int c) 
{
	return IsStr(c, "+-*/%()|[]<>={},&|;!");
};


//--- Is Special ---
int IsSpecial(int c) 
{ 
	char str[5];
	str[0] = ' ';	str[1] = '\t';	str[2] = 13;	str[3] = '\n';
	str[4] = '\0';
	
	return IsStr(c, str);
};

//-----------------------------------------------------------------------


char CLexic :: GetChar()
{
	if (is_prev) 
	{
		c = for_prev;		
		is_prev = 0;
	}
		else
	{
		c_prev = c;
		c = fgetc(fp);
	}
 return c;
};


void CLexic :: GetPrevChar()
{
	if (is_prev) 
	{
		Error.OnError("Lexical analysis :: trying to read symbol more that 2 position. ", -511);
		c = 0;
	}
	
	for_prev = c;
	c = c_prev;
	is_prev = 1;
};


void CLexic :: Make_Ok(Lexeme *lx, char *s, int is_only_char)
{
	lx->line = current_line;
	lx->sign = ok;
	delete [] lx->s;

		if  (is_only_char) {
			lx->s = new char[2];
			lx->s[0] = s[0];
			lx->s[1] = '\0';
		} else
		lx->s = strdup(s);	
}


bool CLexic :: IsStatusTrue(lxStatus t, char c)
{
	switch (t) {
		case N: if ( IsDigit(c) ) return true; 
		break;	
		
		case I: if ( IsDigit(c) || IsSymbol(c) ) return true; 
		break;	

		case K: if ( IsSymbol(c) ) return true;
		break;		

		case S: if ( c != '"') return true;
		
		default : break;
	}

	return false;
}


lxStatus CLexic :: GetStatus (char c)
{
	if ( IsDigit(c) )	return N;

	if ( IsStr(c, "?@$.") )	return I;

	if ( IsSymbol(c) )	return K;

	if ( c == ':' )		return A;

	if ( c == '"' )		return S;

	if ( IsSpacer(c) )	return H;

	return X;
}


void  CLexic :: NIKS (lxStatus t, Lexeme *a)
{
		if ( t == S ) 
			GetChar();

	char s[256];
	int i = 0;
		
		
		do {
			if (c == EOF)  { a->sign = not_ok_all; return; }
			s[i] = c;
			i++;
			GetChar();
		} while (IsStatusTrue(t, c));
		
			s[i] = '\0';
			Make_Ok(a, s);

			if (t == N) a -> id = lx_int_const;
			if (t == S) a -> id = lx_str_const;
			if (t == K) a -> id = lx_keyword;
			if (t == I) {
				switch (s[0]) {
					case '?' : a->id = lx_func;	break;
					case '@' : a->id = lx_label;		break;
					case '$' : a->id = lx_var;	break; 
					case '.' : a->id = lx_robotdata;	break; 
				}
			  if (strlen(s)<2) a->sign = not_ok_all;
			}
}


Lexeme* CLexic :: GetNextLexem ()
{
	Lexeme *a = new Lexeme;
		
	if (stat == N || stat == I || stat == K) ; else GetChar();
		while (c != EOF) 
		{
			if (c == 13) current_line++; 
				else if (!IsStr(c, " \n\t")) break;
					
			GetChar();
		}

		if (c == EOF) { a->sign = ok_all; return a; }
	
		switch (stat = GetStatus(c)) {
			case N: case I : case K : case S : NIKS(stat, a); break; 

			case A :	char s[3]; s[0] = c; s[2]='\0';
							if ((s[1] = GetChar()) == EOF) break; 
						if (s[1] == '=') {						
							Make_Ok(a, s);
							a->id = lx_assign;
						} break;
	
			case H : 	Make_Ok(a, &c, 1);
						a->id = lx_spacer;
						break;

			default :	break;
		}
 return a;
}


bool CLexic :: MakeAnalysis() 
{
	Lexeme *l = GetNextLexem ();
	begin = l;

	while ( l -> sign == ok ) {
		l -> next = GetNextLexem ();
		if ( l->next->sign == ok_all && l->next->s == NULL) {
				l -> sign = ok_all;
				l -> next = NULL;
			 return true;
		}
	 l = l -> next;
	}

 return false;
}


Lexeme* CLexic :: GetBeginLexeme()
{
	return begin;
}


void CLexic :: Print(Lexeme *l)
{	
	printf("-------------------------------------------------\n");
	printf(" Line    ::  %d		Type    ::  _",  l->line) ;
		switch (l->id) {
			case lx_err:		printf( "error" ); break;
			case lx_assign:		printf( "assign" ); break;
			case lx_spacer:		printf( "spacer" ); break;
			case lx_int_const:	printf( "int_const" ); break;
			case lx_str_const:	printf( "str_const" ); break;
			case lx_var:		printf( "var" ); break;
			case lx_label:		printf( "label" ); break;
			case lx_func:		printf( "func" ); break;
			case lx_keyword:	printf( "keyword" ); break;
			case lx_robotdata:	printf( "robotdata" ); break;
		}

	printf("\n Lexeme:  '%s'\n", l->s);
	if (l->sign == not_ok_all) printf("		:: [error]"); 
	
		l = l -> next;
		if (l->next != NULL) Print(l);
}


CLexic :: CLexic (char *s) 
{ 
	fp = fopen(s, "r"); 
	if (fp==NULL) Error.OnError("Robot command file not open.",-511); 
	
	begin = NULL; 
	current_line = 1;   
	c=0; c_prev=0; is_prev=0; for_prev=0; 
	stat = H;
};

