%{
/*
* ===========================================================================
* Copyright (C) 2015 the OpenMoHAA team
* 
* This file is part of OpenMoHAA source code.
* 
* OpenMoHAA source code is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
* 
* OpenMoHAA source code is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with OpenMoHAA source code; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
* ===========================================================================
*
*
* yyLexer.*: FLEX Lexical grammar for MoHScript.
*/

#include "scriptcompiler.h"
#include "./yyParser.hpp"

#include <stdio.h>

void fprintf2( FILE *f, const char *format, ... )
{
	va_list va;
	static char buffer[ 4200 ];

	va_start( va, format );
	vsprintf( buffer, format, va );
	va_end( va );

	gi.Printf( buffer );
}

#define fprintf fprintf2

const char* start_ptr;
const char* in_ptr;
extern int prev_yylex;
extern int out_pos;
extern int success_pos;
parseStage_e parseStage;

extern yyparsedata parsedata;

void yyllocset(YYLTYPE *loc, uint32_t off)
{
	success_pos = out_pos - yyleng + off;
	loc->sourcePos = success_pos;
	parsedata.pos = success_pos;
}

void yyreducepos(uint32_t off)
{
	out_pos -= off;
}

#define YYLEX(n) { yyllocset(&yylloc, 0); prev_yylex = n; return n; }
#define YYLEXOFF(n, off) { yyllocset(&yylloc, off); prev_yylex = n; return n; }

#define YY_USER_ACTION \
{ \
	out_pos += yyleng - yy_more_len; \
	yylloc.sourcePos = out_pos; \
	parsedata.pos = out_pos; \
}

#define YY_FATAL_ERROR( n ) yylexerror( n )

void yylexerror( const char *msg )
{
	gi.DPrintf( "%s\n%s", msg, yytext );
	assert( 0 );
}

static void TextEscapeValue( char *str, size_t len )
{
	char *to = parsetree_malloc( len + 1 );

	yylval.s.val.stringValue = to;

	while( len )
	{
		if( *str == '\\' )
		{
			if( len == 1 )
				break;

			if( str[1] == 'n' )
			{
				*to = '\n';
				to++;
			}
			else if( str[1] == 't' )
			{
				*to = '\t';
				to++;
			}
			else if( str[1] == '"' )
			{
				*to = '\"';
				to++;
			}
			else
			{
				*to = str[1];
				to++;
			}

			len -= 2;
			str += 2;
		}
		else
		{
			*to = *str;
			to++;
			len--;
			str++;
		}
	}

	*to = 0;
}

static void TextValue( char *str, size_t len )
{
	char* s = parsetree_malloc(len + 1);
	strncpy(s, str, len);
	s[len] = 0;
	yylval.s.val.stringValue = s;
}

static bool UseField( void )
{
	return prev_yylex == TOKEN_PERIOD
		|| prev_yylex == TOKEN_DOLLAR;
}

#define YY_INPUT(buf,result,max_size) \
{										\
	char c;								\
	int n;								\
										\
	c = '*';							\
	for(n = 0; n < max_size; n++)		\
	{									\
		c = *in_ptr++;					\
		if (!c || c == '\n') {			\
			break;						\
		}								\
										\
		buf[n] = c;						\
	}									\
										\
	if (c == '\n')						\
	{									\
		buf[n++] = c;					\
	}									\
	else if (!c)						\
	{									\
		in_ptr--;						\
	}									\
										\
	result = n;							\
}

%}

/*%option debug*/

%option warn nodefault

%option never-interactive
%option yylineno

%x SCRIPT
%x C_COMMENT
%x C_LINE_COMMENT
%x VARIABLES
%x IDENTIFIER

