%{
#include <stdint.h>
#include <stdio.h>
#include <bu/string.h>

#include "gamebuilder.h"
#include "astnode.h"

typedef void *yyscan_t;

%}


%locations
%pure-parser
/* %define api.pure */
%debug
%error-verbose

%parse-param	{ yyscan_t yyscanner }
%lex-param		{ yysacn_t yyscanner }

%parse-param	{ GameBuilder &bld }

%union {
	int64_t iValue;
	double dValue;
	Bu::String *sValue;
	bool bValue;
}

%{
int yylex( YYSTYPE *yylval, struct YYLTYPE *llocp, yyscan_t yyscanner );
void yyerror( YYLTYPE *llocp, yyscan_t yyscanner, GameBuilder &, const char *error )
{
	printf("%d:%d-%d:%d: %s\n",
		llocp->first_line, llocp->first_column,
		llocp->last_line, llocp->last_column, error
		);
}
%}

%token tokGame
%token tokGlobal
%token tokFunction
%token tokSituation
%token tokSetup
%token tokEnter
%token tokAnd
%token tokOr
%token tokWhile
%token tokFor
%token tokEach
%token tokDo
%token tokIn
%token tokIf
%token tokThen
%token tokElse
%token tokNot
%token tokCommand
%token tokPlayer
%token tokLtEq
%token tokGtEq
%token tokCmp
%token tokPlusAssign
%token tokMinusAssign
%token tokTimesAssign
%token tokDivideAssign
%token <sValue> tokSituationName
%token <sValue> tokIdent
%token tokGoto
%token <sValue> tokString
%token <iValue> tokInt
%token <dValue> tokFloat
%token <bValue> tokBool
%token tokNull

%destructor { delete $$; printf("string deleted.\n"); } tokString tokSituationName

%token eos 0 "end of stream"

%left NOT
%right '=' tokPlusAssign tokMinusAssign tokTimesAssign tokDivideAssign
%left '-' '+'
%left '*' '/'
%left tokIn tokAnd tokOr
%right '<' '>' tokLtEq tokGtEq tokCmp
%left '(' ')' '[' ']'
%left NEG

%%
input: gameDecls globalDecl bodyDecl
	 ;

gameDecls:
		 | gameDecls tokGame '.' tokIdent '=' literal ';' { bld.setGameParam( *($4) ); }
		 ;

globalDecl:
		  | tokGlobal '{' { bld.beginGlobal(); }  globalExprList '}'
		  	{ bld.closeGlobal(); }
		  ;

globalExprList:
			  | globalExprList cmpltGlobalExpr
			  ;

cmpltGlobalExpr: globalExpr ';'
			   | commandDecl
			   ;

globalExpr: tokIdent '=' expr
		  ;

bodyDecl:
		| bodyDecl situation
		| bodyDecl function
		;

situation: tokSituation tokSituationName { bld.beginSituation( *($2) ); } '{' situationMembers '}' { bld.endSituation(); }
		 ;

situationMembers:
				| situationMembers situationModeFunc
				| situationMembers commandDecl
				;

situationModeFunc: situationMode '{' cmpltExprList '}' {
				 	bld.closeSituationMode();
				 }
				 ;

situationMode: tokSetup { bld.beginSituationMode( Situation::modeSetup ); }
			 | tokEnter { bld.beginSituationMode( Situation::modeEnter ); }
			 ;

function: tokFunction tokIdent { bld.beginFunction( *($2) ); } '(' funcParamList ')' { bld.endFunctionParams(); } '{' cmpltExprList '}' { bld.endFunction(); }
		;

funcParamList:
			 | tokIdent { bld.addFunctionParam( *($1) ); } funcParamListEx
			 ;

funcParamListEx:
			   | funcParamListEx ',' tokIdent { bld.addFunctionParam( *($3) ); }
			   ;

cmpltExprList:
			 | cmpltExprList cmpltExpr
			 ;

cmpltExpr: expr ';'
		 | tokGoto '(' expr ')' ';' { bld.addNode( AstNode::tGoto ); }
		 | ifbase
		 | tokFor tokEach forIterator tokIn expr tokDo '{' cmpltExprList '}'
		 | tokWhile {
		 		bld.addNode( AstNode::tWhile );
				bld.addNode( AstNode::tScope );
			} expr {
				bld.closeNode();
				bld.addNode( AstNode::tScope );
			} tokDo '{' cmpltExprList '}' {
				bld.closeNode();
				bld.closeNode();
			}
		 ;

