/* MasC interpreter/compiler
   (Master Assembly Script Container)
   written by Jared Bruni
   
   After reading Writing Compilers and Interpreters A approach using C++ by Ronald Mak
   and the Dragon Book
   code completly re-written but using concepts teached in this book
*/

// Lexical Anaylizer implementation file 


#include "lex.h"
#include<stdarg.h>


// syntax check anaylize the tokens
int syntaxCheck(struct Token *tokens, int token_length,struct BackEnd *bend)
{

	int token_index = 0;

	if(tokens[0].code != convertIdentifier("mas"))
	{
		printSyntaxError(MasMissing);   
		return 0;                 
	}


	if(1 < token_length && tokens[1].code != 100)
	{
		printSyntaxError(ObjectMissing);
		return 0;
	}
	else
	{
		strcpy(bend->obj_name, tokens[1].text);
	}

	
	while(tokens[token_index].code != End && token_index < token_length)
	{ 
		switch(tokens[token_index].code) {
			case VAR_INT:
			case VAR_FLOAT: {
				struct symNode *node_ptr = Search(&bend->g_stable.root, tokens[token_index+1].text);

				if(tokens[token_index+2].code == EQ) {
					node_ptr->value = tokens[token_index+3].value;
				}
			}
			break;
			case VAR_STR: {
				struct symNode *node_ptr = Search(&bend->g_stable.root, tokens[token_index+1].text);
				if(tokens[token_index+2].code == EQ) {
					strncpy(node_ptr->str_value, tokens[token_index+3].text,max_string_size);
					node_ptr->str_value[strlen(tokens[token_index+3].text)] = 0;
				}
			}
		    break;
		}
		token_index++;
	}

		token_index = 0;
		while(tokens[token_index].code != End) {
			struct symNode *sptr = 0;
			
			switch(tokens[token_index].code) {
				case _PROC:
					sptr = Search(&bend->g_stable.root, tokens[token_index+1].text);
					if(!sptr) 
					  printError("_PROCedure without a name\n");
					else
						sptr->line = token_index;
					break;
			}
			
			token_index++;

		}


	return 1;   
}

int line = 0, p = 0;
struct symTbl symbol_table, label_table, _PROC_table;

// skip white space and " for strings
char *skipWhiteSpace(char *ptr_c, char *tmp_buf)
{
	if(*ptr_c == '#')
		ptr_c = skipSpaces(ptr_c);
		
		if(*ptr_c == '\n')
			line++;

		if(*ptr_c == '\"') {
			do {
				if(*ptr_c == '\\')
				{
					ptr_c++;
					switch(*ptr_c)
					{
					case 'n':
						tmp_buf[p] = '\n';
						break;
					case 't':
						tmp_buf[p] = '\t';
						break;
					case '\"':
						tmp_buf[p] = '\"';
						break;                              
					}

					p++;
					ptr_c++;
				} else {
					tmp_buf[p++] = *ptr_c;
					ptr_c++;
				}
			} while (*ptr_c != '\"');
		}
		else {
			*ptr_c = tolower(*ptr_c);
		}

		return ptr_c;
}
int cob = 0, ccb = 0,no_newline = 0, count = 0, countm = 0;
int line_num = 1;

