code format
This commit is contained in:
@@ -1,600 +1,645 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Code model generator
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "picli.h"
|
||||
#include "picodeparser.h"
|
||||
#include "piiostream.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace PICoutManipulators;
|
||||
|
||||
PICodeParser parser;
|
||||
|
||||
const char help_string[] =
|
||||
"-M (Metainfo)\n"
|
||||
"Generate classes and structs info. It contains\n"
|
||||
"names, subclass info, all methods and variables.\n"
|
||||
"Place information:\n"
|
||||
" * classes and structs - PICodeInfo::classesInfo\n"
|
||||
" * methods - ClassInfo::functions\n"
|
||||
" * variables - ClassInfo::variables\n"
|
||||
"\n"
|
||||
"-E (Enums)\n"
|
||||
"Generate enumeration descriptions.\n"
|
||||
"Useful for GUI integrations, because\n"
|
||||
"you can obtain enumerators value and name.\n"
|
||||
" * enums - PICodeInfo::enumsInfo\n"
|
||||
"\n"
|
||||
"-S (Stream operators)\n"
|
||||
"Generate store/restore operators with format\n"
|
||||
"BINARY_STREAM_WRITE(<type>);\n"
|
||||
"BINARY_STREAM_READ (<type>);\n"
|
||||
"Only public variables used. All variables stored/restored\n"
|
||||
"using PIChunkStream. IDs are variable number, starting from 1.\n"
|
||||
"You can override ID with PIMETA(id=<ID>). If in class or struct\n"
|
||||
"PIMETA(simple-stream) presence, then variables stored/restored\n"
|
||||
"with simple << and >> operators.\n"
|
||||
"If PIMETA(no-stream) presence, then class or struct ignored.\n"
|
||||
"\n"
|
||||
"-G (Getter functions)\n"
|
||||
"Generate anonymous access methods for member typenames and values.\n"
|
||||
"Every class or struct member typename can be obtained with:\n"
|
||||
"const char * getMemberType(const char * class_name, const char * member_name)\n"
|
||||
"Member value can be obtained with:\n"
|
||||
"PIByteArray getMemberValue(const void * p, const char * class_name, const char * member_name)\n"
|
||||
"where \"p\" - class or struct pointer, and returns serialized value.\n"
|
||||
"PIVariant getMemberAsVariant(const void * p, const char * class_name, const char * member_name)\n"
|
||||
"where \"p\" - class or struct pointer, and returns value as registered PIVariant.\n"
|
||||
""
|
||||
;
|
||||
|
||||
void header() {
|
||||
piCout << Bold << "PIP Code model generator";
|
||||
piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine;
|
||||
piCout << Green << Bold << "Usage:" << Default << "\"pip_cmg [-hHqPpsAMESTG] -o <output_file> [-I<include_dir1>] [-I<include_dir1>] [...] [-D<define1>] [-D<define1>] [...] <file1> [<file2>] [<file3>] [...]\"" << NewLine;
|
||||
}
|
||||
|
||||
void usage() {
|
||||
header();
|
||||
piCout << Green << Bold << "Details:";
|
||||
piCout << Bold << "Debug control";
|
||||
piCout << "-h " << Green << "- display this message and exit";
|
||||
piCout << "-H " << Green << "- display details help";
|
||||
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, macro 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\")";
|
||||
}
|
||||
|
||||
void help() {
|
||||
header();
|
||||
piCout << help_string;
|
||||
}
|
||||
|
||||
void printError(const PIString & msg) {
|
||||
std::cerr << msg.data() << std::endl;
|
||||
}
|
||||
|
||||
|
||||
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(PIIOTextStream & ts, const PICodeParser::Entity * e) {
|
||||
ts << "\n\t{\n\tClassInfo * ci = new ClassInfo();\n";
|
||||
ts << "\tci->type = \"" << e->type << "\";\n";
|
||||
ts << "\tci->name = \"" << e->name << "\";\n";
|
||||
ts << "\tci->has_name = " << (e->has_name ? "true" : "false") << ";\n";
|
||||
if (!e->meta.isEmpty()) {
|
||||
auto i = e->meta.makeIterator();
|
||||
while (i.next())
|
||||
ts << "\tci->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
|
||||
}
|
||||
ts << "\t(*classesInfo)[ci->name] = ci;\n";
|
||||
if (e->parent_scope) {
|
||||
ts << "\tpci = " << "classesInfo->value(\"" << e->parent_scope->name << "\", 0);\n";
|
||||
ts << "\tif (pci) pci->children_info << ci;\n";
|
||||
}
|
||||
for (const PICodeParser::Entity * p: e->parents)
|
||||
ts << "\tci->parents << \"" << p->name << "\";\n";
|
||||
if (!e->members.isEmpty()) ts << "\n\tTypeInfo ti;\n";
|
||||
for (const PICodeParser::Member & m: e->members) {
|
||||
ts << "\tti = TypeInfo(\"" << m.name << "\", \"" << m.type << "\"";
|
||||
if (m.attributes != 0) {
|
||||
bool fir = true;
|
||||
ts << ", ";
|
||||
if (m.attributes[PICodeParser::Const ]) {if (fir) fir = false; else ts << " | "; ts << "Const";}
|
||||
if (m.attributes[PICodeParser::Static ]) {if (fir) fir = false; else ts << " | "; ts << "Static";}
|
||||
if (m.attributes[PICodeParser::Mutable ]) {if (fir) fir = false; else ts << " | "; ts << "Mutable";}
|
||||
if (m.attributes[PICodeParser::Volatile]) {if (fir) fir = false; else ts << " | "; ts << "Volatile";}
|
||||
if (m.attributes[PICodeParser::Inline ]) {if (fir) fir = false; else ts << " | "; ts << "Inline";}
|
||||
if (m.attributes[PICodeParser::Virtual ]) {if (fir) fir = false; else ts << " | "; ts << "Virtual";}
|
||||
} else {
|
||||
if (m.isBitfield())
|
||||
ts << ", 0";
|
||||
}
|
||||
if (m.isBitfield())
|
||||
ts << ", " << m.bits;
|
||||
ts << ");\n";
|
||||
if (!m.meta.isEmpty()) {
|
||||
for (PICodeParser::MetaMap::const_iterator i = m.meta.begin(); i != m.meta.end(); ++i)
|
||||
ts << "\tti.meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
|
||||
}
|
||||
ts << "\tci->variables << ti;\n";
|
||||
}
|
||||
PIString arg;
|
||||
bool has_fi = false;
|
||||
for (const PICodeParser::Member & m: e->functions) {
|
||||
if (e->name.findCWord(m.name) >= 0) continue;
|
||||
if (!has_fi)
|
||||
ts << "\n\tFunctionInfo * fi;\n";
|
||||
has_fi = true;
|
||||
ts << "\tci->functions.push_back(FunctionInfo()); fi = &(ci->functions.back());\n";
|
||||
ts << "\tfi->name = \"" << m.name << "\";";
|
||||
ts << " fi->return_type = TypeInfo(\"\", \"" << m.type << "\"";
|
||||
if (m.attributes[PICodeParser::Const] || m.attributes[PICodeParser::Static]) {
|
||||
bool fir = true;
|
||||
ts << ", ";
|
||||
if (m.attributes[PICodeParser::Const ]) {if (fir) fir = false; else ts << " | "; ts << "Const";}
|
||||
if (m.attributes[PICodeParser::Static]) {if (fir) fir = false; else ts << " | "; ts << "Static";}
|
||||
}
|
||||
ts << ");\n";
|
||||
//piCout << "write func" << m.name;
|
||||
for (const PIString & a: m.arguments_full) {
|
||||
//piCout << "write arg" << a;
|
||||
ts << "\tfi->arguments << TypeInfo(";
|
||||
arg = a;
|
||||
bool con = false;
|
||||
arg.prepend(" ");
|
||||
if (arg.find(" const ") >= 0) {
|
||||
arg.replaceAll(" const ", " ");
|
||||
con = true;
|
||||
}
|
||||
arg.trim();
|
||||
int i = 0;
|
||||
for (i = arg.size_s() - 1; i > 0; --i)
|
||||
if (!_isCChar(arg[i]) && !(arg[i].isDigit())) break;
|
||||
ts << "\"" << arg.takeRight(arg.size_s() - i - 1).trim() << "\", ";
|
||||
ts << "\"" << arg.trim() << "\"";
|
||||
if (con) ts << ", Const";
|
||||
ts << ");\n";
|
||||
}
|
||||
if (!m.meta.isEmpty()) {
|
||||
for (PICodeParser::MetaMap::const_iterator i = m.meta.begin(); i != m.meta.end(); ++i)
|
||||
ts << "\tfi->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
|
||||
}
|
||||
}
|
||||
ts << "\n\t}";
|
||||
}
|
||||
|
||||
|
||||
void makeEnumInfo(PIIOTextStream & ts, const PICodeParser::Enum * e) {
|
||||
if (e->name.isEmpty()) {
|
||||
ts << "\n\tei = (*enumsInfo)[\"\"];\n";
|
||||
} else {
|
||||
ts << "\n\tei = new EnumInfo();\n";
|
||||
ts << "\t(*enumsInfo)[\"" << e->name << "\"] = ei;\n";
|
||||
ts << "\tei->name = \"" << e->name << "\";\n";
|
||||
if (!e->meta.isEmpty()) {
|
||||
auto i = e->meta.makeIterator();
|
||||
while (i.next())
|
||||
ts << "\tei->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
|
||||
}
|
||||
}
|
||||
for (const PICodeParser::EnumeratorInfo & m: e->members) {
|
||||
ts << "\tei->members << PICodeInfo::EnumeratorInfo(\"" << m.name << "\", " << m.value << ");\n";
|
||||
if (!m.meta.isEmpty()) {
|
||||
auto i = m.meta.makeIterator();
|
||||
while (i.next())
|
||||
ts << "\tei->members.back().meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool writeClassStreamMembersOut(PIIOTextStream & ts, const PICodeParser::Entity * e, int & cnt, bool simple) {
|
||||
PIVector<PICodeParser::Member> ml;
|
||||
for (const PICodeParser::Member & m: e->members) {
|
||||
if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue;
|
||||
ml << m;
|
||||
}
|
||||
bool is_union = e->type == "union";
|
||||
PISet<int> used_id;
|
||||
for (const PICodeParser::Member & m: ml) {
|
||||
if (is_union && m.isBitfield())
|
||||
continue;
|
||||
if (m.meta.value("id") == "-") continue;
|
||||
++cnt;
|
||||
if (m.meta.contains("id"))
|
||||
cnt = m.meta.value("id").toInt();
|
||||
if (used_id[cnt]) {
|
||||
printError("Error with \"" + e->name + "\" stream operator: ID " + PIString::fromNumber(cnt) + " already used");
|
||||
return false;
|
||||
}
|
||||
if (m.dims.isEmpty()) {
|
||||
if (simple) {
|
||||
ts << "\ts << ";
|
||||
if (parser.isEnum(m.type))
|
||||
ts << "(int)";
|
||||
ts << "v." << m.name << ";\n";
|
||||
} else {
|
||||
ts << "\tcs << cs.chunk(" << cnt << ", ";
|
||||
if (parser.isEnum(m.type))
|
||||
ts << "(int)";
|
||||
ts << "v." << m.name << ");\n";
|
||||
}
|
||||
} else {
|
||||
PIString ptype = m.type.left(m.type.find('[')).trim();
|
||||
PIString size = m.dims[0];
|
||||
for (int i = 1; i < m.dims.size_s(); ++i) {
|
||||
size += " * ";
|
||||
size += m.dims[i];
|
||||
}
|
||||
if (simple) {
|
||||
ts << "\tfor (int i = 0; i < " << size << "; ++i)\n";
|
||||
ts << "\t\ts << ((const " << ptype << " *)(v." << m.name << "))[i];\n";
|
||||
} else {
|
||||
ts << "\tcs << cs.chunk(" << cnt << ", PIVector<" << ptype << " >((const " << ptype << " *)(v." << m.name << "), ";
|
||||
ts << size << "));\n";
|
||||
}
|
||||
}
|
||||
if (is_union)
|
||||
break;
|
||||
}
|
||||
if (is_union) return true;
|
||||
for (const PICodeParser::Entity * ce: e->children) {
|
||||
if (ce->has_name) continue;
|
||||
if (!writeClassStreamMembersOut(ts, ce, cnt, simple)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool writeClassStreamMembersIn(PIIOTextStream & ts, const PICodeParser::Entity * e, int & cnt, bool simple) {
|
||||
PIVector<PICodeParser::Member> ml;
|
||||
for (const PICodeParser::Member & m: e->members) {
|
||||
if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue;
|
||||
ml << m;
|
||||
}
|
||||
bool is_union = e->type == "union";
|
||||
PISet<int> used_id;
|
||||
for (const PICodeParser::Member & m: ml) {
|
||||
if (is_union && m.isBitfield())
|
||||
continue;
|
||||
if (m.meta.value("id") == "-") continue;
|
||||
++cnt;
|
||||
if (m.meta.contains("id"))
|
||||
cnt = m.meta.value("id").toInt();
|
||||
if (used_id[cnt]) {
|
||||
printError("Error with \"" + e->name + "\" stream operator: ID " + PIString::fromNumber(cnt) + " already used");
|
||||
return false;
|
||||
}
|
||||
used_id << cnt;
|
||||
if (m.dims.isEmpty()) {
|
||||
bool is_enum = parser.isEnum(m.type);
|
||||
if (simple) {
|
||||
ts << "\t";
|
||||
if (is_enum) ts << "{int i; ";
|
||||
ts << "s >> ";
|
||||
if (is_enum) ts << "i;";
|
||||
else ts << "v." << m.name << ";";
|
||||
if (is_enum) ts << " v." << m.name << " = (" << m.type << ")i;}";
|
||||
ts << "\n";
|
||||
} else {
|
||||
ts << "\t\tcase " << cnt << ":";
|
||||
if (is_enum) ts << " {int i;";
|
||||
ts << " cs.get(";
|
||||
if (is_enum) ts << "i";
|
||||
else ts << "v." << m.name;
|
||||
ts << ");";
|
||||
if (is_enum) ts << " v." << m.name << " = (" << m.type << ")i;}";
|
||||
ts << " break;\n";
|
||||
}
|
||||
} else {
|
||||
PIString ptype = m.type.left(m.type.find('[')).trim();
|
||||
PIString size = m.dims[0];
|
||||
for (int i = 1; i < m.dims.size_s(); ++i) {
|
||||
size += " * ";
|
||||
size += m.dims[i];
|
||||
}
|
||||
if (simple) {
|
||||
ts << "\tfor (int i = 0; i < " << size << "; ++i)\n";
|
||||
ts << "\t\ts >> ((" << ptype << " *)(v." << m.name << "))[i];\n";
|
||||
} else {
|
||||
ts << "\t\tcase " << cnt << ": {\n\t\t\tPIVector<" << ptype << " > d; cs.get(d);\n";
|
||||
ts << "\t\t\tint cnt = piMini(d.size_s(), " << size << ");\n";
|
||||
ts << "\t\t\tfor (int i = 0; i < cnt; ++i)\n";
|
||||
ts << "\t\t\t\t((" << ptype << " *)(v." << m.name << "))[i] = d[i];\n";
|
||||
ts << "\t\t\t}\n";
|
||||
ts << "\t\t\tbreak;\n";
|
||||
}
|
||||
}
|
||||
if (is_union)
|
||||
break;
|
||||
}
|
||||
if (is_union) return true;
|
||||
for (const PICodeParser::Entity * ce: e->children) {
|
||||
if (ce->has_name) continue;
|
||||
if (!writeClassStreamMembersIn(ts, ce, cnt, simple)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool needClassStream(const PICodeParser::Entity * e) {
|
||||
if (e->meta.contains("no-stream")) return false;
|
||||
for (const PICodeParser::Member & m: e->members) {
|
||||
if (m.is_type_ptr || m.isBitfield() || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public))
|
||||
continue;
|
||||
if (m.meta.value("id") == "-") continue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool makeClassStream(PIIOTextStream & ts, const PICodeParser::Entity * e) {
|
||||
if (!needClassStream(e)) return true;
|
||||
bool simple = e->meta.contains("simple-stream");
|
||||
ts << "\nBINARY_STREAM_WRITE(" << e->name << ") {\n";
|
||||
if (!simple)
|
||||
ts << "\tPIChunkStream cs;\n";
|
||||
int cnt = 0;
|
||||
if (!writeClassStreamMembersOut(ts, e, cnt, simple)) return false;
|
||||
if (!simple)
|
||||
ts << "\ts << cs.data();\n";
|
||||
ts << "\treturn s;\n}\n";
|
||||
ts << "BINARY_STREAM_READ (" << e->name << ") {\n";
|
||||
if (!simple) {
|
||||
//ts << "\tif (s.size_s() < 4) return s;\n";
|
||||
ts << "\tPIByteArray csba; s >> csba;\n";
|
||||
ts << "\tPIChunkStream cs(csba);\n";
|
||||
ts << "\twhile (!cs.atEnd()) {\n";
|
||||
ts << "\t\tswitch (cs.read()) {\n";
|
||||
}
|
||||
cnt = 0;
|
||||
if (!writeClassStreamMembersIn(ts, e, cnt, simple)) return false;
|
||||
if (!simple)
|
||||
ts << "\t\t}\n\t}\n";
|
||||
ts << "\treturn s;\n}\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void makeGetterType(PIIOTextStream & ts, const PICodeParser::Entity * e) {
|
||||
if (!needClassStream(e)) return;
|
||||
ts << "\nconst char * getterType" << toCName(e->name) << "(const char * name) {\n";
|
||||
ts << "\tif (!name) return \"\";\n";
|
||||
for (const PICodeParser::Member & m: e->members) {
|
||||
if (m.is_type_ptr || m.isBitfield() || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public))
|
||||
continue;
|
||||
ts << "\tif (strcmp(name, \"" << m.name << "\") == 0) return \"" << m.type << "\";\n";
|
||||
}
|
||||
ts << "\treturn \"\";\n}\n";
|
||||
}
|
||||
|
||||
|
||||
void makeGetterValue(PIIOTextStream & ts, const PICodeParser::Entity * e) {
|
||||
if (!needClassStream(e)) return;
|
||||
ts << "\nPIByteArray getterValue" << toCName(e->name) << "(const void * p, const char * name) {\n";
|
||||
ts << "\tPIByteArray ret;\n";
|
||||
ts << "\tif (!p || !name) return ret;\n";
|
||||
ts << "\t" << e->name << " * o = (" << e->name << "*)p;\n";
|
||||
for (const PICodeParser::Member & m: e->members) {
|
||||
if (m.is_type_ptr || m.isBitfield() || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public))
|
||||
continue;
|
||||
ts << "\tif (strcmp(name, \"" << m.name << "\") == 0) {serialize(ret, o->" << m.name << "); return ret;}\n";
|
||||
}
|
||||
ts << "\treturn ret;\n}\n";
|
||||
}
|
||||
|
||||
|
||||
bool writeModel(PICodeParser & parser, PICLI & cli, const PIString out, bool meta, bool enums, bool streams, bool texts, bool getters) {
|
||||
PIString defname = "CCM_" + PIString::fromNumber(out.hash()) + "_H";
|
||||
PISet<PIString> inc_files;
|
||||
for (const PICodeParser::Entity * e: parser.entities)
|
||||
if (!e->name.startsWith("_PI"))
|
||||
inc_files << e->file;
|
||||
PIString inc_string;
|
||||
PIVector<PIString> incf = inc_files.toVector();
|
||||
for (const PIString & i: incf) {
|
||||
if ((i != parser.mainFile()) && (streams || texts || getters))
|
||||
inc_string += "\n#include \"" + i + "\"";
|
||||
}
|
||||
|
||||
PIFile f(out + ".cpp");
|
||||
f.clear();
|
||||
if (!f.open(PIIODevice::WriteOnly)) {
|
||||
piCout << "Error: can`t open out file" << f.path();
|
||||
return false;
|
||||
}
|
||||
PIIOTextStream ts(&f);
|
||||
ts << "// Generated by \"PIP Code model generator\" " << PIDateTime::current().toString("dd.MM.yyyy hh:mm:ss\n\n");
|
||||
ts << "#include <string.h>\n";
|
||||
ts << "#include \"" << out << ".h\"\n";
|
||||
ts << "\nusing namespace PICodeInfo;\n";
|
||||
|
||||
if (meta || enums || getters) {
|
||||
if (getters) {
|
||||
ts << "\n\n// Getter funtions\n";
|
||||
for (const PICodeParser::Entity * e: parser.entities) {
|
||||
if (!e->has_name || e->name.startsWith("_PI")) continue;
|
||||
makeGetterType(ts, e);
|
||||
makeGetterValue(ts, e);
|
||||
}
|
||||
}
|
||||
ts << "\n\n// Metainformation\n\n__ClassInfo_" << defname << "_Initializer__::__ClassInfo_" << defname << "_Initializer__() {\n";
|
||||
ts << "\tif (_inited_) return;\n\t_inited_ = true;\n\n";
|
||||
if (meta) {
|
||||
ts << "\tClassInfo * pci = new ClassInfo();\n";
|
||||
ts << "\t(*classesInfo)[\"\"] = pci;\n";
|
||||
}
|
||||
if (enums && !parser.enums.isEmpty()) {
|
||||
ts << "\tEnumInfo * ei;\n";
|
||||
ts << "\t(*enumsInfo)[\"\"] = new EnumInfo();\n";
|
||||
}
|
||||
if (meta) {
|
||||
ts << "\n\n// Classes\n";
|
||||
for (const PICodeParser::Entity * e: parser.entities) {
|
||||
if (e->name.startsWith("_PI")) continue;
|
||||
makeClassInfo(ts, e);
|
||||
}
|
||||
}
|
||||
if (enums) {
|
||||
ts << "\n// Enums\n";
|
||||
for (const PICodeParser::Enum & e: parser.enums)
|
||||
makeEnumInfo(ts, &e);
|
||||
}
|
||||
if (getters) {
|
||||
ts << "\n// Getters\n";
|
||||
for (const PICodeParser::Entity * e: parser.entities) {
|
||||
if (!needClassStream(e)) continue;
|
||||
if (!e->has_name || e->name.startsWith("_PI")) continue;
|
||||
ts << "\t(*accessValueFunctions)[\"" << e->name << "\"] = getterValue" << toCName(e->name) << ";\n";
|
||||
ts << "\t(*accessTypeFunctions)[\"" << e->name << "\"] = getterType" << toCName(e->name) << ";\n";
|
||||
}
|
||||
}
|
||||
ts << "}\n";
|
||||
ts << "\n\nbool __ClassInfo_" << defname << "_Initializer__::_inited_ = false;\n";
|
||||
}
|
||||
|
||||
f.close();
|
||||
|
||||
|
||||
f.setPath(out + ".h");
|
||||
f.clear();
|
||||
if (!f.open(PIIODevice::WriteOnly)) {
|
||||
piCout << "Error: can`t open out file" << f.path();
|
||||
return false;
|
||||
}
|
||||
ts << "// Generated by \"PIP Code model generator\" " << PIDateTime::current().toString("dd.MM.yyyy hh:mm:ss\n");
|
||||
ts << "// Execute command:\n";
|
||||
for (const PIString & _a: cli.rawArguments())
|
||||
ts << "// \"" << _a << "\"\n";
|
||||
ts << "\n";
|
||||
ts << "#ifndef " << defname << "\n#define " << defname << "\n\n";
|
||||
ts << "#include <pivariant.h>\n#include <picodeinfo.h>";
|
||||
if (streams || texts)
|
||||
ts << "\n#include <pichunkstream.h>";
|
||||
ts << inc_string << "\n";
|
||||
if (streams) {
|
||||
ts << "\n\n// Stream operators\n";
|
||||
for (const PICodeParser::Entity * e: parser.entities) {
|
||||
if (!e->has_name || e->name.startsWith("_PI") ||
|
||||
!(e->visibility == PICodeParser::Global || e->visibility == PICodeParser::Public)) continue;
|
||||
if (!makeClassStream(ts, e))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (meta || enums || getters) {
|
||||
ts << "\n\n// Metainformation\n\nclass __ClassInfo_" << defname << "_Initializer__ {\n";
|
||||
ts << "public:\n\t__ClassInfo_" << defname << "_Initializer__();\n\tstatic bool _inited_;\n};\n";
|
||||
ts << "\nstatic __ClassInfo_" << defname << "_Initializer__ __classinfo_" << defname.toLowerCase() << "_initializer__;\n";
|
||||
}
|
||||
ts << "\n\n#endif // " << defname << "\n";
|
||||
f.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
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("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")) {
|
||||
help();
|
||||
return 0;
|
||||
}
|
||||
if (cli.hasArgument("help") || cli.argumentValue("output").isEmpty() || cli.optionalArguments().isEmpty()) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
piDebug = !cli.hasArgument("quiet");
|
||||
for (const 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;
|
||||
for (const 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");
|
||||
if (!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))
|
||||
return 1;
|
||||
piCout << Cyan << Bold << "Writing done";
|
||||
if (cli.hasArgument("print") || cli.hasArgument("Print")) {
|
||||
bool womain = cli.hasArgument("print");
|
||||
piDebug = true;
|
||||
PIStringList pf(parser.parsedFiles());
|
||||
for (const PIString & f: pf) {
|
||||
if (!womain || (f != parser.mainFile()))
|
||||
piCout << f;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Code model generator
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "picli.h"
|
||||
#include "picodeparser.h"
|
||||
#include "piiostream.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace PICoutManipulators;
|
||||
|
||||
PICodeParser parser;
|
||||
|
||||
const char help_string[] = "-M (Metainfo)\n"
|
||||
"Generate classes and structs info. It contains\n"
|
||||
"names, subclass info, all methods and variables.\n"
|
||||
"Place information:\n"
|
||||
" * classes and structs - PICodeInfo::classesInfo\n"
|
||||
" * methods - ClassInfo::functions\n"
|
||||
" * variables - ClassInfo::variables\n"
|
||||
"\n"
|
||||
"-E (Enums)\n"
|
||||
"Generate enumeration descriptions.\n"
|
||||
"Useful for GUI integrations, because\n"
|
||||
"you can obtain enumerators value and name.\n"
|
||||
" * enums - PICodeInfo::enumsInfo\n"
|
||||
"\n"
|
||||
"-S (Stream operators)\n"
|
||||
"Generate store/restore operators with format\n"
|
||||
"BINARY_STREAM_WRITE(<type>);\n"
|
||||
"BINARY_STREAM_READ (<type>);\n"
|
||||
"Only public variables used. All variables stored/restored\n"
|
||||
"using PIChunkStream. IDs are variable number, starting from 1.\n"
|
||||
"You can override ID with PIMETA(id=<ID>). If in class or struct\n"
|
||||
"PIMETA(simple-stream) presence, then variables stored/restored\n"
|
||||
"with simple << and >> operators.\n"
|
||||
"If PIMETA(no-stream) presence, then class or struct ignored.\n"
|
||||
"\n"
|
||||
"-G (Getter functions)\n"
|
||||
"Generate anonymous access methods for member typenames and values.\n"
|
||||
"Every class or struct member typename can be obtained with:\n"
|
||||
"const char * getMemberType(const char * class_name, const char * member_name)\n"
|
||||
"Member value can be obtained with:\n"
|
||||
"PIByteArray getMemberValue(const void * p, const char * class_name, const char * member_name)\n"
|
||||
"where \"p\" - class or struct pointer, and returns serialized value.\n"
|
||||
"PIVariant getMemberAsVariant(const void * p, const char * class_name, const char * member_name)\n"
|
||||
"where \"p\" - class or struct pointer, and returns value as registered PIVariant.\n"
|
||||
"";
|
||||
|
||||
void header() {
|
||||
piCout << Bold << "PIP Code model generator";
|
||||
piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine;
|
||||
piCout << Green << Bold << "Usage:" << Default
|
||||
<< "\"pip_cmg [-hHqPpsAMESTG] -o <output_file> [-I<include_dir1>] [-I<include_dir1>] [...] [-D<define1>] [-D<define1>] [...] "
|
||||
"<file1> [<file2>] [<file3>] [...]\""
|
||||
<< NewLine;
|
||||
}
|
||||
|
||||
void usage() {
|
||||
header();
|
||||
piCout << Green << Bold << "Details:";
|
||||
piCout << Bold << "Debug control";
|
||||
piCout << "-h " << Green << "- display this message and exit";
|
||||
piCout << "-H " << Green << "- display details help";
|
||||
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, macro 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\")";
|
||||
}
|
||||
|
||||
void help() {
|
||||
header();
|
||||
piCout << help_string;
|
||||
}
|
||||
|
||||
void printError(const PIString & msg) {
|
||||
std::cerr << msg.data() << std::endl;
|
||||
}
|
||||
|
||||
|
||||
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(PIIOTextStream & ts, const PICodeParser::Entity * e) {
|
||||
ts << "\n\t{\n\tClassInfo * ci = new ClassInfo();\n";
|
||||
ts << "\tci->type = \"" << e->type << "\";\n";
|
||||
ts << "\tci->name = \"" << e->name << "\";\n";
|
||||
ts << "\tci->has_name = " << (e->has_name ? "true" : "false") << ";\n";
|
||||
if (!e->meta.isEmpty()) {
|
||||
auto i = e->meta.makeIterator();
|
||||
while (i.next())
|
||||
ts << "\tci->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
|
||||
}
|
||||
ts << "\t(*classesInfo)[ci->name] = ci;\n";
|
||||
if (e->parent_scope) {
|
||||
ts << "\tpci = "
|
||||
<< "classesInfo->value(\"" << e->parent_scope->name << "\", 0);\n";
|
||||
ts << "\tif (pci) pci->children_info << ci;\n";
|
||||
}
|
||||
for (const PICodeParser::Entity * p: e->parents)
|
||||
ts << "\tci->parents << \"" << p->name << "\";\n";
|
||||
if (!e->members.isEmpty()) ts << "\n\tTypeInfo ti;\n";
|
||||
for (const PICodeParser::Member & m: e->members) {
|
||||
ts << "\tti = TypeInfo(\"" << m.name << "\", \"" << m.type << "\"";
|
||||
if (m.attributes != 0) {
|
||||
bool fir = true;
|
||||
ts << ", ";
|
||||
if (m.attributes[PICodeParser::Const]) {
|
||||
if (fir)
|
||||
fir = false;
|
||||
else
|
||||
ts << " | ";
|
||||
ts << "Const";
|
||||
}
|
||||
if (m.attributes[PICodeParser::Static]) {
|
||||
if (fir)
|
||||
fir = false;
|
||||
else
|
||||
ts << " | ";
|
||||
ts << "Static";
|
||||
}
|
||||
if (m.attributes[PICodeParser::Mutable]) {
|
||||
if (fir)
|
||||
fir = false;
|
||||
else
|
||||
ts << " | ";
|
||||
ts << "Mutable";
|
||||
}
|
||||
if (m.attributes[PICodeParser::Volatile]) {
|
||||
if (fir)
|
||||
fir = false;
|
||||
else
|
||||
ts << " | ";
|
||||
ts << "Volatile";
|
||||
}
|
||||
if (m.attributes[PICodeParser::Inline]) {
|
||||
if (fir)
|
||||
fir = false;
|
||||
else
|
||||
ts << " | ";
|
||||
ts << "Inline";
|
||||
}
|
||||
if (m.attributes[PICodeParser::Virtual]) {
|
||||
if (fir)
|
||||
fir = false;
|
||||
else
|
||||
ts << " | ";
|
||||
ts << "Virtual";
|
||||
}
|
||||
} else {
|
||||
if (m.isBitfield()) ts << ", 0";
|
||||
}
|
||||
if (m.isBitfield()) ts << ", " << m.bits;
|
||||
ts << ");\n";
|
||||
if (!m.meta.isEmpty()) {
|
||||
for (PICodeParser::MetaMap::const_iterator i = m.meta.begin(); i != m.meta.end(); ++i)
|
||||
ts << "\tti.meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
|
||||
}
|
||||
ts << "\tci->variables << ti;\n";
|
||||
}
|
||||
PIString arg;
|
||||
bool has_fi = false;
|
||||
for (const PICodeParser::Member & m: e->functions) {
|
||||
if (e->name.findCWord(m.name) >= 0) continue;
|
||||
if (!has_fi) ts << "\n\tFunctionInfo * fi;\n";
|
||||
has_fi = true;
|
||||
ts << "\tci->functions.push_back(FunctionInfo()); fi = &(ci->functions.back());\n";
|
||||
ts << "\tfi->name = \"" << m.name << "\";";
|
||||
ts << " fi->return_type = TypeInfo(\"\", \"" << m.type << "\"";
|
||||
if (m.attributes[PICodeParser::Const] || m.attributes[PICodeParser::Static]) {
|
||||
bool fir = true;
|
||||
ts << ", ";
|
||||
if (m.attributes[PICodeParser::Const]) {
|
||||
if (fir)
|
||||
fir = false;
|
||||
else
|
||||
ts << " | ";
|
||||
ts << "Const";
|
||||
}
|
||||
if (m.attributes[PICodeParser::Static]) {
|
||||
if (fir)
|
||||
fir = false;
|
||||
else
|
||||
ts << " | ";
|
||||
ts << "Static";
|
||||
}
|
||||
}
|
||||
ts << ");\n";
|
||||
// piCout << "write func" << m.name;
|
||||
for (const PIString & a: m.arguments_full) {
|
||||
// piCout << "write arg" << a;
|
||||
ts << "\tfi->arguments << TypeInfo(";
|
||||
arg = a;
|
||||
bool con = false;
|
||||
arg.prepend(" ");
|
||||
if (arg.find(" const ") >= 0) {
|
||||
arg.replaceAll(" const ", " ");
|
||||
con = true;
|
||||
}
|
||||
arg.trim();
|
||||
int i = 0;
|
||||
for (i = arg.size_s() - 1; i > 0; --i)
|
||||
if (!_isCChar(arg[i]) && !(arg[i].isDigit())) break;
|
||||
ts << "\"" << arg.takeRight(arg.size_s() - i - 1).trim() << "\", ";
|
||||
ts << "\"" << arg.trim() << "\"";
|
||||
if (con) ts << ", Const";
|
||||
ts << ");\n";
|
||||
}
|
||||
if (!m.meta.isEmpty()) {
|
||||
for (PICodeParser::MetaMap::const_iterator i = m.meta.begin(); i != m.meta.end(); ++i)
|
||||
ts << "\tfi->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
|
||||
}
|
||||
}
|
||||
ts << "\n\t}";
|
||||
}
|
||||
|
||||
|
||||
void makeEnumInfo(PIIOTextStream & ts, const PICodeParser::Enum * e) {
|
||||
if (e->name.isEmpty()) {
|
||||
ts << "\n\tei = (*enumsInfo)[\"\"];\n";
|
||||
} else {
|
||||
ts << "\n\tei = new EnumInfo();\n";
|
||||
ts << "\t(*enumsInfo)[\"" << e->name << "\"] = ei;\n";
|
||||
ts << "\tei->name = \"" << e->name << "\";\n";
|
||||
if (!e->meta.isEmpty()) {
|
||||
auto i = e->meta.makeIterator();
|
||||
while (i.next())
|
||||
ts << "\tei->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
|
||||
}
|
||||
}
|
||||
for (const PICodeParser::EnumeratorInfo & m: e->members) {
|
||||
ts << "\tei->members << PICodeInfo::EnumeratorInfo(\"" << m.name << "\", " << m.value << ");\n";
|
||||
if (!m.meta.isEmpty()) {
|
||||
auto i = m.meta.makeIterator();
|
||||
while (i.next())
|
||||
ts << "\tei->members.back().meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool writeClassStreamMembersOut(PIIOTextStream & ts, const PICodeParser::Entity * e, int & cnt, bool simple) {
|
||||
PIVector<PICodeParser::Member> ml;
|
||||
for (const PICodeParser::Member & m: e->members) {
|
||||
if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue;
|
||||
ml << m;
|
||||
}
|
||||
bool is_union = e->type == "union";
|
||||
PISet<int> used_id;
|
||||
for (const PICodeParser::Member & m: ml) {
|
||||
if (is_union && m.isBitfield()) continue;
|
||||
if (m.meta.value("id") == "-") continue;
|
||||
++cnt;
|
||||
if (m.meta.contains("id")) cnt = m.meta.value("id").toInt();
|
||||
if (used_id[cnt]) {
|
||||
printError("Error with \"" + e->name + "\" stream operator: ID " + PIString::fromNumber(cnt) + " already used");
|
||||
return false;
|
||||
}
|
||||
if (m.dims.isEmpty()) {
|
||||
if (simple) {
|
||||
ts << "\ts << ";
|
||||
if (parser.isEnum(m.type)) ts << "(int)";
|
||||
ts << "v." << m.name << ";\n";
|
||||
} else {
|
||||
ts << "\tcs << cs.chunk(" << cnt << ", ";
|
||||
if (parser.isEnum(m.type)) ts << "(int)";
|
||||
ts << "v." << m.name << ");\n";
|
||||
}
|
||||
} else {
|
||||
PIString ptype = m.type.left(m.type.find('[')).trim();
|
||||
PIString size = m.dims[0];
|
||||
for (int i = 1; i < m.dims.size_s(); ++i) {
|
||||
size += " * ";
|
||||
size += m.dims[i];
|
||||
}
|
||||
if (simple) {
|
||||
ts << "\tfor (int i = 0; i < " << size << "; ++i)\n";
|
||||
ts << "\t\ts << ((const " << ptype << " *)(v." << m.name << "))[i];\n";
|
||||
} else {
|
||||
ts << "\tcs << cs.chunk(" << cnt << ", PIVector<" << ptype << " >((const " << ptype << " *)(v." << m.name << "), ";
|
||||
ts << size << "));\n";
|
||||
}
|
||||
}
|
||||
if (is_union) break;
|
||||
}
|
||||
if (is_union) return true;
|
||||
for (const PICodeParser::Entity * ce: e->children) {
|
||||
if (ce->has_name) continue;
|
||||
if (!writeClassStreamMembersOut(ts, ce, cnt, simple)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool writeClassStreamMembersIn(PIIOTextStream & ts, const PICodeParser::Entity * e, int & cnt, bool simple) {
|
||||
PIVector<PICodeParser::Member> ml;
|
||||
for (const PICodeParser::Member & m: e->members) {
|
||||
if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue;
|
||||
ml << m;
|
||||
}
|
||||
bool is_union = e->type == "union";
|
||||
PISet<int> used_id;
|
||||
for (const PICodeParser::Member & m: ml) {
|
||||
if (is_union && m.isBitfield()) continue;
|
||||
if (m.meta.value("id") == "-") continue;
|
||||
++cnt;
|
||||
if (m.meta.contains("id")) cnt = m.meta.value("id").toInt();
|
||||
if (used_id[cnt]) {
|
||||
printError("Error with \"" + e->name + "\" stream operator: ID " + PIString::fromNumber(cnt) + " already used");
|
||||
return false;
|
||||
}
|
||||
used_id << cnt;
|
||||
if (m.dims.isEmpty()) {
|
||||
bool is_enum = parser.isEnum(m.type);
|
||||
if (simple) {
|
||||
ts << "\t";
|
||||
if (is_enum) ts << "{int i; ";
|
||||
ts << "s >> ";
|
||||
if (is_enum)
|
||||
ts << "i;";
|
||||
else
|
||||
ts << "v." << m.name << ";";
|
||||
if (is_enum) ts << " v." << m.name << " = (" << m.type << ")i;}";
|
||||
ts << "\n";
|
||||
} else {
|
||||
ts << "\t\tcase " << cnt << ":";
|
||||
if (is_enum) ts << " {int i;";
|
||||
ts << " cs.get(";
|
||||
if (is_enum)
|
||||
ts << "i";
|
||||
else
|
||||
ts << "v." << m.name;
|
||||
ts << ");";
|
||||
if (is_enum) ts << " v." << m.name << " = (" << m.type << ")i;}";
|
||||
ts << " break;\n";
|
||||
}
|
||||
} else {
|
||||
PIString ptype = m.type.left(m.type.find('[')).trim();
|
||||
PIString size = m.dims[0];
|
||||
for (int i = 1; i < m.dims.size_s(); ++i) {
|
||||
size += " * ";
|
||||
size += m.dims[i];
|
||||
}
|
||||
if (simple) {
|
||||
ts << "\tfor (int i = 0; i < " << size << "; ++i)\n";
|
||||
ts << "\t\ts >> ((" << ptype << " *)(v." << m.name << "))[i];\n";
|
||||
} else {
|
||||
ts << "\t\tcase " << cnt << ": {\n\t\t\tPIVector<" << ptype << " > d; cs.get(d);\n";
|
||||
ts << "\t\t\tint cnt = piMini(d.size_s(), " << size << ");\n";
|
||||
ts << "\t\t\tfor (int i = 0; i < cnt; ++i)\n";
|
||||
ts << "\t\t\t\t((" << ptype << " *)(v." << m.name << "))[i] = d[i];\n";
|
||||
ts << "\t\t\t}\n";
|
||||
ts << "\t\t\tbreak;\n";
|
||||
}
|
||||
}
|
||||
if (is_union) break;
|
||||
}
|
||||
if (is_union) return true;
|
||||
for (const PICodeParser::Entity * ce: e->children) {
|
||||
if (ce->has_name) continue;
|
||||
if (!writeClassStreamMembersIn(ts, ce, cnt, simple)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool needClassStream(const PICodeParser::Entity * e) {
|
||||
if (e->meta.contains("no-stream")) return false;
|
||||
for (const PICodeParser::Member & m: e->members) {
|
||||
if (m.is_type_ptr || m.isBitfield() || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public)) continue;
|
||||
if (m.meta.value("id") == "-") continue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool makeClassStream(PIIOTextStream & ts, const PICodeParser::Entity * e) {
|
||||
if (!needClassStream(e)) return true;
|
||||
bool simple = e->meta.contains("simple-stream");
|
||||
ts << "\nBINARY_STREAM_WRITE(" << e->name << ") {\n";
|
||||
if (!simple) ts << "\tPIChunkStream cs;\n";
|
||||
int cnt = 0;
|
||||
if (!writeClassStreamMembersOut(ts, e, cnt, simple)) return false;
|
||||
if (!simple) ts << "\ts << cs.data();\n";
|
||||
ts << "\treturn s;\n}\n";
|
||||
ts << "BINARY_STREAM_READ (" << e->name << ") {\n";
|
||||
if (!simple) {
|
||||
// ts << "\tif (s.size_s() < 4) return s;\n";
|
||||
ts << "\tPIByteArray csba; s >> csba;\n";
|
||||
ts << "\tPIChunkStream cs(csba);\n";
|
||||
ts << "\twhile (!cs.atEnd()) {\n";
|
||||
ts << "\t\tswitch (cs.read()) {\n";
|
||||
}
|
||||
cnt = 0;
|
||||
if (!writeClassStreamMembersIn(ts, e, cnt, simple)) return false;
|
||||
if (!simple) ts << "\t\t}\n\t}\n";
|
||||
ts << "\treturn s;\n}\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void makeGetterType(PIIOTextStream & ts, const PICodeParser::Entity * e) {
|
||||
if (!needClassStream(e)) return;
|
||||
ts << "\nconst char * getterType" << toCName(e->name) << "(const char * name) {\n";
|
||||
ts << "\tif (!name) return \"\";\n";
|
||||
for (const PICodeParser::Member & m: e->members) {
|
||||
if (m.is_type_ptr || m.isBitfield() || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public)) continue;
|
||||
ts << "\tif (strcmp(name, \"" << m.name << "\") == 0) return \"" << m.type << "\";\n";
|
||||
}
|
||||
ts << "\treturn \"\";\n}\n";
|
||||
}
|
||||
|
||||
|
||||
void makeGetterValue(PIIOTextStream & ts, const PICodeParser::Entity * e) {
|
||||
if (!needClassStream(e)) return;
|
||||
ts << "\nPIByteArray getterValue" << toCName(e->name) << "(const void * p, const char * name) {\n";
|
||||
ts << "\tPIByteArray ret;\n";
|
||||
ts << "\tif (!p || !name) return ret;\n";
|
||||
ts << "\t" << e->name << " * o = (" << e->name << "*)p;\n";
|
||||
for (const PICodeParser::Member & m: e->members) {
|
||||
if (m.is_type_ptr || m.isBitfield() || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public)) continue;
|
||||
ts << "\tif (strcmp(name, \"" << m.name << "\") == 0) {serialize(ret, o->" << m.name << "); return ret;}\n";
|
||||
}
|
||||
ts << "\treturn ret;\n}\n";
|
||||
}
|
||||
|
||||
|
||||
bool writeModel(PICodeParser & parser, PICLI & cli, const PIString out, bool meta, bool enums, bool streams, bool texts, bool getters) {
|
||||
PIString defname = "CCM_" + PIString::fromNumber(out.hash()) + "_H";
|
||||
PISet<PIString> inc_files;
|
||||
for (const PICodeParser::Entity * e: parser.entities)
|
||||
if (!e->name.startsWith("_PI")) inc_files << e->file;
|
||||
PIString inc_string;
|
||||
PIVector<PIString> incf = inc_files.toVector();
|
||||
for (const PIString & i: incf) {
|
||||
if ((i != parser.mainFile()) && (streams || texts || getters)) inc_string += "\n#include \"" + i + "\"";
|
||||
}
|
||||
|
||||
PIFile f(out + ".cpp");
|
||||
f.clear();
|
||||
if (!f.open(PIIODevice::WriteOnly)) {
|
||||
piCout << "Error: can`t open out file" << f.path();
|
||||
return false;
|
||||
}
|
||||
PIIOTextStream ts(&f);
|
||||
ts << "// Generated by \"PIP Code model generator\" " << PIDateTime::current().toString("dd.MM.yyyy hh:mm:ss\n\n");
|
||||
ts << "#include <string.h>\n";
|
||||
ts << "#include \"" << out << ".h\"\n";
|
||||
ts << "\nusing namespace PICodeInfo;\n";
|
||||
|
||||
if (meta || enums || getters) {
|
||||
if (getters) {
|
||||
ts << "\n\n// Getter funtions\n";
|
||||
for (const PICodeParser::Entity * e: parser.entities) {
|
||||
if (!e->has_name || e->name.startsWith("_PI")) continue;
|
||||
makeGetterType(ts, e);
|
||||
makeGetterValue(ts, e);
|
||||
}
|
||||
}
|
||||
ts << "\n\n// Metainformation\n\n__ClassInfo_" << defname << "_Initializer__::__ClassInfo_" << defname << "_Initializer__() {\n";
|
||||
ts << "\tif (_inited_) return;\n\t_inited_ = true;\n\n";
|
||||
if (meta) {
|
||||
ts << "\tClassInfo * pci = new ClassInfo();\n";
|
||||
ts << "\t(*classesInfo)[\"\"] = pci;\n";
|
||||
}
|
||||
if (enums && !parser.enums.isEmpty()) {
|
||||
ts << "\tEnumInfo * ei;\n";
|
||||
ts << "\t(*enumsInfo)[\"\"] = new EnumInfo();\n";
|
||||
}
|
||||
if (meta) {
|
||||
ts << "\n\n// Classes\n";
|
||||
for (const PICodeParser::Entity * e: parser.entities) {
|
||||
if (e->name.startsWith("_PI")) continue;
|
||||
makeClassInfo(ts, e);
|
||||
}
|
||||
}
|
||||
if (enums) {
|
||||
ts << "\n// Enums\n";
|
||||
for (const PICodeParser::Enum & e: parser.enums)
|
||||
makeEnumInfo(ts, &e);
|
||||
}
|
||||
if (getters) {
|
||||
ts << "\n// Getters\n";
|
||||
for (const PICodeParser::Entity * e: parser.entities) {
|
||||
if (!needClassStream(e)) continue;
|
||||
if (!e->has_name || e->name.startsWith("_PI")) continue;
|
||||
ts << "\t(*accessValueFunctions)[\"" << e->name << "\"] = getterValue" << toCName(e->name) << ";\n";
|
||||
ts << "\t(*accessTypeFunctions)[\"" << e->name << "\"] = getterType" << toCName(e->name) << ";\n";
|
||||
}
|
||||
}
|
||||
ts << "}\n";
|
||||
ts << "\n\nbool __ClassInfo_" << defname << "_Initializer__::_inited_ = false;\n";
|
||||
}
|
||||
|
||||
f.close();
|
||||
|
||||
|
||||
f.setPath(out + ".h");
|
||||
f.clear();
|
||||
if (!f.open(PIIODevice::WriteOnly)) {
|
||||
piCout << "Error: can`t open out file" << f.path();
|
||||
return false;
|
||||
}
|
||||
ts << "// Generated by \"PIP Code model generator\" " << PIDateTime::current().toString("dd.MM.yyyy hh:mm:ss\n");
|
||||
ts << "// Execute command:\n";
|
||||
for (const PIString & _a: cli.rawArguments())
|
||||
ts << "// \"" << _a << "\"\n";
|
||||
ts << "\n";
|
||||
ts << "#ifndef " << defname << "\n#define " << defname << "\n\n";
|
||||
ts << "#include <pivariant.h>\n#include <picodeinfo.h>";
|
||||
if (streams || texts) ts << "\n#include <pichunkstream.h>";
|
||||
ts << inc_string << "\n";
|
||||
if (streams) {
|
||||
ts << "\n\n// Stream operators\n";
|
||||
for (const PICodeParser::Entity * e: parser.entities) {
|
||||
if (!e->has_name || e->name.startsWith("_PI") ||
|
||||
!(e->visibility == PICodeParser::Global || e->visibility == PICodeParser::Public))
|
||||
continue;
|
||||
if (!makeClassStream(ts, e)) return false;
|
||||
}
|
||||
}
|
||||
if (meta || enums || getters) {
|
||||
ts << "\n\n// Metainformation\n\nclass __ClassInfo_" << defname << "_Initializer__ {\n";
|
||||
ts << "public:\n\t__ClassInfo_" << defname << "_Initializer__();\n\tstatic bool _inited_;\n};\n";
|
||||
ts << "\nstatic __ClassInfo_" << defname << "_Initializer__ __classinfo_" << defname.toLowerCase() << "_initializer__;\n";
|
||||
}
|
||||
ts << "\n\n#endif // " << defname << "\n";
|
||||
f.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
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("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")) {
|
||||
help();
|
||||
return 0;
|
||||
}
|
||||
if (cli.hasArgument("help") || cli.argumentValue("output").isEmpty() || cli.optionalArguments().isEmpty()) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
piDebug = !cli.hasArgument("quiet");
|
||||
for (const 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;
|
||||
for (const 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");
|
||||
if (!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))
|
||||
return 1;
|
||||
piCout << Cyan << Bold << "Writing done";
|
||||
if (cli.hasArgument("print") || cli.hasArgument("Print")) {
|
||||
bool womain = cli.hasArgument("print");
|
||||
piDebug = true;
|
||||
PIStringList pf(parser.parsedFiles());
|
||||
for (const PIString & f: pf) {
|
||||
if (!womain || (f != parser.mainFile())) piCout << f;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user