#include "lex.hpp"


//------------------------- LEXIC Module ------------------------//
//						  v 25 . 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 IsLetter(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);
};

//---------------------------------------------------------------------

//------------------------------ Lexeme -------------------------------

//--- Lexeme :: Make Ok
void Lexeme :: MakeOk(char *s_, int current_line)
{
	line = current_line;
	sign = ok;
	s = strdup(s_);	
};


//------------------------------ CLexic -------------------------------

//----------- AUTOMAT -------------

//--- CLexem :: Back Char 
void CLexic :: Parse_Back()
{
	char c = file.c();
	if ( c==13 || IsStr(c, " \n\t") || c==EOF) ; 
	else
		file.GetNextChar(true);
};


//--- CLexem :: Is all right?
bool  CLexic :: IsRight()
{
	for (lex = begin; lex != NULL; lex=lex->next)
		if (lex->sign == not_ok || lex->id == lx_err) 
			return false;
	return true;
};


//--- CLexem :: Get Init Status
EState CLexic :: GetState()
{
	char c = file.c();

	if (IsDigit(c))  return S_NUMBR;
	if (IsLetter(c)) return S_KEYWD;
	if (IsSpacer(c)) return S_SPACE;

	switch (c) {
		case '@' : return S_LABEL;
		case ':' : return S_ASSGN;
		case '"' : return S_STRNG;

		case '$' : return S_VARBL;
		case '?' : return S_FUNCT;
		case '.' : return S_ROBOT;
		
		case ' ' :
		case '\n':
		case '\t':
		case  13 :
			return S_NTHNG;

		default : return S_ERR;
	}
};


//--- CLexec :: Is Status Changed?
bool CLexic :: IsStatusChanged()
{
	int c = file.c();
	switch (state) {
		case S_VARBL : 
		case S_ROBOT : //if (c=='.') return false;
		case S_LABEL : 
		case S_FUNCT : 
					if (IsDigit(c)||IsLetter(c)) return false;
					break;
		case S_NUMBR : if (IsDigit(c))	return false; break;
		case S_KEYWD : if (IsLetter(c)) return false; break;
		case S_ASSGN : if (c=='=') return false; break;
		case S_STRNG : if (c!='"' && c!=EOF) return false; break;
		case S_INIT  : return false; break;
		default : return true;
	};
	return true;
};


//--- CLexem :: Make Analysis 
bool  CLexic :: MakeAnalysis()
{
	begin = lex;
	while (file.GetNextChar() != EOF || state != S_INIT)
	{		
		if (state != S_STRNG) {
			if (file.c()==13) { line++; file.buf.Unadd(); };
			if (IsStr(file.c(), " \n\t")) file.buf.Unadd();
		} else if (file.c()==13) line++;
		
		if (file.c()==EOF) {
			file.buf.Unadd();
			if (lex == begin->next) 
				return false;
			
		}
		Parse();
		//printf("\n state=%d, c=%c, buf=%s. \n", state, file.c(), file.buf.GetStr());
	}
	begin = begin->next;
	return IsRight();
};


//--- CLexem :: Parse_S_INIT
void CLexic :: Parse_Init()
{
	state = GetState();
	if (state == S_NTHNG) 
	{
		state = S_INIT;
		return;
	}
	lex->next = new Lexeme;
	lex = lex->next;
};


//--- CLexem :: Parse Automat
void CLexic :: Parse()
{
	if (state == S_INIT) { Parse_Init(); return; }
	if (IsStatusChanged()) {
		switch (state) {
			case S_VARBL : lex->id = lx_var;		Parse_Back(); break;
			case S_FUNCT : lex->id = lx_func;		Parse_Back(); break;
			case S_LABEL : lex->id = lx_label;		Parse_Back(); break;
			case S_NUMBR : lex->id = lx_int_const;	Parse_Back(); break;
			case S_KEYWD : lex->id = lx_keyword;	Parse_Back(); break;
			case S_SPACE : lex->id = lx_spacer;		Parse_Back(); break;

			case S_ROBOT : lex->id = lx_robotdata;	Parse_Back(); break;

			case S_ASSGN : 
				if (strcmp(":=",file.buf.GetStr())!=0) lex->id=lx_err;
				else lex->id = lx_assign;  
				Parse_Back(); 
				break; 
			
			case S_STRNG : 
				char *s = file.buf.GetStr();
				if (s[strlen(s)-1] != '"') lex->id = lx_err;
				else lex->id = lx_str_const;
				break;

			default : break;
		};
		//printf("\n MAKE_OK - state=%d, c=%c, buf=%s. \n", state, file.c(), file.buf.GetStr());
		lex->MakeOk(file.buf.GetStr(), line);
		file.buf.Clear();
		state = S_INIT;
	};

};

//=========== AUTOMAT =============

//--- CLexic :: Constructer 
CLexic :: CLexic (char *s) 
{ 
	file.Open(s);
	state = S_INIT;
	begin = NULL; 
	line = 1;  
	
	lex = new Lexeme;
	lex->id = lx_begin;
	lex->MakeOk("__begin~", -1);

};


//--- CLexic :: Sub Printing
void CLexic :: SubPrint(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;
		case lx_begin : break;
	}
	printf("\n Lexeme:  '%s'\n", l->s);
	if (l->sign == not_ok) printf("\n		:: [not_ok] \n"); 
	
	l = l -> next;
	if (l != NULL) SubPrint(l);
};


