#include "lex.hpp"


//============================ HELPER ===================================
//--- Is Digit ---
int IsDigit(int c) { return isdigit(c); };


//--- Is Symbol ---
int IsSymbol(int c) 
{
	if (
		c >= 'A' && c <= 'Z' ||
		c >= 'a' && c <= 'z' ||
		c == '_'
		) 
		return 1;
	return 0;
};


//--- Is Operation ---
int IsOperation(int c) 
{
	if (
			c == '+' || 
			c == '-' ||
			c == '*' ||
			c == '/' ||
			c == '%' ||
			c == '(' ||
			c == ')' ||
			c == '|' ||
			c == '[' ||
			c == ']' ||
			c == '<' ||
			c == '>' ||
			c == '=' ||
			c == ';' 
		)	
		return 1;
	return 0;
};


//--- Is Special ---
int IsSpecial(int c) 
{
	if (
			c != ' ' ||  
			c != '\t'|| 
			c != 13  || 
			c != '\n' 
		)	
		return 1;
	return 0;
};

//-----------------------------------------------------------------------


void CLexic :: GetChar()
{
	if (is_prev) 
	{
		c = for_prev;		
		is_prev = 0;
	}
		else
	{
		c_prev = c;
		c = fgetc(fp);
	}
};


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);	
}


int CLexic :: IsStatusTrue(lxStatus t, char c)
{
	switch (t) {
		case N: if ( IsDigit(c) ) return 1; 
		break;	
		
		case I: if ( IsDigit(c) || IsSymbol(c) ) return 1; 
		break;	

		case K: if ( IsSymbol(c) ) return 1;
		break;		

		case S: if ( c != '"') return 1;
	}

	return 0;
}


lxStatus CLexic :: GetStatus (char c)
{
	if ( IsDigit(c) )	return N;

	if ( c == '?' || c == '@' || c == '$' || c == '.')	return I;

	if ( IsSymbol(c) )	return K;

	if ( c == ':' )		return A;

	if ( c == '"' )		return S;

	if ( IsOperation(c) )	return H;

	return X;
}


void  CLexic :: NIKS (lxStatus t, Lexeme *a, int c)
{
		if ( t == S ) 
			c = getc(fp);

	char s[256];
	int i = 0;
		do {
			if (c == EOF)  { a->sign = not_ok_all; return; }
			s[i] = c;
			i++;
			c = getc(fp);
		} while (IsStatusTrue(t, c));
		

		if (t != S) ungetc(c, fp);
			s[i] = '\0';
			Make_Ok(a, s);

			if (t == N) a -> id = intconst;
			if (t == S) a -> id = strconst;
			if (t == K) a -> id = keyword;
			if (t == I) {
				switch (s[0]) {
					case '?' : a->id = function;	break;
					case '@' : a->id = label;		break;
					case '$' : a->id = variable;	break; 
					case '.' : a->id = robotdata;	break; 
				}
			  if (strlen(s)<2) a->sign = not_ok_all;
			}
}


Lexeme* CLexic :: GetNextLexem ()
{
	Lexeme *a = new Lexeme;
	char c;
	
		while ( (c = getc(fp)) != EOF ) 
			if (c == 13) current_line++; 
				else if (c != '\n' &&  c != ' ' && c != '\t') 
						break;
		

		if (c == EOF) { a->sign = ok_all; return a; }
	
		switch (lxStatus stat = GetStatus(c)) {
			case N: case I : case K : case S : NIKS(stat, a, c); break; 

			case A :	char s[3]; s[0] = c; s[2]='\0';
							if ((s[1] = getc(fp)) == EOF) break; 
						if (s[1] == '=') {						
							Make_Ok(a, s);
							a->id = assign;
						} break;
	
			case H : 	Make_Ok(a, &c, 1);
						a->id = space;
						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 err:		printf( "error" ); break;
			case assign:	printf( "assignment" ); break;
			case space:		printf( "spacer" ); break;
			case intconst:	printf( "intconst" ); break;
			case strconst:	printf( "strconst" ); break;
			case variable:	printf( "variable" ); break;
			case label:		printf( "label" ); break;
			case function:	printf( "function" ); break;
			case keyword:	printf( "keyword" ); break;
			case 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);
}


