%{

#include <stdio.h>
#include "symbol.h"
#include "expr.h"
#include "forw.h"
#include "emit.h"

FILE *outputfile;

int   current_address = 0;
int   current_level = 0;

extern FILE *yyin;
extern int	line_number;
extern char	current_file[40];

extern SYMBOL *lookupsymbol();
extern EXPR_NODE *retrootexpr();

yyerror(char *yymsg)
{
	fprintf(stderr,"Error %s, in file %s, line %d\n",
		yymsg,current_file,line_number);
}

%}

%union {
	int	integer;
	char	string[128];
	EXPR_NODE *exprstruct;
}

%token <integer> NUM
%token <string>  ID  STRING
%token           ORG
%token           BLOCK  END
%token           DATA
%token		 WARN  ERROR
%token           THEEOF
%token           ASSIGN  COLON
%token           ADD  SUB
%token           MUL  DIV  MOD
%token           SHL  SHR
%token           LAND  LOR
%token           BNOT LNOT
%token           BAND  BOR  BXOR
%token           EQ  NEQ  GT  GEQ  LS  LEQ
%token           LPAR  RPAR
%token           COMMA  PERIOD

%type <exprstruct> expression
%type <integer> const_expression

%left EQ NEQ GT GEQ LS LEQ
%left ADD SUB SHL SHR LAND LOR
%left MUL DIV MOD BAND BOR BXOR

%nonassoc UMINUS

%%

program	:	chunks		{
					if (anyforwardsleft())
					{
						yyerror("Some Jumps could not be resolved");
						exit(1);
					}
				}
	;

chunks	:	chunk
	|	chunks chunk
	;

chunk	:	vdef
	|	info
	|	block
	;

stmts	:	stmt
	|	stmts stmt
	;

stmt	:	definition	{}
	|	block		{}
	|	plant		{}
	|	info		{}
	;

block	:	BLOCK		{
					current_level++;
				}
		stmts
		END		{
					resolveforwardrefs(current_level);
					removelocalsyms(current_level);
					--current_level;
				}
	;

plant	:	DATA expression	{
					EXPR_NODE *dataexpress;
					int datavalue;

					dataexpress = $2;

					if (exprcheck(dataexpress))
					{
						addforw(current_address, dataexpress, current_level, current_file, line_number);
					}
					else
					{
						datavalue = evaluateexpr(dataexpress);
						emit(current_address, datavalue);
						deletewholeexpr(dataexpress);
					}

					current_address++;
				}
	;

origin	:	ORG const_expression
				{
					current_address = $2;
				}

	;

info	:	WARN STRING	{
					fprintf(stderr,"Warning %s at line %d in file %s\n",
						$2, line_number, current_file);
				}
	|	ERROR STRING	{
					fprintf(stderr,"Error %s at line %d in file %s\n",
						$2, line_number, current_file);
				}
	;

const_expression:
		expression	{
					EXPR_NODE *constexpress;

					constexpress = $1;

					if (exprcheck(constexpress))
					{
						yyerror("Need a constant expression\n");
						exit(1);
					}
					else
					{
						$$ = evaluateexpr(constexpress);
					}

					deletewholeexpr(constexpress);
				}
	;

expression:			{
					initexpr();
				}
		expr
				{
					/* exprlist(); */
					$$ = retrootexpr();
				}
	;

expr	:	expr ADD expr	{ addexpr(EXPR_OP, ADD, NULL); }
	|	expr SUB expr	{ addexpr(EXPR_OP, SUB, NULL); }
	|	expr MUL expr	{ addexpr(EXPR_OP, MUL, NULL); }
	|	expr DIV expr	{ addexpr(EXPR_OP, DIV, NULL); }
	|	expr MOD expr	{ addexpr(EXPR_OP, MOD, NULL); }

	|	expr SHL expr	{ addexpr(EXPR_OP, SHL, NULL); }
	|	expr SHR expr	{ addexpr(EXPR_OP, SHR, NULL); }

	|	expr LAND expr	{ addexpr(EXPR_OP, LAND, NULL); }
	|	expr LOR expr	{ addexpr(EXPR_OP, LOR, NULL); }

	|	expr BAND expr	{ addexpr(EXPR_OP, BAND, NULL); }
	|	expr BOR expr	{ addexpr(EXPR_OP, BOR, NULL); }
	|	expr BXOR expr	{ addexpr(EXPR_OP, BXOR, NULL); }

	|	expr EQ expr	{ addexpr(EXPR_OP, EQ, NULL); }
	|	expr NEQ expr	{ addexpr(EXPR_OP, NEQ, NULL); }
	|	expr GT expr	{ addexpr(EXPR_OP, GT, NULL); }
	|	expr GEQ expr	{ addexpr(EXPR_OP, GEQ, NULL); }
	|	expr LS expr	{ addexpr(EXPR_OP, LS, NULL); }
	|	expr LEQ expr	{ addexpr(EXPR_OP, LEQ, NULL); }

	|	SUB expr %prec UMINUS	{ addexpr(EXPR_OP, UMINUS, NULL); }
	|	LNOT expr %prec UMINUS	{ addexpr(EXPR_OP, LNOT, NULL); }
	|	BNOT expr %prec UMINUS	{ addexpr(EXPR_OP, BNOT, NULL); }
	|	LPAR expr RPAR	{}
	|	NUM		{ addexpr(EXPR_NUM, $1, NULL); }
	|	PERIOD		{ addexpr(EXPR_NUM, current_address, NULL); }
	|	ID		{ addexpr(EXPR_ID, 0, $1); }
	;

definition:	vdef		{}
	|	ldef		{}
	|	origin		{}
	;

vdef	:	ID ASSIGN const_expression
				{
					SYMBOL *symptr;
					int value;

					value = $3;

					symptr = lookupsymbol($1);

					if (symptr == NULL)
					{
						addsymbol($1, 0, UNDEFINED, VARDEF, current_level);
						symptr = lookupsymbol($1);
					}

					if (symptr->scope != current_level)
					{
						addsymbol($1 ,0 , DEFINED, VARDEF, current_level);
						symptr = lookupsymbol($1);
					}

					if (symptr->type == LABDEF)
					{
						yyerror("Variable shadows label in this block\n");
						exit(1);
					}

					symptr->defined = DEFINED;
					symptr->value = value;
				}
	;

ldef	:	ID COLON	{
					SYMBOL *symptr;

					symptr = lookupsymbol($1);

					if (symptr == NULL)
					{
						addsymbol($1, current_address, DEFINED, LABDEF, current_level);
					}
					else
					{
						if (symptr->scope == current_level)
						{
							if (symptr->defined == UNDEFINED)
							{
								symptr->value = current_address;
								symptr->defined = DEFINED;
							}
							else
							{
								yyerror("Redefined label within a block");
								exit(1);
							}
						}
						else
						{
							addsymbol($1 ,current_address , DEFINED, LABDEF, current_level);
						}
					}
				}
	;

%%

int main(int argc, char **argv)
{
	if (argc != 3)
	{
		fprintf(stderr,"\t%s <input> <output>\n",argv[0]);
		exit(1);
	}

	if((yyin=fopen(argv[1],"r"))==NULL)
	{
		fprintf(stderr,"Cannot Open Input file %s\n",argv[1]);
		exit(1);
	}

	if((outputfile=fopen(argv[2],"w"))==NULL)
	{
		fprintf(stderr,"Cannot Open Output file %s\n",argv[2]);
		exit(1);
	}

	initemit();
	initsymbol();
	initforw();

	yyparse();

	outputmemory(ASMIHX16, outputfile);

	fclose(outputfile);
}

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