/* 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 . */ #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 [-I] [-I] [...] [-D] [-D] [...] [] [] [...]\"" << 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 " << Green << "- add include dir (e.g. -I.. -I../some_dir -I/usr/include)"; piCout << "-D " << 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 << "-T " << Green << "- write text serialize functions"; piCout << "-o " << 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 << " " << Green << "- add file to code model, all includes of this file will be proceed (e.g. \"main.cpp\")"; } 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 ml; piForeachC (PICodeParser::Member & m, e->members) { if (m.is_type_ptr) 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 ml; piForeachC (PICodeParser::Member & m, e->members) { if (m.is_type_ptr) 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 writeModel(PICodeParser & parser, PICLI & cli, const PIString out, bool meta, bool enums, bool streams, bool texts) { PIVector ventities; PIString defname = out.replaceAll(".", "_").replaceAll("/", "_").replaceAll(":", "_").replaceAll("-", "_").toUpperCase() + "_H"; PISet inc_files; piForeachC (PICodeParser::Entity * e, parser.entities) if (e->name.find("::") < 0 && !e->name.startsWith("_PI")) inc_files << e->file; 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 \n"; if (streams || texts) f << "#include \n"; f << "#include \"" << out << ".h\"\n"; 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 & 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) { 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); } 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 (streams || texts) { PIVector incf = inc_files.toVector(); piForeachC (PIString & i, incf) { if (i != parser.mainFile()) f << "\n#include \"" << i << "\""; } } /* 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 (meta || enums) { 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"; } 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); } } 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("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); 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; };