%{
#include 
#include 
#include 
#include 
#include 

#include "lex.yy.h"
#include "meme.h"
#include "memetype.h"

void yyerror(char *s);


nodeType *root = NULL;

extern MemeScope globals;

%}

%union {
    int iValue;                 /* integer value */
    double rValue;				/* real value */
	char *sName; 				/* symbol name */
	char *tValue; 				/* char ptr */
    nodeType *nPtr;             /* node pointer */
};


%token  INTEGER
%token  REAL
%token  VARIABLE
%token  STRING

%token WHILE IF PRINT RETURN BREAK EOL DO UNTIL NULLVAL TRUEVAL FALSEVAL APPEND FOR IN DEFINE METHOD

%nonassoc IFX
%nonassoc IDX
%nonassoc ELSE

%nonassoc ','
%left AND OR NOT
%left GE LE EQ NE '>' '<'
%left '+' '-'
%left '*' '/' '%'
%nonassoc UMINUS
%nonassoc '['
%nonassoc ASSIGN
%nonassoc FUNCDEF
%nonassoc VARDEF
%nonassoc SIMPLE

%type  stmt  expr function stmt_list nullstmt fullstmt arglist define stmt_or_def paramlist simplestmt

%%

program:
        function                { root = $1; }
        ;

function:
		stmt_or_def { $$ = $1; }
        | function stmt_or_def { $$ = opr(';', 2, $1, $2); }
        ;

stmt_or_def:
		   define
		   | stmt
		   ;

define:
	   DEFINE VARIABLE METHOD '(' paramlist ')' EOL fullstmt %prec FUNCDEF	{ $$ = define_function($2, $5, $8); }
	   | DEFINE VARIABLE METHOD '('  ')' EOL fullstmt %prec FUNCDEF	{ $$ = define_function($2, NULL, $7); }
	   | DEFINE VARIABLE expr EOL %prec VARDEF { $$ = opr(DEFINE, 2, id($2), $3); }
	   | DEFINE VARIABLE EOL %prec NULLDEF { $$ = opr(DEFINE, 1, id($2) ); }
	   ;


		
stmt:
		nullstmt
		| fullstmt
		;

nullstmt:
          EOL                            { $$ = opr(';', 2, NULL, NULL); }
		  ;

paramlist:
         VARIABLE              			{ $$ = param($1); }
		 | paramlist ',' VARIABLE			{ $$ = chainArg($1, param($3)); }
		 ;

arglist:
	      expr							{ $$ = $1; }
		  | arglist ',' expr			{ $$ = chainArg($1, $3); }
		  ;

fullstmt:		
		simplestmt		%prec SIMPLE	{ $$ = $1; }
		| '{' stmt_list '}' EOL              { $$ = $2; }
        ;
		
simplestmt:  
        expr EOL                       { $$ = $1; }
        | PRINT expr EOL                 { $$ = opr(PRINT, 1, $2); }
        | expr '=' expr EOL %prec ASSIGN{ $$ = opr('=', 2, $1, $3); }
        | WHILE expr EOL stmt        { $$ = opr(WHILE, 2, $2, $4); }
		| DO EOL stmt UNTIL expr EOL { $$ = opr(UNTIL, 2, $3, $5); }
		| FOR VARIABLE IN expr EOL fullstmt		 { $$ = opr(FOR,3, id($2), $4, $6); } 
        | IF expr EOL stmt %prec IFX { $$ = opr(IF, 2, $2, $4); }
        | IF expr EOL stmt ELSE fullstmt { $$ = opr(IF, 3, $2, $4, $6); }
        | IF expr EOL stmt ELSE EOL fullstmt { $$ = opr(IF, 3, $2, $4, $7); }
        | RETURN expr EOL 			{ $$ = opr(RETURN, 1, $2); }
        | BREAK EOL 				{ $$ = opr(BREAK, 1, NULL); }
        ;

stmt_list:
          stmt_or_def                  { $$ = $1; }
        | stmt_list stmt_or_def        { $$ = opr(';', 2, $1, $2); }
        ;

expr:
	     INTEGER               { $$ = con($1); }
        | VARIABLE              { $$ = id($1); }
        | STRING 				{ $$ = str($1); }
		| NULLVAL 				{ $$ = nullval(); }
		| TRUEVAL 				{ $$ = boolval(true); }
		| FALSEVAL 				{ $$ = boolval(false); }
		| REAL 					{ $$ = realval($1); }
		| expr '[' expr ']' 	 %prec IDX	{ $$ = opr('[',2, $1, $3); } 
		| expr '[' ']' 	 	%prec IDX { $$ = opr(APPEND,1, $1); } 
		| VARIABLE '(' arglist ')'  { $$ = callFunction($1,$3); }
		| VARIABLE '('  ')'  { $$ = callFunction($1,NULL); }
		| '[' arglist ']' 				{ $$ = newArray( $2 ); }
		| '[' ']' 				{ $$ = newArray( NULL ); }
    	| '-' expr %prec UMINUS { $$ = opr(UMINUS, 1, $2); }
        | expr '+' expr         { $$ = opr('+', 2, $1, $3); }
         | expr '-' expr         { $$ = opr('-', 2, $1, $3); }
        | expr '*' expr         { $$ = opr('*', 2, $1, $3); }
        | expr '/' expr         { $$ = opr('/', 2, $1, $3); }
        | expr '%' expr         { $$ = opr('%', 2, $1, $3); }
        | expr '<' expr         { $$ = opr('<', 2, $1, $3); }
        | expr '>' expr         { $$ = opr('>', 2, $1, $3); }
		| expr AND expr 		{ $$ = opr(AND, 2, $1, $3); }
		| expr OR expr 			{ $$ = opr(OR, 2, $1, $3); }
		| NOT expr 				{ $$ = opr(NOT,1, $2); }
        | expr GE expr          { $$ = opr(GE, 2, $1, $3); }
        | expr LE expr          { $$ = opr(LE, 2, $1, $3); }
        | expr NE expr          { $$ = opr(NE, 2, $1, $3); }
        | expr EQ expr          { $$ = opr(EQ, 2, $1, $3); }
        | '(' expr ')'          { $$ = $2; }
        ;


%%



void yyerror(char *s) {
	error( s );
}

int yydebug;
int main(int argc, const char **argv, char **envp) {
	yydebug=1;
	if( argc > 1 )
	{
		const char *filename = argv[1];
		FILE *fp = fopen(filename,"r");
		if( !fp )
			error("Could not open input file %s: %s",filename,strerror(errno));
		// YY_BUFFER_STATE buffer =
		yy_switch_to_buffer( yy_create_buffer ( fp, 32768) );
	}

	setup(argc, argv, envp);
	

	MemeReaper reaper;
    yyparse();
	// { ex($1); freeNode($1); exit(0); }

	int eval=9;
	if( root )
	{
		Value result = ex( globals, root );
		eval = result.toInt();
		freeNode( root );
	}

	freeStrings();

    return 0;
}