416 lines
10 KiB
C++
416 lines
10 KiB
C++
// 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
|