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
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(' ') + 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) )
54 QStringList current_context
;
55 QList
<CodeModelItem
> current_scope
;
56 CodeModelItem outer_scope
;
58 XMLVisitor(CodeModelItem c
, bool r
= true):
59 resolve_types(r
), current_scope(), outer_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;
76 TypeInfo
XMLVisitor::simplifyType (TypeInfo
const &__type
, CodeModelItem __scope
)
78 CodeModel
*__model
= __scope
->model ();
79 Q_ASSERT (__model
!= 0);
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());
99 TypeInfo
XMLVisitor::solve(const TypeInfo
& t
, QStringList scope
) {
101 if (!resolve_types
) return t
;
103 for (QList
<CodeModelItem
>::const_iterator i
=current_scope
.begin();
104 i
<current_scope
.end();
107 //qDebug() << tt.toString() << ttt.toString();
111 ttt
= ttt
.resolveType(tt
, *i
);
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\"";
148 #define TAG_CASE(s) case _CodeModelItem::Kind_##s: return #s
150 QString
XMLVisitor::XMLTag(CodeModelItem i
) {
159 TAG_CASE(Enumerator
);
161 TAG_CASE(FunctionDefinition
);
162 TAG_CASE(TemplateParameter
);
169 QString
templateParametersToString (TemplateParameterList list
) {
171 foreach(TemplateParameterModelItem p
,list
) {
172 ret
= ret
+ p
->name() + ';';
177 QString
XMLVisitor::visit(CodeModelItem 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("::"));
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
)) {
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");
238 case CodeModel::Private
:
239 ret
+= ATTR_STR("access", "private");
241 case CodeModel::Protected
:
242 ret
+= ATTR_STR("access", "protected");
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
:
260 case CodeModel::Slot
:
261 ret
+= ATTR_TRUE("slot");
263 case CodeModel::Signal
:
264 ret
+= ATTR_TRUE("signal");
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"));
283 case CodeModel::Struct
:
284 ret
+= ATTR_STR("class_type", QString("struct"));
286 case CodeModel::Union
:
287 ret
+= ATTR_STR("class_type", QString("union"));
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");
299 case CodeModel::Private
:
300 ret
+= ATTR_STR("access", "private");
302 case CodeModel::Protected
:
303 ret
+= ATTR_STR("access", "protected");
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
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
));
354 ret
.replace('&', "&");
355 ret
.replace('>', ">");
356 ret
.replace('<', "<");
358 // TODO fix lua binding generator
359 if(false && children
.isEmpty())
361 ret
= "<" + ret
+ " />\n";
365 ret
= "<" + ret
+ " >\n";
377 "Usage: cpptoxml <flags> <file>\n\n"
379 "<file> file to parse\n"
380 "-C optional parser config file\n"
381 "-P only preprocess\n"
383 "-N generate NO code\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"
392 fprintf(stderr
, "%s", help
);
396 int main (int argc
, char **argv
) {
397 bool onlyPreprocess
= false;
398 bool dontResolve
= false;
400 bool verbose
= false;
408 for(int i
=1; i
<argc
; i
++) {
409 if(argv
[i
][0]=='-' && argv
[i
][1]!=0) {
411 bool separed_args
= (argv
[i
][2]=='\0' && argc
> i
+1);
413 argValue
= QDir::fromNativeSeparators(QString::fromLatin1(argv
[i
+1]).right(strlen(argv
[i
+1])));
415 argValue
= QDir::fromNativeSeparators(QString::fromLatin1(argv
[i
]).right(strlen(argv
[i
])-2));
419 configName
= argValue
;
422 onlyPreprocess
= true;
440 inclist
.append(QDir::fromNativeSeparators(argValue
));
443 qtdir
= QDir::fromNativeSeparators(argValue
);
446 if(QString(argv
[i
]).startsWith("-qt")) {
447 configName
= QDir::fromNativeSeparators("cpptoxml/parser/rpp/pp-qt-configuration");
449 configName
+= QString("-win");
452 fprintf(stderr
, "found unknown parameter: -%s",argv
[i
]);
455 outputFile
= QDir::fromNativeSeparators(argValue
);
459 fprintf(stderr
, "found unknown parameter: %s", argv
[i
]);
466 sourceName
= QString::fromLatin1(argv
[i
]);
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");
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
;
486 QString msg
= "Error: wether '" + sourceName
+ "' nor '" + qtincludefile
;
487 #if defined(Q_OS_MAC)
488 msg
+= "' or '" + macincludefile
;
491 fprintf(stderr
, msg
.toLatin1().constData());
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
));
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
);
514 inclist
<< QDir::convertSeparators(qtdir
+ "/" + lib
);
518 if(debug
) qDebug() << "atdir: " << qtdir
<< "sourceName: " << sourceName
<< inclist
;
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
;
538 TranslationUnitAST
*ast
= p
.parse(contents
, contents
.size(), &__pool
);
541 Binder
binder(&model
, p
.location());
542 FileModelItem f_model
= binder
.run(ast
);
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
;
550 QFile
file(outputFile
);
551 if (file
.open(QFile::WriteOnly
| QFile::Text
)) {
552 file
.write(xml
.toLatin1());