// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.


#ifndef RY_PD_PARSE_RULES_H
#define RY_PD_PARSE_RULES_H

#include "parse_node.h"

#define DECLARE_PARSE(nodename) CParseNode	*parse##nodename(CTokenizer &tokenizer);


DECLARE_PARSE(Main)
DECLARE_PARSE(File)
DECLARE_PARSE(Include)
DECLARE_PARSE(UsePch)
DECLARE_PARSE(Db)
DECLARE_PARSE(CppCode)
DECLARE_PARSE(Type)
DECLARE_PARSE(Class)
DECLARE_PARSE(Declaration)
DECLARE_PARSE(Enum)
DECLARE_PARSE(EnumSimpleValue)
DECLARE_PARSE(EnumRange)
DECLARE_PARSE(Dimension)
DECLARE_PARSE(LogMsg)
DECLARE_PARSE(LogContext)



/*
 * Parser Help Macros
 */

/*
 * Start node parsing without optional description
 * PARSE_START_NO_DESCRIPTION(Main, CParseNode)
 *
 *	...
 *
 * PARSE_END
 */
#define PARSE_START_NO_DESCRIPTION(nodename, nodetype)	\
CParseNode	*parse##nodename(CTokenizer &tokenizer)	\
{	\
	tokenizer.push();	\
	nodetype	*main = new nodetype();	\
	main->StartToken = tokenizer.currentToken();	\
	CParseNode	*parsed;	\
	parsed = NULL;

#define PARSE_END	\
	return main;	\
}


/*
 * Start node parsing with optional description
 * PARSE_START(Main, CParseNode)
 *
 *	...
 *
 * PARSE_END
 */
