added ifdef for using pcall callback
[lqt.git] / cpptoxml / main.cpp
blobf438094f16f469b4ad4a4f63d25bfef27a564fbd
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 if (c->baseClasses().size()>0) {
286 QStringList fullBases;
287 ret += ATTR_STR("bases", c->baseClasses().join(";").append(";"));
288 Q_ASSERT (c->baseClasses().size() == c->baseModifiers().size());
289 for (int j=0;j<c->baseClasses().size();j++) {
290 fullBases.append(c->baseModifiers().at(j) + " " + c->baseClasses().at(j));
292 ret += ATTR_STR("bases_with_attributes", fullBases.join(";").append(";"));
294 switch(c->classType()) {
295 case CodeModel::Class:
296 ret += ATTR_STR("class_type", QString("class"));
297 break;
298 case CodeModel::Struct:
299 ret += ATTR_STR("class_type", QString("struct"));
300 break;
301 case CodeModel::Union:
302 ret += ATTR_STR("class_type", QString("union"));
303 break;
305 QString tp = templateParametersToString(c->templateParameters());
306 if (tp!=QString()) ret += ATTR_STR("member_template_parameters", tp);
307 // TODO also list propertyDeclarations (maybe in content?)
309 if (EnumModelItem e = model_dynamic_cast<EnumModelItem>(i)) {
310 switch (e->accessPolicy()) {
311 case CodeModel::Public:
312 ret += ATTR_STR("access", "public");
313 break;
314 case CodeModel::Private:
315 ret += ATTR_STR("access", "private");
316 break;
317 case CodeModel::Protected:
318 ret += ATTR_STR("access", "protected");
319 break;
322 if (EnumeratorModelItem e = model_dynamic_cast<EnumeratorModelItem>(i)) {
323 ret += ATTR_STR("value", e->value());
325 if (TypeAliasModelItem t = model_dynamic_cast<TypeAliasModelItem>(i)) {
326 ret += visit(t->type(), t->scope());
330 // content of the entry:
331 // - Arguments of functions
332 // - members of scopes
333 // - enumeration values
335 QString children;
336 if (NamespaceModelItem n = model_dynamic_cast<NamespaceModelItem>(i)) {
337 foreach(NamespaceModelItem m, n->namespaces())
338 children += visit(model_static_cast<CodeModelItem>(m));
340 if (i->kind() & _CodeModelItem::Kind_Scope) {
341 //qDebug() << ID_STR(i) << i->name() << current_context;
342 //CodeModelItem os = current_scope; // save old outer scope
343 if (!i->name().isEmpty()) { current_context << i->name(); current_scope << i; }
344 foreach(ClassModelItem n, model_dynamic_cast<ScopeModelItem>(i)->classes())
345 children += visit(model_static_cast<CodeModelItem>(n));
346 foreach(EnumModelItem n, model_dynamic_cast<ScopeModelItem>(i)->enums())
347 children += visit(model_static_cast<CodeModelItem>(n));
348 foreach(FunctionModelItem n, model_dynamic_cast<ScopeModelItem>(i)->functions())
349 children += visit(model_static_cast<CodeModelItem>(n));
350 foreach(TypeAliasModelItem n, model_dynamic_cast<ScopeModelItem>(i)->typeAliases())
351 children += visit(model_static_cast<CodeModelItem>(n));
352 foreach(VariableModelItem n, model_dynamic_cast<ScopeModelItem>(i)->variables())
353 children += visit(model_static_cast<CodeModelItem>(n));
354 if (!i->name().isEmpty()) { current_context.removeLast(); current_scope.pop_back(); }
356 if (FunctionModelItem f = model_dynamic_cast<FunctionModelItem>(i)) {
357 foreach(ArgumentModelItem a, f->arguments())
358 children += visit(model_static_cast<CodeModelItem>(a));
360 if (EnumModelItem e = model_dynamic_cast<EnumModelItem>(i)) {
361 QString last = QChar('0');
362 foreach(EnumeratorModelItem n, model_dynamic_cast<EnumModelItem>(i)->enumerators()) {
363 if (n->value() == QString())
364 n->setValue(last.append("+1")); //FIXME: Is there a reason for not putting the value itself? :S
365 children += visit(model_static_cast<CodeModelItem>(n));
366 last = n->value();
369 //ret.replace('&', "&amp;");
370 //ret.replace('>', "&gt;");
371 //ret.replace('<', "&lt;");
373 // TODO fix lua binding generator
374 if(false && children.isEmpty())
376 ret = "<" + ret + " />\n";
378 else
380 ret = "<" + ret + " >\n";
381 ret += children;
382 ret += "</";
383 ret += XMLTag(i);
384 ret += ">\n";
386 return ret;
389 void printHelp()
391 const char* help =
392 "Usage: cpptoxml <flags> <file>\n\n"
393 "Options:\n"
394 "<file> file to parse\n"
395 "-C optional parser config file\n"
396 "-P only preprocess\n"
397 "-R don't resolve\n"
398 "-N generate NO code\n"
399 "-v verbose\n"
400 "-d debug\n"
401 "-h print this help\n"
402 "-qt default qt config file: -C cpptoxml/parser/rpp/pp-qt-configuration\n"
403 "-I Add another include directory\n"
404 "-Q Qt include dir\n"
405 "-o output file\n"
407 fprintf(stderr, "%s", help);
411 int main (int argc, char **argv) {
412 bool onlyPreprocess = false;
413 bool dontResolve = false;
414 bool noCode = false;
415 bool verbose = false;
416 bool debug = false;
417 QString configName;
418 QString sourceName;
419 QStringList inclist;
420 QString outputFile;
421 QString qtdir;
423 for(int i=1; i<argc; i++) {
424 if(argv[i][0]=='-' && argv[i][1]!=0) {
425 QString argValue;
426 bool separed = (argv[i][2] == '\0' && argc > i+1 && argv[i+1][0] != '-');
427 if (separed)
428 argValue = QDir::fromNativeSeparators(QString::fromLatin1(argv[i+1]).right(strlen(argv[i+1])));
429 else
430 argValue = QDir::fromNativeSeparators(QString::fromLatin1(argv[i]).right(strlen(argv[i])-2));
432 switch(argv[i][1]) {
433 case 'C':
434 configName = argValue;
435 break;
436 case 'P':
437 onlyPreprocess = true;
438 break;
439 case 'R':
440 dontResolve = true;
441 break;
442 case 'N':
443 noCode = true;
444 break;
445 case 'v':
446 verbose = true;
447 break;
448 case 'd':
449 debug = true;
450 break;
451 case 'h':
452 printHelp();
453 return 0;
454 case 'I':
455 inclist.append(argValue);
456 break;
457 case 'Q':
458 qtdir = argValue;
459 break;
460 case 'q':{
461 if(QString(argv[i]).startsWith("-qt")) {
462 configName = "cpptoxml/parser/rpp/pp-qt-configuration";
463 #ifdef Q_OS_WIN
464 configName += QString("-win");
465 #endif
466 } else
467 fprintf(stderr, "found unknown parameter: -%s",argv[i]);
468 } break;
469 case 'o':
470 outputFile = argValue;
471 break;
473 default:
474 fprintf(stderr, "found unknown parameter: %s", argv[i]);
475 return 1;
477 if (separed)
478 i++;
479 } else {
480 sourceName = QString::fromLatin1(argv[i]);
484 if (qtdir.isEmpty())
485 qtdir = QDir::fromNativeSeparators(getenv("QT_INCLUDE"));
486 if (qtdir.isEmpty()) {
487 fprintf(stderr, "Generator requires Qt include dir as option -Q or QT_INCLUDE to be set\n");
488 return 1;
491 QString frameworkDir = "/Library/Frameworks";
492 if (!QFileInfo(sourceName).exists() || QFileInfo(sourceName).isDir()) {
493 QString qtincludefile = QDir::fromNativeSeparators(qtdir+'/'+sourceName+'/'+sourceName);
494 QString macincludefile = QString("%1/%2.framework/Headers/%2").arg(frameworkDir).arg(sourceName);
495 if (QFileInfo(qtincludefile).exists()) {
496 sourceName = qtincludefile;
497 } else if (QFileInfo(macincludefile).exists()) {
498 sourceName = macincludefile;
499 } else {
500 QString msg = "Error: wether '" + sourceName + "' nor '" + qtincludefile;
501 #if defined(Q_OS_MAC)
502 msg += "' or '" + macincludefile;
503 #endif
504 msg += "' found";
505 fprintf(stderr, "%s\n", msg.toLatin1().constData());
506 return 1;
510 if(verbose) fprintf(stderr, "Used file: %s", qPrintable(sourceName));
512 QString currentDir = QDir::current().absolutePath();
513 QFileInfo sourceInfo(sourceName);
514 //QDir::setCurrent(sourceInfo.absolutePath());
516 inclist << (sourceInfo.absolutePath());
517 inclist << (QDir::convertSeparators(qtdir));
519 QStringList qts;
520 qts << "QtXml" << "QtNetwork" << "QtCore" << "QtGui"
521 <<"QtOpenGL" << "QtWebKit"<< "QtScript" << "QtSvg";
523 Q_FOREACH(const QString& lib, qts) {
524 if (sourceName.contains(frameworkDir)) {
525 // TODO does not work with framework because there are no QtCore, QtGui, ... folders
526 inclist << QString("%1/%2.framework/Headers").arg(frameworkDir).arg(lib);
527 } else {
528 inclist << QDir::convertSeparators(qtdir + "/" + lib);
532 if(debug) qDebug() << "atdir: " << qtdir << "sourceName: " << sourceName << inclist;
535 Preprocessor pp;
536 pp.addIncludePaths(inclist);
537 pp.processFile(configName);
538 pp.processFile(sourceName);
539 QByteArray contents = pp.result();
541 if(debug) qDebug() << pp.macroNames();
542 if(debug) qDebug() << contents;
543 if(debug) QTextStream(stdout) << contents;
545 if (onlyPreprocess) {
546 QTextStream(stdout) << contents;
547 } else {
548 Control control;
549 Parser p(&control);
550 pool __pool;
552 TranslationUnitAST *ast = p.parse(contents, contents.size(), &__pool);
554 CodeModel model;
555 Binder binder(&model, p.location());
556 FileModelItem f_model = binder.run(ast);
558 if (!noCode) {
559 XMLVisitor visitor((CodeModelItem)f_model, !dontResolve);
560 QString xml = visitor.visit(model_static_cast<CodeModelItem>(f_model));
561 if (outputFile.isEmpty()) {
562 QTextStream(stdout) << xml;
563 } else {
564 QFile file(outputFile);
565 if (file.open(QFile::WriteOnly | QFile::Text)) {
566 file.write(xml.toLatin1());
572 return 0;