Fix
[ryzomcore.git] / ryzom / tools / pd_parser / cpp_output.h
blob96183b26524fc5e6151a3615972d19af60d28057
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 #ifndef RY_PD_CPP_OUTPUT_H
18 #define RY_PD_CPP_OUTPUT_H
20 #include <nel/misc/types_nl.h>
21 #include <nel/misc/debug.h>
22 #include <nel/misc/file.h>
23 #include <nel/misc/path.h>
24 #include <nel/misc/algo.h>
26 #include <string>
28 inline std::string lcFirst(std::string str)
30 if (!str.empty() && str[0]>='A' && str[0]<='Z')
31 str[0] += 'a'-'A';
32 return str;
35 inline std::string padTab(std::string str, uint sz)
37 uint i;
38 uint csize = 0;
40 for (i=0; i<str.size(); ++i)
42 if (str[i] == '\t')
43 csize = (csize+4)&0xfffc;
44 else
45 ++csize;
48 while (csize < sz-4)
50 str += '\t';
51 csize += 4;
53 str += '\t';
54 return str;
57 inline std::string removeDefaultArgs(const std::string& s)
59 std::string res;
61 uint i;
62 bool foundEqual = false;
63 for (i=0; i<s.size(); ++i)
65 if (s[i] == '=')
66 foundEqual = true;
67 if (foundEqual && s[i] == ',')
68 foundEqual = false;
69 if (!foundEqual)
70 res += s[i];
73 return res;
76 class CCppOutput;
78 class CClassGenerator
80 public:
82 enum TSection
84 Public = 0,
85 Protected = 1,
86 Private = 2
89 struct SMethod
91 SMethod() : IsConst(false), IsInline(false), IsStatic(false), IsVirtual(false), IsAttribute(false), IsSeparator(false) {}
92 std::string Type;
93 std::string Name;
94 std::string Proto;
95 std::string Body;
96 std::string ConstructorInit;
97 std::string Description;
98 bool IsConst;
99 bool IsInline;
100 bool IsStatic;
101 bool IsVirtual;
102 bool IsAttribute;
103 bool IsSeparator;
106 struct SSection
108 TSection Type;
109 std::string Name;
110 std::vector<SMethod> Methods;
111 std::string DisplayName;
112 std::string Description;
113 std::string Other;
116 class SMethodId
118 public:
119 SMethodId(sint section=-1, sint method=-1, CClassGenerator *generator=NULL) : Section(section), Method(method), Generator(generator) {}
121 sint Section;
122 sint Method;
123 CClassGenerator *Generator;
125 void add(const std::string &instruction);
126 void setDescription(const std::string &description);
130 /// name
131 std::string Name;
134 std::string Inherit;
136 /// sections
137 std::vector<SSection> Sections;
139 /// in body
140 std::string Body;
143 sint CurrentSection;
146 SMethodId CurrentMethod;
149 bool StartInline;
151 void init(const std::string &name)
153 clear();
154 Name = name;
157 void clear()
159 Name.clear();
160 Sections.clear();
161 Body.clear();
162 Inherit.clear();
163 CurrentSection = -1;
167 void setSection(const std::string &name)
169 uint i;
170 for (i=0; i<Sections.size(); ++i)
172 if (Sections[i].Name == name)
174 CurrentSection = i;
175 return;
180 void createSection(TSection type, const std::string &name, const std::string &displayname=std::string(""), const std::string &description=std::string(""))
182 SSection newSection;
183 newSection.Type = type;
184 newSection.Name = name;
185 newSection.DisplayName = displayname;
186 newSection.Description = description;
187 CurrentSection = (sint)Sections.size();
188 Sections.push_back(newSection);
191 void createPublic(const std::string &name, const std::string &displayname=std::string(""), const std::string &description=std::string(""))
193 createSection(Public, name, displayname, description);
196 void createProtected(const std::string &name, const std::string &displayname=std::string(""), const std::string &description=std::string(""))
198 createSection(Protected, name, displayname, description);
201 void createPrivate(const std::string &name, const std::string &displayname=std::string(""), const std::string &description=std::string(""))
203 createSection(Private, name, displayname, description);
208 SMethodId startMethod(const std::string &type,
209 const std::string &name,
210 const std::string &proto,
211 const std::string &section=std::string(""),
212 bool isConst=false,
213 bool isInline=true,
214 bool isStatic=false,
215 bool isAttribute = false,
216 const std::string &init=std::string(""),
217 bool isVirtual = false,
218 bool isSep = false)
220 if (!section.empty())
221 setSection(section);
223 SSection &_section = Sections[CurrentSection];
225 SMethod newMethod;
227 newMethod.Name = name;
228 newMethod.Type = type;
229 newMethod.Proto = proto;
230 newMethod.IsConst = isConst;
231 newMethod.IsInline = isInline;
232 newMethod.IsStatic = isStatic;
233 newMethod.IsVirtual = isVirtual;
234 newMethod.IsAttribute = isAttribute;
235 newMethod.ConstructorInit = init;
236 newMethod.IsSeparator = isSep;
238 _section.Methods.push_back(newMethod);
240 CurrentMethod = SMethodId(CurrentSection, (sint)_section.Methods.size()-1, this);
242 return CurrentMethod;
245 SMethod &get(SMethodId id)
247 return Sections[id.Section].Methods[id.Method];
250 SMethodId startConstructor(const std::string &proto, const std::string &section=std::string(""), bool isInline=true, const std::string &init=std::string(""))
252 return startMethod("", "", proto, section, false, isInline, false, false, init);
255 SMethodId startDestructor(const std::string &section=std::string(""), bool isInline=true, bool isVirtual=false)
257 return startMethod("", "~", "", section, false, isInline, false, false, "", isVirtual);
260 SMethodId startRaw(const std::string &section=std::string(""), bool isInline=true)
262 return startMethod("", "", "", section, false, isInline, false, true, "", false, true);
265 void separator(const std::string &section=std::string(""))
267 startMethod("", "", "", section, false, false, false, false, "", false, true);
270 void add(const std::string &instruction, SMethodId methodId = SMethodId())
272 if (methodId.Method == -1)
273 methodId = CurrentMethod;
275 if (instruction.empty())
276 return;
278 Sections[methodId.Section].Methods[methodId.Method].Body += instruction+"\n";
281 void setDescription(const std::string &description, SMethodId methodId = SMethodId())
283 if (methodId.Method == -1)
284 methodId = CurrentMethod;
286 Sections[methodId.Section].Methods[methodId.Method].Description += description;
289 void addOther(const std::string &other, const std::string &section=std::string(""))
291 if (!section.empty())
292 setSection(section);
294 SSection &_section = Sections[CurrentSection];
296 _section.Other += other;
299 void addAttribute(const std::string &type, const std::string &name, const std::string &section=std::string(""), bool isStatic=false, const std::string& value="", bool isFuncPtr=false, const std::string funcPtrProto="")
301 startMethod(type, name, funcPtrProto, section, false, false, isStatic, true, value, isFuncPtr);
305 void flush(CCppOutput &hFile, CCppOutput &cppFile, CCppOutput &hInlineFile);
316 class CFunctionGenerator
318 public:
320 CFunctionGenerator() : IsInline(false) {}
322 /// function name
323 std::string Name;
325 /// return type
326 std::string Type;
328 /// function prototype
329 std::string Proto;
331 /// function body
332 std::string Body;
334 /// is an inlined function
335 bool IsInline;
337 /// function description
338 std::string Description;
340 void init(const std::string &name)
342 clear();
343 Name = name;
346 void setType(const std::string &type)
348 Type = type;
351 void setProto(const std::string &proto)
353 Proto = proto;
356 void clear()
358 Name.clear();
359 Type.clear();
360 Proto.clear();
361 Body.clear();
362 IsInline = false;
365 void add(const std::string &instruction)
367 if (!instruction.empty())
368 Body += instruction+"\n";
372 void flush(CCppOutput &hFile, CCppOutput &cppFile, CCppOutput &hInlineFile);
384 class CCppOutput
386 public:
388 /// Constructor
389 CCppOutput(bool cppmode = true) : _CppMode(cppmode),
390 _XmlMode(false), _NewLine(true), _Indent(0), _DescriptionMode(false), _Clean(true),
391 _XmlInNode(false), _XmlLastSlash(false), _XmlGetNodeName(false), _XmlCloseNode(false), _XmlRootNode(false) {}
393 /// Clear
394 void clear();
396 /// Set Xml Mode
397 void setXmlMode() { _XmlMode = true; _NewLine = false; }
399 /// Flush to file
400 void flush(const std::string &fileName);
402 /// Force indent
403 void indent();
405 /// Force unindent
406 void unindent();
408 /// Stream
409 template<typename T>
410 CCppOutput & operator << (const T& t)
412 addToStream(NLMISC::toString(t).c_str());
413 return *this;
416 /// file description
417 void setFileHeader(const std::string& filename, const std::string& description)
419 if (_Clean)
421 *this << "/** \\file " << filename << "\n";
422 if (!description.empty())
423 *this << description << "\n";
424 *this << "\n";
425 *this << "$Id: cpp_output.h,v 1.15 2004/12/13 17:19:01 legros Exp $\n";
426 *this << "*/\n\n";
432 private:
434 bool _CppMode;
435 bool _XmlMode;
436 bool _NewLine;
437 sint _Indent;
438 std::string _Buffer;
439 bool _DescriptionMode;
440 bool _Clean;
442 std::vector<std::string> _XmlNodes;
443 bool _XmlInNode;
444 bool _XmlLastSlash;
445 bool _XmlGetNodeName;
446 bool _XmlCloseNode;
447 bool _XmlRootNode;
448 std::string _XmlNodeName;
450 /// Stream operator
451 CCppOutput addToStream (const char* str)
453 _Clean = false;
454 if (_CppMode)
456 while (*str != '\0')
458 if (!_DescriptionMode && *str == '}')
459 unindent();
461 bool triggerDescriptionStart = false;
462 bool triggerDescriptionEnd = false;
464 if (!strncmp(str, "/**", 3))
466 // start description
467 triggerDescriptionStart = true;
470 if (_DescriptionMode && !strncmp(str, "*/", 2))
472 // stop description
473 triggerDescriptionEnd = true;
476 bool skipChar = false;
477 if (*str != '\r')
479 if (_NewLine)
481 if ((*str == ' ' || *str == '\t') && !_DescriptionMode)
483 skipChar = true;
485 else
487 sint i;
488 for (i=0; i<_Indent; ++i)
489 _Buffer += '\t';
490 _NewLine = false;
492 if (_DescriptionMode)
494 _Buffer += " ";
495 if (!triggerDescriptionEnd)
496 _Buffer += "* ";
498 if (!triggerDescriptionEnd && *str == '*')
501 skipChar = true;
507 if (!_DescriptionMode && *str == '{')
508 indent();
511 if (*str == '\n')
513 _NewLine = true;
516 if (!skipChar)
517 _Buffer += *str;
519 if (triggerDescriptionStart)
520 _DescriptionMode = true;
521 if (triggerDescriptionEnd)
522 _DescriptionMode = false;
524 ++str;
527 else if (_XmlMode)
529 while (*str != '\0')
531 bool skipChar = false;
532 bool requireIndent = false;
534 if (*str == '<')
537 _XmlCloseNode = (str[1] == '/');
538 _XmlRootNode = (str[1] == '?');
539 _XmlInNode = true;
540 _XmlGetNodeName = true;
541 _XmlNodeName.clear();
543 requireIndent = (!_XmlCloseNode && !_XmlRootNode);
544 if (_XmlCloseNode)
545 unindent();
547 else if (*str == '>')
549 _XmlInNode = false;
551 if (!_XmlCloseNode && !_XmlRootNode)
553 _XmlNodes.push_back(_XmlNodeName);
556 if (_XmlCloseNode || _XmlLastSlash)
558 if (_XmlLastSlash)
559 unindent();
561 nlassert(!_XmlNodes.empty() && _XmlNodes.back() == _XmlNodeName);
562 _XmlNodes.pop_back();
565 else if (*str == '\n')
567 _NewLine = true;
570 if (_NewLine)
572 if (*str == ' ' || *str == '\t')
574 skipChar = true;
576 else if (*str != '\n' && *str != '\r')
578 sint i;
579 for (i=0; i<_Indent; ++i)
580 _Buffer += '\t';
581 _NewLine = false;
585 _XmlLastSlash = (*str == '/');
587 if (_XmlInNode && _XmlGetNodeName && *str != '/' && *str != '<')
589 if (!isalpha(*str))
590 _XmlGetNodeName = false;
592 if (_XmlGetNodeName)
593 _XmlNodeName += *str;
596 if (!skipChar)
597 _Buffer += *str;
599 ++str;
601 if (requireIndent)
602 indent();
605 else
607 _Buffer += str;
610 return *this;
616 // Inline implementation
619 inline void CCppOutput::clear()
621 _Buffer.clear();
624 inline bool searchForId(char* buffer, char** start, char** end)
626 const char* id = "$Id:";
627 uint len = (uint)strlen(id);
628 for (; *buffer != '\0'; ++buffer)
630 if (strncmp(buffer, id, len) == 0)
632 *start = buffer;
633 *end = buffer+1;
634 while (**end != '\0' && **end != '$')
635 ++(*end);
636 if (**end != (char)'$')
637 return false;
639 ++(*end);
640 return true;
644 return false;
647 inline void CCppOutput::flush(const std::string &fileName)
649 NLMISC::COFile f;
651 bool overwrite = true;
653 if (NLMISC::CFile::fileExists(fileName))
655 NLMISC::CIFile fi;
656 if (fi.open(fileName))
658 std::string buffer;
659 buffer.resize(fi.getFileSize(), '*');
660 fi.serialBuffer((uint8*)(&(buffer[0])), fi.getFileSize());
662 // search for $Id$ string in file...
663 char *searchidstart, *searchidend;
664 char *replaceidstart, *replaceidend;
666 if (searchForId(&(buffer[0]), &replaceidstart, &replaceidend) &&
667 searchForId(&(_Buffer[0]), &searchidstart, &searchidend))
669 std::string replace = std::string(replaceidstart, replaceidend-replaceidstart);
670 std::string search = std::string(searchidstart, searchidend-searchidstart);
671 NLMISC::strFindReplace(_Buffer, search, replace);
674 overwrite = (buffer != _Buffer);
678 if (overwrite)
680 if (f.open(fileName))
682 f.serialBuffer((uint8*)(_Buffer.c_str()), (uint)_Buffer.size());
683 f.close();
685 else
687 nlwarning("Unable to open file '%s' for output", fileName.c_str());
690 else
692 nldebug("File '%s' did not changed, skipped", fileName.c_str());
695 clear();
698 inline void CCppOutput::indent()
700 ++_Indent;
704 inline void CCppOutput::unindent()
706 --_Indent;
707 if (_Indent < 0)
708 _Indent = 0;
716 inline void CClassGenerator::flush(CCppOutput &hFile, CCppOutput &cppFile, CCppOutput &hInlineFile)
718 hFile << "class " << Name;
719 if (!Inherit.empty())
720 hFile << " : " << Inherit;
721 hFile << "\n{\n";
723 hInlineFile << "/* -----------------------------------------\n";
724 hInlineFile << " * Inline implementation of " << Name << "\n";
725 hInlineFile << " * ----------------------------------------- */\n";
727 cppFile << "/* -----------------------------------------\n";
728 cppFile << " * Static Implementation of " << Name << "\n";
729 cppFile << " * ----------------------------------------- */\n";
731 uint i;
732 for (i=0; i<Sections.size(); ++i)
734 static const char* stypes[] = { "public", "protected", "private" };
736 SSection &section = Sections[i];
738 if (section.Methods.empty() && section.Other.empty())
739 continue;
741 uint j;
742 for (j=0; j<section.Methods.size(); ++j)
743 if (!section.Methods[j].IsSeparator)
744 break;
746 if (j == section.Methods.size() && section.Other.empty())
747 continue;
749 hFile.unindent();
750 hFile << "\n" << stypes[section.Type] << ":\n\n";
751 hFile.indent();
753 if (!section.DisplayName.empty())
755 hFile << "/// \\name " << section.DisplayName << "\n// @{\n\n";
756 hFile.unindent();
759 if (!section.Description.empty())
761 hFile << "/**\n" << section.Description << "\n*/\n\n";
764 while (!section.Methods.empty() && section.Methods.front().IsSeparator && !section.Methods.front().IsAttribute)
765 section.Methods.erase(section.Methods.begin());
767 while (!section.Methods.empty() && section.Methods.back().IsSeparator && !section.Methods.back().IsAttribute)
768 section.Methods.pop_back();
770 for (j=0; j<section.Methods.size(); ++j)
772 SMethod &method = section.Methods[j];
774 std::string inlinekw = (method.IsInline ? std::string("inline ") : std::string(""));
775 std::string statickw = (method.IsStatic ? std::string("static ") : std::string(""));
776 std::string virtualkw = (method.IsVirtual ? std::string("virtual ") : std::string(""));
777 std::string constkw = (method.IsConst ? std::string(" const") : std::string(""));
779 if (!method.Description.empty())
781 hFile << "\n/**\n" << method.Description << "\n*/\n";
784 // raw
785 if (method.IsAttribute && method.IsSeparator)
787 if (method.IsInline)
789 hInlineFile << method.Body;
791 else
793 cppFile << method.Body;
796 // separator
797 else if (method.IsSeparator)
799 hFile << "\n";
801 // attribute
802 else if (method.IsAttribute)
804 // pointer to function
805 if (method.IsVirtual)
807 hFile << padTab(statickw+method.Type, 32) << "(*" <<method.Name << ")(" << method.Proto << ");\n";
808 if (method.IsStatic)
810 cppFile << padTab(method.Type, 32) << "(*" << Name << "::" << method.Name << ")(" << method.Proto << ")";
811 if (!method.ConstructorInit.empty())
812 cppFile << " = " << method.ConstructorInit;
813 cppFile << ";\n";
816 else
818 hFile << padTab(statickw+method.Type, 32) << method.Name << ";\n";
819 if (method.IsStatic)
821 if (!method.Proto.empty())
822 cppFile << padTab(method.Proto, 32) << Name << "::" << method.Name;
823 else
824 cppFile << padTab(method.Type, 32) << Name << "::" << method.Name;
825 if (!method.ConstructorInit.empty())
826 cppFile << " = " << method.ConstructorInit;
827 cppFile << ";\n";
831 // constructor
832 else if (method.Type.empty())
834 hFile << virtualkw << method.Name << Name << "(" << method.Proto << ");\n";
835 if (method.IsInline)
837 hInlineFile << inlinekw << Name << "::" << method.Name << Name << "(" << removeDefaultArgs(method.Proto) << ")";
838 if (!method.ConstructorInit.empty())
839 hInlineFile << " : " << method.ConstructorInit;
840 hInlineFile << "\n";
841 hInlineFile << "{\n";
842 hInlineFile << method.Body;
843 hInlineFile << "}\n";
845 else
847 cppFile << Name << "::" << method.Name << Name << "(" << removeDefaultArgs(method.Proto) << ")\n";
848 cppFile << "{\n";
849 cppFile << method.Body;
850 cppFile << "}\n";
853 // method
854 else
856 hFile << padTab(statickw+virtualkw+method.Type, 32) << lcFirst(method.Name) << "(" << method.Proto << ")" << constkw << ";\n";
857 if (method.IsInline)
859 hInlineFile << padTab(inlinekw+method.Type, 32) << Name << "::" << lcFirst(method.Name) << "(" + removeDefaultArgs(method.Proto) << ")" << constkw << "\n";
860 hInlineFile << "{\n";
861 hInlineFile << method.Body;
862 hInlineFile << "}\n";
864 else
866 cppFile << padTab(method.Type, 32) << Name << "::" << lcFirst(method.Name) << "(" << removeDefaultArgs(method.Proto) << ")" << constkw << "\n";
867 cppFile << "{\n";
868 cppFile << method.Body;
869 cppFile << "}\n";
874 hFile << section.Other;
876 if (!section.DisplayName.empty())
878 hFile << "\n// @}\n\n";
879 hFile.indent();
883 hFile << "};\n\n";
884 hInlineFile << "// End of inline implementation of " << Name << "\n\n";
886 cppFile << Body;
887 cppFile << "// End of static implementation of " << Name << "\n\n";
890 inline void CClassGenerator::SMethodId::add(const std::string &instruction)
892 nlassert(Generator != NULL);
893 Generator->add(instruction, *this);
896 inline void CClassGenerator::SMethodId::setDescription(const std::string &description)
898 nlassert(Generator != NULL);
899 Generator->add(description, *this);
902 inline void CFunctionGenerator::flush(CCppOutput &hFile, CCppOutput &cppFile, CCppOutput &hInlineFile)
904 std::string inlinekw = (IsInline ? std::string("inline ") : std::string(""));
906 if (!Description.empty())
908 hFile << "\n/**\n" << Description << "\n*/\n";
911 hFile << padTab(Type, 32) << lcFirst(Name) << "(" << Proto << ");\n";
913 if (IsInline)
915 hInlineFile << padTab(inlinekw+Type, 32) << lcFirst(Name) << "(" << removeDefaultArgs(Proto) << ")\n";
916 hInlineFile << "{\n";
917 hInlineFile << Body;
918 hInlineFile << "}\n\n";
920 else
922 cppFile << padTab(Type, 32) << lcFirst(Name) << "(" << removeDefaultArgs(Proto) << ")\n";
923 cppFile << "{\n";
924 cppFile << Body;
925 cppFile << "}\n\n";
931 #endif