1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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
45 TTemplatizerToken Token
;
49 SToken SimpleTokens
[] =
54 { CloseParenth
, ")" },
60 { CommentStart
, "/*" },
64 CTemplatizerParser
skipSpace(CTemplatizerParser t
)
66 while (t
.isValid() && *t
!= '\0' && isspace(*t
))
71 const char* skipSpace(const char* t
)
73 while (t
!= NULL
&& *t
!= '\0' && isspace(*t
))
78 CTemplatizerParser
match(const char* keyword
, CTemplatizerParser match
)
80 while (*keyword
!= '\0' && *match
!= '\0' && *keyword
== *match
)
86 return *keyword
== '\0' ? match
: CTemplatizerParser();
89 TTemplatizerToken
getToken(CTemplatizerParser
& t
, bool skipspc
, std::string
* value
= NULL
)
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())
105 return SimpleTokens
[i
].Token
;
111 TTemplatizerToken retToken
= Identifier
;
126 if (*t
!= EnvSeparator
)
129 retToken
= ListIdentifier
;
143 bool popToken(CTemplatizerParser
& t
, TTemplatizerToken token
, bool skipspc
, std::string
* value
= NULL
)
145 CTemplatizerParser save
= t
;
146 if (getToken(save
, skipspc
, value
) == token
)
155 bool isNextToken(CTemplatizerParser t
, TTemplatizerToken token
, bool skipspc
, std::string
* value
= NULL
)
157 return getToken(t
, skipspc
, value
) == token
;
166 CTemplatizerEnv::~CTemplatizerEnv()
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
;
188 * Evaluate string (string replacement)
190 std::string
CTemplatizerEnv::eval(const std::string
& text
)
193 const char* ptr
= text
.c_str();
197 if (ptr
[0] == '$' && ptr
[1] == '(')
202 ptr
= skipSpace(ptr
);
204 while (isalpha(*ptr
) || *ptr
== '/' || *ptr
== '.')
207 while (*ptr
!= '\0' && *ptr
!= ')')
214 else if (*ptr
!= '\r')
227 ITemplatizerBloc::ITemplatizerBloc()
234 ITemplatizerBloc::~ITemplatizerBloc()
237 for (i
=0; i
<Blocs
.size(); ++i
)
240 TParamMap::iterator it
;
241 for (it
=Params
.begin(); it
!=Params
.end(); ++it
)
249 CTemplatizer::CTemplatizer()
259 CTemplatizer::~CTemplatizer()
261 if (RootBloc
!= NULL
)
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
);
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
);
335 nlwarning("Templatizer: failed to decode bloc '%s' at line %d", blocType
.c_str(), ptr
.getLine());
339 ptr
= bloc
->parseHeader(ptr
);
343 nlwarning("Templatizer: failed to decode header of bloc '%s' at line %d", blocType
.c_str(), ptr
.getLine());
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());
357 ptr
= bloc
->parseInternal(ptr
);
361 nlwarning("Templatizer: failed to parse bloc '%s' at line %d", blocType
.c_str(), ptr
.getLine());
366 if (!popToken(ptr
, BlocEnd
, true))
368 nlwarning("Templatizer: failed to decode end of bloc '%s' at line %d", blocType
.c_str(), ptr
.getLine());
374 else if (isNextToken(ptr
, CommentStart
, true))
376 bloc
= new CTemplatizerCommentBloc();
377 ptr
= bloc
->parseInternal(ptr
);
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
);
386 nlwarning("Templatizer: failed to parse bloc 'Text' at line %d", ptr
.getLine());
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))
413 std::string paramName
;
414 if (!popToken(ptr
, Identifier
, true, ¶mName
))
416 if (args
== NULL
|| args
[currentDefArg
] == NULL
)
422 paramName
= args
[currentDefArg
++];
426 if (!popToken(ptr
, Equal
, true))
433 ITemplatizerBloc
* bloc
= parseBloc(ptr
);
441 Params
[paramName
] = bloc
;
443 if (!popToken(ptr
, Comma
, true))
448 if (!popToken(ptr
, CloseParenth
, true))
460 * Parse bloc internal data
462 CTemplatizerParser
ITemplatizerBloc::parseInternal(CTemplatizerParser ptr
)
464 ITemplatizerBloc
* bloc
= NULL
;
467 bloc
= parseBloc(ptr
);
470 Blocs
.push_back(bloc
);
472 while (bloc
!= NULL
&& *ptr
!= '\0' && !isBlocEnd(ptr
));
479 * Parse bloc internal data
481 CTemplatizerParser
CTemplatizerTextBloc::parseInternal(CTemplatizerParser ptr
)
485 ptr
= skipSpace(ptr
);
488 return CTemplatizerParser();
492 while (*ptr
!= '\0' && *ptr
!= '"')
494 if (*ptr
== '\\' && ptr
[1] != '\0')
529 return CTemplatizerParser();
536 * Parse bloc internal data
538 CTemplatizerParser
CTemplatizerCommentBloc::parseInternal(CTemplatizerParser ptr
)
540 ptr
= skipSpace(ptr
);
542 if (!popToken(ptr
, CommentStart
, true))
545 while (*ptr
!= '\0' && !isNextToken(ptr
, CommentEnd
, false))
548 if (!popToken(ptr
, CommentEnd
, false))