#include "LexAnalyzer.h"

LexAnalyzer::LexAnalyzer(FILE* source)
{
	this->line = 1;
	this->pos = 0;
	this->source = source;
	this->state = s_init;
}

LexAnalyzer::~LexAnalyzer()
{
}

LexAnalyzer::Lexem LexAnalyzer::GetNext()
{
	Lexem lexem;
	lexem.line = line;
	
	while(true)
	{
		char c = getc(source);
		
		switch (state)
		{
			case s_init:
			{
				if (c == EOF)
				{
					lexem.type = Lexem::l_end;
					return lexem;
				}
				
				if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
				{
					state = s_identifier;
					buffer = c;
				} else
					if (c >= '0' && c <= '9')
					{
						state = s_number;
						buffer = c;
					} else
					{										
						switch (c)
						{
							case '+': lexem.type = Lexem::l_plus; return lexem;
							case '-': lexem.type = Lexem::l_minus; return lexem;
							case '*': lexem.type = Lexem::l_mul; return lexem;
							case '/': lexem.type = Lexem::l_div; return lexem;
							case '%': lexem.type = Lexem::l_mod; return lexem;
														
							case '&': lexem.type = Lexem::l_and; return lexem;
							case '|': lexem.type = Lexem::l_or; return lexem;
							
							case '(': lexem.type = Lexem::l_eopen; return lexem;
							case ')': lexem.type = Lexem::l_eclose; return lexem;
							case '{': lexem.type = Lexem::l_bopen; return lexem;
							case '}': lexem.type = Lexem::l_bclose; return lexem;
							case '[': lexem.type = Lexem::l_iopen; return lexem;
							case ']': lexem.type = Lexem::l_iclose; return lexem;
							
							case ';': lexem.type = Lexem::l_semi; return lexem;							
							case '$': lexem.type = Lexem::l_var; return lexem;
							case ',': lexem.type = Lexem::l_fix; return lexem;
							
							case '=':
							{
								state = s_eq;
								break;
							}
							
							case '!':
							{
								state = s_not;
								break;
							}
							
							case '>':
							{
								state = s_more;
								break;
							}
							
							case '<':
							{
								state = s_less;
								break;
							}

							case '"':
							{
								buffer = "";
								state = s_string;
								break;
							}
							
							case '\n': line++; pos = 0; lexem.line++;
							case '\t': case ' ': break;
							
							default: throw ParserError(line, pos, c);
						}
					}
					
				break;
			}
			
			case s_identifier:
			{
				if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
					buffer += c;
				else
				{
					state = s_init;
					ungetc(c, source);
									
					lexem.type = Lexem::l_identifier;
					
					if (buffer == "if") lexem.type = Lexem::l_if; else
					if (buffer == "else") lexem.type = Lexem::l_else; else
					if (buffer == "while") lexem.type = Lexem::l_while;
					if (lexem.type != Lexem::l_identifier) return lexem;
					
					lexem.info = buffer;
					return lexem;
				}
				
				break;
			}
			
			case s_number:
			{
				if (c >= '0' && c <= '9')
					buffer += c;
				else
				{
					state = s_init;
					ungetc(c, source);
									
					lexem.type = Lexem::l_number;								
					lexem.info = buffer;
					
					return lexem;
				}
				
				break;
			}
			
			case s_string:
			{
				if (c != '"')
					buffer += c;
				else
				{
					state = s_init;

					lexem.type = Lexem::l_string;
					lexem.info = buffer;
					
					return lexem;
				}
				
				break;
			}
			
			case s_eq:
			{
				if (c == '=')
				{
					lexem.type = Lexem::l_eq;				
				}
				else
				{
					lexem.type = Lexem::l_assign;
					ungetc(c, source);
				}
				state = s_init;
				
				return lexem;
			}
			
			case s_not:
			{
				if (c == '=')
				{
					lexem.type = Lexem::l_noteq;
				}
				else
				{
					lexem.type = Lexem::l_not;
					ungetc(c, source);
				}
				state = s_init;
				
				return lexem;
			}
			
			case s_more:
			{
				if (c == '=')
				{
					lexem.type = Lexem::l_moreeq;
				}
				else
				{
					lexem.type = Lexem::l_more;
					ungetc(c, source);
				}
				state = s_init;
				
				return lexem;
			}
			
			case s_less:
			{
				if (c == '=')
				{
					lexem.type = Lexem::l_lesseq;
				}
				else
				{
					lexem.type = Lexem::l_less;
					ungetc(c, source);
				}
				state = s_init;
				
				return lexem;
			}
		}
	}
}
