only store modifiers in the new list
[lqt.git] / cpptoxml / main.cpp
blobdea8a2412a1b865e53549ebd18ded09d81a1a812
1 /*
2 * Copyright 2008 Mauro Iazzi <mauro.iazzi@gmail.com>
3 * Copyright 2008 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(' ') + n + QString("=\"") + 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 class XMLVisitor {
51 private:
52 bool resolve_types;
53 QString current_id;
54 QStringList current_context;
55 QList<CodeModelItem> current_scope;
56 CodeModelItem outer_scope;
57 public:
58 XMLVisitor(CodeModelItem c, bool r = true):
59 resolve_types(r), current_scope(), outer_scope(c) {
60 current_scope << c;
62 QString XMLTag(CodeModelItem);
63 TypeInfo solve(const TypeInfo&, QStringList);
64 TypeInfo simplifyType (TypeInfo const &, CodeModelItem __scope);
65 QString visit(const TypeInfo&, QStringList);
66 QString visit(CodeModelItem);
68 template <typename T> QString visit(T) {
69 std::cerr << "unimplemented CodeModelItem: " << typeid(T).name() << std::endl;
70 return "";
76 TypeInfo XMLVisitor::simplifyType (TypeInfo const &__type, CodeModelItem __scope)
78 CodeModel *__model = __scope->model ();
79 Q_ASSERT (__model != 0);
80 TypeInfo t;
81 for (int i=0;i<__type.qualifiedName().size();i++) {
82 QStringList qname = t.qualifiedName();
83 qname << __type.qualifiedName().at(i);
84 t.setQualifiedName(qname);
85 //t = this->solve(t, __scope->qualifiedName());
86 QString oldt = t.toString();
87 t = t.resolveType(t, __scope);
88 if (t.toString()!=oldt) qDebug() << oldt << " --> " << t.toString();
91 TypeInfo otherType = __type;
92 otherType.setQualifiedName(t.qualifiedName());
94 return otherType;
99 TypeInfo XMLVisitor::solve(const TypeInfo& t, QStringList scope) {
100 (void)scope;
101 if (!resolve_types) return t;
102 TypeInfo tt(t);
103 for (QList<CodeModelItem>::const_iterator i=current_scope.begin();
104 i<current_scope.end();
105 i++) {
106 TypeInfo ttt = tt;
107 //qDebug() << tt.toString() << ttt.toString();
108 Q_ASSERT(ttt==tt);
109 do {
110 tt = ttt;
111 ttt = ttt.resolveType(tt, *i);
112 } while (ttt!=tt);
115 return tt;
118 QString XMLVisitor::visit(const TypeInfo& t, QStringList scope) {
119 //t = t.resolveType(t, t.scope());
121 QString oldt = t.toString();
122 TypeInfo tt = solve(t, scope);
123 //tt = simplifyType(tt, current_scope.first());
124 while (oldt!=tt.toString()) {
125 oldt = tt.toString();
126 tt = solve(tt, scope);
128 //if (oldt!=tt.toString()) qDebug() << oldt << " -> " << tt.toString();
130 QString ret(" type_name=\"");
131 ret += tt.toString().replace(">>", "> >").append('\"');
132 ret += " type_base=\"";
133 ret += tt.qualifiedName().join("::").replace(">>", "> >").append('\"');
134 if (tt.isConstant()) ret += ATTR_TRUE("type_constant");
135 if (tt.isVolatile()) ret += ATTR_TRUE("type_volatile");
136 if (tt.isReference()) ret += ATTR_TRUE("type_reference");
137 if (tt.indirections()>0) ret += ATTR_NUM("indirections", tt.indirections());
139 QStringList arr = tt.arrayElements();
140 QString tmp = arr.join(",");
141 if (!tmp.isEmpty()) ret += " array=\"" + tmp + '\"';
143 if (tt.isFunctionPointer()) ret += " function_pointer=\"1\"";
145 return ret;
148 #define TAG_CASE(s) case _CodeModelItem::Kind_##s: return #s
150 QString XMLVisitor::XMLTag(CodeModelItem i) {
151 switch (i->kind()) {
152 TAG_CASE(Scope);
153 TAG_CASE(Namespace);
154 TAG_CASE(Member);
155 TAG_CASE(Function);
156 TAG_CASE(Argument);
157 TAG_CASE(Class);
158 TAG_CASE(Enum);
159 TAG_CASE(Enumerator);
160 TAG_CASE(File);
161 TAG_CASE(FunctionDefinition);
162 TAG_CASE(TemplateParameter);
163 TAG_CASE(TypeAlias);
164 TAG_CASE(Variable);
166 return QString();
169 QString templateParametersToString (TemplateParameterList list) {
170 QString ret;
171 foreach(TemplateParameterModelItem p,list) {
172 ret = ret + p->name() + ';';
174 return ret;
177 QString XMLVisitor::visit(CodeModelItem i) {
178 QString ret;
179 ret += XMLTag(i);
181 current_id = ID_STR(i) + " => " + XMLTag(i) + " => " + i->qualifiedName().join("::"); // FIXME: this is debug code
183 ret += ATTR_STR("id", ID_STR(i));
184 ret += ATTR_STR("name", i->name());
185 ret += ATTR_STR("scope", i->scope().join("::"));
186 ret += ATTR_STR("context", current_context.join("::"));
187 // FIXME: is this a dirty hack? yes, it is!
188 if (ArgumentModelItem a = model_dynamic_cast<ArgumentModelItem>(i)) {
189 //ret += ATTR_STR("fullname", current_context.join("::")+"::"+i->qualifiedName().join("::"));
190 } else if (EnumeratorModelItem a = model_dynamic_cast<EnumeratorModelItem>(i)) {
191 ret += ATTR_STR("fullname", current_context.join("::")+"::"+i->qualifiedName().join("::"));
192 } else {
193 ret += ATTR_STR("fullname", i->qualifiedName().join("::"));
196 if (ScopeModelItem s = model_dynamic_cast<ScopeModelItem>(i)) {
197 ret += " members=\"";
199 if (NamespaceModelItem n = model_dynamic_cast<NamespaceModelItem>(i)) {
200 foreach(NamespaceModelItem m, n->namespaces())
201 ret += ID_STR(m).append(' ');
203 if (ScopeModelItem s = model_dynamic_cast<ScopeModelItem>(i)) {
204 foreach(ClassModelItem n, s->classes())
205 ret += ID_STR(n).append(' ');
206 foreach(EnumModelItem n, s->enums())
207 ret += ID_STR(n).append(' ');
208 foreach(FunctionModelItem n, s->functions())
209 ret += ID_STR(n).append(' ');
210 foreach(TypeAliasModelItem n, s->typeAliases())
211 ret += ID_STR(n).append(' ');
212 foreach(VariableModelItem n, s->variables())
213 ret += ID_STR(n).append(' ');
215 if (ScopeModelItem s = model_dynamic_cast<ScopeModelItem>(i)) {
216 ret += '\"';
218 if (MemberModelItem m = model_dynamic_cast<MemberModelItem>(i)) {
219 if (m->isConstant()) ret += ATTR_TRUE("constant");
220 if (m->isVolatile()) ret += ATTR_TRUE("volatile");
221 if (m->isStatic()) ret += ATTR_TRUE("static");
222 if (m->isAuto()) ret += ATTR_TRUE("auto");
223 if (m->isFriend()) ret += ATTR_TRUE("friend");
224 if (m->isRegister()) ret += ATTR_TRUE("register");
225 if (m->isExtern()) ret += ATTR_TRUE("extern");
226 if (m->isMutable()) ret += ATTR_TRUE("mutable");
227 QStringList ownerName = m->qualifiedName();
228 ownerName.pop_back();
229 ret += ATTR_STR("member_of", ownerName.join("::"));
231 if (ClassModelItem c = model_dynamic_cast<ClassModelItem>(current_scope.last()))
232 ret += ATTR_STR("member_of_class", c->qualifiedName().join("::"));
234 switch (m->accessPolicy()) {
235 case CodeModel::Public:
236 ret += ATTR_STR("access", "public");
237 break;
238 case CodeModel::Private:
239 ret += ATTR_STR("access", "private");
240 break;
241 case CodeModel::Protected:
242 ret += ATTR_STR("access", "protected");
243 break;
246 ret += visit(m->type(), m->scope());
247 QString tp = templateParametersToString(m->templateParameters());
248 if (tp!=QString()) ret += ATTR_STR("member_template_parameters", tp);
250 if (FunctionModelItem f = model_dynamic_cast<FunctionModelItem>(i)) {
251 if (f->isVirtual()) ret += ATTR_TRUE("virtual");
252 if (f->isInline()) ret += ATTR_TRUE("inline");
253 if (f->isExplicit()) ret += ATTR_TRUE("explicit");
254 if (f->isAbstract()) ret += ATTR_TRUE("abstract");
255 if (f->isVariadics()) ret += ATTR_TRUE("variadics");
256 //if (i->name()=="destroyed") qDebug() << CodeModel::Normal << CodeModel::Slot << CodeModel::Signal << m->functionType() << i->qualifiedName();
257 switch(f->functionType()) {
258 case CodeModel::Normal:
259 break;
260 case CodeModel::Slot:
261 ret += ATTR_TRUE("slot");
262 break;
263 case CodeModel::Signal:
264 ret += ATTR_TRUE("signal");
265 break;
268 if (ArgumentModelItem a = model_dynamic_cast<ArgumentModelItem>(i)) {
269 ret += visit(a->type(), a->scope());
270 if (a->defaultValue()) {
271 ret += ATTR_TRUE("default");
272 ret += ATTR_STR("defaultvalue", a->defaultValueExpression());
275 if (ClassModelItem c = model_dynamic_cast<ClassModelItem>(i)) {
276 if (c->baseClasses().size()>0) {
277 ret += ATTR_STR("bases", c->baseClasses().join(";").append(";"));
279 switch(c->classType()) {
280 case CodeModel::Class:
281 ret += ATTR_STR("class_type", QString("class"));
282 break;
283 case CodeModel::Struct:
284 ret += ATTR_STR("class_type", QString("struct"));
285 break;
286 case CodeModel::Union:
287 ret += ATTR_STR("class_type", QString("union"));
288 break;
290 QString tp = templateParametersToString(c->templateParameters());
291 if (tp!=QString()) ret += ATTR_STR("member_template_parameters", tp);
292 // TODO also list propertyDeclarations (maybe in content?)
294 if (EnumModelItem e = model_dynamic_cast<EnumModelItem>(i)) {
295 switch (e->accessPolicy()) {
296 case CodeModel::Public:
297 ret += ATTR_STR("access", "public");
298 break;
299 case CodeModel::Private:
300 ret += ATTR_STR("access", "private");
301 break;
302 case CodeModel::Protected:
303 ret += ATTR_STR("access", "protected");
304 break;
307 if (EnumeratorModelItem e = model_dynamic_cast<EnumeratorModelItem>(i)) {
308 ret += e->value().prepend(" value=\"").append('\"');
310 if (TypeAliasModelItem t = model_dynamic_cast<TypeAliasModelItem>(i)) {
311 ret += visit(t->type(), t->scope());
315 // content of the entry:
316 // - Arguments of functions
317 // - members of scopes
318 // - enumeration values
320 QString children;
321 if (NamespaceModelItem n = model_dynamic_cast<NamespaceModelItem>(i)) {
322 foreach(NamespaceModelItem m, n->namespaces())
323 children += visit(model_static_cast<CodeModelItem>(m));
325 if (i->kind() & _CodeModelItem::Kind_Scope) {
326 //qDebug() << ID_STR(i) << i->name() << current_context;
327 //CodeModelItem os = current_scope; // save old outer scope
328 if (!i->name().isEmpty()) { current_context << i->name(); current_scope << i; }
329 foreach(ClassModelItem n, model_dynamic_cast<ScopeModelItem>(i)->classes())
330 children += visit(model_static_cast<CodeModelItem>(n));
331 foreach(EnumModelItem n, model_dynamic_cast<ScopeModelItem>(i)->enums())
332 children += visit(model_static_cast<CodeModelItem>(n));
333 foreach(FunctionModelItem n, model_dynamic_cast<ScopeModelItem>(i)->functions())
334 children += visit(model_static_cast<CodeModelItem>(n));
335 foreach(TypeAliasModelItem n, model_dynamic_cast<ScopeModelItem>(i)->typeAliases())
336 children += visit(model_static_cast<CodeModelItem>(n));
337 foreach(VariableModelItem n, model_dynamic_cast<ScopeModelItem>(i)->variables())
338 children += visit(model_static_cast<CodeModelItem>(n));
339 if (!i->name().isEmpty()) { current_context.removeLast(); current_scope.pop_back(); }
341 if (FunctionModelItem f = model_dynamic_cast<FunctionModelItem>(i)) {
342 foreach(ArgumentModelItem a, f->arguments())
343 children += visit(model_static_cast<CodeModelItem>(a));
345 if (EnumModelItem e = model_dynamic_cast<EnumModelItem>(i)) {
346 QString last = QChar('0');
347 foreach(EnumeratorModelItem n, model_dynamic_cast<EnumModelItem>(i)->enumerators()) {
348 if (n->value() == QString())
349 n->setValue(last.append("+1")); //FIXME: Is there a reason for not putting the value itself? :S
350 children += visit(model_static_cast<CodeModelItem>(n));
351 last = n->value();
354 ret.replace('&', "&amp;");
355 ret.replace('>', "&gt;");
356 ret.replace('<', "&lt;");
358 // TODO fix lua binding generator
359 if(false && children.isEmpty())
361 ret = "<" + ret + " />\n";
363 else
365 ret = "<" + ret + " >\n";
366 ret += children;
367 ret += "</";
368 ret += XMLTag(i);
369 ret += ">\n";
371 return ret;
374 void printHelp()
376 const char* help =
377 "Usage: cpptoxml <flags> <file>\n\n"
378 "Options:\n"
379 "<file> file to parse\n"
380 "-C optional parser config file\n"
381 "-P only preprocess\n"
382 "-R don't resolve\n"
383 "-N generate NO code\n"
384 "-v verbose\n"
385 "-d debug\n"
386 "-h print this help\n"
387 "-qt default qt config file: -C cpptoxml/parser/rpp/pp-qt-configuration\n"
388 "-I Add another include directory\n"
389 "-Q Qt include dir\n"
390 "-o output file\n"
392 fprintf(stderr, "%s", help);
396 int main (int argc, char **argv) {
397 bool onlyPreprocess = false;
398 bool dontResolve = false;
399 bool noCode = false;
400 bool verbose = false;
401 bool debug = false;
402 QString configName;
403 QString sourceName;
404 QStringList inclist;
405 QString outputFile;
406 QString qtdir;
408 for(int i=1; i<argc; i++) {
409 if(argv[i][0]=='-' && argv[i][1]!=0) {
410 QString argValue;
411 bool separed_args = (argv[i][2]=='\0' && argc > i+1);
412 if (separed_args)
413 argValue = QDir::fromNativeSeparators(QString::fromLatin1(argv[i+1]).right(strlen(argv[i+1])));
414 else
415 argValue = QDir::fromNativeSeparators(QString::fromLatin1(argv[i]).right(strlen(argv[i])-2));
417 switch(argv[i][1]) {
418 case 'C':
419 configName = argValue;
420 break;
421 case 'P':
422 onlyPreprocess = true;
423 break;
424 case 'R':
425 dontResolve = true;
426 break;
427 case 'N':
428 noCode = true;
429 break;
430 case 'v':
431 verbose = true;
432 break;
433 case 'd':
434 debug = true;
435 break;
436 case 'h':
437 printHelp();
438 return 0;
439 case 'I':
440 inclist.append(QDir::fromNativeSeparators(argValue));
441 break;
442 case 'Q':
443 qtdir = QDir::fromNativeSeparators(argValue);
444 break;
445 case 'q':{
446 if(QString(argv[i]).startsWith("-qt")) {
447 configName = QDir::fromNativeSeparators("cpptoxml/parser/rpp/pp-qt-configuration");
448 #ifdef Q_OS_WIN
449 configName += QString("-win");
450 #endif
451 } else
452 fprintf(stderr, "found unknown parameter: -%s",argv[i]);
453 } break;
454 case 'o':
455 outputFile = QDir::fromNativeSeparators(argValue);
456 break;
458 default:
459 fprintf(stderr, "found unknown parameter: %s", argv[i]);
460 return 1;
462 if (separed_args) {
463 i++;
465 } else {
466 sourceName = QString::fromLatin1(argv[i]);
470 if (qtdir.isEmpty())
471 qtdir = QDir::fromNativeSeparators(getenv("QT_INCLUDE"));
472 if (qtdir.isEmpty()) {
473 fprintf(stderr, "Generator requires Qt include dir as option -Q or QT_INCLUDE to be set\n");
474 return 1;
477 QString frameworkDir = "/Library/Frameworks";
478 if (!QFileInfo(sourceName).exists()) {
479 QString qtincludefile = QDir::fromNativeSeparators(qtdir+'/'+sourceName+'/'+sourceName);
480 QString macincludefile = QString("%1/%2.framework/Headers/%2").arg(frameworkDir).arg(sourceName);
481 if (QFileInfo(qtincludefile).exists()) {
482 sourceName = qtincludefile;
483 } else if (QFileInfo(macincludefile).exists()) {
484 sourceName = macincludefile;
485 } else {
486 QString msg = "Error: wether '" + sourceName + "' nor '" + qtincludefile;
487 #if defined(Q_OS_MAC)
488 msg += "' or '" + macincludefile;
489 #endif
490 msg += "' found";
491 fprintf(stderr, msg.toLatin1().constData());
492 return 1;
496 if(verbose) fprintf(stderr, "Used file: %s", qPrintable(sourceName));
498 QString currentDir = QDir::current().absolutePath();
499 QFileInfo sourceInfo(sourceName);
500 //QDir::setCurrent(sourceInfo.absolutePath());
502 inclist << (sourceInfo.absolutePath());
503 inclist << (QDir::convertSeparators(qtdir));
505 QStringList qts;
506 qts << "QtXml" << "QtNetwork" << "QtCore" << "QtGui"
507 <<"QtOpenGL" << "QtWebKit"<< "QtScript" << "QtSvg";
509 Q_FOREACH(const QString& lib, qts) {
510 if (sourceName.contains(frameworkDir)) {
511 // TODO does not work with framework because there are no QtCore, QtGui, ... folders
512 inclist << QString("%1/%2.framework/Headers").arg(frameworkDir).arg(lib);
513 } else {
514 inclist << QDir::convertSeparators(qtdir + "/" + lib);
518 if(debug) qDebug() << "atdir: " << qtdir << "sourceName: " << sourceName << inclist;
521 Preprocessor pp;
522 pp.addIncludePaths(inclist);
523 pp.processFile(configName);
524 pp.processFile(sourceName);
525 QByteArray contents = pp.result();
527 if(debug) qDebug() << pp.macroNames();
528 if(debug) qDebug() << contents;
529 if(debug) QTextStream(stdout) << contents;
531 if (onlyPreprocess) {
532 QTextStream(stdout) << contents;
533 } else {
534 Control control;
535 Parser p(&control);
536 pool __pool;
538 TranslationUnitAST *ast = p.parse(contents, contents.size(), &__pool);
540 CodeModel model;
541 Binder binder(&model, p.location());
542 FileModelItem f_model = binder.run(ast);
544 if (!noCode) {
545 XMLVisitor visitor((CodeModelItem)f_model, !dontResolve);
546 QString xml = visitor.visit(model_static_cast<CodeModelItem>(f_model));
547 if (outputFile.isEmpty()) {
548 QTextStream(stdout) << xml;
549 } else {
550 QFile file(outputFile);
551 if (file.open(QFile::WriteOnly | QFile::Text)) {
552 file.write(xml.toLatin1());
558 return 0;