/* 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
*/
// output C source code implementation file

#include "output_c.h"


struct symNode *cmp_node_ptr = 0, *sloop_node_ptr = 0;
int cmp_inc_index = 0;

char *convertEscapeCharacters(char *src, char *ptmp_buf)
{
	char *ptr_c = src, *tmp_buf = ptmp_buf;
	while (*ptr_c != 0) {
		switch(*ptr_c) {
			case '\n':
				*tmp_buf = '\\';
				tmp_buf++;
				*tmp_buf = 'n';
				tmp_buf++;
				break;
			case '\t':
				*tmp_buf = '\\';
				tmp_buf++;
				*tmp_buf = 't';
				tmp_buf++;
				break;
			case '\"':
				*tmp_buf = '\\';
				tmp_buf++;
				*tmp_buf = '\"';
				tmp_buf++;
				break;
			default:
		*tmp_buf = *ptr_c;
		tmp_buf++;
		break;
		}
		ptr_c++;
	}

	*tmp_buf = 0;

	if(ptmp_buf[strlen(ptmp_buf)-1] == '\"')
		ptmp_buf[strlen(ptmp_buf)-2] = 0;

	if(*ptmp_buf == '\\') {
		ptmp_buf+=2;
	}

	return ptmp_buf;
}

void _PROCcessInc(FILE *pfile, struct BackEnd *bend, struct Token *tokens, int flip)
{
	char tmp_buf[max_string_size];
	struct symNode *node_ptr = 0;
	do {
		switch(tokens[flip].code) {
		case MOV:
		fprintf(pfile, "%s = %s;\n", tokens[flip+1].text,tokens[flip+2].code == 99 ? tokens[flip+2].num_text : tokens[flip+2].text);
		break;
		case INC:
		fprintf(pfile, "%s++;\n", tokens[flip+1].text);
		break;
		case DEC:
		fprintf(pfile, "%s--;\n", tokens[flip+1].text);
		break;
		case ADD:
			fprintf(pfile, "%s += %s;\n", tokens[flip+1].text, tokens[flip+2].code == 99 ? tokens[flip+2].num_text : tokens[flip+2].text);
		break;
		case SUB:
			fprintf(pfile, "%s -= %s;\n", tokens[flip+1].text, tokens[flip+2].code == 99 ? tokens[flip+2].num_text : tokens[flip+2].text);
			break;
		case MUL:
			fprintf(pfile, "%s *= %s;\n", tokens[flip+1].text, tokens[flip+2].code == 99 ? tokens[flip+2].num_text : tokens[flip+2].text);
			break;
		case XOR: 
			fprintf(pfile, "%s = %s ^ %s;\n",tokens[flip+1].text, tokens[flip+1].text, tokens[flip+2].code == 99 ? tokens[flip+2].num_text : tokens[flip+2].text);
			break;
		case OR:
			fprintf(pfile, "%s = %s | %s;\n", tokens[flip+1].text, tokens[flip+1].text, tokens[flip+2].code == 99 ? tokens[flip+2].num_text : tokens[flip+2].text);
			break;
		case NOT:
			fprintf(pfile, "%s = !%s;\n", tokens[flip+1].text,tokens[flip+1].text);
			break;
		case AND:
			fprintf(pfile, "%s = %s &  %s;\n", tokens[flip+1].text,tokens[flip+1].text, tokens[flip+2].code == 99 ? tokens[flip+2].num_text : tokens[flip+2].text);
			break;
		case DIV:
			fprintf(pfile, "%s /= %s;\n", tokens[flip+1].text, tokens[flip+2].code == 99 ? tokens[flip+2].num_text : tokens[flip+2].text);
			break;
		case PUSH: {
				if(tokens[flip+1].code == 99) {
					fprintf(pfile, "Push(&masc__st, %s);\n", tokens[flip+1].num_text);
				}
			else {
				node_ptr = Search(&bend->g_stable.root,tokens[flip+1].text);
			switch(node_ptr->vtype) {
			case tVAR_INT:
				fprintf(pfile,"Push(&masc__st, (int) %s);\n", node_ptr->str);
				break;
		  default:
			  	fprintf(pfile, "Push(&masc__st, %s);\n", tokens[flip+1].text);
			}
			}
		
		}
	    break;
		case POP: {
			node_ptr = Search(&bend->g_stable.root, tokens[flip+1].text);
			if(node_ptr->vtype == tVAR_INT) {
				fprintf(pfile,"%s = (int) Pop(&masc__st);\n", tokens[flip+1].text);
			}
			else {
				fprintf(pfile,"%s = Pop(&masc__st);\n", tokens[flip+1].text);
			}
		}
	    break;
		case SCAT: {

			int sip = flip+2;
			node_ptr = Search(&bend->g_stable.root, tokens[flip+1].text);
			if(!node_ptr) {
				printf("Error. variable %s not found\n", tokens[flip+1].text);
				return;
			}
			fprintf(pfile,"sprintf(%s, \"", node_ptr->str);

				do {
					sip++;
					node_ptr = Search(&bend->g_stable.root, tokens[sip].text);
					if(!node_ptr && tokens[sip].text[0] != '\"') {
						//printError("Error during compilation print function couldnt find variable\n")
 					}

					if(isdigit(tokens[sip].text[0])) {
						fputc('%',pfile);
						fputc('d',pfile);
					}
					else if(tokens[sip].text[0] == '"') {
						fputc('%',pfile);
						fputc('s',pfile);
					}
					else {
					
					switch(node_ptr->vtype) {
					case tVAR_INT:
					case tVAR_CINT:
						fputc('%', pfile);
						fputc('d', pfile);
						break;
					case tVAR_CSTR:
					case tVAR_STR:
						fputc('%', pfile);
						fputc('s', pfile);
						break; 
					case tVAR_FLOAT:
					case tVAR_CFLOAT:
						fputc('%',pfile);
						fputc('f',pfile);
						break;
					default: {
						//printf("%d %s\n", node_ptr->vtype, tokens[sip].text);
					}
					}
					}
					sip++;
				} while (tokens[sip].code == OSTREAM);

				sip = flip+2;
				fprintf(pfile,"%%c\",");
				do {
					sip++;
					if(tokens[sip].vtype == tVAR_CSTR)
					fprintf(pfile,"\"%s\",",convertEscapeCharacters(tokens[sip].text,tmp_buf));
					else if(tokens[sip].code == 100) {
						node_ptr = Search(&bend->g_stable.root, tokens[sip].text);
						if(!node_ptr) { return; }
						fprintf(pfile, "%s,", tokens[sip].text);
					}
					else if(tokens[sip].code == 99)
					fprintf(pfile, "%s,", tokens[sip].num_text);
					sip++;
				} while(tokens[sip].code == OSTREAM);
				fprintf(pfile,"\"\"");
				fprintf(pfile,");\n");
				
		}
	    break;
		case PRINT:
			if(tokens[flip+1].code != OSTREAM && tokens[flip+1].vtype == tVAR_CSTR)
			fprintf(pfile, "printf(\"%s\");\n", convertEscapeCharacters(tokens[flip+1].text,tmp_buf));
			else if(tokens[flip+1].code == 99) {
				fprintf(pfile, "printf(\"%s\");\n", tokens[flip+1].num_text); 
			} else if(tokens[flip+1].code == 100) {
				node_ptr = Search(&bend->g_stable.root, tokens[flip+1].text);
				if(!node_ptr) { return; }
				switch(node_ptr->vtype) {
				case tVAR_INT:
					fprintf(pfile,"printf(\"%%d\",%s);\n", node_ptr->str);
					break;
				case tVAR_STR:
					fprintf(pfile,"printf(%s);\n", node_ptr->str);
					break;
				case tVAR_FLOAT:
					fprintf(pfile,"printf(\"%f\", %s);\n", node_ptr->str);
					break;
				}

			}
			else {
				int sip = flip+1;
				fprintf(pfile,"printf(\"");
				do {
					sip++;
					node_ptr = Search(&bend->g_stable.root, tokens[sip].text);
					if(!node_ptr && tokens[sip].text[0] != '\"') {
						//printError("Error during compilation print function couldnt find variable\n")
 					}

					if(isdigit(tokens[sip].text[0])) {
						fputc('%',pfile);
						fputc('d',pfile);
					}
					else if(tokens[sip].text[0] == '"') {
						fputc('%',pfile);
						fputc('s',pfile);
					}
					else {
					
					switch(node_ptr->vtype) {
					case tVAR_INT:
					case tVAR_CINT:
						fputc('%', pfile);
						fputc('d', pfile);
						break;
					case tVAR_CSTR:
					case tVAR_STR:
						fputc('%', pfile);
						fputc('s', pfile);
						break; 
					case tVAR_FLOAT:
					case tVAR_CFLOAT:
						fputc('%',pfile);
						fputc('f',pfile);
						break;
					default: {
						//printf("%d %s\n", node_ptr->vtype, tokens[sip].text);
					}
					}
					}
					sip++;
				} while (tokens[sip].code == OSTREAM);

				sip = flip+1;
				fprintf(pfile,"%%s\",");
				do {
					sip++;
					if(tokens[sip].vtype == tVAR_CSTR)
					fprintf(pfile,"\"%s\",",convertEscapeCharacters(tokens[sip].text,tmp_buf));
					else if(tokens[sip].code == 100) {
						node_ptr = Search(&bend->g_stable.root, tokens[sip].text);
						if(!node_ptr) { return; }
						fprintf(pfile, "%s,", tokens[sip].text);
					}
					else if(tokens[sip].code == 99)
					fprintf(pfile, "%s,", tokens[sip].num_text);
					sip++;
				} while(tokens[sip].code == OSTREAM);
				fprintf(pfile,"\"\"");
				fprintf(pfile,");\n");
				
			}
			break;
		case CALL: {
			fprintf(pfile,"masc_%s();\n", tokens[flip+1].text);
		}
	    break;
		case IN: {
			node_ptr = Search(&bend->g_stable.root, tokens[flip+1].text);
			if(!node_ptr) { return; }
			switch(node_ptr->vtype) {
			case tVAR_STR:
				fprintf(pfile,"scanf(\"%%s\", %s);\n", node_ptr->str);
				break;
			case tVAR_INT:
				fprintf(pfile,"scanf(\"%%d\", &%s);\n", node_ptr->str);
				break;
			case tVAR_FLOAT:
				fprintf(pfile,"scanf(\"%%f\", &f);\n", node_ptr->str);
				break;
			}
		}
	    break;
		case CMP: {
			cmp_node_ptr = Search(&bend->g_stable.root, tokens[flip+1].text);
			if(!cmp_node_ptr) { return; }
			cmp_inc_index = flip+2;
		}
	    break;
		case JE: {
			if(!cmp_node_ptr || cmp_inc_index == 0) { return; }
			switch(cmp_node_ptr->vtype) {
			case tVAR_INT:
			case tVAR_FLOAT:
				fprintf(pfile,"if(%s == %s) goto %s;\n", cmp_node_ptr->str, tokens[cmp_inc_index].vtype == tVAR_CINT ? tokens[cmp_inc_index].num_text : tokens[cmp_inc_index].text, tokens[flip+1].text);
				break;
			case tVAR_STR:
				fprintf(pfile,"if(strcmp(%s,%s%s%s) == 0) goto %s;\n", cmp_node_ptr->str, tokens[cmp_inc_index].vtype == tVAR_CSTR ? "\"" : "", convertEscapeCharacters(tokens[cmp_inc_index].text,tmp_buf),tokens[cmp_inc_index].vtype == tVAR_CSTR ? "\"" : "",tokens[flip+1].text);
				break;
			}
		}
		break;
		case JNE: {
			if(!cmp_node_ptr || cmp_inc_index == 0) { return; }
			switch(cmp_node_ptr->vtype) {
			case tVAR_INT:
			case tVAR_FLOAT:
				fprintf(pfile,"if(%s != %s) goto %s;\n", cmp_node_ptr->str, tokens[cmp_inc_index].vtype == tVAR_CINT ? tokens[cmp_inc_index].num_text : tokens[cmp_inc_index].text, tokens[flip+1].text);
				break;
			case tVAR_STR:
				fprintf(pfile,"if(strcmp(%s,%s%s%s)) goto %s;\n", cmp_node_ptr->str, tokens[cmp_inc_index].vtype == tVAR_CSTR ? "\"" : "", convertEscapeCharacters(tokens[cmp_inc_index].text,tmp_buf),tokens[cmp_inc_index].vtype == tVAR_CSTR ? "\"" : "",tokens[flip+1].text);
				break;
			}

		 }
	    break;
		case JG:
		case JLE:
		case JGE:
		case JL: {
			char tmp[25];
			switch(tokens[flip].code) { 
		    case JG:
				strcpy(tmp,">");
			break;
			case JL:
				strcpy(tmp,"<");
			break;
			case JGE:
				strcpy(tmp,">=");
			break;
			case JLE:
				strcpy(tmp, "<=");
			break;
			}

			fprintf(pfile, "if(%s %s %s) goto %s;\n", cmp_node_ptr->str, tmp, tokens[cmp_inc_index].vtype == tVAR_CINT ?  tokens[cmp_inc_index].num_text : tokens[cmp_inc_index].text, tokens[flip+1].text);

		}
		break;
		

		case JMP: {
			fprintf(pfile,"goto %s;\n", tokens[flip+1].text);
		}
	    break;
		case RET: {
			fprintf(pfile,"return 0;\n");
		}
	    break;
		case EXIT: {
			fprintf(pfile,"exit(0);\n");
		}
	    break;
		case RESET: {
			fprintf(pfile,"main();\n");
			break;
		}
		case SLOOP:	{
				node_ptr = Search(&bend->g_stable.root, tokens[flip+1].text);
				if(!node_ptr) { return; }
				sloop_node_ptr = node_ptr;
				fprintf(pfile, "do {\n");
			}
			break;
		case LOOP: {
			if(sloop_node_ptr == 0) return;
			fprintf(pfile,"\n} while(%s > 1);\n",sloop_node_ptr->str);
		}
		break;
		case LOOPN0: {
			if(sloop_node_ptr == 0) return;
			fprintf(pfile,"} while(%s != 0);\n", sloop_node_ptr->str);
			break;
		}
		default:
			{
				if(tokens[flip].vtype == tVAR_LABEL && strstr(tokens[flip].text, ":")) {
					fprintf(pfile,"%s\n", tokens[flip].text);
				}
			}
			break;
		}
		flip++;
	} while (tokens[flip].code != CB);
}