// anaylize the source and then feed it to the syntax anaylizer
int lexAnaylize(struct text_in *text_buffer)
{
	char *ptr_c = text_buffer->buff;
	char tmp_buf[max_string_size];
	int pos = (int) strlen(ptr_c), qo = 0,rtval = 1;
    p = 0;
	if(pos < 3) {
		printf("Error (%d) source code does not contain enough characters for a program\n",++err);
		return 0;
	}
    initTable(&symbol_table);
	initTable(&label_table);
	initTable(&_PROC_table);
	line = 1;
	pos--;
	initLabelTable(text_buffer->buff, tmp_buf);
	init_PROCedureTable(text_buffer->buff, tmp_buf);
	line_num = 1;
	checkCall(text_buffer->buff, tmp_buf);
	while(ptr_c[pos] != '\n' || ptr_c[pos] == ' ')
	{
		pos--;
		if(ptr_c[pos] == '\n' || ptr_c[pos] == ' ') {
			no_newline = 0;
			break;
		}
		else {
		no_newline = 1;
		ccb++;
		printerror(" No newline at the end of file. \n");
		}
		
	}
	pos = 0;
	while(ptr_c[++pos] != 0) {
		if(ptr_c[pos] == '\"' && ptr_c[pos-1] != '\\') {
			qo++;
		}
	}

	if(qo%2 != 0) {
		printerror(" \" do not match did you forget a \"");
		return 0;
	}


	do {
		ptr_c = skipWhiteSpace(ptr_c,tmp_buf);
		
		if(*ptr_c != ' ' && *ptr_c != ';' && *ptr_c != '\n' && *ptr_c != '\r' && *ptr_c != ',') {
		if(p < max_string_size)
			tmp_buf[p++] = *ptr_c;
		}
		else {
			tmp_buf[p] = 0;
			if(strlen(tmp_buf) > 0) {
				char *pstr = tmp_buf;
				int ident;

				if(*pstr == -1) {
					pstr++;
				}
				else
					pstr = tmp_buf;

			    ident = convertIdentifier(pstr);
				if(ident == OB) {
					cob++;

				} else if(ident == CB) {
					ccb++;
				}

				if(ident != MAS && ident != VAR) {
				    
				if(lex_PROCcess(tmp_buf) == 0) {
					// printError
				} // first enter all into the symbol table
				}
			}
			ptr_c++;
			p = 0;
			continue;
		}
		ptr_c++;
	}while(*ptr_c != 0);

	if(cob != ccb) {
		printerror(" brackets [{,}] do not match did you forget a { or } or mabye a newline at the end of file");
	}


	//symNode_Print(&_PROC_table.root);
	// then anaylize the source for errors
	if(Anaylize(text_buffer->buff,tmp_buf) == 0) {
		printf("syntax errors compilation cannot continue.\n");
		if(symbol_table.root.next != 0) 
		free(symbol_table.root.next);
    	if(label_table.root.next != 0)
		free(label_table.root.next);
		if(_PROC_table.root.next != 0);
		free(_PROC_table.root.next);
		return 0;

	}

	if(!checkInstructions(text_buffer->buff, tmp_buf)) {
		rtval = 0;
	}

	if(symbol_table.root.next != 0) 
		free(symbol_table.root.next);
	if(label_table.root.next != 0)
		free(label_table.root.next);
	if(_PROC_table.root.next != 0);
	   free(_PROC_table.root.next);



	return rtval;
}


// _PROCcess a Identifier
int lex_PROCcess(char *sptr) 
{
	struct symNode *node_ptr = 0, *snode_ptr = 0;
	int ident = 0;
	ident = convertIdentifier(sptr);
	if( ident == NOT_FOUND) {
		node_ptr = Search(&symbol_table.root, sptr);
		if(!node_ptr) {
			snode_ptr = Enter(&symbol_table, &symbol_table.root, sptr);
			snode_ptr->line = line; // tell what line its loacated at
		}
	}
	

	return 1;
}
int err = 0;
// Anaylize the source code for errors
int Anaylize(char *sptr, char *tmp_buf)
{
	char *ptr_c = sptr;
	struct symNode *node_ptr = 0, *snode_ptr = 0;
	int cr = 0;
	line = 1;
	line_num = 1;
	p = 0;
	while(*ptr_c != 0) {
		ptr_c = skipWhiteSpace(ptr_c, tmp_buf);
		if(*ptr_c != ' ' && *ptr_c != '\n' && *ptr_c != '\r' && *ptr_c != ';') {
			tmp_buf[p++] = *ptr_c;
		}
		else {
			tmp_buf[p] = 0;
			p = 0;
			if(strlen(tmp_buf) != 0) {
				int ident = convertIdentifier(tmp_buf);

				if(ident == NOT_FOUND) {
					if(tmp_buf[0] != '\"' && strstr(tmp_buf, ":") != 0) {
						node_ptr = Search(&label_table.root, tmp_buf);
						if(node_ptr != 0 && node_ptr->line != line) {
							printf(" Error (%d) duplicate label %s on line %d and line %d\n",++err, tmp_buf, node_ptr->line, line);
						}
						else {
							snode_ptr = Enter(&label_table, &label_table.root, tmp_buf);
							snode_ptr->line = line;
						}
					}

				}

				// scan through for syntax that HAS to be there
				switch(cr)  {
					case 0:
					if(ident != MAS) {
						printerror("Mas keyword missing on line %d instead i found %s\n", line, tmp_buf);
					}
					break;
					case 1:
						node_ptr = Search(&symbol_table.root, tmp_buf);
						if(!node_ptr) {
							printerror(" Missing object name instead i found [%s] ", tmp_buf);
							cr++;
						}
						
						break;
					case 2: 
						if(ident != OB) {
							printerror(" Opening bracket ");
						}
						break;
					case 3: 
						if(ident != VAR) {
							printerror (" Missing var keyword or in wrong place instead i found %s ", tmp_buf);
						}
						break;
					case 4:
						if(ident != OB) {
							printerror( " Missing var opening bracket ({) ");
						}
						break;
					case 5:
						switch(ident) {
						case VAR_STR:
						case VAR_INT:
						case VAR_FLOAT:
						case CB:
							break;
						default:
							printerror(" Missing first decleration in var block instead i found %s ", tmp_buf);
						}
						break;
					default:// other syntax like instructions
						{
							
						}
						break;

				}

				cr++;
			}
		}
		ptr_c++;
	}

	//symNode_Print(&label_table.root);
	if(err == 0)
	return 1;
	else
	return 0;
}