forIterator: tokIdent
		   | tokIdent ':' tokIdent

ifbase: tokIf {
	  		bld.addNode( AstNode::tIf );
			bld.addNode( AstNode::tScope );
		} expr {
	  		bld.closeNode();
			bld.addNode( AstNode::tScope );
		} tokThen '{' cmpltExprList '}' {
			bld.closeNode();
		} ifnext {
			bld.closeNode();
		}
		;

ifnext:
	  | tokElse { bld.addNode( AstNode::tScope ); } '{' cmpltExprList '}' {
	  		bld.closeNode();
	  	}
	  | tokElse { bld.addNode( AstNode::tScope ); } ifbase
	  ;

varRef: tokIdent { bld.addVarRef( *($1), sidLocal ); }
	  | tokPlayer '.' tokIdent { bld.addVarRef( *($3), sidPlayer ); }
	  | tokGlobal '.' tokIdent { bld.addVarRef( *($3), sidGlobal ); }
	  | tokSituation '.' tokIdent { bld.addVarRef( *($3), sidSituation ); }
	  ;

literal: tokInt { bld.addLiteral( Variable( $1 ) ); }
	   | tokFloat { bld.addLiteral( Variable( $1 ) ); }
	   | tokString { bld.addLiteral( Variable( *($1) ) ); }
	   | tokBool { bld.addLiteral( Variable( $1 ) ); }
	   | tokNull { bld.addLiteral( Variable( Variable::tNull ) ); }
	   | tokSituationName { bld.addLiteral( Variable::newSituationName( *($1) ) ); }
	   ;

expr: literal
	| tokIdent '(' funcCallParams ')' { bld.addFuncCall( *($1) ); }
	| varRef
	| varRef '=' expr { bld.addNode( AstNode::tStore ); }
	| varRef tokPlusAssign expr { bld.addNode( AstNode::tPlusStore ); } 
	| varRef tokMinusAssign expr { bld.addNode( AstNode::tMinusStore ); }
	| varRef tokTimesAssign expr { bld.addNode( AstNode::tMultiplyStore ); }
	| varRef tokDivideAssign expr { bld.addNode( AstNode::tDivideStore ); }
	| expr '+' expr { bld.addNode( AstNode::tPlus ); }
	| expr '-' expr { bld.addNode( AstNode::tMinus ); }
	| expr '/' expr { bld.addNode( AstNode::tDivide ); }
	| expr '*' expr { bld.addNode( AstNode::tMultiply ); }
	| expr tokAnd expr { bld.addNode( AstNode::tAnd ); }
	| expr tokOr expr { bld.addNode( AstNode::tOr ); }
	| expr tokIn expr { bld.addNode( AstNode::tIn ); }
	| expr tokCmp expr { bld.addNode( AstNode::tComp ); }
	| expr '<' expr { bld.addNode( AstNode::tCompLt ); }
	| expr '>' expr { bld.addNode( AstNode::tCompGt ); }
	| expr tokLtEq expr { bld.addNode( AstNode::tCompLtEq ); }
	| expr tokGtEq expr { bld.addNode( AstNode::tCompGtEq ); }
	| '(' expr ')'
	| expr '[' expr ']'
	| '[' ']'
	| '[' listValues ']'
	| '{' '}'
	| '{' dictValues '}'
	| tokNot expr %prec NOT { bld.addNode( AstNode::tNot ); }
	| '-' expr %prec NEG { bld.addNode( AstNode::tNegate ); }
	;

funcCallParams:
			  | expr funcCallParamsEx
			  ;

funcCallParamsEx:
				| funcCallParamsEx ',' expr
				;

listValues: expr
		  | listValues ',' expr
		  ;

dictValues: expr ':' expr
		  | dictValues ',' expr ':' expr
		  ;

commandDecl: tokCommand ':' tokString { bld.beginCommand( *$3 ); }
		   		commandParamList '{' cmpltExprList '}' { bld.closeCommand(); }
		   ;

commandParamList:
				| commandParamList tokString { bld.addCommandLiteral( *$2 ); }
				| commandParamList tokIdent { bld.addCommandParam( *$2 ); }
				;
%%
/*
void yylex_init( yyscan_t * );
void yylex_destroy( yyscan_t );

int main()
{
	yyscan_t scanner;

	yylex_init ( &scanner );
	yyparse( scanner );
	yylex_destroy ( scanner );	
	return 0;
}
*/