/*
   AngelCode Scripting Library
   Copyright (c) 2003-2024 Andreas Jonsson

   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any
   damages arising from the use of this software.

   Permission is granted to anyone to use this software for any
   purpose, including commercial applications, and to alter it and
   redistribute it freely, subject to the following restrictions:

   1. The origin of this software must not be misrepresented; you
      must not claim that you wrote the original software. If you use
      this software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and
      must not be misrepresented as being the original software.

   3. This notice may not be removed or altered from any source
      distribution.

   The original version of this library can be located at:
   http://www.angelcode.com/angelscript/

   Andreas Jonsson
   andreas@angelcode.com
*/


//
// as_parser.cpp
//
// This class parses the script code and builds a tree for compilation
//
//
// I've documented the syntax in Extended BNF. You'll find it by doing a search in
// this file by "BNF:". The starting point for the script language is SCRIPT ::=.
//
// Ref: http://matt.might.net/articles/grammars-bnf-ebnf/
//
// ( )  - used for grouping
// { }  - 0 or more repetitions
// [ ]  - optional
//  |   - or
// ' '  - token
//



#include "as_config.h"
#include "as_parser.h"
#include "as_tokendef.h"
#include "as_texts.h"
#include "as_debug.h"

#ifdef _MSC_VER
#pragma warning(disable:4702) // unreachable code
#endif

BEGIN_AS_NAMESPACE

asCParser::asCParser(asCBuilder *builder)
{
	this->builder    = builder;
	this->engine     = builder->engine;

	script                = 0;
	scriptNode            = 0;
	checkValidTypes       = false;
	isParsingAppInterface = false;
}

asCParser::~asCParser()
{
	Reset();
}

void asCParser::Reset()
{
	errorWhileParsing     = false;
	isSyntaxError         = false;
	checkValidTypes       = false;
	isParsingAppInterface = false;

	sourcePos = 0;

	if( scriptNode )
	{
		scriptNode->Destroy(engine);
	}

	scriptNode = 0;

	script = 0;

	lastToken.pos = size_t(-1);
}

asCScriptNode *asCParser::GetScriptNode()
{
	return scriptNode;
}

int asCParser::ParseFunctionDefinition(asCScriptCode *in_script, bool in_expectListPattern)
{
	Reset();

	// Set flag that permits ? as datatype for parameters
	isParsingAppInterface = true;

	this->script = in_script;

	scriptNode = ParseFunctionDefinition();

	if( in_expectListPattern )
		scriptNode->AddChildLast(ParseListPattern());

	// The declaration should end after the definition
	if( !isSyntaxError )
	{
		sToken t;
		GetToken(&t);
		if( t.type != ttEnd )
		{
			Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnd)), &t);
			Error(InsteadFound(t), &t);
			return -1;
		}
	}

	if( errorWhileParsing )
		return -1;

	return 0;
}

asCScriptNode *asCParser::CreateNode(eScriptNode type)
{
	void *ptr = engine->memoryMgr.AllocScriptNode();
	if( ptr == 0 )
	{
		// Out of memory
		errorWhileParsing = true;
		return 0;
	}

	return new(ptr) asCScriptNode(type);
}

int asCParser::ParseDataType(asCScriptCode *in_script, bool in_isReturnType)
{
	Reset();

	this->script = in_script;

	scriptNode = CreateNode(snDataType);
	if( scriptNode == 0 ) return -1;

	scriptNode->AddChildLast(ParseType(true));
	if( isSyntaxError ) return -1;

	if( in_isReturnType )
	{
		scriptNode->AddChildLast(ParseTypeMod(false));
		if( isSyntaxError ) return -1;
	}

	// The declaration should end after the type
	sToken t;
	GetToken(&t);
	if( t.type != ttEnd )
	{
		Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnd)), &t);
		Error(InsteadFound(t), &t);
		return -1;
	}

	if( errorWhileParsing )
		return -1;

	return 0;
}


// Parse a template declaration: IDENTIFIER '<' 'class'? IDENTIFIER '>'
int asCParser::ParseTemplateDecl(asCScriptCode *in_script)
{
	Reset();

	this->script = in_script;
	scriptNode = CreateNode(snUndefined);
	if( scriptNode == 0 ) return -1;

	scriptNode->AddChildLast(ParseIdentifier());
	if( isSyntaxError ) return -1;

	sToken t;
	GetToken(&t);
	if( t.type != ttLessThan )
	{
		Error(ExpectedToken(asCTokenizer::GetDefinition(ttLessThan)), &t);
		Error(InsteadFound(t), &t);
		return -1;
	}

	// The class token is optional
	GetToken(&t);
	if( t.type != ttClass )
		RewindTo(&t);

	scriptNode->AddChildLast(ParseIdentifier());
	if( isSyntaxError ) return -1;

	// There can be multiple sub types
	GetToken(&t);

	// Parse template types by list separator
	while(t.type == ttListSeparator)
	{
		GetToken(&t);
		if( t.type != ttClass )
			RewindTo(&t);
		scriptNode->AddChildLast(ParseIdentifier());

		if( isSyntaxError ) return -1;
		GetToken(&t);
	}

	if( t.type != ttGreaterThan )
	{
		Error(ExpectedToken(asCTokenizer::GetDefinition(ttGreaterThan)), &t);
		Error(InsteadFound(t), &t);
		return -1;
	}

	GetToken(&t);
	if( t.type != ttEnd )
	{
		Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnd)), &t);
		Error(InsteadFound(t), &t);
		return -1;
	}

	if( errorWhileParsing )
		return -1;

	return 0;
}

int asCParser::ParsePropertyDeclaration(asCScriptCode *in_script)
{
	Reset();

	this->script = in_script;

	scriptNode = CreateNode(snDeclaration);
	if( scriptNode == 0 ) return -1;

	scriptNode->AddChildLast(ParseType(true));
	if( isSyntaxError ) return -1;

	// Allow optional '&' to indicate that the property is indirect, i.e. stored as reference
	sToken t;
	GetToken(&t);
	RewindTo(&t);
	if( t.type == ttAmp )
		scriptNode->AddChildLast(ParseToken(ttAmp));

	// Allow optional namespace to be defined before the identifier in case
	// the declaration is to be used for searching for an existing property
	ParseOptionalScope(scriptNode);

	scriptNode->AddChildLast(ParseIdentifier());
	if( isSyntaxError ) return -1;

	// The declaration should end after the identifier
	GetToken(&t);
	if( t.type != ttEnd )
	{
		Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnd)), &t);
		Error(InsteadFound(t), &t);
		return -1;
	}

	return 0;
}

// BNF:5: SCOPE         ::= ['::'] {IDENTIFIER '::'} [IDENTIFIER ['<' TYPE {',' TYPE} '>'] '::']
void asCParser::ParseOptionalScope(asCScriptNode *node)
{
	asCScriptNode *scope = CreateNode(snScope);

	sToken t1, t2;
	GetToken(&t1);
	GetToken(&t2);
	if( t1.type == ttScope )
	{
		RewindTo(&t1);
		scope->AddChildLast(ParseToken(ttScope));
		GetToken(&t1);
		GetToken(&t2);
	}
	while( t1.type == ttIdentifier && t2.type == ttScope )
	{
		RewindTo(&t1);
		scope->AddChildLast(ParseIdentifier());
		scope->AddChildLast(ParseToken(ttScope));
		GetToken(&t1);
		GetToken(&t2);
	}

	// The innermost scope may be a template type
	if( t1.type == ttIdentifier && t2.type == ttLessThan )
	{
		tempString.Assign(&script->code[t1.pos], t1.length);
		if (engine->IsTemplateType(tempString.AddressOf()))
		{
			RewindTo(&t1);
			asCScriptNode *restore = scope->lastChild;
			scope->AddChildLast(ParseIdentifier());
			if (ParseTemplTypeList(scope, false))
			{
				GetToken(&t2);
				if (t2.type == ttScope)
				{
					// Template type is part of the scope
					// Nothing more needs to be done
					node->AddChildLast(scope);
					return;
				}
				else
				{
					// The template type is not part of the scope
					// Rewind to the template type and end the scope
					RewindTo(&t1);

					// Restore the previously parsed node
					while (scope->lastChild != restore)
					{
						asCScriptNode *last = scope->lastChild;
						last->DisconnectParent();
						last->Destroy(engine);
					}
					if( scope->lastChild )
						node->AddChildLast(scope);
					else
						scope->Destroy(engine);
					return;
				}
			}
		}
	}

	// The identifier is not part of the scope
	RewindTo(&t1);

	if (scope->lastChild)
		node->AddChildLast(scope);
	else
		scope->Destroy(engine);
}

asCScriptNode *asCParser::ParseFunctionDefinition()
{
	asCScriptNode *node = CreateNode(snFunction);
	if( node == 0 ) return 0;

	node->AddChildLast(ParseType(true));
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseTypeMod(false));
	if( isSyntaxError ) return node;

	ParseOptionalScope(node);

	node->AddChildLast(ParseIdentifier());
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseParameterList());
	if( isSyntaxError ) return node;

	// Parse an optional 'const' after the function definition (used for object methods)
	sToken t1;
	GetToken(&t1);
	RewindTo(&t1);
	if( t1.type == ttConst )
		node->AddChildLast(ParseToken(ttConst));

	// Parse optional attributes
	ParseMethodAttributes(node);

	return node;
}

// BNF:4: TYPEMOD       ::= ['&' ['in' | 'out' | 'inout']]
asCScriptNode *asCParser::ParseTypeMod(bool isParam)
{
	asCScriptNode *node = CreateNode(snDataType);
	if( node == 0 ) return 0;

	sToken t;

	// Parse possible & token
	GetToken(&t);
	RewindTo(&t);
	if( t.type == ttAmp )
	{
		node->AddChildLast(ParseToken(ttAmp));
		if( isSyntaxError ) return node;

		if( isParam )
		{
			GetToken(&t);
			RewindTo(&t);

			if( t.type == ttIn || t.type == ttOut || t.type == ttInOut )
			{
				int tokens[3] = {ttIn, ttOut, ttInOut};
				node->AddChildLast(ParseOneOf(tokens, 3));
			}
		}
	}

	// Parse possible + token
	GetToken(&t);
	RewindTo(&t);
	if( t.type == ttPlus )
	{
		node->AddChildLast(ParseToken(ttPlus));
		if( isSyntaxError ) return node;
	}

	// Parse possible if_handle_then_const token
	GetToken(&t);
	RewindTo(&t);
	if (IdentifierIs(t, IF_HANDLE_TOKEN))
	{
		node->AddChildLast(ParseToken(ttIdentifier));
		if (isSyntaxError) return node;
	}

	return node;
}

// BNF:4: TYPE          ::= ['const'] SCOPE DATATYPE ['<' TYPE {',' TYPE} '>'] { ('[' ']') | ('@' ['const']) }
asCScriptNode *asCParser::ParseType(bool allowConst, bool allowVariableType, bool allowAuto)
{
	asCScriptNode *node = CreateNode(snDataType);
	if( node == 0 ) return 0;

	sToken t;

	if( allowConst )
	{
		GetToken(&t);
		RewindTo(&t);
		if( t.type == ttConst )
		{
			node->AddChildLast(ParseToken(ttConst));
			if( isSyntaxError ) return node;
		}
	}

	// Parse scope prefix
	ParseOptionalScope(node);

	// Parse the actual type
	node->AddChildLast(ParseDataType(allowVariableType, allowAuto));
	if( isSyntaxError ) return node;

	// If the datatype is a template type, then parse the subtype within the < >
	GetToken(&t);
	RewindTo(&t);
	asCScriptNode *type = node->lastChild;
	tempString.Assign(&script->code[type->tokenPos], type->tokenLength);
	if( engine->IsTemplateType(tempString.AddressOf()) && t.type == ttLessThan )
	{
		ParseTemplTypeList(node);
		if (isSyntaxError) return node;
	}

	// Parse [] and @
	GetToken(&t);
	RewindTo(&t);
	while( t.type == ttOpenBracket || t.type == ttHandle)
	{
		if( t.type == ttOpenBracket )
		{
			node->AddChildLast(ParseToken(ttOpenBracket));
			if( isSyntaxError ) return node;

			GetToken(&t);
			if( t.type != ttCloseBracket )
			{
				Error(ExpectedToken("]"), &t);
				Error(InsteadFound(t), &t);
				return node;
			}
		}
		else
		{
			node->AddChildLast(ParseToken(ttHandle));
			if( isSyntaxError ) return node;

			GetToken(&t);
			RewindTo(&t);
			if( t.type == ttConst )
			{
				node->AddChildLast(ParseToken(ttConst));
				if( isSyntaxError ) return node;
			}
		}

		GetToken(&t);
		RewindTo(&t);
	}

	return node;
}

// This parses a template type list, e.g. <type, type, type>
// If 'required' is false, and the template type list is not valid,
// then no change will be done and the function returns false. This
// can be used as do an optional parsing
bool asCParser::ParseTemplTypeList(asCScriptNode *node, bool required)
{
	sToken t;
	bool isValid = true;

	// Remember the last child, so we can restore the state if needed
	asCScriptNode *last = node->lastChild;

	// Starts with '<'
	GetToken(&t);
	if (t.type != ttLessThan)
	{
		if (required)
		{
			Error(ExpectedToken(asCTokenizer::GetDefinition(ttLessThan)), &t);
			Error(InsteadFound(t), &t);
		}
		return false;
	}

	// At least one type
	// TODO: child funcdef: Make this work with !required
	node->AddChildLast(ParseType(true, false));
	if (isSyntaxError) return false;

	GetToken(&t);

	// Parse template types by list separator
	while (t.type == ttListSeparator)
	{
		// TODO: child funcdef: Make this work with !required
		node->AddChildLast(ParseType(true, false));
		if (isSyntaxError) return false;
		GetToken(&t);
	}

	// End with '>'
	// Accept >> and >>> tokens too. But then force the tokenizer to move
	// only 1 character ahead (thus splitting the token in two).
	if (script->code[t.pos] != '>')
	{
		if (required)
		{
			Error(ExpectedToken(asCTokenizer::GetDefinition(ttGreaterThan)), &t);
			Error(InsteadFound(t), &t);
		}
		else
			isValid = false;
	}
	else
	{
		// Break the token so that only the first > is parsed
		SetPos(t.pos + 1);
	}

	if (!required && !isValid)
	{
		// Restore the original state before returning
		while (node->lastChild != last)
		{
			asCScriptNode *n = node->lastChild;
			n->DisconnectParent();
			n->Destroy(engine);
		}

		return false;
	}

	// The template type list was parsed OK
	return true;
}