string		([^\\\"\r\n]|\\.)*
identifier	[^\{\}\(\)\[\]\r\n\,:; \t]
nonexpr		[0-9a-zA-Z_\"'?@#`\.\x80-\xff]
nonnumeric	[a-zA-Z_\"'?@#`\x80-\xff]
alphanum	[a-zA-Z0-9_]+
varname		[a-zA-Z0-9_\"\$]+

%%

"/*"							{ BEGIN( C_COMMENT ); }
<C_COMMENT>"*/"					{ BEGIN( INITIAL ); }
<C_COMMENT>\n					{ ; }
<C_COMMENT>.					{ ; }
"*/"							{ Compiler.CompileError( parsedata.pos - yyleng, "'*/' found outside of comment" ); }

\\[\r\n]+						{ ; }
"//"[^\r\n]*					{ if( prev_yylex != TOKEN_EOL ) YYLEX( TOKEN_EOL ); }

<VARIABLES>"size"							{ BEGIN(INITIAL); YYLEX(TOKEN_SIZE); }
<VARIABLES>[ \t]*\./([0-9]*[^0-9[:space:]])	{ YYLEX(TOKEN_PERIOD); }
<VARIABLES>\"{string}\"						{ BEGIN(INITIAL); TextEscapeValue(yytext + 1, strlen( yytext ) - 2 ); YYLEX(TOKEN_STRING); }
<VARIABLES>{varname}						{
												TextValue(yytext, strlen(yytext));
												YYLEX(TOKEN_IDENTIFIER);
											}
<VARIABLES>[ \t\r\n]						{
												BEGIN(INITIAL);
												unput(yytext[yyleng - 1]);
												yyreducepos(1);
											}
<VARIABLES>.								{
												BEGIN(INITIAL);
												unput(yytext[yyleng - 1]);
												yyreducepos(1);
											}

											

\"{string}\"{nonexpr}			{
									BEGIN(IDENTIFIER);
									yymore();
								}

\"{string}\"					{ TextEscapeValue( yytext + 1, yyleng - 2 ); YYLEX( TOKEN_STRING ); }

"?"								{ YYLEX( TOKEN_TERNARY ); }
"if"							{ YYLEX( TOKEN_IF ); }
"else"							{ YYLEX( TOKEN_ELSE ); }
"while"							{ YYLEX( TOKEN_WHILE ); }
"for"							{ YYLEX( TOKEN_FOR ); }
"do"							{ YYLEX( TOKEN_DO ); }

"game"							{ BEGIN( VARIABLES ); yylval.s.val = node1_( method_game ); YYLEX( TOKEN_LISTENER ); }
"group"							{ BEGIN( VARIABLES ); yylval.s.val = node1_( method_group ); YYLEX( TOKEN_LISTENER ); }
"level"							{ BEGIN( VARIABLES ); yylval.s.val = node1_( method_level ); YYLEX( TOKEN_LISTENER ); }
"local"							{ BEGIN( VARIABLES ); yylval.s.val = node1_( method_local ); YYLEX( TOKEN_LISTENER ); }
"parm"							{ BEGIN( VARIABLES ); yylval.s.val = node1_( method_parm ); YYLEX( TOKEN_LISTENER ); }
"owner"							{ BEGIN( VARIABLES ); yylval.s.val = node1_( method_owner ); YYLEX( TOKEN_LISTENER ); }
"self"							{ BEGIN( VARIABLES ); yylval.s.val = node1_( method_self ); YYLEX( TOKEN_LISTENER ); }

"{"								{ parsedata.braces_count++; YYLEX( TOKEN_LEFT_BRACES ); }
"}"								{ parsedata.braces_count--; YYLEX( TOKEN_RIGHT_BRACES ); }
"("								{ YYLEX(TOKEN_LEFT_BRACKET); }
")"								{ BEGIN(VARIABLES); YYLEX(TOKEN_RIGHT_BRACKET); }
"["								{ YYLEX(TOKEN_LEFT_SQUARE_BRACKET); }
"]"								{ BEGIN(VARIABLES); YYLEX(TOKEN_RIGHT_SQUARE_BRACKET); }

"="								{ YYLEX( TOKEN_ASSIGNMENT ); }
":"								{ YYLEX( TOKEN_COLON ); }
"::"							{ YYLEX( TOKEN_DOUBLE_COLON ); }
";"								{ YYLEX( TOKEN_SEMICOLON ); }

"=="							{ YYLEX( TOKEN_EQUALITY ); }
"||"							{ YYLEX( TOKEN_LOGICAL_OR ); }
"&&"							{ YYLEX( TOKEN_LOGICAL_AND ); }

"|"								{ YYLEX( TOKEN_BITWISE_OR ); }
"^"								{ YYLEX( TOKEN_BITWISE_EXCL_OR ); }
"&"								{ YYLEX( TOKEN_BITWISE_AND ); }
"!="							{ YYLEX( TOKEN_INEQUALITY ); }
"<"								{ YYLEX( TOKEN_LESS_THAN ); }
">"								{ YYLEX( TOKEN_GREATER_THAN ); }
"<="							{ YYLEX( TOKEN_LESS_THAN_OR_EQUAL ); }
">="							{ YYLEX( TOKEN_GREATER_THAN_OR_EQUAL ); }
[ \t]"-"						{ YYLEX( TOKEN_NEG ); }

"+"								{ YYLEX( TOKEN_PLUS ); }
"+="							{ YYLEX( TOKEN_PLUS_EQUALS ); }
"++"|[ \t]"++"					{ YYLEX( TOKEN_INCREMENT ); }
"-"|"-"[ \t]|[ \t]"-"[ \t]		{ YYLEX( TOKEN_MINUS ); }
"-="							{ YYLEX( TOKEN_MINUS_EQUALS ); }
[ \t]"-="						{ YYLEX( TOKEN_MINUS_EQUALS ); }
"--"|[ \t]"--"					{ YYLEX( TOKEN_DECREMENT ); }
"*"								{ YYLEX( TOKEN_MULTIPLY ); }
"*="							{ YYLEX( TOKEN_MULTIPLY_EQUALS ); }
"/"								{ YYLEX( TOKEN_DIVIDE ); }
"/="							{ YYLEX( TOKEN_DIVIDE_EQUALS ); }
"%"								{ YYLEX( TOKEN_MODULUS ); }
"%="							{ YYLEX( TOKEN_MODULUS_EQUALS ); }
"<<"							{ YYLEX( TOKEN_SHIFT_LEFT ); }
"<<="							{ YYLEX( TOKEN_SHIFT_LEFT_EQUALS ); }
">>"							{ YYLEX( TOKEN_SHIFT_RIGHT ); }
">>="							{ YYLEX( TOKEN_SHIFT_RIGHT_EQUALS ); }
"&="							{ YYLEX( TOKEN_AND_EQUALS ); }
"^="							{ YYLEX( TOKEN_EXCL_OR_EQUALS ); }
"|="							{ YYLEX( TOKEN_OR_EQUALS ); }
[$]+							{ BEGIN( VARIABLES ); YYLEX( TOKEN_DOLLAR ); }
"!"								{ YYLEX( TOKEN_NOT ); }
"~"								{ YYLEX( TOKEN_COMPLEMENT ); }

"."								{ YYLEX( TOKEN_PERIOD ); }

","								{ YYLEX( TOKEN_COMMA ); }

"NULL"							{ YYLEX( TOKEN_NULL ); }
"NIL"							{ YYLEX( TOKEN_NIL ); }

"try"							{ YYLEX( TOKEN_TRY ); }
"catch"							{ YYLEX( TOKEN_CATCH ); }
"switch"						{ YYLEX( TOKEN_SWITCH ); }

"case"							{ YYLEX( TOKEN_CASE ); }
"break"							{ YYLEX( TOKEN_BREAK ); }
"continue"						{ YYLEX( TOKEN_CONTINUE ); }

"makearray"|"makeArray"			{ YYLEX( TOKEN_MAKEARRAY ); }
"endarray"|"endArray"			{ YYLEX( TOKEN_ENDARRAY ); }

[\r\n]+							{ if (prev_yylex != TOKEN_EOL) YYLEX(TOKEN_EOL); }
[ \t]							{ ; }

[0-9]+									{
											char* p = nullptr;
											yylval.s.val.intValue = std::strtol(yytext, &p, 10);
											YYLEX(TOKEN_INTEGER);
										}

[0-9\.]+{nonnumeric}					{
											BEGIN(IDENTIFIER);
											yymore();
										}

[0-9\.]+|[0-9\.]+("e+"|"e-")+[0-9\.]	{
											char* p = nullptr;
											yylval.s.val.floatValue = std::strtof(yytext, &p);
											YYLEX(TOKEN_FLOAT);
										}

<IDENTIFIER>{identifier}*		{
									BEGIN(INITIAL);
									TextEscapeValue(yytext, yyleng);
									YYLEX(TOKEN_IDENTIFIER);
								}
<IDENTIFIER>[ \t\r\n]			{
									BEGIN(INITIAL);
									unput(yytext[yyleng - 1]);
									yyreducepos(1);
									TextEscapeValue(yytext, yyleng - 1);
									YYLEXOFF(TOKEN_IDENTIFIER, 1);
								}
<IDENTIFIER>.					{
									BEGIN(INITIAL);
									unput(yytext[yyleng - 1]);
									yyreducepos(1);
									TextEscapeValue(yytext, yyleng - 1);
									YYLEXOFF(TOKEN_IDENTIFIER, 1);
								}

{identifier}					{
									BEGIN(IDENTIFIER);
									yymore();
								}

[a-zA-Z0-9_]+					{
									BEGIN(IDENTIFIER);
									yymore();
								}

<SCRIPT>[a-zA-Z0-9]+			{ BEGIN(INITIAL); }

.								{ YY_FATAL_ERROR("bad token:\n"); }

%{

#undef fprintf

%}

%%

//
// Implements yywrap to always append a newline to the source
//
int yywrap(void) {
	if (parseStage == PS_TYPE) {
		parseStage = PS_BODY;
		in_ptr = start_ptr;
		out_pos = 0;
		success_pos = 0;
		return 0;
	}

	if (parseStage == PS_BODY)
	{
		if (YY_START == C_COMMENT)
		{
			Compiler.CompileError(success_pos, "unexpected end of file found in comment");
			return 1;
		}
		
		parseStage = PS_BODY_END;
		in_ptr = "\n";
		return 0;
	}

	return 1;
}

void yy_init_script() {
	BEGIN(SCRIPT);
}
