Files
pip/utils/code_model_generator/main.cpp

529 lines
20 KiB
C++
Executable File

/*
PIP - Platform Independent Primitives
Code model generator
Copyright (C) 2018 Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "picli.h"
#include "picodeparser.h"
using namespace PICoutManipulators;
PICodeParser parser;
void usage() {
piCout << Bold << "PIP Code model generator";
piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine;
piCout << Green << Bold << "Usage:" << Default << "\"pip_cmg [-hqPpsAMEST] -o <output_file> [-I<include_dir1>] [-I<include_dir1>] [...] [-D<define1>] [-D<define1>] [...] <file1> [<file2>] [<file3>] [...]\"" << NewLine;
piCout << Green << Bold << "Details:";
piCout << Bold << "Debug control";
piCout << "-h " << Green << "- display this message and exit";
piCout << "-q " << Green << "- quiet, no debug output to console";
piCout << "-P " << Green << "- print list of all parsed files to console before exit";
piCout << "-p " << Green << "- print list of all parsed files without file with \"main\" function to console before exit";
piCout << "";
piCout << Bold << "Parsing control";
piCout << "-s " << Green << "- single file (don`t follow includes)";
piCout << "-I<include_dir> " << Green << "- add include dir (e.g. -I.. -I../some_dir -I/usr/include)";
piCout << "-D<define> " << Green << "- add define to preprocessor, define PICODE is always defined (e.g. -DMY_DEFINE will add MY_DEFINE define)";
piCout << "";
piCout << Bold << "Output control";
piCout << "-A " << Green << "- write all";
piCout << "-M " << Green << "- write metainfo";
piCout << "-E " << Green << "- write enums";
piCout << "-S " << Green << "- write stream operators";
piCout << "-G " << Green << "- write getter functions";
//piCout << "-T " << Green << "- write text serialize functions";
piCout << "-o <output_file> " << Green << "- output file for code model without extension (e.g. \"ccm\" - files \"ccm.h\" and \"ccm.cpp\" will be created)";
piCout << "";
piCout << Bold << "Input control";
piCout << "<file> " << Green << "- add file to code model, all includes of this file will be proceed (e.g. \"main.cpp\")";
}
PIString toCName(const PIString &s) {
PIString ret(s.trimmed());
if (ret.isEmpty()) return ret;
ret.replaceAll('<', '_').replaceAll('>', '_');
for (int i = 0; i < ret.size_s(); ++i) {
if (i == 0) {
if (!(ret[i].isAlpha() || ret[i].toAscii() == '_')) {ret.pop_front(); --i;}
} else {
if (!(ret[i].isAlpha() || ret[i].isDigit() || ret[i].toAscii() == '_')) {ret.remove(i); --i;}
}
}
ret.replaceAll("__", "_");
return ret;
}
void makeClassInfo(PIFile & f, const PICodeParser::Entity * e) {
f << "\n\tci = new ClassInfo();\n";
f << "\tci->type = \"" << e->type << "\";\n";
f << "\tci->name = \"" << e->name << "\";\n";
f << "\tci->has_name = " << (e->has_name ? "true" : "false") << ";\n";
if (!e->meta.isEmpty()) {
for (PICodeParser::MetaMap::const_iterator i = e->meta.begin(); i != e->meta.end(); ++i)
f << "\tci->meta[\"" << i.key() << "\"] = \"" << i.value() << "\";\n";
}
f << "\t(*classesInfo)[ci->name] = ci;\n";
if (e->parent_scope) {
f << "\tpci = " << "classesInfo->value(\"" << e->parent_scope->name << "\", 0);\n";
f << "\tif (pci) pci->children_info << ci;\n";
}
piForeachC (PICodeParser::Entity * p, e->parents)
f << "\tci->parents << \"" << p->name << "\";\n";
piForeachC (PICodeParser::Member & m, e->members) {
f << "\tti = TypeInfo(\"" << m.name << "\", \"" << m.type << "\"";
if (m.attributes != 0) {
bool fir = true;
f << ", ";
if (m.attributes[PICodeParser::Const ]) {if (fir) fir = false; else f << " | "; f << "Const";}
if (m.attributes[PICodeParser::Static ]) {if (fir) fir = false; else f << " | "; f << "Static";}
if (m.attributes[PICodeParser::Mutable ]) {if (fir) fir = false; else f << " | "; f << "Mutable";}
if (m.attributes[PICodeParser::Volatile]) {if (fir) fir = false; else f << " | "; f << "Volatile";}
if (m.attributes[PICodeParser::Inline ]) {if (fir) fir = false; else f << " | "; f << "Inline";}
if (m.attributes[PICodeParser::Virtual ]) {if (fir) fir = false; else f << " | "; f << "Virtual";}
} else {
if (m.isBitfield())
f << ", 0";
}
if (m.isBitfield())
f << ", " << m.bits;
f << ");\n";
if (!m.meta.isEmpty()) {
for (PICodeParser::MetaMap::const_iterator i = m.meta.begin(); i != m.meta.end(); ++i)
f << "\tti.meta[\"" << i.key() << "\"] = \"" << i.value() << "\";\n";
}
f << "\tci->variables << ti;\n";
}
PIString arg;
piForeachC (PICodeParser::Member & m, e->functions) {
if (e->name.findCWord(m.name) >= 0) continue;
f << "\tci->functions.push_back(FunctionInfo()); fi = &(ci->functions.back());\n";
f << "\tfi->name = \"" << m.name << "\";";
f << " fi->return_type = TypeInfo(\"\", \"" << m.type << "\"";
if (m.attributes[PICodeParser::Const] || m.attributes[PICodeParser::Static]) {
bool fir = true;
f << ", ";
if (m.attributes[PICodeParser::Const ]) {if (fir) fir = false; else f << " | "; f << "Const";}
if (m.attributes[PICodeParser::Static]) {if (fir) fir = false; else f << " | "; f << "Static";}
}
f << ");\n";
//piCout << "write func" << m.name;
piForeachC (PIString & a, m.arguments_full) {
//piCout << "write arg" << a;
f << "\tfi->arguments << TypeInfo(";
arg = a;
bool con = false;
arg.prepend(" ");
if (arg.find(" const ") >= 0) {
arg.replaceAll(" const ", " ");
con = true;
}
arg.trim();
int ts = 0;
for (ts = arg.size_s() - 1; ts > 0; --ts)
if (!_isCChar(arg[ts]) && !(arg[ts].isDigit())) break;
f << "\"" << arg.takeRight(arg.size_s() - ts - 1).trim() << "\", ";
f << "\"" << arg.trim() << "\"";
if (con) f << ", Const";
f << ");\n";
}
if (!m.meta.isEmpty()) {
for (PICodeParser::MetaMap::const_iterator i = m.meta.begin(); i != m.meta.end(); ++i)
f << "\tfi->meta[\"" << i.key() << "\"] = \"" << i.value() << "\";\n";
}
}
}
void makeEnumInfo(PIFile & f, const PICodeParser::Enum * e) {
if (e->name.isEmpty()) {
f << "\n\tei = (*enumsInfo)[\"\"];\n";
} else {
f << "\n\tei = new EnumInfo();\n";
f << "\t(*enumsInfo)[\"" << e->name << "\"] = ei;\n";
f << "\tei->name = \"" << e->name << "\";\n";
if (!e->meta.isEmpty()) {
for (PICodeParser::MetaMap::const_iterator i = e->meta.begin(); i != e->meta.end(); ++i)
f << "\tei->meta[\"" << i.key() << "\"] = \"" << i.value() << "\";\n";
}
}
piForeachC (PICodeParser::EnumeratorInfo & m, e->members) {
f << "\tei->members << PICodeInfo::EnumeratorInfo(\"" << m.name << "\", " << m.value << ");\n";
if (!m.meta.isEmpty()) {
for (PICodeParser::MetaMap::const_iterator i = m.meta.begin(); i != m.meta.end(); ++i)
f << "\tei->members.back().meta[\"" << i.key() << "\"] = \"" << i.value() << "\";\n";
}
}
}
void writeClassStreamMembersOut(PIFile & f, const PICodeParser::Entity * e, int & cnt) {
PIVector<PICodeParser::Member> ml;
piForeachC (PICodeParser::Member & m, e->members) {
if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue;
ml << m;
}
bool is_union = e->type == "union";
piForeachC (PICodeParser::Member & m, ml) {
if (is_union && m.isBitfield())
continue;
++cnt;
if (m.meta.contains("id"))
cnt = m.meta.value("id").toInt();
if (m.dims.isEmpty()) {
f << "\tcs << cs.chunk(" << cnt << ", ";
if (parser.isEnum(m.type))
f << "(int)";
f << "v." << m.name << ");\n";
} else {
PIString ptype = m.type.left(m.type.find('['));
f << "\tcs << cs.chunk(" << cnt << ", PIVector<" << ptype << " >((const " << ptype << " *)(v." << m.name << "), ";
for (int i = 0; i < m.dims.size_s(); ++i) {
if (i > 0) f << " * ";
f << m.dims[i];
}
f << "));\n";
}
if (is_union)
piBreak;
}
if (is_union) return;
piForeachC (PICodeParser::Entity * ce, e->children) {
if (ce->has_name) continue;
writeClassStreamMembersOut(f, ce, cnt);
}
}
void writeClassStreamMembersIn(PIFile & f, const PICodeParser::Entity * e, int & cnt) {
PIVector<PICodeParser::Member> ml;
piForeachC (PICodeParser::Member & m, e->members) {
if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue;
ml << m;
}
bool is_union = e->type == "union";
piForeachC (PICodeParser::Member & m, ml) {
if (is_union && m.isBitfield())
continue;
++cnt;
if (m.meta.contains("id"))
cnt = m.meta.value("id").toInt();
if (m.dims.isEmpty()) {
f << "\t\tcase " << cnt << ": cs.get(";
if (parser.isEnum(m.type))
f << "(int&)";
f << "v." << m.name << "); break;\n";
} else {
PIString ptype = m.type.left(m.type.find('['));
f << "\t\tcase " << cnt << ": {\n\t\t\tPIVector<" << ptype << " > d; cs.get(d);\n";
f << "\t\t\tint cnt = piMini(d.size_s(), ";
for (int i = 0; i < m.dims.size_s(); ++i) {
if (i > 0) f << " * ";
f << m.dims[i];
}
f << ");\n";
f << "\t\t\tfor (int i = 0; i < cnt; ++i)\n";
f << "\t\t\t\t((" << ptype << " *)(v." << m.name << "))[i] = d[i];\n";
f << "\t\t\t}\n";
f << "\t\t\tbreak;\n";
}
if (is_union)
piBreak;
}
if (is_union) return;
piForeachC (PICodeParser::Entity * ce, e->children) {
if (ce->has_name) continue;
writeClassStreamMembersIn(f, ce, cnt);
}
}
void makeClassStream(PIFile & f, const PICodeParser::Entity * e) {
f << "\nPIByteArray & operator <<(PIByteArray & s, const " << e->name << " & v) {\n";
f << "\tPIChunkStream cs;\n";
int cnt = 0;
writeClassStreamMembersOut(f, e, cnt);
f << "\ts << cs.data();\n\treturn s;\n}\n";
f << "PIByteArray & operator >>(PIByteArray & s, " << e->name << " & v) {\n";
f << "\tif (s.size_s() < 4) return s;\n";
f << "\tPIByteArray csba; s >> csba;\n";
f << "\tPIChunkStream cs(csba);\n";
f << "\twhile (!cs.atEnd()) {\n";
f << "\t\tswitch (cs.read()) {\n";
cnt = 0;
writeClassStreamMembersIn(f, e, cnt);
f << "\t\t}\n\t}\n";
f << "\treturn s;\n}\n";
}
void makeClassStreamHeader(PIFile & f, const PICodeParser::Entity * e) {
PIStringList ml;
piForeachC (PICodeParser::Member & m, e->members) {
if (m.is_type_ptr) continue;
ml << m.name;
}
if (ml.isEmpty()) return;
f << "\nPIByteArray & operator <<(PIByteArray & s, const " << e->name << " & v);";
f << "\nPIByteArray & operator >>(PIByteArray & s, " << e->name << " & v);\n";
}
void makeGetter(PIFile & f, const PICodeParser::Entity * e) {
f << "\nPIByteArray getter" << toCName(e->name) << "(const void * p, const char * name) {\n";
f << "\tPIByteArray ret;\n";
f << "\tif (!p || !name) return ret;\n";
f << "\t" << e->name << " * o = (" << e->name << "*)p;\n";
piForeachC (PICodeParser::Member & m, e->members) {
if (m.is_type_ptr || m.isBitfield() || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public))
continue;
f << "\tif (strcmp(name, \"" << m.name << "\") == 0) {ret << o->" << m.name << "; return ret;}\n";
}
f << "\treturn ret;\n}\n";
}
/*
void makeGetterHeader(PIFile & f, const PICodeParser::Entity * e) {
f << "\nPIByteArray getter" << toCName(e->name) << "(const void * p, const char * name);";
}
*/
void writeModel(PICodeParser & parser, PICLI & cli, const PIString out, bool meta, bool enums, bool streams, bool texts, bool getters) {
PIVector<const PICodeParser::Entity * > ventities;
PIString defname = out.replaceAll(".", "_").replaceAll("/", "_").replaceAll(":", "_").replaceAll("-", "_").removeAll(" ").toUpperCase() + "_H";
bool inc_h, inc_cpp;
inc_h = streams || texts;
inc_cpp = !inc_h && getters;
PISet<PIString> inc_files;
piForeachC (PICodeParser::Entity * e, parser.entities)
if (e->name.find("::") < 0 && !e->name.startsWith("_PI"))
inc_files << e->file;
PIString inc_string;
PIVector<PIString> incf = inc_files.toVector();
piForeachC (PIString & i, incf) {
if (i != parser.mainFile())
inc_string << "\n#include \"" << i << "\"";
}
PIFile f(out + ".cpp");
f.clear();
f.open(PIIODevice::WriteOnly);
f << "// Generated by \"PIP Code model generator\" " << PIDateTime::current().toString("dd.MM.yyyy hh:mm:ss\n\n");
f << "#include <string.h>\n";
if (streams || texts)
f << "#include <pichunkstream.h>\n";
f << "#include \"" << out << ".h\"\n";
if (inc_cpp) f << inc_string;
f << "\nusing namespace PICodeInfo;\n\n";
/*
PIString entry, argtype, rettype, args;
piForeachC (PICodeParser::Entity * e, parser.entities) {
if (e->name.find("::") >= 0 || e->name.startsWith("_PI")) continue;
entry.clear();
entry << "\nPIVariant execFunction(" << e->name << " * object, const char * function, const PIVariant & arg0, const PIVariant & arg1, const PIVariant & arg2, const PIVariant & arg3) {\n";
const PIVector<PICodeParser::Member> & fl(e->functions);
bool efunc = true;
piForeachC (PICodeParser::Member & m, fl) {
if (m.name.startsWith("__stat") || m.attributes[PICodeParser::Static]) continue;
//piCout << e->name << m.name << m.visibility;
args.clear();
rettype = m.type;
if (rettype.startsWith("const") && rettype.endsWith("&"))
rettype.cutLeft(5).cutRight(1).trim();
if (rettype.endsWith("&")) continue;
bool aok = true, ret = (m.type != "void");
if (m.arguments_full.size() > 4 || m.visibility != PICodeParser::Public) continue;
for (int i = 0; i < m.arguments_full.size_s(); ++i) {
if (i > 0) args << ", ";
argtype = m.arguments_type[i];
if (argtype.startsWith("const") && argtype.endsWith("&"))
argtype.cutLeft(5).cutRight(1).trim();
if (argtype.endsWith("&")) {
aok = false;
continue;
}
//entry << "(" << m.arguments_type[i] << ")";
//if (parser.isEnum(m.arguments_type[i])) entry << "(int)";
args << "arg" << i << ".toValue<" << argtype << " >()";
}
if (!aok) continue;
efunc = false;
entry << "\tif (strcmp(function, \"" << m.name << "\") == 0) {";
if (ret) entry << "return PIVariant::fromValue<" << rettype << " >(";
entry << "object->" << m.name << "(" << args << ")";
if (ret) entry << ");";
else entry << "; return PIVariant();";
entry << "}\n";
}
if (efunc) continue;
f << entry << "\tPICout(PICoutManipulators::AddNewLine) << \"Can`t find function \\\"\" << function << \"\\\" in "
<< e->type << " \\\"" << e->name << "\\\"!\";\n\treturn PIVariant();\n}\n";
ventities << e;
}
*/
if (meta || enums || getters) {
if (getters) {
f << "\n\n// Getter funtions\n";
piForeachC (PICodeParser::Entity * e, parser.entities) {
if (!e->has_name || e->name.startsWith("_PI")) continue;
makeGetter(f, e);
}
}
f << "\n\n// Metainformation\n\n__ClassInfo_" << defname << "_Initializer__::__ClassInfo_" << defname << "_Initializer__() {\n";
f << "\tif (_inited_) return;\n\t_inited_ = true;\n\n";
if (meta) {
f << "\tClassInfo * ci, * pci = new ClassInfo();\n\tTypeInfo * ni;\n\tFunctionInfo * fi;\n\tTypeInfo ti;\n";
f << "\t(*classesInfo)[\"\"] = pci;\n";
}
if (enums) {
f << "\tEnumInfo * ei;\n";
f << "\t(*enumsInfo)[\"\"] = new EnumInfo();\n";
}
if (meta) {
f << "\n\n// Classes\n";
piForeachC (PICodeParser::Entity * e, parser.entities) {
if (e->name.startsWith("_PI")) continue;
makeClassInfo(f, e);
}
}
if (enums) {
f << "\n// Enums\n";
piForeachC (PICodeParser::Enum & e, parser.enums)
makeEnumInfo(f, &e);
}
if (getters) {
f << "\n// Getters\n";
piForeachC (PICodeParser::Entity * e, parser.entities) {
if (!e->has_name || e->name.startsWith("_PI")) continue;
f << "\t(*accessFunctions)[\"" << e->name << "\"] = getter" << toCName(e->name) << ";\n";
}
}
f << "}\n";
f << "\n\nbool __ClassInfo_" << defname << "_Initializer__::_inited_ = false;\n";
}
if (streams) {
f << "\n\n// Stream operators\n";
piForeachC (PICodeParser::Entity * e, parser.entities) {
if (!e->has_name || e->name.startsWith("_PI")) continue;
makeClassStream(f, e);
}
}
f.close();
f.setPath(out + ".h");
f.clear();
f.open(PIIODevice::WriteOnly);
f << "// Generated by \"PIP Code model generator\" " << PIDateTime::current().toString("dd.MM.yyyy hh:mm:ss\n");
f << "// Execute command:\n";
piForeachC (PIString & _a, cli.rawArguments())
f << "// \"" << _a << "\"\n";
f << "\n";
f << "#ifndef " << defname << "\n#define " << defname << "\n\n";
f << "#include \"pivariant.h\"\n#include \"picodeinfo.h\"";
if (inc_h) f << inc_string;
/*
f << "\n\n";
piForeachC (PICodeParser::Entity * e, ventities)
f << e->type << " " << e->name << ";\n";
f << "\n";
piForeachC (PICodeParser::Entity * e, ventities) {
f << "\nPIVariant execFunction(" << e->name << " * object, const char * function, const PIVariant & arg0 = PIVariant(), \
const PIVariant & arg1 = PIVariant(), const PIVariant & arg2 = PIVariant(), const PIVariant & arg3 = PIVariant());";
}
*/
if (streams) {
f << "\n\n// Stream operators\n";
piForeachC (PICodeParser::Entity * e, parser.entities) {
if (!e->has_name || e->name.startsWith("_PI")) continue;
makeClassStreamHeader(f, e);
}
}
/*if (getters) {
f << "\n\n// Getter funtions\n";
piForeachC (PICodeParser::Entity * e, parser.entities) {
if (!e->has_name || e->name.startsWith("_PI")) continue;
makeGetterHeader(f, e);
}
f << "\n";
}*/
if (meta || enums || getters) {
f << "\n\n// Metainformation\n\nclass __ClassInfo_" << defname << "_Initializer__ {\n";
f << "public:\n\t__ClassInfo_" << defname << "_Initializer__();\n\tstatic bool _inited_;\n};\n";
f << "\nstatic __ClassInfo_" << defname << "_Initializer__ __classinfo_" << defname.toLowerCase() << "_initializer__;\n";
}
f << "\n\n#endif // " << defname << "\n";
f.close();
}
int main(int argc, char * argv[]) {
PICLI cli(argc, argv);
//piCout << cli.rawArguments();
cli.setOptionalArgumentsCount(-1);
cli.addArgument("output", true);
cli.addArgument("help");
cli.addArgument("quiet");
cli.addArgument("All");
cli.addArgument("Metainfo");
cli.addArgument("Enum");
cli.addArgument("Stream");
cli.addArgument("Getter");
cli.addArgument("Text");
cli.addArgument("print");
cli.addArgument("Print");
cli.addArgument("single");
if (cli.hasArgument("help") || cli.argumentValue("output").isEmpty() || cli.optionalArguments().isEmpty()) {
usage();
return 0;
}
piDebug = !cli.hasArgument("quiet");
piForeachC (PIString & a, cli.rawArguments()) {
if (a.startsWith("-I")) parser.includeDirectory(a.mid(2));
if (a.startsWith("-D")) parser.addDefine(a.mid(2), PIString());
}
PIStringList files;
piForeachC (PIString & a, cli.optionalArguments())
if (!a.startsWith("-")) files << a;
piCout << Cyan << Bold << "Parse files" << files << "...";
parser.parseFiles(files, !cli.hasArgument("single"));
piCout << Cyan << Bold << "Parsing done";
piCout << Cyan << Bold << "Writing code model ...";
bool all = cli.hasArgument("All");
writeModel(parser, cli, cli.argumentValue("output"), cli.hasArgument("Metainfo") || all,
cli.hasArgument("Enum") || all,
cli.hasArgument("Stream") || all,
cli.hasArgument("Text") || all,
cli.hasArgument("Getter") || all);
piCout << Cyan << Bold << "Writing done";
if (cli.hasArgument("print") || cli.hasArgument("Print")) {
bool womain = cli.hasArgument("print");
piDebug = true;
PIStringList pf(parser.parsedFiles());
piForeachC (PIString & f, pf) {
if (!womain || (f != parser.mainFile()))
piCout << f;
}
}
return 0;
};