asCScriptNode *asCParser::ParseToken(int token)
{
	asCScriptNode *node = CreateNode(snUndefined);
	if( node == 0 ) return 0;

	sToken t1;

	GetToken(&t1);
	if( t1.type != token )
	{
		Error(ExpectedToken(asCTokenizer::GetDefinition(token)), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	node->SetToken(&t1);
	node->UpdateSourcePos(t1.pos, t1.length);

	return node;
}

asCScriptNode *asCParser::ParseOneOf(int *tokens, int count)
{
	asCScriptNode *node = CreateNode(snUndefined);
	if( node == 0 ) return 0;

	sToken t1;

	GetToken(&t1);
	int n;
	for( n = 0; n < count; n++ )
	{
		if( tokens[n] == t1.type )
			break;
	}
	if( n == count )
	{
		Error(ExpectedOneOf(tokens, count), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	node->SetToken(&t1);
	node->UpdateSourcePos(t1.pos, t1.length);

	return node;
}

// BNF:5: DATATYPE      ::= (IDENTIFIER | PRIMTYPE | '?' | 'auto')
asCScriptNode *asCParser::ParseDataType(bool allowVariableType, bool allowAuto)
{
	asCScriptNode *node = CreateNode(snDataType);
	if( node == 0 ) return 0;

	sToken t1;

	GetToken(&t1);
	if( !IsDataType(t1) && !(allowVariableType && t1.type == ttQuestion) && !(allowAuto && t1.type == ttAuto) )
	{
		if( t1.type == ttIdentifier )
		{
			asCString errMsg;
			tempString.Assign(&script->code[t1.pos], t1.length);
			errMsg.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE, tempString.AddressOf());
			Error(errMsg, &t1);
		}
		else if( t1.type == ttAuto )
		{
			Error(TXT_AUTO_NOT_ALLOWED, &t1);
		}
		else
		{
			Error(TXT_EXPECTED_DATA_TYPE, &t1);
			Error(InsteadFound(t1), &t1);
		}
		return node;
	}

	node->SetToken(&t1);
	node->UpdateSourcePos(t1.pos, t1.length);

	return node;
}

// BNF:6: PRIMTYPE      ::= 'void' | 'int' | 'int8' | 'int16' | 'int32' | 'int64' | 'uint' | 'uint8' | 'uint16' | 'uint32' | 'uint64' | 'float' | 'double' | 'bool'
asCScriptNode *asCParser::ParseRealType()
{
	asCScriptNode *node = CreateNode(snDataType);
	if( node == 0 ) return 0;

	sToken t1;

	GetToken(&t1);
	if( !IsRealType(t1.type) )
	{
		Error(TXT_EXPECTED_DATA_TYPE, &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	node->SetToken(&t1);
	node->UpdateSourcePos(t1.pos, t1.length);

	return node;
}

// BNF:17: IDENTIFIER    ::= single token:  starts with letter or _, can include any letter and digit, same as in C++
asCScriptNode *asCParser::ParseIdentifier()
{
	asCScriptNode *node = CreateNode(snIdentifier);
	if( node == 0 ) return 0;

	sToken t1;

	GetToken(&t1);
	if( t1.type != ttIdentifier )
	{
		Error(TXT_EXPECTED_IDENTIFIER, &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	node->SetToken(&t1);
	node->UpdateSourcePos(t1.pos, t1.length);

	return node;
}

// BNF:3: PARAMLIST     ::= '(' ['void' | (TYPE TYPEMOD [IDENTIFIER] ['=' EXPR] {',' TYPE TYPEMOD [IDENTIFIER] ['=' EXPR]})] ')'
asCScriptNode *asCParser::ParseParameterList()
{
	asCScriptNode *node = CreateNode(snParameterList);
	if( node == 0 ) return 0;

	sToken t1;
	GetToken(&t1);
	if( t1.type != ttOpenParenthesis)
	{
		Error(ExpectedToken("("), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	node->UpdateSourcePos(t1.pos, t1.length);

	GetToken(&t1);
	if( t1.type == ttCloseParenthesis )
	{
		node->UpdateSourcePos(t1.pos, t1.length);

		// Statement block is finished
		return node;
	}
	else
	{
		// If the parameter list is just (void) then the void token should be ignored
		if( t1.type == ttVoid )
		{
			sToken t2;
			GetToken(&t2);
			if( t2.type == ttCloseParenthesis)
			{
				node->UpdateSourcePos(t2.pos, t2.length);
				return node;
			}
		}

		RewindTo(&t1);

		for(;;)
		{
			// Parse data type
			node->AddChildLast(ParseType(true, isParsingAppInterface));
			if( isSyntaxError ) return node;

			node->AddChildLast(ParseTypeMod(true));
			if( isSyntaxError ) return node;

			// Parse optional identifier
			GetToken(&t1);
			if( t1.type == ttIdentifier )
			{
				RewindTo(&t1);

				node->AddChildLast(ParseIdentifier());
				if( isSyntaxError ) return node;

				GetToken(&t1);
			}

			// Parse optional expression for the default arg
			if( t1.type == ttAssignment )
			{
				// Do a superficial parsing of the default argument
				// The actual parsing will be done when the argument is compiled for a function call
				node->AddChildLast(SuperficiallyParseExpression());
				if( isSyntaxError ) return node;

				GetToken(&t1);
			}

			// Check if list continues
			if( t1.type == ttCloseParenthesis)
			{
				node->UpdateSourcePos(t1.pos, t1.length);

				return node;
			}
			else if( t1.type == ttListSeparator )
				continue;
			else
			{
				Error(ExpectedTokens(")", ","), &t1);
				Error(InsteadFound(t1), &t1);
				return node;
			}
		}
	}
	UNREACHABLE_RETURN;
}

asCScriptNode *asCParser::SuperficiallyParseExpression()
{
	asCScriptNode *node = CreateNode(snExpression);
	if( node == 0 ) return 0;

	// Simply parse everything until the first , or ), whichever comes first.
	// Keeping in mind that () and {} can group expressions.

	sToken start;
	GetToken(&start);
	RewindTo(&start);

	asCString stack;
	sToken t;
	for(;;)
	{
		GetToken(&t);

		if( t.type == ttOpenParenthesis)
			stack += "(";
		else if( t.type == ttCloseParenthesis)
		{
			if( stack == "" )
			{
				// Expression has ended. This token is not part of expression
				RewindTo(&t);
				break;
			}
			else if( stack[stack.GetLength()-1] == '(' )
			{
				// Group has ended
				stack.SetLength(stack.GetLength()-1);
			}
			else
			{
				// Wrong syntax
				RewindTo(&t);
				asCString str;
				str.Format(TXT_UNEXPECTED_TOKEN_s, ")");
				Error(str, &t);
				return node;
			}
		}
		else if( t.type == ttListSeparator )
		{
			if( stack == "" )
			{
				// Expression has ended. This token is not part of expression
				RewindTo(&t);
				break;
			}
		}
		else if( t.type == ttStartStatementBlock )
			stack += "{";
		else if( t.type == ttEndStatementBlock )
		{
			if( stack == "" || stack[stack.GetLength()-1] != '{' )
			{
				// Wrong syntax
				RewindTo(&t);
				asCString str;
				str.Format(TXT_UNEXPECTED_TOKEN_s, "}");
				Error(str, &t);
				return node;
			}
			else
			{
				// Group has ended
				stack.SetLength(stack.GetLength()-1);
			}
		}
		else if( t.type == ttEndStatement )
		{
			// Wrong syntax (since we're parsing a default arg expression)
			RewindTo(&t);
			asCString str;
			str.Format(TXT_UNEXPECTED_TOKEN_s, ";");
			Error(str, &t);
			return node;
		}
		else if( t.type == ttNonTerminatedStringConstant )
		{
			RewindTo(&t);
			Error(TXT_NONTERMINATED_STRING, &t);
			return node;
		}
		else if( t.type == ttEnd )
		{
			// Wrong syntax
			RewindTo(&t);
			Error(TXT_UNEXPECTED_END_OF_FILE, &t);
			Info(TXT_WHILE_PARSING_EXPRESSION, &start);
			return node;
		}

		// Include the token in the node
		node->UpdateSourcePos(t.pos, t.length);
	}

	return node;
}

// BNF:18: COMMENT       ::= single token:  starts with // and ends with new line or starts with /* and ends with */
// BNF:18: WHITESPACE    ::= single token:  spaces, tab, carriage return, line feed, and UTF8 byte-order-mark
void asCParser::GetToken(sToken *token)
{
	// Check if the token has already been parsed
	if( lastToken.pos == sourcePos )
	{
		*token = lastToken;
		sourcePos += token->length;

		if( token->type == ttWhiteSpace ||
			token->type == ttOnelineComment ||
			token->type == ttMultilineComment )
			GetToken(token);

		return;
	}

	// Parse new token
	size_t sourceLength = script->codeLength;
	do
	{
		if( sourcePos >= sourceLength )
		{
			token->type = ttEnd;
			token->length = 0;
		}
		else
			token->type = engine->tok.GetToken(&script->code[sourcePos], sourceLength - sourcePos, &token->length);

		token->pos = sourcePos;

		// Update state
		sourcePos += token->length;
	}
	// Filter out whitespace and comments
	while( token->type == ttWhiteSpace ||
	       token->type == ttOnelineComment ||
	       token->type == ttMultilineComment );
}

void asCParser::SetPos(size_t pos)
{
	lastToken.pos = size_t(-1);
	sourcePos = pos;
}

void asCParser::RewindTo(const sToken *token)
{
	// TODO: optimize: Perhaps we can optimize this further by having the parser
	//                 set an explicit return point, after which each token will
	//                 be stored. That way not just one token will be reused but
	//                 no token will have to be tokenized more than once.

	// Store the token so it doesn't have to be tokenized again
	lastToken = *token;

	sourcePos = token->pos;
}

void asCParser::Error(const asCString &text, sToken *token)
{
	RewindTo(token);

	isSyntaxError     = true;
	errorWhileParsing = true;

	int row, col;
	script->ConvertPosToRowCol(token->pos, &row, &col);

	if( builder )
		builder->WriteError(script->name, text, row, col);
}

void asCParser::Warning(const asCString &text, sToken *token)
{
	int row, col;
	script->ConvertPosToRowCol(token->pos, &row, &col);

	if( builder )
		builder->WriteWarning(script->name, text, row, col);
}

void asCParser::Info(const asCString &text, sToken *token)
{
	RewindTo(token);

	isSyntaxError     = true;
	errorWhileParsing = true;

	int row, col;
	script->ConvertPosToRowCol(token->pos, &row, &col);

	if( builder )
		builder->WriteInfo(script->name, text, row, col, false);
}

bool asCParser::IsRealType(int tokenType)
{
	if( tokenType == ttVoid ||
		tokenType == ttInt ||
		tokenType == ttInt8 ||
		tokenType == ttInt16 ||
		tokenType == ttInt64 ||
		tokenType == ttUInt ||
		tokenType == ttUInt8 ||
		tokenType == ttUInt16 ||
		tokenType == ttUInt64 ||
		tokenType == ttFloat ||
		tokenType == ttBool ||
		tokenType == ttDouble )
		return true;

	return false;
}

bool asCParser::IsDataType(const sToken &token)
{
	if( token.type == ttIdentifier )
	{
#ifndef AS_NO_COMPILER
		if( checkValidTypes )
		{
			// Check if this is an existing type, regardless of namespace
			tempString.Assign(&script->code[token.pos], token.length);
			if( !builder->DoesTypeExist(tempString.AddressOf()) )
				return false;
		}
#endif
		return true;
	}

	if( IsRealType(token.type) )
		return true;

	return false;
}

asCString asCParser::ExpectedToken(const char *token)
{
	asCString str;

	str.Format(TXT_EXPECTED_s, token);

	return str;
}

asCString asCParser::ExpectedTokens(const char *t1, const char *t2)
{
	asCString str;

	str.Format(TXT_EXPECTED_s_OR_s, t1, t2);

	return str;
}

asCString asCParser::ExpectedOneOf(int *tokens, int count)
{
	asCString str;

	str = TXT_EXPECTED_ONE_OF;
	for( int n = 0; n < count; n++ )
	{
		str += asCTokenizer::GetDefinition(tokens[n]);
		if( n < count-1 )
			str += ", ";
	}

	return str;
}

asCString asCParser::ExpectedOneOf(const char **tokens, int count)
{
	asCString str;

	str = TXT_EXPECTED_ONE_OF;
	for( int n = 0; n < count; n++ )
	{
		str += tokens[n];
		if( n < count-1 )
			str += ", ";
	}

	return str;
}

asCString asCParser::InsteadFound(sToken &t)
{
	asCString str;
	if( t.type == ttIdentifier )
	{
		asCString id(&script->code[t.pos], t.length);
		str.Format(TXT_INSTEAD_FOUND_IDENTIFIER_s, id.AddressOf());
	}
	else if( t.type >= ttIf )
		str.Format(TXT_INSTEAD_FOUND_KEYWORD_s, asCTokenizer::GetDefinition(t.type));
	else
		str.Format(TXT_INSTEAD_FOUND_s, asCTokenizer::GetDefinition(t.type));

	return str;
}

asCScriptNode *asCParser::ParseListPattern()
{
	asCScriptNode *node = CreateNode(snListPattern);
	if( node == 0 ) return 0;

	sToken t1;

	GetToken(&t1);
	if( t1.type != ttStartStatementBlock )
	{
		Error(ExpectedToken("{"), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	node->UpdateSourcePos(t1.pos, t1.length);

	sToken start = t1;

	bool isBeginning = true;
	bool afterType = false;
	while( !isSyntaxError )
	{
		GetToken(&t1);
		if( t1.type == ttEndStatementBlock )
		{
			if( !afterType )
			{
				Error(TXT_EXPECTED_DATA_TYPE, &t1);
				Error(InsteadFound(t1), &t1);
			}
			break;
		}
		else if( t1.type == ttStartStatementBlock )
		{
			if( afterType )
			{
				Error(ExpectedTokens(",","}"), &t1);
				Error(InsteadFound(t1), &t1);
			}
			RewindTo(&t1);
			node->AddChildLast(ParseListPattern());
			afterType = true;
		}
		else if( t1.type == ttIdentifier && (IdentifierIs(t1, "repeat") || IdentifierIs(t1, "repeat_same")) )
		{
			if( !isBeginning )
			{
				asCString msg;
				asCString token(&script->code[t1.pos], t1.length);
				msg.Format(TXT_UNEXPECTED_TOKEN_s, token.AddressOf());
				Error(msg.AddressOf(), &t1);
			}
			RewindTo(&t1);
			node->AddChildLast(ParseIdentifier());
		}
		else if( t1.type == ttEnd )
		{
			Error(TXT_UNEXPECTED_END_OF_FILE, &t1);
			Info(TXT_WHILE_PARSING_STATEMENT_BLOCK, &start);
			break;
		}
		else if( t1.type == ttListSeparator )
		{
			if( !afterType )
			{
				Error(TXT_EXPECTED_DATA_TYPE, &t1);
				Error(InsteadFound(t1), &t1);
			}
			afterType = false;
		}
		else
		{
			if( afterType )
			{
				Error(ExpectedTokens(",", "}"), &t1);
				Error(InsteadFound(t1), &t1);
			}
			RewindTo(&t1);
			node->AddChildLast(ParseType(true, true));
			afterType = true;
		}

		isBeginning = false;
	}

	node->UpdateSourcePos(t1.pos, t1.length);

	return node;
}

bool asCParser::IdentifierIs(const sToken &t, const char *str)
{
	if( t.type != ttIdentifier )
		return false;

	return script->TokenEquals(t.pos, t.length, str);
}

// BNF:6: FUNCATTR      ::= {'override' | 'final' | 'explicit' | 'property' | 'delete'}
void asCParser::ParseMethodAttributes(asCScriptNode *funcNode)
{
	sToken t1;

	for(;;)
	{
		GetToken(&t1);
		RewindTo(&t1);

		if( IdentifierIs(t1, FINAL_TOKEN) || 
			IdentifierIs(t1, OVERRIDE_TOKEN) || 
			IdentifierIs(t1, EXPLICIT_TOKEN) ||
			IdentifierIs(t1, PROPERTY_TOKEN) ||
			IdentifierIs(t1, DELETE_TOKEN) )
			funcNode->AddChildLast(ParseIdentifier());
		else
			break;
	}
}

#ifndef AS_NO_COMPILER

// nextToken is only modified if the current position can be interpreted as
// type, in this case it is set to the next token after the type tokens.
// Note, the function doesn't actually verify that an identifier is a declared type
bool asCParser::FindTokenAfterType(sToken &nextToken)
{
	// Set a rewind point
	sToken t, t1;
	GetToken(&t);

	// A type can start with a const
	t1 = t;
	if (t1.type == ttConst)
		GetToken(&t1);

	sToken t2;
	if (t1.type != ttAuto)
	{
		// The type may be initiated with the scope operator
		if (t1.type == ttScope)
			GetToken(&t1);

		// The type may be preceded with a multilevel scope
		GetToken(&t2);
		while (t1.type == ttIdentifier)
		{
			if (t2.type == ttScope)
			{
				GetToken(&t1);
				GetToken(&t2);
				continue;
			}
			else if (t2.type == ttLessThan)
			{
				// Template types can also be used as scope identifiers
				RewindTo(&t2);
				if (CheckTemplateType(t1))
				{
					sToken t3;
					GetToken(&t3);
					if (t3.type == ttScope)
					{
						GetToken(&t1);
						GetToken(&t2);
						continue;
					}
				}
			}

			break;
		}
		RewindTo(&t2);
	}

	// We don't validate if the identifier is an actual declared type at this moment
	// as it may wrongly identify the statement as a non-declaration if the user typed
	// the name incorrectly. The real type is validated in ParseDeclaration where a
	// proper error message can be given.
	if (!IsRealType(t1.type) && t1.type != ttIdentifier && t1.type != ttAuto)
	{
		RewindTo(&t);
		return false;
	}

	if (!CheckTemplateType(t1))
	{
		RewindTo(&t);
		return false;
	}

	// Object handles can be interleaved with the array brackets
	// Even though declaring variables with & is invalid we'll accept
	// it here to give an appropriate error message later
	GetToken(&t2);
	while (t2.type == ttHandle || t2.type == ttAmp || t2.type == ttOpenBracket)
	{
		if( t2.type == ttHandle )
		{
			// A handle can optionally be read-only
			sToken t3;
			GetToken(&t3);
			if(t3.type != ttConst )
				RewindTo(&t3);
		}
		else if (t2.type == ttOpenBracket)
		{
			GetToken(&t2);
			if (t2.type != ttCloseBracket)
			{
				RewindTo(&t);
				return false;
			}
		}
		else if (t2.type == ttAmp)
		{
			// & can be followed by in, out, or inout
			sToken t3;
			GetToken(&t3);
			if (t3.type != ttIn && t3.type != ttOut && t3.type != ttInOut)
				RewindTo(&t3);
		}

		GetToken(&t2);
	}

	// Return the next token so the caller can jump directly to it if desired
	nextToken = t2;

	// Rewind to start point
	RewindTo(&t);

	return true;
}

// This function will return true if the current token is not a template, or if it is and
// the following has a valid syntax for a template type. The source position will be left
// at the first token after the type in case of success
bool asCParser::CheckTemplateType(const sToken &t)
{
	// Is this a template type?
	tempString.Assign(&script->code[t.pos], t.length);
	if( engine->IsTemplateType(tempString.AddressOf()) )
	{
		// If the next token is a < then parse the sub-type too
		sToken t1;
		GetToken(&t1);
		if( t1.type != ttLessThan )
		{
			RewindTo(&t1);
			return true;
		}

		for(;;)
		{
			// There might optionally be a 'const'
			GetToken(&t1);
			if( t1.type == ttConst )
				GetToken(&t1);

			// The type may be initiated with the scope operator
			if( t1.type == ttScope )
				GetToken(&t1);

			// There may be multiple levels of scope operators
			sToken t2;
			GetToken(&t2);
			while( t1.type == ttIdentifier && t2.type == ttScope )
			{
				GetToken(&t1);
				GetToken(&t2);
			}
			RewindTo(&t2);

			// Now there must be a data type
			if( !IsDataType(t1) )
				return false;

			if( !CheckTemplateType(t1) )
				return false;

			GetToken(&t1);

			// Is it a handle or array?
			while( t1.type == ttHandle || t1.type == ttOpenBracket )
			{
				if( t1.type == ttOpenBracket )
				{
					GetToken(&t1);
					if( t1.type != ttCloseBracket )
						return false;
				}
				else if (t1.type == ttHandle)
				{
					// after @ there can be a const
					GetToken(&t1);
					if (t1.type != ttConst)
						RewindTo(&t1);
				}

				GetToken(&t1);
			}

			// Was this the last template subtype?
			if( t1.type != ttListSeparator )
				break;
		}

		// Accept >> and >>> tokens too. But then force the tokenizer to move
		// only 1 character ahead (thus splitting the token in two).
		if( script->code[t1.pos] != '>' )
			return false;
		else if( t1.length != 1 )
		{
			// We need to break the token, so that only the first character is parsed
			SetPos(t1.pos + 1);
		}
	}

	return true;
}

// BNF:12: CAST          ::= 'cast' '<' TYPE '>' '(' ASSIGN ')'
asCScriptNode *asCParser::ParseCast()
{
	asCScriptNode *node = CreateNode(snCast);
	if( node == 0 ) return 0;

	sToken t1;
	GetToken(&t1);
	if( t1.type != ttCast )
	{
		Error(ExpectedToken("cast"), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	node->UpdateSourcePos(t1.pos, t1.length);

	GetToken(&t1);
	if( t1.type != ttLessThan )
	{
		Error(ExpectedToken("<"), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	// Parse the data type
	node->AddChildLast(ParseType(true));
	if( isSyntaxError ) return node;

	GetToken(&t1);
	if( t1.type != ttGreaterThan )
	{
		Error(ExpectedToken(">"), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	GetToken(&t1);
	if( t1.type != ttOpenParenthesis)
	{
		Error(ExpectedToken("("), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	node->AddChildLast(ParseAssignment());
	if( isSyntaxError ) return node;

	GetToken(&t1);
	if( t1.type != ttCloseParenthesis)
	{
		Error(ExpectedToken(")"), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	node->UpdateSourcePos(t1.pos, t1.length);

	return node;
}

// BNF:11: EXPRVALUE     ::= 'void' | CONSTRUCTCALL | FUNCCALL | VARACCESS | CAST | LITERAL | '(' ASSIGN ')' | LAMBDA
asCScriptNode *asCParser::ParseExprValue()
{
	asCScriptNode *node = CreateNode(snExprValue);
	if( node == 0 ) return 0;

	sToken t1, t2;
	GetToken(&t1);
	GetToken(&t2);
	RewindTo(&t1);

	// 'void' is a special expression that doesn't do anything (normally used for skipping output arguments)
	if( t1.type == ttVoid )
		node->AddChildLast(ParseToken(ttVoid));
	else if( IsRealType(t1.type) )
		node->AddChildLast(ParseConstructCall());
	else if( t1.type == ttIdentifier || t1.type == ttScope )
	{
		// Check if the expression is an anonymous function
		if( IsLambda() )
		{
			node->AddChildLast(ParseLambda());
		}
		else
		{
			// Determine the last identifier in order to check if it is a type
			FindIdentifierAfterScope(t2);
			RewindTo(&t2);

			// Get The token after the identifier to determine if it is an [
			sToken t;
			GetToken(&t); 
			GetToken(&t);

			bool isDataType = IsDataType(t2);
			bool isTemplateType = false;
			if( isDataType )
			{
				// Is this a template type?
				tempString.Assign(&script->code[t2.pos], t2.length);
				if( engine->IsTemplateType(tempString.AddressOf()) )
					isTemplateType = true;
			}

			GetToken(&t2);

			// Rewind so the real parsing can be done, after deciding what to parse
			RewindTo(&t1);

			// Check if this is a construct call
			// Just 'type()' isn't considered a construct call, because type may just be a function/method name.
			// The compiler will have to sort this out, since the parser doesn't have enough information.
			if( isDataType && (t.type == ttOpenBracket && t2.type == ttCloseBracket) )      // type[]()
				node->AddChildLast(ParseConstructCall());
			else if( isTemplateType && t.type == ttLessThan )  // type<t>()
				node->AddChildLast(ParseConstructCall());
			else if( IsFunctionCall() )
				node->AddChildLast(ParseFunctionCall());
			else
				node->AddChildLast(ParseVariableAccess());
		}
	}
	else if( t1.type == ttCast )
		node->AddChildLast(ParseCast());
	else if( IsConstant(t1.type) )
		node->AddChildLast(ParseConstant());
	else if( t1.type == ttOpenParenthesis)
	{
		GetToken(&t1);
		node->UpdateSourcePos(t1.pos, t1.length);

		node->AddChildLast(ParseAssignment());
		if( isSyntaxError ) return node;

		GetToken(&t1);
		if( t1.type != ttCloseParenthesis)
		{
			Error(ExpectedToken(")"), &t1);
			Error(InsteadFound(t1), &t1);
		}

		node->UpdateSourcePos(t1.pos, t1.length);
	}
	else
	{
		Error(TXT_EXPECTED_EXPRESSION_VALUE, &t1);
		Error(InsteadFound(t1), &t1);
	}

	return node;
}

// BNF:12: LITERAL       ::= NUMBER | STRING | BITS | 'true' | 'false' | 'null'
// BNF:17: NUMBER        ::= single token:  includes integers and real numbers, same as C++ 
// BNF:17: STRING        ::= single token:  single quoted ', double quoted ", or heredoc multi-line string """
// BNF:17: BITS          ::= single token:  binary 0b or 0B, octal 0o or 0O, decimal 0d or 0D, hexadecimal 0x or 0X
asCScriptNode *asCParser::ParseConstant()
{
	asCScriptNode *node = CreateNode(snConstant);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( !IsConstant(t.type) )
	{
		Error(TXT_EXPECTED_CONSTANT, &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->SetToken(&t);
	node->UpdateSourcePos(t.pos, t.length);

	// We want to gather a list of string constants to concatenate as children
	if( t.type == ttStringConstant || t.type == ttMultilineStringConstant || t.type == ttHeredocStringConstant )
		RewindTo(&t);

	while( t.type == ttStringConstant || t.type == ttMultilineStringConstant || t.type == ttHeredocStringConstant )
	{
		node->AddChildLast(ParseStringConstant());

		GetToken(&t);
		RewindTo(&t);
	}

	return node;
}

bool asCParser::IsLambda()
{
	bool isLambda = false;
	sToken t;
	GetToken(&t);
	if( t.type == ttIdentifier && IdentifierIs(t, FUNCTION_TOKEN) )
	{
		sToken t2;
		GetToken(&t2);
		if( t2.type == ttOpenParenthesis)
		{
			// Skip until )
			while( t2.type != ttCloseParenthesis && t2.type != ttEnd )
				GetToken(&t2);

			// The next token must be a {
			GetToken(&t2);
			if( t2.type == ttStartStatementBlock )
				isLambda = true;
		}
	}

	RewindTo(&t);
	return isLambda;
}

// Finds the last identifier in a multiscope sequence.
// Doesn't move the cursor.
// Returns false if the sequence is not a scope, nor a identifier without scope.
bool asCParser::FindIdentifierAfterScope(sToken& identifierToken)
{
	sToken t1, t2, t3;

	// Determine the last identifier after scope in order to check if it is a type
	GetToken(&t1);
	GetToken(&t2);
	RewindTo(&t1);

	if (t1.type != ttScope && t2.type != ttScope)
	{
		if (t1.type == ttIdentifier)
		{
			identifierToken = t1;
			return true;
		}
		return false;
	}

	if (t1.type == ttScope) t3 = t2; else t3 = t1;
	RewindTo(&t3);
	GetToken(&t2);
	while (t3.type == ttIdentifier)
	{
		t2 = t3;
		GetToken(&t3);
		if (t3.type == ttScope)
			GetToken(&t3);
		else
			break;
	}
	RewindTo(&t1);
	identifierToken = t2;

	return true;
}

// BNF:12: LAMBDA        ::= 'function' '(' [[TYPE TYPEMOD] [IDENTIFIER] {',' [TYPE TYPEMOD] [IDENTIFIER]}] ')' STATBLOCK
asCScriptNode *asCParser::ParseLambda()
{
	asCScriptNode *node = CreateNode(snFunction);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);

	if( t.type != ttIdentifier || !IdentifierIs(t, FUNCTION_TOKEN) )
	{
		Error(ExpectedToken("function"), &t);
		return node;
	}

	GetToken(&t);
	if( t.type != ttOpenParenthesis)
	{
		Error(ExpectedToken("("), &t);
		return node;
	}

	GetToken(&t);
	while (t.type != ttCloseParenthesis && t.type != ttEnd )
	{
		RewindTo(&t);

		// Create node to represent the parameter, the datatype and identifier must be children of the node.
		// If no datatype or identifier is given then the node is still there to be counted as the arg
		asCScriptNode* param = CreateNode(snUndefined);
		node->AddChildLast(param);

		// Skip 'const' for the next check
		sToken t1, t2 = t;
		if (t.type == ttConst)
			GetToken(&t1);

		// Determine the last identifier after scope in order to check if it is a type
		FindIdentifierAfterScope(t2);
		RewindTo(&t);

		// Parse optional type before parameter name
		if (IsDataType(t2) && CheckTemplateType(t2) && FindTokenAfterType(t) && (t.type == ttIdentifier || t.type == ttListSeparator || t.type == ttCloseParenthesis))
		{
			param->AddChildLast(ParseType(true));
			if (isSyntaxError) return node;
			param->AddChildLast(ParseTypeMod(true));
			if (isSyntaxError) return node;
		}

		// Parse optional parameter name
		if (t.type == ttIdentifier)
		{
			param->AddChildLast(ParseIdentifier());
			if (isSyntaxError) return node;
		}

		GetToken(&t);
		if (t.type == ttListSeparator)
			GetToken(&t);
		else if (t.type != ttCloseParenthesis)
		{
			const char *tokens[] = { ",", ")" };
			Error(ExpectedOneOf(tokens, 2), &t);
			return node;
		}
	}

	if( t.type != ttCloseParenthesis)
	{
		Error(ExpectedToken(")"), &t);
		return node;
	}

	// We should just find the end of the statement block here. The statements
	// will be parsed on request by the compiler once it starts the compilation.
	node->AddChildLast(SuperficiallyParseStatementBlock());

	return node;
}

asCScriptNode *asCParser::ParseStringConstant()
{
	asCScriptNode *node = CreateNode(snConstant);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( t.type != ttStringConstant && t.type != ttMultilineStringConstant && t.type != ttHeredocStringConstant )
	{
		Error(TXT_EXPECTED_STRING, &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->SetToken(&t);
	node->UpdateSourcePos(t.pos, t.length);

	return node;
}

// BNF:12: FUNCCALL      ::= SCOPE IDENTIFIER ARGLIST
asCScriptNode *asCParser::ParseFunctionCall()
{
	asCScriptNode *node = CreateNode(snFunctionCall);
	if( node == 0 ) return 0;

	// Parse scope prefix
	ParseOptionalScope(node);

	// Parse the function name followed by the argument list
	node->AddChildLast(ParseIdentifier());
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseArgList());

	return node;
}

// BNF:12: VARACCESS     ::= SCOPE IDENTIFIER
asCScriptNode *asCParser::ParseVariableAccess()
{
	asCScriptNode *node = CreateNode(snVariableAccess);
	if( node == 0 ) return 0;

	// Parse scope prefix
	ParseOptionalScope(node);

	// Parse the variable name
	node->AddChildLast(ParseIdentifier());

	return node;
}

// BNF:11: CONSTRUCTCALL ::= TYPE ARGLIST
asCScriptNode *asCParser::ParseConstructCall()
{
	asCScriptNode *node = CreateNode(snConstructCall);
	if( node == 0 ) return 0;

	node->AddChildLast(ParseType(false));
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseArgList());

	return node;
}

// BNF:13: ARGLIST       ::= '(' [IDENTIFIER ':'] ASSIGN {',' [IDENTIFIER ':'] ASSIGN} ')'
asCScriptNode *asCParser::ParseArgList(bool withParenthesis)
{
	asCScriptNode *node = CreateNode(snArgList);
	if( node == 0 ) return 0;

	sToken t1;
	if( withParenthesis )
	{
		GetToken(&t1);
		if( t1.type != ttOpenParenthesis)
		{
			Error(ExpectedToken("("), &t1);
			Error(InsteadFound(t1), &t1);
			return node;
		}

		node->UpdateSourcePos(t1.pos, t1.length);
	}

	GetToken(&t1);
	if( t1.type == ttCloseParenthesis || t1.type == ttCloseBracket )
	{
		if( withParenthesis )
		{
			if( t1.type == ttCloseParenthesis)
				node->UpdateSourcePos(t1.pos, t1.length);
			else
			{
				asCString str;
				str.Format(TXT_UNEXPECTED_TOKEN_s, asCTokenizer::GetDefinition(ttCloseBracket));

				Error(str.AddressOf(), &t1);
			}
		}
		else
			RewindTo(&t1);

		// Argument list has ended
		return node;
	}
	else
	{
		RewindTo(&t1);

		for(;;)
		{
			// Determine if this is a named argument
			sToken tl, t2;
			GetToken(&tl);
			GetToken(&t2);
			RewindTo(&tl);

			// Named arguments uses the syntax: arg : expr
			// This avoids confusion when the argument has the same name as a local variable, i.e. var = expr
			// It also avoids conflict with expressions to that creates anonymous objects initialized with lists, i.e. type = {...}
			// The alternate syntax: arg = expr, is supported to provide backwards compatibility with 2.29.0
			// TODO: 3.0.0: Remove the alternate syntax
			if( tl.type == ttIdentifier && (t2.type == ttColon || (engine->ep.alterSyntaxNamedArgs && t2.type == ttAssignment)) )
			{
				asCScriptNode *named = CreateNode(snNamedArgument);
				if( named == 0 ) return 0;
				node->AddChildLast(named);

				named->AddChildLast(ParseIdentifier());
				GetToken(&t2);

				if( engine->ep.alterSyntaxNamedArgs == 1 && t2.type == ttAssignment )
					Warning(TXT_NAMED_ARGS_WITH_OLD_SYNTAX, &t2);

				named->AddChildLast(ParseAssignment());
			}
			else
				node->AddChildLast(ParseAssignment());

			if( isSyntaxError ) return node;

			// Check if list continues
			GetToken(&t1);
			if( t1.type == ttListSeparator )
				continue;
			else
			{
				if( withParenthesis )
				{
					if( t1.type == ttCloseParenthesis)
						node->UpdateSourcePos(t1.pos, t1.length);
					else
					{
						Error(ExpectedTokens(")", ","), &t1);
						Error(InsteadFound(t1), &t1);
					}
				}
				else
					RewindTo(&t1);

				return node;
			}
		}
	}
}

bool asCParser::IsFunctionCall()
{
	sToken s;
	sToken t1, t2;

	GetToken(&s);
	t1 = s;

	// A function call may be prefixed with scope resolution
	if( t1.type == ttScope )
		GetToken(&t1);
	GetToken(&t2);

	while( t1.type == ttIdentifier && t2.type == ttScope )
	{
		GetToken(&t1);
		GetToken(&t2);
	}

	// A function call starts with an identifier followed by an argument list
	// The parser doesn't have enough information about scope to determine if the
	// identifier is a datatype, so even if it happens to be the parser will
	// identify the expression as a function call rather than a construct call.
	// The compiler will sort this out later
	if( t1.type != ttIdentifier )
	{
		RewindTo(&s);
		return false;
	}

	if( t2.type == ttOpenParenthesis)
	{
		RewindTo(&s);
		return true;
	}

	RewindTo(&s);
	return false;
}

// BNF:13: ASSIGN        ::= CONDITION [ ASSIGNOP ASSIGN ]
asCScriptNode *asCParser::ParseAssignment()
{
	asCScriptNode *node = CreateNode(snAssignment);
	if( node == 0 ) return 0;

	node->AddChildLast(ParseCondition());
	if( isSyntaxError ) return node;

	sToken t;
	GetToken(&t);
	RewindTo(&t);

	if( IsAssignOperator(t.type) )
	{
		node->AddChildLast(ParseAssignOperator());
		if( isSyntaxError ) return node;

		node->AddChildLast(ParseAssignment());
		if( isSyntaxError ) return node;
	}

	return node;
}

// BNF:14: CONDITION     ::= EXPR ['?' ASSIGN ':' ASSIGN]
asCScriptNode *asCParser::ParseCondition()
{
	asCScriptNode *node = CreateNode(snCondition);
	if( node == 0 ) return 0;

	node->AddChildLast(ParseExpression());
	if( isSyntaxError ) return node;

	sToken t;
	GetToken(&t);
	if( t.type == ttQuestion )
	{
		node->AddChildLast(ParseAssignment());
		if( isSyntaxError ) return node;

		GetToken(&t);
		if( t.type != ttColon )
		{
			Error(ExpectedToken(":"), &t);
			Error(InsteadFound(t), &t);
			return node;
		}

		node->AddChildLast(ParseAssignment());
		if( isSyntaxError ) return node;
	}
	else
		RewindTo(&t);

	return node;
}

// BNF:9: EXPR          ::= EXPRTERM {EXPROP EXPRTERM}
asCScriptNode *asCParser::ParseExpression()
{
	asCScriptNode *node = CreateNode(snExpression);
	if( node == 0 ) return 0;

	node->AddChildLast(ParseExprTerm());
	if( isSyntaxError ) return node;

	for(;;)
	{
		sToken t;
		GetToken(&t);
		RewindTo(&t);

		if( !IsOperator(t.type) )
			return node;

		node->AddChildLast(ParseExprOperator());
		if( isSyntaxError ) return node;

		node->AddChildLast(ParseExprTerm());
		if( isSyntaxError ) return node;
	}
	UNREACHABLE_RETURN;
}

// BNF:10: EXPRTERM      ::= ([TYPE '='] INITLIST) | ({EXPRPREOP} EXPRVALUE {EXPRPOSTOP})
asCScriptNode *asCParser::ParseExprTerm()
{
	asCScriptNode *node = CreateNode(snExprTerm);
	if( node == 0 ) return 0;

	// Check if the expression term is an initialization of a temp object with init list, i.e. type = {...}
	sToken t;
	GetToken(&t);
	sToken t2 = t, t3;
	RewindTo(&t);
	if (IsDataType(t2) && CheckTemplateType(t2) && FindTokenAfterType(t2))
	{
		// Move to token after the type
		RewindTo(&t2);

		// The next token must be a = followed by a {
		GetToken(&t2);
		GetToken(&t3);
		if (t2.type == ttAssignment && t3.type == ttStartStatementBlock)
		{
			// It is an initialization, now parse it for real
			RewindTo(&t);
			node->AddChildLast(ParseType(false));
			GetToken(&t2);
			node->AddChildLast(ParseInitList());
			return node;
		}
	}
	// Or an anonymous init list, i.e. {...}
	else if( t.type == ttStartStatementBlock )
	{
		RewindTo(&t);
		node->AddChildLast(ParseInitList());
		return node;
	}

	// It wasn't an initialization, so it must be an ordinary expression term
	RewindTo(&t);

	for(;;)
	{
		GetToken(&t);
		RewindTo(&t);
		if( !IsPreOperator(t.type) )
			break;

		node->AddChildLast(ParseExprPreOp());
		if( isSyntaxError ) return node;
	}

	node->AddChildLast(ParseExprValue());
	if( isSyntaxError ) return node;


	for(;;)
	{
		GetToken(&t);
		RewindTo(&t);
		if( !IsPostOperator(t.type) )
			return node;

		node->AddChildLast(ParseExprPostOp());
		if( isSyntaxError ) return node;
	}
	UNREACHABLE_RETURN;
}

// BNF:11: EXPRPREOP     ::= '-' | '+' | '!' | '++' | '--' | '~' | '@'
asCScriptNode *asCParser::ParseExprPreOp()
{
	asCScriptNode *node = CreateNode(snExprPreOp);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( !IsPreOperator(t.type) )
	{
		Error(TXT_EXPECTED_PRE_OPERATOR, &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->SetToken(&t);
	node->UpdateSourcePos(t.pos, t.length);

	return node;
}

// BNF:11: EXPRPOSTOP    ::= ('.' (FUNCCALL | IDENTIFIER)) | ('[' [IDENTIFIER ':'] ASSIGN {',' [IDENTIFIER ':' ASSIGN} ']') | ARGLIST | '++' | '--'
asCScriptNode *asCParser::ParseExprPostOp()
{
	asCScriptNode *node = CreateNode(snExprPostOp);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( !IsPostOperator(t.type) )
	{
		Error(TXT_EXPECTED_POST_OPERATOR, &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->SetToken(&t);
	node->UpdateSourcePos(t.pos, t.length);

	if( t.type == ttDot )
	{
		sToken t1, t2;
		GetToken(&t1);
		GetToken(&t2);
		RewindTo(&t1);
		if( t2.type == ttOpenParenthesis)
			node->AddChildLast(ParseFunctionCall());
		else
			node->AddChildLast(ParseIdentifier());
	}
	else if( t.type == ttOpenBracket )
	{
		node->AddChildLast(ParseArgList(false));

		GetToken(&t);
		if( t.type != ttCloseBracket )
		{
			Error(ExpectedToken("]"), &t);
			Error(InsteadFound(t), &t);
			return node;
		}

		node->UpdateSourcePos(t.pos, t.length);
	}
	else if( t.type == ttOpenParenthesis)
	{
		RewindTo(&t);
		node->AddChildLast(ParseArgList());
	}

	return node;
}

// BNF:15: EXPROP        ::= MATHOP | COMPOP | LOGICOP | BITOP
// BNF:16: MATHOP        ::= '+' | '-' | '*' | '/' | '\%' | '**'
// BNF:16: COMPOP        ::= '==' | '!=' | '<' | '<=' | '>' | '>=' | 'is' | '!is'
// BNF:16: LOGICOP       ::= '&&' | '||' | '^^' | 'and' | 'or' | 'xor'
// BNF:16: BITOP         ::= '&' | '|' | '^' | '<<' | '>>' | '>>>'
asCScriptNode *asCParser::ParseExprOperator()
{
	asCScriptNode *node = CreateNode(snExprOperator);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( !IsOperator(t.type) )
	{
		Error(TXT_EXPECTED_OPERATOR, &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->SetToken(&t);
	node->UpdateSourcePos(t.pos, t.length);

	return node;
}

// BNF:16: ASSIGNOP      ::= '=' | '+=' | '-=' | '*=' | '/=' | '|=' | '&=' | '^=' | '%=' | '**=' | '<<=' | '>>=' | '>>>='
asCScriptNode *asCParser::ParseAssignOperator()
{
	asCScriptNode *node = CreateNode(snExprOperator);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( !IsAssignOperator(t.type) )
	{
		Error(TXT_EXPECTED_OPERATOR, &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->SetToken(&t);
	node->UpdateSourcePos(t.pos, t.length);

	return node;
}

bool asCParser::IsOperator(int tokenType)
{
	if( tokenType == ttPlus ||
		tokenType == ttMinus ||
		tokenType == ttStar ||
		tokenType == ttSlash ||
		tokenType == ttPercent ||
		tokenType == ttStarStar ||
		tokenType == ttAnd ||
		tokenType == ttOr ||
		tokenType == ttXor ||
		tokenType == ttEqual ||
		tokenType == ttNotEqual ||
		tokenType == ttLessThan ||
		tokenType == ttLessThanOrEqual ||
		tokenType == ttGreaterThan ||
		tokenType == ttGreaterThanOrEqual ||
		tokenType == ttAmp ||
		tokenType == ttBitOr ||
		tokenType == ttBitXor ||
		tokenType == ttBitShiftLeft ||
		tokenType == ttBitShiftRight ||
		tokenType == ttBitShiftRightArith ||
		tokenType == ttIs ||
		tokenType == ttNotIs )
		return true;

	return false;
}

bool asCParser::IsAssignOperator(int tokenType)
{
	if( tokenType == ttAssignment ||
		tokenType == ttAddAssign ||
		tokenType == ttSubAssign ||
		tokenType == ttMulAssign ||
		tokenType == ttDivAssign ||
		tokenType == ttModAssign ||
		tokenType == ttPowAssign ||
		tokenType == ttAndAssign ||
		tokenType == ttOrAssign ||
		tokenType == ttXorAssign ||
		tokenType == ttShiftLeftAssign ||
		tokenType == ttShiftRightLAssign ||
		tokenType == ttShiftRightAAssign )
		return true;

	return false;
}

bool asCParser::IsPreOperator(int tokenType)
{
	if( tokenType == ttMinus ||
		tokenType == ttPlus ||
		tokenType == ttNot ||
		tokenType == ttInc ||
		tokenType == ttDec ||
		tokenType == ttBitNot ||
		tokenType == ttHandle )
		return true;
	return false;
}

bool asCParser::IsPostOperator(int tokenType)
{
	if( tokenType == ttInc ||            // post increment
		tokenType == ttDec ||            // post decrement
		tokenType == ttDot ||            // member access
		tokenType == ttOpenBracket ||    // index operator
		tokenType == ttOpenParenthesis) // argument list for call on function pointer
		return true;
	return false;
}

bool asCParser::IsConstant(int tokenType)
{
	if( tokenType == ttIntConstant ||
		tokenType == ttFloatConstant ||
		tokenType == ttDoubleConstant ||
		tokenType == ttStringConstant ||
		tokenType == ttMultilineStringConstant ||
		tokenType == ttHeredocStringConstant ||
		tokenType == ttTrue ||
		tokenType == ttFalse ||
		tokenType == ttBitsConstant ||
		tokenType == ttNull )
		return true;

	return false;
}

int asCParser::ParseScript(asCScriptCode *in_script)
{
	Reset();

	this->script = in_script;

	scriptNode = ParseScript(false);

	if( errorWhileParsing )
		return -1;

	// TODO: Should allow application to request this warning to be generated.
	//       It should be off by default, since pre-processor may remove all
	//       code from a section while still being meant as valid code
/*
	// Warn in case there isn't anything in the script
	if( scriptNode->firstChild == 0 )
	{
		if( builder )
			builder->WriteWarning(script->name, TXT_SECTION_IS_EMPTY, 1, 1);
	}
*/
	return 0;
}

int asCParser::ParseExpression(asCScriptCode *in_script)
{
	Reset();

	this->script = in_script;

	checkValidTypes = true;

	scriptNode = ParseExpression();
	if( errorWhileParsing )
		return -1;

	return 0;
}

// BNF:1: IMPORT        ::= 'import' TYPE ['&'] IDENTIFIER PARAMLIST FUNCATTR 'from' STRING ';'
asCScriptNode *asCParser::ParseImport()
{
	asCScriptNode *node = CreateNode(snImport);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( t.type != ttImport )
	{
		Error(ExpectedToken(asCTokenizer::GetDefinition(ttImport)), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->SetToken(&t);
	node->UpdateSourcePos(t.pos, t.length);

	node->AddChildLast(ParseFunctionDefinition());
	if( isSyntaxError ) return node;

	GetToken(&t);
	if( t.type != ttIdentifier )
	{
		Error(ExpectedToken(FROM_TOKEN), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	tempString.Assign(&script->code[t.pos], t.length);
	if( tempString != FROM_TOKEN )
	{
		Error(ExpectedToken(FROM_TOKEN), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	GetToken(&t);
	if( t.type != ttStringConstant )
	{
		Error(TXT_EXPECTED_STRING, &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	asCScriptNode *mod = CreateNode(snConstant);
	if( mod == 0 ) return 0;

	node->AddChildLast(mod);

	mod->SetToken(&t);
	mod->UpdateSourcePos(t.pos, t.length);

	GetToken(&t);
	if( t.type != ttEndStatement )
	{
		Error(ExpectedToken(asCTokenizer::GetDefinition(ttEndStatement)), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	return node;
}

// BNF:0: SCRIPT        ::= {IMPORT | ENUM | TYPEDEF | CLASS | MIXIN | INTERFACE | FUNCDEF | VIRTPROP | VAR | FUNC | NAMESPACE | ';'}
asCScriptNode *asCParser::ParseScript(bool inBlock)
{
	asCScriptNode *node = CreateNode(snScript);
	if( node == 0 ) return 0;

	// Determine type of node
	for(;;)
	{
		while( !isSyntaxError )
		{
			sToken tStart;
			GetToken(&tStart);

			// Optimize by skipping tokens 'shared', 'external', 'final', 'abstract' so they don't have to be checked in every condition
			sToken t1 = tStart;
			while (IdentifierIs(t1, SHARED_TOKEN) ||
				IdentifierIs(t1, EXTERNAL_TOKEN) ||
				IdentifierIs(t1, FINAL_TOKEN) ||
				IdentifierIs(t1, ABSTRACT_TOKEN))
				GetToken(&t1);
			RewindTo(&tStart);

			if( t1.type == ttImport )
				node->AddChildLast(ParseImport());
			else if( t1.type == ttEnum )
				node->AddChildLast(ParseEnumeration());	// Handle enumerations
			else if( t1.type == ttTypedef )
				node->AddChildLast(ParseTypedef());		// Handle primitive typedefs
			else if( t1.type == ttClass )
				node->AddChildLast(ParseClass());
			else if( t1.type == ttMixin )
				node->AddChildLast(ParseMixin());
			else if( t1.type == ttInterface )
				node->AddChildLast(ParseInterface());
			else if( t1.type == ttFuncDef )
				node->AddChildLast(ParseFuncDef());
			else if( t1.type == ttConst || t1.type == ttScope || t1.type == ttAuto || IsDataType(t1) )
			{
				if( IsVirtualPropertyDecl() )
					node->AddChildLast(ParseVirtualPropertyDecl(false, false));
				else if( IsVarDecl() )
					node->AddChildLast(ParseDeclaration(false, true));
				else
					node->AddChildLast(ParseFunction());
			}
			else if( t1.type == ttEndStatement )
			{
				// Ignore a semicolon by itself
				GetToken(&t1);
			}
			else if( t1.type == ttNamespace )
				node->AddChildLast(ParseNamespace());
			else if( t1.type == ttEnd )
				return node;
			else if( inBlock && t1.type == ttEndStatementBlock )
				return node;
			else
			{
				asCString str;
				const char *t = asCTokenizer::GetDefinition(t1.type);
				if( t == 0 ) t = "<unknown token>";

				str.Format(TXT_UNEXPECTED_TOKEN_s, t);

				Error(str, &t1);
			}
		}

		if( isSyntaxError )
		{
			// Search for either ';' or '{' or end
			sToken t1;
			GetToken(&t1);
			while( t1.type != ttEndStatement && t1.type != ttEnd &&
				   t1.type != ttStartStatementBlock )
				GetToken(&t1);

			if( t1.type == ttStartStatementBlock )
			{
				// Find the end of the block and skip nested blocks
				int level = 1;
				while( level > 0 )
				{
					GetToken(&t1);
					if( t1.type == ttStartStatementBlock ) level++;
					if( t1.type == ttEndStatementBlock ) level--;
					if( t1.type == ttEnd ) break;
				}
			}

			isSyntaxError = false;
		}
	}
	UNREACHABLE_RETURN;
}

// BNF:1: NAMESPACE     ::= 'namespace' IDENTIFIER {'::' IDENTIFIER} '{' SCRIPT '}'
asCScriptNode *asCParser::ParseNamespace()
{
	asCScriptNode *node = CreateNode(snNamespace);
	if( node == 0 ) return 0;

	sToken t1;

	GetToken(&t1);
	if( t1.type == ttNamespace )
		node->UpdateSourcePos(t1.pos, t1.length);
	else
	{
		Error(ExpectedToken(asCTokenizer::GetDefinition(ttNamespace)), &t1);
		Error(InsteadFound(t1), &t1);
	}

	node->AddChildLast(ParseIdentifier());
	if( isSyntaxError ) return node;

	asCScriptNode *lowestNode = node;
	GetToken(&t1);
	while (t1.type == ttScope)
	{
		lowestNode->UpdateSourcePos(t1.pos, t1.length);

		asCScriptNode *scopeNode = CreateNode(snScript);
		if (scopeNode == 0) 
			return 0;
		lowestNode->AddChildLast(scopeNode);

		lowestNode = CreateNode(snNamespace);
		if (lowestNode == 0) 
			return 0;

		scopeNode->AddChildLast(lowestNode);
		lowestNode->AddChildLast(ParseIdentifier());
		if (isSyntaxError)
			return node;

		GetToken(&t1);
	}
	
	if( t1.type == ttStartStatementBlock )
		node->UpdateSourcePos(t1.pos, t1.length);
	else
	{
		Error(ExpectedToken(asCTokenizer::GetDefinition(ttStartStatementBlock)), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	sToken start = t1;

	lowestNode->AddChildLast(ParseScript(true));

	if( !isSyntaxError )
	{
		GetToken(&t1);
		if( t1.type == ttEndStatementBlock )
			node->UpdateSourcePos(t1.pos, t1.length);
		else
		{
			if( t1.type == ttEnd )
				Error(TXT_UNEXPECTED_END_OF_FILE, &t1);
			else
			{
				Error(ExpectedToken(asCTokenizer::GetDefinition(ttEndStatementBlock)), &t1);
				Error(InsteadFound(t1), &t1);
			}
			Info(TXT_WHILE_PARSING_NAMESPACE, &start);
			return node;
		}
	}

	return node;
}

int asCParser::ParseStatementBlock(asCScriptCode *in_script, asCScriptNode *in_block)
{
	TimeIt("asCParser::ParseStatementBlock");

	Reset();

	// Tell the parser to validate the identifiers as valid types
	checkValidTypes = true;

	this->script = in_script;
	sourcePos = in_block->tokenPos;

	scriptNode = ParseStatementBlock();

	if( isSyntaxError || errorWhileParsing )
		return -1;

	return 0;
}

// BNF:1: ENUM          ::= {'shared' | 'external'} 'enum' IDENTIFIER (';' | ('{' IDENTIFIER ['=' EXPR] {',' IDENTIFIER ['=' EXPR]} '}'))
asCScriptNode *asCParser::ParseEnumeration()
{
	asCScriptNode *ident;
	asCScriptNode *dataType;

	asCScriptNode *node = CreateNode(snEnum);
	if( node == 0 ) return 0;

	sToken token;

	// Optional 'shared' and 'external' token
	GetToken(&token);
	while( IdentifierIs(token, SHARED_TOKEN) ||
		   IdentifierIs(token, EXTERNAL_TOKEN) )
	{
		RewindTo(&token);
		node->AddChildLast(ParseIdentifier());
		if( isSyntaxError ) return node;

		GetToken(&token);
	}

	// Check for enum
	if( token.type != ttEnum )
	{
		Error(ExpectedToken(asCTokenizer::GetDefinition(ttEnum)), &token);
		Error(InsteadFound(token), &token);
		return node;
	}

	node->SetToken(&token);
	node->UpdateSourcePos(token.pos, token.length);

	// Get the identifier
	GetToken(&token);
	if(ttIdentifier != token.type)
	{
		Error(TXT_EXPECTED_IDENTIFIER, &token);
		Error(InsteadFound(token), &token);
		return node;
	}

	dataType = CreateNode(snDataType);
	if( dataType == 0 ) return node;

	node->AddChildLast(dataType);

	ident = CreateNode(snIdentifier);
	if( ident == 0 ) return node;

	ident->SetToken(&token);
	ident->UpdateSourcePos(token.pos, token.length);
	dataType->AddChildLast(ident);

	// External shared declarations are ended with ';'
	GetToken(&token);
	if (token.type == ttEndStatement)
	{
		RewindTo(&token);
		node->AddChildLast(ParseToken(ttEndStatement));
		return node;
	}

	// check for the start of the declaration block
	if( token.type != ttStartStatementBlock )
	{
		RewindTo(&token);
		int tokens[] = { ttStartStatementBlock, ttEndStatement };
		Error(ExpectedOneOf(tokens, 2), &token);
		Error(InsteadFound(token), &token);
		return node;
	}

	while(ttEnd != token.type)
	{
		GetToken(&token);

		if( ttEndStatementBlock == token.type )
		{
			RewindTo(&token);
			break;
		}

		if(ttIdentifier != token.type)
		{
			Error(TXT_EXPECTED_IDENTIFIER, &token);
			Error(InsteadFound(token), &token);
			return node;
		}

		// Add the enum element
		ident = CreateNode(snIdentifier);
		if( ident == 0 ) return node;

		ident->SetToken(&token);
		ident->UpdateSourcePos(token.pos, token.length);
		node->AddChildLast(ident);

		GetToken(&token);

		if( token.type == ttAssignment )
		{
			asCScriptNode	*tmp;

			RewindTo(&token);

			tmp = SuperficiallyParseVarInit();

			node->AddChildLast(tmp);
			if( isSyntaxError ) return node;
			GetToken(&token);
		}

		if(ttListSeparator != token.type)
		{
			RewindTo(&token);
			break;
		}
	}

	// check for the end of the declaration block
	GetToken(&token);
	if( token.type != ttEndStatementBlock )
	{
		RewindTo(&token);
		Error(ExpectedToken("}"), &token);
		Error(InsteadFound(token), &token);
		return node;
	}

	return node;
}

bool asCParser::IsVarDecl()
{
	// Set start point so that we can rewind
	sToken t;
	GetToken(&t);
	RewindTo(&t);

	// A class property decl can be preceded by 'private' or 'protected'
	sToken t1;
	GetToken(&t1);
	if( t1.type != ttPrivate && t1.type != ttProtected )
		RewindTo(&t1);

	// A variable decl starts with the type
	if (!FindTokenAfterType(t1))
	{
		RewindTo(&t);
		return false;
	}

	// Jump to the token after the type
	RewindTo(&t1);
	GetToken(&t1);
	
	// The declaration needs to have a name
	if( t1.type != ttIdentifier )
	{
		RewindTo(&t);
		return false;
	}

	// It can be followed by an initialization
	GetToken(&t1);
	if( t1.type == ttEndStatement || t1.type == ttAssignment || t1.type == ttListSeparator )
	{
		RewindTo(&t);
		return true;
	}
	if( t1.type == ttOpenParenthesis)
	{
		// If the closing parenthesis is followed by a statement block, 
		// function decorator, or end-of-file, then treat it as a function. 
		// A function decl may have nested parenthesis so we need to check 
		// for this too.
		int nest = 0;
		while( t1.type != ttEnd )
		{
			if( t1.type == ttOpenParenthesis)
				nest++;
			else if( t1.type == ttCloseParenthesis)
			{
				nest--;
				if( nest == 0 )
					break;
			}
			GetToken(&t1);
		}

		if (t1.type == ttEnd)
		{
			RewindTo(&t);
			return false;
		}
		else
		{
			GetToken(&t1);
			RewindTo(&t);
			if( t1.type == ttStartStatementBlock || 
				t1.type == ttIdentifier || // function decorator
				t1.type == ttEnd )
				return false;
		}

		RewindTo(&t);
		return true;
	}

	RewindTo(&t);
	return false;
}

bool asCParser::IsVirtualPropertyDecl()
{
	// Set start point so that we can rewind
	sToken t;
	GetToken(&t);
	RewindTo(&t);

	// A class property decl can be preceded by 'private' or 'protected'
	sToken t1;
	GetToken(&t1);
	if( t1.type != ttPrivate && t1.type != ttProtected )
		RewindTo(&t1);

	// A variable decl starts with the type
	if (!FindTokenAfterType(t1))
	{
		RewindTo(&t);
		return false;
	}

	// Move to the token after the type
	RewindTo(&t1);
	GetToken(&t1);

	// The decl must have an identifier
	if( t1.type != ttIdentifier )
	{
		RewindTo(&t);
		return false;
	}

	// To be a virtual property it must also have a block for the get/set functions
	GetToken(&t1);
	if( t1.type == ttStartStatementBlock )
	{
		RewindTo(&t);
		return true;
	}

	RewindTo(&t);
	return false;
}

bool asCParser::IsFuncDecl(bool isMethod)
{
	// Set start point so that we can rewind
	sToken t;
	GetToken(&t);
	RewindTo(&t);

	if( isMethod )
	{
		// A class method decl can be preceded by 'private' or 'protected'
		sToken t1, t2;
		GetToken(&t1);
		if( t1.type != ttPrivate && t1.type != ttProtected )
			RewindTo(&t1);

		// A class constructor starts with identifier followed by parenthesis
		// A class destructor starts with the ~ token
		GetToken(&t1);
		GetToken(&t2);
		RewindTo(&t1);
		if( (t1.type == ttIdentifier && t2.type == ttOpenParenthesis) || t1.type == ttBitNot )
		{
			RewindTo(&t);
			return true;
		}
	}

	// A function decl starts with a type
	sToken t1;
	if (!FindTokenAfterType(t1))
	{
		RewindTo(&t);
		return false;
	}

	// Move to the token after the type
	RewindTo(&t1);
	GetToken(&t1);

	// There can be an ampersand if the function returns a reference
	if( t1.type == ttAmp )
	{
		RewindTo(&t);
		return true;
	}

	if( t1.type != ttIdentifier )
	{
		RewindTo(&t);
		return false;
	}

	GetToken(&t1);
	if( t1.type == ttOpenParenthesis)
	{
		// If the closing parenthesis is not followed by a
		// statement block then it is not a function.
		// It's possible that there are nested parenthesis due to default
		// arguments so this should be checked for.
		int nest = 0;
		GetToken(&t1);
		while( (nest || t1.type != ttCloseParenthesis) && t1.type != ttEnd )
		{
			if( t1.type == ttOpenParenthesis)
				nest++;
			if( t1.type == ttCloseParenthesis)
				nest--;

			GetToken(&t1);
		}

		if( t1.type == ttEnd )
			return false;
		else
		{
			if( isMethod )
			{
				// A class method can have a 'const' token after the parameter list
				GetToken(&t1);
				if( t1.type != ttConst )
					RewindTo(&t1);
			}

			// A function may also have any number of additional attributes
			bool hasAttribs = false;
			for( ; ; )
			{
				GetToken(&t1);
				if( !IdentifierIs(t1, FINAL_TOKEN) && 
					!IdentifierIs(t1, OVERRIDE_TOKEN) &&
					!IdentifierIs(t1, EXPLICIT_TOKEN) &&
					!IdentifierIs(t1, PROPERTY_TOKEN) &&
					!IdentifierIs(t1, DELETE_TOKEN) )
				{
					RewindTo(&t1);
					break;
				}
				hasAttribs = true;
			}
			
			GetToken(&t1);
			RewindTo(&t);

			// If the function has an attribute, e.g. delete, it can be terminated with a ; if no 
			// implementation is expected otherwise it must have a statement block
			if( (t1.type == ttEndStatement && hasAttribs) || t1.type == ttStartStatementBlock )
				return true;
		}

		RewindTo(&t);
		return false;
	}

	RewindTo(&t);
	return false;
}

// BNF:1: FUNCDEF       ::= {'external' | 'shared'} 'funcdef' TYPE ['&'] IDENTIFIER PARAMLIST ';'
asCScriptNode *asCParser::ParseFuncDef()
{
	asCScriptNode *node = CreateNode(snFuncDef);
	if( node == 0 ) return 0;

	// Allow keywords 'external' and 'shared' before 'interface'
	sToken t1;
	GetToken(&t1);
	while (IdentifierIs(t1, SHARED_TOKEN) ||
		   IdentifierIs(t1, EXTERNAL_TOKEN))
	{
		RewindTo(&t1);
		node->AddChildLast(ParseIdentifier());
		if (isSyntaxError) return node;

		GetToken(&t1);
	}

	if( t1.type != ttFuncDef )
	{
		Error(asCTokenizer::GetDefinition(ttFuncDef), &t1);
		return node;
	}

	node->SetToken(&t1);

	node->AddChildLast(ParseType(true));
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseTypeMod(false));
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseIdentifier());
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseParameterList());
	if( isSyntaxError ) return node;

	GetToken(&t1);
	if( t1.type != ttEndStatement )
	{
		Error(ExpectedToken(asCTokenizer::GetDefinition(ttEndStatement)), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	node->UpdateSourcePos(t1.pos, t1.length);

	return node;
}

// BNF:1: FUNC          ::= {'shared' | 'external'} ['private' | 'protected'] [((TYPE ['&']) | '~')] IDENTIFIER PARAMLIST ['const'] FUNCATTR (';' | STATBLOCK)
asCScriptNode *asCParser::ParseFunction(bool isMethod)
{
	asCScriptNode *node = CreateNode(snFunction);
	if( node == 0 ) return 0;

	// TODO: Why isn't ParseFunctionDefinition used?
	sToken t1;
	GetToken(&t1);
	if (!isMethod)
	{
		// A global function can be marked as shared and external
		while (t1.type == ttIdentifier)
		{
			if (IdentifierIs(t1, SHARED_TOKEN) ||
				IdentifierIs(t1, EXTERNAL_TOKEN))
			{
				RewindTo(&t1);
				node->AddChildLast(ParseIdentifier());
				if (isSyntaxError) return node;
			}
			else
				break;

			GetToken(&t1);
		}
	}

	// A class method can start with 'private' or 'protected'
	if (isMethod && t1.type == ttPrivate)
	{
		RewindTo(&t1);
		node->AddChildLast(ParseToken(ttPrivate));
		GetToken(&t1);
	}
	else if (isMethod && t1.type == ttProtected)
	{
		RewindTo(&t1);
		node->AddChildLast(ParseToken(ttProtected));
		GetToken(&t1);
	}
	if( isSyntaxError ) return node;

	// If it is a global function, or a method, except constructor and destructor, then the return type is parsed
	sToken t2;
	GetToken(&t2);
	RewindTo(&t1);
	if( !isMethod || (t1.type != ttBitNot && t2.type != ttOpenParenthesis) )
	{
		node->AddChildLast(ParseType(true));
		if( isSyntaxError ) return node;

		node->AddChildLast(ParseTypeMod(false));
		if( isSyntaxError ) return node;
	}

	// If this is a class destructor then it starts with ~, and no return type is declared
	if( isMethod && t1.type == ttBitNot )
	{
		node->AddChildLast(ParseToken(ttBitNot));
		if( isSyntaxError ) return node;
	}

	node->AddChildLast(ParseIdentifier());
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseParameterList());
	if( isSyntaxError ) return node;

	if( isMethod )
	{
		GetToken(&t1);
		RewindTo(&t1);

		// Is the method a const?
		if( t1.type == ttConst )
			node->AddChildLast(ParseToken(ttConst));
	}

	// TODO: Should support abstract methods, in which case no statement block should be provided
	ParseMethodAttributes(node);
	if( isSyntaxError ) return node;

	// External shared functions must be ended with ';'
	GetToken(&t1);
	RewindTo(&t1);
	if (t1.type == ttEndStatement)
	{
		node->AddChildLast(ParseToken(ttEndStatement));
		return node;
	}

	// We should just find the end of the statement block here. The statements
	// will be parsed on request by the compiler once it starts the compilation.
	node->AddChildLast(SuperficiallyParseStatementBlock());

	return node;
}

// BNF:2: INTFMTHD      ::= TYPE ['&'] IDENTIFIER PARAMLIST ['const'] ';'
asCScriptNode *asCParser::ParseInterfaceMethod()
{
	asCScriptNode *node = CreateNode(snFunction);
	if( node == 0 ) return 0;

	node->AddChildLast(ParseType(true));
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseTypeMod(false));
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseIdentifier());
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseParameterList());
	if( isSyntaxError ) return node;

	// Parse an optional const after the method definition
	sToken t1;
	GetToken(&t1);
	RewindTo(&t1);
	if( t1.type == ttConst )
		node->AddChildLast(ParseToken(ttConst));

	GetToken(&t1);
	if( t1.type != ttEndStatement )
	{
		Error(ExpectedToken(";"), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	node->UpdateSourcePos(t1.pos, t1.length);

	return node;
}

// BNF:1: VIRTPROP      ::= ['private' | 'protected'] TYPE ['&'] IDENTIFIER '{' {('get' | 'set') ['const'] FUNCATTR (STATBLOCK | ';')} '}'
asCScriptNode *asCParser::ParseVirtualPropertyDecl(bool isMethod, bool isInterface)
{
	asCScriptNode *node = CreateNode(snVirtualProperty);
	if( node == 0 ) return 0;

	sToken t1,t2;
	GetToken(&t1);
	GetToken(&t2);
	RewindTo(&t1);

	// A class method can start with 'private' or 'protected'
	if( isMethod && t1.type == ttPrivate )
		node->AddChildLast(ParseToken(ttPrivate));
	else if( isMethod && t1.type == ttProtected )
		node->AddChildLast(ParseToken(ttProtected));
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseType(true));
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseTypeMod(false));
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseIdentifier());
	if( isSyntaxError ) return node;

	GetToken(&t1);
	if( t1.type != ttStartStatementBlock )
	{
		Error(ExpectedToken("{"), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	for(;;)
	{
		GetToken(&t1);
		asCScriptNode *accessorNode = 0;

		if( IdentifierIs(t1, GET_TOKEN) || IdentifierIs(t1, SET_TOKEN) )
		{
			accessorNode = CreateNode(snVirtualProperty);
			if( accessorNode == 0 ) return 0;

			node->AddChildLast(accessorNode);

			RewindTo(&t1);
			accessorNode->AddChildLast(ParseIdentifier());

			if( isMethod )
			{
				GetToken(&t1);
				RewindTo(&t1);
				if( t1.type == ttConst )
					accessorNode->AddChildLast(ParseToken(ttConst));

				if( !isInterface )
				{
					ParseMethodAttributes(accessorNode);
					if( isSyntaxError ) return node;
				}
			}

			if( !isInterface )
			{
				GetToken(&t1);
				if( t1.type == ttStartStatementBlock )
				{
					RewindTo(&t1);
					accessorNode->AddChildLast(SuperficiallyParseStatementBlock());
					if( isSyntaxError ) return node;
				}
				else if (t1.type == ttEndStatement)
				{
					RewindTo(&t1);
					accessorNode->AddChildLast(ParseToken(ttEndStatement));
					if (isSyntaxError) return node;
				}
				else
				{
					Error(ExpectedTokens(";", "{"), &t1);
					Error(InsteadFound(t1), &t1);
					return node;
				}
			}
			else
			{
				GetToken(&t1);
				if (t1.type == ttEndStatement)
				{
					RewindTo(&t1);
					accessorNode->AddChildLast(ParseToken(ttEndStatement));
					if (isSyntaxError) return node;
				}
				else
				{
					Error(ExpectedToken(";"), &t1);
					Error(InsteadFound(t1), &t1);
					return node;
				}
			}
		}
		else if( t1.type == ttEndStatementBlock )
			break;
		else
		{
			const char *tokens[] = { GET_TOKEN, SET_TOKEN, asCTokenizer::GetDefinition(ttEndStatementBlock) };
			Error(ExpectedOneOf(tokens, 3), &t1);
			Error(InsteadFound(t1), &t1);
			return node;
		}
	}

	return node;
}

// BNF:1: INTERFACE     ::= {'external' | 'shared'} 'interface' IDENTIFIER (';' | ([':' IDENTIFIER {',' IDENTIFIER}] '{' {VIRTPROP | INTFMTHD} '}'))
asCScriptNode *asCParser::ParseInterface()
{
	asCScriptNode *node = CreateNode(snInterface);
	if( node == 0 ) return 0;

	sToken t;

	// Allow keywords 'external' and 'shared' before 'interface'
	GetToken(&t);
	while( IdentifierIs(t, SHARED_TOKEN) ||
		   IdentifierIs(t, EXTERNAL_TOKEN) )
	{
		RewindTo(&t);
		node->AddChildLast(ParseIdentifier());
		if (isSyntaxError) return node;

		GetToken(&t);
	}

	if( t.type != ttInterface )
	{
		Error(ExpectedToken("interface"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->SetToken(&t);
	node->AddChildLast(ParseIdentifier());

	// External shared declarations are ended with ';'
	GetToken(&t);
	if (t.type == ttEndStatement)
	{
		RewindTo(&t);
		node->AddChildLast(ParseToken(ttEndStatement));
		return node;
	}

	// Can optionally have a list of interfaces that are inherited
	if( t.type == ttColon )
	{
		asCScriptNode *inherit = CreateNode(snIdentifier);
		node->AddChildLast(inherit);

		ParseOptionalScope(inherit);
		inherit->AddChildLast(ParseIdentifier());
		GetToken(&t);
		while( t.type == ttListSeparator )
		{
			inherit = CreateNode(snIdentifier);
			node->AddChildLast(inherit);

			ParseOptionalScope(inherit);
			inherit->AddChildLast(ParseIdentifier());
			GetToken(&t);
		}
	}

	if( t.type != ttStartStatementBlock )
	{
		Error(ExpectedToken("{"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	// Parse interface methods
	GetToken(&t);
	RewindTo(&t);
	while( t.type != ttEndStatementBlock && t.type != ttEnd )
	{
		if( IsVirtualPropertyDecl() )
			node->AddChildLast(ParseVirtualPropertyDecl(true, true));
		else if( t.type == ttEndStatement )
			// Skip empty declarations
			GetToken(&t);
		else
			// Parse the method signature
			node->AddChildLast(ParseInterfaceMethod());

		if( isSyntaxError ) return node;

		GetToken(&t);
		RewindTo(&t);
	}

	GetToken(&t);
	if( t.type != ttEndStatementBlock )
	{
		Error(ExpectedToken("}"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	return node;
}

// BNF:1: MIXIN         ::= 'mixin' CLASS
asCScriptNode *asCParser::ParseMixin()
{
	asCScriptNode *node = CreateNode(snMixin);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);

	if( t.type != ttMixin )
	{
		Error(ExpectedToken("mixin"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->SetToken(&t);

	// A mixin token must be followed by a class declaration
	node->AddChildLast(ParseClass());

	return node;
}

// BNF:1: CLASS         ::= {'shared' | 'abstract' | 'final' | 'external'} 'class' IDENTIFIER (';' | ([':' IDENTIFIER {',' IDENTIFIER}] '{' {VIRTPROP | FUNC | VAR | FUNCDEF} '}'))
asCScriptNode *asCParser::ParseClass()
{
	asCScriptNode *node = CreateNode(snClass);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);

	// Allow the keywords 'shared', 'abstract', 'final', and 'external' before 'class'
	while( IdentifierIs(t, SHARED_TOKEN) ||
		   IdentifierIs(t, ABSTRACT_TOKEN) ||
		   IdentifierIs(t, FINAL_TOKEN) ||
		   IdentifierIs(t, EXTERNAL_TOKEN) )
	{
		RewindTo(&t);
		node->AddChildLast(ParseIdentifier());
		GetToken(&t);
	}

	if( t.type != ttClass )
	{
		Error(ExpectedToken("class"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->SetToken(&t);

	if( engine->ep.allowImplicitHandleTypes )
	{
		// Parse 'implicit handle class' construct
		GetToken(&t);

		if ( t.type == ttHandle )
			node->SetToken(&t);
		else
			RewindTo(&t);
	}

	node->AddChildLast(ParseIdentifier());

	// External shared declarations are ended with ';'
	GetToken(&t);
	if (t.type == ttEndStatement)
	{
		RewindTo(&t);
		node->AddChildLast(ParseToken(ttEndStatement));
		return node;
	}

	// Optional list of interfaces that are being implemented and classes that are being inherited
	if( t.type == ttColon )
	{
		asCScriptNode *inherit = CreateNode(snIdentifier);
		node->AddChildLast(inherit);

		ParseOptionalScope(inherit);
		inherit->AddChildLast(ParseIdentifier());
		GetToken(&t);
		while( t.type == ttListSeparator )
		{
			inherit = CreateNode(snIdentifier);
			node->AddChildLast(inherit);

			ParseOptionalScope(inherit);
			inherit->AddChildLast(ParseIdentifier());
			GetToken(&t);
		}
	}

	if( t.type != ttStartStatementBlock )
	{
		Error(ExpectedToken("{"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	// Parse properties
	GetToken(&t);
	RewindTo(&t);
	while( t.type != ttEndStatementBlock && t.type != ttEnd )
	{
		// Is it a property or a method?
		if (t.type == ttFuncDef)
			node->AddChildLast(ParseFuncDef());
		else if( IsFuncDecl(true) )
			node->AddChildLast(ParseFunction(true));
		else if( IsVirtualPropertyDecl() )
			node->AddChildLast(ParseVirtualPropertyDecl(true, false));
		else if( IsVarDecl() )
			node->AddChildLast(ParseDeclaration(true));
		else if( t.type == ttEndStatement )
			// Skip empty declarations
			GetToken(&t);
		else
		{
			Error(TXT_EXPECTED_METHOD_OR_PROPERTY, &t);
			Error(InsteadFound(t), &t);
			return node;
		}

		if( isSyntaxError )
			return node;

		GetToken(&t);
		RewindTo(&t);
	}

	GetToken(&t);
	if( t.type != ttEndStatementBlock )
	{
		Error(ExpectedToken("}"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}
	node->UpdateSourcePos(t.pos, t.length);

	return node;
}

int asCParser::ParseVarInit(asCScriptCode *in_script, asCScriptNode *in_init)
{
	Reset();

	// Tell the parser to validate the identifiers as valid types
	checkValidTypes = true;

	this->script = in_script;
	sourcePos = in_init->tokenPos;

	// If next token is assignment, parse expression
	sToken t;
	GetToken(&t);
	if( t.type == ttAssignment )
	{
		GetToken(&t);
		RewindTo(&t);
		if( t.type == ttStartStatementBlock )
			scriptNode = ParseInitList();
		else
			scriptNode = ParseAssignment();
	}
	else if( t.type == ttOpenParenthesis)
	{
		RewindTo(&t);
		scriptNode = ParseArgList();
	}
	else
	{
		int tokens[] = {ttAssignment, ttOpenParenthesis };
		Error(ExpectedOneOf(tokens, 2), &t);
		Error(InsteadFound(t), &t);
	}

	// Don't allow any more tokens after the expression
	GetToken(&t);
	if( t.type != ttEnd && t.type != ttEndStatement && t.type != ttListSeparator && t.type != ttEndStatementBlock )
	{
		asCString msg;
		msg.Format(TXT_UNEXPECTED_TOKEN_s, asCTokenizer::GetDefinition(t.type));
		Error(msg, &t);
	}

	if( isSyntaxError || errorWhileParsing )
		return -1;

	return 0;
}

asCScriptNode *asCParser::SuperficiallyParseVarInit()
{
	asCScriptNode *node = CreateNode(snAssignment);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	node->UpdateSourcePos(t.pos, t.length);

	if( t.type == ttAssignment )
	{
		GetToken(&t);
		sToken start = t;

		// Find the end of the expression
		int indentParan = 0;
		int indentBrace = 0;
		while( indentParan || indentBrace || (t.type != ttListSeparator && t.type != ttEndStatement && t.type != ttEndStatementBlock) )
		{
			if( t.type == ttOpenParenthesis)
				indentParan++;
			else if( t.type == ttCloseParenthesis)
				indentParan--;
			else if( t.type == ttStartStatementBlock )
				indentBrace++;
			else if( t.type == ttEndStatementBlock )
				indentBrace--;
			else if( t.type == ttNonTerminatedStringConstant )
			{
				Error(TXT_NONTERMINATED_STRING, &t);
				break;
			}
			else if( t.type == ttEnd )
			{
				Error(TXT_UNEXPECTED_END_OF_FILE, &t);
				Info(TXT_WHILE_PARSING_EXPRESSION, &start);
				break;
			}
			GetToken(&t);
		}

		// Rewind so that the next token read is the list separator, end statement, or end statement block
		RewindTo(&t);
	}
	else if( t.type == ttOpenParenthesis)
	{
		sToken start = t;

		// Find the end of the argument list
		int indent = 1;
		while( indent )
		{
			GetToken(&t);
			if( t.type == ttOpenParenthesis)
				indent++;
			else if( t.type == ttCloseParenthesis)
				indent--;
			else if( t.type == ttNonTerminatedStringConstant )
			{
				Error(TXT_NONTERMINATED_STRING, &t);
				break;
			}
			else if( t.type == ttEnd )
			{
				Error(TXT_UNEXPECTED_END_OF_FILE, &t);
				Info(TXT_WHILE_PARSING_ARG_LIST, &start);
				break;
			}
		}
	}
	else
	{
		int tokens[] = {ttAssignment, ttOpenParenthesis };
		Error(ExpectedOneOf(tokens, 2), &t);
		Error(InsteadFound(t), &t);
	}

	return node;
}

asCScriptNode *asCParser::SuperficiallyParseStatementBlock()
{
	asCScriptNode *node = CreateNode(snStatementBlock);
	if( node == 0 ) return 0;

	// This function will only superficially parse the statement block in order to find the end of it
	sToken t1;

	GetToken(&t1);
	if( t1.type != ttStartStatementBlock )
	{
		Error(ExpectedToken("{"), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	node->UpdateSourcePos(t1.pos, t1.length);

	sToken start = t1;

	int level = 1;
	while( level > 0 && !isSyntaxError )
	{
		GetToken(&t1);
		if( t1.type == ttEndStatementBlock )
			level--;
		else if( t1.type == ttStartStatementBlock )
			level++;
		else if( t1.type == ttNonTerminatedStringConstant )
		{
			Error(TXT_NONTERMINATED_STRING, &t1);
			break;
		}
		else if( t1.type == ttEnd )
		{
			Error(TXT_UNEXPECTED_END_OF_FILE, &t1);
			Info(TXT_WHILE_PARSING_STATEMENT_BLOCK, &start);
			break;
		}
	}

	node->UpdateSourcePos(t1.pos, t1.length);

	return node;
}

// BNF:2: STATBLOCK     ::= '{' {VAR | STATEMENT} '}'
asCScriptNode *asCParser::ParseStatementBlock()
{
	asCScriptNode *node = CreateNode(snStatementBlock);
	if( node == 0 ) return 0;

	sToken t1;

	GetToken(&t1);
	if( t1.type != ttStartStatementBlock )
	{
		Error(ExpectedToken("{"), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	sToken start = t1;

	node->UpdateSourcePos(t1.pos, t1.length);

	for(;;)
	{
		while( !isSyntaxError )
		{
			GetToken(&t1);
			if( t1.type == ttEndStatementBlock )
			{
				node->UpdateSourcePos(t1.pos, t1.length);

				// Statement block is finished
				return node;
			}
			else
			{
				RewindTo(&t1);

				if( IsVarDecl() )
					node->AddChildLast(ParseDeclaration());
				else
					node->AddChildLast(ParseStatement());
			}
		}

		if( isSyntaxError )
		{
			// Search for either ';', '{', '}', or end
			GetToken(&t1);
			while( t1.type != ttEndStatement && t1.type != ttEnd &&
				   t1.type != ttStartStatementBlock && t1.type != ttEndStatementBlock )
			{
				GetToken(&t1);
			}

			// Skip this statement block
			if( t1.type == ttStartStatementBlock )
			{
				// Find the end of the block and skip nested blocks
				int level = 1;
				while( level > 0 )
				{
					GetToken(&t1);
					if( t1.type == ttStartStatementBlock ) level++;
					if( t1.type == ttEndStatementBlock ) level--;
					if( t1.type == ttEnd ) break;
				}
			}
			else if( t1.type == ttEndStatementBlock )
			{
				RewindTo(&t1);
			}
			else if( t1.type == ttEnd )
			{
				Error(TXT_UNEXPECTED_END_OF_FILE, &t1);
				Info(TXT_WHILE_PARSING_STATEMENT_BLOCK, &start);
				return node;
			}

			isSyntaxError = false;
		}
	}
	UNREACHABLE_RETURN;
}

// BNF:4: INITLIST      ::= '{' [ASSIGN | INITLIST] {',' [ASSIGN | INITLIST]} '}'
asCScriptNode *asCParser::ParseInitList()
{
	asCScriptNode *node = CreateNode(snInitList);
	if( node == 0 ) return 0;

	sToken t1;

	GetToken(&t1);
	if( t1.type != ttStartStatementBlock )
	{
		Error(ExpectedToken("{"), &t1);
		Error(InsteadFound(t1), &t1);
		return node;
	}

	node->UpdateSourcePos(t1.pos, t1.length);

	GetToken(&t1);
	if( t1.type == ttEndStatementBlock )
	{
		node->UpdateSourcePos(t1.pos, t1.length);

		// Statement block is finished
		return node;
	}
	else
	{
		RewindTo(&t1);
		for(;;)
		{
			GetToken(&t1);
			if( t1.type == ttListSeparator )
			{
				// No expression
				node->AddChildLast(CreateNode(snUndefined));
				node->lastChild->UpdateSourcePos(t1.pos, 1);

				GetToken(&t1);
				if( t1.type == ttEndStatementBlock )
				{
					// No expression
					node->AddChildLast(CreateNode(snUndefined));
					node->lastChild->UpdateSourcePos(t1.pos, 1);
					node->UpdateSourcePos(t1.pos, t1.length);
					return node;
				}
				RewindTo(&t1);
			}
			else if( t1.type == ttEndStatementBlock )
			{
				// No expression
				node->AddChildLast(CreateNode(snUndefined));
				node->lastChild->UpdateSourcePos(t1.pos, 1);
				node->UpdateSourcePos(t1.pos, t1.length);

				// Statement block is finished
				return node;
			}
			else if( t1.type == ttStartStatementBlock )
			{
				RewindTo(&t1);
				node->AddChildLast(ParseInitList());
				if( isSyntaxError ) return node;

				GetToken(&t1);
				if( t1.type == ttListSeparator )
					continue;
				else if( t1.type == ttEndStatementBlock )
				{
					node->UpdateSourcePos(t1.pos, t1.length);

					// Statement block is finished
					return node;
				}
				else
				{
					Error(ExpectedTokens("}", ","), &t1);
					Error(InsteadFound(t1), &t1);
					return node;
				}
			}
			else
			{
				RewindTo(&t1);
				node->AddChildLast(ParseAssignment());
				if( isSyntaxError ) return node;


				GetToken(&t1);
				if( t1.type == ttListSeparator )
					continue;
				else if( t1.type == ttEndStatementBlock )
				{
					node->UpdateSourcePos(t1.pos, t1.length);

					// Statement block is finished
					return node;
				}
				else
				{
					Error(ExpectedTokens("}", ","), &t1);
					Error(InsteadFound(t1), &t1);
					return node;
				}
			}
		}
	}
	UNREACHABLE_RETURN;
}

// BNF:1: VAR           ::= ['private'|'protected'] TYPE IDENTIFIER [( '=' (INITLIST | EXPR)) | ARGLIST] {',' IDENTIFIER [( '=' (INITLIST | EXPR)) | ARGLIST]} ';'
asCScriptNode *asCParser::ParseDeclaration(bool isClassProp, bool isGlobalVar)
{
	asCScriptNode *node = CreateNode(snDeclaration);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	RewindTo(&t);

	// A class property can be preceeded by private
	if( t.type == ttPrivate && isClassProp )
		node->AddChildLast(ParseToken(ttPrivate));
	else if( t.type == ttProtected && isClassProp )
		node->AddChildLast(ParseToken(ttProtected));

	// Parse data type
	node->AddChildLast(ParseType(true, false, !isClassProp));
	if( isSyntaxError ) return node;

	for(;;)
	{
		// Parse identifier
		node->AddChildLast(ParseIdentifier());
		if( isSyntaxError ) return node;

		if( isClassProp || isGlobalVar )
		{
			// Only superficially parse the initialization info for the class property
			GetToken(&t);
			RewindTo(&t);
			if( t.type == ttAssignment || t.type == ttOpenParenthesis)
			{
				node->AddChildLast(SuperficiallyParseVarInit());
				if( isSyntaxError ) return node;
			}
		}
		else
		{
			// If next token is assignment, parse expression
			GetToken(&t);
			if( t.type == ttOpenParenthesis)
			{
				RewindTo(&t);
				node->AddChildLast(ParseArgList());
				if( isSyntaxError ) return node;
			}
			else if( t.type == ttAssignment )
			{
				GetToken(&t);
				RewindTo(&t);
				if( t.type == ttStartStatementBlock )
				{
					node->AddChildLast(ParseInitList());
					if( isSyntaxError ) return node;
				}
				else
				{
					node->AddChildLast(ParseAssignment());
					if( isSyntaxError ) return node;
				}
			}
			else
				RewindTo(&t);
		}

		// continue if list separator, else terminate with end statement
		GetToken(&t);
		if( t.type == ttListSeparator )
			continue;
		else if( t.type == ttEndStatement )
		{
			node->UpdateSourcePos(t.pos, t.length);

			return node;
		}
		else
		{
			Error(ExpectedTokens(",", ";"), &t);
			Error(InsteadFound(t), &t);
			return node;
		}
	}
	UNREACHABLE_RETURN;
}

// BNF:7: STATEMENT     ::= (IF | FOR | WHILE | RETURN | STATBLOCK | BREAK | CONTINUE | DOWHILE | SWITCH | EXPRSTAT | TRY)
asCScriptNode *asCParser::ParseStatement()
{
	sToken t1;

	GetToken(&t1);
	RewindTo(&t1);

	if (t1.type == ttIf)
		return ParseIf();
	else if (t1.type == ttFor)
		return ParseFor();
	else if (t1.type == ttWhile)
		return ParseWhile();
	else if (t1.type == ttReturn)
		return ParseReturn();
	else if (t1.type == ttStartStatementBlock)
		return ParseStatementBlock();
	else if (t1.type == ttBreak)
		return ParseBreak();
	else if (t1.type == ttContinue)
		return ParseContinue();
	else if (t1.type == ttDo)
		return ParseDoWhile();
	else if (t1.type == ttSwitch)
		return ParseSwitch();
	else if (t1.type == ttTry)
		return ParseTryCatch();
	else
	{
		if( IsVarDecl() )
		{
			Error(TXT_UNEXPECTED_VAR_DECL, &t1);
			return 0;
		}
		return ParseExpressionStatement();
	}
}

// BNF:8: EXPRSTAT      ::= [ASSIGN] ';'
asCScriptNode *asCParser::ParseExpressionStatement()
{
	asCScriptNode *node = CreateNode(snExpressionStatement);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( t.type == ttEndStatement )
	{
		node->UpdateSourcePos(t.pos, t.length);

		return node;
	}

	RewindTo(&t);

	node->AddChildLast(ParseAssignment());
	if( isSyntaxError ) return node;

	GetToken(&t);
	if( t.type != ttEndStatement )
	{
		Error(ExpectedToken(";"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	return node;
}

// BNF:8: SWITCH        ::= 'switch' '(' ASSIGN ')' '{' {CASE} '}'
asCScriptNode *asCParser::ParseSwitch()
{
	asCScriptNode *node = CreateNode(snSwitch);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( t.type != ttSwitch )
	{
		Error(ExpectedToken("switch"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	GetToken(&t);
	if( t.type != ttOpenParenthesis)
	{
		Error(ExpectedToken("("), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->AddChildLast(ParseAssignment());
	if( isSyntaxError ) return node;

	GetToken(&t);
	if( t.type != ttCloseParenthesis)
	{
		Error(ExpectedToken(")"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	GetToken(&t);
	if( t.type != ttStartStatementBlock )
	{
		Error(ExpectedToken("{"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	while( !isSyntaxError )
	{
		GetToken(&t);

		if( t.type == ttEndStatementBlock )
			break;

		RewindTo(&t);

		if( t.type != ttCase && t.type != ttDefault )
		{
			const char *tokens[] = {"case", "default"};
			Error(ExpectedOneOf(tokens, 2), &t);
			Error(InsteadFound(t), &t);
			return node;
		}

		node->AddChildLast(ParseCase());
		if( isSyntaxError ) return node;
	}

	if( t.type != ttEndStatementBlock )
	{
		Error(ExpectedToken("}"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	return node;
}

// BNF:9: CASE          ::= (('case' EXPR) | 'default') ':' {STATEMENT}
asCScriptNode *asCParser::ParseCase()
{
	asCScriptNode *node = CreateNode(snCase);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( t.type != ttCase && t.type != ttDefault )
	{
		Error(ExpectedTokens("case", "default"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	if(t.type == ttCase)
	{
		node->AddChildLast(ParseExpression());
	}

	GetToken(&t);
	if( t.type != ttColon )
	{
		Error(ExpectedToken(":"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	// Parse statements until we find either of }, case, default, and break
	GetToken(&t);
	RewindTo(&t);
	while( t.type != ttCase &&
		   t.type != ttDefault &&
		   t.type != ttEndStatementBlock &&
		   t.type != ttBreak )
	{
		if( IsVarDecl() )
			// Variable declarations are not allowed, but we parse it anyway to give a good error message
			node->AddChildLast(ParseDeclaration());
		else
			node->AddChildLast(ParseStatement());
		if( isSyntaxError ) return node;

		GetToken(&t);
		RewindTo(&t);
	}

	// If the case was ended with a break statement, add it to the node
	if( t.type == ttBreak )
		node->AddChildLast(ParseBreak());

	return node;
}

// BNF:8: IF            ::= 'if' '(' ASSIGN ')' STATEMENT ['else' STATEMENT]
asCScriptNode *asCParser::ParseIf()
{
	asCScriptNode *node = CreateNode(snIf);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( t.type != ttIf )
	{
		Error(ExpectedToken("if"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	GetToken(&t);
	if( t.type != ttOpenParenthesis)
	{
		Error(ExpectedToken("("), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->AddChildLast(ParseAssignment());
	if( isSyntaxError ) return node;

	GetToken(&t);
	if( t.type != ttCloseParenthesis)
	{
		Error(ExpectedToken(")"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->AddChildLast(ParseStatement());
	if( isSyntaxError ) return node;

	GetToken(&t);
	if( t.type != ttElse )
	{
		// No else statement return already
		RewindTo(&t);
		return node;
	}

	node->AddChildLast(ParseStatement());

	return node;
}

// BNF:8: TRY           ::= 'try' STATBLOCK 'catch' STATBLOCK
asCScriptNode *asCParser::ParseTryCatch()
{
	asCScriptNode *node = CreateNode(snTryCatch);
	if (node == 0) return 0;

	sToken t;
	GetToken(&t);
	if (t.type != ttTry)
	{
		Error(ExpectedToken("try"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	node->AddChildLast(ParseStatementBlock());
	if (isSyntaxError) return node;

	GetToken(&t);
	if (t.type != ttCatch)
	{
		Error(ExpectedToken("catch"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->AddChildLast(ParseStatementBlock());
	if (isSyntaxError) return node;

	return node;
}

// BNF:8: FOR           ::= 'for' '(' (VAR | EXPRSTAT) EXPRSTAT [ASSIGN {',' ASSIGN}] ')' STATEMENT
asCScriptNode *asCParser::ParseFor()
{
	asCScriptNode *node = CreateNode(snFor);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( t.type != ttFor )
	{
		Error(ExpectedToken("for"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	GetToken(&t);
	if( t.type != ttOpenParenthesis)
	{
		Error(ExpectedToken("("), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	if( IsVarDecl() )
		node->AddChildLast(ParseDeclaration());
	else
		node->AddChildLast(ParseExpressionStatement());
	if( isSyntaxError ) return node;

	node->AddChildLast(ParseExpressionStatement());
	if( isSyntaxError ) return node;

	GetToken(&t);
	if( t.type != ttCloseParenthesis)
	{
		RewindTo(&t);

		// Parse N increment statements separated by ,
		for(;;)
		{
			asCScriptNode *n = CreateNode(snExpressionStatement);
			if( n == 0 ) return 0;
			node->AddChildLast(n);
			n->AddChildLast(ParseAssignment());
			if( isSyntaxError ) return node;

			GetToken(&t);
			if( t.type == ttListSeparator )
				continue;
			else if( t.type == ttCloseParenthesis)
				break;
			else
			{
				const char *tokens[] = {",", ")"};
				Error(ExpectedOneOf(tokens, 2), &t);
				Error(InsteadFound(t), &t);
				return node;
			}
		}
	}

	node->AddChildLast(ParseStatement());

	return node;
}

// BNF:8: WHILE         ::= 'while' '(' ASSIGN ')' STATEMENT
asCScriptNode *asCParser::ParseWhile()
{
	asCScriptNode *node = CreateNode(snWhile);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( t.type != ttWhile )
	{
		Error(ExpectedToken("while"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	GetToken(&t);
	if( t.type != ttOpenParenthesis)
	{
		Error(ExpectedToken("("), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->AddChildLast(ParseAssignment());
	if( isSyntaxError ) return node;

	GetToken(&t);
	if( t.type != ttCloseParenthesis)
	{
		Error(ExpectedToken(")"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->AddChildLast(ParseStatement());

	return node;
}

// BNF:8: DOWHILE       ::= 'do' STATEMENT 'while' '(' ASSIGN ')' ';'
asCScriptNode *asCParser::ParseDoWhile()
{
	asCScriptNode *node = CreateNode(snDoWhile);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( t.type != ttDo )
	{
		Error(ExpectedToken("do"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	node->AddChildLast(ParseStatement());
	if( isSyntaxError ) return node;

	GetToken(&t);
	if( t.type != ttWhile )
	{
		Error(ExpectedToken("while"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	GetToken(&t);
	if( t.type != ttOpenParenthesis)
	{
		Error(ExpectedToken("("), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->AddChildLast(ParseAssignment());
	if( isSyntaxError ) return node;

	GetToken(&t);
	if( t.type != ttCloseParenthesis)
	{
		Error(ExpectedToken(")"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	GetToken(&t);
	if( t.type != ttEndStatement )
	{
		Error(ExpectedToken(";"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}
	node->UpdateSourcePos(t.pos, t.length);

	return node;
}

// BNF:8: RETURN        ::= 'return' [ASSIGN] ';'
asCScriptNode *asCParser::ParseReturn()
{
	asCScriptNode *node = CreateNode(snReturn);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( t.type != ttReturn )
	{
		Error(ExpectedToken("return"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	GetToken(&t);
	if( t.type == ttEndStatement )
	{
		node->UpdateSourcePos(t.pos, t.length);
		return node;
	}

	RewindTo(&t);

	node->AddChildLast(ParseAssignment());
	if( isSyntaxError ) return node;

	GetToken(&t);
	if( t.type != ttEndStatement )
	{
		Error(ExpectedToken(";"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	return node;
}

// BNF:8: BREAK         ::= 'break' ';'
asCScriptNode *asCParser::ParseBreak()
{
	asCScriptNode *node = CreateNode(snBreak);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( t.type != ttBreak )
	{
		Error(ExpectedToken("break"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	GetToken(&t);
	if( t.type != ttEndStatement )
	{
		Error(ExpectedToken(";"), &t);
		Error(InsteadFound(t), &t);
	}

	node->UpdateSourcePos(t.pos, t.length);

	return node;
}

// BNF:8: CONTINUE      ::= 'continue' ';'
asCScriptNode *asCParser::ParseContinue()
{
	asCScriptNode *node = CreateNode(snContinue);
	if( node == 0 ) return 0;

	sToken t;
	GetToken(&t);
	if( t.type != ttContinue )
	{
		Error(ExpectedToken("continue"), &t);
		Error(InsteadFound(t), &t);
		return node;
	}

	node->UpdateSourcePos(t.pos, t.length);

	GetToken(&t);
	if( t.type != ttEndStatement )
	{
		Error(ExpectedToken(";"), &t);
		Error(InsteadFound(t), &t);
	}

	node->UpdateSourcePos(t.pos, t.length);

	return node;
}

// TODO: typedef: Typedefs should accept complex types as well
// BNF:1: TYPEDEF       ::= 'typedef' PRIMTYPE IDENTIFIER ';'
asCScriptNode *asCParser::ParseTypedef()
{
	// Create the typedef node
	asCScriptNode *node = CreateNode(snTypedef);
	if( node == 0 ) return 0;

	sToken	token;

	GetToken(&token);
	if( token.type != ttTypedef)
	{
		Error(ExpectedToken(asCTokenizer::GetDefinition(ttTypedef)), &token);
		Error(InsteadFound(token), &token);
		return node;
	}

	node->SetToken(&token);
	node->UpdateSourcePos(token.pos, token.length);

	// Parse the base type
	GetToken(&token);
	RewindTo(&token);

	// Make sure it is a primitive type (except ttVoid)
	if( !IsRealType(token.type) || token.type == ttVoid )
	{
		asCString str;
		str.Format(TXT_UNEXPECTED_TOKEN_s, asCTokenizer::GetDefinition(token.type));
		Error(str, &token);
		return node;
	}

	node->AddChildLast(ParseRealType());
	node->AddChildLast(ParseIdentifier());

	// Check for the end of the typedef
	GetToken(&token);
	if( token.type != ttEndStatement )
	{
		RewindTo(&token);
		Error(ExpectedToken(asCTokenizer::GetDefinition(token.type)), &token);
		Error(InsteadFound(token), &token);
	}

	return node;
}

#endif

END_AS_NAMESPACE


