khanat-opennel-code/code/ryzom/tools/pd_parser/cpp_output.h
2013-09-14 19:02:50 +02:00

931 lines
20 KiB
C++

// 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_CPP_OUTPUT_H
#define RY_PD_CPP_OUTPUT_H
#include <nel/misc/types_nl.h>
#include <nel/misc/debug.h>
#include <nel/misc/file.h>
#include <nel/misc/path.h>
#include <nel/misc/algo.h>
#include <string>
inline std::string lcFirst(std::string str)
{
if (!str.empty() && str[0]>='A' && str[0]<='Z')
str[0] += 'a'-'A';
return str;
}
inline std::string padTab(std::string str, uint sz)
{
uint i;
uint csize = 0;
for (i=0; i<str.size(); ++i)
{
if (str[i] == '\t')
csize = (csize+4)&0xfffc;
else
++csize;
}
while (csize < sz-4)
{
str += '\t';
csize += 4;
}
str += '\t';
return str;
}
inline std::string removeDefaultArgs(const std::string& s)
{
std::string res;
uint i;
bool foundEqual = false;
for (i=0; i<s.size(); ++i)
{
if (s[i] == '=')
foundEqual = true;
if (foundEqual && s[i] == ',')
foundEqual = false;
if (!foundEqual)
res += s[i];
}
return res;
}
class CCppOutput;
class CClassGenerator
{
public:
enum TSection
{
Public = 0,
Protected = 1,
Private = 2
};
struct SMethod
{
SMethod() : IsConst(false), IsInline(false), IsStatic(false), IsVirtual(false), IsAttribute(false), IsSeparator(false) {}
std::string Type;
std::string Name;
std::string Proto;
std::string Body;
std::string ConstructorInit;
std::string Description;
bool IsConst;
bool IsInline;
bool IsStatic;
bool IsVirtual;
bool IsAttribute;
bool IsSeparator;
};
struct SSection
{
TSection Type;
std::string Name;
std::vector<SMethod> Methods;
std::string DisplayName;
std::string Description;
std::string Other;
};
class SMethodId
{
public:
SMethodId(sint section=-1, sint method=-1, CClassGenerator *generator=NULL) : Section(section), Method(method), Generator(generator) {}
sint Section;
sint Method;
CClassGenerator *Generator;
void add(const std::string &instruction);
void setDescription(const std::string &description);
};
/// name
std::string Name;
///
std::string Inherit;
/// sections
std::vector<SSection> Sections;
/// in body
std::string Body;
///
sint CurrentSection;
///
SMethodId CurrentMethod;
///
bool StartInline;
void init(const std::string &name)
{
clear();
Name = name;
}
void clear()
{
Name.clear();
Sections.clear();
Body.clear();
Inherit.clear();
CurrentSection = -1;
}
void setSection(const std::string &name)
{
uint i;
for (i=0; i<Sections.size(); ++i)
{
if (Sections[i].Name == name)
{
CurrentSection = i;
return;
}
}
}
void createSection(TSection type, const std::string &name, const std::string &displayname=std::string(""), const std::string &description=std::string(""))
{
SSection newSection;
newSection.Type = type;
newSection.Name = name;
newSection.DisplayName = displayname;
newSection.Description = description;
CurrentSection = (sint)Sections.size();
Sections.push_back(newSection);
}
void createPublic(const std::string &name, const std::string &displayname=std::string(""), const std::string &description=std::string(""))
{
createSection(Public, name, displayname, description);
}
void createProtected(const std::string &name, const std::string &displayname=std::string(""), const std::string &description=std::string(""))
{
createSection(Protected, name, displayname, description);
}
void createPrivate(const std::string &name, const std::string &displayname=std::string(""), const std::string &description=std::string(""))
{
createSection(Private, name, displayname, description);
}
SMethodId startMethod(const std::string &type,
const std::string &name,
const std::string &proto,
const std::string &section=std::string(""),
bool isConst=false,
bool isInline=true,
bool isStatic=false,
bool isAttribute = false,
const std::string &init=std::string(""),
bool isVirtual = false,
bool isSep = false)
{
if (!section.empty())
setSection(section);
SSection &_section = Sections[CurrentSection];
SMethod newMethod;
newMethod.Name = name;
newMethod.Type = type;
newMethod.Proto = proto;
newMethod.IsConst = isConst;
newMethod.IsInline = isInline;
newMethod.IsStatic = isStatic;
newMethod.IsVirtual = isVirtual;
newMethod.IsAttribute = isAttribute;
newMethod.ConstructorInit = init;
newMethod.IsSeparator = isSep;
_section.Methods.push_back(newMethod);
CurrentMethod = SMethodId(CurrentSection, (sint)_section.Methods.size()-1, this);
return CurrentMethod;
}
SMethod &get(SMethodId id)
{
return Sections[id.Section].Methods[id.Method];
}
SMethodId startConstructor(const std::string &proto, const std::string &section=std::string(""), bool isInline=true, const std::string &init=std::string(""))
{
return startMethod("", "", proto, section, false, isInline, false, false, init);
}
SMethodId startDestructor(const std::string &section=std::string(""), bool isInline=true, bool isVirtual=false)
{
return startMethod("", "~", "", section, false, isInline, false, false, "", isVirtual);
}
SMethodId startRaw(const std::string &section=std::string(""), bool isInline=true)
{
return startMethod("", "", "", section, false, isInline, false, true, "", false, true);
}
void separator(const std::string &section=std::string(""))
{
startMethod("", "", "", section, false, false, false, false, "", false, true);
}
void add(const std::string &instruction, SMethodId methodId = SMethodId())
{
if (methodId.Method == -1)
methodId = CurrentMethod;
if (instruction.empty())
return;
Sections[methodId.Section].Methods[methodId.Method].Body += instruction+"\n";
}
void setDescription(const std::string &description, SMethodId methodId = SMethodId())
{
if (methodId.Method == -1)
methodId = CurrentMethod;
Sections[methodId.Section].Methods[methodId.Method].Description += description;
}
void addOther(const std::string &other, const std::string &section=std::string(""))
{
if (!section.empty())
setSection(section);
SSection &_section = Sections[CurrentSection];
_section.Other += other;
}
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="")
{
startMethod(type, name, funcPtrProto, section, false, false, isStatic, true, value, isFuncPtr);
}
void flush(CCppOutput &hFile, CCppOutput &cppFile, CCppOutput &hInlineFile);
};
class CFunctionGenerator
{
public:
CFunctionGenerator() : IsInline(false) {}
/// function name
std::string Name;
/// return type
std::string Type;
/// function prototype
std::string Proto;
/// function body
std::string Body;
/// is an inlined function
bool IsInline;
/// function description
std::string Description;
void init(const std::string &name)
{
clear();
Name = name;
}
void setType(const std::string &type)
{
Type = type;
}
void setProto(const std::string &proto)
{
Proto = proto;
}
void clear()
{
Name.clear();
Type.clear();
Proto.clear();
Body.clear();
IsInline = false;
}
void add(const std::string &instruction)
{
if (!instruction.empty())
Body += instruction+"\n";
}
void flush(CCppOutput &hFile, CCppOutput &cppFile, CCppOutput &hInlineFile);
};
class CCppOutput
{
public:
/// Constructor
CCppOutput(bool cppmode = true) : _CppMode(cppmode),
_XmlMode(false), _NewLine(true), _Indent(0), _DescriptionMode(false), _Clean(true),
_XmlInNode(false), _XmlLastSlash(false), _XmlGetNodeName(false), _XmlCloseNode(false), _XmlRootNode(false) {}
/// Clear
void clear();
/// Set Xml Mode
void setXmlMode() { _XmlMode = true; _NewLine = false; }
/// Flush to file
void flush(const std::string &fileName);
/// Force indent
void indent();
/// Force unindent
void unindent();
/// Stream
template<typename T>
CCppOutput & operator << (const T& t)
{
addToStream(NLMISC::toString(t).c_str());
return *this;
}
/// file description
void setFileHeader(const std::string& filename, const std::string& description)
{
if (_Clean)
{
*this << "/** \\file " << filename << "\n";
if (!description.empty())
*this << description << "\n";
*this << "\n";
*this << "$Id: cpp_output.h,v 1.15 2004/12/13 17:19:01 legros Exp $\n";
*this << "*/\n\n";
}
}
private:
bool _CppMode;
bool _XmlMode;
bool _NewLine;
sint _Indent;
std::string _Buffer;
bool _DescriptionMode;
bool _Clean;
std::vector<std::string> _XmlNodes;
bool _XmlInNode;
bool _XmlLastSlash;
bool _XmlGetNodeName;
bool _XmlCloseNode;
bool _XmlRootNode;
std::string _XmlNodeName;
/// Stream operator
CCppOutput addToStream (const char* str)
{
_Clean = false;
if (_CppMode)
{
while (*str != '\0')
{
if (!_DescriptionMode && *str == '}')
unindent();
bool triggerDescriptionStart = false;
bool triggerDescriptionEnd = false;
if (!strncmp(str, "/**", 3))
{
// start description
triggerDescriptionStart = true;
}
if (_DescriptionMode && !strncmp(str, "*/", 2))
{
// stop description
triggerDescriptionEnd = true;
}
bool skipChar = false;
if (*str != '\r')
{
if (_NewLine)
{
if ((*str == ' ' || *str == '\t') && !_DescriptionMode)
{
skipChar = true;
}
else
{
sint i;
for (i=0; i<_Indent; ++i)
_Buffer += '\t';
_NewLine = false;
if (_DescriptionMode)
{
_Buffer += " ";
if (!triggerDescriptionEnd)
_Buffer += "* ";
if (!triggerDescriptionEnd && *str == '*')
{
skipChar = true;
}
}
}
}
if (!_DescriptionMode && *str == '{')
indent();
}
if (*str == '\n')
{
_NewLine = true;
}
if (!skipChar)
_Buffer += *str;
if (triggerDescriptionStart)
_DescriptionMode = true;
if (triggerDescriptionEnd)
_DescriptionMode = false;
++str;
}
}
else if (_XmlMode)
{
while (*str != '\0')
{
bool skipChar = false;
bool requireIndent = false;
if (*str == '<')
{
//
_XmlCloseNode = (str[1] == '/');
_XmlRootNode = (str[1] == '?');
_XmlInNode = true;
_XmlGetNodeName = true;
_XmlNodeName.clear();
requireIndent = (!_XmlCloseNode && !_XmlRootNode);
if (_XmlCloseNode)
unindent();
}
else if (*str == '>')
{
_XmlInNode = false;
if (!_XmlCloseNode && !_XmlRootNode)
{
_XmlNodes.push_back(_XmlNodeName);
}
if (_XmlCloseNode || _XmlLastSlash)
{
if (_XmlLastSlash)
unindent();
nlassert(!_XmlNodes.empty() && _XmlNodes.back() == _XmlNodeName);
_XmlNodes.pop_back();
}
}
else if (*str == '\n')
{
_NewLine = true;
}
if (_NewLine)
{
if (*str == ' ' || *str == '\t')
{
skipChar = true;
}
else if (*str != '\n' && *str != '\r')
{
sint i;
for (i=0; i<_Indent; ++i)
_Buffer += '\t';
_NewLine = false;
}
}
_XmlLastSlash = (*str == '/');
if (_XmlInNode && _XmlGetNodeName && *str != '/' && *str != '<')
{
if (!isalpha(*str))
_XmlGetNodeName = false;
if (_XmlGetNodeName)
_XmlNodeName += *str;
}
if (!skipChar)
_Buffer += *str;
++str;
if (requireIndent)
indent();
}
}
else
{
_Buffer += str;
}
return *this;
}
};
//
// Inline implementation
//
inline void CCppOutput::clear()
{
_Buffer.clear();
}
inline bool searchForId(char* buffer, char** start, char** end)
{
const char* id = "$Id:";
uint len = (uint)strlen(id);
for (; *buffer != '\0'; ++buffer)
{
if (strncmp(buffer, id, len) == 0)
{
*start = buffer;
*end = buffer+1;
while (**end != '\0' && **end != '$')
++(*end);
if (**end != (char)'$')
return false;
++(*end);
return true;
}
}
return false;
}
inline void CCppOutput::flush(const std::string &fileName)
{
NLMISC::COFile f;
bool overwrite = true;
if (NLMISC::CFile::fileExists(fileName))
{
NLMISC::CIFile fi;
if (fi.open(fileName))
{
std::string buffer;
buffer.resize(fi.getFileSize(), '*');
fi.serialBuffer((uint8*)(&(buffer[0])), fi.getFileSize());
// search for $Id: cpp_output.h,v 1.15 2004/12/13 17:19:01 legros Exp $ string in file...
char *searchidstart, *searchidend;
char *replaceidstart, *replaceidend;
if (searchForId(&(buffer[0]), &replaceidstart, &replaceidend) &&
searchForId(&(_Buffer[0]), &searchidstart, &searchidend))
{
std::string replace = std::string(replaceidstart, replaceidend-replaceidstart);
std::string search = std::string(searchidstart, searchidend-searchidstart);
NLMISC::strFindReplace(_Buffer, search, replace);
}
overwrite = (buffer != _Buffer);
}
}
if (overwrite)
{
if (f.open(fileName))
{
f.serialBuffer((uint8*)(_Buffer.c_str()), (uint)_Buffer.size());
f.close();
}
else
{
nlwarning("Unable to open file '%s' for output", fileName.c_str());
}
}
else
{
nldebug("File '%s' did not changed, skipped", fileName.c_str());
}
clear();
}
inline void CCppOutput::indent()
{
++_Indent;
}
inline void CCppOutput::unindent()
{
--_Indent;
if (_Indent < 0)
_Indent = 0;
}
inline void CClassGenerator::flush(CCppOutput &hFile, CCppOutput &cppFile, CCppOutput &hInlineFile)
{
hFile << "class " << Name;
if (!Inherit.empty())
hFile << " : " << Inherit;
hFile << "\n{\n";
hInlineFile << "/* -----------------------------------------\n";
hInlineFile << " * Inline implementation of " << Name << "\n";
hInlineFile << " * ----------------------------------------- */\n";
cppFile << "/* -----------------------------------------\n";
cppFile << " * Static Implementation of " << Name << "\n";
cppFile << " * ----------------------------------------- */\n";
uint i;
for (i=0; i<Sections.size(); ++i)
{
static const char* stypes[] = { "public", "protected", "private" };
SSection &section = Sections[i];
if (section.Methods.empty() && section.Other.empty())
continue;
uint j;
for (j=0; j<section.Methods.size(); ++j)
if (!section.Methods[j].IsSeparator)
break;
if (j == section.Methods.size() && section.Other.empty())
continue;
hFile.unindent();
hFile << "\n" << stypes[section.Type] << ":\n\n";
hFile.indent();
if (!section.DisplayName.empty())
{
hFile << "/// \\name " << section.DisplayName << "\n// @{\n\n";
hFile.unindent();
}
if (!section.Description.empty())
{
hFile << "/**\n" << section.Description << "\n*/\n\n";
}
while (!section.Methods.empty() && section.Methods.front().IsSeparator && !section.Methods.front().IsAttribute)
section.Methods.erase(section.Methods.begin());
while (!section.Methods.empty() && section.Methods.back().IsSeparator && !section.Methods.back().IsAttribute)
section.Methods.pop_back();
for (j=0; j<section.Methods.size(); ++j)
{
SMethod &method = section.Methods[j];
std::string inlinekw = (method.IsInline ? std::string("inline ") : std::string(""));
std::string statickw = (method.IsStatic ? std::string("static ") : std::string(""));
std::string virtualkw = (method.IsVirtual ? std::string("virtual ") : std::string(""));
std::string constkw = (method.IsConst ? std::string(" const") : std::string(""));
if (!method.Description.empty())
{
hFile << "\n/**\n" << method.Description << "\n*/\n";
}
// raw
if (method.IsAttribute && method.IsSeparator)
{
if (method.IsInline)
{
hInlineFile << method.Body;
}
else
{
cppFile << method.Body;
}
}
// separator
else if (method.IsSeparator)
{
hFile << "\n";
}
// attribute
else if (method.IsAttribute)
{
// pointer to function
if (method.IsVirtual)
{
hFile << padTab(statickw+method.Type, 32) << "(*" <<method.Name << ")(" << method.Proto << ");\n";
if (method.IsStatic)
{
cppFile << padTab(method.Type, 32) << "(*" << Name << "::" << method.Name << ")(" << method.Proto << ")";
if (!method.ConstructorInit.empty())
cppFile << " = " << method.ConstructorInit;
cppFile << ";\n";
}
}
else
{
hFile << padTab(statickw+method.Type, 32) << method.Name << ";\n";
if (method.IsStatic)
{
if (!method.Proto.empty())
cppFile << padTab(method.Proto, 32) << Name << "::" << method.Name;
else
cppFile << padTab(method.Type, 32) << Name << "::" << method.Name;
if (!method.ConstructorInit.empty())
cppFile << " = " << method.ConstructorInit;
cppFile << ";\n";
}
}
}
// constructor
else if (method.Type.empty())
{
hFile << virtualkw << method.Name << Name << "(" << method.Proto << ");\n";
if (method.IsInline)
{
hInlineFile << inlinekw << Name << "::" << method.Name << Name << "(" << removeDefaultArgs(method.Proto) << ")";
if (!method.ConstructorInit.empty())
hInlineFile << " : " << method.ConstructorInit;
hInlineFile << "\n";
hInlineFile << "{\n";
hInlineFile << method.Body;
hInlineFile << "}\n";
}
else
{
cppFile << Name << "::" << method.Name << Name << "(" << removeDefaultArgs(method.Proto) << ")\n";
cppFile << "{\n";
cppFile << method.Body;
cppFile << "}\n";
}
}
// method
else
{
hFile << padTab(statickw+virtualkw+method.Type, 32) << lcFirst(method.Name) << "(" << method.Proto << ")" << constkw << ";\n";
if (method.IsInline)
{
hInlineFile << padTab(inlinekw+method.Type, 32) << Name << "::" << lcFirst(method.Name) << "(" + removeDefaultArgs(method.Proto) << ")" << constkw << "\n";
hInlineFile << "{\n";
hInlineFile << method.Body;
hInlineFile << "}\n";
}
else
{
cppFile << padTab(method.Type, 32) << Name << "::" << lcFirst(method.Name) << "(" << removeDefaultArgs(method.Proto) << ")" << constkw << "\n";
cppFile << "{\n";
cppFile << method.Body;
cppFile << "}\n";
}
}
}
hFile << section.Other;
if (!section.DisplayName.empty())
{
hFile << "\n// @}\n\n";
hFile.indent();
}
}
hFile << "};\n\n";
hInlineFile << "// End of inline implementation of " << Name << "\n\n";
cppFile << Body;
cppFile << "// End of static implementation of " << Name << "\n\n";
}
inline void CClassGenerator::SMethodId::add(const std::string &instruction)
{
nlassert(Generator != NULL);
Generator->add(instruction, *this);
}
inline void CClassGenerator::SMethodId::setDescription(const std::string &description)
{
nlassert(Generator != NULL);
Generator->add(description, *this);
}
inline void CFunctionGenerator::flush(CCppOutput &hFile, CCppOutput &cppFile, CCppOutput &hInlineFile)
{
std::string inlinekw = (IsInline ? std::string("inline ") : std::string(""));
if (!Description.empty())
{
hFile << "\n/**\n" << Description << "\n*/\n";
}
hFile << padTab(Type, 32) << lcFirst(Name) << "(" << Proto << ");\n";
if (IsInline)
{
hInlineFile << padTab(inlinekw+Type, 32) << lcFirst(Name) << "(" << removeDefaultArgs(Proto) << ")\n";
hInlineFile << "{\n";
hInlineFile << Body;
hInlineFile << "}\n\n";
}
else
{
cppFile << padTab(Type, 32) << lcFirst(Name) << "(" << removeDefaultArgs(Proto) << ")\n";
cppFile << "{\n";
cppFile << Body;
cppFile << "}\n\n";
}
}
#endif