Merge branch 'lua_versions' into main/rendor-staging
[ryzomcore.git] / ryzom / tools / pd_parser / templatizer.cpp
blob21eb9d765a835731ff2dae36c9bc3dd78ec6c616
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "templatizer.h"
21 bool isBlocStart(CTemplatizerParser t) { return (t.isValid() && t[0] == '{' && t[1] == '{'); }
22 bool isBlocEnd(CTemplatizerParser t) { return (t.isValid() && t[0] == '}' && t[1] == '}'); }
24 enum TTemplatizerToken
26 BlocStart,
27 BlocEnd,
28 OpenParenth,
29 CloseParenth,
30 Equal,
31 Comma,
32 Arobace,
33 Dollar,
34 Quote,
35 CommentStart,
36 CommentEnd,
37 Identifier,
38 ListIdentifier,
40 Unknown
43 struct SToken
45 TTemplatizerToken Token;
46 const char* Text;
49 SToken SimpleTokens[] =
51 { BlocStart, "{" },
52 { BlocEnd, "}" },
53 { OpenParenth, "(" },
54 { CloseParenth, ")" },
55 { Equal, "=" },
56 { Arobace, "@" },
57 { Dollar, "$" },
58 { Comma, "," },
59 { Quote, "\"" },
60 { CommentStart, "/*" },
61 { CommentEnd, "*/" },
64 CTemplatizerParser skipSpace(CTemplatizerParser t)
66 while (t.isValid() && *t != '\0' && isspace(*t))
67 ++t;
68 return t;
71 const char* skipSpace(const char* t)
73 while (t != NULL && *t != '\0' && isspace(*t))
74 ++t;
75 return t;
78 CTemplatizerParser match(const char* keyword, CTemplatizerParser match)
80 while (*keyword != '\0' && *match != '\0' && *keyword == *match)
82 ++keyword;
83 ++match;
86 return *keyword == '\0' ? match : CTemplatizerParser();
89 TTemplatizerToken getToken(CTemplatizerParser& t, bool skipspc, std::string* value = NULL)
91 if (skipspc)
92 t = skipSpace(t);
94 if (!t.isValid())
95 return Unknown;
97 uint i;
98 CTemplatizerParser result;
99 for (i=0; i<sizeof(SimpleTokens)/sizeof(SimpleTokens[0]); ++i)
101 result = match(SimpleTokens[i].Text, t);
102 if (result.isValid())
104 t = result;
105 return SimpleTokens[i].Token;
109 if (isalpha(*t))
111 TTemplatizerToken retToken = Identifier;
113 if (value != NULL)
114 value->clear();
117 while (isalpha(*t))
119 if (value != NULL)
120 *value += *t;
121 ++t;
124 t = skipSpace(t);
126 if (*t != EnvSeparator)
127 break;
129 retToken = ListIdentifier;
131 if (value != NULL)
132 *value += *t;
133 ++t;
135 while (true);
137 return retToken;
140 return Unknown;
143 bool popToken(CTemplatizerParser& t, TTemplatizerToken token, bool skipspc, std::string* value = NULL)
145 CTemplatizerParser save = t;
146 if (getToken(save, skipspc, value) == token)
148 t = save;
149 return true;
152 return false;
155 bool isNextToken(CTemplatizerParser t, TTemplatizerToken token, bool skipspc, std::string* value = NULL)
157 return getToken(t, skipspc, value) == token;
164 * Destructor
166 CTemplatizerEnv::~CTemplatizerEnv()
168 clear();
171 // Clear Env
172 void CTemplatizerEnv::clear()
174 TEnvMap::iterator ite;
175 for (ite=Envs.begin(); ite!=Envs.end(); ++ite)
176 delete (*ite).second;
178 TValueMap::iterator itv;
179 for (itv=Values.begin(); itv!=Values.end(); ++itv)
180 delete (*itv).second;
182 Envs.clear();
183 Values.clear();
188 * Evaluate string (string replacement)
190 std::string CTemplatizerEnv::eval(const std::string& text)
192 std::string res;
193 const char* ptr = text.c_str();
195 while (*ptr != '\0')
197 if (ptr[0] == '$' && ptr[1] == '(')
199 ptr += 2;
200 std::string var;
202 ptr = skipSpace(ptr);
204 while (isalpha(*ptr) || *ptr == '/' || *ptr == '.')
205 var += *(ptr++);
207 while (*ptr != '\0' && *ptr != ')')
208 ++ptr;
209 if (*ptr == ')')
210 ++ptr;
212 res += get(var);
214 else if (*ptr != '\r')
216 res += *(ptr++);
220 return res;
225 * Constructor
227 ITemplatizerBloc::ITemplatizerBloc()
232 * Destructor
234 ITemplatizerBloc::~ITemplatizerBloc()
236 uint i;
237 for (i=0; i<Blocs.size(); ++i)
238 delete Blocs[i];
240 TParamMap::iterator it;
241 for (it=Params.begin(); it!=Params.end(); ++it)
242 delete (*it).second;
247 * Constructor
249 CTemplatizer::CTemplatizer()
251 RootBloc = NULL;
252 RootEnv = NULL;
257 * Destructor
259 CTemplatizer::~CTemplatizer()
261 if (RootBloc != NULL)
262 delete RootBloc;
263 if (RootEnv != NULL)
264 delete RootEnv;
271 * Build templatizer from text
273 bool CTemplatizer::build(const char* text)
275 CTemplatizerParser parser(text);
276 RootBloc = ITemplatizerBloc::parseBloc(parser);
277 RootEnv = new CTemplatizerEnv(NULL);
279 return (RootBloc != NULL);
284 * Evaluate template and render to string
286 std::string CTemplatizer::eval()
288 if (RootBloc != NULL && RootEnv != NULL)
289 return RootBloc->eval(RootEnv);
290 else
291 return "";
302 * Parse bloc
304 ITemplatizerBloc* ITemplatizerBloc::parseBloc(CTemplatizerParser& ptr)
306 std::string blocType;
307 ITemplatizerBloc* bloc = NULL;
309 if (popToken(ptr, Identifier, true, &blocType) || popToken(ptr, ListIdentifier, true, &blocType))
311 if (blocType == "root") bloc = new CTemplatizerRootBloc();
312 else if (blocType == "sub") bloc = new CTemplatizerSubBloc();
313 else if (blocType == "loop") bloc = new CTemplatizerLoopBloc();
314 else if (blocType == "ifdefenv") bloc = new CTemplatizerIfDefEnvBloc();
315 else if (blocType == "ifdef") bloc = new CTemplatizerIfDefBloc();
316 else if (blocType == "ifnotdefenv") bloc = new CTemplatizerIfNotDefEnvBloc();
317 else if (blocType == "ifnotdef") bloc = new CTemplatizerIfNotDefBloc();
318 else if (blocType == "switch") bloc = new CTemplatizerSwitchBloc();
319 else if (blocType == "file") bloc = new CTemplatizerFileBloc();
320 else if (blocType == "set") bloc = new CTemplatizerSetBloc();
321 else if (blocType == "append") bloc = new CTemplatizerAppendBloc();
322 else if (blocType == "define") bloc = new CTemplatizerDefineBloc();
323 else if (blocType == "if") bloc = new CTemplatizerIfBloc();
324 else if (blocType == "ifnot") bloc = new CTemplatizerIfNotBloc();
325 else if (blocType == "join") bloc = new CTemplatizerJoinBloc();
326 else if (blocType == "class") bloc = new CTemplatizerClassBloc();
327 else if (blocType == "object") bloc = new CTemplatizerObjectBloc();
328 else if (blocType == "ref") bloc = new CTemplatizerReferenceBloc();
329 else if (blocType == "refenv") bloc = new CTemplatizerRefEnvBloc();
330 else if (blocType == "breakpoint") bloc = new CTemplatizerBreakpointBloc();
331 else bloc = new CTemplatizerUserFunctionBloc(blocType);
333 if (bloc == NULL)
335 nlwarning("Templatizer: failed to decode bloc '%s' at line %d", blocType.c_str(), ptr.getLine());
336 return NULL;
339 ptr = bloc->parseHeader(ptr);
341 if (!ptr.isValid())
343 nlwarning("Templatizer: failed to decode header of bloc '%s' at line %d", blocType.c_str(), ptr.getLine());
344 delete bloc;
345 return NULL;
348 if (bloc->hasInternal())
350 if (!popToken(ptr, BlocStart, true))
352 nlwarning("Templatizer: failed to decode start of bloc '%s' at line %d", blocType.c_str(), ptr.getLine());
353 delete bloc;
354 return NULL;
357 ptr = bloc->parseInternal(ptr);
359 if (!ptr.isValid())
361 nlwarning("Templatizer: failed to parse bloc '%s' at line %d", blocType.c_str(), ptr.getLine());
362 delete bloc;
363 return NULL;
366 if (!popToken(ptr, BlocEnd, true))
368 nlwarning("Templatizer: failed to decode end of bloc '%s' at line %d", blocType.c_str(), ptr.getLine());
369 delete bloc;
370 return NULL;
374 else if (isNextToken(ptr, CommentStart, true))
376 bloc = new CTemplatizerCommentBloc();
377 ptr = bloc->parseInternal(ptr);
378 if (!ptr.isValid())
379 nlwarning("Templatizer: failed to parse bloc 'Comment' at line %d", ptr.getLine());
381 else if (isNextToken(ptr, Quote, true))
383 bloc = new CTemplatizerTextBloc();
384 ptr = bloc->parseInternal(ptr);
385 if (!ptr.isValid())
386 nlwarning("Templatizer: failed to parse bloc 'Text' at line %d", ptr.getLine());
389 if (!ptr.isValid())
391 delete bloc;
392 return NULL;
395 return bloc;
399 * Parse bloc header
401 CTemplatizerParser ITemplatizerBloc::parseHeader(CTemplatizerParser ptr)
403 if (popToken(ptr, OpenParenth, true))
405 uint currentDefArg = 0;
406 const char** args = getDefParamList();
408 if (popToken(ptr, CloseParenth, true))
409 return ptr;
413 std::string paramName;
414 if (!popToken(ptr, Identifier, true, &paramName))
416 if (args == NULL || args[currentDefArg] == NULL)
418 ptr.invalidate();
419 return ptr;
422 paramName = args[currentDefArg++];
424 else
426 if (!popToken(ptr, Equal, true))
428 ptr.invalidate();
429 return ptr;
433 ITemplatizerBloc* bloc = parseBloc(ptr);
435 if (bloc == NULL)
437 ptr.invalidate();
438 return ptr;
441 Params[paramName] = bloc;
443 if (!popToken(ptr, Comma, true))
444 break;
446 while (true);
448 if (!popToken(ptr, CloseParenth, true))
450 ptr.invalidate();
451 return ptr;
455 return ptr;
460 * Parse bloc internal data
462 CTemplatizerParser ITemplatizerBloc::parseInternal(CTemplatizerParser ptr)
464 ITemplatizerBloc* bloc = NULL;
467 bloc = parseBloc(ptr);
469 if (bloc != NULL)
470 Blocs.push_back(bloc);
472 while (bloc != NULL && *ptr != '\0' && !isBlocEnd(ptr));
474 return ptr;
479 * Parse bloc internal data
481 CTemplatizerParser CTemplatizerTextBloc::parseInternal(CTemplatizerParser ptr)
483 Text.clear();
485 ptr = skipSpace(ptr);
487 if (*ptr != '"')
488 return CTemplatizerParser();
490 ++ptr;
492 while (*ptr != '\0' && *ptr != '"')
494 if (*ptr == '\\' && ptr[1] != '\0')
496 ++ptr;
497 switch (*ptr)
499 case 'n':
500 Text += '\n';
501 break;
502 case 't':
503 Text += '\t';
504 break;
505 case '\r':
506 ++ptr;
507 if (*ptr == '\n')
508 ++ptr;
509 break;
510 case '\n':
511 ++ptr;
512 if (*ptr == '\r')
513 ++ptr;
514 break;
515 default:
516 Text += *ptr;
517 break;
519 ++ptr;
521 else
523 if (*ptr != '\r')
524 Text += *ptr;
525 ++ptr;
528 if (*ptr != '"')
529 return CTemplatizerParser();
531 return ++ptr;
536 * Parse bloc internal data
538 CTemplatizerParser CTemplatizerCommentBloc::parseInternal(CTemplatizerParser ptr)
540 ptr = skipSpace(ptr);
542 if (!popToken(ptr, CommentStart, true))
543 return NULL;
545 while (*ptr != '\0' && !isNextToken(ptr, CommentEnd, false))
546 ++ptr;
548 if (!popToken(ptr, CommentEnd, false))
550 ptr.invalidate();
551 return ptr;
554 return ptr;