// 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 "parser.h"
#include "cpp_output.h"
#include "templatizer.h"
#include "parser_rules.h"
#include
#include
using namespace std;
using namespace NLMISC;
//using namespace RY_PDS;
bool GenerateCpp = true;
bool GenerateXmlDescription = true;
bool VerboseMode = false;
bool GenerateDebugMessages = false;
bool GenerateHAuto = false;
bool GenerateOnlyLogs = false;
CTokenizer Tokenizer;
CTemplatizer Templatizer;
CHashKey HashKey;
string CurrentEnum;
CEnumNode *CurrentEnumNode = NULL;
string DbDescriptionVersion("0.0");
string formatDescription(const string &str)
{
string result;
uint pos = 0;
while (pos < str.size() && (str[pos] == ' ' || str[pos] == '\t' || str[pos] == '\r' || str[pos] == '\n'))
++pos;
bool first = true;
while (pos < str.size())
{
if (!first)
result += "\n";
first = false;
result += "* ";
while (pos < str.size() && str[pos] != '\n')
result += str[pos++];
while (pos < str.size() && (str[pos] == ' ' || str[pos] == '\t' || str[pos] == '\r' || str[pos] == '\n'))
++pos;
}
return result;
}
string strReplace(string str, const string &search, const string &replace)
{
std::string::size_type pos = 0;
while ((pos = str.find(search)) != string::npos)
str.replace(pos, search.size(), replace);
return str;
}
string xmlSpecialChars(string str)
{
str = strReplace(str, "&", "&");
str = strReplace(str, "<", "<");
str = strReplace(str, ">", ">");
str = strReplace(str, "\"", """);
str = strReplace(str, "'", "'");
return str;
}
string getFullStdPathNoExt(const string &path)
{
string dir = NLMISC::toLower(NLMISC::CFile::getPath(path));
string file = NLMISC::toLower(NLMISC::CFile::getFilenameWithoutExtension(path));
return dir.empty() ? file : NLMISC::CPath::standardizePath(dir)+file;
}
string getFullStdPath(const string &path)
{
string dir = NLMISC::toLower(NLMISC::CFile::getPath(path));
string file = NLMISC::toLower(NLMISC::CFile::getFilename(path));
return dir.empty() ? file : NLMISC::CPath::standardizePath(dir)+file;
}
string appendArg(const std::string& firstArgs, const std::string& nextArg)
{
if (firstArgs.empty())
return nextArg;
return firstArgs + ", " + nextArg;
}
/*
* Start parsing
*/
CParseNode *parse(const string &file)
{
HashKey = getSHA1(file);
Tokenizer.readFile(file);
if (Tokenizer.tokenize())
return parseMain(Tokenizer);
return NULL;
}
/*
* Execute nodes
*/
// DB Node
bool CDbNode::prolog()
{
addTypeNode("CSheetId", "NLMISC::CSheetId", "NLMISC::CSheetId::Unknown");
addTypeNode("CEntityId", "NLMISC::CEntityId", "NLMISC::CEntityId::Unknown");
addTypeNode("double", "double", "0.0");
addTypeNode("float", "float", "0.0f");
addTypeNode("sint64", "sint64", "(sint64)0");
addTypeNode("uint64", "uint64", "(uint64)0");
addTypeNode("sint32", "sint32", "0");
addTypeNode("uint32", "uint32", "0");
addTypeNode("sint16", "sint16", "0");
addTypeNode("uint16", "uint16", "0");
addTypeNode("sint8", "sint8", "0");
addTypeNode("uint8", "uint8", "0");
addTypeNode("ucchar", "ucchar", "0");
addTypeNode("char", "char", "0");
addTypeNode("bool", "bool", "false");
return true;
}
bool inlineAccessors = false;
string getFunctionPrefix = "get";
string setFunctionPrefix = "set";
string newFunction = "addTo";
string deleteFunction = "deleteFrom";
string indexVariable = "__i";
string valueVariable = "__v";
string keyVariable = "__k";
string objectVariable = "__o";
bool inlineUserInitDefaultCode = false;
string userInitFunction = "init";
string userReleaseFunction = "release";
bool inlineStaticPublic = false;
string staticCreateFunction = "create";
string staticRemoveFunction = "remove";
string staticSetUserFactoryFunction = "setFactory";
string staticLoadFunction = "load";
string staticUnloadFunction = "unload";
string staticSetLoadCbFunction = "setLoadCallback";
string staticGetFunction = "get";
string staticCastFunction = "cast";
string staticConstCastFunction = "cast";
string staticBeginFunction = "begin";
string staticEndFunction = "end";
bool inlineInternal = false;
string initFunction = "pds__init";
string destroyFunction = "pds__destroy";
string registerFunction = "pds__register";
string registerAttributesFunction = "pds__registerAttributes";
string unregisterFunction = "pds__unregister";
string unregisterAttributesFunction = "pds__unregisterAttributes";
string fetchFunction = "pds__fetch";
string setParentFunction = "pds__setParent";
string setUnnotifiedParentFunction = "pds__setParentUnnotified";
string getTableFunction = "pds__getTable";
string unlinkFunction = "pds__unlink";
string notifyInitFunction = "pds__notifyInit";
string notifyReleaseFunction = "pds__notifyRelease";
string clearFunction = "clear";
string storeFunction = "store";
string applyFunction = "apply";
string declareTokensFunction = "pds__declareTokens";
bool inlineStaticInternal = false;
string staticInitFactoryFunction = "pds_static__setFactory";
string staticFactoryFunction = "pds_static__factory";
string staticFetchFunction = "pds_static__fetch";
string staticInitFunction = "pds_static__init";
string staticNotifyLoadFailure = "pds_static__notifyFailure";
string staticLoadCbAttribute = "__pds__LoadCallback";
bool inlineLog = false;
string logStartFunction = "pds__startLog";
string logStopFunction = "pds__stopLog";
string PDSNamespace = "RY_PDS";
string CPDSLibName = PDSNamespace+"::CPDSLib";
string objectIndexName = PDSNamespace+"::CObjectIndex";
string nullIndexName = PDSNamespace+"::CObjectIndex::null()";
string TTableIndexName = PDSNamespace+"::TTableIndex";
string TRowIndexName = PDSNamespace+"::TRowIndex";
string TColumnIndexName = PDSNamespace+"::TColumnIndex";
string INVALID_TABLE_INDEXName = PDSNamespace+"::INVALID_TABLE_INDEX";
string INVALID_ROW_INDEXName = PDSNamespace+"::INVALID_ROW_INDEX";
string indexAllocatorName = PDSNamespace+"::CIndexAllocator";
string pdBaseDataName = PDSNamespace+"::IPDBaseData";
//string pdBaseInheritDataName = PDSNamespace+"::IPDBaseInheritData";
string CPDataName = PDSNamespace+"::CPData";
string TPDFactoryName = PDSNamespace+"::TPDFactory";
string TPDFetchName = PDSNamespace+"::TPDFetch";
string TPDFetchFailureName = PDSNamespace+"::TPDFetchFailure";
bool inlineLogFunctions = false;
string pdslibFunc(const std::string& func)
{
//return CPDSLibName+"::"+func;
return "PDSLib."+func;
}
bool CDbNode::epilog()
{
uint i;
Env = Templatizer.RootEnv;
setEnv("db", Name);
string fullfile = getFullStdPathNoExt(MainFile.empty() ? Name : MainFile);
string filename = NLMISC::toLower(NLMISC::CFile::getFilenameWithoutExtension(fullfile));
setEnv("filename", filename);
setEnv("fullfilename", fullfile);
setEnv("headerfilename", strReplace(strupr(filename+".h"), ".", "_"));
DbHpp.setFileHeader(filename+".h", "Initialisation of the "+Name+" database, declarations\n"+Description);
DbCpp.setFileHeader(filename+".cpp", "Initialisation of the "+Name+" database, implementation\n"+Description);
DbHppInline.setFileHeader(filename+"_inline.h", "Initialisation of the "+Name+" database, inline implementation\n"+Description);
DbSummary << "\n\n";
DbSummary << "Summary of " << Name << " database classes of database\n";
DbSummary << "-----------------------------------------------------------------------------\n\n";
DbSummary << "This file is automatically generated.\n";
DbSummary << "This is a reminder of all classes generated and methods implemented.\n\n\n";
DbSummary << "Database " << Name << " is managed through 4 functions located in " << fullfile << ".h:\n\n";
DbSummary << Name << "::init():\n";
DbSummary << "Initialises database context and connection towards database server (refered as PDS).\n";
DbSummary << "All user factories must have been set before call to this function.\n";
DbSummary << "Call this function in service init method.\n\n";
DbSummary << Name << "::ready():\n";
DbSummary << "Tells whether the whole database engine is ready to work.\n";
DbSummary << "You must not update any value nor call update() unless ready() is true.\n\n";
DbSummary << Name << "::update():\n";
DbSummary << "Updates the database engine and sends updates to the PDS.\n";
DbSummary << "Call this function each tick, provided ready() returned true.\n\n";
DbSummary << Name << "::release():\n";
DbSummary << "Releases the database engine. Drops all data, closes the connection to the PDS.\n";
DbSummary << "Call this function in service release method.\n\n";
DbSummary << "\n\n";
DbSummary << "Summary of generated classes for " << Name << "\n\n";
DbHpp << "\n#ifndef " << strReplace(strupr(filename+".h"), ".", "_") << "\n";
DbHpp << "#define " << strReplace(strupr(filename+".h"), ".", "_") << "\n";
DbHpp << "\n";
DbHpp << "#include \n";
DbHpp << "#include \n";
DbHpp << "\n";
DbHpp << "namespace " << Name << "\n{\n\n";
DbHpp.unindent();
if (!Pch.empty())
{
DbCpp << "\n#include \"" << Pch << "\"\n\n";
}
DbCpp << "#include \"" << filename << ".h\"\n\n";
DbCpp << "namespace " << Name << "\n{\n\n";
DbCpp.unindent();
DbCpp << "RY_PDS::CPDSLib PDSLib;\n";
DbHppInline << "namespace " << Name << "\n{\n\n";
DbHppInline.unindent();
uint maxClassId = 0;
for (i=0; i");
xmlDescription.push_back("");
//xmlDescription.push_back("");
xmlDescription.push_back("");
pass1();
pass2();
pass3();
// Check dependencies order
vector classesOrder;
vector filesOrder;
buildClassOrder(classesOrder, filesOrder);
// generate all file prologs
for (i=0; igenerateProlog();
}
pass4();
//
initDb.init("init");
initDb.IsInline = false;
initDb.Proto = "uint32 overrideDbId";
initDb.Type = "void";
initDb.Description = "Initialise the whole database engine.\nCall this function at service init.";
readyDb.init("ready");
readyDb.IsInline = false;
readyDb.Proto.clear();
readyDb.Type = "bool";
readyDb.Description = "Tells if database engine is ready to work.\nEngine may not be ready because PDS is down, not yet ready\nor message queue to PDS is full.";
updateDb.init("update");
updateDb.IsInline = false;
updateDb.Proto.clear();
updateDb.Type = "void";
updateDb.Description = "Update the database engine.\nCall this method once per tick, only if engine is ready (see also ready() above).";
logChatDb.init("logChat");
logChatDb.IsInline = false;
logChatDb.Proto = "const ucstring& sentence, const NLMISC::CEntityId& from, const std::vector& to";
logChatDb.Type = "void";
logChatDb.Description = "Logs chat sentence with sender and receipiants.";
logTellDb.init("logTell");
logTellDb.IsInline = false;
logTellDb.Proto = "const ucstring& sentence, const NLMISC::CEntityId& from, const NLMISC::CEntityId& to";
logTellDb.Type = "void";
logTellDb.Description = "Logs tell sentence with sender and single recipient (might be player or group).";
releaseDb.init("release");
releaseDb.IsInline = false;
releaseDb.Proto.clear();
releaseDb.Type = "void";
releaseDb.Description = "Release the whole database engine.\nCall this function at service release.";
//
generateClassesDeclaration();
//
generateClassesContent(classesOrder);
//
generateLogContent();
xmlDescription.push_back("");
xmlDescription.push_back("");;
initDb.add("std::string\txmlDescription;");
DbXml.setXmlMode();
for (i=0; i\tids;");
logTellDb.add("ids.push_back(to);");
logTellDb.add(pdslibFunc("logChat")+"(sentence, from, ids);");
logTellDb.flush(DbHpp, DbCpp, DbHppInline);
releaseDb.add(pdslibFunc("release")+"();");
releaseDb.flush(DbHpp, DbCpp, DbHppInline);
DbHpp << "\n// @}\n\n";
DbHpp << "extern RY_PDS::CPDSLib PDSLib;\n";
DbHpp.indent();
DbHpp << "\n} // End of " << Name <<"\n";
generateIncludes(filesOrder);
DbHpp << "\n#include \"" << filename << "_inline.h\"\n\n";
DbHpp << "\n#endif\n";
DbHpp.flush(fullfile+".h");
DbCpp.indent();
DbCpp << "\n} // End of " << Name <<"\n";
DbCpp.flush(fullfile+".cpp");
DbHppInline.indent();
DbHppInline << "\n} // End of " << Name <<"\n";
DbHppInline.flush(fullfile+"_inline.h");
DbSummary.flush(fullfile+"_summary.txt");
for (i=0; igenerateEpilog();
}
return true;
}
void CDbNode::pass1()
{
/*
* PASS 1
* - all type names and class names are known
* -> look for classes in set or backreferences
*/
uint i;
uint classId = 0;
uint typeId = 0;
for (i=0; icheckClassReferences();
classnd->Id = classId++;
classnd->getFileNode()->IncludeStandard = true;
classnd->getFileNode()->IncludeDbFile = true;
}
for (i=0; iId = typeId++;
uint tsize = 0;
if (typend->StorageType == "bool") tsize = 1;
if (typend->StorageType == "char") tsize = 1;
if (typend->StorageType == "ucchar") tsize = 2;
if (typend->StorageType == "uint8") tsize = 1;
if (typend->StorageType == "sint8") tsize = 1;
if (typend->StorageType == "uint16") tsize = 2;
if (typend->StorageType == "sint16") tsize = 2;
if (typend->StorageType == "uint32") tsize = 4;
if (typend->StorageType == "sint32") tsize = 4;
if (typend->StorageType == "uint64") tsize = 8;
if (typend->StorageType == "sint64") tsize = 8;
if (typend->StorageType == "float") tsize = 4;
if (typend->StorageType == "double") tsize = 8;
if (typend->StorageType == "CEntityId") tsize = 8;
if (typend->StorageType == "CSheetId") tsize = 4;
typend->Size = tsize;
string xmlnode = "isEnum())
{
CEnumNode *enumnd = static_cast(typend);
xmlnode += " type='enum'>";
xmlDescription.push_back(xmlnode);
uint j;
for (j=0; jValues.size(); ++j)
xmlDescription.push_back("");
xmlDescription.push_back("");
}
else if (typend->isDimension())
{
CDimensionNode *dimnd = static_cast(typend);
xmlnode += " type='dimension' dimension='"+toString(dimnd->Dimension)+"'/>";
xmlDescription.push_back(xmlnode);
}
else
{
xmlnode += " type='type'/>";
xmlDescription.push_back(xmlnode);
}
}
}
void CDbNode::pass2()
{
/*
* PASS 2
* - class hierarchy, backreferences and in set information are known
* -> fill up attributes
*/
uint i;
for (i=0; iInherited.empty() && child->MappedFlag && !child->HasParent)
child->error("class cannot inherit another class and be mapped. Try to map base class instead.");
if (child->MappedFlag && child->getClassKey() == NULL)
child->error("class is mapped and has no key defined");
child->ForceReference = (child->HasInheritance || child->MappedFlag || child->DerivatedFlag || (child->HasParent && !child->ParentIsHidden));
if (child->ForceReference && child->ParentIsHidden)
child->error("Parent attribute cannot be hidden because class has inheritance, is mapped or is derivated.");
child->getFileNode()->IncludePDSLib = true;
if (!child->Inherited.empty())
{
CClassNode *icln = child;
CClassNode *lastMapped = (child->MappedFlag ? child : NULL);
while (!icln->Inherited.empty())
{
icln = getClassNode(icln->Inherited);
if (icln->MappedFlag)
{
if (lastMapped != NULL)
lastMapped->error("class cannot be remapped since parent "+icln->Name+" is already mapped");
lastMapped = icln;
}
}
/*
if (icln->MappedFlag)
child->MapClass = icln;
*/
child->MapClass = lastMapped;
}
else if (child->MappedFlag)
{
child->MapClass = child;
}
child->fillAttributes();
}
}
void CDbNode::pass3()
{
/*
* PASS 3
* - attributes are known
* -> fill up backrefs and array/set forwardrefs
*/
uint i;
for (i=0; ifillRefs();
}
for (i=0; icomputeFriends();
}
}
void CDbNode::pass4()
{
/*
* PASS 4
* - everything is ok in database descriptor
* -> output c++ code
*/
uint i;
for (i=0; iHpp << "\n\n//\n// Typedefs & Enums\n//\n\n";
}
for (i=0; iExternFlag || type->InternFlag)
continue;
type->generateContent();
}
}
void CDbNode::generateClassesDeclaration()
{
uint i;
DbHpp << "\n//\n";
DbHpp << "// Global Forward Declarations\n";
DbHpp << "//\n\n";
for (i=0; iName << ";\n";
}
DbHpp << "\n";
DbHpp << "//\n\n";
}
void CDbNode::generateIncludes(vector& filesOrder)
{
uint i;
DbHpp << "\n//\n";
DbHpp << "// Includes\n";
DbHpp << "//\n\n";
for (i=0; iSeparatedFlag)
{
filesOrder[i]->setEnv("as", getFileNoExtPath(filesOrder[i]->IncludeAs));
DbHpp << "#include \"" << getFileNoExtPath(filesOrder[i]->IncludeAs) << ".h\"\n";
}
}
DbHpp << "\n";
for (i=0; iIncludeDbFile && !filesOrder[i]->SeparatedFlag)
{
filesOrder[i]->define("incinline");
DbHpp << "#include \"" << getFileNoExtPath(filesOrder[i]->IncludeAs) << "_inline.h\"\n";
}
}
DbHpp << "\n";
DbHpp << "//\n\n";
}
void CDbNode::generateClassesContent(vector& classesOrder)
{
uint i;
//
// output classes content
//
for (i=0; iId)+", \""+cln->Name+"\");");
cln->generateContent();
}
}
void CDbNode::buildClassOrder(vector& classesOrder, vector& filesOrder)
{
set checkedClasses;
uint i;
for (i=0; i beingChecked;
child->checkDependencies(beingChecked, checkedClasses, classesOrder);
}
set checkedFiles;
filesOrder.clear();
for (i=0; i beingChecked;
child->checkDependencies(beingChecked, checkedFiles, filesOrder);
}
for (i=0; iEnv = Env->nextArrayNode("files");
for (i=0; iEnv = classesOrder[i]->getFileNode()->Env->nextArrayNode("classes");
}
void CDbNode::generateLogContent()
{
uint logid = 0;
uint i;
for (i=0; iId = logid;
logid += (uint)child->Logs.size();
child->generateContent();
}
}
// get file path from this file
string CDbNode::getFileNoExtPath(const std::string& file)
{
string thisPath = NLMISC::CFile::getPath(NLMISC::toLower(getDbFile()));
string filePath = NLMISC::CFile::getPath(NLMISC::toLower(file));
string fileName = NLMISC::CFile::getFilename(NLMISC::toLower(file));
if (thisPath == filePath)
return CFile::getFilenameWithoutExtension(fileName);
else
return CPath::standardizePath(filePath)+CFile::getFilenameWithoutExtension(NLMISC::toLower(file));
}
// File Node
bool CFileNode::prolog()
{
CDbNode* db = getDbNode();
db->FileNodes.push_back(this);
if (GenerateOnlyLogs)
Generate = false;
return true;
}
bool CFileNode::epilog()
{
return true;
}
bool CFileNode::generateProlog()
{
CDbNode* db = getDbNode();
if (!db->Description.empty())
Hpp << db->Description << "\n";
string filename = NLMISC::toLower(CFile::getFilenameWithoutExtension(Name));
setEnv("fullfilename", getFullStdPathNoExt(Name));
setEnv("filename", filename);
setEnv("headerfilename", strReplace(strupr(filename+".h"), ".", "_"));
setEnv("description", Description);
Hpp.setFileHeader(filename+".h", Description);
Cpp.setFileHeader(filename+".cpp", Description);
HppInline.setFileHeader(filename+"_inline.h", Description);
Hpp << "\n#ifndef " << strReplace(strupr(filename+".h"), ".", "_") << "\n";
Hpp << "#define " << strReplace(strupr(filename+".h"), ".", "_") << "\n\n";
Hpp << "#include \n";
Hpp << "#include \n";
Hpp << "#include \n";
if (GenerateHAuto)
{
Hpp << "#include \n";
}
if (IncludeStandard)
{
define("incstd");
Hpp << "#include \n";
Hpp << "#include \n";
}
Hpp << "#include \n";
Hpp << "#include