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
26 #include "codemodel.h"
29 #include "preprocessor.h"
34 #include <QTextStream>
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) )
50 QString
escape_chars (QString s
) {
52 ret
.replace('&', "&");
53 ret
.replace('"', """);
54 ret
.replace('\'', "'");
55 ret
.replace('>', ">");
56 ret
.replace('<', "<");
64 QStringList current_context
;
65 QList
<CodeModelItem
> current_scope
;
66 CodeModelItem outer_scope
;
68 XMLVisitor(CodeModelItem c
, bool r
= true):
69 resolve_types(r
), current_scope(), outer_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;
86 TypeInfo
XMLVisitor::simplifyType (TypeInfo
const &__type
, CodeModelItem __scope
)
88 CodeModel
*__model
= __scope
->model ();
89 Q_ASSERT (__model
!= 0);
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());
109 TypeInfo
XMLVisitor::solve(const TypeInfo
& t
, QStringList scope
) {
111 if (!resolve_types
) return t
;
113 for (QList
<CodeModelItem
>::const_iterator i
=current_scope
.begin();
114 i
<current_scope
.end();
117 //qDebug() << tt.toString() << ttt.toString();
121 ttt
= ttt
.resolveType(tt
, *i
);
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();
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");
157 #define TAG_CASE(s) case _CodeModelItem::Kind_##s: return #s
159 QString
XMLVisitor::XMLTag(CodeModelItem i
) {
168 TAG_CASE(Enumerator
);
170 TAG_CASE(FunctionDefinition
);
171 TAG_CASE(TemplateParameter
);
178 QString
templateParametersToString (TemplateParameterList list
) {
180 foreach(TemplateParameterModelItem p
,list
) {
181 ret
= ret
+ p
->name() + ';';
186 QString
XMLVisitor::visit(CodeModelItem 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("::"));
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
)) {
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");
247 case CodeModel::Private
:
248 ret
+= ATTR_STR("access", "private");
250 case CodeModel::Protected
:
251 ret
+= ATTR_STR("access", "protected");
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
:
269 case CodeModel::Slot
:
270 ret
+= ATTR_TRUE("slot");
272 case CodeModel::Signal
:
273 ret
+= ATTR_TRUE("signal");
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");
289 case CodeModel::Private
:
290 ret
+= ATTR_STR("access", "private");
292 case CodeModel::Protected
:
293 ret
+= ATTR_STR("access", "protected");
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"));
310 case CodeModel::Struct
:
311 ret
+= ATTR_STR("class_type", QString("struct"));
313 case CodeModel::Union
:
314 ret
+= ATTR_STR("class_type", QString("union"));
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");
326 case CodeModel::Private
:
327 ret
+= ATTR_STR("access", "private");
329 case CodeModel::Protected
:
330 ret
+= ATTR_STR("access", "protected");
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
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
));
381 //ret.replace('&', "&");
382 //ret.replace('>', ">");
383 //ret.replace('<', "<");
385 // TODO fix lua binding generator
386 if(false && children
.isEmpty())
388 ret
= "<" + ret
+ " />\n";
392 ret
= "<" + ret
+ " >\n";
404 "Usage: cpptoxml <flags> <file>\n\n"
406 "<file> file to parse\n"
407 "-C optional parser config file\n"
408 "-P only preprocess\n"
410 "-N generate NO code\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"
419 fprintf(stderr
, "%s", help
);
423 int main (int argc
, char **argv
) {
424 bool onlyPreprocess
= false;
425 bool dontResolve
= false;
427 bool verbose
= false;
435 for(int i
=1; i
<argc
; i
++) {
436 if(argv
[i
][0]=='-' && argv
[i
][1]!=0) {
438 bool separed
= (argv
[i
][2] == '\0' && argc
> i
+1 && argv
[i
+1][0] != '-');
440 argValue
= QDir::fromNativeSeparators(QString::fromLatin1(argv
[i
+1]).right(strlen(argv
[i
+1])));
442 argValue
= QDir::fromNativeSeparators(QString::fromLatin1(argv
[i
]).right(strlen(argv
[i
])-2));
446 configName
= argValue
;
449 onlyPreprocess
= true;
467 inclist
.append(argValue
);
473 if(QString(argv
[i
]).startsWith("-qt")) {
474 configName
= "cpptoxml/parser/rpp/pp-qt-configuration";
476 configName
+= QString("-win");
479 fprintf(stderr
, "found unknown parameter: -%s",argv
[i
]);
482 outputFile
= argValue
;
486 fprintf(stderr
, "found unknown parameter: %s", argv
[i
]);
492 sourceName
= QString::fromLatin1(argv
[i
]);
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");
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
;
512 QString msg
= "Error: wether '" + sourceName
+ "' nor '" + qtincludefile
;
513 #if defined(Q_OS_MAC)
514 msg
+= "' or '" + macincludefile
;
517 fprintf(stderr
, "%s\n", msg
.toLatin1().constData());
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
));
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
);
540 inclist
<< QDir::convertSeparators(qtdir
+ "/" + lib
);
544 if(debug
) qDebug() << "atdir: " << qtdir
<< "sourceName: " << sourceName
<< inclist
;
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
;
564 TranslationUnitAST
*ast
= p
.parse(contents
, contents
.size(), &__pool
);
567 Binder
binder(&model
, p
.location());
568 FileModelItem f_model
= binder
.run(ast
);
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
;
576 QFile
file(outputFile
);
577 if (file
.open(QFile::WriteOnly
| QFile::Text
)) {
578 file
.write(xml
.toLatin1());