khanat-opennel-code/code/ryzom/tools/pd_parser/parser_rules.h

417 lines
10 KiB
C
Raw Normal View History

2014-11-19 15:20:14 +00:00
// 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