template experiments
[lqt/mk.git] / cpptoxml / main.cpp
blob1cd264ef840a70f2dbf4cbea6b2c3457a9accac2
1 /*
2 * Copyright 2009 Mauro Iazzi <mauro.iazzi@gmail.com>
3 * Copyright 2009 Peter Kümmel <syntheticpp@gmx.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
22 #include <iostream>
23 #include <typeinfo>
25 #include "binder.h"
26 #include "codemodel.h"
27 #include "control.h"
28 #include "parser.h"
29 #include "preprocessor.h"
31 #include <QByteArray>
32 #include <QFile>
33 #include <QTextCodec>
34 #include <QTextStream>
36 #include <QObject>
37 #include <QDir>
39 #include <QDebug>
42 #define ID_STR(i) (QString('_').append(QString::number(i->creationId())))
43 #define ATTR_STR(n, v) ( QString(' ') + escape_chars(n) + QString("=\"") + escape_chars(v) + QString('\"') )
44 #define ATTR_NUM(n, v) ( (QString::number(v)).prepend(" " n "=\"").append('\"') )
45 #define ATTR_TRUE(n) ( ATTR_NUM(n, 1) )
48 using namespace std;
50 QString escape_chars (QString s) {
51 QString ret = s;
52 ret.replace('&', "&amp;");
53 ret.replace('"', "&quot;");
54 ret.replace('\'', "&apos;");
55 ret.replace('>', "&gt;");
56 ret.replace('<', "&lt;");
57 return ret;
60 class XMLVisitor {
61 private:
62 bool resolve_types;
63 QString current_id;
64 QStringList current_context;
65 QList<CodeModelItem> current_scope;
66 CodeModelItem outer_scope;
67 public:
68 XMLVisitor(CodeModelItem c, bool r = true):
69 resolve_types(r), current_scope(), outer_scope(c) {
70 current_scope << c;
72 QString XMLTag(CodeModelItem);
73 TypeInfo solve(const TypeInfo&, QStringList);
74 TypeInfo simplifyType (TypeInfo const &, CodeModelItem __scope);
75 QString visit(const TypeInfo&, QStringList);
76 QString visit(CodeModelItem);
78 template <typename T> QString visit(T) {
79 std::cerr << "unimplemented CodeModelItem: " << typeid(T).name() << std::endl;
80 return "";
86 TypeInfo XMLVisitor::simplifyType (TypeInfo const &__type, CodeModelItem __scope)
88 CodeModel *__model = __scope->model ();
89 Q_ASSERT (__model != 0);
90 TypeInfo t;
91 for (int i=0;i<__type.qualifiedName().size();i++) {
92 QStringList qname = t.qualifiedName();
93 qname << __type.qualifiedName().at(i);
94 t.setQualifiedName(qname);
95 //t = this->solve(t, __scope->qualifiedName());
96 QString oldt = t.toString();
97 t = t.resolveType(t, __scope);
98 if (t.toString()!=oldt) qDebug() << oldt << " --> " << t.toString();
101 TypeInfo otherType = __type;
102 otherType.setQualifiedName(t.qualifiedName());
104 return otherType;
109 TypeInfo XMLVisitor::solve(const TypeInfo& t, QStringList scope) {
110 (void)scope;
111 if (!resolve_types) return t;
112 TypeInfo tt(t);
113 for (QList<CodeModelItem>::const_iterator i=current_scope.begin();
114 i<current_scope.end();
115 i++) {
116 TypeInfo ttt = tt;
117 //qDebug() << tt.toString() << ttt.toString();
118 Q_ASSERT(ttt==tt);
119 do {
120 tt = ttt;
121 ttt = ttt.resolveType(tt, *i);
122 } while (ttt!=tt);
125 return tt;
128 QString XMLVisitor::visit(const TypeInfo& t, QStringList scope) {
129 //t = t.resolveType(t, t.scope());
131 QString oldt = t.toString();
132 TypeInfo tt = solve(t, scope);
133 //tt = simplifyType(tt, current_scope.first());
134 while (oldt!=tt.toString()) {
135 oldt = tt.toString();
136 tt = solve(tt, scope);
138 //if (oldt!=tt.toString()) qDebug() << oldt << " -> " << tt.toString();
140 QString ret;
141 ret += ATTR_STR("type_name", tt.toString().replace(">>", "> >"));
142 ret += ATTR_STR("type_base", tt.qualifiedName().join("::").replace(">>", "> >"));
143 if (tt.isConstant()) ret += ATTR_TRUE("type_constant");
144 if (tt.isVolatile()) ret += ATTR_TRUE("type_volatile");
145 if (tt.isReference()) ret += ATTR_TRUE("type_reference");
146 if (tt.indirections()>0) ret += ATTR_NUM("indirections", tt.indirections());
148 QStringList arr = tt.arrayElements();
149 QString tmp = arr.join(",");
150 if (!tmp.isEmpty()) ret += " array=\"" + tmp + '\"';
152 if (tt.isFunctionPointer()) ret += ATTR_TRUE("function_pointer");
154 return ret;
157 #define TAG_CASE(s) case _CodeModelItem::Kind_##s: return #s
159 QString XMLVisitor::XMLTag(CodeModelItem i) {
160 switch (i->kind()) {
161 TAG_CASE(Scope);
162 TAG_CASE(Namespace);
163 TAG_CASE(Member);
164 TAG_CASE(Function);
165 TAG_CASE(Argument);
166 TAG_CASE(Class);
167 TAG_CASE(Enum);
168 TAG_CASE(Enumerator);
169 TAG_CASE(File);
170 TAG_CASE(FunctionDefinition);
171 TAG_CASE(TemplateParameter);
172 TAG_CASE(TypeAlias);
173 TAG_CASE(Variable);
175 return QString();
178 QString templateParametersToString (TemplateParameterList list) {
179 QString ret;
180 foreach(TemplateParameterModelItem p,list) {
181 ret = ret + p->name() + ';';
183 return ret;
186 QString XMLVisitor::visit(CodeModelItem i) {
187 QString ret;
188 ret += XMLTag(i);
190 current_id = ID_STR(i) + " => " + XMLTag(i) + " => " + i->qualifiedName().join("::"); // FIXME: this is debug code
192 ret += ATTR_STR("id", ID_STR(i));
193 ret += ATTR_STR("name", i->name());
194 ret += ATTR_STR("scope", i->scope().join("::"));
195 ret += ATTR_STR("context", current_context.join("::"));
196 // FIXME: is this a dirty hack? yes, it is!
197 if (ArgumentModelItem a = model_dynamic_cast<ArgumentModelItem>(i)) {
198 //ret += ATTR_STR("fullname", current_context.join("::")+"::"+i->qualifiedName().join("::"));
199 } else if (EnumeratorModelItem a = model_dynamic_cast<EnumeratorModelItem>(i)) {
200 ret += ATTR_STR("fullname", current_context.join("::")+"::"+i->qualifiedName().join("::"));
201 } else {
202 ret += ATTR_STR("fullname", i->qualifiedName().join("::"));
205 if (ScopeModelItem s = model_dynamic_cast<ScopeModelItem>(i)) {
206 ret += " members=\"";
208 if (NamespaceModelItem n = model_dynamic_cast<NamespaceModelItem>(i)) {
209 foreach(NamespaceModelItem m, n->namespaces())
210 ret += ID_STR(m).append(' ');
212 if (ScopeModelItem s = model_dynamic_cast<ScopeModelItem>(i)) {
213 foreach(ClassModelItem n, s->classes())
214 ret += ID_STR(n).append(' ');
215 foreach(EnumModelItem n, s->enums())
216 ret += ID_STR(n).append(' ');
217 foreach(FunctionModelItem n, s->functions())
218 ret += ID_STR(n).append(' ');
219 foreach(TypeAliasModelItem n, s->typeAliases())
220 ret += ID_STR(n).append(' ');
221 foreach(VariableModelItem n, s->variables())
222 ret += ID_STR(n).append(' ');
224 if (ScopeModelItem s = model_dynamic_cast<ScopeModelItem>(i)) {
225 ret += '\"';
227 if (MemberModelItem m = model_dynamic_cast<MemberModelItem>(i)) {
228 if (m->isConstant()) ret += ATTR_TRUE("constant");
229 if (m->isVolatile()) ret += ATTR_TRUE("volatile");
230 if (m->isStatic()) ret += ATTR_TRUE("static");
231 if (m->isAuto()) ret += ATTR_TRUE("auto");
232 if (m->isFriend()) ret += ATTR_TRUE("friend");
233 if (m->isRegister()) ret += ATTR_TRUE("register");
234 if (m->isExtern()) ret += ATTR_TRUE("extern");
235 if (m->isMutable()) ret += ATTR_TRUE("mutable");
236 QStringList ownerName = m->qualifiedName();
237 ownerName.pop_back();
238 ret += ATTR_STR("member_of", ownerName.join("::"));
240 if (ClassModelItem c = model_dynamic_cast<ClassModelItem>(current_scope.last()))
241 ret += ATTR_STR("member_of_class", c->qualifiedName().join("::"));
243 switch (m->accessPolicy()) {
244 case CodeModel::Public:
245 ret += ATTR_STR("access", "public");
246 break;
247 case CodeModel::Private:
248 ret += ATTR_STR("access", "private");
249 break;
250 case CodeModel::Protected:
251 ret += ATTR_STR("access", "protected");
252 break;
255 ret += visit(m->type(), m->scope());
256 QString tp = templateParametersToString(m->templateParameters());
257 if (tp!=QString()) ret += ATTR_STR("member_template_parameters", tp);
259 if (FunctionModelItem f = model_dynamic_cast<FunctionModelItem>(i)) {
260 if (f->isVirtual()) ret += ATTR_TRUE("virtual");
261 if (f->isInline()) ret += ATTR_TRUE("inline");
262 if (f->isExplicit()) ret += ATTR_TRUE("explicit");
263 if (f->isAbstract()) ret += ATTR_TRUE("abstract");
264 if (f->isVariadics()) ret += ATTR_TRUE("variadics");
265 //if (i->name()=="destroyed") qDebug() << CodeModel::Normal << CodeModel::Slot << CodeModel::Signal << m->functionType() << i->qualifiedName();
266 switch(f->functionType()) {
267 case CodeModel::Normal:
268 break;
269 case CodeModel::Slot:
270 ret += ATTR_TRUE("slot");
271 break;
272 case CodeModel::Signal:
273 ret += ATTR_TRUE("signal");
274 break;
277 if (ArgumentModelItem a = model_dynamic_cast<ArgumentModelItem>(i)) {
278 ret += visit(a->type(), a->scope());
279 if (a->defaultValue()) {
280 ret += ATTR_TRUE("default");
281 ret += ATTR_STR("defaultvalue", a->defaultValueExpression());
284 if (ClassModelItem c = model_dynamic_cast<ClassModelItem>(i)) {
285 switch (c->accessPolicy()) {
286 case CodeModel::Public:
287 ret += ATTR_STR("access", "public");
288 break;
289 case CodeModel::Private:
290 ret += ATTR_STR("access", "private");
291 break;
292 case CodeModel::Protected:
293 ret += ATTR_STR("access", "protected");
294 break;
297 if (c->baseClasses().size()>0) {
298 QStringList fullBases;
299 ret += ATTR_STR("bases", c->baseClasses().join(";").append(";"));
300 Q_ASSERT (c->baseClasses().size() == c->baseModifiers().size());
301 for (int j=0;j<c->baseClasses().size();j++) {
302 fullBases.append(c->baseModifiers().at(j) + " " + c->baseClasses().at(j));
304 ret += ATTR_STR("bases_with_attributes", fullBases.join(";").append(";"));
306 switch(c->classType()) {
307 case CodeModel::Class:
308 ret += ATTR_STR("class_type", QString("class"));
309 break;
310 case CodeModel::Struct:
311 ret += ATTR_STR("class_type", QString("struct"));
312 break;
313 case CodeModel::Union:
314 ret += ATTR_STR("class_type", QString("union"));
315 break;
317 QString tp = templateParametersToString(c->templateParameters());
318 if (tp!=QString()) ret += ATTR_STR("member_template_parameters", tp);
319 // TODO also list propertyDeclarations (maybe in content?)
321 if (EnumModelItem e = model_dynamic_cast<EnumModelItem>(i)) {
322 switch (e->accessPolicy()) {
323 case CodeModel::Public:
324 ret += ATTR_STR("access", "public");
325 break;
326 case CodeModel::Private:
327 ret += ATTR_STR("access", "private");
328 break;
329 case CodeModel::Protected:
330 ret += ATTR_STR("access", "protected");
331 break;
334 if (EnumeratorModelItem e = model_dynamic_cast<EnumeratorModelItem>(i)) {
335 ret += ATTR_STR("value", e->value());
337 if (TypeAliasModelItem t = model_dynamic_cast<TypeAliasModelItem>(i)) {
338 ret += visit(t->type(), t->scope());
342 // content of the entry:
343 // - Arguments of functions
344 // - members of scopes
345 // - enumeration values
347 QString children;
348 if (NamespaceModelItem n = model_dynamic_cast<NamespaceModelItem>(i)) {
349 foreach(NamespaceModelItem m, n->namespaces())
350 children += visit(model_static_cast<CodeModelItem>(m));
352 if (i->kind() & _CodeModelItem::Kind_Scope) {
353 //qDebug() << ID_STR(i) << i->name() << current_context;
354 //CodeModelItem os = current_scope; // save old outer scope
355 if (!i->name().isEmpty()) { current_context << i->name(); current_scope << i; }
356 foreach(ClassModelItem n, model_dynamic_cast<ScopeModelItem>(i)->classes())
357 children += visit(model_static_cast<CodeModelItem>(n));
358 foreach(EnumModelItem n, model_dynamic_cast<ScopeModelItem>(i)->enums())
359 children += visit(model_static_cast<CodeModelItem>(n));
360 foreach(FunctionModelItem n, model_dynamic_cast<ScopeModelItem>(i)->functions())
361 children += visit(model_static_cast<CodeModelItem>(n));
362 foreach(TypeAliasModelItem n, model_dynamic_cast<ScopeModelItem>(i)->typeAliases())
363 children += visit(model_static_cast<CodeModelItem>(n));
364 foreach(VariableModelItem n, model_dynamic_cast<ScopeModelItem>(i)->variables())
365 children += visit(model_static_cast<CodeModelItem>(n));
366 if (!i->name().isEmpty()) { current_context.removeLast(); current_scope.pop_back(); }
368 if (FunctionModelItem f = model_dynamic_cast<FunctionModelItem>(i)) {
369 foreach(ArgumentModelItem a, f->arguments())
370 children += visit(model_static_cast<CodeModelItem>(a));
372 if (EnumModelItem e = model_dynamic_cast<EnumModelItem>(i)) {
373 QString last = QChar('0');
374 foreach(EnumeratorModelItem n, model_dynamic_cast<EnumModelItem>(i)->enumerators()) {
375 if (n->value() == QString())
376 n->setValue(last.append("+1")); //FIXME: Is there a reason for not putting the value itself? :S
377 children += visit(model_static_cast<CodeModelItem>(n));
378 last = n->value();
381 //ret.replace('&', "&amp;");
382 //ret.replace('>', "&gt;");
383 //ret.replace('<', "&lt;");
385 // TODO fix lua binding generator
386 if(false && children.isEmpty())
388 ret = "<" + ret + " />\n";
390 else
392 ret = "<" + ret + " >\n";
393 ret += children;
394 ret += "</";
395 ret += XMLTag(i);
396 ret += ">\n";
398 return ret;
401 void printHelp()
403 const char* help =
404 "Usage: cpptoxml <flags> <file>\n\n"
405 "Options:\n"
406 "<file> file to parse\n"
407 "-C optional parser config file\n"
408 "-P only preprocess\n"
409 "-R don't resolve\n"
410 "-N generate NO code\n"
411 "-v verbose\n"
412 "-d debug\n"
413 "-h print this help\n"
414 "-qt default qt config file: -C cpptoxml/parser/rpp/pp-qt-configuration\n"
415 "-I Add another include directory\n"
416 "-Q Qt include dir\n"
417 "-o output file\n"
419 fprintf(stderr, "%s", help);
423 int main (int argc, char **argv) {
424 bool onlyPreprocess = false;
425 bool dontResolve = false;
426 bool noCode = false;
427 bool verbose = false;
428 bool debug = false;
429 QString configName;
430 QString sourceName;
431 QStringList inclist;
432 QString outputFile;
433 QString qtdir;
435 for(int i=1; i<argc; i++) {
436 if(argv[i][0]=='-' && argv[i][1]!=0) {
437 QString argValue;
438 bool separed = (argv[i][2] == '\0' && argc > i+1 && argv[i+1][0] != '-');
439 if (separed)
440 argValue = QDir::fromNativeSeparators(QString::fromLatin1(argv[i+1]).right(strlen(argv[i+1])));
441 else
442 argValue = QDir::fromNativeSeparators(QString::fromLatin1(argv[i]).right(strlen(argv[i])-2));
444 switch(argv[i][1]) {
445 case 'C':
446 configName = argValue;
447 break;
448 case 'P':
449 onlyPreprocess = true;
450 break;
451 case 'R':
452 dontResolve = true;
453 break;
454 case 'N':
455 noCode = true;
456 break;
457 case 'v':
458 verbose = true;
459 break;
460 case 'd':
461 debug = true;
462 break;
463 case 'h':
464 printHelp();
465 return 0;
466 case 'I':
467 inclist.append(argValue);
468 break;
469 case 'Q':
470 qtdir = argValue;
471 break;
472 case 'q':{
473 if(QString(argv[i]).startsWith("-qt")) {
474 configName = "cpptoxml/parser/rpp/pp-qt-configuration";
475 #ifdef Q_OS_WIN
476 configName += QString("-win");
477 #endif
478 } else
479 fprintf(stderr, "found unknown parameter: -%s",argv[i]);
480 } break;
481 case 'o':
482 outputFile = argValue;
483 break;
485 default:
486 fprintf(stderr, "found unknown parameter: %s", argv[i]);
487 return 1;
489 if (separed)
490 i++;
491 } else {
492 sourceName = QString::fromLatin1(argv[i]);
496 if (qtdir.isEmpty())
497 qtdir = QDir::fromNativeSeparators(getenv("QT_INCLUDE"));
498 if (qtdir.isEmpty()) {
499 fprintf(stderr, "Generator requires Qt include dir as option -Q or QT_INCLUDE to be set\n");
500 return 1;
503 QString frameworkDir = "/Library/Frameworks";
504 if (!QFileInfo(sourceName).exists() || QFileInfo(sourceName).isDir()) {
505 QString qtincludefile = QDir::fromNativeSeparators(qtdir+'/'+sourceName+'/'+sourceName);
506 QString macincludefile = QString("%1/%2.framework/Headers/%2").arg(frameworkDir).arg(sourceName);
507 if (QFileInfo(qtincludefile).exists()) {
508 sourceName = qtincludefile;
509 } else if (QFileInfo(macincludefile).exists()) {
510 sourceName = macincludefile;
511 } else {
512 QString msg = "Error: wether '" + sourceName + "' nor '" + qtincludefile;
513 #if defined(Q_OS_MAC)
514 msg += "' or '" + macincludefile;
515 #endif
516 msg += "' found";
517 fprintf(stderr, "%s\n", msg.toLatin1().constData());
518 return 1;
522 if(verbose) fprintf(stderr, "Used file: %s", qPrintable(sourceName));
524 QString currentDir = QDir::current().absolutePath();
525 QFileInfo sourceInfo(sourceName);
526 //QDir::setCurrent(sourceInfo.absolutePath());
528 inclist << (sourceInfo.absolutePath());
529 inclist << (QDir::convertSeparators(qtdir));
531 QStringList qts;
532 qts << "QtXml" << "QtNetwork" << "QtCore" << "QtGui"
533 <<"QtOpenGL" << "QtWebKit"<< "QtScript" << "QtSvg";
535 Q_FOREACH(const QString& lib, qts) {
536 if (sourceName.contains(frameworkDir)) {
537 // TODO does not work with framework because there are no QtCore, QtGui, ... folders
538 inclist << QString("%1/%2.framework/Headers").arg(frameworkDir).arg(lib);
539 } else {
540 inclist << QDir::convertSeparators(qtdir + "/" + lib);
544 if(debug) qDebug() << "atdir: " << qtdir << "sourceName: " << sourceName << inclist;
547 Preprocessor pp;
548 pp.addIncludePaths(inclist);
549 pp.processFile(configName);
550 pp.processFile(sourceName);
551 QByteArray contents = pp.result();
553 if(debug) qDebug() << pp.macroNames();
554 if(debug) qDebug() << contents;
555 if(debug) QTextStream(stdout) << contents;
557 if (onlyPreprocess) {
558 QTextStream(stdout) << contents;
559 } else {
560 Control control;
561 Parser p(&control);
562 pool __pool;
564 TranslationUnitAST *ast = p.parse(contents, contents.size(), &__pool);
566 CodeModel model;
567 Binder binder(&model, p.location());
568 FileModelItem f_model = binder.run(ast);
570 if (!noCode) {
571 XMLVisitor visitor((CodeModelItem)f_model, !dontResolve);
572 QString xml = visitor.visit(model_static_cast<CodeModelItem>(f_model));
573 if (outputFile.isEmpty()) {
574 QTextStream(stdout) << xml;
575 } else {
576 QFile file(outputFile);
577 if (file.open(QFile::WriteOnly | QFile::Text)) {
578 file.write(xml.toLatin1());
584 return 0;