// from the C Programming language by Dennis Ritche and Brian Kernighan
// changed to suit this programs needs
int printerror(char *fmt, ...)
{
	va_list lst;
	char *p, *strval;
	int ival;

	if(no_newline == 0)
	printf("Error (#%d) ", ++err);
	else
		printf("Warning ");
	va_start(lst, fmt);
	for(p = fmt; *p; p++) {
		if(*p != '%') {
			putchar(*p);
			continue;
		}

		switch(*++p) {
			case 'd':
				ival = va_arg(lst, int);
				printf("%d", ival);
				break;
			case 's':
				for(strval = va_arg(lst, char*); *strval; strval++) 
					putchar(*strval);
				break;
			default:
				putchar(*p);

		}

	}

	va_end(lst);
	if(no_newline == 0)
	printf(" on line %d\n", line);
	else
		printf("\n");

	return 1;
}

char *prev_token = 0;

char *getnexttoken(char *source, char *tmp_buf) 
{
	char *ptr_c = source;
	prev_token = ptr_c;
	p = 0;
	do {

		if(*ptr_c == '#')
			ptr_c = skipSpaces(ptr_c);
		else
		ptr_c = skipWhiteSpace(ptr_c,tmp_buf);

		if(*ptr_c != ' ' && *ptr_c != ';' && *ptr_c != '\n' && *ptr_c != '\r' && *ptr_c != ',') {
			if(p < max_string_size)
				tmp_buf[p++] = *ptr_c;
		}
		else {
			if(*ptr_c == '\n') {
				line_num++;
			}
			tmp_buf[p] = 0;
			if(strlen(tmp_buf) != 0) {
				return ptr_c;
			}
			ptr_c++;
			p = 0;
			continue;
		}
		ptr_c++;
	}while(*ptr_c != 0);

	return 0;
}

void initLabelTable(char *ptr_c, char *tmp_buf) 
{
	char *ptr_cc = ptr_c;
	struct symNode *pnode = 0, *ppnode = 0;
	line_num = 1;

	while(ptr_cc++ != 0) {
		p = 0;
		ptr_cc = getnexttoken(ptr_cc, tmp_buf);
		if(strstr(tmp_buf, ":") && tmp_buf[0] != '\"') {
			pnode = Search(&label_table.root, tmp_buf);
			if(!pnode) {
				ppnode = Enter(&label_table, &label_table.root, tmp_buf);
				ppnode->line = line_num;
				//printf("Entered label %s on line %d\n", tmp_buf, line_num);
			}
		}

	} 	
}

void init_PROCedureTable(char *ptr_c, char *tmp_buf)
{
	char *ptr_cc = ptr_c;
	struct symNode *pnode = 0, *ppnode = 0;
	line_num = 1;
	while(ptr_cc++ && *ptr_cc != 0) {
		p = 0;
		ptr_cc = getnexttoken(ptr_cc, tmp_buf);
		if(convertIdentifier(tmp_buf) == _PROC) {
			   p = 0;
			   ptr_cc = getnexttoken(ptr_cc, tmp_buf);
			pnode = Search(&_PROC_table.root, tmp_buf);
			if(!pnode) {
				ppnode = Enter(&_PROC_table, &_PROC_table.root, tmp_buf);
				ppnode->line = line_num;
			}
		}

	} 	
}

