/* parser.c */

#include "picasm.h"

void statement();	/* to allow mutual recursion */

void openafile(char *filename)
{
	last_lexlevel++;
	lexstack[last_lexlevel].type     = LEXFILE;
	strcpy(lexstack[last_lexlevel].filename,filename);
	if((lexstack[last_lexlevel].file=fopen(filename,"r")) == NULL)
		error("Could not open inputfile \"",filename,"\"","");
	lexstack[last_lexlevel].lineno = 1;
}

void match(t)
	int t;
{
	char tmp1[30];
	char tmp2[30];

	if (current_symbol.token == t)
		getanother();
	else
	{
		strcpy(tmp1,lookupstr(current_symbol.token));
		strcpy(tmp2,lookupstr(t));
		error("syntax error got ",tmp1," wanted ",tmp2);
	}
}

int have(int t)
{
	return current_symbol.token == t;
}

void hw_functions()
{
	getanother();

	switch(current_symbol.token)
	{
		case PROC:	getanother();
				if (architecture != NOARCH)
				{
					if (ourpic.pictype != expression())
						error("Cannot redefine PIC type","","","");
				}
				else
				{
					ourpic.pictype = expression();
					if (ourpic.pictype < 60)
						architecture = PIC12;
					else
						architecture = PIC14;
				}
				break;

		case WATCH:	getanother();
				if(have(ON))
					ourpic.wd_fuse = 1;
				else
				{
					if(!have(OFF))
						error("Flag must be either on or off","","","");
					ourpic.wd_fuse = 0;
				}
				getanother();
				break;

		case PWRUP:	getanother();
				if(have(ON))
					ourpic.pu_fuse = 1;
				else
				{
					if(!have(OFF))
						error("Flag must be either on or off","","","");
					ourpic.pu_fuse = 0;
				}
				getanother();
				break;

		case PROTECT:	getanother();
				if(have(ON))
					ourpic.cp_fuse = 0;
				else
				{
					if(!have(OFF))
						error("Flag must be either on or off","","","");
					ourpic.cp_fuse = 1;
				}
				getanother();
				break;

		case IDWRDS:	getanother();
				ourpic.picid[0] = expression();
				match(COMMA);
				ourpic.picid[1] = expression();
				match(COMMA);
				ourpic.picid[2] = expression();
				match(COMMA);
				ourpic.picid[3] = expression();
				break;

		case CLK:	getanother();
				switch(current_symbol.token)
				{
					case LP:	ourpic.osctype = 0;
							break;
					case XT:	ourpic.osctype = 1;
							break;
					case HS:	ourpic.osctype = 2;
							break;
					case RC:	ourpic.osctype = 3;
							break;
					default:	error("Not a valid oscillator type","","","");
				}
				getanother();
				match(COMMA);
				ourpic.clock = (float)expression();
				break;

		default:	error("Not a valid hardware definition statement","","","");
	}

}

void definelabelorvalue()
{
	SYMBOL *idptr;

	idptr = lookup(current_symbol.lexptr);	/* point to the entry */

	if (idptr == (SYMBOL *)0)
	{
		printf("Internal compiler error\n");
		printf("Could not find symbol %s\n",current_symbol.lexptr);
		exit(1); /* before seg fault! */
	}

	getanother();

	switch(current_symbol.token)
	{
		case COLON:	getanother();
				idptr->value = asm_current_address;
				idptr->type = VALUE_DEF;
				break;

		case ASSIGN:
		case EQU:	getanother();
				idptr->value = expression();
				idptr->type = VALUE_DEF;
				break;

		default:	error("syntax error in definition","","","");
	}
}

