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 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"));
298 case CodeModel::Struct
:
299 ret
+= ATTR_STR("class_type", QString("struct"));
301 case CodeModel::Union
:
302 ret
+= ATTR_STR("class_type", QString("union"));
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");
314 case CodeModel::Private
:
315 ret
+= ATTR_STR("access", "private");
317 case CodeModel::Protected
:
318 ret
+= ATTR_STR("access", "protected");
322 if (EnumeratorModelItem e
= model_dynamic_cast
<EnumeratorModelItem
>(i
)) {
323 ret
+= e
->value().prepend(" value=\"").append('\"');
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
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
));
369 //ret.replace('&', "&");
370 //ret.replace('>', ">");
371 //ret.replace('<', "<");
373 // TODO fix lua binding generator
374 if(false && children
.isEmpty())
376 ret
= "<" + ret
+ " />\n";
380 ret
= "<" + ret
+ " >\n";
392 "Usage: cpptoxml <flags> <file>\n\n"
394 "<file> file to parse\n"
395 "-C optional parser config file\n"
396 "-P only preprocess\n"
398 "-N generate NO code\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"
407 fprintf(stderr
, "%s", help
);
411 int main (int argc
, char **argv
) {
412 bool onlyPreprocess
= false;
413 bool dontResolve
= false;
415 bool verbose
= false;
423 for(int i
=1; i
<argc
; i
++) {
424 if(argv
[i
][0]=='-' && argv
[i
][1]!=0) {
426 bool separed
= (argv
[i
][2] == '\0' && argc
> i
+1 && argv
[i
+1][0] != '-');
428 argValue
= QDir::fromNativeSeparators(QString::fromLatin1(argv
[i
+1]).right(strlen(argv
[i
+1])));
430 argValue
= QDir::fromNativeSeparators(QString::fromLatin1(argv
[i
]).right(strlen(argv
[i
])-2));
434 configName
= argValue
;
437 onlyPreprocess
= true;
455 inclist
.append(argValue
);
461 if(QString(argv
[i
]).startsWith("-qt")) {
462 configName
= "cpptoxml/parser/rpp/pp-qt-configuration";
464 configName
+= QString("-win");
467 fprintf(stderr
, "found unknown parameter: -%s",argv
[i
]);
470 outputFile
= argValue
;
474 fprintf(stderr
, "found unknown parameter: %s", argv
[i
]);
480 sourceName
= QString::fromLatin1(argv
[i
]);
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");
491 QString frameworkDir
= "/Library/Frameworks";
492 if (!QFileInfo(sourceName
).exists()) {
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
;
500 QString msg
= "Error: wether '" + sourceName
+ "' nor '" + qtincludefile
;
501 #if defined(Q_OS_MAC)
502 msg
+= "' or '" + macincludefile
;
505 fprintf(stderr
, "%s\n", msg
.toLatin1().constData());
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
));
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
);
528 inclist
<< QDir::convertSeparators(qtdir
+ "/" + lib
);
532 if(debug
) qDebug() << "atdir: " << qtdir
<< "sourceName: " << sourceName
<< inclist
;
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
;
552 TranslationUnitAST
*ast
= p
.parse(contents
, contents
.size(), &__pool
);
555 Binder
binder(&model
, p
.location());
556 FileModelItem f_model
= binder
.run(ast
);
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
;
564 QFile
file(outputFile
);
565 if (file
.open(QFile::WriteOnly
| QFile::Text
)) {
566 file
.write(xml
.toLatin1());