// Ryzom - MMORPG Framework // 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 . #include "templatizer.h" bool isBlocStart(CTemplatizerParser t) { return (t.isValid() && t[0] == '{' && t[1] == '{'); } bool isBlocEnd(CTemplatizerParser t) { return (t.isValid() && t[0] == '}' && t[1] == '}'); } enum TTemplatizerToken { BlocStart, BlocEnd, OpenParenth, CloseParenth, Equal, Comma, Arobace, Dollar, Quote, CommentStart, CommentEnd, Identifier, ListIdentifier, Unknown }; struct SToken { TTemplatizerToken Token; const char* Text; }; SToken SimpleTokens[] = { { BlocStart, "{" }, { BlocEnd, "}" }, { OpenParenth, "(" }, { CloseParenth, ")" }, { Equal, "=" }, { Arobace, "@" }, { Dollar, "$" }, { Comma, "," }, { Quote, "\"" }, { CommentStart, "/*" }, { CommentEnd, "*/" }, }; CTemplatizerParser skipSpace(CTemplatizerParser t) { while (t.isValid() && *t != '\0' && isspace(*t)) ++t; return t; } const char* skipSpace(const char* t) { while (t != NULL && *t != '\0' && isspace(*t)) ++t; return t; } CTemplatizerParser match(const char* keyword, CTemplatizerParser match) { while (*keyword != '\0' && *match != '\0' && *keyword == *match) { ++keyword; ++match; } return *keyword == '\0' ? match : CTemplatizerParser(); } TTemplatizerToken getToken(CTemplatizerParser& t, bool skipspc, std::string* value = NULL) { if (skipspc) t = skipSpace(t); if (!t.isValid()) return Unknown; uint i; CTemplatizerParser result; for (i=0; ieval(RootEnv); else return ""; } /* * Parse bloc */ ITemplatizerBloc* ITemplatizerBloc::parseBloc(CTemplatizerParser& ptr) { std::string blocType; ITemplatizerBloc* bloc = NULL; if (popToken(ptr, Identifier, true, &blocType) || popToken(ptr, ListIdentifier, true, &blocType)) { if (blocType == "root") bloc = new CTemplatizerRootBloc(); else if (blocType == "sub") bloc = new CTemplatizerSubBloc(); else if (blocType == "loop") bloc = new CTemplatizerLoopBloc(); else if (blocType == "ifdefenv") bloc = new CTemplatizerIfDefEnvBloc(); else if (blocType == "ifdef") bloc = new CTemplatizerIfDefBloc(); else if (blocType == "ifnotdefenv") bloc = new CTemplatizerIfNotDefEnvBloc(); else if (blocType == "ifnotdef") bloc = new CTemplatizerIfNotDefBloc(); else if (blocType == "switch") bloc = new CTemplatizerSwitchBloc(); else if (blocType == "file") bloc = new CTemplatizerFileBloc(); else if (blocType == "set") bloc = new CTemplatizerSetBloc(); else if (blocType == "append") bloc = new CTemplatizerAppendBloc(); else if (blocType == "define") bloc = new CTemplatizerDefineBloc(); else if (blocType == "if") bloc = new CTemplatizerIfBloc(); else if (blocType == "ifnot") bloc = new CTemplatizerIfNotBloc(); else if (blocType == "join") bloc = new CTemplatizerJoinBloc(); else if (blocType == "class") bloc = new CTemplatizerClassBloc(); else if (blocType == "object") bloc = new CTemplatizerObjectBloc(); else if (blocType == "ref") bloc = new CTemplatizerReferenceBloc(); else if (blocType == "refenv") bloc = new CTemplatizerRefEnvBloc(); else if (blocType == "breakpoint") bloc = new CTemplatizerBreakpointBloc(); else bloc = new CTemplatizerUserFunctionBloc(blocType); if (bloc == NULL) { nlwarning("Templatizer: failed to decode bloc '%s' at line %d", blocType.c_str(), ptr.getLine()); return NULL; } ptr = bloc->parseHeader(ptr); if (!ptr.isValid()) { nlwarning("Templatizer: failed to decode header of bloc '%s' at line %d", blocType.c_str(), ptr.getLine()); delete bloc; return NULL; } if (bloc->hasInternal()) { if (!popToken(ptr, BlocStart, true)) { nlwarning("Templatizer: failed to decode start of bloc '%s' at line %d", blocType.c_str(), ptr.getLine()); delete bloc; return NULL; } ptr = bloc->parseInternal(ptr); if (!ptr.isValid()) { nlwarning("Templatizer: failed to parse bloc '%s' at line %d", blocType.c_str(), ptr.getLine()); delete bloc; return NULL; } if (!popToken(ptr, BlocEnd, true)) { nlwarning("Templatizer: failed to decode end of bloc '%s' at line %d", blocType.c_str(), ptr.getLine()); delete bloc; return NULL; } } } else if (isNextToken(ptr, CommentStart, true)) { bloc = new CTemplatizerCommentBloc(); ptr = bloc->parseInternal(ptr); if (!ptr.isValid()) nlwarning("Templatizer: failed to parse bloc 'Comment' at line %d", ptr.getLine()); } else if (isNextToken(ptr, Quote, true)) { bloc = new CTemplatizerTextBloc(); ptr = bloc->parseInternal(ptr); if (!ptr.isValid()) nlwarning("Templatizer: failed to parse bloc 'Text' at line %d", ptr.getLine()); } if (!ptr.isValid()) { delete bloc; return NULL; } return bloc; } /* * Parse bloc header */ CTemplatizerParser ITemplatizerBloc::parseHeader(CTemplatizerParser ptr) { if (popToken(ptr, OpenParenth, true)) { uint currentDefArg = 0; const char** args = getDefParamList(); if (popToken(ptr, CloseParenth, true)) return ptr; do { std::string paramName; if (!popToken(ptr, Identifier, true, ¶mName)) { if (args == NULL || args[currentDefArg] == NULL) { ptr.invalidate(); return ptr; } paramName = args[currentDefArg++]; } else { if (!popToken(ptr, Equal, true)) { ptr.invalidate(); return ptr; } } ITemplatizerBloc* bloc = parseBloc(ptr); if (bloc == NULL) { ptr.invalidate(); return ptr; } Params[paramName] = bloc; if (!popToken(ptr, Comma, true)) break; } while (true); if (!popToken(ptr, CloseParenth, true)) { ptr.invalidate(); return ptr; } } return ptr; } /* * Parse bloc internal data */ CTemplatizerParser ITemplatizerBloc::parseInternal(CTemplatizerParser ptr) { ITemplatizerBloc* bloc = NULL; do { bloc = parseBloc(ptr); if (bloc != NULL) Blocs.push_back(bloc); } while (bloc != NULL && *ptr != '\0' && !isBlocEnd(ptr)); return ptr; } /* * Parse bloc internal data */ CTemplatizerParser CTemplatizerTextBloc::parseInternal(CTemplatizerParser ptr) { Text = ""; ptr = skipSpace(ptr); if (*ptr != '"') return CTemplatizerParser(); ++ptr; while (*ptr != '\0' && *ptr != '"') { if (*ptr == '\\' && ptr[1] != '\0') { ++ptr; switch (*ptr) { case 'n': Text += '\n'; break; case 't': Text += '\t'; break; case '\r': ++ptr; if (*ptr == '\n') ++ptr; break; case '\n': ++ptr; if (*ptr == '\r') ++ptr; break; default: Text += *ptr; break; } ++ptr; } else { if (*ptr != '\r') Text += *ptr; ++ptr; } } if (*ptr != '"') return CTemplatizerParser(); return ++ptr; } /* * Parse bloc internal data */ CTemplatizerParser CTemplatizerCommentBloc::parseInternal(CTemplatizerParser ptr) { ptr = skipSpace(ptr); if (!popToken(ptr, CommentStart, true)) return NULL; while (*ptr != '\0' && !isNextToken(ptr, CommentEnd, false)) ++ptr; if (!popToken(ptr, CommentEnd, false)) { ptr.invalidate(); return ptr; } return ptr; }