#define PARSE_START(nodename, nodetype)	\
CParseNode	*parse##nodename(CTokenizer &tokenizer)	\
{	\
	tokenizer.push();	\
	nodetype	*main = new nodetype();	\
	main->StartToken = tokenizer.currentToken();	\
	CParseNode	*parsed;	\
	parsed = NULL;	\
	PARSE_OPT_KEYWORD_IN(Description, Description)


/*
 * Lock parsing to the current token. If parse fail later, this will cause
 * parser to generate an error after this token.
 * PARSE_KEYWORD(Colon)
 * PARSE_MARK
 */
#define PARSE_MARK	tokenizer.leaveMark();



/*
 * Force the parser to fail and leave current node
 */
#define PARSE_FAIL \
	{	\
		delete main;	\
		tokenizer.pop();	\
		return NULL;	\
	}

/*
 * Parse a given token
 * PARSE_KEYWORD(Extern)
 */
#define PARSE_KEYWORD(keyword) \
	{	\
		if (tokenizer.end() || tokenizer.current() != Token##keyword)	\
			PARSE_FAIL	\
		tokenizer.next();	\
	}

/*
 * Parse a token and store value into a string
 * PARSE_KEYWORD_IN_TEMP(Identifier, Name)
 */
#define PARSE_KEYWORD_IN_TEMP(keyword, temp) \
	{	\
		if (tokenizer.end() || tokenizer.current() != Token##keyword)	\
			PARSE_FAIL	\
		temp = tokenizer.get(tokenizer.currentToken());	\
		tokenizer.next();	\
	}

/*
 * Parse a token and store value into a node string attribute
 * PARSE_KEYWORD_IN(Identifier, Name) where Name is an attribute of the current node
 */
#define PARSE_KEYWORD_IN(keyword, savein) PARSE_KEYWORD_IN_TEMP(keyword, main->savein)

/*
 * Parse a token and push value into a vector of string of the current node
 * PARSE_ADD_KEYWORD_IN(Identifier, Names) where Names is a vector<string> of the node
 */
#define PARSE_ADD_KEYWORD_IN(keyword, savein) \
	{	\
		if (tokenizer.end() || tokenizer.current() != Token##keyword)	\
			PARSE_FAIL	\
		main->savein.push_back(tokenizer.get(tokenizer.currentToken()));	\
		tokenizer.next();	\
	}

/*
 * Parse a couple of 2 tokens and push them as string into a vector of pair of string of the current node
 * PARSE_ADD_2_KEYWORD_IN(Identifier, Identifier, Prototype) where Prototype is a vector<pair<string, string> > of the node
 */
#define PARSE_ADD_2_KEYWORD_IN(keyword1, keyword2, savein) \
	{	\
		if (tokenizer.end() || tokenizer.current() != Token##keyword1)	\
			PARSE_FAIL	\
		CTokenizer::CToken	t1 = tokenizer.currentToken();	\
		tokenizer.next();	\
		if (tokenizer.end() || tokenizer.current() != Token##keyword2)	\
			PARSE_FAIL	\
		CTokenizer::CToken	t2 = tokenizer.currentToken();	\
		tokenizer.next();	\
		main->savein.push_back(std::make_pair<std::string, std::string>(tokenizer.get(t1), tokenizer.get(t2)));	\
	}

/*
 * Parse a string into a attribute of the node
 * PARSE_STRING(FileName)
 */
#define PARSE_STRING(savein)		PARSE_KEYWORD_IN(String, savein)

/*
 * Parse a scoped identifier and store value into an attribute of the node
 * PARSE_SCOPE_IDF(ScopedType) where a scoped identifier may be of the form 'Identifier' or 'Identifier::Identifier'
 */
#define PARSE_SCOPE_IDF(savein)	\
	{	\
		if (tokenizer.end() || (tokenizer.current() != TokenIdentifier && tokenizer.current() != TokenScopedIdentifier))	\
			PARSE_FAIL	\
		main->savein = tokenizer.get(tokenizer.currentToken());	\
		tokenizer.next();	\
	}


/*
 * Parse a single Identifier into a node'a attribute
 * PARSE_IDENTIFIER(Name)
 */
#define PARSE_IDENTIFIER(savein)	PARSE_KEYWORD_IN(Identifier, savein)

/*
 * Parse a single Identifier and push it to a vector<string>
 * PARSE_ADD_IDENTIFIER(Names)
 */
#define PARSE_ADD_IDENTIFIER(savein)	PARSE_ADD_KEYWORD_IN(Identifier, savein)

/*
 * Parse a integer and store value (as int)
 * PARSE_INT(DefaultIntValue)
 */
#define PARSE_INT(savein)	\
	{	\
		if (tokenizer.end() || tokenizer.current() != TokenNumber)	\
			PARSE_FAIL	\
		NLMISC::fromString(tokenizer.get(tokenizer.currentToken()), main->savein);	\
		tokenizer.next();	\
	}

/*
 * Parse a value, which may be a string or a number (float/int), and store it as a string
 * PARSE_VALUE(DefaultValue)
 */
#define PARSE_VALUE(savein) \
	{	\
		if (tokenizer.end() || (tokenizer.current() != TokenIdentifier && tokenizer.current() != TokenNumber && tokenizer.current() != TokenString))	\
			PARSE_FAIL	\
		main->savein = tokenizer.get(tokenizer.currentToken());	\
		tokenizer.next();	\
	}




/*
 * Start a bloc of alternative bloc to examine. Parser will examine first alternative, and if parsing fails
 * it will examine the second alternative. Usually alternative bloc is ended by a PARSE_FAIL so parsing may examine
 * previous following rules...
 * PARSE_ALTERNATIVE(Include)
 * PARSE_ALTERNATIVE(Type)
 * PARSE_ALTERNATIVE(Class)
 * PARSE_FAIL					// parsing fails if neither Include can be parsed nor Type nor Class
 */
#define PARSE_ALTERNATIVE(nodename) \
	if ( (parsed = parse##nodename(tokenizer)) )	\
	{	\
		parsed->Parent = main;	\
		main->Nodes.push_back(parsed);	\
	}	\
	else

/*
 * End a bloc of alternative without failing, usually to allow parsing of optionnal node blocs
 */
#define PARSE_END_ALTERNATIVE() \
	{}

/*
 * Parse a node. This is not an alternative, if node parsing fails, current node parsing fails
 * PARSE_NODE(Include)
 */
#define PARSE_NODE(nodename, savein) \
	if ( (main->savein = parse##nodename(tokenizer)) )	\
	{	\
		main->savein->Parent = main;	\
	}	\
	else	\
		PARSE_FAIL




/*
 * Start a bloc with '{'
 */
#define PARSE_START_BLOC	\
	PARSE_KEYWORD(OpenBrace)	\
	while (!tokenizer.end() && tokenizer.current() != TokenCloseBrace)	\
	{

/*
 * End a bloc with '}'
 */
#define PARSE_END_BLOC	\
	}	\
	PARSE_KEYWORD(CloseBrace)



/*
 * Parse a list of tokens (unknown yet) separated by special tokens
 * PARSE_START_LIST(OpenParenthesis, CloseParenthesis)
 *   PARSE_ADD_KEYWORD_IN(Identifier, Names)
 * PARSE_END_LIST(CloseParenthesis, Comma)
 */
#define PARSE_START_LIST(starttoken, endtoken)	\
	PARSE_KEYWORD(starttoken)	\
	while (!tokenizer.end() && tokenizer.current() != Token##endtoken)	\
	{

#define PARSE_END_LIST(endtoken, separator)	\
		if (tokenizer.end() || tokenizer.current() != Token##separator)	\
			break;	\
		tokenizer.next();	\
	}	\
	PARSE_KEYWORD(endtoken)


/*
 * Parse an optionnal list, see also PARSE_START_LIST/PARSE_END_LIST
 */
#define PARSE_OPT_LIST(starttoken, endtoken)	\
	if (!tokenizer.end() && tokenizer.current() == Token##starttoken)	\
	{	\
		PARSE_START_LIST(starttoken, endtoken)

#define PARSE_END_OPT_LIST(endtoken, separator)	\
		PARSE_END_LIST(endtoken, separator)	\
	}


/*
 * Repeat parsing between PARSE_OPT_BEFORE and PARSE_END_OPT_BEFORE while next token is not reached
 * To be used with PARSE_OPT_IN
 * PARSE_OPT_BEFORE(Class)
 *     PARSE_OPT_IN(Mapped, MappedFlag);
 *     PARSE_OPT_IN(Derived, DerivedFlag);
 * PARSE_END_OPT_BEFORE()
 */
#define PARSE_OPT_BEFORE(token)	\
	while (!tokenizer.end() && tokenizer.current() != Token##token)	\
	{	\
		switch (tokenizer.current())	\
		{

#define PARSE_OPT_BEFORE_2(token1, token2)	\
	while (!tokenizer.end() && tokenizer.current() != Token##token1 && tokenizer.current() != Token##token2)	\
	{	\
		switch (tokenizer.current())	\
		{

#define PARSE_OPT_IN(token, flag)	\
		case Token##token:	\
			if (!main->flag)	\
			{	\
				main->flag = true;	\
			}	\
			else	\
			{	\
				PARSE_FAIL	\
			}	\
			break;

#define PARSE_END_OPT_BEFORE()	\
		default:	\
			PARSE_FAIL	\
			break;	\
		}	\
		tokenizer.next();	\
	}


/*
 * Parse a list of keywork separated by a given token, and store them into a vector<string>
 * see also PARSE_ADD_KEYWORD_IN
 */
#define PARSE_LIST_KEYWORDS(keyword, savein, separator)	\
	PARSE_ADD_KEYWORD_IN(keyword, savein)	\
	while (!tokenizer.end() && tokenizer.current() == Token##separator)	\
	{	\
		tokenizer.next();	\
		PARSE_ADD_KEYWORD_IN(keyword, savein)	\
	}

/*
 * PArse a list of couple of 2 keywords and store them into a vector<pair<string, string> >
 * see also PARSE_ADD_2_KEYWORD_IN
 */
#define PARSE_LIST_2_KEYWORDS(keyword1, keyword2, savein, separator)	\
	PARSE_ADD_2_KEYWORD_IN(keyword1, keyword2, savein)	\
	while (!tokenizer.end() && tokenizer.current() == Token##separator)	\
	{	\
		tokenizer.next();	\
		PARSE_ADD_2_KEYWORD_IN(keyword1, keyword2, savein)	\
	}

#define PARSE_LIST_IDENTIFIERS(starttoken, separator, endtoken, savein)	\
		PARSE_START_LIST(starttoken, endtoken)	\
			PARSE_ADD_IDENTIFIER(savein)	\
		PARSE_END_LIST(endtoken, separator)





#define PARSE_OPT(opttoken)	\
	if (!tokenizer.end() && tokenizer.current() == Token##opttoken)	\
	{	\
		tokenizer.next();	\

#define PARSE_NEXT_OPT(opttoken)	\
	}	\
	else if (!tokenizer.end() && tokenizer.current() == Token##opttoken)	\
	{	\
		tokenizer.next();	\

#define PARSE_LAST_OPT	\
	}	\
	else	\
	{

#define PARSE_END_OPT	\
	}



#define PARSE_OPT_KEYWORD_IN(keyword, savein) \
	{	\
		if (!tokenizer.end() && tokenizer.current() == Token##keyword)	\
		{	\
			main->savein = tokenizer.get(tokenizer.currentToken());	\
			tokenizer.next();	\
		}	\
	}

#endif