void checkCall(char *ptr_c, char *tmp_buf)
{
	char *ptr_cc = ptr_c;
	struct symNode *pnode = 0;
	line_num = 1;
	while(ptr_cc++ && *ptr_c != 0) {
		ptr_cc = getnexttoken(ptr_cc, tmp_buf);

		if(convertIdentifier(tmp_buf) == CALL) {
			p = 0;
			ptr_cc = getnexttoken(ptr_cc, tmp_buf);
			pnode = Search(&_PROC_table.root, tmp_buf);
			if(!pnode)  {
				printf("_PROCedure %s undefined assuming callback.\n", tmp_buf);
			}
		}
	}

}

int checkInstructions(char *ptr_c, char *tmp_buf)
{
	char *ptr_cc = ptr_c;
	struct symNode *snode_ptr = 0 , *node_ptr = 0, *ssnode_ptr = 0;
	int found_begin = 0, found_end = 0,z;
	int rtval = 1;
	line_num = 1;
	while(ptr_cc && *ptr_cc != 0) {
		ptr_cc = getnexttoken(ptr_cc, tmp_buf);
		z = convertIdentifier(tmp_buf);
		switch(z) {
			case 256: {
			char tmp_ptr[max_string_size];
			int i = 0;
			snode_ptr = Search(&symbol_table.root, tmp_buf);
			ssnode_ptr = Search(&_PROC_table.root, tmp_buf);
			strcpy(tmp_ptr,tmp_buf);
			i = (int) strlen(tmp_ptr);
			tmp_ptr[i] = ':';
			tmp_ptr[i+1] = 0;
			node_ptr = Search(&label_table.root, tmp_ptr);
			if(!node_ptr && !snode_ptr && !ssnode_ptr && tmp_buf[0] != '\"'  && isdigit(tmp_buf[0]) == 0 && strstr(tmp_buf, ":") == 0 && strlen(tmp_buf) > 1) {
				printf(" Error(%d) invalid instruction [%s] on line %d\n", ++err, tmp_buf, line_num);
				rtval = 0;
			} 
			}
			break;
			case BEGIN:
				found_begin = 1;
				break;
			case END:
				found_end = 1;
				break;
		}
	}

	if(found_begin != 1) 
		printf("Error (%d) Begin keyword not found.\n", ++err);

	if(found_end != 1)
		printf("Error (%d) End keyword not found.\n", ++err);

	if(!rtval)
		return rtval;
	checkVariables(ptr_c,tmp_buf);
	return rtval;
}

void checkVariables(char *ptr_c, char *tmp_buf)
{
	char *ptr_cc = ptr_c;
	struct symNode *ptr_node = 0, *sptr_node = 0, *ssptr_node = 0;
	int z = 0;
	line_num = 1;

	while(ptr_cc && *ptr_cc != 0)
	{
		ptr_cc = getnexttoken(ptr_cc, tmp_buf);
		z = convertIdentifier(tmp_buf);
		if(z == NOT_FOUND && strstr(tmp_buf,":") == 0 && tmp_buf[0] != '\"' && isdigit(tmp_buf[0]) == 0) {
			char tmp_ptr[max_string_size];
			int len = (int) strlen(tmp_buf);
			ptr_node = Search(&symbol_table.root, tmp_buf);
			sptr_node = Search(&_PROC_table.root, tmp_buf);
			strcpy(tmp_ptr, tmp_buf);
			tmp_ptr[len] = ':';
			tmp_ptr[len+1] = 0;
			ssptr_node = Search(&label_table.root, tmp_ptr);
			if(!ptr_node && !sptr_node && !ssptr_node && tmp_buf[0] != -1) {
				printf("Error (%d) Variable or label %s not declared on line %d.\n",++err, tmp_buf, line_num);
			}

		}
	}
}

char *putbacktoken() { return prev_token; }

// ask me for a contribution
// I dont think your funny when you ask for money and things , yeah
// I should of known better