void instruction()
{
	int f;
	int d;
	int b;
	int current_instruction;
	SYMBOL savesym;
	int includestr;
	int local_label;

	current_instruction = current_symbol.token;

	switch(current_symbol.token)
	{
		case IFDEF:
		case IFNDEF:
		case IF:	switch(current_symbol.token)
				{
					case IF:	getanother();
							f = expression();
							break;

					case IFDEF:	getanother();
							if(!have(ID))
								error("ifdef must have identifer to check","","","");
							f = current_symbol.type == VALUE_DEF;
							getanother();
							break;

					case IFNDEF:	getanother();
							if(!have(ID))
								error("ifndef must have identifer to check","","","");
							f = current_symbol.type == VALUE_UNDEF;
							getanother();
							break;

					default:	f = 0;	/* stop comiler warning */
							error("eh?","","","");
							break;
				}

				if(f)
				{
					while(!have(ELSE) && !have(ENDIF) && !have(DONE))
						statement();
					if (have(ELSE))
					{
						while(!have(ENDIF) && !have(DONE))
							getanother();
					}
				}
				else
				{
					while(!have(ELSE) && !have(ENDIF) && !have(DONE))
						getanother();
					if (have(ELSE))
					{
						getanother();
						while(!have(ENDIF) && !have(DONE))
							statement();
					}
				}
				match(ENDIF);
				break;

		case ORG:	getanother();
				asm_current_address = expression();
				break;

		case END:	getanother();
				asm_current_address = INVALIDADDR;
				break;

		case ENDB:	getanother();
				error("endb without begin","","","");
				break;

		case BEGIN:	local_label = lastentry;	/* save id position */
				getanother();

				while(!have(ENDB))
					statement();

				lastentry = local_label;	/* erase locals */

				getanother();	/* move past endb */

				fillinjumps(FALSE);

				break;

		case INCLUDE:	getanother();

				if(!have(STRING))
					error("Need a string for include","","","");

				includestr = current_symbol.value;

				getanother(); /* move on from string */

				cpsym(&savesym, &current_symbol);

				openafile(lexstrings[includestr]);

				getanother(); /* from the new file */
				while(!have(DONE))
					statement();

				cpsym(&current_symbol, &savesym);
				break;

		/* "real instructions" */

		case CLRW:
		case NOP:
		case CLRWDT:
		case RETFIE:
		case RETURN:
		case SLEEP:
		case OPTION:
				getanother();
				emit_fixed(current_instruction);
				break;

		case MOVWF:
		case CLRF:	getanother();
				f = expression();
				emit_f_d(current_instruction, f, 0);
				break;

		case ADDWF:
		case ANDWF:
		case COMF:
		case DECF:
		case DECFSZ:
		case INCF:
		case INCFSZ:
		case IORWF:
		case MOVF:
		case RLF:
		case RRF:
		case SUBWF:
		case SWAPF:
		case XORWF:	getanother();
				f = expression();
				match(COMMA);
				d = expression();
				emit_f_d(current_instruction, f, d);
				break;

		case BCF:
		case BSF:
		case BTFSC:
		case BTFSS:	getanother();
				f = expression();
				match(COMMA);
				b = expression();
				emit_f_b(current_instruction, f, b);
				break;

		case DATA:	/* ok so this isn't quite a real instruction */
		case ADDLW:
		case ANDLW:
		case IORLW:
		case MOVLW:
		case RETLW:
		case SUBLW:
		case XORLW:
		case TRIS:	getanother();
				f = expression();
				emit_literal(current_instruction, f);
				break;

		case CALL:
		case GOTO:	getanother();
				if ((current_symbol.token == ID) && (current_symbol.type == VALUE_UNDEF))
				{
					forwardjump(current_instruction);
					f = 0;		/* fill in space with null jump! */
					getanother();
				}
				else
					f = expression();
				emit_literal(current_instruction, f);
				break;

		default:	error("should never reach here!","","","");
	}
}

void statement()
{
	int macnum;
	int i;
	char macroname[BSIZE];
	SYMBOL macrosave;

	switch(current_symbol.token)
	{
		case ID:	/* label or value definition of macro call */
				if(current_symbol.type == MACRO_DEF)
				{
					macnum = current_symbol.value;
					strcpy(macroname, current_symbol.lexptr);

					getanother();

					if(macros[macnum].numparams != 0)
					{
						for (i = 0; i<(macros[macnum].numparams - 1); i++)
						{
							macros[macnum].params[i]->value = expression();
							macros[macnum].params[i]->type  = VALUE_DEF;
							match(COMMA);
						}
						macros[macnum].params[(macros[macnum].numparams-1)]->value = expression();
						macros[macnum].params[(macros[macnum].numparams-1)]->type  = VALUE_DEF;
					}

					/* finished parsing, so point the lexical analyser at the macro */

					cpsym(&macrosave, &current_symbol);

					last_lexlevel++;
					lexstack[last_lexlevel].type     = LEXMACRO;
					strcpy(lexstack[last_lexlevel].filename,macroname);

					lexstack[last_lexlevel].whereupto = macros[macnum].startsym;
					lexstack[last_lexlevel].symsleft  = macros[macnum].numsyms;

					getanother();	/* from the macro */
					while(!have(DONE))
						statement();
					/* now back in previous level */
					cpsym(&current_symbol, &macrosave);
				}
				else
					definelabelorvalue();
				break;

		case MACRO:	definemacro();
				break;

		default:	/* its an assembler statement */
				/* or a directive */

				instruction();
				break;
	}
}

void program(char *filename)
{
	openafile(filename);

	getanother();	/* kick us off with a symbol */

	while(current_symbol.token == DOLLAR)
		hw_functions();

	while(current_symbol.token != DONE)
		statement();

	fillinjumps(TRUE);	/* fill in forward jumps */
}

/* ... The End ... */