void _PROCcess_PROC(FILE *pfile, struct BackEnd *bend, struct Token *tokens, int flip) 
{
	do {
		if(tokens[flip].code == End) { return; }
		switch(tokens[flip].code) {
			case _PROC: {
				fprintf(pfile,"int masc_%s();\n", tokens[flip+1].text);
			}
			break;
		}
		flip++;
	} while (tokens[flip].code != End);
}

void put_PROC(FILE *pfile, struct BackEnd *bend, struct Token *tokens, int flip) 
{
	do {
		switch(tokens[flip].code) {
		case _PROC: {
			fprintf(pfile, "int masc_%s () {\n",tokens[flip+1].text);
			_PROCcessInc(pfile,bend,tokens,flip);
			fprintf(pfile, "}\n");
		}
		break;
		}

		flip++;
	} while(tokens[flip].code != End);
}

// translate intermediate code to C source code
void translateMasc_C(struct BackEnd *bend, struct Token *tokens)
{
	int i_p = 0;
	int cip = 0;
	int tip = 0;
	int flip = 0;
	FILE *pfile = fopen(outfile, "w");
	if(!pfile) {
		printf("Error could not open file for output.\n");
		return;
	}

	fprintf(pfile, "#include<stdio.h>\n#include<stdlib.h>\n#include<string.h>\n");
	fprintf(pfile, "struct Stack {\nfloat stack[%d];\nint stack_ptr;\n};\n", max_string_size);
	fprintf(pfile, "void initStack(struct Stack *st)\n{\nst->stack_ptr = 0;\n}");
	fprintf(pfile, "\nvoid Push(struct Stack *st, float var)\n{\nif(st->stack_ptr < %d)\nst->stack[st->stack_ptr++] = var;\nelse\nprintf(\"Stack overflow\\n\");\n}\n", max_string_size);
	fprintf(pfile, "\nfloat Pop(struct Stack *st)\n{\nif(st->stack_ptr >= 0)\nreturn st->stack[--st->stack_ptr];\nelse\nprintf(\"Stack Underflow\\n\");return 0;\n}\n");
	fprintf(pfile, "\nstruct Stack masc__st;\n");
	_PROCcess_PROC(pfile,bend,tokens,flip);

	while(i_p < bend->inc_len && tokens[i_p].code != End)
	{
		switch(tokens[i_p].code) {
			case VAR: {
				cip = tip = i_p;
				do {
					switch(tokens[cip].code) {
					case VAR_INT:
						if(tokens[cip+2].code == EQ) {
							fprintf(pfile, "int %s = %d;\n", tokens[cip+1].text, (int)tokens[cip+3].value);
						}
						else {
							fprintf(pfile, "int %s = 0;\n", tokens[cip+1].text);
						}
						break;
					case VAR_FLOAT: {
						if(tokens[cip+2].code == EQ) {
							fprintf(pfile, "float %s = %d;\n", tokens[cip+1].text, tokens[cip+3].value);

						}
						else
							fprintf(pfile, "float %s = 0;\n", tokens[cip+1].text);
					}
					break;
					case VAR_STR: {
						fprintf(pfile,"char %s[%d];\n", tokens[cip+1].text, max_string_size); 
					}
				    break;
					}

					cip++;
				} while(tokens[cip].code != CB);
			}
		    break;
			case BEGIN:
				fprintf(pfile, "int main() {\n");
				fprintf(pfile,"initStack(&masc__st);\n");
				// first print out the variables
				do {
					if(tokens[tip+2].code == EQ) {
					switch(tokens[tip].code) {
					case VAR_STR:
						fprintf(pfile,"strncpy(%s,%s,%d);\n", tokens[tip+1].text,tokens[tip+3].text,max_string_size);
						fprintf(pfile,"%s[%d] = 0;\n", tokens[tip+1].text, strlen(tokens[tip+3].text)-2);

					break;
					}
					}
					tip++;
				} while(tokens[tip].code != CB);
				fprintf(pfile, "begin:\n");
				flip = i_p;
				// begin instructions here
                _PROCcessInc(pfile,bend,tokens,flip);
				// goto the end
				fprintf(pfile, "goto end;\n");
				break;
			case END:
				fprintf(pfile, "end:\n");
				flip = i_p;
				_PROCcessInc(pfile,bend,tokens,flip);
				// end instructions here

				break;
		}

		i_p++;
	}
	fprintf(pfile, "\nreturn 0;\n}\n");
	put_PROC(pfile,bend,tokens,0);
	fclose(pfile);
}

// please dont spoil my day im miles away
