diff options
Diffstat (limited to 'umbrello/umbrello/codeimport')
42 files changed, 15671 insertions, 0 deletions
diff --git a/umbrello/umbrello/codeimport/Makefile.am b/umbrello/umbrello/codeimport/Makefile.am new file mode 100644 index 00000000..5f821fab --- /dev/null +++ b/umbrello/umbrello/codeimport/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = -I./kdevcppparser $(all_includes) + +noinst_LTLIBRARIES = libcodeimport.la +libcodeimport_la_SOURCES = adaimport.cpp classimport.cpp cppimport.cpp idlimport.cpp import_utils.cpp javaimport.cpp nativeimportbase.cpp pascalimport.cpp pythonimport.cpp + +SUBDIRS = kdevcppparser diff --git a/umbrello/umbrello/codeimport/adaimport.cpp b/umbrello/umbrello/codeimport/adaimport.cpp new file mode 100644 index 00000000..54ac3907 --- /dev/null +++ b/umbrello/umbrello/codeimport/adaimport.cpp @@ -0,0 +1,588 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005-2007 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +// own header +#include "adaimport.h" + +#include <stdio.h> +// qt/kde includes +#include <qregexp.h> +#include <kdebug.h> +// app includes +#include "import_utils.h" +#include "../uml.h" +#include "../umldoc.h" +#include "../package.h" +#include "../folder.h" +#include "../classifier.h" +#include "../enum.h" +#include "../operation.h" +#include "../attribute.h" +#include "../association.h" + +AdaImport::AdaImport() : NativeImportBase("--") { + initVars(); +} + +AdaImport::~AdaImport() { +} + +void AdaImport::initVars() { + m_inGenericFormalPart = false; + m_classesDefinedInThisScope.clear(); + m_renaming.clear(); +} + +/// Split the line so that a string is returned as a single element of the list, +/// when not in a string then split at white space. +QStringList AdaImport::split(const QString& lin) { + QStringList list; + QString listElement; + bool inString = false; + bool seenSpace = false; + QString line = lin.stripWhiteSpace(); + uint len = line.length(); + for (uint i = 0; i < len; i++) { + const QChar& c = line[i]; + if (inString) { + listElement += c; + if (c == '"') { + if (i < len - 1 && line[i + 1] == '"') { + i++; // escaped quotation mark + continue; + } + list.append(listElement); + listElement = QString(); + inString = false; + } + } else if (c == '"') { + inString = true; + if (!listElement.isEmpty()) + list.append(listElement); + listElement = QString(c); + seenSpace = false; + } else if (c == '\'') { + if (i < len - 2 && line[i + 2] == '\'') { + // character constant + if (!listElement.isEmpty()) + list.append(listElement); + listElement = line.mid(i, 3); + i += 2; + list.append(listElement); + listElement = QString(); + continue; + } + listElement += c; + seenSpace = false; + } else if (c.isSpace()) { + if (seenSpace) + continue; + seenSpace = true; + if (!listElement.isEmpty()) { + list.append(listElement); + listElement = QString(); + } + } else { + listElement += c; + seenSpace = false; + } + } + if (!listElement.isEmpty()) + list.append(listElement); + return list; +} + +void AdaImport::fillSource(const QString& word) { + QString lexeme; + const uint len = word.length(); + for (uint i = 0; i < len; i++) { + QChar c = word[i]; + if (c.isLetterOrNumber() || c == '_' || c == '.' || c == '#') { + lexeme += c; + } else { + if (!lexeme.isEmpty()) { + m_source.append(lexeme); + lexeme = QString(); + } + if (c == ':' && word[i + 1] == '=') { + m_source.append(":="); + i++; + } else { + m_source.append(QString(c)); + } + } + } + if (!lexeme.isEmpty()) + m_source.append(lexeme); +} + +QString AdaImport::expand(const QString& name) { + QRegExp pfxRegExp("^(\\w+)\\."); + pfxRegExp.setCaseSensitive(false); + int pos = pfxRegExp.search(name); + if (pos == -1) + return name; + QString result = name; + QString pfx = pfxRegExp.cap(1); + if (m_renaming.contains(pfx)) { + result.remove(pfxRegExp); + result.prepend(m_renaming[pfx] + '.'); + } + return result; +} + +void AdaImport::parseStems(const QStringList& stems) { + if (stems.isEmpty()) + return; + QString base = stems.first(); + uint i = 0; + while (1) { + QString filename = base + ".ads"; + if (! m_parsedFiles.contains(filename)) { + // Save current m_source and m_srcIndex. + QStringList source(m_source); + uint srcIndex = m_srcIndex; + m_source.clear(); + parseFile(filename); + // Restore m_source and m_srcIndex. + m_source = source; + m_srcIndex = srcIndex; + // Also reset m_currentAccess. + // CHECK: need to reset more stuff? + m_currentAccess = Uml::Visibility::Public; + } + if (++i >= stems.count()) + break; + base += '-' + stems[i]; + } +} + +bool AdaImport::parseStmt() { + const uint srcLength = m_source.count(); + QString keyword = m_source[m_srcIndex]; + UMLDoc *umldoc = UMLApp::app()->getDocument(); + //kDebug() << '"' << keyword << '"' << endl; + if (keyword == "with") { + if (m_inGenericFormalPart) { + // mapping of generic formal subprograms or packages is not yet implemented + return false; + } + while (++m_srcIndex < srcLength && m_source[m_srcIndex] != ";") { + QStringList components = QStringList::split(".", m_source[m_srcIndex].lower()); + const QString& prefix = components.first(); + if (prefix == "system" || prefix == "ada" || prefix == "gnat" || + prefix == "interfaces" || prefix == "text_io" || + prefix == "unchecked_conversion" || + prefix == "unchecked_deallocation") { + if (advance() != ",") + break; + continue; + } + parseStems(components); + if (advance() != ",") + break; + } + return true; + } + if (keyword == "generic") { + m_inGenericFormalPart = true; + return true; + } + if (keyword == "package") { + const QString& name = advance(); + QStringList parentPkgs = QStringList::split(".", name.lower()); + parentPkgs.pop_back(); // exclude the current package + parseStems(parentPkgs); + UMLObject *ns = NULL; + if (advance() == "is") { + ns = Import_Utils::createUMLObject(Uml::ot_Package, name, + m_scope[m_scopeIndex], m_comment); + if (m_source[m_srcIndex + 1] == "new") { + m_srcIndex++; + QString pkgName = advance(); + UMLObject *gp = Import_Utils::createUMLObject(Uml::ot_Package, pkgName, + m_scope[m_scopeIndex]); + gp->setStereotype("generic"); + // Add binding from instantiator to instantiatee + UMLAssociation *assoc = new UMLAssociation(Uml::at_Dependency, ns, gp); + assoc->setUMLPackage(umldoc->getRootFolder(Uml::mt_Logical)); + assoc->setStereotype("bind"); + // Work around missing display of stereotype in AssociationWidget: + assoc->setName(assoc->getStereotype(true)); + umldoc->addAssociation(assoc); + skipStmt(); + } else { + m_scope[++m_scopeIndex] = static_cast<UMLPackage*>(ns); + } + } else if (m_source[m_srcIndex] == "renames") { + m_renaming[name] = advance(); + } else { + kError() << "AdaImport::parseStmt: unexpected: " << m_source[m_srcIndex] << endl; + skipStmt("is"); + } + if (m_inGenericFormalPart) { + ns->setStereotype("generic"); + m_inGenericFormalPart = false; + } + return true; + } + if (m_inGenericFormalPart) + return false; // skip generic formal parameter (not yet implemented) + if (keyword == "subtype") { + QString name = advance(); + advance(); // "is" + QString base = expand(advance()); + base.remove("Standard.", false); + UMLObject *type = umldoc->findUMLObject(base, Uml::ot_UMLObject, m_scope[m_scopeIndex]); + if (type == NULL) { + type = Import_Utils::createUMLObject(Uml::ot_Datatype, base, m_scope[m_scopeIndex]); + } + UMLObject *subtype = Import_Utils::createUMLObject(type->getBaseType(), name, + m_scope[m_scopeIndex], m_comment); + UMLAssociation *assoc = new UMLAssociation(Uml::at_Dependency, subtype, type); + assoc->setUMLPackage(umldoc->getRootFolder(Uml::mt_Logical)); + assoc->setStereotype("subtype"); + // Work around missing display of stereotype in AssociationWidget: + assoc->setName(assoc->getStereotype(true)); + umldoc->addAssociation(assoc); + skipStmt(); + return true; + } + if (keyword == "type") { + QString name = advance(); + QString next = advance(); + if (next == "(") { + kDebug() << "AdaImport::parseStmt(" << name << "): " + << "discriminant handling is not yet implemented" << endl; + // @todo Find out how to map discriminated record to UML. + // For now, we just create a pro forma empty record. + Import_Utils::createUMLObject(Uml::ot_Class, name, m_scope[m_scopeIndex], + m_comment, "record"); + skipStmt("end"); + if ((next = advance()) == "case") + m_srcIndex += 2; // skip "case" ";" + skipStmt(); + return true; + } + if (next == ";") { + // forward declaration + Import_Utils::createUMLObject(Uml::ot_Class, name, m_scope[m_scopeIndex], + m_comment); + return true; + } + if (next != "is") { + kError() << "AdaImport::parseStmt: expecting \"is\"" << endl; + return false; + } + next = advance(); + if (next == "(") { + // enum type + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Enum, + name, m_scope[m_scopeIndex], m_comment); + UMLEnum *enumType = static_cast<UMLEnum*>(ns); + while ((next = advance()) != ")") { + Import_Utils::addEnumLiteral(enumType, next, m_comment); + m_comment = QString(); + if (advance() != ",") + break; + } + skipStmt(); + return true; + } + bool isTaggedType = false; + if (next == "abstract") { + m_isAbstract = true; + next = advance(); + } + if (next == "tagged") { + isTaggedType = true; + next = advance(); + } + if (next == "limited" || + next == "task" || + next == "protected" || + next == "synchronized") { + next = advance(); // we can't (yet?) represent that + } + if (next == "private" || + next == "interface" || + next == "record" || + (next == "null" && + m_source[m_srcIndex+1] == "record")) { + Uml::Object_Type t = (next == "interface" ? Uml::ot_Interface : Uml::ot_Class); + UMLObject *ns = Import_Utils::createUMLObject(t, name, m_scope[m_scopeIndex], m_comment); + if (t == Uml::ot_Interface) { + while ((next = advance()) == "and") { + UMLClassifier *klass = static_cast<UMLClassifier*>(ns); + QString base = expand(advance()); + UMLObject *p = Import_Utils::createUMLObject(Uml::ot_Interface, base, m_scope[m_scopeIndex]); + UMLClassifier *parent = static_cast<UMLClassifier*>(p); + Import_Utils::createGeneralization(klass, parent); + } + } else { + ns->setAbstract(m_isAbstract); + } + m_isAbstract = false; + if (isTaggedType) { + if (! m_classesDefinedInThisScope.contains(ns)) + m_classesDefinedInThisScope.append(ns); + } else { + ns->setStereotype("record"); + } + if (next == "record") + m_klass = static_cast<UMLClassifier*>(ns); + else + skipStmt(); + return true; + } + if (next == "new") { + QString base = expand(advance()); + QStringList baseInterfaces; + while ((next = advance()) == "and") { + baseInterfaces.append(expand(advance())); + } + const bool isExtension = (next == "with"); + Uml::Object_Type t; + if (isExtension || m_isAbstract) { + t = Uml::ot_Class; + } else { + base.remove("Standard.", false); + UMLObject *known = umldoc->findUMLObject(base, Uml::ot_UMLObject, m_scope[m_scopeIndex]); + t = (known ? known->getBaseType() : Uml::ot_Datatype); + } + UMLObject *ns = Import_Utils::createUMLObject(t, base, NULL); + UMLClassifier *parent = static_cast<UMLClassifier*>(ns); + ns = Import_Utils::createUMLObject(t, name, m_scope[m_scopeIndex], m_comment); + if (isExtension) { + next = advance(); + if (next == "null" || next == "record") { + UMLClassifier *klass = static_cast<UMLClassifier*>(ns); + Import_Utils::createGeneralization(klass, parent); + if (next == "record") { + // Set the m_klass for attributes. + m_klass = klass; + } + if (baseInterfaces.count()) { + t = Uml::ot_Interface; + QStringList::Iterator end(baseInterfaces.end()); + for (QStringList::Iterator bi(baseInterfaces.begin()); bi != end; ++bi) { + ns = Import_Utils::createUMLObject(t, *bi, m_scope[m_scopeIndex]); + parent = static_cast<UMLClassifier*>(ns); + Import_Utils::createGeneralization(klass, parent); + } + } + } + } + skipStmt(); + return true; + } + // Datatypes: TO BE DONE + return false; + } + if (keyword == "private") { + m_currentAccess = Uml::Visibility::Private; + return true; + } + if (keyword == "end") { + if (m_klass) { + if (advance() != "record") { + kError() << "end: expecting \"record\" at " + << m_source[m_srcIndex] << endl; + } + m_klass = NULL; + } else if (m_scopeIndex) { + if (advance() != ";") { + QString scopeName = m_scope[m_scopeIndex]->getFullyQualifiedName(); + if (scopeName.lower() != m_source[m_srcIndex].lower()) + kError() << "end: expecting " << scopeName << ", found " + << m_source[m_srcIndex] << endl; + } + m_scopeIndex--; + m_currentAccess = Uml::Visibility::Public; // @todo make a stack for this + } else { + kError() << "importAda: too many \"end\"" << endl; + } + skipStmt(); + return true; + } + // subprogram + if (keyword == "not") + keyword = advance(); + if (keyword == "overriding") + keyword = advance(); + if (keyword == "function" || keyword == "procedure") { + const QString& name = advance(); + QString returnType; + if (advance() != "(") { + // Unlike an Ada package, a UML package does not support + // subprograms. + // In order to map those, we would need to create a UML + // class with stereotype <<utility>> for the Ada package. + kDebug() << "ignoring parameterless " << keyword << " " << name << endl; + skipStmt(); + return true; + } + UMLClassifier *klass = NULL; + UMLOperation *op = NULL; + const uint MAX_PARNAMES = 16; + while (m_srcIndex < srcLength && m_source[m_srcIndex] != ")") { + QString parName[MAX_PARNAMES]; + uint parNameCount = 0; + do { + if (parNameCount >= MAX_PARNAMES) { + kError() << "MAX_PARNAMES is exceeded at " << name << endl; + break; + } + parName[parNameCount++] = advance(); + } while (advance() == ","); + if (m_source[m_srcIndex] != ":") { + kError() << "importAda: expecting ':'" << endl; + skipStmt(); + break; + } + const QString &direction = advance(); + QString typeName; + Uml::Parameter_Direction dir = Uml::pd_In; + if (direction == "access") { + // Oops, we have to improvise here because there + // is no such thing as "access" in UML. + // So we use the next best thing, "inout". + // Better ideas, anyone? + dir = Uml::pd_InOut; + typeName = advance(); + } else if (direction == "in") { + if (m_source[m_srcIndex + 1] == "out") { + dir = Uml::pd_InOut; + m_srcIndex++; + } + typeName = advance(); + } else if (direction == "out") { + dir = Uml::pd_Out; + typeName = advance(); + } else { + typeName = direction; // In Ada, the default direction is "in" + } + typeName.remove("Standard.", false); + typeName = expand(typeName); + if (op == NULL) { + // In Ada, the first parameter indicates the class. + UMLObject *type = Import_Utils::createUMLObject(Uml::ot_Class, typeName, m_scope[m_scopeIndex]); + Uml::Object_Type t = type->getBaseType(); + if ((t != Uml::ot_Interface && + (t != Uml::ot_Class || type->getStereotype() == "record")) || + !m_classesDefinedInThisScope.contains(type)) { + // Not an instance bound method - we cannot represent it. + skipStmt(")"); + break; + } + klass = static_cast<UMLClassifier*>(type); + op = Import_Utils::makeOperation(klass, name); + // The controlling parameter is suppressed. + parNameCount--; + if (parNameCount) { + for (uint i = 0; i < parNameCount; i++) + parName[i] = parName[i + 1]; + } + } + for (uint i = 0; i < parNameCount; i++) { + UMLAttribute *att = Import_Utils::addMethodParameter(op, typeName, parName[i]); + att->setParmKind(dir); + } + if (advance() != ";") + break; + } + if (keyword == "function") { + if (advance() != "return") { + if (klass) + kError() << "importAda: expecting \"return\" at function " + << name << endl; + return false; + } + returnType = expand(advance()); + returnType.remove("Standard.", false); + } + bool isAbstract = false; + if (advance() == "is" && advance() == "abstract") + isAbstract = true; + if (klass != NULL && op != NULL) + Import_Utils::insertMethod(klass, op, m_currentAccess, returnType, + false, isAbstract, false, false, m_comment); + skipStmt(); + return true; + } + if (keyword == "task" || keyword == "protected") { + // Can task and protected objects/types be mapped to UML? + bool isType = false; + QString name = advance(); + if (name == "type") { + isType = true; + name = advance(); + } + QString next = advance(); + if (next == "(") { + skipStmt(")"); // skip discriminant + next = advance(); + } + if (next == "is") + skipStmt("end"); + skipStmt(); + return true; + } + if (keyword == "for") { // rep spec + QString typeName = advance(); + QString next = advance(); + if (next == "'") { + advance(); // skip qualifier + next = advance(); + } + if (next == "use") { + if (advance() == "record") + skipStmt("end"); + } else { + kError() << "importAda: expecting \"use\" at rep spec of " + << typeName << endl; + } + skipStmt(); + return true; + } + // At this point we're only interested in attribute declarations. + if (m_klass == NULL || keyword == "null") { + skipStmt(); + return true; + } + const QString& name = keyword; + if (advance() != ":") { + kError() << "adaImport: expecting \":\" at " << name << " " + << m_source[m_srcIndex] << endl; + skipStmt(); + return true; + } + QString nextToken = advance(); + if (nextToken == "aliased") + nextToken = advance(); + QString typeName = expand(nextToken); + QString initialValue; + if (advance() == ":=") { + initialValue = advance(); + QString token; + while ((token = advance()) != ";") { + initialValue.append(' ' + token); + } + } + UMLObject *o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name, + typeName, m_comment); + UMLAttribute *attr = static_cast<UMLAttribute*>(o); + attr->setInitialValue(initialValue); + skipStmt(); + return true; +} + + diff --git a/umbrello/umbrello/codeimport/adaimport.h b/umbrello/umbrello/codeimport/adaimport.h new file mode 100644 index 00000000..14e41926 --- /dev/null +++ b/umbrello/umbrello/codeimport/adaimport.h @@ -0,0 +1,88 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005-2007 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +#ifndef ADAIMPORT_H +#define ADAIMPORT_H + +#include <qmap.h> +#include <qstringlist.h> + +#include "nativeimportbase.h" +#include "../umlobjectlist.h" + +/** + * Ada code import + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class AdaImport : public NativeImportBase { +public: + AdaImport(); + virtual ~AdaImport(); + +protected: + /** + * Reimplement operation from NativeImportBase. + */ + void initVars(); + + /** + * Implement abstract operation from NativeImportBase. + */ + bool parseStmt(); + + /** + * Split the line so that a string is returned as a single element of the list. + * When not in a string then split at white space. + * Reimplementation of method from NativeImportBase is required because of + * Ada's tic which is liable to be confused with the beginning of a character + * constant. + */ + QStringList split(const QString& line); + + /** + * Implement abstract operation from NativeImportBase. + */ + void fillSource(const QString& word); + + /** + * Apply package renamings to the given name. + * + * @return expanded name + */ + QString expand(const QString& name); + + /** + * Parse all files that can be formed by concatenation of the given stems. + */ + void parseStems(const QStringList& stems); + + bool m_inGenericFormalPart; ///< auxiliary variable + + /** + * List for keeping track of tagged objects declared in the current scope. + * This is required for distinguishing primitive from non primitive + * methods. + */ + UMLObjectList m_classesDefinedInThisScope; + + typedef QMap<QString, QString> StringMap; + + /** + * Map of package renamings. + * Keyed by the renaming. Value returns the expanded name. + */ + StringMap m_renaming; + +}; + +#endif + diff --git a/umbrello/umbrello/codeimport/classimport.cpp b/umbrello/umbrello/codeimport/classimport.cpp new file mode 100644 index 00000000..ed675bda --- /dev/null +++ b/umbrello/umbrello/codeimport/classimport.cpp @@ -0,0 +1,58 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +// own header +#include "classimport.h" +// qt/kde includes +#include <qregexp.h> +#include <klocale.h> +// app includes +#include "../umldoc.h" +#include "../uml.h" +#include "idlimport.h" +#include "pythonimport.h" +#include "javaimport.h" +#include "adaimport.h" +#include "pascalimport.h" +#include "cppimport.h" + +void ClassImport::importFiles(const QStringList &fileList) { + initialize(); + UMLDoc *umldoc = UMLApp::app()->getDocument(); + uint processedFilesCount = 0; + for (QStringList::const_iterator fileIT = fileList.begin(); + fileIT != fileList.end(); ++fileIT) { + QString fileName = (*fileIT); + umldoc->writeToStatusBar(i18n("Importing file: %1 Progress: %2/%3"). + arg(fileName).arg(processedFilesCount).arg(fileList.size())); + parseFile(fileName); + processedFilesCount++; + } + umldoc->writeToStatusBar(i18n("Ready.")); +} + +ClassImport *ClassImport::createImporterByFileExt(const QString &filename) { + ClassImport *classImporter; + if (filename.endsWith(".idl")) + classImporter = new IDLImport(); + else if (filename.endsWith(".py")) + classImporter = new PythonImport(); + else if (filename.endsWith(".java")) + classImporter = new JavaImport(); + else if (filename.contains( QRegExp("\\.ad[sba]$") )) + classImporter = new AdaImport(); + else if (filename.endsWith(".pas")) + classImporter = new PascalImport(); + else + classImporter = new CppImport(); // the default. + return classImporter; +} + diff --git a/umbrello/umbrello/codeimport/classimport.h b/umbrello/umbrello/codeimport/classimport.h new file mode 100644 index 00000000..351ddec5 --- /dev/null +++ b/umbrello/umbrello/codeimport/classimport.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005-2006 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +#ifndef CLASSIMPORT_H +#define CLASSIMPORT_H + +#include <qstringlist.h> + +/** + * Interfaces classparser library to uml models + * Abstract base for programming language specific import classes + * @author Mikko Pasanen + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class ClassImport { +public: + ClassImport() {} + virtual ~ClassImport() {} + + /** + * Import files. + * + * @param files List of files to import. + */ + void importFiles(const QStringList &files); + + /** + * Factory method. + */ + static ClassImport *createImporterByFileExt(const QString &filename); + +protected: + /** + * Initialize the importer. + * This is called by importFiles() once, before entering + * the loop for importing one or more files. + * To be implemented by inheriting classes. + */ + virtual void initialize() = 0; + + /** + * Import a single file. + * To be implemented by inheriting classes. + * + * @param filename The file to import. + */ + virtual void parseFile(const QString& filename) = 0; + +}; + +#endif diff --git a/umbrello/umbrello/codeimport/cppimport.cpp b/umbrello/umbrello/codeimport/cppimport.cpp new file mode 100644 index 00000000..4537c038 --- /dev/null +++ b/umbrello/umbrello/codeimport/cppimport.cpp @@ -0,0 +1,109 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005-2007 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +// own header +#include "cppimport.h" +// qt/kde includes +#include <qmap.h> +#include <qregexp.h> +#include <kmessagebox.h> +#include <kdebug.h> +// app includes +#include "import_utils.h" +#include "../umlobject.h" +#include "../docwindow.h" +#include "../package.h" +#include "../enum.h" +#include "../classifier.h" +#include "../operation.h" +#include "../attribute.h" +#include "../template.h" +#include "../association.h" +#include "kdevcppparser/lexer.h" +#include "kdevcppparser/driver.h" +#include "kdevcppparser/cpptree2uml.h" + +// static members +CppDriver * CppImport::ms_driver; +QStringList CppImport::ms_seenFiles; + +class CppDriver : public Driver { +public: + void setupLexer(Lexer* lexer) { + Driver::setupLexer(lexer); + lexer->setRecordComments(true); + } +}; + +CppImport::CppImport() { + ms_driver = new CppDriver(); +} + +CppImport::~CppImport() {} + +void CppImport::feedTheModel(const QString& fileName) { + if (ms_seenFiles.find(fileName) != ms_seenFiles.end()) + return; + ms_seenFiles.append(fileName); + QMap<QString, Dependence> deps = ms_driver->dependences(fileName); + if (! deps.empty()) { + QMap<QString, Dependence>::Iterator it; + for (it = deps.begin(); it != deps.end(); ++it) { + if (it.data().second == Dep_Global) // don't want these + continue; + QString includeFile = it.key(); + if (includeFile.isEmpty()) { + kError() << fileName << ": " << it.data().first + << " not found" << endl; + continue; + } + kDebug() << fileName << ": " << includeFile << " => " << it.data().first << endl; + if (ms_seenFiles.find(includeFile) == ms_seenFiles.end()) + feedTheModel(includeFile); + } + } + TranslationUnitAST *ast = ms_driver->translationUnit( fileName ); + if (ast == NULL) { + kError() << "CppImport::feedTheModel: " << fileName << " not found" << endl; + return; + } + CppTree2Uml modelFeeder( fileName ); + modelFeeder.parseTranslationUnit( ast ); +} + +void CppImport::initialize() { + // Reset the driver + ms_driver->reset(); + // The driver shall attempt to parse included files. + ms_driver->setResolveDependencesEnabled( true ); + // Add some standard include paths + ms_driver->addIncludePath( "/usr/include" ); + ms_driver->addIncludePath( "/usr/include/c++" ); + ms_driver->addIncludePath( "/usr/include/g++" ); + ms_driver->addIncludePath( "/usr/local/include" ); + QStringList incPathList = Import_Utils::includePathList(); + if (incPathList.count()) { + QStringList::Iterator end(incPathList.end()); + for (QStringList::Iterator i(incPathList.begin()); i != end; ++i) { + ms_driver->addIncludePath( *i ); + } + + } + ms_seenFiles.clear(); +} + +void CppImport::parseFile(const QString& fileName) { + if (ms_seenFiles.find(fileName) != ms_seenFiles.end()) + return; + ms_driver->parseFile( fileName ); + feedTheModel(fileName); +} + diff --git a/umbrello/umbrello/codeimport/cppimport.h b/umbrello/umbrello/codeimport/cppimport.h new file mode 100644 index 00000000..3d5d8195 --- /dev/null +++ b/umbrello/umbrello/codeimport/cppimport.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005-2006 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +#ifndef CPPIMPORT_H +#define CPPIMPORT_H + +#include <qstring.h> +#include "classimport.h" + +class CppDriver; + +/** + * C++ code import + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +class CppImport : public ClassImport { +public: + CppImport(); + virtual ~CppImport(); + +protected: + /** + * Implement abstract operation from ClassImport for C++. + */ + void initialize(); + + /** + * Import a single file. + * + * @param filename The file to import. + */ + void parseFile(const QString& filename); + +private: + /** + * Auxiliary method for recursively traversing the #include dependencies + * in order to feed innermost includes to the model before dependent + * includes. It is important that includefiles are fed to the model + * in proper order so that references between UML objects are created + * properly. + */ + void feedTheModel(const QString& fileName); + + static CppDriver * ms_driver; + static QStringList ms_seenFiles; ///< auxiliary buffer for feedTheModel() + +}; + +#endif diff --git a/umbrello/umbrello/codeimport/idlimport.cpp b/umbrello/umbrello/codeimport/idlimport.cpp new file mode 100644 index 00000000..6d228baf --- /dev/null +++ b/umbrello/umbrello/codeimport/idlimport.cpp @@ -0,0 +1,356 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +// own header +#include "idlimport.h" + +#include <stdio.h> +// qt/kde includes +// #include <qprocess.h> //should use this instead of popen() +#include <qstringlist.h> +#include <qregexp.h> +#include <kdebug.h> +// app includes +#include "import_utils.h" +#include "../uml.h" +#include "../umldoc.h" +#include "../umlpackagelist.h" +#include "../package.h" +#include "../classifier.h" +#include "../enum.h" +#include "../operation.h" +#include "../attribute.h" + +IDLImport::IDLImport() : NativeImportBase("//") { + m_isOneway = m_isReadonly = m_isAttribute = false; + setMultiLineComment("/*", "*/"); +} + +IDLImport::~IDLImport() { +} + +/// Check for split type names (e.g. unsigned long long) +QString IDLImport::joinTypename() { + QString typeName = m_source[m_srcIndex]; + if (m_source[m_srcIndex] == "unsigned") + typeName += ' ' + advance(); + if (m_source[m_srcIndex] == "long" && + (m_source[m_srcIndex + 1] == "long" || m_source[m_srcIndex + 1] == "double")) + typeName += ' ' + advance(); + return typeName; +} + +bool IDLImport::preprocess(QString& line) { + // Ignore C preprocessor generated lines. + if (line.startsWith("#")) + return true; // done + return NativeImportBase::preprocess(line); +} + +void IDLImport::fillSource(const QString& word) { + QString lexeme; + const uint len = word.length(); + for (uint i = 0; i < len; i++) { + QChar c = word[i]; + if (c.isLetterOrNumber() || c == '_') { + lexeme += c; + } else if (c == ':' && word[i + 1] == ':') { + // compress scoped name into lexeme + lexeme += "::"; + i++; + } else if (c == '<') { + // compress sequence or bounded string into lexeme + do { + lexeme += word[i]; + } while (word[i] != '>' && ++i < len); + } else { + if (!lexeme.isEmpty()) { + m_source.append(lexeme); + lexeme = QString(); + } + m_source.append(QString(c)); + } + } + if (!lexeme.isEmpty()) + m_source.append(lexeme); +} + +void IDLImport::parseFile(const QString& filename) { + if (filename.contains('/')) { + QString path = filename; + path.remove( QRegExp("/[^/]+$") ); + kDebug() << "IDLImport::parseFile: adding path " << path << endl; + Import_Utils::addIncludePath(path); + } + QStringList includePaths = Import_Utils::includePathList(); + //QProcess command("cpp", UMLAp::app()); + QString command("cpp -C"); // -C means "preserve comments" + for (QStringList::Iterator pathIt = includePaths.begin(); + pathIt != includePaths.end(); ++pathIt) { + QString path = (*pathIt); + //command.addArgument(" -I" + path); + command += " -I" + path; + } + command += ' ' + filename; + kDebug() << "importIDL: " << command << endl; + FILE *fp = popen(command.ascii(), "r"); + if (fp == NULL) { + kError() << "IDLImport::parseFile: cannot popen(" << command << ")" << endl; + return; + } + // Scan the input file into the QStringList m_source. + m_source.clear(); + char buf[256]; + while (fgets(buf, sizeof(buf), fp) != NULL) { + int len = strlen(buf); + if (buf[len - 1] == '\n') + buf[--len] = '\0'; + NativeImportBase::scan( QString(buf) ); + } + // Parse the QStringList m_source. + m_scopeIndex = 0; + m_scope[0] = NULL; + const uint srcLength = m_source.count(); + for (m_srcIndex = 0; m_srcIndex < srcLength; m_srcIndex++) { + const QString& keyword = m_source[m_srcIndex]; + //kDebug() << '"' << keyword << '"' << endl; + if (keyword.startsWith(m_singleLineCommentIntro)) { + m_comment = keyword.mid(m_singleLineCommentIntro.length()); + continue; + } + if (! parseStmt()) + skipStmt(); + m_currentAccess = Uml::Visibility::Public; + m_comment = QString(); + } + pclose(fp); +} + +bool IDLImport::parseStmt() { + const QString& keyword = m_source[m_srcIndex]; + const uint srcLength = m_source.count(); + if (keyword == "module") { + const QString& name = advance(); + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Package, + name, m_scope[m_scopeIndex], m_comment); + m_scope[++m_scopeIndex] = static_cast<UMLPackage*>(ns); + m_scope[m_scopeIndex]->setStereotype("CORBAModule"); + if (advance() != "{") { + kError() << "importIDL: unexpected: " << m_source[m_srcIndex] << endl; + skipStmt("{"); + } + return true; + } + if (keyword == "interface") { + const QString& name = advance(); + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Class, + name, m_scope[m_scopeIndex], m_comment); + m_scope[++m_scopeIndex] = m_klass = static_cast<UMLClassifier*>(ns); + m_klass->setStereotype("CORBAInterface"); + m_klass->setAbstract(m_isAbstract); + m_isAbstract = false; + m_comment = QString(); + if (advance() == ";") // forward declaration + return true; + if (m_source[m_srcIndex] == ":") { + while (++m_srcIndex < srcLength && m_source[m_srcIndex] != "{") { + const QString& baseName = m_source[m_srcIndex]; + Import_Utils::createGeneralization(m_klass, baseName); + if (advance() != ",") + break; + } + } + if (m_source[m_srcIndex] != "{") { + kError() << "importIDL: ignoring excess chars at " + << name << endl; + skipStmt("{"); + } + return true; + } + if (keyword == "struct" || keyword == "exception") { + const QString& name = advance(); + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Class, + name, m_scope[m_scopeIndex], m_comment); + m_scope[++m_scopeIndex] = m_klass = static_cast<UMLClassifier*>(ns); + if (keyword == "struct") + m_klass->setStereotype("CORBAStruct"); + else + m_klass->setStereotype("CORBAException"); + if (advance() != "{") { + kError() << "importIDL: expecting '{' at " << name << endl; + skipStmt("{"); + } + return true; + } + if (keyword == "union") { + // TBD. <gulp> + skipStmt("}"); + m_srcIndex++; // advance to ';' + return true; + } + if (keyword == "enum") { + const QString& name = advance(); + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Enum, + name, m_scope[m_scopeIndex], m_comment); + UMLEnum *enumType = static_cast<UMLEnum*>(ns); + m_srcIndex++; // skip name + while (++m_srcIndex < srcLength && m_source[m_srcIndex] != "}") { + Import_Utils::addEnumLiteral(enumType, m_source[m_srcIndex]); + if (advance() != ",") + break; + } + skipStmt(); + return true; + } + if (keyword == "typedef") { + const QString& existingType = advance(); + const QString& newType = advance(); + Import_Utils::createUMLObject(Uml::ot_Class, newType, m_scope[m_scopeIndex], + m_comment, "CORBATypedef" /* stereotype */); + // @todo How do we convey the existingType ? + skipStmt(); + return true; + } + if (keyword == "const") { + skipStmt(); + return true; + } + if (keyword == "custom") { + return true; + } + if (keyword == "abstract") { + m_isAbstract = true; + return true; + } + if (keyword == "valuetype") { + const QString& name = advance(); + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Class, + name, m_scope[m_scopeIndex], m_comment); + m_scope[++m_scopeIndex] = m_klass = static_cast<UMLClassifier*>(ns); + m_klass->setAbstract(m_isAbstract); + m_isAbstract = false; + if (advance() == ";") // forward declaration + return true; + if (m_source[m_srcIndex] == ":") { + if (advance() == "truncatable") + m_srcIndex++; + while (m_srcIndex < srcLength && m_source[m_srcIndex] != "{") { + const QString& baseName = m_source[m_srcIndex]; + Import_Utils::createGeneralization(m_klass, baseName); + if (advance() != ",") + break; + m_srcIndex++; + } + } + if (m_source[m_srcIndex] != "{") { + kError() << "importIDL: ignoring excess chars at " + << name << endl; + skipStmt("{"); + } + return true; + } + if (keyword == "public") { + return true; + } + if (keyword == "private") { + m_currentAccess = Uml::Visibility::Private; + return true; + } + if (keyword == "readonly") { + m_isReadonly = true; + return true; + } + if (keyword == "attribute") { + m_isAttribute = true; + return true; + } + if (keyword == "oneway") { + m_isOneway = true; + return true; + } + if (keyword == "}") { + if (m_scopeIndex) + m_klass = dynamic_cast<UMLClassifier*>(m_scope[--m_scopeIndex]); + else + kError() << "importIDL: too many }" << endl; + m_srcIndex++; // skip ';' + return true; + } + if (keyword == ";") + return true; + // At this point, we expect `keyword' to be a type name + // (of a member of struct or valuetype, or return type + // of an operation.) Up next is the name of the attribute + // or operation. + if (! keyword.contains( QRegExp("^\\w") )) { + kError() << "importIDL: ignoring " << keyword << endl; + return false; + } + QString typeName = joinTypename(); + QString name = advance(); + if (name.contains( QRegExp("\\W") )) { + kError() << "importIDL: expecting name in " << name << endl; + return false; + } + // At this point we most definitely need a class. + if (m_klass == NULL) { + kError() << "importIDL: no class set for " << name << endl; + return false; + } + QString nextToken = advance(); + if (nextToken == "(") { + // operation + UMLOperation *op = Import_Utils::makeOperation(m_klass, name); + m_srcIndex++; + while (m_srcIndex < srcLength && m_source[m_srcIndex] != ")") { + const QString &direction = m_source[m_srcIndex++]; + QString typeName = joinTypename(); + const QString &parName = advance(); + UMLAttribute *att = Import_Utils::addMethodParameter(op, typeName, parName); + Uml::Parameter_Direction dir; + if (Model_Utils::stringToDirection(direction, dir)) + att->setParmKind(dir); + else + kError() << "importIDL: expecting parameter direction at " + << direction << endl; + if (advance() != ",") + break; + m_srcIndex++; + } + Import_Utils::insertMethod(m_klass, op, Uml::Visibility::Public, typeName, + false, false, false, false, m_comment); + if (m_isOneway) { + op->setStereotype("oneway"); + m_isOneway = false; + } + skipStmt(); // skip possible "raises" clause + return true; + } + // At this point we know it's some kind of attribute declaration. + while (1) { + while (nextToken != "," && nextToken != ";") { + name += nextToken; // add possible array dimensions to `name' + nextToken = advance(); + } + UMLObject *o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name, typeName, m_comment); + UMLAttribute *attr = static_cast<UMLAttribute*>(o); + if (m_isReadonly) { + attr->setStereotype("readonly"); + m_isReadonly = false; + } + if (nextToken != ",") + break; + name = advance(); + nextToken = advance(); + } + return true; +} + diff --git a/umbrello/umbrello/codeimport/idlimport.h b/umbrello/umbrello/codeimport/idlimport.h new file mode 100644 index 00000000..6945364f --- /dev/null +++ b/umbrello/umbrello/codeimport/idlimport.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +#ifndef IDLIMPORT_H +#define IDLIMPORT_H + +#include "nativeimportbase.h" + +/** + * CORBA IDL code import + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class IDLImport : public NativeImportBase { +public: + IDLImport(); + virtual ~IDLImport(); + + /** + * Implement abstract operation from NativeImportBase. + */ + bool parseStmt(); + + /** + * Reimplement operation from NativeImportBase. + * Need to do this because we use the external C preprocessor. + */ + void parseFile(const QString& file); + + /** + * Override operation from NativeImportBase. + */ + bool preprocess(QString& line); + + /** + * Implement abstract operation from NativeImportBase. + */ + void fillSource(const QString& word); + +protected: + QString joinTypename(); + bool m_isOneway, m_isReadonly, m_isAttribute; +}; + +#endif + diff --git a/umbrello/umbrello/codeimport/import_utils.cpp b/umbrello/umbrello/codeimport/import_utils.cpp new file mode 100644 index 00000000..92a87867 --- /dev/null +++ b/umbrello/umbrello/codeimport/import_utils.cpp @@ -0,0 +1,464 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005-2007 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +// own header +#include "import_utils.h" +// qt/kde includes +#include <qmap.h> +#include <qregexp.h> +#include <kmessagebox.h> +#include <kdebug.h> +#include <klocale.h> +// app includes +#include "../uml.h" +#include "../umldoc.h" +#include "../umllistview.h" +#include "../umllistviewitem.h" +#include "../umlobject.h" +#include "../package.h" +#include "../folder.h" +#include "../enum.h" +#include "../classifier.h" +#include "../operation.h" +#include "../attribute.h" +#include "../template.h" +#include "../association.h" +#include "../object_factory.h" + +#include <stdlib.h> + +namespace Import_Utils { + +/** + * Flag manipulated by createUMLObject(). + * Global state is generally bad, I know. + * It would be cleaner to make this into a return value from + * createUMLObject(). + */ +bool bNewUMLObjectWasCreated = false; + +/** + * Related classifier for creation of dependencies on template + * parameters in createUMLObject(). + */ +UMLClassifier * gRelatedClassifier = NULL; + +/** + * On encountering a scoped typename string where the scopes + * have not yet been seen, we synthesize UML objects for the + * unknown scopes (using a question dialog to the user to decide + * whether to treat a scope as a class or as a package.) + * However, such an unknown scope is put at the global level. + * I.e. before calling createUMLObject() we set this flag to true. + */ +bool bPutAtGlobalScope = false; + +/** + * The include path list (see addIncludePath() and includePathList()) + */ +QStringList incPathList; + +void putAtGlobalScope(bool yesno) { + bPutAtGlobalScope = yesno; +} + +void setRelatedClassifier(UMLClassifier *c) { + gRelatedClassifier = c; +} + +void assignUniqueIdOnCreation(bool yesno) { + Object_Factory::assignUniqueIdOnCreation(yesno); +} + +bool newUMLObjectWasCreated() { + return bNewUMLObjectWasCreated; +} + +QString formatComment(const QString &comment) { + if (comment.isEmpty()) + return comment; + + QStringList lines = QStringList::split("\n", comment); + QString& first = lines.first(); + QRegExp wordex("\\w"); + if (first.startsWith("/*")) { + int wordpos = wordex.search(first); + if (wordpos != -1) + first = first.mid(wordpos); // remove comment start + else + lines.pop_front(); // nothing interesting on this line + } + QString& last = lines.last(); + int endpos = last.find("*/"); + if (endpos != -1) { + if (last.contains(wordex)) + last = last.mid(0, endpos - 1); // remove comment end + else + lines.pop_back(); // nothing interesting on this line + } + if (! lines.count()) + return ""; + + QStringList::Iterator end(lines.end()); + for (QStringList::Iterator lit(lines.begin()); lit != end; ++lit) { + (*lit).remove(QRegExp("^\\s+")); + (*lit).remove(QRegExp("^\\*+\\s?")); + } + return lines.join("\n"); +} + +/* +UMLObject* findUMLObject(QString name, + Uml::Object_Type type) { + // Why an extra wrapper? See comment at addMethodParameter() + UMLObject * o = umldoc->findUMLObject(name, type); + return o; +} + */ + +UMLObject *createUMLObject(Uml::Object_Type type, + const QString& inName, + UMLPackage *parentPkg, + const QString& comment, + const QString& stereotype) { + QString name = inName; + UMLDoc *umldoc = UMLApp::app()->getDocument(); + UMLFolder *logicalView = umldoc->getRootFolder(Uml::mt_Logical); + const Uml::Programming_Language pl = UMLApp::app()->getActiveLanguage(); + if (parentPkg == NULL) { + // kDebug() << "Import_Utils::createUMLObject(" << name + // << "): parentPkg is NULL, assuming Logical View" << endl; + parentPkg = logicalView; + } + UMLObject * o = umldoc->findUMLObject(name, type, parentPkg); + bNewUMLObjectWasCreated = false; + if (o == NULL) { + // Strip possible adornments and look again. + int isConst = name.contains(QRegExp("^const ")); + name.remove(QRegExp("^const\\s+")); + QString typeName(name); + const int isAdorned = typeName.contains( QRegExp("[^\\w:\\. ]") ); + const int isPointer = typeName.contains('*'); + const int isRef = typeName.contains('&'); + typeName.remove(QRegExp("[^\\w:\\. ].*$")); + typeName = typeName.simplifyWhiteSpace(); + UMLObject *origType = umldoc->findUMLObject(typeName, Uml::ot_UMLObject, parentPkg); + if (origType == NULL) { + // Still not found. Create the stripped down type. + if (bPutAtGlobalScope) + parentPkg = logicalView; + // Find, or create, the scopes. + QStringList components; + if (typeName.contains("::")) { + components = QStringList::split("::", typeName); + } else if (typeName.contains(".")) { + components = QStringList::split(".", typeName); + } + if (components.count() > 1) { + typeName = components.back(); + components.pop_back(); + while ( components.count() ) { + QString scopeName = components.front(); + components.pop_front(); + o = umldoc->findUMLObject(scopeName, Uml::ot_UMLObject, parentPkg); + if (o) { + parentPkg = static_cast<UMLPackage*>(o); + continue; + } + int wantNamespace = KMessageBox::Yes; + if (pl == Uml::pl_Cpp) { + /* We know std and Qt are namespaces */ + if (scopeName != "std" && scopeName != "Qt") { + wantNamespace = KMessageBox::questionYesNo(NULL, + i18n("Is the scope %1 a namespace or a class?").arg(scopeName), + i18n("C++ Import Requests Your Help"), + i18n("Namespace"), i18n("Class")); + } + } + Uml::Object_Type ot = (wantNamespace == KMessageBox::Yes ? Uml::ot_Package : Uml::ot_Class); + o = Object_Factory::createUMLObject(ot, scopeName, parentPkg); + parentPkg = static_cast<UMLPackage*>(o); + UMLListView *listView = UMLApp::app()->getListView(); + UMLListViewItem *lvitem = listView->findUMLObject(o); + listView->setCurrentItem(lvitem); + } + // All scope qualified datatypes live in the global scope. + bPutAtGlobalScope = true; + } + Uml::Object_Type t = type; + if (type == Uml::ot_UMLObject || isAdorned) + t = Uml::ot_Class; + origType = Object_Factory::createUMLObject(t, typeName, parentPkg, false); + bNewUMLObjectWasCreated = true; + bPutAtGlobalScope = false; + } + if (isConst || isAdorned) { + // Create the full given type (including adornments.) + if (isConst) + name.prepend("const "); + o = Object_Factory::createUMLObject(Uml::ot_Datatype, name, + umldoc->getDatatypeFolder(), + false); //solicitNewName + UMLClassifier *dt = static_cast<UMLClassifier*>(o); + UMLClassifier *c = dynamic_cast<UMLClassifier*>(origType); + if (c) + dt->setOriginType(c); + else + kError() << "createUMLObject(" << name << "): " + << "origType " << typeName << " is not a UMLClassifier" + << endl; + if (isRef || isPointer) + dt->setIsReference(); + /* + if (isPointer) { + UMLObject *pointerDecl = Object_Factory::createUMLObject(Uml::ot_Datatype, type); + UMLClassifier *dt = static_cast<UMLClassifier*>(pointerDecl); + dt->setOriginType(classifier); + dt->setIsReference(); + classifier = dt; + } */ + } else { + o = origType; + } + } else if (parentPkg && !bPutAtGlobalScope) { + UMLPackage *existingPkg = o->getUMLPackage(); + if (existingPkg != umldoc->getDatatypeFolder()) { + if (existingPkg) + existingPkg->removeObject(o); + else + kError() << "createUMLObject(" << name << "): " + << "o->getUMLPackage() was NULL" << endl; + o->setUMLPackage(parentPkg); + parentPkg->addObject(o); + } + } + QString strippedComment = formatComment(comment); + if (! strippedComment.isEmpty()) { + o->setDoc(strippedComment); + } + if (!stereotype.isEmpty()) { + o->setStereotype(stereotype); + } + if (gRelatedClassifier == NULL || gRelatedClassifier == o) + return o; + QRegExp templateInstantiation("^[\\w:\\.]+\\s*<(.*)>"); + int pos = templateInstantiation.search(name); + if (pos == -1) + return o; + // Create dependencies on template parameters. + QString caption = templateInstantiation.cap(1); + QStringList params = QStringList::split(QRegExp("[^\\w:\\.]+"), caption); + if (!params.count()) + return o; + QStringList::Iterator end(params.end()); + for (QStringList::Iterator it(params.begin()); it != end; ++it) { + UMLObject *p = umldoc->findUMLObject(*it, Uml::ot_UMLObject, parentPkg); + if (p == NULL || p->getBaseType() == Uml::ot_Datatype) + continue; + const Uml::Association_Type at = Uml::at_Dependency; + UMLAssociation *assoc = umldoc->findAssociation(at, gRelatedClassifier, p); + if (assoc) + continue; + assoc = new UMLAssociation(at, gRelatedClassifier, p); + assoc->setUMLPackage(umldoc->getRootFolder(Uml::mt_Logical)); + umldoc->addAssociation(assoc); + } + return o; +} + +UMLOperation* makeOperation(UMLClassifier *parent, const QString &name) { + UMLOperation *op = Object_Factory::createOperation(parent, name); + return op; +} + +UMLObject* insertAttribute(UMLClassifier *owner, + Uml::Visibility scope, + const QString& name, + UMLClassifier *attrType, + const QString& comment /* ="" */, + bool isStatic /* =false */) { + Uml::Object_Type ot = owner->getBaseType(); + Uml::Programming_Language pl = UMLApp::app()->getActiveLanguage(); + if (! (ot == Uml::ot_Class || ot == Uml::ot_Interface && pl == Uml::pl_Java)) { + kDebug() << "insertAttribute: Don't know what to do with " + << owner->getName() << " (object type " << ot << ")" << endl; + return NULL; + } + UMLObject *o = owner->findChildObject(name, Uml::ot_Attribute); + if (o) { + return o; + } + + UMLAttribute *attr = owner->addAttribute(name, attrType, scope); + attr->setStatic(isStatic); + QString strippedComment = formatComment(comment); + if (! strippedComment.isEmpty()) { + attr->setDoc(strippedComment); + } + + UMLApp::app()->getDocument()->setModified(true); + return attr; +} + +UMLObject* insertAttribute(UMLClassifier *owner, Uml::Visibility scope, + const QString& name, + const QString& type, + const QString& comment /* ="" */, + bool isStatic /* =false */) { + UMLObject *attrType = owner->findTemplate(type); + if (attrType == NULL) { + bPutAtGlobalScope = true; + gRelatedClassifier = owner; + attrType = createUMLObject(Uml::ot_UMLObject, type, owner); + gRelatedClassifier = NULL; + bPutAtGlobalScope = false; + } + return insertAttribute (owner, scope, name, + static_cast<UMLClassifier*>(attrType), + comment, isStatic); +} + +void insertMethod(UMLClassifier *klass, UMLOperation* &op, + Uml::Visibility scope, const QString& type, + bool isStatic, bool isAbstract, + bool isFriend, bool isConstructor, + const QString& comment) { + op->setVisibility(scope); + if (!type.isEmpty() // return type may be missing (constructor/destructor) + && type != "void") { + if (type == klass->getName()) { + op->setType(klass); + } else { + UMLObject *typeObj = klass->findTemplate(type); + if (typeObj == NULL) { + bPutAtGlobalScope = true; + gRelatedClassifier = klass; + typeObj = createUMLObject(Uml::ot_UMLObject, type, klass); + gRelatedClassifier = NULL; + bPutAtGlobalScope = false; + op->setType(typeObj); + } + } + } + + op->setStatic(isStatic); + op->setAbstract(isAbstract); + + // if the operation is friend, add it as a stereotype + if (isFriend) + op->setStereotype("friend"); + // if the operation is a constructor, add it as a stereotype + if (isConstructor) + op->setStereotype("constructor"); + + QString strippedComment = formatComment(comment); + if (! strippedComment.isEmpty()) { + op->setDoc(strippedComment); + } + + UMLAttributeList params = op->getParmList(); + UMLOperation *exist = klass->checkOperationSignature(op->getName(), params); + if (exist) { + // copy contents to existing operation + exist->setVisibility(scope); + exist->setStatic(isStatic); + exist->setAbstract(isAbstract); + if (! strippedComment.isEmpty()) + exist->setDoc(strippedComment); + UMLAttributeList exParams = exist->getParmList(); + UMLAttribute *param, *exParam = exParams.first(); + for (UMLAttributeListIt it(params); (param = it.current()) != NULL; + ++it, exParam = exParams.next()) { + exParam->setName(param->getName()); + exParam->setVisibility(param->getVisibility()); + exParam->setStatic(param->getStatic()); + exParam->setAbstract(param->getAbstract()); + exParam->setDoc(param->getDoc()); + exParam->setInitialValue(param->getInitialValue()); + exParam->setParmKind(param->getParmKind()); + } + // delete incoming UMLOperation and pass out the existing one + delete op; + op = exist; + } else { + klass->addOperation(op); + } +} + +UMLAttribute* addMethodParameter(UMLOperation *method, + const QString& type, + const QString& name) { + UMLClassifier *owner = static_cast<UMLClassifier*>(method->parent()); + UMLObject *typeObj = owner->findTemplate(type); + if (typeObj == NULL) { + bPutAtGlobalScope = true; + gRelatedClassifier = owner; + typeObj = createUMLObject(Uml::ot_UMLObject, type, owner); + gRelatedClassifier = NULL; + bPutAtGlobalScope = false; + } + UMLAttribute *attr = Object_Factory::createAttribute(method, name, typeObj); + method->addParm(attr); + return attr; +} + +void addEnumLiteral(UMLEnum *enumType, const QString &literal, const QString &comment) { + UMLObject *el = enumType->addEnumLiteral(literal); + el->setDoc(comment); +} + +void createGeneralization(UMLClassifier *child, UMLClassifier *parent) { + // if the child is an interface, so is the parent. + if (child->isInterface()) + parent->setBaseType(Uml::ot_Interface); + Uml::Association_Type association = Uml::at_Generalization; + + if (parent->isInterface() && !child->isInterface()) { + // if the parent is an interface, but the child is not, then + // this is really realization. + // + association = Uml::at_Realization; + } + UMLAssociation *assoc = new UMLAssociation(association, child, parent); + UMLDoc *umldoc = UMLApp::app()->getDocument(); + assoc->setUMLPackage(umldoc->getRootFolder(Uml::mt_Logical)); + umldoc->addAssociation(assoc); +} + +void createGeneralization(UMLClassifier *child, const QString &parentName) { + UMLObject *parentObj = createUMLObject( Uml::ot_Class, parentName ); + UMLClassifier *parent = static_cast<UMLClassifier*>(parentObj); + createGeneralization(child, parent); +} + +QStringList includePathList() { + QStringList includePathList(incPathList); + char *umbrello_incpath = getenv( "UMBRELLO_INCPATH" ); + if (umbrello_incpath) { + includePathList += QStringList::split( ':', umbrello_incpath ); + } + return includePathList; +} + +void addIncludePath(const QString& path) { + if (! incPathList.contains(path)) + incPathList.append(path); +} + + +bool isDatatype(const QString& name, UMLPackage *parentPkg) { + UMLDoc *umldoc = UMLApp::app()->getDocument(); + UMLObject * o = umldoc->findUMLObject(name, Uml::ot_Datatype, parentPkg); + return (o!=NULL); +} + +} // end namespace Import_Utils + diff --git a/umbrello/umbrello/codeimport/import_utils.h b/umbrello/umbrello/codeimport/import_utils.h new file mode 100644 index 00000000..7d36bc77 --- /dev/null +++ b/umbrello/umbrello/codeimport/import_utils.h @@ -0,0 +1,175 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005-2006 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +#ifndef IMPORT_UTILS_H +#define IMPORT_UTILS_H + +#include <qstringlist.h> +#include "../umlnamespace.h" +#include "../umlattributelist.h" + +class UMLObject; +class UMLClassifier; +class UMLPackage; +class UMLOperation; +class UMLEnum; + +/** + * Utilities for code import + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ + +namespace Import_Utils { + + /** + * Find or create a document object. + */ + UMLObject* createUMLObject(Uml::Object_Type type, + const QString& name, + UMLPackage *parentPkg = NULL, + const QString& comment = QString::null, + const QString& stereotype = QString::null); + /** + * Control whether an object which is newly created by createUMLObject() + * is put at the global scope. + * + * @param yesno When set to false, the object is created at the scope + * given by the parentPkg argument of createUMLObject(). + */ + void putAtGlobalScope(bool yesno); + + /** + * Set a related classifier for creation of dependencies on template + * parameters in createUMLObject(). + */ + void setRelatedClassifier(UMLClassifier *c); + + /** + * Control whether the creation methods solicit a new unique ID for the + * created object. + * By default, unique ID generation is turned on. + * + * @param yesno False turns UID generation off, true turns it on. + */ + void assignUniqueIdOnCreation(bool yesno); + + /** + * Create a UMLAttribute and insert it into the document. + */ + UMLObject* insertAttribute(UMLClassifier *klass, Uml::Visibility scope, + const QString& name, + const QString& type, + const QString& comment = QString::null, + bool isStatic = false); + /** + * Create a UMLAttribute and insert it into the document. + * Use the specified existing attrType. + */ + UMLObject* insertAttribute(UMLClassifier *klass, Uml::Visibility scope, + const QString& name, + UMLClassifier *attrType, + const QString& comment /* ="" */, + bool isStatic /* =false */); + /** + * Create a UMLOperation. + * The reason for this method is to not generate any Qt signals. + * Instead, these are generated by insertMethod(). + * (If we generated a creation signal prematurely, i.e. without + * the method parameters being known yet, then that would lead to + * a conflict with a pre-existing parameterless method of the same + * name.) + */ + UMLOperation* makeOperation(UMLClassifier *parent, const QString &name); + + /** + * Insert the UMLOperation into the given classifier. + * + * @param klass The classifier into which the operation shall be added. + * @param op Reference to pointer to the temporary UMLOperation + * for insertion. The caller relinquishes ownership of the + * object pointed to. If an UMLOperation of same signature + * already exists at the classifier then the incoming + * UMLOperation is deleted and the pointer is set to the + * existing UMLOperation. + * @param scope The Uml::Visibility of the method + * @param type The return type + * @param isStatic boolean switch to decide if method is static + * @param isAbstract boolean switch to decide if method is abstract + * @param isFriend true boolean switch to decide if methods is a friend function + * @param isConstructor boolean switch to decide if methods is a constructor + * @param comment The Documentation for this method + */ + void insertMethod(UMLClassifier *klass, UMLOperation* &op, + Uml::Visibility scope, const QString& type, + bool isStatic, bool isAbstract, + bool isFriend = false, bool isConstructor = false, + const QString& comment = QString::null); + + /** + * Add an argument to a UMLOperation. + * The parentPkg arg is only used for resolving possible scope + * prefixes in the `type'. + */ + UMLAttribute* addMethodParameter(UMLOperation *method, + const QString& type, + const QString& name); + + /** + * Add an enum literal to an UMLEnum. + */ + void addEnumLiteral(UMLEnum *enumType, const QString &literal, + const QString &comment = QString()); + + /** + * Create a generalization from the given child classifier to the given + * parent classifier. + */ + void createGeneralization(UMLClassifier *child, UMLClassifier *parent); + + /** + * Create a generalization from the existing child UMLObject to the given + * parent class name. + */ + void createGeneralization(UMLClassifier *child, const QString &parentName); + + /** + * Strip comment lines of leading whitespace and stars. + */ + QString formatComment(const QString &comment); + + /** + * Return the list of paths set by previous calls to addIncludePath() + * and the environment variable UMBRELLO_INCPATH. + * This list can be used for finding #included (or Ada with'ed or...) + * files. + */ + QStringList includePathList(); + + /** + * Add a path to the include path list. + */ + void addIncludePath(const QString& path); + + /** + * Returns whether the last createUMLObject() actually created + * a new object or just returned an existing one. + */ + bool newUMLObjectWasCreated(); + + /** + * Returns true if a type is an actual Datatype + */ + bool isDatatype(const QString& name, UMLPackage *parentPkg = NULL); + +} // end namespace Import_Utils + +#endif diff --git a/umbrello/umbrello/codeimport/javaimport.cpp b/umbrello/umbrello/codeimport/javaimport.cpp new file mode 100644 index 00000000..8df6e5e7 --- /dev/null +++ b/umbrello/umbrello/codeimport/javaimport.cpp @@ -0,0 +1,549 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +// own header +#include "javaimport.h" + +// qt/kde includes +#include <qfile.h> +#include <qtextstream.h> +#include <qstringlist.h> +#include <qregexp.h> +#include <kdebug.h> +// app includes +#include "import_utils.h" +#include "../uml.h" +#include "../umldoc.h" +#include "../umlpackagelist.h" +#include "../package.h" +#include "../classifier.h" +#include "../enum.h" +#include "../operation.h" +#include "../attribute.h" + +QStringList JavaImport::s_filesAlreadyParsed; +int JavaImport::s_parseDepth = 0; + +JavaImport::JavaImport() : NativeImportBase("//") { + setMultiLineComment("/*", "*/"); + initVars(); +} + +JavaImport::~JavaImport() { +} + +void JavaImport::initVars() { + m_isStatic = false; +} + +/// Catenate possible template arguments/array dimensions to the end of the type name. +QString JavaImport::joinTypename(QString typeName) { + if (m_source[m_srcIndex + 1] == "<" || + m_source[m_srcIndex + 1] == "[") { + uint start = ++m_srcIndex; + if (! skipToClosing(m_source[start][0])) + return typeName; + for (uint i = start; i <= m_srcIndex; i++) { + typeName += m_source[i]; + } + } + // to handle multidimensional arrays, call recursively + if (m_source[m_srcIndex + 1] == "[") { + typeName = joinTypename( typeName ); + } + return typeName; +} + +void JavaImport::fillSource(const QString& word) { + QString lexeme; + const uint len = word.length(); + for (uint i = 0; i < len; i++) { + const QChar& c = word[i]; + if (c.isLetterOrNumber() || c == '_' || c == '.') { + lexeme += c; + } else { + if (!lexeme.isEmpty()) { + m_source.append(lexeme); + lexeme = QString(); + } + m_source.append(QString(c)); + } + } + if (!lexeme.isEmpty()) + m_source.append(lexeme); +} + + +///Spawn off an import of the specified file +void JavaImport::spawnImport( QString file ) { + // if the file is being parsed, don't bother + // + if (s_filesAlreadyParsed.contains( file ) ) { + return; + } + if (QFile::exists(file)) { + JavaImport importer; + QStringList fileList; + fileList.append( file ); + s_filesAlreadyParsed.append( file ); + importer.importFiles( fileList ); + } +} + + +///returns the UML Object if found, or null otherwise +UMLObject* findObject( QString name, UMLPackage *parentPkg ) { + UMLDoc *umldoc = UMLApp::app()->getDocument(); + UMLObject * o = umldoc->findUMLObject(name, Uml::ot_UMLObject , parentPkg); + return o; +} + + +///Resolve the specified className +UMLObject* JavaImport::resolveClass (QString className) { + kDebug() << "importJava trying to resolve " << className << endl; + // keep track if we are dealing with an array + // + bool isArray = className.contains('['); + // remove any [] so that the class itself can be resolved + // + QString baseClassName = className; + baseClassName.remove('['); + baseClassName.remove(']'); + + // java has a few implicit imports. Most relevant for this is the + // current package, which is in the same directory as the current file + // being parsed + // + QStringList file = QStringList::split( '/', m_currentFileName); + // remove the filename. This leaves the full path to the containing + // dir which should also include the package hierarchy + // + file.pop_back(); + + // the file we're looking for might be in the same directory as the + // current class + // + QString myDir = file.join( "/" ); + QString myFile = '/' + myDir + '/' + baseClassName + ".java"; + if ( QFile::exists(myFile) ) { + spawnImport( myFile ); + if ( isArray ) { + // we have imported the type. For arrays we want to return + // the array type + return Import_Utils::createUMLObject(Uml::ot_Class, className, m_scope[m_scopeIndex]); + } + return findObject(baseClassName, m_scope[m_scopeIndex]); + } + + // the class we want is not in the same package as the one being imported. + // use the imports to find the one we want. + // + QStringList package = QStringList::split( '.', m_currentPackage); + int dirsInPackageCount = package.size(); + + for (int count=0; count < dirsInPackageCount; count ++ ) { + // pop off one by one the directories, until only the source root remains + // + file.pop_back(); + } + // this is now the root of any further source imports + QString sourceRoot = '/' + file.join("/") + '/'; + + for (QStringList::Iterator pathIt = m_imports.begin(); + pathIt != m_imports.end(); ++pathIt) { + QString import = (*pathIt); + QStringList split = QStringList::split( '.', import ); + split.pop_back(); // remove the * or the classname + if ( import.endsWith( "*" ) || import.endsWith( baseClassName) ) { + // check if the file we want is in this imported package + // convert the org.test type package into a filename + // + QString aFile = sourceRoot + split.join("/") + '/' + baseClassName + ".java"; + if ( QFile::exists(aFile) ) { + spawnImport( aFile ); + // we need to set the package for the class that will be resolved + // start at the root package + UMLPackage *parent = m_scope[0]; + UMLPackage *current = NULL; + + for (QStringList::Iterator it = split.begin(); it != split.end(); ++it) { + QString name = (*it); + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Package, + name, parent); + current = static_cast<UMLPackage*>(ns); + parent = current; + } // for + if ( isArray ) { + // we have imported the type. For arrays we want to return + // the array type + return Import_Utils::createUMLObject(Uml::ot_Class, className, current); + } + // now that we have the right package, the class should be findable + return findObject(baseClassName, current); + } // if file exists + } // if import matches + } //foreach import + return NULL; // no match +} + + +/// keep track of the current file being parsed and reset the list of imports +void JavaImport::parseFile(const QString& filename) { + m_currentFileName= filename; + m_imports.clear(); + // default visibility is Impl, unless we are an interface, then it is + // public for member vars and methods + m_defaultCurrentAccess = Uml::Visibility::Implementation; + m_currentAccess = m_defaultCurrentAccess; + s_parseDepth++; + // in the case of self referencing types, we can avoid parsing the + // file twice by adding it to the list + s_filesAlreadyParsed.append(filename); + NativeImportBase::parseFile(filename); + s_parseDepth--; + if ( s_parseDepth <= 0 ) { + // if the user decides to clear things out and reparse, we need + // to honor the request, so reset things for next time. + s_filesAlreadyParsed.clear(); + s_parseDepth = 0; + } +} + + + + +bool JavaImport::parseStmt() { + const uint srcLength = m_source.count(); + const QString& keyword = m_source[m_srcIndex]; + //kDebug() << '"' << keyword << '"' << endl; + if (keyword == "package") { + m_currentPackage = advance(); + const QString& qualifiedName = m_currentPackage; + QStringList names = QStringList::split(".", qualifiedName); + for (QStringList::Iterator it = names.begin(); it != names.end(); ++it) { + QString name = (*it); + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Package, + name, m_scope[m_scopeIndex], m_comment); + m_scope[++m_scopeIndex] = static_cast<UMLPackage*>(ns); + } + if (advance() != ";") { + kError() << "importJava: unexpected: " << m_source[m_srcIndex] << endl; + skipStmt(); + } + return true; + } + if (keyword == "class" || keyword == "interface") { + const QString& name = advance(); + const Uml::Object_Type t = (keyword == "class" ? Uml::ot_Class : Uml::ot_Interface); + UMLObject *ns = Import_Utils::createUMLObject(t, name, m_scope[m_scopeIndex], m_comment); + m_scope[++m_scopeIndex] = m_klass = static_cast<UMLClassifier*>(ns); + m_klass->setAbstract(m_isAbstract); + m_klass->setStatic(m_isStatic); + m_klass->setVisibility(m_currentAccess); + // The UMLObject found by createUMLObject might originally have been created as a + // placeholder with a type of class but if is really an interface, then we need to + // change it. + Uml::Object_Type ot = (keyword == "interface" ? Uml::ot_Interface : Uml::ot_Class); + m_klass->setBaseType(ot); + m_isAbstract = m_isStatic = false; + // if no modifier is specified in an interface, then it means public + if ( m_klass->isInterface() ) { + m_defaultCurrentAccess = Uml::Visibility::Public; + } + if (advance() == ";") // forward declaration + return true; + if (m_source[m_srcIndex] == "<") { + // template args - preliminary, rudimentary implementation + // @todo implement all template arg syntax + uint start = m_srcIndex; + if (! skipToClosing('<')) { + kError() << "importJava(" << name << "): template syntax error" << endl; + return false; + } + while (1) { + const QString arg = m_source[++start]; + if (! arg.contains( QRegExp("^[A-Za-z_]") )) { + kDebug() << "importJava(" << name << "): cannot handle template syntax (" + << arg << ")" << endl; + break; + } + /* UMLTemplate *tmpl = */ m_klass->addTemplate(arg); + const QString next = m_source[++start]; + if (next == ">") + break; + if (next != ",") { + kDebug() << "importJava(" << name << "): can't handle template syntax (" + << next << ")" << endl; + break; + } + } + advance(); // skip over ">" + } + if (m_source[m_srcIndex] == "extends") { + const QString& baseName = advance(); + // try to resolve the class we are extending, or if impossible + // create a placeholder + UMLObject *parent = resolveClass( baseName ); + if ( parent ) { + Import_Utils::createGeneralization(m_klass, static_cast<UMLClassifier*>(parent)); + } else { + kDebug() << "importJava parentClass " << baseName + << " is not resolveable. Creating placeholder" << endl; + Import_Utils::createGeneralization(m_klass, baseName); + } + advance(); + } + if (m_source[m_srcIndex] == "implements") { + while (m_srcIndex < srcLength - 1 && advance() != "{") { + const QString& baseName = m_source[m_srcIndex]; + // try to resolve the interface we are implementing, if this fails + // create a placeholder + UMLObject *interface = resolveClass( baseName ); + if ( interface ) { + Import_Utils::createGeneralization(m_klass, static_cast<UMLClassifier*>(interface)); + } else { + kDebug() << "importJava implementing interface "<< baseName + <<" is not resolvable. Creating placeholder" <<endl; + Import_Utils::createGeneralization(m_klass, baseName); + } + if (advance() != ",") + break; + } + } + if (m_source[m_srcIndex] != "{") { + kError() << "importJava: ignoring excess chars at " << name + << " (" << m_source[m_srcIndex] << ")" << endl; + skipStmt("{"); + } + return true; + } + if (keyword == "enum") { + const QString& name = advance(); + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Enum, + name, m_scope[m_scopeIndex], m_comment); + UMLEnum *enumType = static_cast<UMLEnum*>(ns); + skipStmt("{"); + while (m_srcIndex < srcLength - 1 && advance() != "}") { + Import_Utils::addEnumLiteral(enumType, m_source[m_srcIndex]); + QString next = advance(); + if (next == "{" || next == "(") { + if (! skipToClosing(next[0])) + return false; + next = advance(); + } + if (next != ",") { + if (next == ";") { + // @todo handle methods in enum + // For now, we cheat (skip them) + m_source[m_srcIndex] = "{"; + if (! skipToClosing('{')) + return false; + } + break; + } + } + return true; + } + if (keyword == "static") { + m_isStatic = true; + return true; + } + // if we detected static previously and keyword is { then this is a static block + if (m_isStatic && keyword == "{") { + // reset static flag and jump to end of static block + m_isStatic = false; + return skipToClosing('{'); + } + if (keyword == "abstract") { + m_isAbstract = true; + return true; + } + if (keyword == "public") { + m_currentAccess = Uml::Visibility::Public; + return true; + } + if (keyword == "protected") { + m_currentAccess = Uml::Visibility::Protected; + return true; + } + if (keyword == "private") { + m_currentAccess = Uml::Visibility::Private; + return true; + } + if (keyword == "final" || + keyword == "native" || + keyword == "synchronized" || + keyword == "transient" || + keyword == "volatile") { + //@todo anything to do here? + return true; + } + if (keyword == "import") { + // keep track of imports so we can resolve classes we are dependent on + QString import = advance(); + if ( import.endsWith(".") ) { + //this most likely an import that ends with a * + // + import = import + advance(); + } + m_imports.append( import ); + + // move past ; + skipStmt(); + return true; + } + if (keyword == "@") { // annotation + advance(); + if (m_source[m_srcIndex + 1] == "(") { + advance(); + skipToClosing('('); + } + return true; + } + if (keyword == "}") { + if (m_scopeIndex) + m_klass = dynamic_cast<UMLClassifier*>(m_scope[--m_scopeIndex]); + else + kError() << "importJava: too many }" << endl; + return true; + } + // At this point, we expect `keyword' to be a type name + // (of a member of class or interface, or return type + // of an operation.) Up next is the name of the attribute + // or operation. + if (! keyword.contains( QRegExp("^\\w") )) { + kError() << "importJava: ignoring " << keyword << endl; + return false; + } + QString typeName = m_source[m_srcIndex]; + typeName = joinTypename(typeName); + // At this point we need a class. + if (m_klass == NULL) { + kError() << "importJava: no class set for " << typeName << endl; + return false; + } + QString name = advance(); + QString nextToken; + if (typeName == m_klass->getName() && name == "(") { + // Constructor. + nextToken = name; + name = typeName; + typeName = QString(); + } else { + nextToken = advance(); + } + if (name.contains( QRegExp("\\W") )) { + kError() << "importJava: expecting name in " << name << endl; + return false; + } + if (nextToken == "(") { + // operation + UMLOperation *op = Import_Utils::makeOperation(m_klass, name); + m_srcIndex++; + while (m_srcIndex < srcLength && m_source[m_srcIndex] != ")") { + QString typeName = m_source[m_srcIndex]; + if ( typeName == "final" || typeName.startsWith( "//") ) { + // ignore the "final" keyword and any comments in method args + typeName = advance(); + } + typeName = joinTypename(typeName); + QString parName = advance(); + // the Class might not be resolved yet so resolve it if necessary + UMLObject *obj = resolveClass(typeName); + if (obj) { + // by prepending the package, unwanted placeholder types will not get created + typeName = obj->getFullyQualifiedName("."); + } + /* UMLAttribute *att = */ Import_Utils::addMethodParameter(op, typeName, parName); + if (advance() != ",") + break; + m_srcIndex++; + } + // before adding the method, try resolving the return type + UMLObject *obj = resolveClass(typeName); + if (obj) { + // using the fully qualified name means that a placeholder type will not be created. + typeName = obj->getFullyQualifiedName("."); + } + Import_Utils::insertMethod(m_klass, op, m_currentAccess, typeName, + m_isStatic, m_isAbstract, false /*isFriend*/, + false /*isConstructor*/, m_comment); + m_isAbstract = m_isStatic = false; + // reset the default visibility + m_currentAccess = m_defaultCurrentAccess; + // At this point we do not know whether the method has a body or not. + do { + nextToken = advance(); + } while (nextToken != "{" && nextToken != ";"); + if (nextToken == ";") { + // No body (interface or abstract) + return true; + } else { + return skipToClosing('{'); + } + } + // At this point we know it's some kind of attribute declaration. + while (1) { + while (nextToken != "," && nextToken != ";") { + if (nextToken == "=") { + if ((nextToken = advance()) == "new") { + advance(); + if ((nextToken = advance()) == "(") { + skipToClosing('('); + if ((nextToken = advance()) == "{") { + skipToClosing('{'); + } else { + skipStmt(); + break; + } + } else { + skipStmt(); + break; + } + } else { + skipStmt(); + break; + } + } else { + name += nextToken; // add possible array dimensions to `name' + } + nextToken = advance(); + } + // try to resolve the class type, or create a placeholder if that fails + UMLObject *type = resolveClass( typeName ); + UMLObject *o; + if (type) { + o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name, + static_cast<UMLClassifier*>(type), m_comment, m_isStatic); + } else { + o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name, + typeName, m_comment, m_isStatic); + } + // UMLAttribute *attr = static_cast<UMLAttribute*>(o); + if (nextToken != ",") { + // reset the modifiers + m_isStatic = m_isAbstract = false; + break; + } + name = advance(); + nextToken = advance(); + } + // reset visibility to default + m_currentAccess = m_defaultCurrentAccess; + if (m_source[m_srcIndex] != ";") { + kError() << "importJava: ignoring trailing items at " << name << endl; + skipStmt(); + } + return true; +} + + diff --git a/umbrello/umbrello/codeimport/javaimport.h b/umbrello/umbrello/codeimport/javaimport.h new file mode 100644 index 00000000..30fa2395 --- /dev/null +++ b/umbrello/umbrello/codeimport/javaimport.h @@ -0,0 +1,106 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +#ifndef JAVAIMPORT_H +#define JAVAIMPORT_H + +#include "nativeimportbase.h" +#include "../umlobject.h" + +/** + * Java code import + * @author Oliver Kellogg + * @author JP Fournier + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class JavaImport : public NativeImportBase { +public: + JavaImport(); + virtual ~JavaImport(); + +protected: + /** + * Reimplement operation from NativeImportBase. + */ + void initVars(); + + /** + * Implement abstract operation from NativeImportBase. + */ + bool parseStmt(); + + /** + * Implement abstract operation from NativeImportBase. + */ + void fillSource(const QString& word); + + /** + * Keep track of the filename currently being parsed + */ + void parseFile(const QString& filename); + + /** + * Try to resolve the specified class the current class depends on + */ + UMLObject* resolveClass (QString className); + + /** + * spawn off an import of the specified file + */ + void spawnImport(QString file); + + /** + * figure out if the type is really an array or template of the given typeName + */ + QString joinTypename(QString typeName); + + /** + * true if the member var or method is declared static + */ + bool m_isStatic; + + /** + * The current filename being parsed + */ + QString m_currentFileName; + + /** + * the current package of the file being parsed + */ + QString m_currentPackage; + + /** + * the imports included in the current file + */ + QStringList m_imports; + + /** + * Keep track of the files we have already parsed so we don't + * reparse the same ones over and over again. + */ + static QStringList s_filesAlreadyParsed; + + /** + * Keep track of the parses so that the filesAlreadyParsed + * can be reset when we're done. + */ + static int s_parseDepth; + + /** + * The current visibility for when the visibility is absent + */ + Uml::Visibility m_defaultCurrentAccess; + + +}; + +#endif + diff --git a/umbrello/umbrello/codeimport/kdevcppparser/Makefile.am b/umbrello/umbrello/codeimport/kdevcppparser/Makefile.am new file mode 100644 index 00000000..b8325478 --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/Makefile.am @@ -0,0 +1,5 @@ +INCLUDES = $(all_includes) + +noinst_LTLIBRARIES = libkdevcppparser.la +libkdevcppparser_la_SOURCES = ast.cpp driver.cpp errors.cpp lexer.cpp lookup.cpp parser.cpp tree_parser.cpp urlutil.cpp ast_utils.cpp cpptree2uml.cpp + diff --git a/umbrello/umbrello/codeimport/kdevcppparser/README b/umbrello/umbrello/codeimport/kdevcppparser/README new file mode 100644 index 00000000..3ed39eba --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/README @@ -0,0 +1,56 @@ +This directory contains the C++ parser from Kdevelop-3.0. + +Following files are copies from the directory kdevelop/lib/cppparser: + ast.{h,cpp} driver.{h,cpp} errors.{h,cpp} keywords.lut.h lexer.{h,cpp} + lookup.{h,cpp} parser.{h,cpp} tree_parser.{h,cpp} + +Following files are copies from the directory kdevelop/languages/cpp: + ast_utils.{h,cpp} + +Following files are copies from the directory kdevelop/lib/util: + urlutil.{h,cpp} + +The source files cpptree2uml.{h,cpp} are based on kdevelop/languages/cpp/ +store_walker.{h,cpp}. The class CppTree2Uml inherits from class TreeParser +and overrides certain methods from that class. + +CppTree2Uml visits the nodes of the abstract syntax tree constructed by the +CppParser, and constructs UML objects on the way. + +The import_utils.h (in the parent directory) is the interface between the +CppTree2Uml and Umbrello. +import_utils implements the construction of the UML objects. CppTree2Uml calls +the create/insert methods in the import_utils while traversing the syntax +tree. + +The one and only method that Umbrello uses for accessing the C++ parser, +and any other parser for that matter, is ClassImport::importFiles(). +The class CppImport (in the parent directory) implements that operation. +Thus we have these classes: + + +-------------------+ + | <<interface>> | + | ClassImport | + +===================+ +Umbrello ------>| importFiles() = 0 | + +-------------------+ + A + | <<realize>> + | + +-------------------+ +-------------------+ + | CppImport | | CppTree2Uml | + +===================+ <<invoke>> +===================+ + | importFiles() |-------------->| | + +-------------------+ | | + +-------------------+ + | + +-------------------+ | + | <<utility>> | | + | Import_Utils | | + +===================+ | + | createUMLObject() | <<invoke>> | + | insertMethod() |<-----------------------+ + | insertAttribute() | + +-------------------+ + | +Umbrello <--------------+ diff --git a/umbrello/umbrello/codeimport/kdevcppparser/ast.cpp b/umbrello/umbrello/codeimport/kdevcppparser/ast.cpp new file mode 100644 index 00000000..6baca685 --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/ast.cpp @@ -0,0 +1,1183 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "ast.h" +#include <qstringlist.h> +#include <kdebug.h> + +QString nodeTypeToString( int type ) +{ + switch( type ) + { + case NodeType_Generic: + return "Generic"; + case NodeType_TemplateArgumentList: + return "TemplateArgumentList"; + case NodeType_ClassOrNamespaceName: + return "ClassOrNamespaceName"; + case NodeType_Name: + return "Name"; + case NodeType_Declaration: + return "Declaration"; + case NodeType_TypeSpecifier: + return "TypeSpecifier"; + case NodeType_BaseSpecifier: + return "BaseSpecifier"; + case NodeType_BaseClause: + return "BaseClause"; + case NodeType_ClassSpecifier: + return "ClassSpecifier"; + case NodeType_Enumerator: + return "Enumerator"; + case NodeType_EnumSpecifier: + return "EnumSpecifier"; + case NodeType_ElaboratedTypeSpecifier: + return "ElaboratedTypeSpecifier"; + case NodeType_LinkageBody: + return "LinkageBody"; + case NodeType_LinkageSpecification: + return "LinkageSpecification"; + case NodeType_Namespace: + return "Namespace"; + case NodeType_NamespaceAlias: + return "NamespaceAlias"; + case NodeType_Using: + return "Using"; + case NodeType_UsingDirective: + return "UsingDirective"; + case NodeType_InitDeclaratorList: + return "InitDeclaratorList"; + case NodeType_Typedef: + return "Typedef"; + case NodeType_Declarator: + return "Declarator"; + case NodeType_InitDeclarator: + return "InitDeclarator"; + case NodeType_TemplateDeclaration: + return "TemplateDeclaration"; + case NodeType_SimpleDeclaration: + return "SimpleDeclaration"; + case NodeType_Statement: + return "Statement"; + case NodeType_IfStatement: + return "IfStatement"; + case NodeType_WhileStatement: + return "WhileStatement"; + case NodeType_DoStatement: + return "DoStatement"; + case NodeType_ForStatement: + return "ForStatement"; + case NodeType_SwitchStatement: + return "SwitchStatement"; + case NodeType_DeclarationStatement: + return "DeclarationStatement"; + case NodeType_StatementList: + return "StatementList"; + case NodeType_TranslationUnit: + return "TranslationUnit"; + case NodeType_FunctionDefinition: + return "FunctionDefinition"; + case NodeType_ExpressionStatement: + return "ExpressionStatement"; + case NodeType_ParameterDeclaration: + return "ParameterDeclaration"; + case NodeType_ParameterDeclarationList: + return "ParameterDeclarationList"; + case NodeType_ParameterDeclarationClause: + return "ParameterDeclarationClause"; + case NodeType_Group: + return "Group"; + case NodeType_AccessDeclaration: + return "AccessDeclaration"; + case NodeType_TypeParameter: + return "TypeParameter"; + case NodeType_TemplateParameter: + return "TemplateParameter"; + case NodeType_TemplateParameterList: + return "TemplateParameterList"; + case NodeType_Condition: + return "Condition"; + case NodeType_Custom: + return "Custom"; + } + + return QString::null; +} + + +// ------------------------------------------------------------------------ +AST::AST() + : m_nodeType( NodeType_Generic ), m_parent( 0 ), + m_startLine( 0 ), m_startColumn( 0 ), + m_endLine( 0 ), m_endColumn( 0 ) +{ +#ifndef CPPPARSER_NO_CHILDREN + m_children.setAutoDelete( false ); +#endif +} + +AST::~AST() +{ +#ifndef CPPPARSER_NO_CHILDREN + if( m_parent ) + m_parent->removeChild( this ); +#endif +} + +void AST::setStartPosition( int line, int col ) +{ + m_startLine = line; + m_startColumn = col; +} + +void AST::getStartPosition( int* line, int* col ) const +{ + if( line ) + *line = m_startLine; + + if( col ) + * col = m_startColumn; +} + +void AST::setEndPosition( int line, int col ) +{ + m_endLine = line; + m_endColumn = col; +} + +void AST::getEndPosition( int* line, int* col ) const +{ + if( line ) + *line = m_endLine; + + if( col ) + * col = m_endColumn; +} + +void AST::setParent( AST* parent ) +{ +#ifndef CPPPARSER_NO_CHILDREN + if( m_parent ) + m_parent->removeChild( this ); +#endif + + m_parent = parent; + +#ifndef CPPPARSER_NO_CHILDREN + if( m_parent ) + m_parent->appendChild( this ); +#endif +} + +#ifndef CPPPARSER_NO_CHILDREN +void AST::appendChild( AST* child ) +{ + m_children.append( child ); +} + +void AST::removeChild( AST* child ) +{ + m_children.remove( child ); +} +#endif + +// ------------------------------------------------------------------------ +NameAST::NameAST() + : m_global( false ) +{ + m_classOrNamespaceNameList.setAutoDelete( true ); +} + +void NameAST::setGlobal( bool b ) +{ + m_global = b; +} + +void NameAST::setUnqualifiedName( ClassOrNamespaceNameAST::Node& unqualifiedName ) +{ + m_unqualifiedName = unqualifiedName; + if( m_unqualifiedName.get() ) m_unqualifiedName->setParent( this ); +} + +void NameAST::addClassOrNamespaceName( ClassOrNamespaceNameAST::Node& classOrNamespaceName ) +{ + if( !classOrNamespaceName.get() ) + return; + + classOrNamespaceName->setParent( this ); + m_classOrNamespaceNameList.append( classOrNamespaceName.release() ); +} + +QString NameAST::text() const +{ + if( !m_unqualifiedName.get() ) + return QString::null; + + QString str; + + if( m_global ) + str += "::"; + + QStringList l; + QPtrListIterator<ClassOrNamespaceNameAST> it( m_classOrNamespaceNameList ); + while( it.current() ){ + str += it.current()->text() + "::"; + ++it; + } + + if( m_unqualifiedName.get() ) + str += m_unqualifiedName->text(); + + return str; +} + +// ------------------------------------------------------------------------ +DeclarationAST::DeclarationAST() +{ +} + +// ------------------------------------------------------------------------ +LinkageBodyAST::LinkageBodyAST() +{ + m_declarationList.setAutoDelete( true ); +} + +void LinkageBodyAST::addDeclaration( DeclarationAST::Node& ast ) +{ + if( !ast.get() ) + return; + + ast->setParent( this ); + m_declarationList.append( ast.release() ); +} + +// ------------------------------------------------------------------------ +LinkageSpecificationAST::LinkageSpecificationAST() +{ +} + +void LinkageSpecificationAST::setExternType( AST::Node& externType ) +{ + m_externType = externType; + if( m_externType.get() ) m_externType->setParent( this ); +} + +void LinkageSpecificationAST::setLinkageBody( LinkageBodyAST::Node& linkageBody ) +{ + m_linkageBody = linkageBody; + if( m_linkageBody.get() ) m_linkageBody->setParent( this ); +} + +void LinkageSpecificationAST::setDeclaration( DeclarationAST::Node& decl ) +{ + m_declaration = decl; + if( m_declaration.get() ) m_declaration->setParent( this ); +} + +// ------------------------------------------------------------------------ +TranslationUnitAST::TranslationUnitAST() +{ + //kdDebug(9007) << "++ TranslationUnitAST::TranslationUnitAST()" << endl; + m_declarationList.setAutoDelete( true ); +} + +void TranslationUnitAST::addDeclaration( DeclarationAST::Node& ast ) +{ + if( !ast.get() ) + return; + + ast->setParent( this ); + m_declarationList.append( ast.release() ); +} + +// ------------------------------------------------------------------------ +NamespaceAST::NamespaceAST() +{ +} + +void NamespaceAST::setNamespaceName( AST::Node& namespaceName ) +{ + m_namespaceName = namespaceName; + if( m_namespaceName.get() ) m_namespaceName->setParent( this ); +} + +void NamespaceAST::setLinkageBody( LinkageBodyAST::Node& linkageBody ) +{ + m_linkageBody = linkageBody; + if( m_linkageBody.get() ) m_linkageBody->setParent( this ); +} + + +// ------------------------------------------------------------------------ +NamespaceAliasAST::NamespaceAliasAST() +{ +} + +void NamespaceAliasAST::setNamespaceName( AST::Node& namespaceName ) +{ + m_namespaceName = namespaceName; + if( m_namespaceName.get() ) m_namespaceName->setParent( this ); +} + +void NamespaceAliasAST::setAliasName( NameAST::Node& name ) +{ + m_aliasName = name; + if( m_aliasName.get() ) m_aliasName->setParent( this ); +} + +// ------------------------------------------------------------------------ +UsingAST::UsingAST() +{ +} + +void UsingAST::setTypeName( AST::Node& typeName ) +{ + m_typeName = typeName; + if( m_typeName.get() ) m_typeName->setParent( this ); +} + +void UsingAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +// ------------------------------------------------------------------------ +UsingDirectiveAST::UsingDirectiveAST() +{ +} + +void UsingDirectiveAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +TypedefAST::TypedefAST() +{ +} + +void TypeSpecifierAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +void TypedefAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void TypedefAST::setInitDeclaratorList( InitDeclaratorListAST::Node& initDeclaratorList ) +{ + m_initDeclaratorList = initDeclaratorList; + if( m_initDeclaratorList.get() ) m_initDeclaratorList->setParent( this ); +} + +// ------------------------------------------------------------------------ +TemplateArgumentListAST::TemplateArgumentListAST() +{ + m_argumentList.setAutoDelete( true ); +} + +void TemplateArgumentListAST::addArgument( AST::Node& arg ) +{ + if( !arg.get() ) + return; + + arg->setParent( this ); + m_argumentList.append( arg.release() ); +} + +QString TemplateArgumentListAST::text() const +{ + QStringList l; + + QPtrListIterator<AST> it( m_argumentList ); + while( it.current() ){ + l.append( it.current()->text() ); + ++it; + } + + return l.join( ", " ); +} + +// ------------------------------------------------------------------------ +TemplateDeclarationAST::TemplateDeclarationAST() +{ +} + +void TemplateDeclarationAST::setExported( AST::Node& exported ) +{ + m_exported = exported; + if( m_exported.get() ) m_exported->setParent( this ); +} + +void TemplateDeclarationAST::setTemplateParameterList( TemplateParameterListAST::Node& templateParameterList ) +{ + m_templateParameterList = templateParameterList; + if( m_templateParameterList.get() ) m_templateParameterList->setParent( this ); +} + +void TemplateDeclarationAST::setDeclaration( DeclarationAST::Node& declaration ) +{ + m_declaration = declaration; + if( m_declaration.get() ) m_declaration->setParent( this ); +} + +// ------------------------------------------------------------------------ +ClassOrNamespaceNameAST::ClassOrNamespaceNameAST() +{ +} + +void ClassOrNamespaceNameAST::setName( AST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +void ClassOrNamespaceNameAST::setTemplateArgumentList( TemplateArgumentListAST::Node& templateArgumentList ) +{ + m_templateArgumentList = templateArgumentList; + if( m_templateArgumentList.get() ) m_templateArgumentList->setParent( this ); +} + +QString ClassOrNamespaceNameAST::text() const +{ + if( !m_name.get() ) + return QString::null; + + QString str = m_name->text(); + if( m_templateArgumentList.get() ) + str += QString::fromLatin1("< ") + m_templateArgumentList->text() + QString::fromLatin1(" >"); + + return str; +} + +// ------------------------------------------------------------------------ +TypeSpecifierAST::TypeSpecifierAST() +{ +} + +void TypeSpecifierAST::setCvQualify( GroupAST::Node& cvQualify ) +{ + m_cvQualify = cvQualify; + if( m_cvQualify.get() ) m_cvQualify->setParent( this ); +} + +void TypeSpecifierAST::setCv2Qualify( GroupAST::Node& cv2Qualify ) +{ + m_cv2Qualify = cv2Qualify; + if( m_cv2Qualify.get() ) m_cv2Qualify->setParent( this ); +} + +QString TypeSpecifierAST::text() const +{ + QString str; + + if( m_cvQualify.get() ) + str += m_cvQualify->text() + ' '; + + if( m_name.get() ) + str += m_name->text(); + + if( m_cv2Qualify.get() ) + str += QString(" ") + m_cv2Qualify->text(); + + return str; +} + +// ------------------------------------------------------------------------ +ClassSpecifierAST::ClassSpecifierAST() +{ + m_declarationList.setAutoDelete( true ); +} + +void ClassSpecifierAST::setClassKey( AST::Node& classKey ) +{ + m_classKey = classKey; + if( m_classKey.get() ) m_classKey->setParent( this ); +} + +void ClassSpecifierAST::addDeclaration( DeclarationAST::Node& declaration ) +{ + if( !declaration.get() ) + return; + + declaration->setParent( this ); + m_declarationList.append( declaration.release() ); +} + +void ClassSpecifierAST::setBaseClause( BaseClauseAST::Node& baseClause ) +{ + m_baseClause = baseClause; + if( m_baseClause.get() ) m_baseClause->setParent( this ); +} + +// ------------------------------------------------------------------------ +EnumSpecifierAST::EnumSpecifierAST() +{ + m_enumeratorList.setAutoDelete( true ); +} + +void EnumSpecifierAST::addEnumerator( EnumeratorAST::Node& enumerator ) +{ + if( !enumerator.get() ) + return; + + enumerator->setParent( this ); + m_enumeratorList.append( enumerator.release() ); +} + + +// ------------------------------------------------------------------------ +ElaboratedTypeSpecifierAST::ElaboratedTypeSpecifierAST() +{ +} + +void ElaboratedTypeSpecifierAST::setKind( AST::Node& kind ) +{ + m_kind = kind; + if( m_kind.get() ) m_kind->setParent( this ); +} + +QString ElaboratedTypeSpecifierAST::text() const +{ + if( m_kind.get() ) + return m_kind->text() + ' ' + TypeSpecifierAST::text(); + + return TypeSpecifierAST::text(); +} + +// ------------------------------------------------------------------------ +StatementAST::StatementAST() +{ +} + +// ------------------------------------------------------------------------ +EnumeratorAST::EnumeratorAST() +{ +} + +void EnumeratorAST::setId( AST::Node& id ) +{ + m_id = id; + if( m_id.get() ) m_id->setParent( this ); +} + +void EnumeratorAST::setExpr( AST::Node& expr ) +{ + m_expr = expr; + if( m_expr.get() ) m_expr->setParent( this ); +} + +// ------------------------------------------------------------------------ +BaseClauseAST::BaseClauseAST() +{ + m_baseSpecifierList.setAutoDelete( true ); +} + +void BaseClauseAST::addBaseSpecifier( BaseSpecifierAST::Node& baseSpecifier ) +{ + if( !baseSpecifier.get() ) + return; + + baseSpecifier->setParent( this ); + m_baseSpecifierList.append( baseSpecifier.release() ); +} + +// ------------------------------------------------------------------------ +BaseSpecifierAST::BaseSpecifierAST() +{ +} + +void BaseSpecifierAST::setIsVirtual( AST::Node& isVirtual ) +{ + m_isVirtual = isVirtual; + if( m_isVirtual.get() ) m_isVirtual->setParent( this ); +} + +void BaseSpecifierAST::setAccess( AST::Node& access ) +{ + m_access = access; + if( m_access.get() ) m_access->setParent( this ); +} + +void BaseSpecifierAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +// ------------------------------------------------------------------------ +SimpleDeclarationAST::SimpleDeclarationAST() +{ +} + +void SimpleDeclarationAST::setFunctionSpecifier( GroupAST::Node& functionSpecifier ) +{ + m_functionSpecifier = functionSpecifier; + if( m_functionSpecifier.get() ) m_functionSpecifier->setParent( this ); +} + +void SimpleDeclarationAST::setStorageSpecifier( GroupAST::Node& storageSpecifier ) +{ + m_storageSpecifier = storageSpecifier; + if( m_storageSpecifier.get() ) m_storageSpecifier->setParent( this ); +} + +void SimpleDeclarationAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void SimpleDeclarationAST::setInitDeclaratorList( InitDeclaratorListAST::Node& initDeclaratorList ) +{ + m_initDeclaratorList = initDeclaratorList; + if( m_initDeclaratorList.get() ) m_initDeclaratorList->setParent( this ); +} + +void SimpleDeclarationAST::setWinDeclSpec( GroupAST::Node& winDeclSpec ) +{ + m_winDeclSpec = winDeclSpec; + if( m_winDeclSpec.get() ) m_winDeclSpec->setParent( this ); +} + + +// ------------------------------------------------------------------------ +InitDeclaratorListAST::InitDeclaratorListAST() +{ + m_initDeclaratorList.setAutoDelete( true ); +} + +void InitDeclaratorListAST::addInitDeclarator( InitDeclaratorAST::Node& decl ) +{ + if( !decl.get() ) + return; + + decl->setParent( this ); + m_initDeclaratorList.append( decl.release() ); +} + +// ------------------------------------------------------------------------ +DeclaratorAST::DeclaratorAST() +{ + m_ptrOpList.setAutoDelete( true ); + m_arrayDimensionList.setAutoDelete( true ); +} + +void DeclaratorAST::setSubDeclarator( DeclaratorAST::Node& subDeclarator ) +{ + m_subDeclarator = subDeclarator; + if( m_subDeclarator.get() ) m_subDeclarator->setParent( this ); +} + +void DeclaratorAST::setDeclaratorId( NameAST::Node& declaratorId ) +{ + m_declaratorId = declaratorId; + if( m_declaratorId.get() ) m_declaratorId->setParent( this ); +} + +void DeclaratorAST::setBitfieldInitialization( AST::Node& bitfieldInitialization ) +{ + m_bitfieldInitialization = bitfieldInitialization; + if( m_bitfieldInitialization.get() ) m_bitfieldInitialization->setParent( this ); +} + +void DeclaratorAST::addArrayDimension( AST::Node& arrayDimension ) +{ + if( !arrayDimension.get() ) + return; + + arrayDimension->setParent( this ); + m_arrayDimensionList.append( arrayDimension.release() ); +} + +void DeclaratorAST::setParameterDeclarationClause( AUTO_PTR<class ParameterDeclarationClauseAST>& parameterDeclarationClause ) +{ + m_parameterDeclarationClause = parameterDeclarationClause; + if( m_parameterDeclarationClause.get() ) m_parameterDeclarationClause->setParent( this ); +} + +void DeclaratorAST::setConstant( AST::Node& constant ) +{ + m_constant = constant; + if( m_constant.get() ) m_constant->setParent( this ); +} + +void DeclaratorAST::setExceptionSpecification( GroupAST::Node& exceptionSpecification ) +{ + m_exceptionSpecification = exceptionSpecification; + if( m_exceptionSpecification.get() ) m_exceptionSpecification->setParent( this ); +} + +void DeclaratorAST::addPtrOp( AST::Node& ptrOp ) +{ + if( !ptrOp.get() ) + return; + + ptrOp->setParent( this ); + m_ptrOpList.append( ptrOp.release() ); +} + +// -------------------------------------------------------------------------- +InitDeclaratorAST::InitDeclaratorAST() +{ +} + +void InitDeclaratorAST::setDeclarator( DeclaratorAST::Node& declarator ) +{ + m_declarator = declarator; + if( m_declarator.get() ) m_declarator->setParent( this ); +} + +void InitDeclaratorAST::setInitializer( AST::Node& initializer ) +{ + m_initializer = initializer; + if( m_initializer.get() ) m_initializer->setParent( this ); +} + +// -------------------------------------------------------------------------- +FunctionDefinitionAST::FunctionDefinitionAST() +{ +} + +void FunctionDefinitionAST::setFunctionSpecifier( GroupAST::Node& functionSpecifier ) +{ + m_functionSpecifier = functionSpecifier; + if( m_functionSpecifier.get() ) m_functionSpecifier->setParent( this ); +} + +void FunctionDefinitionAST::setStorageSpecifier( GroupAST::Node& storageSpecifier ) +{ + m_storageSpecifier = storageSpecifier; + if( m_storageSpecifier.get() ) m_storageSpecifier->setParent( this ); +} + +void FunctionDefinitionAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void FunctionDefinitionAST::setInitDeclarator( InitDeclaratorAST::Node& initDeclarator ) +{ + m_initDeclarator = initDeclarator; + if( m_initDeclarator.get() ) m_initDeclarator->setParent( this ); +} + +void FunctionDefinitionAST::setFunctionBody( StatementListAST::Node& functionBody ) +{ + m_functionBody = functionBody; + if( m_functionBody.get() ) m_functionBody->setParent( this ); +} + +void FunctionDefinitionAST::setWinDeclSpec( GroupAST::Node& winDeclSpec ) +{ + m_winDeclSpec = winDeclSpec; + if( m_winDeclSpec.get() ) m_winDeclSpec->setParent( this ); +} + +// -------------------------------------------------------------------------- +StatementListAST::StatementListAST() +{ + m_statementList.setAutoDelete( true ); +} + +void StatementListAST::addStatement( StatementAST::Node& statement ) +{ + if( !statement.get() ) + return; + + statement->setParent( this ); + m_statementList.append( statement.release() ); +} + +// -------------------------------------------------------------------------- +IfStatementAST::IfStatementAST() +{ +} + +void IfStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void IfStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +void IfStatementAST::setElseStatement( StatementAST::Node& elseStatement ) +{ + m_elseStatement = elseStatement; + if( m_elseStatement.get() ) m_elseStatement->setParent( this ); +} + +// -------------------------------------------------------------------------- +WhileStatementAST::WhileStatementAST() +{ +} + +void WhileStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void WhileStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +// -------------------------------------------------------------------------- +DoStatementAST::DoStatementAST() +{ +} + +void DoStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void DoStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +// -------------------------------------------------------------------------- +ForStatementAST::ForStatementAST() +{ +} + +void ForStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void ForStatementAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + +void ForStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +void ForStatementAST::setInitStatement( StatementAST::Node& initStatement ) +{ + m_initStatement = initStatement; + if( m_initStatement.get() ) m_initStatement->setParent( this ); +} + +// -------------------------------------------------------------------------- +SwitchStatementAST::SwitchStatementAST() +{ +} + +void SwitchStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void SwitchStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +// -------------------------------------------------------------------------- +DeclarationStatementAST::DeclarationStatementAST() +{ +} + +void DeclarationStatementAST::setDeclaration( DeclarationAST::Node& declaration ) +{ + m_declaration = declaration; + if( m_declaration.get() ) m_declaration->setParent( this ); +} + +// -------------------------------------------------------------------------- +ExpressionStatementAST::ExpressionStatementAST() +{ +} + +void ExpressionStatementAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + + +// -------------------------------------------------------------------------- +ParameterDeclarationAST::ParameterDeclarationAST() +{ +} + +void ParameterDeclarationAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void ParameterDeclarationAST::setDeclarator( DeclaratorAST::Node& declarator ) +{ + m_declarator = declarator; + if( m_declarator.get() ) m_declarator->setParent( this ); +} + +void ParameterDeclarationAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + +QString ParameterDeclarationAST::text() const +{ + QString str; + if( m_typeSpec.get() ) + str += m_typeSpec->text() + ' '; + + if( m_declarator.get() ) + str += m_declarator->text(); + + if( m_expression.get() ) + str += QString( " = " ) + m_expression->text(); + + return str; +} + +// -------------------------------------------------------------------------- +ParameterDeclarationListAST::ParameterDeclarationListAST() +{ + m_parameterList.setAutoDelete( true ); +} + +void ParameterDeclarationListAST::addParameter( ParameterDeclarationAST::Node& parameter ) +{ + if( !parameter.get() ) + return; + + parameter->setParent( this ); + m_parameterList.append( parameter.release() ); +} + +QString ParameterDeclarationListAST::text() const +{ + QStringList l; + + QPtrListIterator<ParameterDeclarationAST> it( m_parameterList ); + while( it.current() ){ + l.append( it.current()->text() ); + ++it; + } + + return l.join( ", " ); +} + + +// -------------------------------------------------------------------------- +ParameterDeclarationClauseAST::ParameterDeclarationClauseAST() +{ +} + +void ParameterDeclarationClauseAST::setParameterDeclarationList( ParameterDeclarationListAST::Node& parameterDeclarationList ) +{ + m_parameterDeclarationList = parameterDeclarationList; + if( m_parameterDeclarationList.get() ) m_parameterDeclarationList->setParent( this ); +} + +void ParameterDeclarationClauseAST::setEllipsis( AST::Node& ellipsis ) +{ + m_ellipsis = ellipsis; + if( m_ellipsis.get() ) m_ellipsis->setParent( this ); +} + +QString ParameterDeclarationClauseAST::text() const +{ + QString str; + + if( m_parameterDeclarationList.get() ) + str += m_parameterDeclarationList->text(); + + if( m_ellipsis.get() ) + str += " ..."; + + return str; +} + + +// -------------------------------------------------------------------------- +GroupAST::GroupAST() +{ + m_nodeList.setAutoDelete( true ); +} + +void GroupAST::addNode( AST::Node& node ) +{ + if( !node.get() ) + return; + + node->setParent( this ); + m_nodeList.append( node.release() ); +} + +QString GroupAST::text() const +{ + QStringList l; + + QPtrListIterator<AST> it( m_nodeList ); + while( it.current() ){ + l.append( it.current()->text() ); + ++it; + } + + return l.join( " " ); +} + +// -------------------------------------------------------------------------- +AccessDeclarationAST::AccessDeclarationAST() +{ + m_accessList.setAutoDelete( true ); +} + +void AccessDeclarationAST::addAccess( AST::Node& access ) +{ + if( !access.get() ) + return; + + access->setParent( this ); + m_accessList.append( access.release() ); +} + +QString AccessDeclarationAST::text() const +{ + QStringList l; + + QPtrListIterator<AST> it( m_accessList ); + while( it.current() ){ + l.append( it.current()->text() ); + ++it; + } + + return l.join( " " ); +} + +// -------------------------------------------------------------------------- +TypeParameterAST::TypeParameterAST() +{ +} + +void TypeParameterAST::setKind( AST::Node& kind ) +{ + m_kind = kind; + if( m_kind.get() ) m_kind->setParent( this ); +} + +void TypeParameterAST::setTemplateParameterList( AUTO_PTR<class TemplateParameterListAST>& templateParameterList ) +{ + m_templateParameterList = templateParameterList; + if( m_templateParameterList.get() ) m_templateParameterList->setParent( this ); +} + +void TypeParameterAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +void TypeParameterAST::setTypeId( AST::Node& typeId ) +{ + m_typeId = typeId; + if( m_typeId.get() ) m_typeId->setParent( this ); +} + +// -------------------------------------------------------------------------- +TemplateParameterAST::TemplateParameterAST() +{ +} + +void TemplateParameterAST::setTypeParameter( TypeParameterAST::Node& typeParameter ) +{ + m_typeParameter = typeParameter; + if( m_typeParameter.get() ) m_typeParameter->setParent( this ); +} + +void TemplateParameterAST::setTypeValueParameter( ParameterDeclarationAST::Node& typeValueParameter ) +{ + m_typeValueParameter = typeValueParameter; + if( m_typeValueParameter.get() ) m_typeValueParameter->setParent( this ); +} + +// -------------------------------------------------------------------------- +TemplateParameterListAST::TemplateParameterListAST() +{ + m_templateParameterList.setAutoDelete( true ); +} + +void TemplateParameterListAST::addTemplateParameter( TemplateParameterAST::Node& templateParameter ) +{ + if( !templateParameter.get() ) + return; + + templateParameter->setParent( this ); + m_templateParameterList.append( templateParameter.release() ); +} + +// -------------------------------------------------------------------------- +ConditionAST::ConditionAST() +{ +} + +void ConditionAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void ConditionAST::setDeclarator( DeclaratorAST::Node& declarator ) +{ + m_declarator = declarator; + if( m_declarator.get() ) m_declarator->setParent( this ); +} + +void ConditionAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + +void ClassSpecifierAST::setWinDeclSpec( GroupAST::Node & winDeclSpec ) +{ + m_winDeclSpec = winDeclSpec; + if( m_winDeclSpec.get() ) m_winDeclSpec->setParent( this ); +} + diff --git a/umbrello/umbrello/codeimport/kdevcppparser/ast.h b/umbrello/umbrello/codeimport/kdevcppparser/ast.h new file mode 100644 index 00000000..9b7b5aac --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/ast.h @@ -0,0 +1,1449 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef __ast_h +#define __ast_h + +#include <memory> +#include <qstring.h> +#include <qptrlist.h> + +#if defined( Q_OS_WIN32 ) || defined( Q_CC_SUN ) + +#ifndef _THROW0 +# define _THROW0() +#endif + +template <class _Tp> class AUTO_PTR { +private: + _Tp* _M_ptr; + +public: + typedef _Tp element_type; + + explicit AUTO_PTR(_Tp* __p = 0) _THROW0() : _M_ptr(__p) {} + + template <class _Tp1> AUTO_PTR(AUTO_PTR<_Tp1>& __a) _THROW0() + : _M_ptr(__a.release()) {} + + AUTO_PTR(AUTO_PTR& __a) _THROW0() : _M_ptr(__a.release()) {} + + + + template <class _Tp1> + AUTO_PTR& operator=(AUTO_PTR<_Tp1>& __a) _THROW0() { + if (__a.get() != this->get()) { + delete _M_ptr; + _M_ptr = __a.release(); + } + return *this; + } + + AUTO_PTR& operator=(AUTO_PTR& __a) _THROW0() { + if (&__a != this) { + delete _M_ptr; + _M_ptr = __a.release(); + } + return *this; + } + + ~AUTO_PTR() _THROW0() { delete _M_ptr; } + + _Tp& operator*() const _THROW0() { + return *_M_ptr; + } + _Tp* operator->() const _THROW0() { + return _M_ptr; + } + _Tp* get() const _THROW0() { + return _M_ptr; + } + _Tp* release() _THROW0() { + _Tp* __tmp = _M_ptr; + _M_ptr = 0; + return __tmp; + } + void reset(_Tp* __p = 0) _THROW0() { + delete _M_ptr; + _M_ptr = __p; + } + + // According to the C++ standard, these conversions are required. Most + // present-day compilers, however, do not enforce that requirement---and, + // in fact, most present-day compilers do not support the language + // features that these conversions rely on. + + +private: + template<class _Tp1> struct AUTO_PTR_ref { + _Tp1* _M_ptr; + AUTO_PTR_ref(_Tp1* __p) : _M_ptr(__p) {} + }; + +public: + AUTO_PTR(AUTO_PTR_ref<_Tp> __ref) _THROW0() + : _M_ptr(__ref._M_ptr) {} + template <class _Tp1> operator AUTO_PTR_ref<_Tp1>() _THROW0() + { return AUTO_PTR_ref<_Tp>(this->release()); } + template <class _Tp1> operator AUTO_PTR<_Tp1>() _THROW0() + { return AUTO_PTR<_Tp1>(this->release()) } + +}; + +#else +#define AUTO_PTR std::auto_ptr +#endif + +template <class T> typename T::Node CreateNode() +{ + typename T::Node node( new T ); + node->setNodeType( T::Type ); + return node; +} + +template <class T> typename T::Node NullNode() +{ + typename T::Node node; + return node; +} + +enum NodeType +{ + NodeType_Generic = 0, + + NodeType_TemplateArgumentList = 1000, + NodeType_ClassOrNamespaceName, + NodeType_Name, + NodeType_Declaration, + NodeType_TypeSpecifier, + NodeType_BaseSpecifier, + NodeType_BaseClause, + NodeType_ClassSpecifier, + NodeType_Enumerator, + NodeType_EnumSpecifier, + NodeType_ElaboratedTypeSpecifier, + NodeType_LinkageBody, + NodeType_LinkageSpecification, + NodeType_Namespace, + NodeType_NamespaceAlias, + NodeType_Using, + NodeType_UsingDirective, + NodeType_InitDeclaratorList, + NodeType_Typedef, + NodeType_Declarator, + NodeType_InitDeclarator, + NodeType_TemplateDeclaration, + NodeType_SimpleDeclaration, + NodeType_Statement, + NodeType_StatementList, + NodeType_IfStatement, + NodeType_WhileStatement, + NodeType_DoStatement, + NodeType_ForStatement, + NodeType_SwitchStatement, + NodeType_DeclarationStatement, + NodeType_TranslationUnit, + NodeType_FunctionDefinition, + NodeType_ExpressionStatement, + NodeType_ParameterDeclaration, + NodeType_ParameterDeclarationList, + NodeType_ParameterDeclarationClause, + NodeType_Group, + NodeType_AccessDeclaration, + NodeType_TypeParameter, + NodeType_TemplateParameter, + NodeType_TemplateParameterList, + NodeType_Condition, + + NodeType_Custom = 2000 +}; + +QString nodeTypeToString( int type ); + + +#if defined(CPPPARSER_QUICK_ALLOCATOR) + +#include <quick_allocator.h> + +#define DECLARE_ALLOC(tp) \ + void * operator new(std::size_t) \ + { \ + return quick_allocator< tp >::alloc(); \ + } \ + \ + void operator delete(void * p) \ + { \ + quick_allocator< tp >::dealloc(p); \ + } +#else + +#define DECLARE_ALLOC(tp) + +#endif + +struct Slice +{ + QString source; + int position; + int length; + + inline Slice() + : position(0), length(0) {} +}; + +class AST +{ +public: + typedef AUTO_PTR<AST> Node; + enum { Type=NodeType_Generic }; + + DECLARE_ALLOC( AST ) + +public: + AST(); + virtual ~AST(); + + int nodeType() const { return m_nodeType; } + void setNodeType( int nodeType ) { m_nodeType = nodeType; } + + AST* parent() { return m_parent; } + void setParent( AST* parent ); + + void setStartPosition( int line, int col ); + void getStartPosition( int* line, int* col ) const; + + void setEndPosition( int line, int col ); + void getEndPosition( int* line, int* col ) const; + +#ifndef CPPPARSER_NO_CHILDREN + QPtrList<AST> children() { return m_children; } + void appendChild( AST* child ); + void removeChild( AST* child ); +#endif + + virtual inline QString text() const + { return m_slice.source.mid(m_slice.position, m_slice.length); } + + QString comment() const + { return m_comment; } + + inline void setSlice( const Slice& slice ) + { m_slice = slice; } + + inline void setSlice( const QString &text, int position, int length ) + { + m_slice.source = text; + m_slice.position = position; + m_slice.length = length; + } + + inline void setText(const QString &text) + { setSlice(text, 0, text.length()); } + + void setComment( const QString &comment ) + { m_comment = comment; } + +private: + int m_nodeType; + AST* m_parent; + int m_startLine, m_startColumn; + int m_endLine, m_endColumn; + Slice m_slice; +#ifndef CPPPARSER_NO_CHILDREN + QPtrList<AST> m_children; +#endif + QString m_comment; + +private: + AST( const AST& source ); + void operator = ( const AST& source ); +}; + +class GroupAST: public AST +{ +public: + typedef AUTO_PTR<GroupAST> Node; + enum { Type = NodeType_Group }; + + DECLARE_ALLOC( GroupAST ) + +public: + GroupAST(); + + QPtrList<AST> nodeList() { return m_nodeList; } + void addNode( AST::Node& node ); + + virtual QString text() const; + +private: + QPtrList<AST> m_nodeList; + +private: + GroupAST( const GroupAST& source ); + void operator = ( const GroupAST& source ); +}; + + +class TemplateArgumentListAST: public AST +{ +public: + typedef AUTO_PTR<TemplateArgumentListAST> Node; + enum { Type = NodeType_TemplateArgumentList }; + + DECLARE_ALLOC( TemplateArgumentListAST ) + +public: + TemplateArgumentListAST(); + + void addArgument( AST::Node& arg ); + QPtrList<AST> argumentList() { return m_argumentList; } + + virtual QString text() const; + +private: + QPtrList<AST> m_argumentList; + +private: + TemplateArgumentListAST( const TemplateArgumentListAST& source ); + void operator = ( const TemplateArgumentListAST& source ); +}; + +class ClassOrNamespaceNameAST: public AST +{ +public: + typedef AUTO_PTR<ClassOrNamespaceNameAST> Node; + enum { Type = NodeType_ClassOrNamespaceName }; + + DECLARE_ALLOC( ClassOrNamespaceNameAST ) + +public: + ClassOrNamespaceNameAST(); + + AST* name() { return m_name.get(); } + void setName( AST::Node& name ); + + TemplateArgumentListAST* templateArgumentList() { return m_templateArgumentList.get(); } + void setTemplateArgumentList( TemplateArgumentListAST::Node& templateArgumentList ); + + virtual QString text() const; + +private: + AST::Node m_name; + TemplateArgumentListAST::Node m_templateArgumentList; + +private: + ClassOrNamespaceNameAST( const ClassOrNamespaceNameAST& source ); + void operator = ( const ClassOrNamespaceNameAST& source ); +}; + +class NameAST: public AST +{ +public: + typedef AUTO_PTR<NameAST> Node; + enum { Type = NodeType_Name }; + + DECLARE_ALLOC( NameAST ) + +public: + NameAST(); + + bool isGlobal() const { return m_global; } + void setGlobal( bool b ); + + void addClassOrNamespaceName( ClassOrNamespaceNameAST::Node& classOrNamespaceName ); + QPtrList<ClassOrNamespaceNameAST> classOrNamespaceNameList() { return m_classOrNamespaceNameList; } + + ClassOrNamespaceNameAST* unqualifiedName() { return m_unqualifiedName.get(); } + void setUnqualifiedName( ClassOrNamespaceNameAST::Node& unqualifiedName ); + + virtual QString text() const; + +private: + bool m_global; + ClassOrNamespaceNameAST::Node m_unqualifiedName; + QPtrList<ClassOrNamespaceNameAST> m_classOrNamespaceNameList; + +private: + NameAST( const NameAST& source ); + void operator = ( const NameAST& source ); +}; + +class TypeParameterAST: public AST +{ +public: + typedef AUTO_PTR<TypeParameterAST> Node; + enum { Type = NodeType_TypeParameter }; + + DECLARE_ALLOC( TypeParameterAST ) + +public: + TypeParameterAST(); + + AST* kind() { return m_kind.get(); } + void setKind( AST::Node& kind ); + + class TemplateParameterListAST* templateParameterList() { return m_templateParameterList.get(); } + void setTemplateParameterList( AUTO_PTR<class TemplateParameterListAST>& templateParameterList ); + + NameAST* name() { return m_name.get(); } + void setName( NameAST::Node& name ); + + AST* typeId() { return m_typeId.get(); } + void setTypeId( AST::Node& typeId ); + +private: + AST::Node m_kind; + AUTO_PTR<class TemplateParameterListAST> m_templateParameterList; + NameAST::Node m_name; + AST::Node m_typeId; + +private: + TypeParameterAST( const TypeParameterAST& source ); + void operator = ( const TypeParameterAST& source ); +}; + +class DeclarationAST: public AST +{ +public: + typedef AUTO_PTR<DeclarationAST> Node; + enum { Type = NodeType_Declaration }; + + DECLARE_ALLOC( DeclarationAST ) + +public: + DeclarationAST(); + +private: + DeclarationAST( const DeclarationAST& source ); + void operator = ( const DeclarationAST& source ); +}; + +class AccessDeclarationAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<AccessDeclarationAST> Node; + enum { Type = NodeType_AccessDeclaration }; + + DECLARE_ALLOC( AccessDeclarationAST ) + +public: + AccessDeclarationAST(); + + QPtrList<AST> accessList() { return m_accessList; } + void addAccess( AST::Node& access ); + + virtual QString text() const; + +private: + QPtrList<AST> m_accessList; + +private: + AccessDeclarationAST( const AccessDeclarationAST& source ); + void operator = ( const AccessDeclarationAST& source ); +}; + +class TypeSpecifierAST: public AST +{ +public: + typedef AUTO_PTR<TypeSpecifierAST> Node; + enum { Type = NodeType_TypeSpecifier }; + + DECLARE_ALLOC( TypeSpecifierAST ) + +public: + TypeSpecifierAST(); + + virtual NameAST* name() { return m_name.get(); } + virtual void setName( NameAST::Node& name ); + + GroupAST* cvQualify() { return m_cvQualify.get(); } + void setCvQualify( GroupAST::Node& cvQualify ); + + GroupAST* cv2Qualify() { return m_cv2Qualify.get(); } + void setCv2Qualify( GroupAST::Node& cv2Qualify ); + + virtual QString text() const; + +private: + NameAST::Node m_name; + GroupAST::Node m_cvQualify; + GroupAST::Node m_cv2Qualify; + +private: + TypeSpecifierAST( const TypeSpecifierAST& source ); + void operator = ( const TypeSpecifierAST& source ); +}; + +class BaseSpecifierAST: public AST +{ +public: + typedef AUTO_PTR<BaseSpecifierAST> Node; + enum { Type = NodeType_BaseSpecifier }; + + DECLARE_ALLOC( BaseSpecifierAST ) + +public: + BaseSpecifierAST(); + + AST* isVirtual() { return m_isVirtual.get(); } + void setIsVirtual( AST::Node& isVirtual ); + + AST* access() { return m_access.get(); } + void setAccess( AST::Node& access ); + + NameAST* name() { return m_name.get(); } + void setName( NameAST::Node& name ); + +private: + AST::Node m_isVirtual; + AST::Node m_access; + NameAST::Node m_name; + +private: + BaseSpecifierAST( const BaseSpecifierAST& source ); + void operator = ( const BaseSpecifierAST& source ); +}; + +class BaseClauseAST: public AST +{ +public: + typedef AUTO_PTR<BaseClauseAST> Node; + enum { Type = NodeType_BaseClause }; + + DECLARE_ALLOC( BaseClauseAST ) + +public: + BaseClauseAST(); + + void addBaseSpecifier( BaseSpecifierAST::Node& baseSpecifier ); + QPtrList<BaseSpecifierAST> baseSpecifierList() { return m_baseSpecifierList; } + +private: + QPtrList<BaseSpecifierAST> m_baseSpecifierList; + +private: + BaseClauseAST( const BaseClauseAST& source ); + void operator = ( const BaseClauseAST& source ); +}; + +class ClassSpecifierAST: public TypeSpecifierAST +{ +public: + typedef AUTO_PTR<ClassSpecifierAST> Node; + enum { Type = NodeType_ClassSpecifier }; + + DECLARE_ALLOC( ClassSpecifierAST ) + +public: + ClassSpecifierAST(); + + GroupAST* winDeclSpec() { return m_winDeclSpec.get(); } + void setWinDeclSpec( GroupAST::Node& winDeclSpec ); + + AST* classKey() { return m_classKey.get(); } + void setClassKey( AST::Node& classKey ); + + BaseClauseAST* baseClause() { return m_baseClause.get(); } + void setBaseClause( BaseClauseAST::Node& baseClause ); + + QPtrList<DeclarationAST> declarationList() { return m_declarationList; } + void addDeclaration( DeclarationAST::Node& declaration ); + +private: + GroupAST::Node m_winDeclSpec; + AST::Node m_classKey; + BaseClauseAST::Node m_baseClause; + QPtrList<DeclarationAST> m_declarationList; + +private: + ClassSpecifierAST( const ClassSpecifierAST& source ); + void operator = ( const ClassSpecifierAST& source ); +}; + +class EnumeratorAST: public AST +{ +public: + typedef AUTO_PTR<EnumeratorAST> Node; + enum { Type = NodeType_Enumerator }; + + DECLARE_ALLOC( EnumeratorAST ) + +public: + EnumeratorAST(); + + AST* id() { return m_id.get(); } + void setId( AST::Node& id ); + + AST* expr() { return m_expr.get(); } + void setExpr( AST::Node& expr ); + +private: + AST::Node m_id; + AST::Node m_expr; + +private: + EnumeratorAST( const EnumeratorAST& source ); + void operator = ( const EnumeratorAST& source ); +}; + +class EnumSpecifierAST: public TypeSpecifierAST +{ +public: + typedef AUTO_PTR<EnumSpecifierAST> Node; + enum { Type = NodeType_EnumSpecifier }; + + DECLARE_ALLOC( EnumSpecifierAST ) + +public: + EnumSpecifierAST(); + + void addEnumerator( EnumeratorAST::Node& enumerator ); + QPtrList<EnumeratorAST> enumeratorList() { return m_enumeratorList; } + +private: + QPtrList<EnumeratorAST> m_enumeratorList; + +private: + EnumSpecifierAST( const EnumSpecifierAST& source ); + void operator = ( const EnumSpecifierAST& source ); +}; + +class ElaboratedTypeSpecifierAST: public TypeSpecifierAST +{ +public: + typedef AUTO_PTR<ElaboratedTypeSpecifierAST> Node; + enum { Type = NodeType_ElaboratedTypeSpecifier }; + + DECLARE_ALLOC( ElaboratedTypeSpecifierAST ) + +public: + ElaboratedTypeSpecifierAST(); + + AST* kind() { return m_kind.get(); } + void setKind( AST::Node& kind ); + + virtual QString text() const; + +private: + AST::Node m_kind; + +private: + ElaboratedTypeSpecifierAST( const ElaboratedTypeSpecifierAST& source ); + void operator = ( const ElaboratedTypeSpecifierAST& source ); +}; + + +class LinkageBodyAST: public AST +{ +public: + typedef AUTO_PTR<LinkageBodyAST> Node; + enum { Type = NodeType_LinkageBody }; + + DECLARE_ALLOC( LinkageBodyAST ) + +public: + LinkageBodyAST(); + + void addDeclaration( DeclarationAST::Node& ast ); + QPtrList<DeclarationAST> declarationList() { return m_declarationList; } + +private: + QPtrList<DeclarationAST> m_declarationList; + +private: + LinkageBodyAST( const LinkageBodyAST& source ); + void operator = ( const LinkageBodyAST& source ); +}; + +class LinkageSpecificationAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<LinkageSpecificationAST> Node; + enum { Type = NodeType_LinkageSpecification }; + + DECLARE_ALLOC( LinkageSpecificationAST ) + +public: + LinkageSpecificationAST(); + + AST* externType() { return m_externType.get(); } + void setExternType( AST::Node& externType ); + + LinkageBodyAST* linkageBody() { return m_linkageBody.get(); } + void setLinkageBody( LinkageBodyAST::Node& linkageBody ); + + DeclarationAST* declaration() { return m_declaration.get(); } + void setDeclaration( DeclarationAST::Node& decl ); + +private: + AST::Node m_externType; + LinkageBodyAST::Node m_linkageBody; + DeclarationAST::Node m_declaration; + +private: + LinkageSpecificationAST( const LinkageSpecificationAST& source ); + void operator = ( const LinkageSpecificationAST& source ); +}; + +class NamespaceAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<NamespaceAST> Node; + enum { Type = NodeType_Namespace }; + + DECLARE_ALLOC( NamespaceAST ) + +public: + NamespaceAST(); + + AST* namespaceName() { return m_namespaceName.get(); } + void setNamespaceName( AST::Node& namespaceName ); + + LinkageBodyAST* linkageBody() { return m_linkageBody.get(); } + void setLinkageBody( LinkageBodyAST::Node& linkageBody ); + +private: + AST::Node m_namespaceName; + LinkageBodyAST::Node m_linkageBody; + +private: + NamespaceAST( const NamespaceAST& source ); + void operator = ( const NamespaceAST& source ); +}; + +class NamespaceAliasAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<NamespaceAliasAST> Node; + enum { Type = NodeType_NamespaceAlias }; + + DECLARE_ALLOC( NamespaceAliasAST ) + +public: + NamespaceAliasAST(); + + AST* namespaceName() { return m_namespaceName.get(); } + void setNamespaceName( AST::Node& name ); + + NameAST* aliasName() { return m_aliasName.get(); } + void setAliasName( NameAST::Node& name ); + +private: + AST::Node m_namespaceName; + NameAST::Node m_aliasName; + +private: + NamespaceAliasAST( const NamespaceAliasAST& source ); + void operator = ( const NamespaceAliasAST& source ); +}; + +class UsingAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<UsingAST> Node; + enum { Type = NodeType_Using }; + + DECLARE_ALLOC( UsingAST ) + +public: + UsingAST(); + + AST* typeName() { return m_typeName.get(); } + void setTypeName( AST::Node& typeName ); + + NameAST* name() { return m_name.get(); } + void setName( NameAST::Node& name ); + +private: + AST::Node m_typeName; + NameAST::Node m_name; + +private: + UsingAST( const UsingAST& source ); + void operator = ( const UsingAST& source ); +}; + +class UsingDirectiveAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<UsingDirectiveAST> Node; + enum { Type = NodeType_UsingDirective }; + + DECLARE_ALLOC( UsingDirectiveAST ) + +public: + UsingDirectiveAST(); + + NameAST* name() { return m_name.get(); } + void setName( NameAST::Node& name ); + +private: + NameAST::Node m_name; + +private: + UsingDirectiveAST( const UsingDirectiveAST& source ); + void operator = ( const UsingDirectiveAST& source ); +}; + +class DeclaratorAST: public AST +{ +public: + typedef AUTO_PTR<DeclaratorAST> Node; + enum { Type = NodeType_Declarator }; + + DECLARE_ALLOC( DeclaratorAST ) + +public: + DeclaratorAST(); + + QPtrList<AST> ptrOpList() { return m_ptrOpList; } + void addPtrOp( AST::Node& ptrOp ); + + DeclaratorAST* subDeclarator() { return m_subDeclarator.get(); } + void setSubDeclarator( Node& subDeclarator ); + + NameAST* declaratorId() { return m_declaratorId.get(); } + void setDeclaratorId( NameAST::Node& declaratorId ); + + AST* bitfieldInitialization() { return m_bitfieldInitialization.get(); } + void setBitfieldInitialization( AST::Node& bitfieldInitialization ); + + QPtrList<AST> arrayDimensionList() { return m_arrayDimensionList; } + void addArrayDimension( AST::Node& arrayDimension ); + + class ParameterDeclarationClauseAST* parameterDeclarationClause() { return m_parameterDeclarationClause.get(); } + void setParameterDeclarationClause( AUTO_PTR<class ParameterDeclarationClauseAST>& parameterDeclarationClause ); + + // ### replace 'constant' with cvQualify + AST* constant() { return m_constant.get(); } + void setConstant( AST::Node& constant ); + + GroupAST* exceptionSpecification() { return m_exceptionSpecification.get(); } + void setExceptionSpecification( GroupAST::Node& exceptionSpecification ); + +private: + QPtrList<AST> m_ptrOpList; + Node m_subDeclarator; + NameAST::Node m_declaratorId; + AST::Node m_bitfieldInitialization; + QPtrList<AST> m_arrayDimensionList; + AUTO_PTR<class ParameterDeclarationClauseAST> m_parameterDeclarationClause; + AST::Node m_constant; + GroupAST::Node m_exceptionSpecification; + +private: + DeclaratorAST( const DeclaratorAST& source ); + void operator = ( const DeclaratorAST& source ); +}; + +class ParameterDeclarationAST: public AST +{ +public: + typedef AUTO_PTR<ParameterDeclarationAST> Node; + enum { Type = NodeType_ParameterDeclaration }; + + DECLARE_ALLOC( ParameterDeclarationAST ) + +public: + ParameterDeclarationAST(); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + DeclaratorAST* declarator() { return m_declarator.get(); } + void setDeclarator( DeclaratorAST::Node& declarator ); + + AST* expression() { return m_expression.get(); } + void setExpression( AST::Node& expression ); + + virtual QString text() const; + +private: + TypeSpecifierAST::Node m_typeSpec; + DeclaratorAST::Node m_declarator; + AST::Node m_expression; + +private: + ParameterDeclarationAST( const ParameterDeclarationAST& source ); + void operator = ( const ParameterDeclarationAST& source ); +}; + +class ParameterDeclarationListAST: public AST +{ +public: + typedef AUTO_PTR<ParameterDeclarationListAST> Node; + enum { Type = NodeType_ParameterDeclarationList }; + + DECLARE_ALLOC( ParameterDeclarationListAST ) + +public: + ParameterDeclarationListAST(); + + QPtrList<ParameterDeclarationAST> parameterList() { return m_parameterList; } + void addParameter( ParameterDeclarationAST::Node& parameter ); + + virtual QString text() const; + +private: + QPtrList<ParameterDeclarationAST> m_parameterList; + +private: + ParameterDeclarationListAST( const ParameterDeclarationListAST& source ); + void operator = ( const ParameterDeclarationListAST& source ); +}; + +class ParameterDeclarationClauseAST: public AST +{ +public: + typedef AUTO_PTR<ParameterDeclarationClauseAST> Node; + enum { Type = NodeType_ParameterDeclarationClause }; + + DECLARE_ALLOC( ParameterDeclarationClauseAST ) + +public: + ParameterDeclarationClauseAST(); + + ParameterDeclarationListAST* parameterDeclarationList() { return m_parameterDeclarationList.get(); } + void setParameterDeclarationList( ParameterDeclarationListAST::Node& parameterDeclarationList ); + + AST* ellipsis() { return m_ellipsis.get(); } + void setEllipsis( AST::Node& ellipsis ); + + virtual QString text() const; + +private: + ParameterDeclarationListAST::Node m_parameterDeclarationList; + AST::Node m_ellipsis; + +private: + ParameterDeclarationClauseAST( const ParameterDeclarationClauseAST& source ); + void operator = ( const ParameterDeclarationClauseAST& source ); +}; + + +class InitDeclaratorAST: public AST +{ +public: + typedef AUTO_PTR<InitDeclaratorAST> Node; + enum { Type = NodeType_InitDeclarator }; + + DECLARE_ALLOC( InitDeclaratorAST ) + +public: + InitDeclaratorAST(); + + DeclaratorAST* declarator() { return m_declarator.get(); } + void setDeclarator( DeclaratorAST::Node& declarator ); + + AST* initializer() { return m_initializer.get(); } + void setInitializer( AST::Node& initializer ); + +private: + DeclaratorAST::Node m_declarator; + AST::Node m_initializer; + +private: + InitDeclaratorAST( const InitDeclaratorAST& source ); + void operator = ( const InitDeclaratorAST& source ); +}; + +class InitDeclaratorListAST: public AST +{ +public: + typedef AUTO_PTR<InitDeclaratorListAST> Node; + enum { Type = NodeType_InitDeclaratorList }; + + DECLARE_ALLOC( InitDeclaratorListAST ) + +public: + InitDeclaratorListAST(); + + QPtrList<InitDeclaratorAST> initDeclaratorList() { return m_initDeclaratorList; } + void addInitDeclarator( InitDeclaratorAST::Node& decl ); + +private: + QPtrList<InitDeclaratorAST> m_initDeclaratorList; + +private: + InitDeclaratorListAST( const InitDeclaratorListAST& source ); + void operator = ( const InitDeclaratorListAST& source ); +}; + +class TypedefAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<TypedefAST> Node; + enum { Type = NodeType_Typedef }; + + DECLARE_ALLOC( TypedefAST ) + +public: + TypedefAST(); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + InitDeclaratorListAST* initDeclaratorList() { return m_initDeclaratorList.get(); } + void setInitDeclaratorList( InitDeclaratorListAST::Node& initDeclaratorList ); + +private: + TypeSpecifierAST::Node m_typeSpec; + InitDeclaratorListAST::Node m_initDeclaratorList; + +private: + TypedefAST( const TypedefAST& source ); + void operator = ( const TypedefAST& source ); +}; + +class TemplateParameterAST: public AST +{ +public: + typedef AUTO_PTR<TemplateParameterAST> Node; + enum { Type = NodeType_TemplateParameter }; + + DECLARE_ALLOC( TemplateParameterAST ) + +public: + TemplateParameterAST(); + + TypeParameterAST* typeParameter() { return m_typeParameter.get(); } + void setTypeParameter( TypeParameterAST::Node& typeParameter ); + + ParameterDeclarationAST* typeValueParameter() { return m_typeValueParameter.get(); } + void setTypeValueParameter( ParameterDeclarationAST::Node& typeValueParameter ); + +private: + TypeParameterAST::Node m_typeParameter; + ParameterDeclarationAST::Node m_typeValueParameter; + +private: + TemplateParameterAST( const TemplateParameterAST& source ); + void operator = ( const TemplateParameterAST& source ); +}; + +class TemplateParameterListAST: public AST +{ +public: + typedef AUTO_PTR<TemplateParameterListAST> Node; + enum { Type = NodeType_TemplateParameterList }; + + DECLARE_ALLOC( TemplateParameterListAST ) + +public: + TemplateParameterListAST(); + + QPtrList<TemplateParameterAST> templateParameterList() { return m_templateParameterList; } + void addTemplateParameter( TemplateParameterAST::Node& templateParameter ); + +private: + QPtrList<TemplateParameterAST> m_templateParameterList; + +private: + TemplateParameterListAST( const TemplateParameterListAST& source ); + void operator = ( const TemplateParameterListAST& source ); +}; + +class TemplateDeclarationAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<TemplateDeclarationAST> Node; + enum { Type = NodeType_TemplateDeclaration }; + + DECLARE_ALLOC( TemplateDeclarationAST ) + +public: + TemplateDeclarationAST(); + + AST* exported() { return m_exported.get(); } + void setExported( AST::Node& exported ); + + TemplateParameterListAST* templateParameterList() { return m_templateParameterList.get(); } + void setTemplateParameterList( TemplateParameterListAST::Node& templateParameterList ); + + DeclarationAST* declaration() { return m_declaration.get(); } + void setDeclaration( DeclarationAST::Node& declaration ); + +private: + AST::Node m_exported; + TemplateParameterListAST::Node m_templateParameterList; + DeclarationAST::Node m_declaration; + +private: + TemplateDeclarationAST( const TemplateDeclarationAST& source ); + void operator = ( const TemplateDeclarationAST& source ); +}; + +class SimpleDeclarationAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<SimpleDeclarationAST> Node; + enum { Type = NodeType_SimpleDeclaration }; + + DECLARE_ALLOC( SimpleDeclarationAST ) + +public: + SimpleDeclarationAST(); + + GroupAST* functionSpecifier() { return m_functionSpecifier.get(); } + void setFunctionSpecifier( GroupAST::Node& functionSpecifier ); + + GroupAST* storageSpecifier() { return m_storageSpecifier.get(); } + void setStorageSpecifier( GroupAST::Node& storageSpecifier ); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + InitDeclaratorListAST* initDeclaratorList() { return m_initDeclaratorList.get(); } + void setInitDeclaratorList( InitDeclaratorListAST::Node& initDeclaratorList ); + + GroupAST* winDeclSpec() { return m_winDeclSpec.get(); } + void setWinDeclSpec( GroupAST::Node& winDeclSpec ); + +private: + GroupAST::Node m_functionSpecifier; + GroupAST::Node m_storageSpecifier; + TypeSpecifierAST::Node m_typeSpec; + InitDeclaratorListAST::Node m_initDeclaratorList; + GroupAST::Node m_winDeclSpec; + +private: + SimpleDeclarationAST( const SimpleDeclarationAST& source ); + void operator = ( const SimpleDeclarationAST& source ); +}; + +class StatementAST: public AST +{ +public: + typedef AUTO_PTR<StatementAST> Node; + enum { Type = NodeType_Statement }; + + DECLARE_ALLOC( StatementAST ) + +public: + StatementAST(); + +private: + StatementAST( const StatementAST& source ); + void operator = ( const StatementAST& source ); +}; + +class ExpressionStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<ExpressionStatementAST> Node; + enum { Type = NodeType_ExpressionStatement }; + + DECLARE_ALLOC( ExpressionStatementAST ) + +public: + ExpressionStatementAST(); + + AST* expression() { return m_expression.get(); } + void setExpression( AST::Node& expression ); + +private: + AST::Node m_expression; + +private: + ExpressionStatementAST( const ExpressionStatementAST& source ); + void operator = ( const ExpressionStatementAST& source ); +}; + +class ConditionAST: public AST +{ +public: + typedef AUTO_PTR<ConditionAST> Node; + enum { Type = NodeType_Condition }; + + DECLARE_ALLOC( ConditionAST ) + +public: + ConditionAST(); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + DeclaratorAST* declarator() { return m_declarator.get(); } + void setDeclarator( DeclaratorAST::Node& declarator ); + + AST* expression() { return m_expression.get(); } + void setExpression( AST::Node& expression ); + +private: + TypeSpecifierAST::Node m_typeSpec; + DeclaratorAST::Node m_declarator; + AST::Node m_expression; + +private: + ConditionAST( const ConditionAST& source ); + void operator = ( const ConditionAST& source ); +}; + +class IfStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<IfStatementAST> Node; + enum { Type = NodeType_IfStatement }; + + DECLARE_ALLOC( IfStatementAST ) + +public: + IfStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + + StatementAST* elseStatement() { return m_elseStatement.get(); } + void setElseStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + StatementAST::Node m_elseStatement; + +private: + IfStatementAST( const IfStatementAST& source ); + void operator = ( const IfStatementAST& source ); +}; + +class WhileStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<WhileStatementAST> Node; + enum { Type = NodeType_WhileStatement }; + + DECLARE_ALLOC( WhileStatementAST ) + +public: + WhileStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + +private: + WhileStatementAST( const WhileStatementAST& source ); + void operator = ( const WhileStatementAST& source ); +}; + +class DoStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<DoStatementAST> Node; + enum { Type = NodeType_DoStatement }; + + DECLARE_ALLOC( DoStatementAST ) + +public: + DoStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + +private: + DoStatementAST( const DoStatementAST& source ); + void operator = ( const DoStatementAST& source ); +}; + +class ForStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<ForStatementAST> Node; + enum { Type = NodeType_ForStatement }; + + DECLARE_ALLOC( ForStatementAST ) + +public: + ForStatementAST(); + + StatementAST* initStatement() { return m_initStatement.get(); } + void setInitStatement( StatementAST::Node& statement ); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + AST* expression() const { return m_expression.get(); } + void setExpression( AST::Node& expression ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_initStatement; + StatementAST::Node m_statement; + AST::Node m_expression; + +private: + ForStatementAST( const ForStatementAST& source ); + void operator = ( const ForStatementAST& source ); +}; + +class SwitchStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<SwitchStatementAST> Node; + enum { Type = NodeType_SwitchStatement }; + + DECLARE_ALLOC( SwitchStatementAST ) + +public: + SwitchStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + +private: + SwitchStatementAST( const SwitchStatementAST& source ); + void operator = ( const SwitchStatementAST& source ); +}; + +class StatementListAST: public StatementAST +{ +public: + typedef AUTO_PTR<StatementListAST> Node; + enum { Type = NodeType_StatementList }; + + DECLARE_ALLOC( StatementListAST ) + +public: + StatementListAST(); + + QPtrList<StatementAST> statementList() { return m_statementList; } + void addStatement( StatementAST::Node& statement ); + +private: + QPtrList<StatementAST> m_statementList; + +private: + StatementListAST( const StatementListAST& source ); + void operator = ( const StatementListAST& source ); +}; + +class DeclarationStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<DeclarationStatementAST> Node; + enum { Type = NodeType_DeclarationStatement }; + + DECLARE_ALLOC( DeclarationStatementAST ) + +public: + DeclarationStatementAST(); + + DeclarationAST* declaration() { return m_declaration.get(); } + void setDeclaration( DeclarationAST::Node& declaration ); + +private: + DeclarationAST::Node m_declaration; + +private: + DeclarationStatementAST( const DeclarationStatementAST& source ); + void operator = ( const DeclarationStatementAST& source ); +}; + +class FunctionDefinitionAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<FunctionDefinitionAST> Node; + enum { Type = NodeType_FunctionDefinition }; + + DECLARE_ALLOC( FunctionDefinitionAST ) + +public: + FunctionDefinitionAST(); + + GroupAST* functionSpecifier() { return m_functionSpecifier.get(); } + void setFunctionSpecifier( GroupAST::Node& functionSpecifier ); + + GroupAST* storageSpecifier() { return m_storageSpecifier.get(); } + void setStorageSpecifier( GroupAST::Node& storageSpecifier ); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + InitDeclaratorAST* initDeclarator() { return m_initDeclarator.get(); } + void setInitDeclarator( InitDeclaratorAST::Node& initDeclarator ); + + StatementListAST* functionBody() { return m_functionBody.get(); } + void setFunctionBody( StatementListAST::Node& functionBody ); + + GroupAST* winDeclSpec() { return m_winDeclSpec.get(); } + void setWinDeclSpec( GroupAST::Node& winDeclSpec ); + +private: + GroupAST::Node m_functionSpecifier; + GroupAST::Node m_storageSpecifier; + TypeSpecifierAST::Node m_typeSpec; + InitDeclaratorAST::Node m_initDeclarator; + StatementListAST::Node m_functionBody; + GroupAST::Node m_winDeclSpec; + +private: + FunctionDefinitionAST( const FunctionDefinitionAST& source ); + void operator = ( const FunctionDefinitionAST& source ); +}; + + +class TranslationUnitAST: public AST +{ +public: + typedef AUTO_PTR<TranslationUnitAST> Node; + enum { Type = NodeType_TranslationUnit }; + + DECLARE_ALLOC( TranslationUnitAST ) + +public: + TranslationUnitAST(); + + void addDeclaration( DeclarationAST::Node& ast ); + QPtrList<DeclarationAST> declarationList() { return m_declarationList; } + +private: + QPtrList<DeclarationAST> m_declarationList; + +private: + TranslationUnitAST( const TranslationUnitAST& source ); + void operator = ( const TranslationUnitAST& source ); +}; + +#endif diff --git a/umbrello/umbrello/codeimport/kdevcppparser/ast_utils.cpp b/umbrello/umbrello/codeimport/kdevcppparser/ast_utils.cpp new file mode 100644 index 00000000..e30f0c1e --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/ast_utils.cpp @@ -0,0 +1,176 @@ +/*************************************************************************** + * Copyright (C) 2002 by Roberto Raggi * + * roberto@kdevelop.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "ast_utils.h" +#include "ast.h" + +#include <qstringlist.h> +#include <qregexp.h> + +#include <klocale.h> +#include <kdebug.h> +#include <kapplication.h> + +AST* findNodeAt( AST* node, int line, int column ) +{ + // kdDebug(9007) << "findNodeAt(" << node << ")" << endl; + + if( !node ) + return 0; + + int startLine, startColumn; + int endLine, endColumn; + + node->getStartPosition( &startLine, &startColumn ); + node->getEndPosition( &endLine, &endColumn ); + + if( (line > startLine || (line == startLine && column >= startColumn)) && + (line < endLine || (line == endLine && column < endColumn)) ){ + + QPtrList<AST> children = node->children(); + QPtrListIterator<AST> it( children ); + while( it.current() ){ + AST* a = it.current(); + ++it; + + AST* r = findNodeAt( a, line, column ); + if( r ) + return r; + } + + return node; + } + + return 0; +} + +void scopeOfNode( AST* ast, QStringList& scope ) +{ + if( !ast ) + return; + + if( ast->parent() ) + scopeOfNode( ast->parent(), scope ); + + QString s; + switch( ast->nodeType() ) + { + case NodeType_ClassSpecifier: + if( ((ClassSpecifierAST*)ast)->name() ){ + s = ((ClassSpecifierAST*)ast)->name()->text(); + s = s.isEmpty() ? QString::fromLatin1("<unnamed>") : s; + scope.push_back( s ); + } + break; + + case NodeType_Namespace: + { + AST* namespaceName = ((NamespaceAST*)ast)->namespaceName(); + s = namespaceName ? namespaceName->text() : QString::fromLatin1("<unnamed>"); + scope.push_back( s ); + } + break; + + case NodeType_FunctionDefinition: + { + FunctionDefinitionAST* funDef = static_cast<FunctionDefinitionAST*>( ast ); + DeclaratorAST* d = funDef->initDeclarator()->declarator(); + + // hotfix for bug #68726 + if ( !d->declaratorId() ) + break; + + QPtrList<ClassOrNamespaceNameAST> l = d->declaratorId()->classOrNamespaceNameList(); + QPtrListIterator<ClassOrNamespaceNameAST> nameIt( l ); + while( nameIt.current() ){ + AST* name = nameIt.current()->name(); + scope.push_back( name->text() ); + + ++nameIt; + } + } + break; + + default: + break; + } +} + + +QString typeSpecToString( TypeSpecifierAST* typeSpec ) /// @todo remove +{ + if( !typeSpec ) + return QString::null; + + return typeSpec->text().replace( QRegExp(" :: "), "::" ); +} + +QString declaratorToString( DeclaratorAST* declarator, const QString& scope, bool skipPtrOp ) +{ + if( !declarator ) + return QString::null; + + QString text; + + if( !skipPtrOp ){ + QPtrList<AST> ptrOpList = declarator->ptrOpList(); + for( QPtrListIterator<AST> it(ptrOpList); it.current(); ++it ){ + text += it.current()->text(); + } + text += ' '; + } + + text += scope; + + if( declarator->subDeclarator() ) + text += QString::fromLatin1("(") + declaratorToString(declarator->subDeclarator()) + QString::fromLatin1(")"); + + if( declarator->declaratorId() ) + text += declarator->declaratorId()->text(); + + QPtrList<AST> arrays = declarator->arrayDimensionList(); + QPtrListIterator<AST> it( arrays ); + while( it.current() ){ + text += "[]"; + ++it; + } + + if( declarator->parameterDeclarationClause() ){ + text += "( "; + + ParameterDeclarationListAST* l = declarator->parameterDeclarationClause()->parameterDeclarationList(); + if( l != 0 ){ + QPtrList<ParameterDeclarationAST> params = l->parameterList(); + QPtrListIterator<ParameterDeclarationAST> it( params ); + + while( it.current() ){ + QString type = typeSpecToString( it.current()->typeSpec() ); + text += type; + if( !type.isEmpty() ) + text += ' '; + text += declaratorToString( it.current()->declarator() ); + + ++it; + + if( it.current() ) + text += ", "; + } + } + + text += " )"; + + if( declarator->constant() != 0 ) + text += " const"; + } + + return text.replace( QRegExp(" :: "), "::" ).simplifyWhiteSpace(); +} + diff --git a/umbrello/umbrello/codeimport/kdevcppparser/ast_utils.h b/umbrello/umbrello/codeimport/kdevcppparser/ast_utils.h new file mode 100644 index 00000000..187647b7 --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/ast_utils.h @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2002 by Roberto Raggi * + * roberto@kdevelop.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef __ast_utils_h +#define __ast_utils_h + +#include <qstring.h> + +class AST; +class DeclaratorAST; +class TypeSpecifierAST; +class QStringList; + +namespace KTextEditor{ class EditInterface; } + +AST* findNodeAt( AST* unit, int line, int column ); +void scopeOfNode( AST* ast, QStringList& ); +QString typeSpecToString( TypeSpecifierAST* typeSpec ); +QString declaratorToString( DeclaratorAST* declarator, const QString& scope = QString::null, bool skipPtrOp=false ); + +#endif // __ast_utils_h diff --git a/umbrello/umbrello/codeimport/kdevcppparser/cpptree2uml.cpp b/umbrello/umbrello/codeimport/kdevcppparser/cpptree2uml.cpp new file mode 100644 index 00000000..e7d0b848 --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/cpptree2uml.cpp @@ -0,0 +1,640 @@ +/*************************************************************************** + * Based on kdevelop-3.0 languages/cpp/store_walker.cpp by Roberto Raggi * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2004-2007 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +// own header +#include "cpptree2uml.h" +// qt/kde includes +#include <qfileinfo.h> +#include <qdir.h> +#include <qregexp.h> +#include <kdebug.h> +// app includes +#include "ast_utils.h" +#include "urlutil.h" +#include "../import_utils.h" +// FIXME: The sole reason for the next 2 includes is parseTypedef(). +// Make capsule methods in ClassImport, and remove these includes. +#include "../../classifier.h" +// FIXME The next include is motivated by template params +#include "../../template.h" + +CppTree2Uml::CppTree2Uml( const QString& fileName) + : m_anon( 0 ), m_nsCnt( 0 ), m_clsCnt( 0 ) +{ + m_fileName = URLUtil::canonicalPath(fileName); +} + +CppTree2Uml::~CppTree2Uml() +{ +} + +void CppTree2Uml::parseTranslationUnit( TranslationUnitAST* ast ) +{ + m_currentScope.clear(); + m_currentNamespace[0] = NULL; // index 0 is reserved (always NULL) + m_currentClass[0] = NULL; // index 0 is reserved (always NULL) + m_nsCnt = 0; + m_clsCnt = 0; + + m_currentAccess = Uml::Visibility::Public; + m_inSlots = false; + m_inSignals = false; + m_inStorageSpec = false; + m_inTypedef = false; + m_currentDeclarator = 0; + m_anon = 0; + + TreeParser::parseTranslationUnit( ast ); +} + +void CppTree2Uml::parseNamespace( NamespaceAST* ast ) +{ + if (m_clsCnt > 0) { + kDebug() << "CppTree2Uml::parseNamespace: error - cannot nest namespace inside class" + << endl; + return; + } + + QString nsName; + if( !ast->namespaceName() || ast->namespaceName()->text().isEmpty() ){ + QFileInfo fileInfo( m_fileName ); + QString shortFileName = fileInfo.baseName(); + + nsName.sprintf( "(%s_%d)", shortFileName.local8Bit().data(), m_anon++ ); + } else { + nsName = ast->namespaceName()->text(); + } + +#ifdef DEBUG_CPPTREE2UML + kDebug() << "CppTree2Uml::parseNamespace: " << nsName << endl; +#endif + UMLObject * o = Import_Utils::createUMLObject( Uml::ot_Package, nsName, + m_currentNamespace[m_nsCnt], + ast->comment()); + UMLPackage *ns = (UMLPackage *)o; + m_currentScope.push_back( nsName ); + if (++m_nsCnt > STACKSIZE) { + kdError() << "CppTree2Uml::parseNamespace: excessive namespace nesting" << endl; + m_nsCnt = STACKSIZE; + } + m_currentNamespace[m_nsCnt] = ns; + + TreeParser::parseNamespace( ast ); + + --m_nsCnt; + m_currentScope.pop_back(); +} + +void CppTree2Uml::parseTypedef( TypedefAST* ast ) +{ +#if 0 + DeclaratorAST* oldDeclarator = m_currentDeclarator; + + if( ast && ast->initDeclaratorList() && ast->initDeclaratorList()->initDeclaratorList().count() > 0 ) { + QPtrList<InitDeclaratorAST> lst( ast->initDeclaratorList()->initDeclaratorList() ); + m_currentDeclarator = lst.at( 0 )->declarator(); + } + + m_inTypedef = true; + + TreeParser::parseTypedef( ast ); + + m_inTypedef = false; + m_currentDeclarator = oldDeclarator; +#else + TypeSpecifierAST* typeSpec = ast->typeSpec(); + InitDeclaratorListAST* declarators = ast->initDeclaratorList(); + + if( typeSpec && declarators ){ + QString typeId; + + if( typeSpec->name() ) + typeId = typeSpec->name()->text(); + + QPtrList<InitDeclaratorAST> l( declarators->initDeclaratorList() ); + QPtrListIterator<InitDeclaratorAST> it( l ); + + InitDeclaratorAST* initDecl = 0; + while( 0 != (initDecl = it.current()) ){ + + QString type, id; + if( initDecl->declarator() ){ + type = typeOfDeclaration( typeSpec, initDecl->declarator() ); + + DeclaratorAST* d = initDecl->declarator(); + while( d->subDeclarator() ){ + d = d->subDeclarator(); + } + + if( d->declaratorId() ) + id = d->declaratorId()->text(); + } +//#ifdef DEBUG_CPPTREE2UML + kDebug() << "CppTree2Uml::parseTypedef: name=" << id << ", type=" << type << endl; +//#endif + /* @todo Trace typedefs back to their root type for deciding + whether to build a Datatype (for pointers.) */ + /* check out if the ID type is a Datatype + ex: typedef unsigned int uint; + where unsigned int is a known datatype + I'm not sure if setIsReference() should be run + */ + bool isDatatype = Import_Utils::isDatatype(typeId, m_currentNamespace[m_nsCnt]); + + if (type.contains('*') || isDatatype) { + UMLObject *inner = + Import_Utils::createUMLObject( Uml::ot_Class, typeId, + m_currentNamespace[m_nsCnt] ); + UMLObject *typedefObj = + Import_Utils::createUMLObject( Uml::ot_Datatype, id, + m_currentNamespace[m_nsCnt] ); + UMLClassifier *dt = static_cast<UMLClassifier*>(typedefObj); + dt->setIsReference(); + dt->setOriginType(static_cast<UMLClassifier*>(inner)); + } else { + Import_Utils::createUMLObject( Uml::ot_Class, id, + m_currentNamespace[m_nsCnt], + "" /* doc */, + "typedef" /* stereotype */); + } + ++it; + } + + } +#endif +} + +void CppTree2Uml::parseTemplateDeclaration( TemplateDeclarationAST* ast ) +{ + TemplateParameterListAST* parmListAST = ast->templateParameterList(); + if (parmListAST == NULL) + return; + QPtrList<TemplateParameterAST> parmList = parmListAST->templateParameterList(); + for (QPtrListIterator<TemplateParameterAST> it(parmList); it.current(); ++it) { + // The template is either a typeParameter or a typeValueParameter. + + TemplateParameterAST* tmplParmNode = it.current(); + TypeParameterAST* typeParmNode = tmplParmNode->typeParameter(); + if (typeParmNode) { + NameAST* nameNode = typeParmNode->name(); + if (nameNode) { + QString typeName = nameNode->unqualifiedName()->text(); + Model_Utils::NameAndType nt(typeName, NULL); + m_templateParams.append(nt); + } else { + kdError() << "CppTree2Uml::parseTemplateDeclaration(type):" + << " nameNode is NULL" << endl; + } + } + + ParameterDeclarationAST* valueNode = tmplParmNode->typeValueParameter(); + if (valueNode) { + TypeSpecifierAST* typeSpec = valueNode->typeSpec(); + if (typeSpec == NULL) { + kdError() << "CppTree2Uml::parseTemplateDeclaration(value):" + << " typeSpec is NULL" << endl; + continue; + } + QString typeName = typeSpec->name()->text(); + UMLObject *t = Import_Utils::createUMLObject( Uml::ot_UMLObject, typeName, + m_currentNamespace[m_nsCnt] ); + DeclaratorAST* declNode = valueNode->declarator(); + NameAST* nameNode = declNode->declaratorId(); + if (nameNode == NULL) { + kdError() << "CppTree2Uml::parseTemplateDeclaration(value):" + << " nameNode is NULL" << endl; + continue; + } + QString paramName = nameNode->unqualifiedName()->text(); + Model_Utils::NameAndType nt(paramName, t); + m_templateParams.append(nt); + } + } + + if( ast->declaration() ) + TreeParser::parseDeclaration( ast->declaration() ); +} + +void CppTree2Uml::parseSimpleDeclaration( SimpleDeclarationAST* ast ) +{ + TypeSpecifierAST* typeSpec = ast->typeSpec(); + InitDeclaratorListAST* declarators = ast->initDeclaratorList(); + + m_comment = ast->comment(); + + if( typeSpec ) + parseTypeSpecifier( typeSpec ); + + if( declarators ){ + QPtrList<InitDeclaratorAST> l = declarators->initDeclaratorList(); + + QPtrListIterator<InitDeclaratorAST> it( l ); + while( it.current() ){ + parseDeclaration( ast->functionSpecifier(), ast->storageSpecifier(), typeSpec, it.current() ); + ++it; + } + } +} + +void CppTree2Uml::parseFunctionDefinition( FunctionDefinitionAST* ast ) +{ + TypeSpecifierAST* typeSpec = ast->typeSpec(); + GroupAST* funSpec = ast->functionSpecifier(); + GroupAST* storageSpec = ast->storageSpecifier(); + + if( !ast->initDeclarator() ) + return; + + DeclaratorAST* d = ast->initDeclarator()->declarator(); + + if( !d->declaratorId() ) + return; + + bool isFriend = false; + bool isVirtual = false; + bool isStatic = false; + bool isInline = false; + bool isConstructor = false; + + if( funSpec ){ + QPtrList<AST> l = funSpec->nodeList(); + QPtrListIterator<AST> it( l ); + while( it.current() ){ + QString text = it.current()->text(); + if( text == "virtual" ) isVirtual = true; + else if( text == "inline" ) isInline = true; + ++it; + } + } + + if( storageSpec ){ + QPtrList<AST> l = storageSpec->nodeList(); + QPtrListIterator<AST> it( l ); + while( it.current() ){ + QString text = it.current()->text(); + if( text == "friend" ) isFriend = true; + else if( text == "static" ) isStatic = true; + ++it; + } + } + + QString id = d->declaratorId()->unqualifiedName()->text().stripWhiteSpace(); + + UMLClassifier *c = m_currentClass[m_clsCnt]; + if (c == NULL) { + kDebug() << "CppTree2Uml::parseFunctionDefinition (" << id + << "): need a surrounding class." << endl; + return; + } + + QString returnType = typeOfDeclaration( typeSpec, d ); + UMLOperation *m = Import_Utils::makeOperation(c, id); + // if a class has no return type, it could be a constructor or + // a destructor + if (d && returnType.isEmpty() && id.find("~") == -1) + isConstructor = true; + + parseFunctionArguments( d, m ); + Import_Utils::insertMethod( c, m, m_currentAccess, returnType, + isStatic, false /*isAbstract*/, isFriend, isConstructor, m_comment); + m_comment = ""; + +/* For reference, Kdevelop does some more: + method->setFileName( m_fileName ); + if( m_inSignals ) + method->setSignal( true ); + if( m_inSlots ) + method->setSlot( true ); + */ +} + +void CppTree2Uml::parseClassSpecifier( ClassSpecifierAST* ast ) +{ + Uml::Visibility oldAccess = m_currentAccess; + bool oldInSlots = m_inSlots; + bool oldInSignals = m_inSignals; + + QString kind = ast->classKey()->text(); + m_currentAccess=Uml::Visibility::fromString(kind); + m_inSlots = false; + m_inSignals = false; + + QString className; + if( !ast->name() && m_currentDeclarator && m_currentDeclarator->declaratorId() ) { + className = m_currentDeclarator->declaratorId()->text().stripWhiteSpace(); + } else if( !ast->name() ){ + QFileInfo fileInfo( m_fileName ); + QString shortFileName = fileInfo.baseName(); + className.sprintf( "(%s_%d)", shortFileName.local8Bit().data(), m_anon++ ); + } else { + className = ast->name()->unqualifiedName()->text().stripWhiteSpace(); + } +//#ifdef DEBUG_CPPTREE2UML + kDebug() << "CppTree2Uml::parseClassSpecifier: name=" << className << endl; +//#endif + if( !scopeOfName( ast->name(), QStringList() ).isEmpty() ){ + kDebug() << "skip private class declarations" << endl; + return; + } + + if (className.isEmpty()) { + className = "anon_" + QString::number(m_anon); + m_anon++; + } + UMLObject * o = Import_Utils::createUMLObject( Uml::ot_Class, className, + m_currentNamespace[m_nsCnt], + ast->comment() ); + UMLClassifier *klass = static_cast<UMLClassifier*>(o); + flushTemplateParams(klass); + if ( ast->baseClause() ) + parseBaseClause( ast->baseClause(), klass ); + + m_currentScope.push_back( className ); + if (++m_clsCnt > STACKSIZE) { + kdError() << "CppTree2Uml::parseNamespace: excessive class nesting" << endl; + m_clsCnt = STACKSIZE; + } + m_currentClass[m_clsCnt] = klass; + if (++m_nsCnt > STACKSIZE) { + kdError() << "CppTree2Uml::parseNamespace: excessive namespace nesting" << endl; + m_nsCnt = STACKSIZE; + } + m_currentNamespace[m_nsCnt] = (UMLPackage*)klass; + + TreeParser::parseClassSpecifier( ast ); + + --m_nsCnt; + --m_clsCnt; + + m_currentScope.pop_back(); + + m_currentAccess = oldAccess; + m_inSlots = oldInSlots; + m_inSignals = oldInSignals; +} + +void CppTree2Uml::parseEnumSpecifier( EnumSpecifierAST* ast ) +{ + NameAST *nameNode = ast->name(); + if (nameNode == NULL) + return; // skip constants + QString typeName = nameNode->unqualifiedName()->text().stripWhiteSpace(); + if (typeName.isEmpty()) + return; // skip constants + UMLObject *o = Import_Utils::createUMLObject( Uml::ot_Enum, typeName, + m_currentNamespace[m_nsCnt], + ast->comment() ); + + QPtrList<EnumeratorAST> l = ast->enumeratorList(); + QPtrListIterator<EnumeratorAST> it( l ); + while ( it.current() ) { + QString enumLiteral = it.current()->id()->text(); + Import_Utils::addEnumLiteral( (UMLEnum*)o, enumLiteral ); + ++it; + } +} + +void CppTree2Uml::parseElaboratedTypeSpecifier( ElaboratedTypeSpecifierAST* typeSpec ) +{ + // This is invoked for forward declarations. + /// @todo Refine - Currently only handles class forward declarations. + /// - Using typeSpec->text() is probably not good, decode + /// the kind() instead. + QString text = typeSpec->text(); + kDebug() << "CppTree2Uml::parseElaboratedTypeSpecifier: text is " << text << endl; + text.remove(QRegExp("^class\\s+")); + UMLObject *o = Import_Utils::createUMLObject(Uml::ot_Class, text, m_currentNamespace[m_nsCnt]); + flushTemplateParams( static_cast<UMLClassifier*>(o) ); +} + +void CppTree2Uml::parseDeclaration( GroupAST* funSpec, GroupAST* storageSpec, + TypeSpecifierAST* typeSpec, InitDeclaratorAST* decl ) +{ + if( m_inStorageSpec ) + return; + + DeclaratorAST* d = decl->declarator(); + + if( !d ) + return; + + if( !d->subDeclarator() && d->parameterDeclarationClause() ) + return parseFunctionDeclaration( funSpec, storageSpec, typeSpec, decl ); + + DeclaratorAST* t = d; + while( t && t->subDeclarator() ) + t = t->subDeclarator(); + + QString id; + if( t && t->declaratorId() && t->declaratorId()->unqualifiedName() ) + id = t->declaratorId()->unqualifiedName()->text(); + + if( !scopeOfDeclarator(d, QStringList()).isEmpty() ){ + kDebug() << "CppTree2Uml::parseDeclaration (" << id << "): skipping." + << endl; + return; + } + + UMLClassifier *c = m_currentClass[m_clsCnt]; + if (c == NULL) { + kDebug() << "CppTree2Uml::parseDeclaration (" << id + << "): need a surrounding class." << endl; + return; + } + + QString typeName = typeOfDeclaration( typeSpec, d ); + bool isFriend = false; + bool isStatic = false; + //bool isInitialized = decl->initializer() != 0; + + if( storageSpec ){ + QPtrList<AST> l = storageSpec->nodeList(); + QPtrListIterator<AST> it( l ); + while( it.current() ){ + QString text = it.current()->text(); + if( text == "friend" ) isFriend = true; + else if( text == "static" ) isStatic = true; + ++it; + } + } + + Import_Utils::insertAttribute( c, m_currentAccess, id, typeName, + m_comment, isStatic); + m_comment = ""; +} + +void CppTree2Uml::parseAccessDeclaration( AccessDeclarationAST * access ) +{ + QPtrList<AST> l = access->accessList(); + + QString accessStr = l.at( 0 )->text(); + + m_currentAccess=Uml::Visibility::fromString(accessStr); + + m_inSlots = l.count() > 1 ? l.at( 1 )->text() == "slots" : false; + m_inSignals = l.count() >= 1 ? l.at( 0 )->text() == "signals" : false; +} + +void CppTree2Uml::parseFunctionDeclaration( GroupAST* funSpec, GroupAST* storageSpec, + TypeSpecifierAST * typeSpec, InitDeclaratorAST * decl ) +{ + bool isFriend = false; + bool isVirtual = false; + bool isStatic = false; + bool isInline = false; + bool isPure = decl->initializer() != 0; + bool isConstructor = false; + + if( funSpec ){ + QPtrList<AST> l = funSpec->nodeList(); + QPtrListIterator<AST> it( l ); + while( it.current() ){ + QString text = it.current()->text(); + if( text == "virtual" ) isVirtual = true; + else if( text == "inline" ) isInline = true; + ++it; + } + } + + if( storageSpec ){ + QPtrList<AST> l = storageSpec->nodeList(); + QPtrListIterator<AST> it( l ); + while( it.current() ){ + QString text = it.current()->text(); + if( text == "friend" ) isFriend = true; + else if( text == "static" ) isStatic = true; + ++it; + } + } + + DeclaratorAST* d = decl->declarator(); + QString id = d->declaratorId()->unqualifiedName()->text(); + + UMLClassifier *c = m_currentClass[m_clsCnt]; + if (c == NULL) { + kDebug() << "CppTree2Uml::parseFunctionDeclaration (" << id + << "): need a surrounding class." << endl; + return; + } + + QString returnType = typeOfDeclaration( typeSpec, d ); + UMLOperation *m = Import_Utils::makeOperation(c, id); + // if a class has no return type, it could be a constructor or + // a destructor + if (d && returnType.isEmpty() && id.find("~") == -1) + isConstructor = true; + + parseFunctionArguments( d, m ); + Import_Utils::insertMethod( c, m, m_currentAccess, returnType, + isStatic, isPure, isFriend, isConstructor, m_comment); + m_comment = ""; +} + +void CppTree2Uml::parseFunctionArguments(DeclaratorAST* declarator, + UMLOperation* method) +{ + ParameterDeclarationClauseAST* clause = declarator->parameterDeclarationClause(); + + if( clause && clause->parameterDeclarationList() ){ + ParameterDeclarationListAST* params = clause->parameterDeclarationList(); + QPtrList<ParameterDeclarationAST> l( params->parameterList() ); + QPtrListIterator<ParameterDeclarationAST> it( l ); + while( it.current() ){ + ParameterDeclarationAST* param = it.current(); + ++it; + + QString name; + if (param->declarator()) + name = declaratorToString(param->declarator(), QString::null, true ); + + QString tp = typeOfDeclaration( param->typeSpec(), param->declarator() ); + + if (tp != "void") + Import_Utils::addMethodParameter( method, tp, name ); + } + } +} + +QString CppTree2Uml::typeOfDeclaration( TypeSpecifierAST* typeSpec, DeclaratorAST* declarator ) +{ + if( !typeSpec || !declarator ) + return QString::null; + + QString text; + + text += typeSpec->text(); + + QPtrList<AST> ptrOpList = declarator->ptrOpList(); + for( QPtrListIterator<AST> it(ptrOpList); it.current(); ++it ){ + text += it.current()->text(); + } + + return text; +} + +void CppTree2Uml::parseBaseClause( BaseClauseAST * baseClause, UMLClassifier* klass ) +{ + QPtrList<BaseSpecifierAST> l = baseClause->baseSpecifierList(); + QPtrListIterator<BaseSpecifierAST> it( l ); + while( it.current() ){ + BaseSpecifierAST* baseSpecifier = it.current(); + ++it; + + if (baseSpecifier->name() == NULL) { + kDebug() << "CppTree2Uml::parseBaseClause: baseSpecifier->name() is NULL" + << endl; + continue; + } + + QString baseName = baseSpecifier->name()->text(); + Import_Utils::createGeneralization( klass, baseName ); + } +} + +QStringList CppTree2Uml::scopeOfName( NameAST* id, const QStringList& startScope ) +{ + QStringList scope = startScope; + if( id && id->classOrNamespaceNameList().count() ){ + if( id->isGlobal() ) + scope.clear(); + QPtrList<ClassOrNamespaceNameAST> l = id->classOrNamespaceNameList(); + QPtrListIterator<ClassOrNamespaceNameAST> it( l ); + while( it.current() ){ + if( it.current()->name() ){ + scope << it.current()->name()->text(); + } + ++it; + } + } + + return scope; +} + +QStringList CppTree2Uml::scopeOfDeclarator( DeclaratorAST* d, const QStringList& startScope ) +{ + return scopeOfName( d->declaratorId(), startScope ); +} + +void CppTree2Uml::flushTemplateParams(UMLClassifier *klass) { + if (m_templateParams.count()) { + Model_Utils::NameAndType_ListIt it; + for (it = m_templateParams.begin(); it != m_templateParams.end(); ++it) { + const Model_Utils::NameAndType &nt = *it; + kDebug() << "CppTree2Uml::parseClassSpecifier: adding template param: " + << nt.m_name << endl; + UMLTemplate *tmpl = klass->addTemplate(nt.m_name); + tmpl->setType(nt.m_type); + } + m_templateParams.clear(); + } +} + diff --git a/umbrello/umbrello/codeimport/kdevcppparser/cpptree2uml.h b/umbrello/umbrello/codeimport/kdevcppparser/cpptree2uml.h new file mode 100644 index 00000000..b9791372 --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/cpptree2uml.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005-2007 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +#ifndef CPPTREE2UML_H +#define CPPTREE2UML_H + +#include "tree_parser.h" +#include <qstringlist.h> +#include "../../model_utils.h" + +// fwd decls +class UMLClassifier; +class UMLOperation; +class UMLPackage; + +class CppTree2Uml: public TreeParser +{ +public: + CppTree2Uml( const QString& fileName); + virtual ~CppTree2Uml(); + + //FileDom file() { return m_file; } + + // translation-unit + virtual void parseTranslationUnit( TranslationUnitAST* ); + + // declarations + //virtual void parseDeclaration( DeclarationAST* ); // use parent method + //virtual void parseLinkageSpecification( LinkageSpecificationAST* ); // use parent method + virtual void parseNamespace( NamespaceAST* ); + //virtual void parseNamespaceAlias( NamespaceAliasAST* ); // use parent method + //virtual void parseUsing( UsingAST* ); // use parent method + //virtual void parseUsingDirective( UsingDirectiveAST* ); // use parent method + virtual void parseTypedef( TypedefAST* ); + virtual void parseTemplateDeclaration( TemplateDeclarationAST* ); + virtual void parseSimpleDeclaration( SimpleDeclarationAST* ); + virtual void parseFunctionDefinition( FunctionDefinitionAST* ); + //virtual void parseLinkageBody( LinkageBodyAST* ); // use parent method + virtual void parseAccessDeclaration( AccessDeclarationAST* ); + + // type-specifier + //virtual void parseTypeSpecifier( TypeSpecifierAST* ); // use parent method + virtual void parseClassSpecifier( ClassSpecifierAST* ); + virtual void parseEnumSpecifier( EnumSpecifierAST* ); + virtual void parseElaboratedTypeSpecifier( ElaboratedTypeSpecifierAST* ); + + // non-overriding (locally added) methods + + virtual void parseDeclaration( GroupAST* funSpec, GroupAST* storageSpec, TypeSpecifierAST* typeSpec, InitDeclaratorAST* decl ); + virtual void parseFunctionDeclaration( GroupAST* funSpec, GroupAST* storageSpec, TypeSpecifierAST* typeSpec, InitDeclaratorAST* decl ); + void parseFunctionArguments( DeclaratorAST* declarator, UMLOperation* method); + virtual void parseBaseClause( BaseClauseAST* baseClause, UMLClassifier* klass ); + +private: + //NamespaceDom findOrInsertNamespace( NamespaceAST* ast, const QString& name ); + + QString typeOfDeclaration( TypeSpecifierAST* typeSpec, DeclaratorAST* declarator ); + QStringList scopeOfName( NameAST* id, const QStringList& scope ); + QStringList scopeOfDeclarator( DeclaratorAST* d, const QStringList& scope ); + /** + * Flush template parameters pending in m_templateParams to the klass. + */ + void flushTemplateParams(UMLClassifier *klass); + +private: + //FileDom m_file; + QString m_fileName; + QStringList m_currentScope; + Uml::Visibility m_currentAccess; + bool m_inSlots; + bool m_inSignals; + int m_anon; + bool m_inStorageSpec; + bool m_inTypedef; + QString m_comment; + Model_Utils::NameAndType_List m_templateParams; + + DeclaratorAST* m_currentDeclarator; +# define STACKSIZE 30 + UMLPackage* m_currentNamespace[STACKSIZE+1]; ///< stack + UMLClassifier* m_currentClass[STACKSIZE+1]; ///< stack + int m_nsCnt; ///< stack top for m_currentNamespace + int m_clsCnt; ///< stack top for m_currentClass + +private: + CppTree2Uml( const CppTree2Uml& source ); + void operator = ( const CppTree2Uml& source ); +}; + +#endif // CPPTREE2UML diff --git a/umbrello/umbrello/codeimport/kdevcppparser/driver.cpp b/umbrello/umbrello/codeimport/kdevcppparser/driver.cpp new file mode 100644 index 00000000..84941025 --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/driver.cpp @@ -0,0 +1,435 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "driver.h" +#include "lexer.h" +#include "parser.h" +#include <kdebug.h> +#include <stdlib.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qdir.h> + +class DefaultSourceProvider: public SourceProvider +{ +public: + DefaultSourceProvider() {} + + virtual QString contents( const QString& fileName ) + { + QString source; + + QFile f( fileName ); + if( f.open(IO_ReadOnly) ){ + QTextStream s( &f ); + source = s.read(); + f.close(); + } + return source; + } + + virtual bool isModified( const QString& fileName ) + { + Q_UNUSED( fileName ); + return true; + } + +private: + DefaultSourceProvider( const DefaultSourceProvider& source ); + void operator = ( const DefaultSourceProvider& source ); +}; + + +Driver::Driver() + : depresolv( false ), lexer( 0 ) +{ + m_sourceProvider = new DefaultSourceProvider(); +} + +Driver::~Driver() +{ + reset(); + delete( m_sourceProvider ); +} + +SourceProvider* Driver::sourceProvider() +{ + return m_sourceProvider; +} + +void Driver::setSourceProvider( SourceProvider* sourceProvider ) +{ + if( m_sourceProvider ) + delete( m_sourceProvider ); + m_sourceProvider = sourceProvider; +} + +void Driver::reset( ) +{ + m_dependences.clear(); + m_macros.clear(); + m_problems.clear(); + m_includePaths.clear(); + + while( m_parsedUnits.size() ){ + TranslationUnitAST* unit = *m_parsedUnits.begin(); + m_parsedUnits.remove( m_parsedUnits.begin() ); + delete( unit ); + } +} + +void Driver::remove( const QString & fileName ) +{ + m_dependences.remove( fileName ); + m_problems.remove( fileName ); + removeAllMacrosInFile( fileName ); + + QMap<QString, TranslationUnitAST*>::Iterator it = m_parsedUnits.find( fileName ); + if( it != m_parsedUnits.end() ){ + TranslationUnitAST* unit = *it; + m_parsedUnits.remove( it ); + delete( unit ); + } +} + +void Driver::removeAllMacrosInFile( const QString& fileName ) +{ + QMap<QString, Macro>::Iterator it = m_macros.begin(); + while( it != m_macros.end() ){ + Macro m = *it++; + if( m.fileName() == fileName ) + removeMacro( m.name() ); + } +} + +TranslationUnitAST::Node Driver::takeTranslationUnit( const QString& fileName ) +{ + QMap<QString, TranslationUnitAST*>::Iterator it = m_parsedUnits.find( fileName ); + TranslationUnitAST::Node unit( *it ); + //m_parsedUnits.remove( it ); + m_parsedUnits[ fileName] = 0; + return unit; +} + +TranslationUnitAST* Driver::translationUnit( const QString& fileName ) const +{ + QMap<QString, TranslationUnitAST*>::ConstIterator it = m_parsedUnits.find( fileName ); + return it != m_parsedUnits.end() ? *it : 0; +} + +void Driver::addDependence( const QString & fileName, const Dependence & dep ) +{ + QFileInfo fileInfo( dep.first ); + QString fn = fileInfo.absFilePath(); + + if ( !depresolv ){ + findOrInsertDependenceList( fileName ).insert( fn, dep ); + return; + } + + QString file = findIncludeFile( dep ); + findOrInsertDependenceList( fileName ).insert( file, dep ); + + if ( m_parsedUnits.find(file) != m_parsedUnits.end() ) + return; + + if ( !QFile::exists( file ) ) { + Problem p( "Couldn't find include file " + dep.first, + lexer ? lexer->currentLine() : -1, + lexer ? lexer->currentColumn() : -1 ); + addProblem( fileName, p ); + return; + } + + QString cfn = m_currentFileName; + Lexer *l = lexer; + parseFile( file ); + m_currentFileName = cfn; + lexer = l; +} + +void Driver::addMacro( const Macro & macro ) +{ + m_macros.insert( macro.name(), macro ); +} + +void Driver::addProblem( const QString & fileName, const Problem & problem ) +{ + findOrInsertProblemList( fileName ).append( problem ); +} + +QMap< QString, Dependence >& Driver::findOrInsertDependenceList( const QString & fileName ) +{ + QMap<QString, QMap<QString, Dependence> >::Iterator it = m_dependences.find( fileName ); + if( it != m_dependences.end() ) + return it.data(); + + QMap<QString, Dependence> l; + m_dependences.insert( fileName, l ); + return m_dependences[ fileName ]; +} + +QValueList < Problem >& Driver::findOrInsertProblemList( const QString & fileName ) +{ + QMap<QString, QValueList<Problem> >::Iterator it = m_problems.find( fileName ); + if( it != m_problems.end() ) + return it.data(); + + QValueList<Problem> l; + m_problems.insert( fileName, l ); + return m_problems[ fileName ]; +} + +QMap< QString, Dependence > Driver::dependences( const QString & fileName ) const +{ + QMap<QString, QMap<QString, Dependence> >::ConstIterator it = m_dependences.find( fileName ); + if( it != m_dependences.end() ) + return it.data(); + return QMap<QString, Dependence>(); +} + +QMap< QString, Macro > Driver::macros() const +{ + return m_macros; +} + +QValueList < Problem > Driver::problems( const QString & fileName ) const +{ + QMap<QString, QValueList<Problem> >::ConstIterator it = m_problems.find( fileName ); + if( it != m_problems.end() ) + return it.data(); + return QValueList<Problem>(); +} + +void Driver::parseFile( const QString& fileName, bool onlyPreProcess, bool force ) +{ + QFileInfo fileInfo( fileName ); + QString absFilePath = fileInfo.absFilePath(); + + QMap<QString, TranslationUnitAST*>::Iterator it = m_parsedUnits.find( absFilePath ); + + if( force && it != m_parsedUnits.end() ){ + takeTranslationUnit( absFilePath ); + } else if( it != m_parsedUnits.end() && *it != 0 ){ + // file already processed + return; + } + + m_dependences.remove( fileName ); + m_problems.remove( fileName ); + + m_currentFileName = fileName; + + Lexer lex( this ); + lexer = &lex; + setupLexer( &lex ); + + lex.setSource( sourceProvider()->contents(fileName) ); + + if( !onlyPreProcess ){ + Parser parser( this, &lex ); + setupParser( &parser ); + + TranslationUnitAST :: Node translationUnit; + parser.parseTranslationUnit( translationUnit ); + m_parsedUnits.insert( fileName, translationUnit.release() ); + fileParsed( fileName ); + } + + m_currentFileName = QString::null; + lexer = 0; +} + +void Driver::setupLexer( Lexer * lexer ) +{ + // stl + lexer->addSkipWord( "__STL_BEGIN_NAMESPACE" ); + lexer->addSkipWord( "__STL_END_NAMESPACE" ); + lexer->addSkipWord( "__STL_BEGIN_RELOPS_NAMESPACE" ); + lexer->addSkipWord( "__STL_END_RELOPS_NAMESPACE" ); + lexer->addSkipWord( "__STL_TEMPLATE_NULL" ); + lexer->addSkipWord( "__STL_TRY" ); + lexer->addSkipWord( "__STL_UNWIND" ); + lexer->addSkipWord( "__STL_NOTHROW" ); + lexer->addSkipWord( "__STL_NULL_TMPL_ARGS" ); + lexer->addSkipWord( "__STL_UNWIND", SkipWordAndArguments ); + lexer->addSkipWord( "__GC_CONST" ); + lexer->addSkipWord( "__HASH_ALLOC_INIT", SkipWordAndArguments ); + lexer->addSkipWord( "__STL_DEFAULT_ALLOCATOR", SkipWordAndArguments, "T" ); + lexer->addSkipWord( "__STL_MUTEX_INITIALIZER" ); + lexer->addSkipWord( "__STL_NULL_TMPL_ARGS" ); + + // antlr + lexer->addSkipWord( "ANTLR_BEGIN_NAMESPACE", SkipWordAndArguments ); + lexer->addSkipWord( "ANTLR_USE_NAMESPACE", SkipWordAndArguments ); + lexer->addSkipWord( "ANTLR_USING_NAMESPACE", SkipWordAndArguments ); + lexer->addSkipWord( "ANTLR_END_NAMESPACE" ); + lexer->addSkipWord( "ANTLR_C_USING", SkipWordAndArguments ); + lexer->addSkipWord( "ANTLR_API" ); + + // gnu + lexer->addSkipWord( "__extension__", SkipWordAndArguments ); + lexer->addSkipWord( "__attribute__", SkipWordAndArguments ); + lexer->addSkipWord( "__BEGIN_DECLS" ); + lexer->addSkipWord( "__END_DECLS" ); + lexer->addSkipWord( "__THROW" ); + lexer->addSkipWord( "__restrict" ); + lexer->addSkipWord( "__restrict__" ); + lexer->addSkipWord( "__attribute_pure__" ); + lexer->addSkipWord( "__attribute_malloc__" ); + lexer->addSkipWord( "__attribute_format_strfmon__" ); + lexer->addSkipWord( "__asm__", SkipWordAndArguments ); + lexer->addSkipWord( "__devinit" ); + lexer->addSkipWord( "__devinit__" ); + lexer->addSkipWord( "__init" ); + lexer->addSkipWord( "__init__" ); + lexer->addSkipWord( "__signed" ); + lexer->addSkipWord( "__signed__" ); + lexer->addSkipWord( "__unsigned" ); + lexer->addSkipWord( "__unsigned__" ); + lexer->addSkipWord( "asmlinkage" ); + lexer->addSkipWord( "____cacheline_aligned" ); + lexer->addSkipWord( "__glibcpp_class_requires", SkipWordAndArguments ); + lexer->addSkipWord( "__glibcpp_class2_requires", SkipWordAndArguments ); + lexer->addSkipWord( "__glibcpp_class4_requires", SkipWordAndArguments ); + lexer->addSkipWord( "__glibcpp_function_requires", SkipWordAndArguments ); + lexer->addSkipWord( "restrict" ); + + lexer->addSkipWord( "__BEGIN_NAMESPACE_STD" ); + lexer->addSkipWord( "__END_NAMESPACE_STD" ); + lexer->addSkipWord( "__BEGIN_NAMESPACE_C99" ); + lexer->addSkipWord( "__END_NAMESPACE_C99" ); + lexer->addSkipWord( "__USING_NAMESPACE_STD", SkipWordAndArguments ); + + // kde + lexer->addSkipWord( "K_SYCOCATYPE", SkipWordAndArguments ); + lexer->addSkipWord( "EXPORT_DOCKCLASS" ); + lexer->addSkipWord( "K_EXPORT_COMPONENT_FACTORY", SkipWordAndArguments ); + lexer->addSkipWord( "K_SYCOCAFACTORY", SkipWordAndArguments ); + lexer->addSkipWord( "KDE_DEPRECATED" ); + + // qt + lexer->addSkipWord( "Q_OVERRIDE", SkipWordAndArguments ); + lexer->addSkipWord( "Q_ENUMS", SkipWordAndArguments ); + lexer->addSkipWord( "Q_PROPERTY", SkipWordAndArguments ); + lexer->addSkipWord( "Q_CLASSINFO", SkipWordAndArguments ); + lexer->addSkipWord( "Q_SETS", SkipWordAndArguments ); + lexer->addSkipWord( "Q_UNUSED", SkipWordAndArguments ); + lexer->addSkipWord( "Q_CREATE_INSTANCE", SkipWordAndArguments ); + lexer->addSkipWord( "Q_DUMMY_COMPARISON_OPERATOR", SkipWordAndArguments ); + lexer->addSkipWord( "ACTIVATE_SIGNAL_WITH_PARAM", SkipWordAndArguments ); + lexer->addSkipWord( "Q_INLINE_TEMPLATES" ); + lexer->addSkipWord( "Q_TEMPLATE_EXTERN" ); + lexer->addSkipWord( "Q_TYPENAME" ); + lexer->addSkipWord( "Q_REFCOUNT" ); + lexer->addSkipWord( "Q_EXPLICIT" ); + lexer->addSkipWord( "QMAC_PASCAL" ); + lexer->addSkipWord( "QT_STATIC_CONST" ); + lexer->addSkipWord( "QT_STATIC_CONST_IMPL" ); + lexer->addSkipWord( "QT_WIN_PAINTER_MEMBERS" ); + lexer->addSkipWord( "QT_NC_MSGBOX" ); + lexer->addSkipWord( "Q_VARIANT_AS", SkipWordAndArguments ); + lexer->addSkipWord( "CALLBACK_CALL_TYPE" ); + + // flex + lexer->addSkipWord( "yyconst" ); + lexer->addSkipWord( "YY_RULE_SETUP" ); + lexer->addSkipWord( "YY_BREAK" ); + lexer->addSkipWord( "YY_RESTORE_YY_MORE_OFFSET" ); + + // gtk + lexer->addSkipWord( "G_BEGIN_DECLS" ); + lexer->addSkipWord( "G_END_DECLS" ); + lexer->addSkipWord( "G_GNUC_CONST" ); + lexer->addSkipWord( "G_CONST_RETURN" ); + lexer->addSkipWord( "GTKMAIN_C_VAR" ); + lexer->addSkipWord( "GTKVAR" ); + lexer->addSkipWord( "GDKVAR" ); + lexer->addSkipWord( "G_GNUC_PRINTF", SkipWordAndArguments ); + + // windows + lexer->addSkipWord( "WINAPI" ); + lexer->addSkipWord( "__stdcall" ); + lexer->addSkipWord( "__cdecl" ); + lexer->addSkipWord( "_cdecl" ); + lexer->addSkipWord( "CALLBACK" ); + + // gcc extensions + addMacro( Macro("__asm__", "asm") ); + addMacro( Macro("__inline", "inline") ); + addMacro( Macro("__inline__", "inline") ); + addMacro( Macro("__const", "const") ); + addMacro( Macro("__const__", "const") ); + addMacro( Macro("__volatile__", "volatile") ); + addMacro( Macro("__complex__", "") ); +} + +void Driver::setupParser( Parser * parser ) +{ + Q_UNUSED( parser ); +} + +void Driver::removeMacro( const QString& macroName ) +{ + m_macros.remove( macroName ); +} + +void Driver::addIncludePath( const QString &path ) +{ + if( !path.stripWhiteSpace().isEmpty() ) + m_includePaths << path; +} + +QString Driver::findIncludeFile( const Dependence& dep ) const +{ + QString fileName = dep.first; + + if( dep.second == Dep_Local ){ + QString path = QFileInfo( currentFileName() ).dirPath( true ); + QFileInfo fileInfo( QFileInfo(path, fileName) ); + if ( fileInfo.exists() && fileInfo.isFile() ) + return fileInfo.absFilePath(); + + } + + QStringList::ConstIterator end(m_includePaths.end()); + for ( QStringList::ConstIterator it(m_includePaths.begin()); it != end; ++it ) { + QFileInfo fileInfo( *it, fileName ); + if ( fileInfo.exists() && fileInfo.isFile() ) + return fileInfo.absFilePath(); + } + + return QString::null; +} + +void Driver::setResolveDependencesEnabled( bool enabled ) +{ + depresolv = enabled; + if ( depresolv ) + setupPreProcessor(); +} + +void Driver::setupPreProcessor() +{ +} + +void Driver::fileParsed( const QString & fileName ) +{ + Q_UNUSED( fileName ); +} diff --git a/umbrello/umbrello/codeimport/kdevcppparser/driver.h b/umbrello/umbrello/codeimport/kdevcppparser/driver.h new file mode 100644 index 00000000..ecb603ab --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/driver.h @@ -0,0 +1,230 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef DRIVER_H +#define DRIVER_H + +#include "ast.h" + +#include <qpair.h> +#include <qvaluestack.h> +#include <qstringlist.h> +#include <qmap.h> + +class Lexer; +class Parser; + +class Problem +{ +public: + enum + { + Level_Error = 0, + Level_Warning, + Level_Todo, + Level_Fixme + }; + +public: + Problem() {} + Problem( const Problem& source ) + : m_text( source.m_text ), m_line( source.m_line ), + m_column( source.m_column ), m_level( source.m_level ) {} + Problem( const QString& text, int line, int column, int level=Level_Error ) + : m_text( text ), m_line( line ), m_column( column ), m_level(level) {} + + Problem& operator = ( const Problem& source ) + { + m_text = source.m_text; + m_line = source.m_line; + m_column = source.m_column; + m_level = source.m_level; + return( *this ); + } + + bool operator == ( const Problem& p ) const + { + return m_text == p.m_text && m_line == p.m_line && m_column == p.m_column && m_level == p.m_level; + } + + QString text() const { return m_text; } + int line() const { return m_line; } + int column() const { return m_column; } + int level() const { return m_level; } + +private: + QString m_text; + int m_line; + int m_column; + int m_level; +}; + +enum +{ + Dep_Global, + Dep_Local +}; + +typedef QPair<QString, int> Dependence; + +class Macro +{ +public: + typedef QString Argument; + +public: + Macro( bool hasArguments = false ): m_hasArguments( hasArguments ) {} + Macro( const QString &n, const QString &b ) : m_name( n ), m_body( b ), m_hasArguments( false ) {} + + Macro( const Macro& source ) + : m_name( source.m_name), + m_fileName( source.m_fileName ), + m_body( source.m_body ), + m_hasArguments( source.m_hasArguments ), + m_argumentList( source.m_argumentList ) {} + + Macro& operator = ( const Macro& source ) + { + m_name = source.m_name; + m_body = source.m_body; + m_fileName = source.m_fileName; + m_hasArguments = source.m_hasArguments; + m_argumentList = source.m_argumentList; + return *this; + } + + bool operator == ( const Macro& source ) const + { + return + m_name == source.m_name && + m_fileName == source.m_fileName && + m_body == source.m_body && + m_hasArguments == source.m_hasArguments && + m_argumentList == source.m_argumentList; + } + + QString name() const { return m_name; } + void setName( const QString& name ) { m_name = name; } + + QString fileName() const { return m_fileName; } + void setFileName( const QString& fileName ) { m_fileName = fileName; } + + QString body() const { return m_body; } + void setBody( const QString& body ) { m_body = body; } + + bool hasArguments() const { return m_hasArguments; } + void setHasArguments( bool hasArguments ) { m_hasArguments = hasArguments; } + QValueList<Argument> argumentList() const { return m_argumentList; } + + void clearArgumentList() { m_argumentList.clear(); m_hasArguments = false; } + void addArgument( const Argument& argument ) { m_argumentList << argument; } + void addArgumentList( const QValueList<Argument>& arguments ) { m_argumentList += arguments; } + +private: + QString m_name; + QString m_fileName; + QString m_body; + bool m_hasArguments; + QValueList<Argument> m_argumentList; +}; + +class SourceProvider +{ +public: + SourceProvider() {} + virtual ~SourceProvider() {} + + virtual QString contents( const QString& fileName ) = 0; + virtual bool isModified( const QString& fileName ) = 0; + +private: + SourceProvider( const SourceProvider& source ); + void operator = ( const SourceProvider& source ); +}; + +class Driver +{ +public: + Driver(); + virtual ~Driver(); + + SourceProvider* sourceProvider(); + void setSourceProvider( SourceProvider* sourceProvider ); + + virtual void reset(); + + virtual void parseFile( const QString& fileName, bool onlyPreProcesss=false, bool force=false ); + virtual void fileParsed( const QString& fileName ); + virtual void remove( const QString& fileName ); + + virtual void addDependence( const QString& fileName, const Dependence& dep ); + virtual void addMacro( const Macro& macro ); + virtual void addProblem( const QString& fileName, const Problem& problem ); + + + QString currentFileName() const { return m_currentFileName; } + TranslationUnitAST::Node takeTranslationUnit( const QString& fileName ); + TranslationUnitAST* translationUnit( const QString& fileName ) const; + QMap<QString, Dependence> dependences( const QString& fileName ) const; + QMap<QString, Macro> macros() const; + QValueList<Problem> problems( const QString& fileName ) const; + + bool hasMacro( const QString& name ) const { return m_macros.contains( name ); } + const Macro& macro( const QString& name ) const { return m_macros[ name ]; } + Macro& macro( const QString& name ) { return m_macros[ name ]; } + + virtual void removeMacro( const QString& macroName ); + virtual void removeAllMacrosInFile( const QString& fileName ); + + QStringList includePaths() const { return m_includePaths; } + virtual void addIncludePath( const QString &path ); + + /// @todo remove + const QMap<QString, TranslationUnitAST*> &parsedUnits() const { return m_parsedUnits; } + + virtual void setResolveDependencesEnabled( bool enabled ); + bool isResolveDependencesEnabled() const { return depresolv; } + +protected: + virtual void setupLexer( Lexer* lexer ); + virtual void setupParser( Parser* parser ); + virtual void setupPreProcessor(); + +private: + QMap<QString, Dependence>& findOrInsertDependenceList( const QString& fileName ); + QValueList<Problem>& findOrInsertProblemList( const QString& fileName ); + QString findIncludeFile( const Dependence& dep ) const; + +private: + QString m_currentFileName; + QMap< QString, QMap<QString, Dependence> > m_dependences; + QMap<QString, Macro> m_macros; + QMap< QString, QValueList<Problem> > m_problems; + QMap<QString, TranslationUnitAST*> m_parsedUnits; + QStringList m_includePaths; + uint depresolv : 1; + Lexer *lexer; + SourceProvider* m_sourceProvider; + +private: + Driver( const Driver& source ); + void operator = ( const Driver& source ); +}; + +#endif diff --git a/umbrello/umbrello/codeimport/kdevcppparser/errors.cpp b/umbrello/umbrello/codeimport/kdevcppparser/errors.cpp new file mode 100644 index 00000000..154301c6 --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/errors.cpp @@ -0,0 +1,25 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "errors.h" +#include <klocale.h> + +QT_STATIC_CONST_IMPL Error& Errors::InternalError = Error( 1, -1, i18n("Internal Error") ); +QT_STATIC_CONST_IMPL Error& Errors::SyntaxError = Error( 2, -1, i18n("Syntax Error before '%1'") ); +QT_STATIC_CONST_IMPL Error& Errors::ParseError = Error( 3, -1, i18n("Parse Error before '%1'") ); diff --git a/umbrello/umbrello/codeimport/kdevcppparser/errors.h b/umbrello/umbrello/codeimport/kdevcppparser/errors.h new file mode 100644 index 00000000..f846533d --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/errors.h @@ -0,0 +1,45 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef ERRORS_H +#define ERRORS_H + +#include <qstring.h> + + +struct Error{ + int code; + int level; + QString text; + + Error( int c, int l, const QString& s ) + : code( c ), level( l ), text( s ) + {} +}; + +class Errors{ +public: + QT_STATIC_CONST Error& InternalError; + QT_STATIC_CONST Error& SyntaxError; + QT_STATIC_CONST Error& ParseError; +}; + + + +#endif diff --git a/umbrello/umbrello/codeimport/kdevcppparser/keywords.lut.h b/umbrello/umbrello/codeimport/kdevcppparser/keywords.lut.h new file mode 100644 index 00000000..5c276953 --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/keywords.lut.h @@ -0,0 +1,123 @@ +/* Automatically generated from keywords.table using /home/roberto/src/kdelibs/kjs/create_hash_table. DO NOT EDIT ! */ + + +static const struct HashEntry keywordEntries[] = { + { "template", Token_template, 0, 0, 0 }, + { "emit", Token_emit, 0, 0, &keywordEntries[88] }, + { "long", Token_long, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "void", Token_void, 0, 0, &keywordEntries[113] }, + { 0, 0, 0, 0, 0 }, + { "explicit", Token_explicit, 0, 0, &keywordEntries[100] }, + { "enum", Token_enum, 0, 0, &keywordEntries[105] }, + { 0, 0, 0, 0, 0 }, + { "continue", Token_continue, 0, 0, &keywordEntries[99] }, + { "k_dcop_signals", Token_k_dcop_signals, 0, 0, &keywordEntries[104] }, + { "auto", Token_auto, 0, 0, &keywordEntries[91] }, + { 0, 0, 0, 0, 0 }, + { "Q_OBJECT", Token_Q_OBJECT, 0, 0, &keywordEntries[86] }, + { "and_eq", Token_and_eq, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "operator", Token_operator, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "class", Token_class, 0, 0, &keywordEntries[90] }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "while", Token_while, 0, 0, 0 }, + { "k_dcop", Token_k_dcop, 0, 0, 0 }, + { "compl", Token_compl, 0, 0, 0 }, + { "bitand", Token_bitand, 0, 0, &keywordEntries[97] }, + { "__int64", Token_int, 0, 0, &keywordEntries[89] }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "bitor", Token_bitor, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "friend", Token_friend, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "signed", Token_signed, 0, 0, 0 }, + { "double", Token_double, 0, 0, 0 }, + { "K_DCOP", Token_K_DCOP, 0, 0, &keywordEntries[111] }, + { "const", Token_const, 0, 0, &keywordEntries[92] }, + { 0, 0, 0, 0, 0 }, + { "inline", Token_inline, 0, 0, &keywordEntries[98] }, + { 0, 0, 0, 0, 0 }, + { "do", Token_do, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "const_cast", Token_const_cast, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "not_eq", Token_not_eq, 0, 0, &keywordEntries[102] }, + { 0, 0, 0, 0, 0 }, + { "static", Token_static, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "throw", Token_throw, 0, 0, 0 }, + { "slots", Token_slots, 0, 0, &keywordEntries[87] }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "static_cast", Token_static_cast, 0, 0, &keywordEntries[115] }, + { "default", Token_default, 0, 0, &keywordEntries[95] }, + { "sizeof", Token_sizeof, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "switch", Token_switch, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "mutable", Token_mutable, 0, 0, 0 }, + { "dynamic_cast", Token_dynamic_cast, 0, 0, 0 }, + { "extern", Token_extern, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "asm", Token_asm, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "signals", Token_signals, 0, 0, &keywordEntries[106] }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "case", Token_case, 0, 0, 0 }, + { "for", Token_for, 0, 0, 0 }, + { "char", Token_char, 0, 0, &keywordEntries[101] }, + { 0, 0, 0, 0, 0 }, + { "export", Token_export, 0, 0, &keywordEntries[94] }, + { "int", Token_int, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "private", Token_private, 0, 0, &keywordEntries[103] }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "not", Token_not, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "else", Token_else, 0, 0, &keywordEntries[93] }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { "bool", Token_bool, 0, 0, 0 }, + { "catch", Token_catch, 0, 0, 0 }, + { "__asm__", Token_asm, 0, 0, 0 }, + { "and", Token_and, 0, 0, 0 }, + { "break", Token_break, 0, 0, &keywordEntries[110] }, + { "delete", Token_delete, 0, 0, 0 }, + { "float", Token_float, 0, 0, &keywordEntries[96] }, + { "goto", Token_goto, 0, 0, 0 }, + { "if", Token_if, 0, 0, 0 }, + { "namespace", Token_namespace, 0, 0, 0 }, + { "new", Token_new, 0, 0, 0 }, + { "or", Token_or, 0, 0, &keywordEntries[107] }, + { "or_eq", Token_or_eq, 0, 0, 0 }, + { "protected", Token_protected, 0, 0, 0 }, + { "public", Token_public, 0, 0, &keywordEntries[109] }, + { "register", Token_register, 0, 0, 0 }, + { "reinterpret_cast", Token_reinterpret_cast, 0, 0, 0 }, + { "return", Token_return, 0, 0, 0 }, + { "short", Token_short, 0, 0, 0 }, + { "struct", Token_struct, 0, 0, 0 }, + { "this", Token_this, 0, 0, 0 }, + { "try", Token_try, 0, 0, &keywordEntries[108] }, + { "typedef", Token_typedef, 0, 0, 0 }, + { "typeid", Token_typeid, 0, 0, 0 }, + { "typename", Token_typename, 0, 0, 0 }, + { "union", Token_union, 0, 0, 0 }, + { "unsigned", Token_unsigned, 0, 0, &keywordEntries[112] }, + { "using", Token_using, 0, 0, 0 }, + { "virtual", Token_virtual, 0, 0, &keywordEntries[114] }, + { "volatile", Token_volatile, 0, 0, 0 }, + { "xor", Token_xor, 0, 0, 0 }, + { "xor_eq", Token_xor_eq, 0, 0, 0 } +}; + +static const struct HashTable keyword = { 2, 116, keywordEntries, 86 }; diff --git a/umbrello/umbrello/codeimport/kdevcppparser/lexer.cpp b/umbrello/umbrello/codeimport/kdevcppparser/lexer.cpp new file mode 100644 index 00000000..2748688f --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/lexer.cpp @@ -0,0 +1,1002 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "lexer.h" +#include "lookup.h" +#include "keywords.lut.h" + +#include <kdebug.h> +#include <klocale.h> + +#include <qregexp.h> +#include <qmap.h> +#include <qvaluelist.h> + +#if defined( KDEVELOP_BGPARSER ) +#include <qthread.h> + +class KDevTread: public QThread +{ +public: + static void yield() + { + msleep( 0 ); + } +}; + +inline void qthread_yield() +{ + KDevTread::yield(); +} + +#endif + +#define CREATE_TOKEN(type, start, len) Token( (type), (start), (len), m_source ) +#define ADD_TOKEN(tk) m_tokens.insert( m_size++, new Token(tk) ); + +using namespace std; + +struct LexerData +{ + typedef QMap<QString, QString> Scope; + typedef QValueList<Scope> StaticChain; + + StaticChain staticChain; + + void beginScope() + { + Scope scope; + staticChain.push_front( scope ); + } + + void endScope() + { + staticChain.pop_front(); + } + + void bind( const QString& name, const QString& value ) + { + Q_ASSERT( staticChain.size() > 0 ); + staticChain.front().insert( name, value ); + } + + bool hasBind( const QString& name ) const + { + StaticChain::ConstIterator it = staticChain.begin(); + while( it != staticChain.end() ){ + const Scope& scope = *it; + ++it; + + if( scope.contains(name) ) + return true; + } + + return false; + } + + QString apply( const QString& name ) const + { + StaticChain::ConstIterator it = staticChain.begin(); + while( it != staticChain.end() ){ + const Scope& scope = *it; + ++it; + + if( scope.contains(name) ) + return scope[ name ]; + } + + return QString::null; + } + +}; + +Lexer::Lexer( Driver* driver ) + : d( new LexerData), + m_driver( driver ), + m_recordComments( false ), + m_recordWhiteSpaces( false ), + m_skipWordsEnabled( true ), + m_preprocessorEnabled( true ), + m_reportWarnings( false ), + m_reportMessages( false ) +{ + m_tokens.setAutoDelete( true ); + reset(); + d->beginScope(); +} + +Lexer::~Lexer() +{ + d->endScope(); + delete( d ); +} + +void Lexer::setSource( const QString& source ) +{ + reset(); + m_source = source; + m_ptr = 0; + m_endPtr = m_source.length(); + m_inPreproc = false; + + tokenize(); +} + +void Lexer::reset() +{ + m_index = 0; + m_size = 0; + m_tokens.clear(); + m_source = QString::null; + m_ptr = 0; + m_endPtr = 0; + m_startLine = false; + m_ifLevel = 0; + m_skipping.resize( 200 ); + m_skipping.fill( 0 ); + m_trueTest.resize( 200 ); + m_trueTest.fill( 0 ); + + m_currentLine = 0; + m_currentColumn = 0; +} + +// ### should all be done with a "long" type IMO +int Lexer::toInt( const Token& token ) +{ + QString s = token.text(); + if( token.type() == Token_number_literal ){ + // hex literal ? + if( s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) + return s.mid( 2 ).toInt( 0, 16 ); + QString n; + int i = 0; + while( i < int(s.length()) && s[i].isDigit() ) + n += s[i++]; + // ### respect more prefixes and suffixes ? + return n.toInt(); + } else if( token.type() == Token_char_literal ){ + int i = s[0] == 'L' ? 2 : 1; // wide char ? + if( s[i] == '\\' ){ + // escaped char + int c = s[i+1].unicode(); + switch( c ) { + case '0': + return 0; + case 'n': + return '\n'; + // ### more + default: + return c; + } + } else { + return s[i].unicode(); + } + } else { + return 0; + } +} + +void Lexer::getTokenPosition( const Token& token, int* line, int* col ) +{ + token.getStartPosition( line, col ); +} + +void Lexer::nextToken( Token& tk, bool stopOnNewline ) +{ + int op = 0; + + if( m_size == (int)m_tokens.size() ){ + m_tokens.resize( m_tokens.size() + 5000 ); + } + + readWhiteSpaces( !stopOnNewline ); + + int startLine = m_currentLine; + int startColumn = m_currentColumn; + + QChar ch = currentChar(); + QChar ch1 = peekChar(); + + if( ch.isNull() || ch.isSpace() ){ + /* skip */ + } else if( m_startLine && ch == '#' ){ + + nextChar(); // skip # + readWhiteSpaces( false ); // skip white spaces + m_startLine = false; + + int start = currentPosition(); + readIdentifier(); // read the directive + QString directive = m_source.mid( start, currentPosition() - start ); + + handleDirective( directive ); + } else if( m_startLine && m_skipping[ m_ifLevel ] ){ + // skip line and continue + m_startLine = false; + int ppe = preprocessorEnabled(); + setPreprocessorEnabled( false ); + while( currentChar() && currentChar() != '\n' ){ + Token tok; + nextToken( tok, true ); + } + m_startLine = true; + setPreprocessorEnabled( ppe ); + return; + } else if( ch == '/' && ch1 == '/' ){ + int start = currentPosition(); + readLineComment(); + if( recordComments() ){ + tk = CREATE_TOKEN( Token_comment, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } + } else if( ch == '/' && ch1 == '*' ){ + int start = currentPosition(); + nextChar( 2 ); + readMultiLineComment(); + + if( recordComments() ){ + tk = CREATE_TOKEN( Token_comment, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } + } else if( ch == '\'' || (ch == 'L' && ch1 == '\'') ){ + int start = currentPosition(); + readCharLiteral(); + tk = CREATE_TOKEN( Token_char_literal, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } else if( ch == '"' ){ + int start = currentPosition(); + readStringLiteral(); + tk = CREATE_TOKEN( Token_string_literal, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } else if( ch.isLetter() || ch == '_' ){ + int start = currentPosition(); + readIdentifier(); + QString ide = m_source.mid( start, currentPosition() - start ); + int k = Lookup::find( &keyword, ide ); + if( m_preprocessorEnabled && m_driver->hasMacro(ide) && + (k == -1 || !m_driver->macro(ide).body().isEmpty()) ){ + + + bool preproc = m_preprocessorEnabled; + m_preprocessorEnabled = false; + + d->beginScope(); + + int svLine = currentLine(); + int svColumn = currentColumn(); + +// Macro& m = m_driver->macro( ide ); + Macro m = m_driver->macro( ide ); + //m_driver->removeMacro( m.name() ); + + QString ellipsisArg; + + if( m.hasArguments() ){ + int endIde = currentPosition(); + + readWhiteSpaces(); + if( currentChar() == '(' ){ + nextChar(); + int argIdx = 0; + int argCount = m.argumentList().size(); + while( currentChar() && argIdx<argCount ){ + readWhiteSpaces(); + + QString argName = m.argumentList()[ argIdx ]; + + bool ellipsis = argName == "..."; + + QString arg = readArgument(); + + if( !ellipsis ) + d->bind( argName, arg ); + else + ellipsisArg += arg; + + if( currentChar() == ',' ){ + nextChar(); + if( !ellipsis ){ + ++argIdx; + } else { + ellipsisArg += ", "; + } + } else if( currentChar() == ')' ){ + break; + } + } + if( currentChar() == ')' ){ + // valid macro + nextChar(); + } + } else { + tk = CREATE_TOKEN( Token_identifier, start, endIde - start ); + tk.setStartPosition( svLine, svColumn ); + tk.setEndPosition( svLine, svColumn + (endIde - start) ); + + m_startLine = false; + + d->endScope(); // OPS!! + m_preprocessorEnabled = preproc; + return; + } + } + + int argsEndAtLine = currentLine(); + int argsEndAtColumn = currentColumn(); + +#if defined( KDEVELOP_BGPARSER ) + qthread_yield(); +#endif + m_source.insert( currentPosition(), m.body() ); + + // tokenize the macro body + + QString textToInsert; + + m_endPtr = currentPosition() + m.body().length(); + while( currentChar() ){ + + readWhiteSpaces(); + + Token tok; + nextToken( tok ); + + bool stringify = !m_inPreproc && tok == '#'; + bool merge = !m_inPreproc && tok == Token_concat; + + if( stringify || merge ) + nextToken( tok ); + + if( tok == Token_eof ) + break; + + QString tokText = tok.text(); + QString str = (tok == Token_identifier && d->hasBind(tokText)) ? d->apply( tokText ) : tokText; + if( str == ide ){ + //Problem p( i18n("unsafe use of macro '%1'").arg(ide), m_currentLine, m_currentColumn ); + //m_driver->addProblem( m_driver->currentFileName(), p ); + m_driver->removeMacro( ide ); + // str = QString::null; + } + + if( stringify ) { + textToInsert.append( QString::fromLatin1("\"") + str + QString::fromLatin1("\" ") ); + } else if( merge ){ + textToInsert.truncate( textToInsert.length() - 1 ); + textToInsert.append( str ); + } else if( tok == Token_ellipsis && d->hasBind("...") ){ + textToInsert.append( ellipsisArg ); + } else { + textToInsert.append( str + QString::fromLatin1(" ") ); + } + } + +#if defined( KDEVELOP_BGPARSER ) + qthread_yield(); +#endif + m_source.insert( currentPosition(), textToInsert ); + + d->endScope(); + m_preprocessorEnabled = preproc; + //m_driver->addMacro( m ); + m_currentLine = argsEndAtLine; + m_currentColumn = argsEndAtColumn; + m_endPtr = m_source.length(); + } else if( k != -1 ){ + tk = CREATE_TOKEN( k, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } else if( m_skipWordsEnabled ){ + QMap< QString, QPair<SkipType, QString> >::Iterator pos = m_words.find( ide ); + if( pos != m_words.end() ){ + if( (*pos).first == SkipWordAndArguments ){ + readWhiteSpaces(); + if( currentChar() == '(' ) + skip( '(', ')' ); + } + if( !(*pos).second.isEmpty() ){ +#if defined( KDEVELOP_BGPARSER ) + qthread_yield(); +#endif + m_source.insert( currentPosition(), QString(" ") + (*pos).second + QString(" ") ); + m_endPtr = m_source.length(); + } + } else if( /*qt_rx.exactMatch(ide) ||*/ + ide.endsWith("EXPORT") || + (ide.startsWith("Q_EXPORT") && ide != "Q_EXPORT_INTERFACE") || + ide.startsWith("QM_EXPORT") || + ide.startsWith("QM_TEMPLATE")){ + + readWhiteSpaces(); + if( currentChar() == '(' ) + skip( '(', ')' ); + } else if( ide.startsWith("K_TYPELIST_") || ide.startsWith("TYPELIST_") ){ + tk = CREATE_TOKEN( Token_identifier, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + readWhiteSpaces(); + if( currentChar() == '(' ) + skip( '(', ')' ); + } else{ + tk = CREATE_TOKEN( Token_identifier, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } + } else { + tk = CREATE_TOKEN( Token_identifier, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } + } else if( ch.isNumber() ){ + int start = currentPosition(); + readNumberLiteral(); + tk = CREATE_TOKEN( Token_number_literal, start, currentPosition() - start ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } else if( -1 != (op = findOperator3()) ){ + tk = CREATE_TOKEN( op, currentPosition(), 3 ); + nextChar( 3 ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } else if( -1 != (op = findOperator2()) ){ + tk = CREATE_TOKEN( op, currentPosition(), 2 ); + nextChar( 2 ); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } else { + tk = CREATE_TOKEN( ch.unicode(), currentPosition(), 1 ); + nextChar(); + tk.setStartPosition( startLine, startColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + } + + m_startLine = false; +} + + +void Lexer::tokenize() +{ + m_startLine = true; + m_size = 0; + + for( ;; ) { + Token tk; + nextToken( tk ); + + if( tk.type() != -1 ) + ADD_TOKEN( tk ); + + if( currentChar().isNull() ) + break; + } + + Token tk = CREATE_TOKEN( Token_eof, currentPosition(), 0 ); + tk.setStartPosition( m_currentLine, m_currentColumn ); + tk.setEndPosition( m_currentLine, m_currentColumn ); + ADD_TOKEN( tk ); +} + +void Lexer::resetSkipWords() +{ + m_words.clear(); +} + +void Lexer::addSkipWord( const QString& word, SkipType skipType, const QString& str ) +{ + m_words[ word ] = qMakePair( skipType, str ); +} + +void Lexer::skip( int l, int r ) +{ + int svCurrentLine = m_currentLine; + int svCurrentColumn = m_currentColumn; + + int count = 0; + + while( !eof() ){ + Token tk; + nextToken( tk ); + + if( (int)tk == l ) + ++count; + else if( (int)tk == r ) + --count; + + if( count == 0 ) + break; + } + + m_currentLine = svCurrentLine; + m_currentColumn = svCurrentColumn; +} + +QString Lexer::readArgument() +{ + int count = 0; + + QString arg; + + readWhiteSpaces(); + while( currentChar() ){ + + readWhiteSpaces(); + QChar ch = currentChar(); + + if( ch.isNull() || (!count && (ch == ',' || ch == ')')) ) + break; + + Token tk; + nextToken( tk ); + + if( tk == '(' ){ + ++count; + } else if( tk == ')' ){ + --count; + } + + if( tk != -1 ) + arg += tk.text() + ' '; + } + + return arg.stripWhiteSpace(); +} + +void Lexer::handleDirective( const QString& directive ) +{ + m_inPreproc = true; + + bool skip = skipWordsEnabled(); + bool preproc = preprocessorEnabled(); + + setSkipWordsEnabled( false ); + setPreprocessorEnabled( false ); + + if( directive == "define" ){ + if( !m_skipping[ m_ifLevel ] ){ + Macro m; + processDefine( m ); + } + } else if( directive == "else" ){ + processElse(); + } else if( directive == "elif" ){ + processElif(); + } else if( directive == "endif" ){ + processEndif(); + } else if( directive == "if" ){ + processIf(); + } else if( directive == "ifdef" ){ + processIfdef(); + } else if( directive == "ifndef" ){ + processIfndef(); + } else if( directive == "include" ){ + if( !m_skipping[ m_ifLevel ] ){ + processInclude(); + } + } else if( directive == "undef" ){ + if( !m_skipping[ m_ifLevel ] ){ + processUndef(); + } + } + + // skip line + while( currentChar() && currentChar() != '\n' ){ + Token tk; + nextToken( tk, true ); + } + + setSkipWordsEnabled( skip ); + setPreprocessorEnabled( preproc ); + + m_inPreproc = false; +} + +int Lexer::testIfLevel() +{ + int rtn = !m_skipping[ m_ifLevel++ ]; + m_skipping[ m_ifLevel ] = m_skipping[ m_ifLevel - 1 ]; + return rtn; +} + +int Lexer::macroDefined() +{ + readWhiteSpaces( false ); + int startWord = currentPosition(); + readIdentifier(); + QString word = m_source.mid( startWord, currentPosition() - startWord ); + bool r = m_driver->hasMacro( word ); + + return r; +} + +void Lexer::processDefine( Macro& m ) +{ + m.setFileName( m_driver->currentFileName() ); + readWhiteSpaces( false ); + + int startMacroName = currentPosition(); + readIdentifier(); + QString macroName = m_source.mid( startMacroName, int(currentPosition()-startMacroName) ); + m_driver->removeMacro( macroName ); + m.setName( macroName ); + + if( currentChar() == '(' ){ + m.setHasArguments( true ); + nextChar(); + + readWhiteSpaces( false ); + + while( currentChar() && currentChar() != ')' ){ + readWhiteSpaces( false ); + + int startArg = currentPosition(); + + if( currentChar() == '.' && peekChar() == '.' && peekChar(2) == '.' ) + nextChar( 3 ); + else + readIdentifier(); + + QString arg = m_source.mid( startArg, int(currentPosition()-startArg) ); + + m.addArgument( Macro::Argument(arg) ); + + readWhiteSpaces( false ); + if( currentChar() != ',' ) + break; + + nextChar(); // skip ',' + } + + if( currentChar() == ')' ) + nextChar(); // skip ')' + } + + setPreprocessorEnabled( true ); + + QString body; + while( currentChar() && currentChar() != '\n' ){ + + if( currentChar().isSpace() ){ + readWhiteSpaces( false ); + body += ' '; + } else { + + Token tk; + nextToken( tk, true ); + + if( tk.type() != -1 ){ + QString s = tk.text(); + body += s; + } + } + } + + m.setBody( body ); + m_driver->addMacro( m ); +} + +void Lexer::processElse() +{ + if( m_ifLevel == 0 ) + /// @todo report error + return; + + if( m_ifLevel > 0 && m_skipping[m_ifLevel-1] ) + m_skipping[ m_ifLevel ] = m_skipping[ m_ifLevel - 1 ]; + else + m_skipping[ m_ifLevel ] = m_trueTest[ m_ifLevel ]; +} + +void Lexer::processElif() +{ + if( m_ifLevel == 0 ) + /// @todo report error + return; + + if( !m_trueTest[m_ifLevel] ){ + /// @todo implement the correct semantic for elif!! + bool inSkip = m_ifLevel > 0 && m_skipping[ m_ifLevel-1 ]; + m_trueTest[ m_ifLevel ] = macroExpression() != 0; + m_skipping[ m_ifLevel ] = inSkip ? inSkip : !m_trueTest[ m_ifLevel ]; + } + else + m_skipping[ m_ifLevel ] = true; +} + +void Lexer::processEndif() +{ + if( m_ifLevel == 0 ) + /// @todo report error + return; + + m_skipping[ m_ifLevel ] = 0; + m_trueTest[ m_ifLevel-- ] = 0; +} + +void Lexer::processIf() +{ + bool inSkip = m_skipping[ m_ifLevel ]; + + if( testIfLevel() ) { +#if 0 + int n; + if( (n = testDefined()) != 0 ) { + int isdef = macroDefined(); + m_trueTest[ m_ifLevel ] = (n == 1 && isdef) || (n == -1 && !isdef); + } else +#endif + m_trueTest[ m_ifLevel ] = macroExpression() != 0; + m_skipping[ m_ifLevel ] = inSkip ? inSkip : !m_trueTest[ m_ifLevel ]; + } +} + +void Lexer::processIfdef() +{ + bool inSkip = m_skipping[ m_ifLevel ]; + + if( testIfLevel() ){ + m_trueTest[ m_ifLevel ] = macroDefined(); + m_skipping[ m_ifLevel ] = inSkip ? inSkip : !m_trueTest[ m_ifLevel ]; + } +} + +void Lexer::processIfndef() +{ + bool inSkip = m_skipping[ m_ifLevel ]; + + if( testIfLevel() ){ + m_trueTest[ m_ifLevel ] = !macroDefined(); + m_skipping[ m_ifLevel ] = inSkip ? inSkip : !m_trueTest[ m_ifLevel ]; + } +} + +void Lexer::processInclude() +{ + if( m_skipping[m_ifLevel] ) + return; + + readWhiteSpaces( false ); + if( currentChar() ){ + QChar ch = currentChar(); + if( ch == '"' || ch == '<' ){ + nextChar(); + QChar ch2 = ch == QChar('"') ? QChar('"') : QChar('>'); + + int startWord = currentPosition(); + while( currentChar() && currentChar() != ch2 ) + nextChar(); + if( currentChar() ){ + QString word = m_source.mid( startWord, int(currentPosition()-startWord) ); + m_driver->addDependence( m_driver->currentFileName(), + Dependence(word, ch == '"' ? Dep_Local : Dep_Global) ); + nextChar(); + } + } + } +} + +void Lexer::processUndef() +{ + readWhiteSpaces(); + int startWord = currentPosition(); + readIdentifier(); + QString word = m_source.mid( startWord, currentPosition() - startWord ); + m_driver->removeMacro( word ); +} + +int Lexer::macroPrimary() +{ + readWhiteSpaces( false ); + int result = 0; + switch( currentChar().unicode() ) { + case '(': + nextChar(); + result = macroExpression(); + if( currentChar() != ')' ){ + /// @todo report error + return 0; + } + nextChar(); + return result; + + case '+': + case '-': + case '!': + case '~': + { + QChar tk = currentChar(); + nextChar(); + int result = macroPrimary(); + if( tk == '-' ) return -result; + else if( tk == '!' ) return !result; + else if( tk == '~' ) return ~result; + } + break; + + default: + { + Token tk; + nextToken( tk, false ); + switch( tk.type() ){ + case Token_identifier: + if( tk.text() == "defined" ){ + return macroPrimary(); + } + /// @todo implement + return m_driver->hasMacro( tk.text() ); + case Token_number_literal: + case Token_char_literal: + return toInt( tk ); + default: + break; + } // end switch + + } // end default + + } // end switch + + return 0; +} + +int Lexer::macroMultiplyDivide() +{ + int result = macroPrimary(); + int iresult, op; + for (;;) { + readWhiteSpaces( false ); + if( currentChar() == '*' ) + op = 0; + else if( currentChar() == '/' && !(peekChar() == '*' || peekChar() == '/') ) + op = 1; + else if( currentChar() == '%' ) + op = 2; + else + break; + nextChar(); + iresult = macroPrimary(); + result = op == 0 ? (result * iresult) : + op == 1 ? (iresult == 0 ? 0 : (result / iresult)) : + (iresult == 0 ? 0 : (result % iresult)) ; + } + return result; +} + +int Lexer::macroAddSubtract() +{ + int result = macroMultiplyDivide(); + int iresult, ad; + readWhiteSpaces( false ); + while( currentChar() == '+' || currentChar() == '-') { + ad = currentChar() == '+'; + nextChar(); + iresult = macroMultiplyDivide(); + result = ad ? (result+iresult) : (result-iresult); + } + return result; +} + +int Lexer::macroRelational() +{ + int result = macroAddSubtract(); + int iresult; + readWhiteSpaces( false ); + while( currentChar() == '<' || currentChar() == '>') { + int lt = currentChar() == '<'; + nextChar(); + if( currentChar() == '=') { + nextChar(); + + iresult = macroAddSubtract(); + result = lt ? (result <= iresult) : (result >= iresult); + } + else { + iresult = macroAddSubtract(); + result = lt ? (result < iresult) : (result > iresult); + } + } + + return result; +} + +int Lexer::macroEquality() +{ + int result = macroRelational(); + int iresult, eq; + readWhiteSpaces( false ); + while ((currentChar() == '=' || currentChar() == '!') && peekChar() == '=') { + eq = currentChar() == '='; + nextChar( 2 ); + iresult = macroRelational(); + result = eq ? (result==iresult) : (result!=iresult); + } + return result; +} + +int Lexer::macroBoolAnd() +{ + int result = macroEquality(); + readWhiteSpaces( false ); + while( currentChar() == '&' && peekChar() != '&') { + nextChar(); + result &= macroEquality(); + } + return result; +} + +int Lexer::macroBoolXor() +{ + int result = macroBoolAnd(); + readWhiteSpaces( false ); + while( currentChar() == '^') { + nextChar(); + result ^= macroBoolAnd(); + } + return result; +} + +int Lexer::macroBoolOr() +{ + int result = macroBoolXor(); + readWhiteSpaces( false ); + while( currentChar() == '|' && peekChar() != '|') { + nextChar(); + result |= macroBoolXor(); + } + return result; +} + +int Lexer::macroLogicalAnd() +{ + int result = macroBoolOr(); + readWhiteSpaces( false ); + while( currentChar() == '&' && peekChar() == '&') { + nextChar( 2 ); + int start = currentPosition(); + result = macroBoolOr() && result; + QString s = m_source.mid( start, currentPosition() - start ); + } + return result; +} + +int Lexer::macroLogicalOr() +{ + int result = macroLogicalAnd(); + readWhiteSpaces( false ); + while( currentChar() == '|' && peekChar() == '|') { + nextChar( 2 ); + result = macroLogicalAnd() || result; + } + return result; +} + +int Lexer::macroExpression() +{ + readWhiteSpaces( false ); + return macroLogicalOr(); +} + +// *IMPORTANT* +// please, don't include lexer.moc here, because Lexer isn't a QObject class!! +// if you have problem while recompiling try to remove cppsupport/.deps, +// cppsupport/Makefile.in and rerun automake/autoconf + diff --git a/umbrello/umbrello/codeimport/kdevcppparser/lexer.h b/umbrello/umbrello/codeimport/kdevcppparser/lexer.h new file mode 100644 index 00000000..cce951d4 --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/lexer.h @@ -0,0 +1,791 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef LEXER_H +#define LEXER_H + +#include "driver.h" + +#include <qstring.h> +#include <qmap.h> +#include <qvaluestack.h> +#include <qpair.h> +#include <qptrvector.h> + +enum Type { + Token_eof = 0, + Token_identifier = 1000, + Token_number_literal, + Token_char_literal, + Token_string_literal, + Token_whitespaces, + Token_comment, + Token_preproc, + + Token_assign = 2000, + Token_ptrmem, + Token_ellipsis, + Token_scope, + Token_shift, + Token_eq, + Token_leq, + Token_geq, + Token_incr, + Token_decr, + Token_arrow, + + Token_concat, + + Token_K_DCOP, + Token_k_dcop, + Token_k_dcop_signals, + + Token_Q_OBJECT, + Token_signals, + Token_slots, + Token_emit, + + Token_and, + Token_and_eq, + Token_asm, + Token_auto, + Token_bitand, + Token_bitor, + Token_bool, + Token_break, + Token_case, + Token_catch, + Token_char, + Token_class, + Token_compl, + Token_const, + Token_const_cast, + Token_continue, + Token_default, + Token_delete, + Token_do, + Token_double, + Token_dynamic_cast, + Token_else, + Token_enum, + Token_explicit, + Token_export, + Token_extern, + Token_false, + Token_float, + Token_for, + Token_friend, + Token_goto, + Token_if, + Token_inline, + Token_int, + Token_long, + Token_mutable, + Token_namespace, + Token_new, + Token_not, + Token_not_eq, + Token_operator, + Token_or, + Token_or_eq, + Token_private, + Token_protected, + Token_public, + Token_register, + Token_reinterpret_cast, + Token_return, + Token_short, + Token_signed, + Token_sizeof, + Token_static, + Token_static_cast, + Token_struct, + Token_switch, + Token_template, + Token_this, + Token_throw, + Token_true, + Token_try, + Token_typedef, + Token_typeid, + Token_typename, + Token_union, + Token_unsigned, + Token_using, + Token_virtual, + Token_void, + Token_volatile, + Token_wchar_t, + Token_while, + Token_xor, + Token_xor_eq +}; + +enum SkipType { + SkipWord, + SkipWordAndArguments +}; + +struct LexerData; + +class Token +{ +public: + Token(); + Token( int type, int position, int length, const QString& text ); + Token( const Token& source ); + + Token& operator = ( const Token& source ); + bool operator == ( const Token& token ) const; + operator int () const; + + bool isNull() const; + + int type() const; + void setType( int type ); + + void getStartPosition( int* line, int* column ) const; + void setStartPosition( int line, int column ); + void getEndPosition( int* line, int* column ) const; + void setEndPosition( int line, int column ); + + unsigned int length() const; + void setLength( unsigned int length ); + + int position() const; + void setPosition( int position ); + + QString text() const; + +private: + int m_type; + int m_position; + int m_length; + int m_startLine; + int m_startColumn; + int m_endLine; + int m_endColumn; + QString m_text; + + friend class Lexer; + friend class Parser; +}; // class Token + +class Lexer +{ +public: + Lexer( Driver* driver ); + ~Lexer(); + + bool recordComments() const; + void setRecordComments( bool record ); + + bool recordWhiteSpaces() const; + void setRecordWhiteSpaces( bool record ); + + bool reportWarnings() const; + void setReportWarnings( bool enable ); + + bool reportMessages() const; + void setReportMessages( bool enable ); + + bool skipWordsEnabled() const; + void setSkipWordsEnabled( bool enabled ); + + bool preprocessorEnabled() const; + void setPreprocessorEnabled( bool enabled ); + + void resetSkipWords(); + void addSkipWord( const QString& word, SkipType skipType=SkipWord, const QString& str = QString::null ); + + QString source() const; + void setSource( const QString& source ); + + int index() const; + void setIndex( int index ); + + void reset(); + + const Token& tokenAt( int position ) const; + const Token& nextToken(); + const Token& lookAhead( int n ) const; + + static int toInt( const Token& token ); + + int tokenPosition( const Token& token ) const; + void getTokenPosition( const Token& token, int* line, int* col ); + + int currentLine() const { return m_currentLine; } + int currentColumn() const { return m_currentColumn; } + +private: + QChar currentChar() const; + QChar peekChar( int n=1 ) const; + int currentPosition() const; + + void tokenize(); + void nextToken( Token& token, bool stopOnNewline=false ); + void nextChar(); + void nextChar( int n ); + void skip( int l, int r ); + void readIdentifier(); + void readWhiteSpaces( bool skipNewLine=true ); + void readLineComment(); + void readMultiLineComment(); + void readCharLiteral(); + void readStringLiteral(); + void readNumberLiteral(); + + int findOperator3() const; + int findOperator2() const; + bool eof() const; + + // preprocessor (based on an article of Al Stevens on Dr.Dobb's journal) + int testIfLevel(); + int macroDefined(); + QString readArgument(); + + int macroPrimary(); + int macroMultiplyDivide(); + int macroAddSubtract(); + int macroRelational(); + int macroEquality(); + int macroBoolAnd(); + int macroBoolXor(); + int macroBoolOr(); + int macroLogicalAnd(); + int macroLogicalOr(); + int macroExpression(); + + void handleDirective( const QString& directive ); + void processDefine( Macro& macro ); + void processElse(); + void processElif(); + void processEndif(); + void processIf(); + void processIfdef(); + void processIfndef(); + void processInclude(); + void processUndef(); + +private: + LexerData* d; + Driver* m_driver; + QPtrVector< Token > m_tokens; + int m_size; + int m_index; + QString m_source; + int m_ptr; + int m_endPtr; + bool m_recordComments; + bool m_recordWhiteSpaces; + bool m_startLine; + QMap< QString, QPair<SkipType, QString> > m_words; + + int m_currentLine; + int m_currentColumn; + bool m_skipWordsEnabled; + + // preprocessor + QMemArray<bool> m_skipping; + QMemArray<bool> m_trueTest; + int m_ifLevel; + bool m_preprocessorEnabled; + bool m_inPreproc; + + bool m_reportWarnings; + bool m_reportMessages; + +private: + Lexer( const Lexer& source ); + void operator = ( const Lexer& source ); +}; + + +inline Token::Token() + : m_type( -1 ), + m_position( 0 ), + m_length( 0 ), + m_text( 0 ) +{ +} + +inline Token::Token( int type, int position, int length, const QString& text ) + : m_type( type ), + m_position( position ), + m_length( length ), + m_text( text ) +{ +} + +inline Token::Token( const Token& source ) + : m_type( source.m_type ), + m_position( source.m_position ), + m_length( source.m_length ), + m_startLine( source.m_startLine ), + m_startColumn( source.m_startColumn ), + m_endLine( source.m_endLine ), + m_endColumn( source.m_endColumn ), + m_text( source.m_text ) +{ +} + +inline Token& Token::operator = ( const Token& source ) +{ + m_type = source.m_type; + m_position = source.m_position; + m_length = source.m_length; + m_startLine = source.m_startLine; + m_startColumn = source.m_startColumn; + m_endLine = source.m_endLine; + m_endColumn = source.m_endColumn; + m_text = source.m_text; + return( *this ); +} + +inline Token::operator int () const +{ + return m_type; +} + +inline bool Token::operator == ( const Token& token ) const +{ + return m_type == token.m_type && + m_position == token.m_position && + m_length == token.m_length && + m_startLine == token.m_startLine && + m_startColumn == token.m_startColumn && + m_endLine == token.m_endLine && + m_endColumn == token.m_endColumn && + m_text == token.m_text; +} + +inline bool Token::isNull() const +{ + return m_type == Token_eof || m_length == 0; +} + +inline int Token::type() const +{ + return m_type; +} + +inline void Token::setType( int type ) +{ + m_type = type; +} + +inline int Token::position() const +{ + return m_position; +} + +inline QString Token::text() const +{ + return m_text.mid(m_position, m_length); +} + +inline void Token::setStartPosition( int line, int column ) +{ + m_startLine = line; + m_startColumn = column; +} + +inline void Token::setEndPosition( int line, int column ) +{ + m_endLine = line; + m_endColumn = column; +} + +inline void Token::getStartPosition( int* line, int* column ) const +{ + if( line ) *line = m_startLine; + if( column ) *column = m_startColumn; +} + +inline void Token::getEndPosition( int* line, int* column ) const +{ + if( line ) *line = m_endLine; + if( column ) *column = m_endColumn; +} + +inline void Token::setPosition( int position ) +{ + m_position = position; +} + +inline unsigned int Token::length() const +{ + return m_length; +} + +inline void Token::setLength( unsigned int length ) +{ + m_length = length; +} + +inline bool Lexer::recordComments() const +{ + return m_recordComments; +} + +inline void Lexer::setRecordComments( bool record ) +{ + m_recordComments = record; +} + +inline bool Lexer::recordWhiteSpaces() const +{ + return m_recordWhiteSpaces; +} + +inline void Lexer::setRecordWhiteSpaces( bool record ) +{ + m_recordWhiteSpaces = record; +} + +inline QString Lexer::source() const +{ + return m_source; +} + +inline int Lexer::index() const +{ + return m_index; +} + +inline void Lexer::setIndex( int index ) +{ + m_index = index; +} + +inline const Token& Lexer::nextToken() +{ + if( m_index < m_size ) + return *m_tokens[ m_index++ ]; + + return *m_tokens[ m_index ]; +} + +inline const Token& Lexer::tokenAt( int n ) const +{ + return *m_tokens[ QMIN(n, m_size-1) ]; +} + +inline const Token& Lexer::lookAhead( int n ) const +{ + return *m_tokens[ QMIN(m_index + n, m_size-1) ]; +} + +inline int Lexer::tokenPosition( const Token& token ) const +{ + return token.position(); +} + +inline void Lexer::nextChar() +{ + if(m_source[m_ptr++] == '\n') { + ++m_currentLine; + m_currentColumn = 0; + m_startLine = true; + } else { + ++m_currentColumn; + } +} + +inline void Lexer::nextChar( int n ) +{ + m_currentColumn += n; + m_ptr += n; +} + +inline void Lexer::readIdentifier() +{ + while( currentChar().isLetterOrNumber() || currentChar() == '_' ) + nextChar(); +} + +inline void Lexer::readWhiteSpaces( bool skipNewLine ) +{ + while( !currentChar().isNull() ){ + QChar ch = currentChar(); + + if( ch == '\n' && !skipNewLine ){ + break; + } else if( ch.isSpace() ){ + nextChar(); + } else if( m_inPreproc && currentChar() == '\\' ){ + nextChar(); + readWhiteSpaces( true ); + } else { + break; + } + } +} + +inline void Lexer::readLineComment() +{ + while( !currentChar().isNull() && currentChar() != '\n' ){ + if( m_reportMessages && currentChar() == '@' && m_source.mid(currentPosition()+1, 4).lower() == "todo" ){ + nextChar( 5 ); + QString msg; + int line = m_currentLine; + int col = m_currentColumn; + + while( currentChar() ){ + if( currentChar() == '*' && peekChar() == '/' ) + break; + else if( currentChar() == '\n' ) + break; + + msg += currentChar(); + nextChar(); + } + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col, Problem::Level_Todo) ); + } else + if( m_reportMessages && m_source.mid(currentPosition(), 5).lower() == "fixme" ){ + nextChar( 5 ); + QString msg; + int line = m_currentLine; + int col = m_currentColumn; + + while( currentChar() ){ + if( currentChar() == '*' && peekChar() == '/' ) + break; + else if( currentChar() == '\n' ) + break; + + msg += currentChar(); + nextChar(); + } + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col, Problem::Level_Fixme) ); + } else + nextChar(); + } +} + +inline void Lexer::readMultiLineComment() +{ + while( !currentChar().isNull() ){ + if( currentChar() == '*' && peekChar() == '/' ){ + nextChar( 2 ); + return; + } else if( m_reportMessages && currentChar() == '@' && m_source.mid(currentPosition()+1, 4).lower() == "todo" ){ + nextChar( 5 ); + QString msg; + int line = m_currentLine; + int col = m_currentColumn; + + while( currentChar() ){ + if( currentChar() == '*' && peekChar() == '/' ) + break; + else if( currentChar() == '\n' ) + break; + msg += currentChar(); + nextChar(); + } + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col, Problem::Level_Todo) ); + } else + if( m_reportMessages && m_source.mid(currentPosition(), 5).lower() == "fixme" ){ + nextChar( 5 ); + QString msg; + int line = m_currentLine; + int col = m_currentColumn; + + while( currentChar() ){ + if( currentChar() == '*' && peekChar() == '/' ) + break; + else if( currentChar() == '\n' ) + break; + + msg += currentChar(); + nextChar(); + } + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col, Problem::Level_Fixme) ); + } else + nextChar(); + } +} + +inline void Lexer::readCharLiteral() +{ + if( currentChar() == '\'' ) + nextChar(); // skip ' + else if( currentChar() == 'L' && peekChar() == '\'' ) + nextChar( 2 ); // slip L' + else + return; + + while( !currentChar().isNull() ){ + int len = m_endPtr - currentPosition(); + + if( len>=2 && (currentChar() == '\\' && peekChar() == '\'') ){ + nextChar( 2 ); + } else if( len>=2 && (currentChar() == '\\' && peekChar() == '\\') ){ + nextChar( 2 ); + } else if( currentChar() == '\'' ){ + nextChar(); + break; + } else { + nextChar(); + } + } +} + +inline void Lexer::readStringLiteral() +{ + if( currentChar() != '"' ) + return; + + nextChar(); // skip " + + while( !currentChar().isNull() ){ + int len = m_endPtr - m_ptr; + + if( len>=2 && currentChar() == '\\' && peekChar() == '"' ){ + nextChar( 2 ); + } else if( len>=2 && currentChar() == '\\' && peekChar() == '\\' ){ + nextChar( 2 ); + } else if( currentChar() == '"' ){ + nextChar(); + break; + } else { + nextChar(); + } + } +} + +inline void Lexer::readNumberLiteral() +{ + while( currentChar().isLetterOrNumber() || currentChar() == '.' ) + nextChar(); +} + +inline int Lexer::findOperator3() const +{ + int n = int(m_endPtr - m_ptr); + + if( n >= 3){ + QChar ch = currentChar(), ch1=peekChar(), ch2=peekChar(2); + + if( ch == '<' && ch1 == '<' && ch2 == '=' ) return Token_assign; + else if( ch == '>' && ch1 == '>' && ch2 == '=' ) return Token_assign; + else if( ch == '-' && ch1 == '>' && ch2 == '*' ) return Token_ptrmem; + else if( ch == '.' && ch1 == '.' && ch2 == '.' ) return Token_ellipsis; + } + + return -1; +} + +inline int Lexer::findOperator2() const +{ + int n = int(m_endPtr - m_ptr); + + if( n>=2 ){ + QChar ch = currentChar(), ch1=peekChar(); + + if( ch == ':' && ch1 == ':' ) return Token_scope; + else if( ch == '.' && ch1 == '*' ) return Token_ptrmem; + else if( ch == '+' && ch1 == '=' ) return Token_assign; + else if( ch == '-' && ch1 == '=' ) return Token_assign; + else if( ch == '*' && ch1 == '=' ) return Token_assign; + else if( ch == '/' && ch1 == '=' ) return Token_assign; + else if( ch == '%' && ch1 == '=' ) return Token_assign; + else if( ch == '^' && ch1 == '=' ) return Token_assign; + else if( ch == '&' && ch1 == '=' ) return Token_assign; + else if( ch == '|' && ch1 == '=' ) return Token_assign; + else if( ch == '<' && ch1 == '<' ) return Token_shift; + else if( ch == '>' && ch1 == '>' ) return Token_shift; + else if( ch == '=' && ch1 == '=' ) return Token_eq; + else if( ch == '!' && ch1 == '=' ) return Token_eq; + else if( ch == '<' && ch1 == '=' ) return Token_leq; + else if( ch == '>' && ch1 == '=' ) return Token_geq; + else if( ch == '&' && ch1 == '&' ) return Token_and; + else if( ch == '|' && ch1 == '|' ) return Token_or; + else if( ch == '+' && ch1 == '+' ) return Token_incr; + else if( ch == '-' && ch1 == '-' ) return Token_decr; + else if( ch == '-' && ch1 == '>' ) return Token_arrow; + else if( ch == '#' && ch1 == '#' ) return Token_concat; + } + + return -1; +} + +inline bool Lexer::skipWordsEnabled() const +{ + return m_skipWordsEnabled; +} + +inline void Lexer::setSkipWordsEnabled( bool enabled ) +{ + m_skipWordsEnabled = enabled; +} + +inline bool Lexer::preprocessorEnabled() const +{ + return m_preprocessorEnabled; +} + +inline void Lexer::setPreprocessorEnabled( bool enabled ) +{ + m_preprocessorEnabled = enabled; +} + +inline int Lexer::currentPosition() const +{ + return m_ptr; +} + +inline QChar Lexer::currentChar() const +{ + return m_ptr < m_endPtr ? m_source[m_ptr] : QChar::null; +} + +inline QChar Lexer::peekChar( int n ) const +{ + return m_ptr+n < m_endPtr ? m_source[m_ptr + n] : QChar::null; +} + +inline bool Lexer::eof() const +{ + return m_ptr >= m_endPtr; +} + +inline bool Lexer::reportWarnings() const +{ + return m_reportWarnings; +} + +inline void Lexer::setReportWarnings( bool enable ) +{ + m_reportWarnings = enable; +} + +inline bool Lexer::reportMessages() const +{ + return m_reportMessages; +} + +inline void Lexer::setReportMessages( bool enable ) +{ + m_reportMessages = enable; +} + + +#endif diff --git a/umbrello/umbrello/codeimport/kdevcppparser/lookup.cpp b/umbrello/umbrello/codeimport/kdevcppparser/lookup.cpp new file mode 100644 index 00000000..86299304 --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/lookup.cpp @@ -0,0 +1,113 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +// adapted to kdevelop by Roberto Raggi <roberto@kdevelop.org> + +#include "lookup.h" + +#include <kdebug.h> + +#include <stdio.h> +#include <string.h> + +const HashEntry* Lookup::findEntry( const struct HashTable *table, + const QChar *c, unsigned int len ) +{ + if (table->type != 2) { + kdDebug() << "KJS: Unknown hash table version" << endl; + return 0; + } + char *ascii = new char[len+1]; + unsigned int i; + for(i = 0; i < len; i++, c++) { + if (!c->row()) + ascii[i] = c->cell(); + else + break; + } + ascii[i] = '\0'; + + int h = hash(ascii) % table->hashSize; + const HashEntry *e = &table->entries[h]; + + // empty bucket ? + if (!e->s) { + delete [] ascii; + return 0; + } + + do { + // compare strings + if (strcmp(ascii, e->s) == 0) { + delete [] ascii; + return e; + } + // try next bucket + e = e->next; + } while (e); + + delete [] ascii; + return 0; +} + +const HashEntry* Lookup::findEntry( const struct HashTable *table, + const QString &s ) +{ + return findEntry( table, s.unicode(), s.length() ); +} + +int Lookup::find(const struct HashTable *table, + const QChar *c, unsigned int len) +{ + const HashEntry *entry = findEntry( table, c, len ); + if (entry) + return entry->value; + return -1; +} + +int Lookup::find(const struct HashTable *table, const QString &s) +{ + return find(table, s.unicode(), s.length()); +} + +unsigned int Lookup::hash(const QChar *c, unsigned int len) +{ + unsigned int val = 0; + // ignoring rower byte + for (unsigned int i = 0; i < len; i++, c++) + val += c->cell(); + + return val; +} + +unsigned int Lookup::hash(const QString &key) +{ + return hash(key.unicode(), key.length()); +} + +unsigned int Lookup::hash(const char *s) +{ + unsigned int val = 0; + while (*s) + val += *s++; + + return val; +} diff --git a/umbrello/umbrello/codeimport/kdevcppparser/lookup.h b/umbrello/umbrello/codeimport/kdevcppparser/lookup.h new file mode 100644 index 00000000..3e9c713c --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/lookup.h @@ -0,0 +1,119 @@ +// -*- c-basic-offset: 2 -*- +/* + * This file is part of the KDE libraries + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +// adapted to kdevelop by Roberto Raggi <roberto@kdevelop.org> + +#ifndef _KJSLOOKUP_H_ +#define _KJSLOOKUP_H_ + +#include <qstring.h> +#include <stdio.h> + + /** + * An entry in a hash table. + */ + struct HashEntry { + /** + * s is the key (e.g. a property name) + */ + const char *s; + /** + * value is the result value (usually an enum value) + */ + int value; + /** + * attr is a set for flags (e.g. the property flags, see object.h) + */ + short int attr; + /** + * params is another number. For property hashtables, it is used to + * denote the number of argument of the function + */ + short int params; + /** + * next is the pointer to the next entry for the same hash value + */ + const HashEntry *next; + }; + + /** + * A hash table + * Usually the hashtable is generated by the create_hash_table script, from a .table file. + * + * The implementation uses an array of entries, "size" is the total size of that array. + * The entries between 0 and hashSize-1 are the entry points + * for each hash value, and the entries between hashSize and size-1 + * are the overflow entries for the hash values that need one. + * The "next" pointer of the entry links entry points to overflow entries, + * and links overflow entries between them. + */ + struct HashTable { + /** + * type is a version number. Currently always 2 + */ + int type; + /** + * size is the total number of entries in the hashtable, including the null entries, + * i.e. the size of the "entries" array. + * Used to iterate over all entries in the table + */ + int size; + /** + * pointer to the array of entries + * Mind that some entries in the array are null (0,0,0,0). + */ + const HashEntry *entries; + /** + * the maximum value for the hash. Always smaller than size. + */ + int hashSize; + }; + + /** + * @short Fast keyword lookup. + */ + class Lookup { + public: + /** + * Find an entry in the table, and return its value (i.e. the value field of HashEntry) + */ + static int find(const struct HashTable *table, const QString& s); + static int find(const struct HashTable *table, const QChar *c, unsigned int len); + + /** + * Find an entry in the table, and return the entry + * This variant gives access to the other attributes of the entry, + * especially the attr field. + */ + static const HashEntry* findEntry(const struct HashTable *table, + const QString &s); + static const HashEntry* findEntry(const struct HashTable *table, + const QChar *c, unsigned int len); + + /** + * Calculate the hash value for a given key + */ + static unsigned int hash(const QString &key); + static unsigned int hash(const QChar *c, unsigned int len); + static unsigned int hash(const char *s); + }; + +#endif diff --git a/umbrello/umbrello/codeimport/kdevcppparser/parser.cpp b/umbrello/umbrello/codeimport/kdevcppparser/parser.cpp new file mode 100644 index 00000000..0314a60c --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/parser.cpp @@ -0,0 +1,4238 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +// c++ support +#include "parser.h" +#include "driver.h" +#include "lexer.h" +#include "errors.h" + +// qt +#include <qstring.h> +#include <qstringlist.h> +#include <qasciidict.h> + +#include <kdebug.h> +#include <klocale.h> + +using namespace std; + +#define ADVANCE(tk, descr) \ +{ \ + const Token& token = lex->lookAhead( 0 ); \ + if( token != tk ){ \ + reportError( i18n("'%1' expected found '%2'").arg(descr).arg(token.text()) ); \ + return false; \ + } \ + lex->nextToken(); \ +} + +#define ADVANCE_NR(tk, descr) \ +{ \ + const Token& token = lex->lookAhead( 0 ); \ + if( token != tk ){ \ + reportError( i18n("'%1' expected found '%2'").arg(descr).arg(token.text()) ); \ + } \ + else \ + lex->nextToken(); \ +} + +#define CHECK(tk, descr) \ +{ \ + const Token& token = lex->lookAhead( 0 ); \ + if( token != tk ){ \ + return false; \ + } \ + lex->nextToken(); \ +} + +#define MATCH(tk, descr) \ +{ \ + const Token& token = lex->lookAhead( 0 ); \ + if( token != tk ){ \ + reportError( Errors::SyntaxError ); \ + return false; \ + } \ +} + +#define UPDATE_POS(node, start, end) \ +{ \ + int line, col; \ + const Token &a = lex->tokenAt(start); \ + const Token &b = lex->tokenAt( end!=start ? end-1 : end ); \ + a.getStartPosition( &line, &col ); \ + (node)->setStartPosition( line, col ); \ + b.getEndPosition( &line, &col ); \ + (node)->setEndPosition( line, col ); \ + if( (node)->nodeType() == NodeType_Generic ) { \ + if ((start) == (end) || (end) == (start)+1) \ + (node)->setSlice(lex->source(), a.position(), a.length()); \ + else \ + (node)->setText( toString((start),(end)) ); \ + } \ +} + +#define AST_FROM_TOKEN(node, tk) \ + AST::Node node = CreateNode<AST>(); \ + UPDATE_POS( node, (tk), (tk)+1 ); + + +//@todo remove me +enum +{ + OBJC_CLASS, + OBJC_PROTOCOL, + OBJC_ALIAS +}; + +struct ParserPrivateData +{ + ParserPrivateData() + {} +}; + +Parser::Parser( Driver* driver, Lexer* lexer ) + : m_driver( driver ), + lex( lexer ) +{ + d = new ParserPrivateData(); + + m_maxProblems = 5; + objcp = false; +} + +Parser::~Parser() +{ + delete d; + d = 0; +} + +bool Parser::reportError( const Error& err ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::reportError()" << endl; + if( m_problems < m_maxProblems ){ + ++m_problems; + int line=0, col=0; + const Token& token = lex->lookAhead( 0 ); + lex->getTokenPosition( token, &line, &col ); + + QString s = lex->lookAhead(0).text(); + s = s.left( 30 ).stripWhiteSpace(); + if( s.isEmpty() ) + s = i18n( "<eof>" ); + + m_driver->addProblem( m_driver->currentFileName(), Problem(err.text.arg(s), line, col) ); + } + + return true; +} + +bool Parser::reportError( const QString& msg ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::reportError()" << endl; + if( m_problems < m_maxProblems ){ + ++m_problems; + int line=0, col=0; + const Token& token = lex->lookAhead( 0 ); + lex->getTokenPosition( token, &line, &col ); + + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col) ); + } + + return true; +} + +void Parser::syntaxError() +{ + (void) reportError( Errors::SyntaxError ); +} + +bool Parser::skipUntil( int token ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipUntil()" << endl; + while( !lex->lookAhead(0).isNull() ){ + if( lex->lookAhead(0) == token ) + return true; + + lex->nextToken(); + } + + return false; +} + +bool Parser::skipUntilDeclaration() +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipUntilDeclaration()" << endl; + + while( !lex->lookAhead(0).isNull() ){ + + switch( lex->lookAhead(0) ){ + case ';': + case '~': + case Token_scope: + case Token_identifier: + case Token_operator: + case Token_char: + case Token_wchar_t: + case Token_bool: + case Token_short: + case Token_int: + case Token_long: + case Token_signed: + case Token_unsigned: + case Token_float: + case Token_double: + case Token_void: + case Token_extern: + case Token_namespace: + case Token_using: + case Token_typedef: + case Token_asm: + case Token_template: + case Token_export: + + case Token_const: // cv + case Token_volatile: // cv + + case Token_public: + case Token_protected: + case Token_private: + case Token_signals: // Qt + case Token_slots: // Qt + return true; + + case '}': + return false; + + default: + lex->nextToken(); + } + } + + return false; +} + +bool Parser::skipUntilStatement() +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipUntilStatement() -- token = " << lex->lookAhead(0).text() << endl; + + while( !lex->lookAhead(0).isNull() ){ + switch( lex->lookAhead(0) ){ + case ';': + case '{': + case '}': + case Token_const: + case Token_volatile: + case Token_identifier: + case Token_case: + case Token_default: + case Token_if: + case Token_switch: + case Token_while: + case Token_do: + case Token_for: + case Token_break: + case Token_continue: + case Token_return: + case Token_goto: + case Token_try: + case Token_catch: + case Token_throw: + case Token_char: + case Token_wchar_t: + case Token_bool: + case Token_short: + case Token_int: + case Token_long: + case Token_signed: + case Token_unsigned: + case Token_float: + case Token_double: + case Token_void: + case Token_class: + case Token_struct: + case Token_union: + case Token_enum: + case Token_scope: + case Token_template: + case Token_using: + return true; + + default: + lex->nextToken(); + } + } + + return false; +} + +bool Parser::skip( int l, int r ) +{ + int count = 0; + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + + if( tk == l ) + ++count; + else if( tk == r ) + --count; + else if( l != '{' && (tk == '{' || tk == '}' || tk == ';') ) + return false; + + if( count == 0 ) + return true; + + lex->nextToken(); + } + + return false; +} + +bool Parser::skipCommaExpression( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipCommaExpression()" << endl; + + int start = lex->index(); + + AST::Node expr; + if( !skipExpression(expr) ) + return false; + + QString comment; + while( lex->lookAhead(0) == ',' ){ + comment = QString::null; + advanceAndCheckTrailingComment( comment ); + + if( !skipExpression(expr) ){ + reportError( i18n("expression expected") ); + return false; + } + } + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::skipExpression( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipExpression()" << endl; + + int start = lex->index(); + + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + + switch( tk ){ + case '(': + skip( '(', ')' ); + lex->nextToken(); + break; + + case '[': + skip( '[', ']' ); + lex->nextToken(); + break; + +#if 0 + case Token_identifier: + lex->nextToken(); + if( lex->lookAhead( 0 ) == Token_identifier ) + return true; + break; +#endif + + case ';': + case ',': + case ']': + case ')': + case '{': + case '}': + case Token_case: + case Token_default: + case Token_if: + case Token_while: + case Token_do: + case Token_for: + case Token_break: + case Token_continue: + case Token_return: + case Token_goto: + { + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + } + return true; + + default: + lex->nextToken(); + } + } + + return false; +} + +bool Parser::parseName( NameAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseName()" << endl; + + GroupAST::Node winDeclSpec; + parseWinDeclSpec( winDeclSpec ); + + int start = lex->index(); + + NameAST::Node ast = CreateNode<NameAST>(); + + if( lex->lookAhead(0) == Token_scope ){ + ast->setGlobal( true ); + lex->nextToken(); + } + + int idx = lex->index(); + + while( true ){ + ClassOrNamespaceNameAST::Node n; + if( !parseUnqualifiedName(n) ) { + return false; + } + + if( lex->lookAhead(0) == Token_scope ){ + lex->nextToken(); + ast->addClassOrNamespaceName( n ); + if( lex->lookAhead(0) == Token_template ) + lex->nextToken(); /// skip optional template #### @todo CHECK + } else { + ast->setUnqualifiedName( n ); + break; + } + } + + if( idx == lex->index() ) + return false; + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTranslationUnit( TranslationUnitAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTranslationUnit()" << endl; + + int start = lex->index(); + + m_problems = 0; + TranslationUnitAST::Node tun = CreateNode<TranslationUnitAST>(); + node = tun; + while( !lex->lookAhead(0).isNull() ){ + DeclarationAST::Node def; + int startDecl = lex->index(); + if( !parseDeclaration(def) ){ + // error recovery + if( startDecl == lex->index() ) + lex->nextToken(); // skip at least one token + skipUntilDeclaration(); + } + node->addDeclaration( def ); + } + + UPDATE_POS( node, start, lex->index() ); + + // force (0,0) as start position + node->setStartPosition( 0, 0 ); + + return m_problems == 0; +} + +bool Parser::parseDeclaration( DeclarationAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclaration()" << endl; + + QString comment; + while( lex->lookAhead(0) == Token_comment ) { + comment += lex->lookAhead(0).text(); + lex->nextToken(); + } + if( lex->lookAhead(0).isNull() ) + return false; + + int start = lex->index(); + bool success = false; + + switch( lex->lookAhead(0) ){ + + case ';': + lex->nextToken(); + return true; + + case Token_extern: + success = parseLinkageSpecification( node ); + break; + + case Token_namespace: + success = parseNamespace( node ); + break; + + case Token_using: + success = parseUsing( node ); + break; + + case Token_typedef: + success = parseTypedef( node ); + break; + + case Token_asm: + success = parseAsmDefinition( node ); + break; + + case Token_template: + case Token_export: + success = parseTemplateDeclaration( node ); + break; + + default: + { + // lex->setIndex( start ); + + if( objcp && parseObjcDef(node) ) + return true; + + lex->setIndex( start ); + + GroupAST::Node storageSpec; + parseStorageClassSpecifier( storageSpec ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + TypeSpecifierAST::Node spec; + AST::Node declarator; + if( parseEnumSpecifier(spec) || parseClassSpecifier(spec) ){ + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify( cv2 ); + spec->setCv2Qualify( cv2 ); + + InitDeclaratorListAST::Node declarators; + parseInitDeclaratorList(declarators); + ADVANCE( ';', ";" ); + + if( !comment.isEmpty() ) { + //kdDebug(9007) << "Parser::parseDeclaration(spec): comment is " << comment << endl; + spec->setComment( comment ); + } + + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + ast->setStorageSpecifier( storageSpec ); + ast->setTypeSpec( spec ); + ast->setInitDeclaratorList( declarators ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + + lex->setIndex( start ); + success = parseDeclarationInternal( node, comment ); + } + + } // end switch + + if( success && !comment.isEmpty() ) { + //kdDebug(9007) << "Parser::parseDeclaration(): comment is " << comment << endl; + node->setComment( comment ); + } + return success; +} + +bool Parser::parseLinkageSpecification( DeclarationAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLinkageSpecification()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_extern ){ + return false; + } + lex->nextToken(); + + LinkageSpecificationAST::Node ast = CreateNode<LinkageSpecificationAST>(); + + int startExternType = lex->index(); + if( lex->lookAhead(0) == Token_string_literal ){ + lex->nextToken(); + AST::Node externType = CreateNode<AST>(); + UPDATE_POS( externType, startExternType, lex->index() ); + + ast->setExternType( externType ); + } + + if( lex->lookAhead(0) == '{' ){ + LinkageBodyAST::Node linkageBody; + parseLinkageBody( linkageBody ); + ast->setLinkageBody( linkageBody ); + } else { + DeclarationAST::Node decl; + if( !parseDeclaration(decl) ){ + reportError( i18n("Declaration syntax error") ); + } + ast->setDeclaration( decl ); + } + + UPDATE_POS( ast, start, lex->index() ); + + node = ast; + + return true; +} + +bool Parser::parseLinkageBody( LinkageBodyAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLinkageBody()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != '{' ){ + return false; + } + lex->nextToken(); + + LinkageBodyAST::Node lba = CreateNode<LinkageBodyAST>(); + node = lba; + + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + + if( tk == '}' ) + break; + + DeclarationAST::Node def; + int startDecl = lex->index(); + if( parseDeclaration(def) ){ + node->addDeclaration( def ); + } else { + // error recovery + if( startDecl == lex->index() ) + lex->nextToken(); // skip at least one token + skipUntilDeclaration(); + } + } + + if( lex->lookAhead(0) != '}' ){ + reportError( i18n("} expected") ); + } else + lex->nextToken(); + + UPDATE_POS( node, start, lex->index() ); + return true; +} + +bool Parser::parseNamespace( DeclarationAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNamespace()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_namespace ){ + return false; + } + lex->nextToken(); + + int startNamespaceName = lex->index(); + if( lex->lookAhead(0) == Token_identifier ){ + lex->nextToken(); + } + AST::Node namespaceName = CreateNode<AST>(); + UPDATE_POS( namespaceName, startNamespaceName, lex->index() ); + + if ( lex->lookAhead(0) == '=' ) { + // namespace alias + lex->nextToken(); + + NameAST::Node name; + if( parseName(name) ){ + ADVANCE( ';', ";" ); + + NamespaceAliasAST::Node ast = CreateNode<NamespaceAliasAST>(); + ast->setNamespaceName( namespaceName ); + ast->setAliasName( name ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; + } else { + reportError( i18n("namespace expected") ); + return false; + } + } else if( lex->lookAhead(0) != '{' ){ + reportError( i18n("{ expected") ); + return false; + } + + NamespaceAST::Node ast = CreateNode<NamespaceAST>(); + ast->setNamespaceName( namespaceName ); + + LinkageBodyAST::Node linkageBody; + parseLinkageBody( linkageBody ); + + ast->setLinkageBody( linkageBody ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseUsing( DeclarationAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseUsing()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_using ){ + return false; + } + lex->nextToken(); + + if( lex->lookAhead(0) == Token_namespace ){ + if( !parseUsingDirective(node) ){ + return false; + } + UPDATE_POS( node, start, lex->index() ); + return true; + } + + UsingAST::Node ast = CreateNode<UsingAST>(); + + int startTypeName = lex->index(); + if( lex->lookAhead(0) == Token_typename ){ + lex->nextToken(); + AST::Node tn = CreateNode<AST>(); + UPDATE_POS( tn, startTypeName, lex->index() ); + ast->setTypeName( tn ); + } + + NameAST::Node name; + if( !parseName(name) ) + return false; + + ast->setName( name ); + + ADVANCE( ';', ";" ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseUsingDirective( DeclarationAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseUsingDirective()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_namespace ){ + return false; + } + lex->nextToken(); + + NameAST::Node name; + if( !parseName(name) ){ + reportError( i18n("Namespace name expected") ); + return false; + } + + ADVANCE( ';', ";" ); + + UsingDirectiveAST::Node ast = CreateNode<UsingDirectiveAST>(); + ast->setName( name ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseOperatorFunctionId( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseOperatorFunctionId()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_operator ){ + return false; + } + lex->nextToken(); + + AST::Node op; + if( parseOperator(op) ){ + AST::Node asn = CreateNode<AST>(); + node = asn; + UPDATE_POS( node, start, lex->index() ); + return true; + } else { + // parse cast operator + GroupAST::Node cv; + parseCvQualify(cv); + + TypeSpecifierAST::Node spec; + if( !parseSimpleTypeSpecifier(spec) ){ + syntaxError(); + return false; + } + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify(cv2); + spec->setCv2Qualify( cv2 ); + + AST::Node ptrOp; + while( parsePtrOperator(ptrOp) ) + ; + + AST::Node asn = CreateNode<AST>(); + node = asn; + UPDATE_POS( node, start, lex->index() ); + return true; + } +} + +bool Parser::parseTemplateArgumentList( TemplateArgumentListAST::Node& node, bool reportError ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateArgumentList()" << endl; + + int start = lex->index(); + + TemplateArgumentListAST::Node ast = CreateNode<TemplateArgumentListAST>(); + + AST::Node templArg; + if( !parseTemplateArgument(templArg) ) + return false; + ast->addArgument( templArg ); + + QString comment; + while( lex->lookAhead(0) == ',' ){ + comment = QString::null; + advanceAndCheckTrailingComment( comment ); + + if( !parseTemplateArgument(templArg) ){ + if( reportError ){ + syntaxError(); + break; + } else + return false; + } + if (!comment.isEmpty()) + templArg->setComment(comment); + ast->addArgument( templArg ); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTypedef( DeclarationAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypedef()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_typedef ){ + return false; + } + lex->nextToken(); + + TypeSpecifierAST::Node spec; + if( !parseTypeSpecifierOrClassSpec(spec) ){ + reportError( i18n("Need a type specifier to declare") ); + return false; + } + + InitDeclaratorListAST::Node declarators; + if( !parseInitDeclaratorList(declarators) ){ + //reportError( i18n("Need an identifier to declare") ); + //return false; + } + + ADVANCE( ';', ";" ); + + TypedefAST::Node ast = CreateNode<TypedefAST>(); + ast->setTypeSpec( spec ); + ast->setInitDeclaratorList( declarators ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseAsmDefinition( DeclarationAST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAsmDefinition()" << endl; + + ADVANCE( Token_asm, "asm" ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + skip( '(', ')' ); + ADVANCE( ')', ")" ); + ADVANCE( ';', ';' ); + + return true; +} + +bool Parser::parseTemplateDeclaration( DeclarationAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateDeclaration()" << endl; + + int start = lex->index(); + + AST::Node exp; + + int startExport = lex->index(); + if( lex->lookAhead(0) == Token_export ){ + lex->nextToken(); + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, startExport, lex->index() ); + exp = n; + } + + if( lex->lookAhead(0) != Token_template ){ + return false; + } + lex->nextToken(); + + TemplateParameterListAST::Node params; + if( lex->lookAhead(0) == '<' ){ + lex->nextToken(); + if (lex->lookAhead(0) != '>') + parseTemplateParameterList( params ); + + ADVANCE( '>', ">" ); + } + + DeclarationAST::Node def; + if( !parseDeclaration(def) ){ + reportError( i18n("expected a declaration") ); + } + + TemplateDeclarationAST::Node ast = CreateNode<TemplateDeclarationAST>(); + ast->setExported( exp ); + ast->setTemplateParameterList( params ); + ast->setDeclaration( def ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseOperator( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseOperator()" << endl; + QString text = lex->lookAhead(0).text(); + + switch( lex->lookAhead(0) ){ + case Token_new: + case Token_delete: + lex->nextToken(); + if( lex->lookAhead(0) == '[' && lex->lookAhead(1) == ']' ){ + lex->nextToken(); + lex->nextToken(); + text += "[]"; + } + return true; + + case '+': + case '-': + case '*': + case '/': + case '%': + case '^': + case '&': + case '|': + case '~': + case '!': + case '=': + case '<': + case '>': + case ',': + case Token_assign: + case Token_shift: + case Token_eq: + case Token_not_eq: + case Token_leq: + case Token_geq: + case Token_and: + case Token_or: + case Token_incr: + case Token_decr: + case Token_ptrmem: + case Token_arrow: + lex->nextToken(); + return true; + + default: + if( lex->lookAhead(0) == '(' && lex->lookAhead(1) == ')' ){ + lex->nextToken(); + lex->nextToken(); + return true; + } else if( lex->lookAhead(0) == '[' && lex->lookAhead(1) == ']' ){ + lex->nextToken(); + lex->nextToken(); + return true; + } + } + + return false; +} + +bool Parser::parseCvQualify( GroupAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCvQualify()" << endl; + + int start = lex->index(); + + GroupAST::Node ast = CreateNode<GroupAST>(); + + int n = 0; + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + if( tk == Token_const || tk == Token_volatile ){ + ++n; + int startWord = lex->index(); + lex->nextToken(); + AST::Node word = CreateNode<AST>(); + UPDATE_POS( word, startWord, lex->index() ); + ast->addNode( word ); + } else + break; + } + + if( n == 0 ) + return false; + + + //kdDebug(9007)<< "-----------------> token = " << lex->lookAhead(0).text() << endl; + UPDATE_POS( ast, start, lex->index() ); + + node = ast; + return true; +} + +bool Parser::parseSimpleTypeSpecifier( TypeSpecifierAST::Node& node ) +{ + int start = lex->index(); + bool isIntegral = false; + bool done = false; + + while( !done ){ + + switch( lex->lookAhead(0) ){ + case Token_char: + case Token_wchar_t: + case Token_bool: + case Token_short: + case Token_int: + case Token_long: + case Token_signed: + case Token_unsigned: + case Token_float: + case Token_double: + case Token_void: + isIntegral = true; + lex->nextToken(); + break; + + default: + done = true; + } + } + + TypeSpecifierAST::Node ast = CreateNode<TypeSpecifierAST>(); + if( isIntegral ){ + ClassOrNamespaceNameAST::Node cl = CreateNode<ClassOrNamespaceNameAST>(); + + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, start, lex->index() ); + cl->setName( n ); + UPDATE_POS( cl, start, lex->index() ); + + NameAST::Node name = CreateNode<NameAST>(); + name->setUnqualifiedName( cl ); + UPDATE_POS( name, start, lex->index() ); + ast->setName( name ); + + } else { + NameAST::Node name; + if( !parseName(name) ){ + lex->setIndex( start ); + return false; + } + ast->setName( name ); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parsePtrOperator( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parsePtrOperator()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) == '&' ){ + lex->nextToken(); + } else if( lex->lookAhead(0) == '*' ){ + lex->nextToken(); + } else { + int index = lex->index(); + AST::Node memPtr; + if( !parsePtrToMember(memPtr) ){ + lex->setIndex( index ); + return false; + } + } + + GroupAST::Node cv; + parseCvQualify( cv ); + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseTemplateArgument( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateArgument()" << endl; + + int start = lex->index(); + if( parseTypeId(node) ){ + if( lex->lookAhead(0) == ',' || lex->lookAhead(0) == '>' ) + return true; + } + + lex->setIndex( start ); + if( !parseLogicalOrExpression(node, true) ){ + return false; + } + + return true; +} + +bool Parser::parseTypeSpecifier( TypeSpecifierAST::Node& spec ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypeSpecifier()" << endl; + + GroupAST::Node cv; + parseCvQualify( cv ); + + if( parseElaboratedTypeSpecifier(spec) || parseSimpleTypeSpecifier(spec) ){ + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify( cv2 ); + spec->setCv2Qualify( cv2 ); + + return true; + } + + return false; +} + +bool Parser::parseDeclarator( DeclaratorAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclarator()" << endl; + + int start = lex->index(); + + DeclaratorAST::Node ast = CreateNode<DeclaratorAST>(); + + DeclaratorAST::Node decl; + NameAST::Node declId; + + AST::Node ptrOp; + while( parsePtrOperator(ptrOp) ){ + ast->addPtrOp( ptrOp ); + } + + if( lex->lookAhead(0) == '(' ){ + lex->nextToken(); + + if( !parseDeclarator(decl) ){ + return false; + } + ast->setSubDeclarator( decl ); + + if( lex->lookAhead(0) != ')'){ + return false; + } + lex->nextToken(); + } else { + + if( lex->lookAhead(0) == ':' ){ + // unnamed bitfield + } else if( parseDeclaratorId(declId) ){ + ast->setDeclaratorId( declId ); + } else { + lex->setIndex( start ); + return false; + } + + if( lex->lookAhead(0) == ':' ){ + lex->nextToken(); + AST::Node expr; + if( !parseConstantExpression(expr) ){ + reportError( i18n("Constant expression expected") ); + } + goto update_pos; + } + } + + { + bool isVector = true; + + while( lex->lookAhead(0) == '[' ){ + int startArray = lex->index(); + lex->nextToken(); + AST::Node expr; + parseCommaExpression( expr ); + + ADVANCE( ']', "]" ); + AST::Node array = CreateNode<AST>(); + UPDATE_POS( array, startArray, lex->index() ); + ast->addArrayDimension( array ); + isVector = true; + } + + bool skipParen = false; + if( lex->lookAhead(0) == Token_identifier && lex->lookAhead(1) == '(' && lex->lookAhead(2) == '(' ){ + lex->nextToken(); + lex->nextToken(); + skipParen = true; + } + + if( ast->subDeclarator() && (!isVector || lex->lookAhead(0) != '(') ){ + lex->setIndex( start ); + return false; + } + + int index = lex->index(); + if( lex->lookAhead(0) == '(' ){ + lex->nextToken(); + + ParameterDeclarationClauseAST::Node params; + if( !parseParameterDeclarationClause(params) ){ + //kdDebug(9007)<< "----------------------> not a parameter declaration, maybe an initializer!?" << endl; + lex->setIndex( index ); + goto update_pos; + } + ast->setParameterDeclarationClause( params ); + + if( lex->lookAhead(0) != ')' ){ + lex->setIndex( index ); + goto update_pos; + } + + lex->nextToken(); // skip ')' + + int startConstant = lex->index(); + if( lex->lookAhead(0) == Token_const ){ + lex->nextToken(); + AST::Node constant = CreateNode<AST>(); + UPDATE_POS( constant, startConstant, lex->index() ); + ast->setConstant( constant ); + } + + GroupAST::Node except; + if( parseExceptionSpecification(except) ){ + ast->setExceptionSpecification( except ); + } + } + + if( skipParen ){ + if( lex->lookAhead(0) != ')' ){ + reportError( i18n("')' expected") ); + } else + lex->nextToken(); + } + + } + +update_pos: + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseAbstractDeclarator( DeclaratorAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclarator()" << endl; + int start = lex->index(); + + DeclaratorAST::Node ast = CreateNode<DeclaratorAST>(); + + DeclaratorAST::Node decl; + NameAST::Node declId; + + AST::Node ptrOp; + while( parsePtrOperator(ptrOp) ){ + ast->addPtrOp( ptrOp ); + } + + if( lex->lookAhead(0) == '(' ){ + lex->nextToken(); + + if( !parseAbstractDeclarator(decl) ){ + return false; + } + ast->setSubDeclarator( decl ); + + if( lex->lookAhead(0) != ')'){ + return false; + } + lex->nextToken(); + } + + { + + while( lex->lookAhead(0) == '[' ){ + int startArray = lex->index(); + lex->nextToken(); + AST::Node expr; + skipCommaExpression( expr ); + + ADVANCE( ']', "]" ); + AST::Node array = CreateNode<AST>(); + UPDATE_POS( array, startArray, lex->index() ); + ast->addArrayDimension( array ); + } + + bool skipParen = false; + if( lex->lookAhead(0) == Token_identifier && lex->lookAhead(1) == '(' && lex->lookAhead(2) == '(' ){ + lex->nextToken(); + lex->nextToken(); + skipParen = true; + } + + int index = lex->index(); + if( lex->lookAhead(0) == '(' ){ + lex->nextToken(); + + ParameterDeclarationClauseAST::Node params; + if( !parseParameterDeclarationClause(params) ){ + lex->setIndex( index ); + goto UPDATE_POS; + } + ast->setParameterDeclarationClause( params ); + + if( lex->lookAhead(0) != ')' ){ + lex->setIndex( index ); + goto UPDATE_POS; + } else + lex->nextToken(); + + int startConstant = lex->index(); + if( lex->lookAhead(0) == Token_const ){ + lex->nextToken(); + AST::Node constant = CreateNode<AST>(); + UPDATE_POS( constant, startConstant, lex->index() ); + ast->setConstant( constant ); + } + + GroupAST::Node except; + if( parseExceptionSpecification(except) ){ + ast->setExceptionSpecification( except ); + } + } + + if( skipParen ){ + if( lex->lookAhead(0) != ')' ){ + reportError( i18n("')' expected") ); + } else + lex->nextToken(); + } + + } + +UPDATE_POS: + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseEnumSpecifier( TypeSpecifierAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseEnumSpecifier()" << endl; + + QString comment; + while( lex->lookAhead(0) == Token_comment ) { + comment += lex->lookAhead(0).text(); + lex->nextToken(); + } + if( lex->lookAhead(0).isNull() ) + return false; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_enum ){ + return false; + } + + lex->nextToken(); + + NameAST::Node name; + parseName( name ); + + if( lex->lookAhead(0) != '{' ){ + lex->setIndex( start ); + return false; + } + lex->nextToken(); + + EnumSpecifierAST::Node ast = CreateNode<EnumSpecifierAST>(); + ast->setName( name ); + + EnumeratorAST::Node enumerator; + if( parseEnumerator(enumerator) ){ + ast->addEnumerator( enumerator ); + + QString comment; + while( lex->lookAhead(0) == ',' ){ + comment = ""; + advanceAndCheckTrailingComment( comment ); + if ( !comment.isEmpty() ){ + EnumeratorAST *lastLit = ast->enumeratorList().last(); + if( lastLit ) + lastLit->setComment( comment ); + } + + if( !parseEnumerator(enumerator) ){ + //reportError( i18n("Enumerator expected") ); + break; + } + + ast->addEnumerator( enumerator ); + } + } + + if( lex->lookAhead(0) == Token_comment ) + lex->nextToken(); + if( lex->lookAhead(0) != '}' ) + reportError( i18n("} missing") ); + else + lex->nextToken(); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTemplateParameterList( TemplateParameterListAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateParameterList()" << endl; + + int start = lex->index(); + + TemplateParameterListAST::Node ast = CreateNode<TemplateParameterListAST>(); + + TemplateParameterAST::Node param; + if( !parseTemplateParameter(param) ){ + return false; + } + ast->addTemplateParameter( param ); + + QString comment; + while( lex->lookAhead(0) == ',' ){ + comment = QString::null; + advanceAndCheckTrailingComment( comment ); + + if( !parseTemplateParameter(param) ){ + syntaxError(); + break; + } else { + if (!comment.isEmpty()) + param->setComment(comment); + ast->addTemplateParameter( param ); + } + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTemplateParameter( TemplateParameterAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateParameter()" << endl; + + int start = lex->index(); + TemplateParameterAST::Node ast = CreateNode<TemplateParameterAST>(); + + TypeParameterAST::Node typeParameter; + ParameterDeclarationAST::Node param; + + int tk = lex->lookAhead( 0 ); + + if( (tk == Token_class || tk == Token_typename || tk == Token_template) && parseTypeParameter(typeParameter) ){ + ast->setTypeParameter( typeParameter ); + goto ok; + } + + if( !parseParameterDeclaration(param) ) + return false; + ast->setTypeValueParameter( param ); + +ok: + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTypeParameter( TypeParameterAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypeParameter()" << endl; + + int start = lex->index(); + TypeParameterAST::Node ast = CreateNode<TypeParameterAST>(); + + AST_FROM_TOKEN( kind, lex->index() ); + ast->setKind( kind ); + + switch( lex->lookAhead(0) ){ + + case Token_class: + case Token_typename: + { + lex->nextToken(); // skip class + + // parse optional name + NameAST::Node name; + if( parseName(name) ){ + ast->setName( name ); + if( lex->lookAhead(0) == '=' ){ + lex->nextToken(); + + AST::Node typeId; + if( !parseTypeId(typeId) ){ + syntaxError(); + return false; + } + ast->setTypeId( typeId ); + } + } + } + break; + + case Token_template: + { + lex->nextToken(); // skip template + ADVANCE( '<', '<' ); + + TemplateParameterListAST::Node params; + if( !parseTemplateParameterList(params) ){ + return false; + } + ast->setTemplateParameterList( params ); + + ADVANCE( '>', ">" ); + + if( lex->lookAhead(0) == Token_class ) + lex->nextToken(); + + // parse optional name + NameAST::Node name; + if( parseName(name) ){ + ast->setName( name ); + if( lex->lookAhead(0) == '=' ){ + lex->nextToken(); + + AST::Node typeId; + if( !parseTypeId(typeId) ){ + syntaxError(); + return false; + } + ast->setTypeId( typeId ); + } + } + + if( lex->lookAhead(0) == '=' ){ + lex->nextToken(); + + NameAST::Node templ_name; + parseName( templ_name ); + } + } + break; + + default: + return false; + + } // end switch + + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseStorageClassSpecifier( GroupAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseStorageClassSpecifier()" << endl; + + int start = lex->index(); + GroupAST::Node ast = CreateNode<GroupAST>(); + + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + if( tk == Token_friend || tk == Token_auto || tk == Token_register || tk == Token_static || + tk == Token_extern || tk == Token_mutable ){ + int startNode = lex->index(); + lex->nextToken(); + + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, startNode, lex->index() ); + ast->addNode( n ); + } else + break; + } + + if( ast->nodeList().count() == 0 ) + return false; + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseFunctionSpecifier( GroupAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseFunctionSpecifier()" << endl; + + int start = lex->index(); + GroupAST::Node ast = CreateNode<GroupAST>(); + + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + if( tk == Token_inline || tk == Token_virtual || tk == Token_explicit ){ + int startNode = lex->index(); + lex->nextToken(); + + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, startNode, lex->index() ); + ast->addNode( n ); + } else { + break; + } + } + + if( ast->nodeList().count() == 0 ) + return false; + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseTypeId( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypeId()" << endl; + + /// @todo implement the AST for typeId + int start = lex->index(); + AST::Node ast = CreateNode<AST>(); + + TypeSpecifierAST::Node spec; + if( !parseTypeSpecifier(spec) ){ + return false; + } + + DeclaratorAST::Node decl; + parseAbstractDeclarator( decl ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseInitDeclaratorList( InitDeclaratorListAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitDeclaratorList()" << endl; + + int start = lex->index(); + + InitDeclaratorListAST::Node ast = CreateNode<InitDeclaratorListAST>(); + InitDeclaratorAST::Node decl; + + if( !parseInitDeclarator(decl) ){ + return false; + } + ast->addInitDeclarator( decl ); + + QString comment; + while( lex->lookAhead(0) == ',' ){ + comment = ""; + advanceAndCheckTrailingComment( comment ); + + if( !parseInitDeclarator(decl) ){ + syntaxError(); + break; + } + if ( !comment.isEmpty() ) + decl->setComment( comment ); + ast->addInitDeclarator( decl ); + } + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitDeclaratorList() -- end" << endl; + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseParameterDeclarationClause( ParameterDeclarationClauseAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseParameterDeclarationClause()" << endl; + + int start = lex->index(); + + ParameterDeclarationClauseAST::Node ast = CreateNode<ParameterDeclarationClauseAST>(); + + ParameterDeclarationListAST::Node params; + if( !parseParameterDeclarationList(params) ){ + + if ( lex->lookAhead(0) == ')' ) + goto good; + + if( lex->lookAhead(0) == Token_ellipsis && lex->lookAhead(1) == ')' ){ + AST_FROM_TOKEN( ellipsis, lex->index() ); + ast->setEllipsis( ellipsis ); + lex->nextToken(); + goto good; + } + return false; + } + + if( lex->lookAhead(0) == Token_ellipsis ){ + AST_FROM_TOKEN( ellipsis, lex->index() ); + ast->setEllipsis( ellipsis ); + lex->nextToken(); + } + +good: + ast->setParameterDeclarationList( params ); + + /// @todo add ellipsis + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseParameterDeclarationList( ParameterDeclarationListAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseParameterDeclarationList()" << endl; + + int start = lex->index(); + + ParameterDeclarationListAST::Node ast = CreateNode<ParameterDeclarationListAST>(); + + ParameterDeclarationAST::Node param; + if( !parseParameterDeclaration(param) ){ + lex->setIndex( start ); + return false; + } + ast->addParameter( param ); + + QString comment; + while( lex->lookAhead(0) == ',' ){ + comment = QString::null; + advanceAndCheckTrailingComment( comment ); + + if( lex->lookAhead(0) == Token_ellipsis ) + break; + + if( !parseParameterDeclaration(param) ){ + lex->setIndex( start ); + return false; + } + if (!comment.isEmpty()) + param->setComment(comment); + ast->addParameter( param ); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseParameterDeclaration( ParameterDeclarationAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseParameterDeclaration()" << endl; + + int start = lex->index(); + + // parse decl spec + TypeSpecifierAST::Node spec; + if( !parseTypeSpecifier(spec) ){ + lex->setIndex( start ); + return false; + } + + int index = lex->index(); + + DeclaratorAST::Node decl; + if( !parseDeclarator(decl) ){ + lex->setIndex( index ); + + // try with abstract declarator + if( !parseAbstractDeclarator(decl) ) + return false; + } + + AST::Node expr; + if( lex->lookAhead(0) == '=' ){ + lex->nextToken(); + if( !parseLogicalOrExpression(expr,true) ){ + //reportError( i18n("Expression expected") ); + } + } + + ParameterDeclarationAST::Node ast = CreateNode<ParameterDeclarationAST>(); + ast->setTypeSpec( spec ); + ast->setDeclarator( decl ); + ast->setExpression( expr ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseClassSpecifier( TypeSpecifierAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseClassSpecifier()" << endl; + + int start = lex->index(); + + AST::Node classKey; + int classKeyStart = lex->index(); + + int kind = lex->lookAhead( 0 ); + if( kind == Token_class || kind == Token_struct || kind == Token_union ){ + AST::Node asn = CreateNode<AST>(); + classKey = asn; + lex->nextToken(); + UPDATE_POS( classKey, classKeyStart, lex->index() ); + } else { + return false; + } + + GroupAST::Node winDeclSpec; + parseWinDeclSpec( winDeclSpec ); + + while( lex->lookAhead(0) == Token_identifier && lex->lookAhead(1) == Token_identifier ) + lex->nextToken(); + + NameAST::Node name; + parseName( name ); + + BaseClauseAST::Node bases; + if( lex->lookAhead(0) == ':' ){ + if( !parseBaseClause(bases) ){ + skipUntil( '{' ); + } + } + + QString comment; + while (lex->lookAhead(0) == Token_comment) { + comment += lex->lookAhead(0).text(); + lex->nextToken(); + } + if( lex->lookAhead(0) != '{' ){ + lex->setIndex( start ); + return false; + } + + ADVANCE( '{', '{' ); + + ClassSpecifierAST::Node ast = CreateNode<ClassSpecifierAST>(); + ast->setWinDeclSpec( winDeclSpec ); + ast->setClassKey( classKey ); + ast->setName( name ); + ast->setBaseClause( bases ); + + while( !lex->lookAhead(0).isNull() ){ + if( lex->lookAhead(0) == '}' ) + break; + + DeclarationAST::Node memSpec = CreateNode<DeclarationAST>(); + int startDecl = lex->index(); + if( !parseMemberSpecification(memSpec) ){ + if( startDecl == lex->index() ) + lex->nextToken(); // skip at least one token + skipUntilDeclaration(); + } else + ast->addDeclaration( memSpec ); + } + + if( lex->lookAhead(0) != '}' ){ + reportError( i18n("} missing") ); + } else + lex->nextToken(); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseAccessSpecifier( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAccessSpecifier()" << endl; + + int start = lex->index(); + + switch( lex->lookAhead(0) ){ + case Token_public: + case Token_protected: + case Token_private: { + AST::Node asn = CreateNode<AST>(); + node = asn; + lex->nextToken(); + UPDATE_POS( node, start, lex->index() ); + return true; + } + } + + return false; +} + +void Parser::advanceAndCheckTrailingComment(QString& comment) +{ + Token t = lex->tokenAt( lex->index() ); + int previousTokenEndLine = 0; + t.getEndPosition( &previousTokenEndLine, 0 ); + lex->nextToken(); + if( lex->lookAhead(0) != Token_comment ) + return; + t = lex->tokenAt( lex->index() ); + int commentStartLine = 0; + t.getStartPosition( &commentStartLine, 0 ); + if( commentStartLine != previousTokenEndLine ) + return; + comment += lex->lookAhead(0).text(); + lex->nextToken(); +} + +bool Parser::parseMemberSpecification( DeclarationAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMemberSpecification()" << endl; + + QString comment; + while( lex->lookAhead(0) == Token_comment ) { + comment += lex->lookAhead(0).text(); + lex->nextToken(); + } + if( lex->lookAhead(0).isNull() ) + return false; + + int start = lex->index(); + + AST::Node access; + + if( lex->lookAhead(0) == ';' ){ + advanceAndCheckTrailingComment( comment ); + if ( !comment.isEmpty() ) + node->setComment( comment ); + return true; + } else if( lex->lookAhead(0) == Token_Q_OBJECT || lex->lookAhead(0) == Token_K_DCOP ){ + lex->nextToken(); + return true; + } else if( lex->lookAhead(0) == Token_signals || lex->lookAhead(0) == Token_k_dcop || lex->lookAhead(0) == Token_k_dcop_signals ){ + AccessDeclarationAST::Node ast = CreateNode<AccessDeclarationAST>(); + lex->nextToken(); + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, start, lex->index() ); + ast->addAccess( n ); + ADVANCE( ':', ":" ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; + } else if( parseTypedef(node) ){ + return true; + } else if( parseUsing(node) ){ + return true; + } else if( parseTemplateDeclaration(node) ){ + return true; + } else if( parseAccessSpecifier(access) ){ + AccessDeclarationAST::Node ast = CreateNode<AccessDeclarationAST>(); + ast->addAccess( access ); + + int startSlot = lex->index(); + if( lex->lookAhead(0) == Token_slots ){ + lex->nextToken(); + AST::Node sl = CreateNode<AST>(); + UPDATE_POS( sl, startSlot, lex->index() ); + ast->addAccess( sl ); + } + ADVANCE( ':', ":" ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; + } + + lex->setIndex( start ); + + GroupAST::Node storageSpec; + parseStorageClassSpecifier( storageSpec ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + TypeSpecifierAST::Node spec; + if( parseEnumSpecifier(spec) || parseClassSpecifier(spec) ){ + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify( cv2 ); + spec->setCv2Qualify( cv2 ); + + InitDeclaratorListAST::Node declarators; + parseInitDeclaratorList( declarators ); + ADVANCE( ';', ";" ); + + if( !comment.isEmpty() ) { + //kdDebug(9007) << "Parser::parseMemberSpecification(spec): comment is " << comment << endl; + spec->setComment( comment ); + } + + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + ast->setTypeSpec( spec ); + ast->setInitDeclaratorList( declarators ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + + lex->setIndex( start ); + + bool success = parseDeclarationInternal(node, comment); + if( success && !comment.isEmpty() ) { + node->setComment( comment ); + //kdDebug(9007) << "Parser::parseMemberSpecification(): comment is " << comment << endl; + } + return success; +} + +bool Parser::parseCtorInitializer( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCtorInitializer()" << endl; + + if( lex->lookAhead(0) != ':' ){ + return false; + } + lex->nextToken(); + + AST::Node inits; + if( !parseMemInitializerList(inits) ){ + reportError( i18n("Member initializers expected") ); + } + + return true; +} + +bool Parser::parseElaboratedTypeSpecifier( TypeSpecifierAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseElaboratedTypeSpecifier()" << endl; + + int start = lex->index(); + + int tk = lex->lookAhead( 0 ); + if( tk == Token_class || + tk == Token_struct || + tk == Token_union || + tk == Token_enum || + tk == Token_typename ) + { + AST::Node kind = CreateNode<AST>(); + lex->nextToken(); + UPDATE_POS( kind, start, lex->index() ); + + NameAST::Node name; + + if( parseName(name) ){ + ElaboratedTypeSpecifierAST::Node ast = CreateNode<ElaboratedTypeSpecifierAST>(); + ast->setKind( kind ); + ast->setName( name ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + } + + lex->setIndex( start ); + return false; +} + +bool Parser::parseDeclaratorId( NameAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclaratorId()" << endl; + return parseName( node ); +} + +bool Parser::parseExceptionSpecification( GroupAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseExceptionSpecification()" << endl; + + if( lex->lookAhead(0) != Token_throw ){ + return false; + } + lex->nextToken(); + + ADVANCE( '(', "(" ); + if( lex->lookAhead(0) == Token_ellipsis ){ + // extension found in MSVC++ 7.x headers + int start = lex->index(); + GroupAST::Node ast = CreateNode<GroupAST>(); + AST_FROM_TOKEN( ellipsis, lex->index() ); + ast->addNode( ellipsis ); + lex->nextToken(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + } else { + parseTypeIdList( node ); + } + ADVANCE( ')', ")" ); + + return true; +} + +bool Parser::parseEnumerator( EnumeratorAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseEnumerator()" << endl; + + QString comment; + while( lex->lookAhead(0) == Token_comment ) { + comment += lex->lookAhead(0).text(); + lex->nextToken(); + } + if( lex->lookAhead(0).isNull() ) + return false; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_identifier ){ + return false; + } + lex->nextToken(); + + EnumeratorAST::Node ena = CreateNode<EnumeratorAST>(); + node = ena; + + AST::Node id = CreateNode<AST>(); + UPDATE_POS( id, start, lex->index() ); + node->setId( id ); + + if( lex->lookAhead(0) == '=' ){ + lex->nextToken(); + + AST::Node expr; + if( !parseConstantExpression(expr) ){ + reportError( i18n("Constant expression expected") ); + } + node->setExpr( expr ); + } + + UPDATE_POS( node, start, lex->index() ); + + return true; +} + +bool Parser::parseInitDeclarator( InitDeclaratorAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitDeclarator()" << endl; + + int start = lex->index(); + + DeclaratorAST::Node decl; + AST::Node init; + if( !parseDeclarator(decl) ){ + return false; + } + + parseInitializer( init ); + + InitDeclaratorAST::Node ast = CreateNode<InitDeclaratorAST>(); + ast->setDeclarator( decl ); + ast->setInitializer( init ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + + +bool Parser::parseBaseClause( BaseClauseAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseBaseClause()" << endl; + + int start = lex->index(); + if( lex->lookAhead(0) != ':' ){ + return false; + } + lex->nextToken(); + + BaseClauseAST::Node bca = CreateNode<BaseClauseAST>(); + + BaseSpecifierAST::Node baseSpec; + if( parseBaseSpecifier(baseSpec) ){ + bca->addBaseSpecifier( baseSpec ); + + QString comment; + while( lex->lookAhead(0) == ',' ){ + comment = QString::null; + advanceAndCheckTrailingComment( comment ); + + if( !parseBaseSpecifier(baseSpec) ){ + reportError( i18n("Base class specifier expected") ); + return false; + } + if (!comment.isEmpty()) + baseSpec->setComment(comment); + bca->addBaseSpecifier( baseSpec ); + } + } else + return false; + + UPDATE_POS( bca, start, lex->index() ); + node = bca; + + return true; +} + +bool Parser::parseInitializer( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitializer()" << endl; + + if( lex->lookAhead(0) == '=' ){ + lex->nextToken(); + + AST::Node init; + if( !parseInitializerClause(node) ){ + reportError( i18n("Initializer clause expected") ); + return false; + } + } else if( lex->lookAhead(0) == '(' ){ + lex->nextToken(); + AST::Node expr; + skipCommaExpression( expr ); + + ADVANCE( ')', ")" ); + } + + return false; +} + +bool Parser::parseMemInitializerList( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMemInitializerList()" << endl; + + AST::Node init; + if( !parseMemInitializer(init) ){ + return false; + } + + QString comment; + while( lex->lookAhead(0) == ',' ){ + comment = QString::null; + advanceAndCheckTrailingComment( comment ); + + if( parseMemInitializer(init) ){ + } else { + break; + } + } + + return true; +} + +bool Parser::parseMemInitializer( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMemInitializer()" << endl; + + NameAST::Node initId; + if( !parseMemInitializerId(initId) ){ + reportError( i18n("Identifier expected") ); + return false; + } + ADVANCE( '(', '(' ); + AST::Node expr; + skipCommaExpression( expr ); + ADVANCE( ')', ')' ); + + return true; +} + +bool Parser::parseTypeIdList( GroupAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypeIdList()" << endl; + + int start = lex->index(); + + AST::Node typeId; + if( !parseTypeId(typeId) ){ + return false; + } + + GroupAST::Node ast = CreateNode<GroupAST>(); + ast->addNode( typeId ); + + QString comment; + while( lex->lookAhead(0) == ',' ){ + comment = QString::null; + advanceAndCheckTrailingComment( comment ); + if( parseTypeId(typeId) ){ + if (!comment.isEmpty()) + typeId->setComment(comment); + ast->addNode( typeId ); + } else { + reportError( i18n("Type id expected") ); + break; + } + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseBaseSpecifier( BaseSpecifierAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseBaseSpecifier()" << endl; + + int start = lex->index(); + BaseSpecifierAST::Node ast = CreateNode<BaseSpecifierAST>(); + + AST::Node access; + if( lex->lookAhead(0) == Token_virtual ){ + AST_FROM_TOKEN( virt, lex->index() ); + ast->setIsVirtual( virt ); + + lex->nextToken(); + + parseAccessSpecifier( access ); + } else { + parseAccessSpecifier( access ); + + if( lex->lookAhead(0) == Token_virtual ){ + AST_FROM_TOKEN( virt, lex->index() ); + ast->setIsVirtual( virt ); + lex->nextToken(); + } + } + + NameAST::Node name; + if( !parseName(name) ){ + reportError( i18n("Class name expected") ); + } + + ast->setAccess( access ); + ast->setName( name ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseInitializerClause( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitializerClause()" << endl; + + if( lex->lookAhead(0) == '{' ){ + if( !skip('{','}') ){ + reportError( i18n("} missing") ); + } else + lex->nextToken(); + } else { + if( !parseAssignmentExpression(node) ){ + //reportError( i18n("Expression expected") ); + } + } + + return true; +} + +bool Parser::parseMemInitializerId( NameAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMemInitializerId()" << endl; + + return parseName( node ); +} + +bool Parser::parsePtrToMember( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parsePtrToMember()" << endl; + + if( lex->lookAhead(0) == Token_scope ){ + lex->nextToken(); + } + + while( lex->lookAhead(0) == Token_identifier ){ + lex->nextToken(); + + if( lex->lookAhead(0) == Token_scope && lex->lookAhead(1) == '*' ){ + lex->nextToken(); // skip :: + lex->nextToken(); // skip * + return true; + } else + break; + } + + return false; +} + +bool Parser::parseUnqualifiedName( ClassOrNamespaceNameAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseUnqualifiedName()" << endl; + + int start = lex->index(); + bool isDestructor = false; + + ClassOrNamespaceNameAST::Node ast = CreateNode<ClassOrNamespaceNameAST>(); + + if( lex->lookAhead(0) == Token_identifier ){ + int startName = lex->index(); + AST::Node n = CreateNode<AST>(); + lex->nextToken(); + UPDATE_POS( n, startName, lex->index() ); + ast->setName( n ); + } else if( lex->lookAhead(0) == '~' && lex->lookAhead(1) == Token_identifier ){ + int startName = lex->index(); + AST::Node n = CreateNode<AST>(); + lex->nextToken(); // skip ~ + lex->nextToken(); // skip classname + UPDATE_POS( n, startName, lex->index() ); + ast->setName( n ); + isDestructor = true; + } else if( lex->lookAhead(0) == Token_operator ){ + AST::Node n; + if( !parseOperatorFunctionId(n) ) + return false; + ast->setName( n ); + } else { + return false; + } + + if( !isDestructor ){ + + int index = lex->index(); + + if( lex->lookAhead(0) == '<' ){ + lex->nextToken(); + + // optional template arguments + TemplateArgumentListAST::Node args; + parseTemplateArgumentList( args ); + + if( lex->lookAhead(0) != '>' ){ + lex->setIndex( index ); + } else { + lex->nextToken(); + ast->setTemplateArgumentList( args ); + } + } + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseStringLiteral( AST::Node& /*node*/ ) +{ + while( !lex->lookAhead(0).isNull() ) { + if( lex->lookAhead(0) == Token_identifier && + lex->lookAhead(0).text() == "L" && lex->lookAhead(1) == Token_string_literal ) { + + lex->nextToken(); + lex->nextToken(); + } else if( lex->lookAhead(0) == Token_string_literal ) { + lex->nextToken(); + } else + return false; + } + return true; +} + +bool Parser::skipExpressionStatement( StatementAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipExpressionStatement()" << endl; + + int start = lex->index(); + + AST::Node expr; + skipCommaExpression( expr ); + + ADVANCE( ';', ";" ); + + ExpressionStatementAST::Node ast = CreateNode<ExpressionStatementAST>(); + ast->setExpression( expr ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseStatement( StatementAST::Node& node ) // thanks to fiore@8080.it ;) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseStatement()" << endl; + switch( lex->lookAhead(0) ){ + + case Token_while: + return parseWhileStatement( node ); + + case Token_do: + return parseDoStatement( node ); + + case Token_for: + return parseForStatement( node ); + + case Token_if: + return parseIfStatement( node ); + + case Token_switch: + return parseSwitchStatement( node ); + + case Token_try: + return parseTryBlockStatement( node ); + + case Token_case: + case Token_default: + return parseLabeledStatement( node ); + + case Token_break: + case Token_continue: + lex->nextToken(); + ADVANCE( ';', ";" ); + return true; + + case Token_goto: + lex->nextToken(); + ADVANCE( Token_identifier, "identifier" ); + ADVANCE( ';', ";" ); + return true; + + case Token_return: + { + lex->nextToken(); + AST::Node expr; + skipCommaExpression( expr ); + ADVANCE( ';', ";" ); + } + return true; + + case '{': + return parseCompoundStatement( node ); + + case Token_identifier: + if( parseLabeledStatement(node) ) + return true; + break; + } + + //kdDebug(9007)<< "------------> try with declaration statement" << endl; + if ( parseDeclarationStatement(node) ) + return true; + + return skipExpressionStatement( node ); +} + +bool Parser::parseCondition( ConditionAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCondition()" << endl; + + int start = lex->index(); + + ConditionAST::Node ast = CreateNode<ConditionAST>(); + + TypeSpecifierAST::Node spec; + if( parseTypeSpecifier(spec) ){ + DeclaratorAST::Node decl; + if( parseDeclarator(decl) && lex->lookAhead(0) == '=' ) { + lex->nextToken(); + + AST::Node expr; + if( skipExpression(expr) ){ + ast->setTypeSpec( spec ); + ast->setDeclarator( decl ); + ast->setExpression( expr ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + } + } + + lex->setIndex( start ); + + AST::Node expr; + if( !skipCommaExpression(expr) ) + return false; + + ast->setExpression( expr ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + + +bool Parser::parseWhileStatement( StatementAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseWhileStatement()" << endl; + int start = lex->index(); + + ADVANCE( Token_while, "while" ); + ADVANCE( '(' , "(" ); + + ConditionAST::Node cond; + if( !parseCondition(cond) ){ + reportError( i18n("condition expected") ); + return false; + } + ADVANCE( ')', ")" ); + + StatementAST::Node body; + if( !parseStatement(body) ){ + reportError( i18n("statement expected") ); + return false; + } + + WhileStatementAST::Node ast = CreateNode<WhileStatementAST>(); + ast->setCondition( cond ); + ast->setStatement( body ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseDoStatement( StatementAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDoStatement()" << endl; + int start = lex->index(); + + ADVANCE( Token_do, "do" ); + + StatementAST::Node body; + if( !parseStatement(body) ){ + reportError( i18n("statement expected") ); + //return false; + } + + ADVANCE_NR( Token_while, "while" ); + ADVANCE_NR( '(' , "(" ); + + AST::Node expr; + if( !skipCommaExpression(expr) ){ + reportError( i18n("expression expected") ); + //return false; + } + + ADVANCE_NR( ')', ")" ); + ADVANCE_NR( ';', ";" ); + + DoStatementAST::Node ast = CreateNode<DoStatementAST>(); + ast->setStatement( body ); + //ast->setCondition( condition ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseForStatement( StatementAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseForStatement()" << endl; + int start = lex->index(); + + ADVANCE( Token_for, "for" ); + ADVANCE( '(', "(" ); + + StatementAST::Node init; + if( !parseForInitStatement(init) ){ + reportError( i18n("for initialization expected") ); + return false; + } + + ConditionAST::Node cond; + parseCondition( cond ); + ADVANCE( ';', ";" ); + + AST::Node expr; + skipCommaExpression( expr ); + ADVANCE( ')', ")" ); + + StatementAST::Node body; + if( !parseStatement(body) ) + return false; + + ForStatementAST::Node ast = CreateNode<ForStatementAST>(); + ast->setInitStatement( init ); + ast->setCondition( cond ); + // ast->setExpression( expression ); + ast->setStatement( body ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseForInitStatement( StatementAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseForInitStatement()" << endl; + + if ( parseDeclarationStatement(node) ) + return true; + + return skipExpressionStatement( node ); +} + +bool Parser::parseCompoundStatement( StatementAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCompoundStatement()" << endl; + int start = lex->index(); + + if( lex->lookAhead(0) != '{' ){ + return false; + } + lex->nextToken(); + + StatementListAST::Node ast = CreateNode<StatementListAST>(); + + while( !lex->lookAhead(0).isNull() ){ + if( lex->lookAhead(0) == '}' ) + break; + + StatementAST::Node stmt; + int startStmt = lex->index(); + if( !parseStatement(stmt) ){ + if( startStmt == lex->index() ) + lex->nextToken(); + skipUntilStatement(); + } else { + ast->addStatement( stmt ); + } + } + + if( lex->lookAhead(0) != '}' ){ + reportError( i18n("} expected") ); + } else { + lex->nextToken(); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseIfStatement( StatementAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseIfStatement()" << endl; + + int start = lex->index(); + + ADVANCE( Token_if, "if" ); + + ADVANCE( '(' , "(" ); + + IfStatementAST::Node ast = CreateNode<IfStatementAST>(); + + ConditionAST::Node cond; + if( !parseCondition(cond) ){ + reportError( i18n("condition expected") ); + return false; + } + ADVANCE( ')', ")" ); + + StatementAST::Node stmt; + if( !parseStatement(stmt) ){ + reportError( i18n("statement expected") ); + return false; + } + + ast->setCondition( cond ); + ast->setStatement( stmt ); + + if( lex->lookAhead(0) == Token_else ){ + lex->nextToken(); + StatementAST::Node elseStmt; + if( !parseStatement(elseStmt) ) { + reportError( i18n("statement expected") ); + return false; + } + ast->setElseStatement( elseStmt ); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseSwitchStatement( StatementAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseSwitchStatement()" << endl; + int start = lex->index(); + ADVANCE( Token_switch, "switch" ); + + ADVANCE( '(' , "(" ); + + ConditionAST::Node cond; + if( !parseCondition(cond) ){ + reportError( i18n("condition expected") ); + return false; + } + ADVANCE( ')', ")" ); + + StatementAST::Node stmt; + if( !parseCompoundStatement(stmt) ){ + syntaxError(); + return false; + } + + SwitchStatementAST::Node ast = CreateNode<SwitchStatementAST>(); + ast->setCondition( cond ); + ast->setStatement( stmt ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseLabeledStatement( StatementAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLabeledStatement()" << endl; + switch( lex->lookAhead(0) ){ + case Token_identifier: + case Token_default: + if( lex->lookAhead(1) == ':' ){ + lex->nextToken(); + lex->nextToken(); + + StatementAST::Node stmt; + if( parseStatement(stmt) ){ + node = stmt; + return true; + } + } + break; + + case Token_case: + { + lex->nextToken(); + AST::Node expr; + if( !parseConstantExpression(expr) ){ + reportError( i18n("expression expected") ); + } else if( lex->lookAhead(0) == Token_ellipsis ){ + lex->nextToken(); + + AST::Node expr2; + if( !parseConstantExpression(expr2) ){ + reportError( i18n("expression expected") ); + } + } + ADVANCE( ':', ":" ); + + StatementAST::Node stmt; + if( parseStatement(stmt) ){ + node = stmt; + return true; + } + } + break; + + } + + return false; +} + +bool Parser::parseBlockDeclaration( DeclarationAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseBlockDeclaration()" << endl; + switch( lex->lookAhead(0) ) { + case Token_typedef: + return parseTypedef( node ); + case Token_using: + return parseUsing( node ); + case Token_asm: + return parseAsmDefinition( node ); + case Token_namespace: + return parseNamespaceAliasDefinition( node ); + } + + int start = lex->index(); + + GroupAST::Node storageSpec; + parseStorageClassSpecifier( storageSpec ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + TypeSpecifierAST::Node spec; + if ( !parseTypeSpecifierOrClassSpec(spec) ) { // replace with simpleTypeSpecifier?!?! + lex->setIndex( start ); + return false; + } + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify( cv2 ); + spec->setCv2Qualify( cv2 ); + + InitDeclaratorListAST::Node declarators; + parseInitDeclaratorList( declarators ); + + if( lex->lookAhead(0) != ';' ){ + lex->setIndex( start ); + return false; + } + lex->nextToken(); + + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + ast->setTypeSpec( spec ); + ast->setInitDeclaratorList( declarators ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseNamespaceAliasDefinition( DeclarationAST::Node& /*node*/ ) +{ + if ( lex->lookAhead(0) != Token_namespace ) { + return false; + } + lex->nextToken(); + + ADVANCE( Token_identifier, "identifier" ); + ADVANCE( '=', "=" ); + + NameAST::Node name; + if( !parseName(name) ){ + reportError( i18n("Namespace name expected") ); + } + + ADVANCE( ';', ";" ); + + return true; + +} + +bool Parser::parseDeclarationStatement( StatementAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclarationStatement()" << endl; + + int start = lex->index(); + + DeclarationAST::Node decl; + if ( !parseBlockDeclaration(decl) ){ + return false; + } + + DeclarationStatementAST::Node ast = CreateNode<DeclarationStatementAST>(); + ast->setDeclaration( decl ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + //kdDebug(9007)<< "---------------------> found a block declaration" << endl; + return true; +} + +bool Parser::parseDeclarationInternal( DeclarationAST::Node& node, QString& comment ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclarationInternal()" << endl; + + int start = lex->index(); + + // that is for the case '__declspec(dllexport) int ...' or + // '__declspec(dllexport) inline int ...', etc. + GroupAST::Node winDeclSpec; + parseWinDeclSpec( winDeclSpec ); + + GroupAST::Node funSpec; + bool hasFunSpec = parseFunctionSpecifier( funSpec ); + + GroupAST::Node storageSpec; + bool hasStorageSpec = parseStorageClassSpecifier( storageSpec ); + + if( hasStorageSpec && !hasFunSpec ) + hasFunSpec = parseFunctionSpecifier( funSpec ); + + // that is for the case 'friend __declspec(dllexport) ....' + GroupAST::Node winDeclSpec2; + parseWinDeclSpec( winDeclSpec2 ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + int index = lex->index(); + NameAST::Node name; + if( parseName(name) && lex->lookAhead(0) == '(' ){ + // no type specifier, maybe a constructor or a cast operator?? + + lex->setIndex( index ); + + InitDeclaratorAST::Node declarator; + if( parseInitDeclarator(declarator) ){ + int endSignature = lex->index(); + + switch( lex->lookAhead(0) ){ + case ';': + { + lex->nextToken(); + + InitDeclaratorListAST::Node declarators = CreateNode<InitDeclaratorListAST>(); + + // update declarators position + int startLine, startColumn, endLine, endColumn; + if( declarator.get() ){ + declarator->getStartPosition( &startLine, &startColumn ); + declarator->getEndPosition( &endLine, &endColumn ); + declarators->setStartPosition( startLine, startColumn ); + declarators->setEndPosition( endLine, endColumn ); + } + declarators->addInitDeclarator( declarator ); + + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + ast->setInitDeclaratorList( declarators ); + ast->setText( toString(start, endSignature) ); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + + } + break; + + case ':': + { + AST::Node ctorInit; + StatementListAST::Node funBody; + if( parseCtorInitializer(ctorInit) && parseFunctionBody(funBody) ){ + FunctionDefinitionAST::Node ast = CreateNode<FunctionDefinitionAST>(); + ast->setStorageSpecifier( storageSpec ); + ast->setFunctionSpecifier( funSpec ); + ast->setInitDeclarator( declarator ); + ast->setFunctionBody( funBody ); + ast->setText( toString(start, endSignature) ); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + } + } + break; + + case '{': + { + StatementListAST::Node funBody; + if( parseFunctionBody(funBody) ){ + FunctionDefinitionAST::Node ast = CreateNode<FunctionDefinitionAST>(); + ast->setStorageSpecifier( storageSpec ); + ast->setFunctionSpecifier( funSpec ); + ast->setInitDeclarator( declarator ); + ast->setText( toString(start, endSignature) ); + ast->setFunctionBody( funBody ); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + } + } + break; + + case '(': + case '[': + // ops!! it seems a declarator + goto start_decl; + break; + } + + } + + syntaxError(); + return false; + } + +start_decl: + lex->setIndex( index ); + + if( lex->lookAhead(0) == Token_const && lex->lookAhead(1) == Token_identifier && lex->lookAhead(2) == '=' ){ + // constant definition + lex->nextToken(); + InitDeclaratorListAST::Node declarators; + if( parseInitDeclaratorList(declarators) ){ + ADVANCE( ';', ";" ); + DeclarationAST::Node ast = CreateNode<DeclarationAST>(); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + } + syntaxError(); + return false; + } + + TypeSpecifierAST::Node spec; + if( parseTypeSpecifier(spec) ){ + if ( !hasFunSpec ) + parseFunctionSpecifier( funSpec ); // e.g. "void inline" + spec->setCvQualify( cv ); + + InitDeclaratorListAST::Node declarators; + + InitDeclaratorAST::Node decl; + int startDeclarator = lex->index(); + bool maybeFunctionDefinition = false; + + if( lex->lookAhead(0) != ';' ){ + if( parseInitDeclarator(decl) && lex->lookAhead(0) == '{' ){ + // function definition + maybeFunctionDefinition = true; + } else { + lex->setIndex( startDeclarator ); + if( !parseInitDeclaratorList(declarators) ){ + syntaxError(); + return false; + } + } + } + + int endSignature = lex->index(); + switch( lex->lookAhead(0) ){ + case ';': + { + advanceAndCheckTrailingComment( comment ); + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + ast->setStorageSpecifier( storageSpec ); + ast->setFunctionSpecifier( funSpec ); + ast->setText( toString(start, endSignature) ); + ast->setTypeSpec( spec ); + ast->setWinDeclSpec( winDeclSpec ); + ast->setInitDeclaratorList( declarators ); + node = ast; + UPDATE_POS( node, start, lex->index() ); + } + return true; + + case '{': + { + if( !maybeFunctionDefinition ){ + syntaxError(); + return false; + } + StatementListAST::Node funBody; + if ( parseFunctionBody(funBody) ) { + FunctionDefinitionAST::Node ast = CreateNode<FunctionDefinitionAST>(); + ast->setWinDeclSpec( winDeclSpec ); + ast->setStorageSpecifier( storageSpec ); + ast->setFunctionSpecifier( funSpec ); + ast->setText( toString(start, endSignature) ); + ast->setTypeSpec( spec ); + ast->setFunctionBody( funBody ); + ast->setInitDeclarator( decl ); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + } + } + break; + + } + } + + syntaxError(); + return false; +} + +bool Parser::parseFunctionBody( StatementListAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseFunctionBody()" << endl; + + int start = lex->index(); + if( lex->lookAhead(0) != '{' ){ + return false; + } + lex->nextToken(); + + StatementListAST::Node ast = CreateNode<StatementListAST>(); + + while( !lex->lookAhead(0).isNull() ){ + if( lex->lookAhead(0) == '}' ) + break; + + StatementAST::Node stmt; + int startStmt = lex->index(); + if( !parseStatement(stmt) ){ + if( startStmt == lex->index() ) + lex->nextToken(); + skipUntilStatement(); + } else + ast->addStatement( stmt ); + } + + if( lex->lookAhead(0) != '}' ){ + reportError( i18n("} expected") ); + } else + lex->nextToken(); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +QString Parser::toString( int start, int end, const QString& sep ) const +{ + QStringList l; + + for( int i=start; i<end; ++i ){ + l << lex->tokenAt(i).text(); + } + + return l.join( sep ).stripWhiteSpace(); +} + +bool Parser::parseTypeSpecifierOrClassSpec( TypeSpecifierAST::Node& node ) +{ + if( parseClassSpecifier(node) ) + return true; + else if( parseEnumSpecifier(node) ) + return true; + else if( parseTypeSpecifier(node) ) + return true; + + return false; +} + +bool Parser::parseTryBlockStatement( StatementAST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTryBlockStatement()" << endl; + + if( lex->lookAhead(0) != Token_try ){ + return false; + } + lex->nextToken(); + + StatementAST::Node stmt; + if( !parseCompoundStatement(stmt) ){ + syntaxError(); + return false; + } + + if( lex->lookAhead(0) != Token_catch ){ + reportError( i18n("catch expected") ); + return false; + } + + while( lex->lookAhead(0) == Token_catch ){ + lex->nextToken(); + ADVANCE( '(', "(" ); + ConditionAST::Node cond; + if( !parseCondition(cond) ){ + reportError( i18n("condition expected") ); + return false; + } + ADVANCE( ')', ")" ); + + StatementAST::Node body; + if( !parseCompoundStatement(body) ){ + syntaxError(); + return false; + } + } + + node = stmt; + return true; +} + +bool Parser::parsePrimaryExpression( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parsePrimarExpression()" << endl; + + + switch( lex->lookAhead(0) ){ + case Token_string_literal: + { + AST::Node lit; + parseStringLiteral( lit ); + } + return true; + + case Token_number_literal: + case Token_char_literal: + case Token_true: + case Token_false: + lex->nextToken(); + return true; + + case Token_this: + lex->nextToken(); + return true; + + case Token_dynamic_cast: + case Token_static_cast: + case Token_reinterpret_cast: + case Token_const_cast: + { + lex->nextToken(); + + CHECK( '<', "<" ); + AST::Node typeId; + parseTypeId( typeId ); + CHECK( '>', ">" ); + + CHECK( '(', "(" ); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + } + return true; + + case Token_typeid: + { + lex->nextToken(); + CHECK( '(', "(" ); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + } + return true; + + case '(': + { + lex->nextToken(); + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "token = " << lex->lookAhead(0).text() << endl; + AST::Node expr; + if( !parseExpression(expr) ){ + return false; + } + CHECK( ')', ")" ); + } + return true; + + default: + { + int start = lex->index(); + TypeSpecifierAST::Node typeSpec; + if( parseSimpleTypeSpecifier(typeSpec) && lex->lookAhead(0) == '(' ){ + lex->nextToken(); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + return true; + } + + lex->setIndex( start ); + NameAST::Node name; + if( parseName(name) ) + return true; + } + } + + return false; +} + +bool Parser::parsePostfixExpression( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parsePostfixExpression()" << endl; + + AST::Node expr; + if( !parsePrimaryExpression(expr) ) + return false; + + while( true ){ + switch(lex->lookAhead(0)) + { + case '[': + { + lex->nextToken(); + AST::Node e; + parseCommaExpression( e ); + CHECK( ']', "]" ); + } + break; + + case '(': + { + lex->nextToken(); + AST::Node funArgs; + parseCommaExpression( funArgs ); + CHECK( ')', ")" ); + } + break; + + case Token_incr: + case Token_decr: + lex->nextToken(); + break; + + case '.': + case Token_arrow: + { + lex->nextToken(); + if( lex->lookAhead(0) == Token_template ) + lex->nextToken(); + + NameAST::Node name; + if( !parseName(name) ){ + return false; + } + } + break; + + case Token_typename: + { + lex->nextToken(); + + NameAST::Node name; + if( !parseName(name) ){ + return false; + } + + CHECK( '(', "(" ); + AST::Node expr; + parseCommaExpression(expr); + CHECK( ')', ")" ); + } + return true; + + default: + return true; + + } // end switch + + } // end while + + return true; +} + +bool Parser::parseUnaryExpression( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseUnaryExpression()" << endl; + + switch( lex->lookAhead(0) ){ + case Token_incr: + case Token_decr: + case '*': + case '&': + case '+': + case '-': + case '!': + case '~': + { + lex->nextToken(); + AST::Node expr; + return parseCastExpression( expr ); + } + + case Token_sizeof: + { + lex->nextToken(); + int index = lex->index(); + if( lex->lookAhead(0) == '(' ){ + lex->nextToken(); + AST::Node typeId; + if( parseTypeId(typeId) && lex->lookAhead(0) == ')' ){ + lex->nextToken(); + return true; + } + lex->setIndex( index ); + } + AST::Node expr; + return parseUnaryExpression( expr ); + } + + case Token_new: + return parseNewExpression( node ); + + case Token_delete: + return parseDeleteExpression( node ); + } + + return parsePostfixExpression( node ); +} + +bool Parser::parseNewExpression( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNewExpression()" << endl; + if( lex->lookAhead(0) == Token_scope && lex->lookAhead(1) == Token_new ) + lex->nextToken(); + + CHECK( Token_new, "new"); + + if( lex->lookAhead(0) == '(' ){ + lex->nextToken(); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + } + + if( lex->lookAhead(0) == '(' ){ + lex->nextToken(); + AST::Node typeId; + parseTypeId( typeId ); + CHECK( ')', ")" ); + } else { + AST::Node typeId; + parseNewTypeId( typeId ); + } + + AST::Node init; + parseNewInitializer( init ); + return true; +} + +bool Parser::parseNewTypeId( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNewTypeId()" << endl; + TypeSpecifierAST::Node typeSpec; + if( parseTypeSpecifier(typeSpec) ){ + AST::Node declarator; + parseNewDeclarator( declarator ); + return true; + } + + return false; +} + +bool Parser::parseNewDeclarator( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNewDeclarator()" << endl; + AST::Node ptrOp; + if( parsePtrOperator(ptrOp) ){ + AST::Node declarator; + parseNewDeclarator( declarator ); + return true; + } + + if( lex->lookAhead(0) == '[' ){ + while( lex->lookAhead(0) == '[' ){ + lex->nextToken(); + AST::Node expr; + parseExpression( expr ); + ADVANCE( ']', "]" ); + } + return true; + } + + return false; +} + +bool Parser::parseNewInitializer( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNewInitializer()" << endl; + if( lex->lookAhead(0) != '(' ) + return false; + + lex->nextToken(); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + + return true; +} + +bool Parser::parseDeleteExpression( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeleteExpression()" << endl; + if( lex->lookAhead(0) == Token_scope && lex->lookAhead(1) == Token_delete ) + lex->nextToken(); + + CHECK( Token_delete, "delete" ); + + if( lex->lookAhead(0) == '[' ){ + lex->nextToken(); + CHECK( ']', "]" ); + } + + AST::Node expr; + return parseCastExpression( expr ); +} + +bool Parser::parseCastExpression( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCastExpression()" << endl; + + int index = lex->index(); + + if( lex->lookAhead(0) == '(' ){ + lex->nextToken(); + AST::Node typeId; + if ( parseTypeId(typeId) ) { + if ( lex->lookAhead(0) == ')' ) { + lex->nextToken(); + AST::Node expr; + if( parseCastExpression(expr) ) + return true; + } + } + } + + lex->setIndex( index ); + + AST::Node expr; + return parseUnaryExpression( expr ); +} + +bool Parser::parsePmExpression( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser:parsePmExpression()" << endl; + AST::Node expr; + if( !parseCastExpression(expr) ) + return false; + + while( lex->lookAhead(0) == Token_ptrmem ){ + lex->nextToken(); + + if( !parseCastExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseMultiplicativeExpression( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMultiplicativeExpression()" << endl; + AST::Node expr; + if( !parsePmExpression(expr) ) + return false; + + while( lex->lookAhead(0) == '*' || lex->lookAhead(0) == '/' || lex->lookAhead(0) == '%' ){ + lex->nextToken(); + + if( !parsePmExpression(expr) ) + return false; + } + + return true; +} + + +bool Parser::parseAdditiveExpression( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAdditiveExpression()" << endl; + AST::Node expr; + if( !parseMultiplicativeExpression(expr) ) + return false; + + while( lex->lookAhead(0) == '+' || lex->lookAhead(0) == '-' ){ + lex->nextToken(); + + if( !parseMultiplicativeExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseShiftExpression( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseShiftExpression()" << endl; + AST::Node expr; + if( !parseAdditiveExpression(expr) ) + return false; + + while( lex->lookAhead(0) == Token_shift ){ + lex->nextToken(); + + if( !parseAdditiveExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseRelationalExpression( AST::Node& /*node*/, bool templArgs ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseRelationalExpression()" << endl; + AST::Node expr; + if( !parseShiftExpression(expr) ) + return false; + + while( lex->lookAhead(0) == '<' || (lex->lookAhead(0) == '>' && !templArgs) || + lex->lookAhead(0) == Token_leq || lex->lookAhead(0) == Token_geq ){ + lex->nextToken(); + + if( !parseShiftExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseEqualityExpression( AST::Node& /*node*/, bool templArgs ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseEqualityExpression()" << endl; + AST::Node expr; + if( !parseRelationalExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == Token_eq || lex->lookAhead(0) == Token_not_eq ){ + lex->nextToken(); + + if( !parseRelationalExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseAndExpression( AST::Node& /*node*/, bool templArgs ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAndExpression()" << endl; + AST::Node expr; + if( !parseEqualityExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == '&' ){ + lex->nextToken(); + + if( !parseEqualityExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseExclusiveOrExpression( AST::Node& /*node*/, bool templArgs ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseExclusiveOrExpression()" << endl; + AST::Node expr; + if( !parseAndExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == '^' ){ + lex->nextToken(); + + if( !parseAndExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseInclusiveOrExpression( AST::Node& /*node*/, bool templArgs ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInclusiveOrExpression()" << endl; + AST::Node expr; + if( !parseExclusiveOrExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == '|' ){ + lex->nextToken(); + + if( !parseExclusiveOrExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseLogicalAndExpression( AST::Node& /*node*/, bool templArgs ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLogicalAndExpression()" << endl; + + AST::Node expr; + if( !parseInclusiveOrExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == Token_and ){ + lex->nextToken(); + + if( !parseInclusiveOrExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseLogicalOrExpression( AST::Node& node, bool templArgs ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLogicalOrExpression()" << endl; + + int start = lex->index(); + + AST::Node expr; + if( !parseLogicalAndExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == Token_or ){ + lex->nextToken(); + + if( !parseLogicalAndExpression(expr, templArgs) ) + return false; + } + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseConditionalExpression( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseConditionalExpression()" << endl; + AST::Node expr; + if( !parseLogicalOrExpression(expr) ) + return false; + + if( lex->lookAhead(0) == '?' ){ + lex->nextToken(); + + if( !parseExpression(expr) ) + return false; + + CHECK( ':', ":" ); + + if( !parseAssignmentExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseAssignmentExpression( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAssignmentExpression()" << endl; + int start = lex->index(); + AST::Node expr; + if( lex->lookAhead(0) == Token_throw && !parseThrowExpression(expr) ) + return false; + else if( !parseConditionalExpression(expr) ) + return false; + + while( lex->lookAhead(0) == Token_assign || lex->lookAhead(0) == '=' ){ + lex->nextToken(); + + if( !parseConditionalExpression(expr) ) + return false; + } + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseConstantExpression( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseConstantExpression()" << endl; + int start = lex->index(); + if( parseConditionalExpression(node) ){ + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; + } + return false; +} + +bool Parser::parseExpression( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseExpression()" << endl; + + int start = lex->index(); + + if( !parseCommaExpression(node) ) + return false; + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseCommaExpression( AST::Node& node ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCommaExpression()" << endl; + int start = lex->index(); + + AST::Node expr; + if( !parseAssignmentExpression(expr) ) + return false; + + QString comment; + while( lex->lookAhead(0) == ',' ){ + comment = QString::null; + advanceAndCheckTrailingComment( comment ); + + if( !parseAssignmentExpression(expr) ) + return false; + if (!comment.isEmpty()) + expr->setComment(comment); + } + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseThrowExpression( AST::Node& /*node*/ ) +{ + //kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseThrowExpression()" << endl; + if( lex->lookAhead(0) != Token_throw ) + return false; + + CHECK( Token_throw, "throw" ); + AST::Node expr; + if( !parseAssignmentExpression(expr) ) + return false; + + return true; +} + +bool Parser::parseIvarDeclList( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIvarDecls( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIvarDecl( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIvars( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIvarDeclarator( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseMethodDecl( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseUnarySelector( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordSelector( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseSelector( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordDecl( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseReceiver( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcMessageExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseMessageArgs( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordArgList( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordArg( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseReservedWord( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseMyParms( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseMyParm( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseOptParmList( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcSelectorExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseSelectorArg( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordNameList( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordName( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcEncodeExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcString( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseProtocolRefs( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIdentifierList( GroupAST::Node & node ) +{ + int start = lex->index(); + + if( lex->lookAhead(0) != Token_identifier ) + return false; + + GroupAST::Node ast = CreateNode<GroupAST>(); + + AST_FROM_TOKEN( tk, lex->index() ); + ast->addNode( tk ); + lex->nextToken(); + + QString comment; + while( lex->lookAhead(0) == ',' ){ + comment = QString::null; + advanceAndCheckTrailingComment( comment ); + if( lex->lookAhead(0) == Token_identifier ){ + AST_FROM_TOKEN( tk, lex->index() ); + ast->addNode( tk ); + lex->nextToken(); + } + ADVANCE( Token_identifier, "identifier" ); + } + + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; +} + +bool Parser::parseIdentifierColon( AST::Node & node ) +{ + Q_UNUSED( node ); + + if( lex->lookAhead(0) == Token_identifier && lex->lookAhead(1) == ':' ){ + lex->nextToken(); + lex->nextToken(); + return true; + } // ### else if PTYPENAME -> return true ; + + return false; +} + +bool Parser::parseObjcProtocolExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcOpenBracketExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcCloseBracket( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcDef( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcClassDef( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcClassDecl( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + + ADVANCE( OBJC_CLASS, "@class" ); + + GroupAST::Node idList; + parseIdentifierList( idList ); + ADVANCE( ';', ";" ); + + return true; +} + +bool Parser::parseObjcProtocolDecl( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + + ADVANCE( OBJC_PROTOCOL, "@protocol" ); + + GroupAST::Node idList; + parseIdentifierList( idList ); + ADVANCE( ';', ";" ); + + return true; +} + +bool Parser::parseObjcAliasDecl( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + + ADVANCE( OBJC_ALIAS, "@alias" ); + + GroupAST::Node idList; + parseIdentifierList( idList ); + ADVANCE( ';', ";" ); + + return true; +} + +bool Parser::parseObjcProtocolDef( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcMethodDef( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseWinDeclSpec( GroupAST::Node & node ) +{ + if( lex->lookAhead(0) == Token_identifier && lex->lookAhead(0).text() == "__declspec" && lex->lookAhead(1) == '(' ){ + int start = lex->index(); + lex->nextToken(); + lex->nextToken(); // skip '(' + + parseIdentifierList( node ); + ADVANCE( ')', ")" ); + + UPDATE_POS( node, start, lex->index() ); + return true; + } + + return false; +} + diff --git a/umbrello/umbrello/codeimport/kdevcppparser/parser.h b/umbrello/umbrello/codeimport/kdevcppparser/parser.h new file mode 100644 index 00000000..611ceb14 --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/parser.h @@ -0,0 +1,221 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef PARSER_H +#define PARSER_H + +#include "ast.h" + +#include <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> +#include <qvaluestack.h> + +struct ParserPrivateData; + +class Driver; +class Lexer; +class Token; +struct Error; + +class Parser +{ +public: + Parser( Driver* driver, Lexer* lexer ); + virtual ~Parser(); + +private: + virtual bool reportError( const Error& err ); + /** @todo remove*/ virtual bool reportError( const QString& msg ); + /** @todo remove*/ virtual void syntaxError(); + +public: /*rules*/ + + bool parseTranslationUnit( TranslationUnitAST::Node& node ); + + bool parseDeclaration( DeclarationAST::Node& node ); + bool parseBlockDeclaration( DeclarationAST::Node& node ); + bool parseLinkageSpecification( DeclarationAST::Node& node ); + bool parseLinkageBody( LinkageBodyAST::Node& node ); + bool parseNamespace( DeclarationAST::Node& node ); + bool parseNamespaceAliasDefinition( DeclarationAST::Node& node ); + bool parseUsing( DeclarationAST::Node& node ); + bool parseUsingDirective( DeclarationAST::Node& node ); + bool parseTypedef( DeclarationAST::Node& node ); + bool parseAsmDefinition( DeclarationAST::Node& node ); + bool parseTemplateDeclaration( DeclarationAST::Node& node ); + bool parseDeclarationInternal( DeclarationAST::Node& node, QString& comment ); + + bool parseUnqualifiedName( ClassOrNamespaceNameAST::Node& node ); + bool parseStringLiteral( AST::Node& node ); + bool parseName( NameAST::Node& node ); + bool parseOperatorFunctionId( AST::Node& node ); + bool parseTemplateArgumentList( TemplateArgumentListAST::Node& node, bool reportError=true ); + bool parseOperator( AST::Node& node ); + bool parseCvQualify( GroupAST::Node& node ); + bool parseSimpleTypeSpecifier( TypeSpecifierAST::Node& node ); + bool parsePtrOperator( AST::Node& node ); + bool parseTemplateArgument( AST::Node& node ); + bool parseTypeSpecifier( TypeSpecifierAST::Node& node ); + bool parseTypeSpecifierOrClassSpec( TypeSpecifierAST::Node& node ); + bool parseDeclarator( DeclaratorAST::Node& node ); + bool parseTemplateParameterList( TemplateParameterListAST::Node& node ); + bool parseTemplateParameter( TemplateParameterAST::Node& node ); + bool parseStorageClassSpecifier( GroupAST::Node& node ); + bool parseFunctionSpecifier( GroupAST::Node& node ); + bool parseInitDeclaratorList( InitDeclaratorListAST::Node& node ); + bool parseInitDeclarator( InitDeclaratorAST::Node& node ); + bool parseParameterDeclarationClause( ParameterDeclarationClauseAST::Node& node ); + bool parseCtorInitializer( AST::Node& node ); + bool parsePtrToMember( AST::Node& node ); + bool parseEnumSpecifier( TypeSpecifierAST::Node& node ); + bool parseClassSpecifier( TypeSpecifierAST::Node& node ); + bool parseWinDeclSpec( GroupAST::Node& node ); + bool parseElaboratedTypeSpecifier( TypeSpecifierAST::Node& node ); + bool parseDeclaratorId( NameAST::Node& node ); + bool parseExceptionSpecification( GroupAST::Node& node ); + bool parseEnumerator( EnumeratorAST::Node& node ); + bool parseTypeParameter( TypeParameterAST::Node& node ); + bool parseParameterDeclaration( ParameterDeclarationAST::Node& node ); + bool parseTypeId( AST::Node& node ); + bool parseAbstractDeclarator( DeclaratorAST::Node& node ); + bool parseParameterDeclarationList( ParameterDeclarationListAST::Node& node ); + bool parseMemberSpecification( DeclarationAST::Node& node ); + bool parseAccessSpecifier( AST::Node& node ); + bool parseTypeIdList( GroupAST::Node& node ); + bool parseMemInitializerList( AST::Node& node ); + bool parseMemInitializer( AST::Node& node ); + bool parseInitializer( AST::Node& node ); + bool parseBaseClause( BaseClauseAST::Node& node ); + bool parseBaseSpecifier( BaseSpecifierAST::Node& node ); + bool parseInitializerClause( AST::Node& node ); + bool parseMemInitializerId( NameAST::Node& node ); + bool parseFunctionBody( StatementListAST::Node& node ); + + // expression + bool skipExpression( AST::Node& node ); + bool skipCommaExpression( AST::Node& node ); + bool skipExpressionStatement( StatementAST::Node& node ); + + bool parseExpression( AST::Node& node ); + bool parsePrimaryExpression( AST::Node& node ); + bool parsePostfixExpression( AST::Node& node ); + bool parseUnaryExpression( AST::Node& node ); + bool parseNewExpression( AST::Node& node ); + bool parseNewTypeId( AST::Node& node ); + bool parseNewDeclarator( AST::Node& node ); + bool parseNewInitializer( AST::Node& node ); + bool parseDeleteExpression( AST::Node& node ); + bool parseCastExpression( AST::Node& node ); + bool parsePmExpression( AST::Node& node ); + bool parseMultiplicativeExpression( AST::Node& node ); + bool parseAdditiveExpression( AST::Node& node ); + bool parseShiftExpression( AST::Node& node ); + bool parseRelationalExpression( AST::Node& node, bool templArgs=false ); + bool parseEqualityExpression( AST::Node& node, bool templArgs=false ); + bool parseAndExpression( AST::Node& node, bool templArgs=false ); + bool parseExclusiveOrExpression( AST::Node& node, bool templArgs=false ); + bool parseInclusiveOrExpression( AST::Node& node, bool templArgs=false ); + bool parseLogicalAndExpression( AST::Node& node, bool templArgs=false ); + bool parseLogicalOrExpression( AST::Node& node, bool templArgs=false ); + bool parseConditionalExpression( AST::Node& node ); + bool parseAssignmentExpression( AST::Node& node ); + bool parseConstantExpression( AST::Node& node ); + bool parseCommaExpression( AST::Node& node ); + bool parseThrowExpression( AST::Node& node ); + + // statement + bool parseCondition( ConditionAST::Node& node ); + bool parseStatement( StatementAST::Node& node ); + bool parseWhileStatement( StatementAST::Node& node ); + bool parseDoStatement( StatementAST::Node& node ); + bool parseForStatement( StatementAST::Node& node ); + bool parseCompoundStatement( StatementAST::Node& node ); + bool parseForInitStatement( StatementAST::Node& node ); + bool parseIfStatement( StatementAST::Node& node ); + bool parseSwitchStatement( StatementAST::Node& node ); + bool parseLabeledStatement( StatementAST::Node& node ); + bool parseDeclarationStatement( StatementAST::Node& node ); + bool parseTryBlockStatement( StatementAST::Node& node ); + + // objective c + bool parseObjcDef( DeclarationAST::Node& node ); + bool parseObjcClassDef( DeclarationAST::Node& node ); + bool parseObjcClassDecl( DeclarationAST::Node& node ); + bool parseObjcProtocolDecl( DeclarationAST::Node& node ); + bool parseObjcAliasDecl( DeclarationAST::Node& node ); + bool parseObjcProtocolDef( DeclarationAST::Node& node ); + bool parseObjcMethodDef( DeclarationAST::Node& node ); + + bool parseIvarDeclList( AST::Node& node ); + bool parseIvarDecls( AST::Node& node ); + bool parseIvarDecl( AST::Node& node ); + bool parseIvars( AST::Node& node ); + bool parseIvarDeclarator( AST::Node& node ); + bool parseMethodDecl( AST::Node& node ); + bool parseUnarySelector( AST::Node& node ); + bool parseKeywordSelector( AST::Node& node ); + bool parseSelector( AST::Node& node ); + bool parseKeywordDecl( AST::Node& node ); + bool parseReceiver( AST::Node& node ); + bool parseObjcMessageExpr( AST::Node& node ); + bool parseMessageArgs( AST::Node& node ); + bool parseKeywordExpr( AST::Node& node ); + bool parseKeywordArgList( AST::Node& node ); + bool parseKeywordArg( AST::Node& node ); + bool parseReservedWord( AST::Node& node ); + bool parseMyParms( AST::Node& node ); + bool parseMyParm( AST::Node& node ); + bool parseOptParmList( AST::Node& node ); + bool parseObjcSelectorExpr( AST::Node& node ); + bool parseSelectorArg( AST::Node& node ); + bool parseKeywordNameList( AST::Node& node ); + bool parseKeywordName( AST::Node& node ); + bool parseObjcEncodeExpr( AST::Node& node ); + bool parseObjcString( AST::Node& node ); + bool parseProtocolRefs( AST::Node& node ); + bool parseIdentifierList( GroupAST::Node& node ); + bool parseIdentifierColon( AST::Node& node ); + bool parseObjcProtocolExpr( AST::Node& node ); + bool parseObjcOpenBracketExpr( AST::Node& node ); + bool parseObjcCloseBracket( AST::Node& node ); + + void advanceAndCheckTrailingComment(QString& comment); + + bool skipUntil( int token ); + bool skipUntilDeclaration(); + bool skipUntilStatement(); + bool skip( int l, int r ); + QString toString( int start, int end, const QString& sep=" " ) const; + +private: + ParserPrivateData* d; + Driver* m_driver; + Lexer* lex; + int m_problems; + int m_maxProblems; + bool objcp; + +private: + Parser( const Parser& source ); + void operator = ( const Parser& source ); +}; + + +#endif diff --git a/umbrello/umbrello/codeimport/kdevcppparser/tree_parser.cpp b/umbrello/umbrello/codeimport/kdevcppparser/tree_parser.cpp new file mode 100644 index 00000000..7f9210e2 --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/tree_parser.cpp @@ -0,0 +1,207 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "tree_parser.h" +#include <kdebug.h> + +TreeParser::TreeParser() +{ +} + +TreeParser::~TreeParser() +{ +} + +void TreeParser::parseTranslationUnit( TranslationUnitAST* translationUnit ) +{ + //kdDebug(9007) << "TreeParser::parseTranslationUnit()" << endl; + + QPtrList<DeclarationAST> declarations = translationUnit->declarationList(); + QPtrListIterator<DeclarationAST> it( declarations ); + while( it.current() ){ + parseDeclaration( it.current() ); + ++it; + } +} + +void TreeParser::parseDeclaration( DeclarationAST* declaration ) +{ + //kdDebug(9007) << "TreeParser::parseDeclaration()" << endl; + + if( !declaration ) + return; + + switch( declaration->nodeType() ) + { + case NodeType_LinkageSpecification: + parseLinkageSpecification( static_cast<LinkageSpecificationAST*>(declaration) ); + break; + + case NodeType_Namespace: + parseNamespace( static_cast<NamespaceAST*>(declaration) ); + break; + + case NodeType_NamespaceAlias: + parseNamespaceAlias( static_cast<NamespaceAliasAST*>(declaration) ); + break; + + case NodeType_Using: + parseUsing( static_cast<UsingAST*>(declaration) ); + break; + + case NodeType_UsingDirective: + parseUsingDirective( static_cast<UsingDirectiveAST*>(declaration) ); + break; + + case NodeType_Typedef: + parseTypedef( static_cast<TypedefAST*>(declaration) ); + break; + + case NodeType_TemplateDeclaration: + parseTemplateDeclaration( static_cast<TemplateDeclarationAST*>(declaration) ); + break; + + case NodeType_SimpleDeclaration: + parseSimpleDeclaration( static_cast<SimpleDeclarationAST*>(declaration) ); + break; + + case NodeType_FunctionDefinition: + parseFunctionDefinition( static_cast<FunctionDefinitionAST*>(declaration) ); + break; + + case NodeType_AccessDeclaration: + parseAccessDeclaration( static_cast<AccessDeclarationAST*>(declaration) ); + break; + } +} + +void TreeParser::parseLinkageSpecification( LinkageSpecificationAST* ast ) +{ + //kdDebug(9007) << "TreeParser::parseLinkageSpecification()" << endl; + if( ast->linkageBody() ) + parseLinkageBody( ast->linkageBody() ); + else if( ast->declaration() ) + parseDeclaration( ast->declaration() ); +} + +void TreeParser::parseNamespace( NamespaceAST* decl ) +{ + //kdDebug(9007) << "TreeParser::parseNamespace()" << endl; + if( decl->linkageBody() ) + parseLinkageBody( decl->linkageBody() ); +} + +void TreeParser::parseNamespaceAlias( NamespaceAliasAST* decl ) +{ + //kdDebug(9007) << "TreeParser::parseNamespaceAlias()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseUsing( UsingAST* decl ) +{ + //kdDebug(9007) << "TreeParser::parseUsing()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseUsingDirective( UsingDirectiveAST* decl ) +{ + //kdDebug(9007) << "TreeParser::parseUsingDirective()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseTypedef( TypedefAST* decl ) +{ + //kdDebug(9007) << "TreeParser::parseTypedef()" << endl; + if( decl->typeSpec() ) + parseTypeSpecifier( decl->typeSpec() ); +} + +void TreeParser::parseTemplateDeclaration( TemplateDeclarationAST* decl ) +{ + //kdDebug(9007) << "TreeParser::parseTemplateDeclaration()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseSimpleDeclaration( SimpleDeclarationAST* decl ) +{ + //kdDebug(9007) << "TreeParser::parseSimpleDeclaration()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseFunctionDefinition( FunctionDefinitionAST* def ) +{ + //kdDebug(9007) << "TreeParser::parseFunctionDefinition()" << endl; + Q_UNUSED( def ); +} + +void TreeParser::parseLinkageBody( LinkageBodyAST* linkageBody ) +{ + //kdDebug(9007) << "TreeParser::parseLinkageBody()" << endl; + QPtrList<DeclarationAST> declarations = linkageBody->declarationList(); + for( QPtrListIterator<DeclarationAST> it(declarations); it.current(); ++it ){ + parseDeclaration( it.current() ); + } +} + +void TreeParser::parseTypeSpecifier( TypeSpecifierAST* typeSpec ) +{ + //kdDebug(9007) << "TreeParser::parseTypeSpecifier()" << endl; + switch( typeSpec->nodeType() ) + { + case NodeType_ClassSpecifier: + parseClassSpecifier( static_cast<ClassSpecifierAST*>(typeSpec) ); + break; + + case NodeType_EnumSpecifier: + parseEnumSpecifier( static_cast<EnumSpecifierAST*>(typeSpec) ); + break; + + case NodeType_ElaboratedTypeSpecifier: + parseElaboratedTypeSpecifier( static_cast<ElaboratedTypeSpecifierAST*>(typeSpec) ); + break; + } +} + +void TreeParser::parseClassSpecifier( ClassSpecifierAST* classSpec ) +{ + //kdDebug(9007) << "TreeParser::parseClassSpecifier()" << endl; + QPtrList<DeclarationAST> declarations = classSpec->declarationList(); + for( QPtrListIterator<DeclarationAST> it(declarations); it.current(); ++it ){ + parseDeclaration( it.current() ); + } +} + +void TreeParser::parseEnumSpecifier( EnumSpecifierAST* enumSpec ) +{ + //kdDebug(9007) << "TreeParser::parseEnumSpecifier()" << endl; + Q_UNUSED( enumSpec ); +} + +void TreeParser::parseElaboratedTypeSpecifier( ElaboratedTypeSpecifierAST* typeSpec ) +{ + //kdDebug(9007) << "TreeParser::parseElaboratedTypeSpecifier()" << endl; + Q_UNUSED( typeSpec ); +} + +void TreeParser::parseAccessDeclaration ( AccessDeclarationAST * access ) +{ + //kdDebug(9007) << "TreeParser::parseAccessDeclaration()" << endl; + Q_UNUSED( access ); +} + diff --git a/umbrello/umbrello/codeimport/kdevcppparser/tree_parser.h b/umbrello/umbrello/codeimport/kdevcppparser/tree_parser.h new file mode 100644 index 00000000..42059408 --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/tree_parser.h @@ -0,0 +1,59 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef __tree_parser_h +#define __tree_parser_h + +#include "ast.h" + +class TreeParser +{ +public: + TreeParser(); + virtual ~TreeParser(); + + // translation-unit + virtual void parseTranslationUnit( TranslationUnitAST* ); + + // declarations + virtual void parseDeclaration( DeclarationAST* ); + virtual void parseLinkageSpecification( LinkageSpecificationAST* ); + virtual void parseNamespace( NamespaceAST* ); + virtual void parseNamespaceAlias( NamespaceAliasAST* ); + virtual void parseUsing( UsingAST* ); + virtual void parseUsingDirective( UsingDirectiveAST* ); + virtual void parseTypedef( TypedefAST* ); + virtual void parseTemplateDeclaration( TemplateDeclarationAST* ); + virtual void parseSimpleDeclaration( SimpleDeclarationAST* ); + virtual void parseFunctionDefinition( FunctionDefinitionAST* ); + virtual void parseLinkageBody( LinkageBodyAST* ); + virtual void parseAccessDeclaration( AccessDeclarationAST* ); + + // type-specifier + virtual void parseTypeSpecifier( TypeSpecifierAST* ); + virtual void parseClassSpecifier( ClassSpecifierAST* ); + virtual void parseEnumSpecifier( EnumSpecifierAST* ); + virtual void parseElaboratedTypeSpecifier( ElaboratedTypeSpecifierAST* ); + +private: + TreeParser( const TreeParser& source ); + void operator = ( const TreeParser& source ); +}; + +#endif // __tree_parser_h diff --git a/umbrello/umbrello/codeimport/kdevcppparser/urlutil.cpp b/umbrello/umbrello/codeimport/kdevcppparser/urlutil.cpp new file mode 100644 index 00000000..a2fd29dd --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/urlutil.cpp @@ -0,0 +1,310 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Julian Rockey <linux@jrockey.com> + Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net> + Copyright (C) 2003 Mario Scalas <mario.scalas@libero.it> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#include "urlutil.h" + +#include <qstringlist.h> + +#include <qdir.h> +#include <qfileinfo.h> +#include <kdebug.h> + +#include <unistd.h> +#include <limits.h> +#include <stdlib.h> + +#include <kdeversion.h> +#if (KDE_VERSION_MINOR==0) && (KDE_VERSION_MAJOR==3) +#include <kdevkurl.h> +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Namespace URLUtil +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::filename(const QString & name) { + int slashPos = name.findRev("/"); + return slashPos<0 ? name : name.mid(slashPos+1); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::directory(const QString & name) { + int slashPos = name.findRev("/"); + return slashPos<0 ? QString("") : name.left(slashPos); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::relativePath(const KURL & parent, const KURL & child, uint slashPolicy) { + bool slashPrefix = slashPolicy & SLASH_PREFIX; + bool slashSuffix = slashPolicy & SLASH_SUFFIX; + if (parent == child) + return slashPrefix ? QString("/") : QString(""); + + if (!parent.isParentOf(child)) return QString(); + int a=slashPrefix ? -1 : 1; + int b=slashSuffix ? 1 : -1; + return child.path(b).mid(parent.path(a).length()); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::relativePath(const QString & parent, const QString & child, uint slashPolicy) { + return relativePath(KURL(parent), KURL(child), slashPolicy); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::upDir(const QString & path, bool slashSuffix) { + int slashPos = path.findRev("/"); + if (slashPos<1) return QString::null; + return path.mid(0,slashPos+ (slashSuffix ? 1 : 0) ); +} + +/////////////////////////////////////////////////////////////////////////////// + +KURL URLUtil::mergeURL(const KURL & source, const KURL & dest, const KURL & child) { + + // if already a child of source, then fine + if (source.isParentOf(child) || source == child) return child; + + // if not a child of dest, return blank URL (error) + if (!dest.isParentOf(child) && dest != child) return KURL(); + + // if child is same as dest, return source + if (dest == child) return source; + + // calculate + QString childUrlStr = child.url(-1); + QString destStemStr = dest.url(1); + QString sourceStemStr = source.url(1); + return KURL(sourceStemStr.append( childUrlStr.mid( destStemStr.length() ) ) ); + +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::getExtension(const QString & path) { + int dotPos = path.findRev('.'); + if (dotPos<0) return QString(""); + return path.mid(dotPos+1); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::extractPathNameRelative(const KURL &baseDirUrl, const KURL &url ) +{ + QString absBase = extractPathNameAbsolute( baseDirUrl ), + absRef = extractPathNameAbsolute( url ); + int i = absRef.find( absBase, 0, true ); + + if (i == -1) + return QString(); + + if (absRef == absBase) + return QString( "." ); + else + return absRef.replace( 0, absBase.length(), QString() ); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::extractPathNameRelative(const QString &basePath, const KURL &url ) +{ +#if (KDE_VERSION_MINOR!=0) || (KDE_VERSION_MAJOR!=3) + KURL baseDirUrl = KURL::fromPathOrURL( basePath ); +#else + KURL baseDirUrl = KdevKURL::fromPathOrURL( basePath ); +#endif + return extractPathNameRelative( baseDirUrl, url ); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::extractPathNameRelative(const QString &basePath, const QString &absFilePath ) +{ +#if (KDE_VERSION_MINOR!=0) || (KDE_VERSION_MAJOR!=3) + KURL baseDirUrl = KURL::fromPathOrURL( basePath ), + fileUrl = KURL::fromPathOrURL( absFilePath ); +#else + KURL baseDirUrl = KdevKURL::fromPathOrURL( basePath ), + fileUrl = KdevKURL::fromPathOrURL( absFilePath ); +#endif + return extractPathNameRelative( baseDirUrl, fileUrl ); +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::extractPathNameAbsolute( const KURL &url ) +{ + if (isDirectory( url )) + return url.path( +1 ); // with trailing "/" if none is present + else + { + // Ok, this is an over-tight pre-condition on "url" since I hope nobody will never + // stress this function with absurd cases ... but who knows? + /* + QString path = url.path(); + QFileInfo fi( path ); // Argh: QFileInfo is back ;)) + return ( fi.exists()? path : QString() ); + */ + return url.path(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool URLUtil::isDirectory( const KURL &url ) +{ + return isDirectory( url.path() ); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool URLUtil::isDirectory( const QString &absFilePath ) +{ + return QDir( absFilePath ).exists(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void URLUtil::dump( const KURL::List &urls, const QString &aMessage ) +{ + if (!aMessage.isNull()) + { + kdDebug(9000) << aMessage << endl; + } + kdDebug(9000) << " List has " << urls.count() << " elements." << endl; + + for (size_t i = 0; i<urls.count(); ++i) + { + KURL url = urls[ i ]; +// kdDebug(9000) << " * Element = " << url.path() << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +QStringList URLUtil::toRelativePaths( const QString &baseDir, const KURL::List &urls) +{ + QStringList paths; + + for (size_t i=0; i<urls.count(); ++i) + { + paths << extractPathNameRelative( baseDir, urls[i] ); + } + + return paths; +} + +/////////////////////////////////////////////////////////////////////////////// + +QString URLUtil::relativePathToFile( const QString & dirUrl, const QString & fileUrl ) +{ + if (dirUrl.isEmpty() || (dirUrl == "/")) + return fileUrl; + + QStringList dir = QStringList::split("/", dirUrl, false); + QStringList file = QStringList::split("/", fileUrl, false); + + QString resFileName = file.last(); + file.remove(file.last()); + + uint i = 0; + while ( (i < dir.count()) && (i < (file.count())) && (dir[i] == file[i]) ) + i++; + + QString result_up; + QString result_down; + QString currDir; + QString currFile; + do + { + i >= dir.count() ? currDir = "" : currDir = dir[i]; + i >= file.count() ? currFile = "" : currFile = file[i]; + qWarning("i = %d, currDir = %s, currFile = %s", i, currDir.latin1(), currFile.latin1()); + if (currDir.isEmpty() && currFile.isEmpty()) + break; + else if (currDir.isEmpty()) + result_down += file[i] + '/'; + else if (currFile.isEmpty()) + result_up += "../"; + else + { + result_down += file[i] + '/'; + result_up += "../"; + } + i++; + } + while ( (!currDir.isEmpty()) || (!currFile.isEmpty()) ); + + return result_up + result_down + resFileName; +} + +/////////////////////////////////////////////////////////////////////////////// + +// code from qt-3.1.2 version of QDir::canonicalPath() +QString URLUtil::canonicalPath( const QString & path ) +{ + QString r; + char cur[PATH_MAX+1]; + if ( ::getcwd( cur, PATH_MAX ) ) + { + char tmp[PATH_MAX+1]; + if( ::realpath( QFile::encodeName( path ), tmp ) ) + { + r = QFile::decodeName( tmp ); + } + //always make sure we go back to the current dir + ::chdir( cur ); + } + return r; +} + +/////////////////////////////////////////////////////////////////////////////// + +//written by "Dawit A." <adawit@kde.org> +//borrowed from his patch to KShell +QString URLUtil::envExpand ( const QString& str ) +{ + uint len = str.length(); + + if (len > 1 && str[0] == '$') + { + int pos = str.find ('/'); + + if (pos < 0) + pos = len; + + char* ret = getenv( QConstString(str.unicode()+1, pos-1).string().local8Bit().data() ); + + if (ret) + { + QString expandedStr ( QFile::decodeName( ret ) ); + if (pos < (int)len) + expandedStr += str.mid(pos); + return expandedStr; + } + } + + return str; +} + diff --git a/umbrello/umbrello/codeimport/kdevcppparser/urlutil.h b/umbrello/umbrello/codeimport/kdevcppparser/urlutil.h new file mode 100644 index 00000000..3460d28b --- /dev/null +++ b/umbrello/umbrello/codeimport/kdevcppparser/urlutil.h @@ -0,0 +1,132 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Julian Rockey <linux@jrockey.com> + Copyright (C) 2003 Mario Scalas <mario.scalas@libero.it> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _URLUTIL_H_ +#define _URLUTIL_H_ + +#include <qstring.h> +#include <qvaluelist.h> +#include <kurl.h> + +namespace URLUtil +{ + enum SlashesPosition { SLASH_PREFIX = 1, SLASH_SUFFIX = 2 }; + + /** + * Returns the filename part of a pathname (i.e. everything past the last slash) + */ + QString filename(const QString & pathName); + /** + * Returns the directory part of a path (i.e. everything up to but not including the last slash) + */ + QString directory(const QString & pathName); + /** + * Returns the relative path between a parent and child URL, or blank if the specified child is not a child of parent + */ + QString relativePath(const KURL & parent, const KURL & child, uint slashPolicy = SLASH_PREFIX); + /** + * Returns the relative path between a parent and child URL, or blank if the specified child is not a child of parent + */ + QString relativePath(const QString & parent, const QString & child, uint slashPolicy = SLASH_PREFIX); + /** + * Returns the relative path between a directory and file. Should never return empty path. + * Example: + * dirUrl: /home/test/src + * fileUrl: /home/test/lib/mylib.cpp + * returns: ../lib/mylib.cpp + */ + QString relativePathToFile( const QString & dirUrl, const QString & fileUrl ); + /** + *Returns the path 'up one level' - the opposite of what filename returns + */ + QString upDir(const QString & path, bool slashSuffix = false); + /** + * 'Merges' URLs - changes a URL that starts with dest to start with source instead + * Example: + * source is /home/me/ + * dest is /home/you/ + * child is /home/you/dir1/file1 + * returns /home/me/dir1/fil1 + */ + KURL mergeURL(const KURL & source, const KURL & dest, const KURL & child); + /** + * Returns the file extension for a filename or path + */ + QString getExtension(const QString & path); + + /** + * Given a base directory url in @p baseDirUrl and the url referring to a date sub-directory or file, + * it will return the path relative to @p baseDirUrl. + * If baseDirUrl == url.path() then it will return ".". + * <code> + * KURL baseUrl, dirUrl; + * baseUrl.setPath( "/home/mario/src/kdevelop/" ); + * dirUrl.setPath( "/home/mario/src/kdevelop/parts/cvs/" ); + * QString relPathName = extractDirPathRelative( baseUrl, url ); // == "parts/cvs/" + * QString absPathName = extractDirPathAbsolute( url ); // == "/home/mario/src/kdevelop/parts/cvs/" + * </code> + * Note that if you pass a file name in @p url (instead of a directory) or the @p baseUrl is not contained + * in @p url then the function will return "" (void string). + */ + QString extractPathNameRelative(const KURL &baseDirUrl, const KURL &url ); + QString extractPathNameRelative(const QString &basePath, const KURL &url ); + QString extractPathNameRelative(const QString &basePath, const QString &absFilePath ); + + /** + * Will return the absolute path name referred in @p url. + * Look at above for an example. + */ + QString extractPathNameAbsolute( const KURL &url ); + + /** + * Returns a QStringList of relative (to @p baseDir) paths from a list of KURLs in @p urls + */ + QStringList toRelativePaths( const QString &baseDir, const KURL::List &urls); + + /** + * If @p url is a directory will return true, false otherwise. + */ + bool isDirectory( const KURL &url ); + bool isDirectory( const QString &absFilePath ); + + /** + * Will dump the list of KURL @p urls on standard output, eventually printing @ aMessage if it + * is not null. + */ + void dump( const KURL::List &urls, const QString &aMessage = QString::null ); + + /** + * Same as QDir::canonicalPath in later versions of QT. Earlier versions of QT + * had this broken, so it's reproduced here. + */ + QString canonicalPath( const QString & path ); + + /** + * Performs environment variable expansion on @p variable. + * + * @param variable the string with the environment variable to expand. + * @return the expanded environment variable value. if the variable + * cannot be expanded, @p variable itself is returned. + */ + QString envExpand ( const QString &variable ); + +} + +#endif diff --git a/umbrello/umbrello/codeimport/nativeimportbase.cpp b/umbrello/umbrello/codeimport/nativeimportbase.cpp new file mode 100644 index 00000000..058b4d19 --- /dev/null +++ b/umbrello/umbrello/codeimport/nativeimportbase.cpp @@ -0,0 +1,340 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005-2007 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +// own header +#include "nativeimportbase.h" + +// qt/kde includes +#include <qfile.h> +#include <qtextstream.h> +#include <qregexp.h> +#include <klocale.h> +#include <kdebug.h> +// app includes +#include "import_utils.h" + +NativeImportBase::NativeImportBase(const QString &singleLineCommentIntro) { + m_singleLineCommentIntro = singleLineCommentIntro; + m_srcIndex = 0; + m_scopeIndex = 0; // index 0 is reserved for global scope + m_klass = NULL; + m_currentAccess = Uml::Visibility::Public; + m_isAbstract = false; + m_inComment = false; +} + +NativeImportBase::~NativeImportBase() { +} + +void NativeImportBase::setMultiLineComment(const QString &intro, const QString &end) { + m_multiLineCommentIntro = intro; + m_multiLineCommentEnd = end; +} + +void NativeImportBase::setMultiLineAltComment(const QString &intro, const QString &end) { + m_multiLineAltCommentIntro = intro; + m_multiLineAltCommentEnd = end; +} + +void NativeImportBase::skipStmt(QString until /* = ";" */) { + const uint srcLength = m_source.count(); + while (m_srcIndex < srcLength && m_source[m_srcIndex] != until) + m_srcIndex++; +} + +bool NativeImportBase::skipToClosing(QChar opener) { + QString closing; + switch (opener) { + case '{': + closing = "}"; + break; + case '[': + closing = "]"; + break; + case '(': + closing = ")"; + break; + case '<': + closing = ">"; + break; + default: + kError() << "NativeImportBase::skipToClosing(" << opener + << "): " << "illegal input character" << endl; + return false; + } + const QString opening(opener); + skipStmt(opening); + const uint srcLength = m_source.count(); + int nesting = 0; + while (m_srcIndex < srcLength) { + QString nextToken = advance(); + if (nextToken.isEmpty()) + break; + if (nextToken == closing) { + if (nesting <= 0) + break; + nesting--; + } else if (nextToken == opening) { + nesting++; + } + } + if (m_srcIndex == srcLength) + return false; + return true; +} + +QString NativeImportBase::advance() { + while (m_srcIndex < m_source.count() - 1) { + if (m_source[++m_srcIndex].startsWith(m_singleLineCommentIntro)) + m_comment += m_source[m_srcIndex]; + else + break; + } + if (m_srcIndex >= m_source.count() - 1 || + // if last item in m_source is a comment then it is dropped too + (m_srcIndex == m_source.count() - 1 && + m_source[m_srcIndex].startsWith(m_singleLineCommentIntro))) { + return QString(); + } + return m_source[m_srcIndex]; +} + +bool NativeImportBase::preprocess(QString& line) { + if (m_multiLineCommentIntro.isEmpty()) + return false; + // Check for end of multi line comment. + if (m_inComment) { + int delimiterLen = 0; + int pos = line.find(m_multiLineCommentEnd); + if (pos == -1) { + if (! m_multiLineAltCommentEnd.isEmpty()) + pos = line.find(m_multiLineAltCommentEnd); + if (pos == -1) { + m_comment += line + "\n"; + return true; // done + } + delimiterLen = m_multiLineAltCommentEnd.length(); + } else { + delimiterLen = m_multiLineCommentEnd.length(); + } + if (pos > 0) { + QString text = line.mid(0, pos - 1); + m_comment += text.stripWhiteSpace(); + } + m_source.append(m_singleLineCommentIntro + m_comment); // denotes comments in `m_source' + m_srcIndex++; + m_comment = ""; + m_inComment = false; + pos += delimiterLen; // pos now points behind the closed comment + if (pos == (int)line.length()) + return true; // done + line = line.mid(pos); + } + // If we get here then m_inComment is false. + // Check for start of multi line comment. + int delimIntroLen = 0; + int delimEndLen = 0; + int pos = line.find(m_multiLineCommentIntro); + if (pos != -1) { + delimIntroLen = m_multiLineCommentIntro.length(); + } else if (!m_multiLineAltCommentIntro.isEmpty()) { + pos = line.find(m_multiLineAltCommentIntro); + if (pos != -1) + delimIntroLen = m_multiLineAltCommentIntro.length(); + } + if (pos != -1) { + int endpos = line.find(m_multiLineCommentEnd); + if (endpos != -1) { + delimEndLen = m_multiLineCommentEnd.length(); + } else if (!m_multiLineAltCommentEnd.isEmpty()) { + endpos = line.find(m_multiLineAltCommentEnd); + if (endpos != -1) + delimEndLen = m_multiLineAltCommentEnd.length(); + } + if (endpos == -1) { + m_inComment = true; + if (pos + delimIntroLen < (int)line.length()) { + QString cmnt = line.mid(pos + delimIntroLen); + m_comment += cmnt.stripWhiteSpace() + "\n"; + } + if (pos == 0) + return true; // done + line = line.left(pos); + } else { // It's a multiline comment on a single line. + if (endpos > pos + delimIntroLen) { + QString cmnt = line.mid(pos + delimIntroLen, endpos - pos - delimIntroLen); + cmnt = cmnt.stripWhiteSpace(); + if (!cmnt.isEmpty()) + m_source.append(m_singleLineCommentIntro + cmnt); + } + endpos++; // endpos now points at the slash of "*/" + QString pre; + if (pos > 0) + pre = line.left(pos); + QString post; + if (endpos + delimEndLen < (int)line.length()) + post = line.mid(endpos + 1); + line = pre + post; + } + } + return false; // The input was not completely consumed by preprocessing. +} + +/// Split the line so that a string is returned as a single element of the list, +/// when not in a string then split at white space. +QStringList NativeImportBase::split(const QString& lin) { + QStringList list; + QString listElement; + QChar stringIntro = 0; // buffers the string introducer character + bool seenSpace = false; + QString line = lin.stripWhiteSpace(); + for (uint i = 0; i < line.length(); i++) { + const QChar& c = line[i]; + if (stringIntro) { // we are in a string + listElement += c; + if (c == stringIntro) { + if (line[i - 1] != '\\') { + list.append(listElement); + listElement = QString(); + stringIntro = 0; // we are no longer in a string + } + } + } else if (c == '"' || c == '\'') { + if (!listElement.isEmpty()) { + list.append(listElement); + } + listElement = stringIntro = c; + seenSpace = false; + } else if (c == ' ' || c == '\t') { + if (seenSpace) + continue; + seenSpace = true; + if (!listElement.isEmpty()) { + list.append(listElement); + listElement = QString(); + } + } else { + listElement += c; + seenSpace = false; + } + } + if (!listElement.isEmpty()) + list.append(listElement); + return list; +} + +/// The lexer. Tokenizes the given string and fills `m_source'. +/// Stores possible comments in `m_comment'. +void NativeImportBase::scan(QString line) { + if (preprocess(line)) + return; + // Check for single line comment. + int pos = line.find(m_singleLineCommentIntro); + if (pos != -1) { + QString cmnt = line.mid(pos); + m_source.append(cmnt); + if (pos == 0) + return; + line = line.left(pos); + } + if (line.contains(QRegExp("^\\s*$"))) + return; + QStringList words = split(line); + for (QStringList::Iterator it = words.begin(); it != words.end(); ++it) { + QString word = *it; + if (word[0] == '"' || word[0] == '\'') + m_source.append(word); // string constants are handled by split() + else + fillSource(word); + } +} + +void NativeImportBase::initVars() { +} + +void NativeImportBase::parseFile(const QString& filename) { + QString nameWithoutPath = filename; + nameWithoutPath.remove(QRegExp("^.*/")); + if (m_parsedFiles.contains(nameWithoutPath)) + return; + m_parsedFiles.append(nameWithoutPath); + QString fname = filename; + const QString msgPrefix = "NativeImportBase::parseFile(" + filename + "): "; + if (filename.contains('/')) { + QString path = filename; + path.remove( QRegExp("/[^/]+$") ); + kDebug() << msgPrefix << "adding path " << path << endl; + Import_Utils::addIncludePath(path); + } + if (! QFile::exists(filename)) { + if (filename.startsWith("/")) { + kError() << msgPrefix << "cannot find file" << endl; + return; + } + bool found = false; + QStringList includePaths = Import_Utils::includePathList(); + for (QStringList::Iterator pathIt = includePaths.begin(); + pathIt != includePaths.end(); ++pathIt) { + QString path = (*pathIt); + if (! path.endsWith("/")) { + path.append("/"); + } + if (QFile::exists(path + filename)) { + fname.prepend(path); + found = true; + break; + } + } + if (! found) { + kError() << msgPrefix << "cannot find file" << endl; + return; + } + } + QFile file(fname); + if (! file.open(IO_ReadOnly)) { + kError() << msgPrefix << "cannot open file" << endl; + return; + } + kDebug() << msgPrefix << "parsing." << endl; + // Scan the input file into the QStringList m_source. + m_source.clear(); + m_srcIndex = 0; + initVars(); + QTextStream stream(&file); + while (! stream.atEnd()) { + QString line = stream.readLine(); + scan(line); + } + file.close(); + // Parse the QStringList m_source. + m_klass = NULL; + m_currentAccess = Uml::Visibility::Public; + m_scopeIndex = 0; + m_scope[0] = NULL; // index 0 is reserved for global scope + const uint srcLength = m_source.count(); + for (m_srcIndex = 0; m_srcIndex < srcLength; m_srcIndex++) { + const QString& firstToken = m_source[m_srcIndex]; + //kDebug() << '"' << firstToken << '"' << endl; + if (firstToken.startsWith(m_singleLineCommentIntro)) { + m_comment = firstToken.mid(m_singleLineCommentIntro.length()); + continue; + } + if (! parseStmt()) + skipStmt(); + m_comment = QString(); + } + kDebug() << msgPrefix << "end of parse." << endl; +} + +void NativeImportBase::initialize() { + m_parsedFiles.clear(); +} + diff --git a/umbrello/umbrello/codeimport/nativeimportbase.h b/umbrello/umbrello/codeimport/nativeimportbase.h new file mode 100644 index 00000000..cc82fd91 --- /dev/null +++ b/umbrello/umbrello/codeimport/nativeimportbase.h @@ -0,0 +1,227 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2005-2007 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +#ifndef NATIVEIMPORTBASE_H +#define NATIVEIMPORTBASE_H + +#include <qstring.h> +#include <qstringlist.h> +#include "classimport.h" +#include "../umlnamespace.h" + +class UMLPackage; +class UMLClassifier; + +/** + * Intermediate base class for native Umbrello implementations of + * programming language import + * + * The default call sequence is as follows (RealizedLanguageImport + * is used as a placeholder name for the concrete language importer.) + * NativeImportBase RealizedLanguageImport + * --> importFiles() + * parseFile() + * -----------------------------------> initVars() + * scan() + * preprocess() (may be reimplemented) + * ---------------------------------> fillSource() + * -----------------------------------> parseStmt() + * This sequence may be changed by overriding default implementations + * of virtual methods in NativeImportBase. + * + * @short Base class for native implementations of language import + * @author Oliver Kellogg <okellogg@users.sourceforge.net> + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class NativeImportBase : public ClassImport { +public: + /** + * Constructor + * @param singleLineCommentIntro "//" for IDL and Java, "--" for Ada + */ + NativeImportBase(const QString &singleLineCommentIntro); + virtual ~NativeImportBase(); + +protected: + /** + * Implement abstract operation from ClassImport. + */ + void initialize(); + + /** + * Set the delimiter strings for a multi line comment. + * + * @param intro In languages with a C style multiline comment + * this is slash-star. + * @param end In languages with a C style multiline comment + * this is star-slash. + */ + void setMultiLineComment(const QString &intro, const QString &end); + /** + * Set the delimiter strings for an alternative form of + * multi line comment. See setMultiLineComment(). + */ + void setMultiLineAltComment(const QString &intro, const QString &end); + + /** + * Import a single file. + * The default implementation should be feasible for languages that + * don't depend on an external preprocessor. + * + * @param filename The file to import. + */ + virtual void parseFile(const QString& filename); + + /** + * Initialize auxiliary variables. + * This is called by the default implementation of parseFile() + * after scanning (before parsing the QStringList m_source.) + * The default implementation is empty. + */ + virtual void initVars(); + + /** + * Scan a single line. + * parseFile() calls this for each line read from the input file. + * This in turn calls other methods such as preprocess() and fillSource(). + * + * @param line The line to scan. + */ + void scan(QString line); + + /** + * Preprocess a line. + * May modify the given line to remove items consumed by the + * preprocessing such as comments or preprocessor directives. + * The default implementation handles multi-line comments. + * + * @param line The line to preprocess. + * @return True if the line was completely consumed, + * false if there are still items left in the line + * for further analysis. + */ + virtual bool preprocess(QString& line); + + /** + * Split the line so that a string is returned as a single element of the list. + * When not in a string then split at white space. + * The default implementation is suitable for C style strings and char constants. + */ + virtual QStringList split(const QString& line); + + /** + * Analyze the given word and fill `m_source'. + * A "word" is a whitespace delimited item from the input line. + * To be provided by the specific importer class. + */ + virtual void fillSource(const QString& word) = 0; + + /** + * Parse the statement which starts at m_source[m_srcIndex] + * leaving m_srcIndex pointing to the end of the recognized + * statement. + * To be provided by the concrete importer. + * + * @return True if the statement was recognized. + */ + virtual bool parseStmt() = 0; + + /** + * Advance m_srcIndex until m_source[m_srcIndex] contains the lexeme + * given by `until'. + */ + void skipStmt(QString until = ";"); + + /** + * Advance m_srcIndex to the index of the corresponding closing character + * of the given opening. Nested opening/closing pairs are respected. + * Valid openers are: '{' '[' '(' '<' + * + * @return True for success, false for misuse (invalid opener) or + * if no matching closing character is found in m_source. + */ + bool skipToClosing(QChar opener); + + /** + * Advance m_srcIndex until m_source[m_srcIndex] contains a non-comment. + * Comments encountered during advancement are accumulated in `m_comment'. + * If m_srcIndex hits the end of m_source then QString::null is returned. + */ + QString advance(); + + /** + * How to start a single line comment in this programming language. + */ + QString m_singleLineCommentIntro; + + /** + * The scanned lexemes. + */ + QStringList m_source; + /** + * Used for indexing m_source. + */ + uint m_srcIndex; + + /** + * Stack of scopes for use by the specific importer. + */ + UMLPackage *m_scope[32]; + /** + * Indexes m_scope. Index 0 is reserved for global scope. + */ + uint m_scopeIndex; + + /** + * The class currently being processed. + */ + UMLClassifier *m_klass; + /** + * The current access (public/protected/private) + */ + Uml::Visibility m_currentAccess; + /** + * Intermediate accumulator for comment text. + */ + QString m_comment; + /** + * True if we are currently in a multi-line comment. + * Only applies to languages with multi-line comments. + */ + bool m_inComment; + /** + * Accumulator for abstractness + */ + bool m_isAbstract; + + /** + * List of parsed files. Contains file names without paths. + * Before actually parsing a given file, NativeImportBase checks + * whether the name is already present in this list in order to + * avoid parsing the same file multiple times. + */ + QStringList m_parsedFiles; + + /** + * Multi line comment delimiters + */ + QString m_multiLineCommentIntro; + QString m_multiLineCommentEnd; + /** + * Some languages support an alternative set of multi line + * comment delimiters. + */ + QString m_multiLineAltCommentIntro; + QString m_multiLineAltCommentEnd; +}; + +#endif + diff --git a/umbrello/umbrello/codeimport/pascalimport.cpp b/umbrello/umbrello/codeimport/pascalimport.cpp new file mode 100644 index 00000000..ffe291ff --- /dev/null +++ b/umbrello/umbrello/codeimport/pascalimport.cpp @@ -0,0 +1,413 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006-2007 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +// own header +#include "pascalimport.h" + +#include <stdio.h> +// qt/kde includes +#include <qregexp.h> +#include <kdebug.h> +// app includes +#include "import_utils.h" +#include "../uml.h" +#include "../umldoc.h" +#include "../package.h" +#include "../classifier.h" +#include "../enum.h" +#include "../operation.h" +#include "../attribute.h" + +PascalImport::PascalImport() : NativeImportBase("//") { + setMultiLineComment("(*", "*)"); + setMultiLineAltComment("{", "}"); + initVars(); +} + +PascalImport::~PascalImport() { +} + +void PascalImport::initVars() { + m_inInterface = false; + m_section = sect_NONE; + NativeImportBase::m_currentAccess = Uml::Visibility::Public; +} + +void PascalImport::fillSource(const QString& word) { + QString lexeme; + const uint len = word.length(); + for (uint i = 0; i < len; i++) { + QChar c = word[i]; + if (c.isLetterOrNumber() || c == '_' || c == '.' || c == '#') { + lexeme += c; + } else { + if (!lexeme.isEmpty()) { + m_source.append(lexeme); + lexeme = QString(); + } + if (c == ':' && word[i + 1] == '=') { + m_source.append(":="); + i++; + } else { + m_source.append(QString(c)); + } + } + } + if (!lexeme.isEmpty()) + m_source.append(lexeme); +} + +void PascalImport::checkModifiers(bool& isVirtual, bool& isAbstract) { + const uint srcLength = m_source.count(); + while (m_srcIndex < srcLength - 1) { + QString lookAhead = m_source[m_srcIndex + 1].lower(); + if (lookAhead != "virtual" && lookAhead != "abstract" && + lookAhead != "override" && + lookAhead != "register" && lookAhead != "cdecl" && + lookAhead != "pascal" && lookAhead != "stdcall" && + lookAhead != "safecall" && lookAhead != "saveregisters" && + lookAhead != "popstack") + break; + if (lookAhead == "abstract") + isAbstract = true; + else if (lookAhead == "virtual") + isVirtual = true; + advance(); + skipStmt(); + } +} + +bool PascalImport::parseStmt() { + const uint srcLength = m_source.count(); + QString keyword = m_source[m_srcIndex].lower(); + //kDebug() << '"' << keyword << '"' << endl; + if (keyword == "uses") { + while (m_srcIndex < srcLength - 1) { + QString unit = advance(); + const QString& prefix = unit.lower(); + if (prefix == "sysutils" || prefix == "types" || prefix == "classes" || + prefix == "graphics" || prefix == "controls" || prefix == "strings" || + prefix == "forms" || prefix == "windows" || prefix == "messages" || + prefix == "variants" || prefix == "stdctrls" || prefix == "extctrls" || + prefix == "activex" || prefix == "comobj" || prefix == "registry" || + prefix == "classes" || prefix == "dialogs") { + if (advance() != ",") + break; + continue; + } + QString filename = unit + ".pas"; + if (! m_parsedFiles.contains(unit)) { + // Save current m_source and m_srcIndex. + QStringList source(m_source); + uint srcIndex = m_srcIndex; + m_source.clear(); + parseFile(filename); + // Restore m_source and m_srcIndex. + m_source = source; + m_srcIndex = srcIndex; + // Also reset m_currentAccess. + // CHECK: need to reset more stuff? + m_currentAccess = Uml::Visibility::Public; + } + if (advance() != ",") + break; + } + return true; + } + if (keyword == "unit") { + const QString& name = advance(); + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Package, name, + m_scope[m_scopeIndex], m_comment); + m_scope[++m_scopeIndex] = static_cast<UMLPackage*>(ns); + skipStmt(); + return true; + } + if (keyword == "interface") { + m_inInterface = true; + return true; + } + if (keyword == "initialization" || keyword == "implementation") { + m_inInterface = false; + return true; + } + if (! m_inInterface) { + // @todo parseStmt() should support a notion for "quit parsing, close file immediately" + return false; + } + if (keyword == "label") { + m_section = sect_LABEL; + return true; + } + if (keyword == "const") { + m_section = sect_CONST; + return true; + } + if (keyword == "resourcestring") { + m_section = sect_RESOURCESTRING; + return true; + } + if (keyword == "type") { + m_section = sect_TYPE; + return true; + } + if (keyword == "var") { + m_section = sect_VAR; + return true; + } + if (keyword == "threadvar") { + m_section = sect_THREADVAR; + return true; + } + if (keyword == "automated" || keyword == "published" // no concept in UML + || keyword == "public") { + m_currentAccess = Uml::Visibility::Public; + return true; + } + if (keyword == "protected") { + m_currentAccess = Uml::Visibility::Protected; + return true; + } + if (keyword == "private") { + m_currentAccess = Uml::Visibility::Private; + return true; + } + if (keyword == "packed") { + return true; // TBC: perhaps this could be stored in a TaggedValue + } + if (keyword == "[") { + skipStmt("]"); + return true; + } + if (keyword == "end") { + if (m_klass) { + m_klass = NULL; + } else if (m_scopeIndex) { + m_scopeIndex--; + m_currentAccess = Uml::Visibility::Public; + } else { + kError() << "importPascal: too many \"end\"" << endl; + } + skipStmt(); + return true; + } + if (keyword == "function" || keyword == "procedure" || + keyword == "constructor" || keyword == "destructor") { + if (m_klass == NULL) { + // Unlike a Pascal unit, a UML package does not support subprograms. + // In order to map those, we would need to create a UML class with + // stereotype <<utility>> for the unit, http://bugs.kde.org/89167 + bool dummyVirtual = false; + bool dummyAbstract = false; + checkModifiers(dummyVirtual, dummyAbstract); + return true; + } + const QString& name = advance(); + UMLOperation *op = Import_Utils::makeOperation(m_klass, name); + if (m_source[m_srcIndex + 1] == "(") { + advance(); + const uint MAX_PARNAMES = 16; + while (m_srcIndex < srcLength && m_source[m_srcIndex] != ")") { + QString nextToken = m_source[m_srcIndex + 1].lower(); + Uml::Parameter_Direction dir = Uml::pd_In; + if (nextToken == "var") { + dir = Uml::pd_InOut; + advance(); + } else if (nextToken == "const") { + advance(); + } else if (nextToken == "out") { + dir = Uml::pd_Out; + advance(); + } + QString parName[MAX_PARNAMES]; + uint parNameCount = 0; + do { + if (parNameCount >= MAX_PARNAMES) { + kError() << "MAX_PARNAMES is exceeded at " << name << endl; + break; + } + parName[parNameCount++] = advance(); + } while (advance() == ","); + if (m_source[m_srcIndex] != ":") { + kError() << "importPascal: expecting ':' at " << m_source[m_srcIndex] << endl; + skipStmt(); + break; + } + nextToken = advance(); + if (nextToken.lower() == "array") { + nextToken = advance().lower(); + if (nextToken != "of") { + kError() << "importPascal(" << name << "): expecting 'array OF' at " + << nextToken << endl; + skipStmt(); + return false; + } + nextToken = advance(); + } + for (uint i = 0; i < parNameCount; i++) { + UMLAttribute *att = Import_Utils::addMethodParameter(op, nextToken, parName[i]); + att->setParmKind(dir); + } + if (advance() != ";") + break; + } + } + QString returnType; + if (keyword == "function") { + if (advance() != ":") { + kError() << "importPascal: expecting \":\" at function " + << name << endl; + return false; + } + returnType = advance(); + } else if (keyword == "constructor" || keyword == "destructor") { + op->setStereotype(keyword); + } + skipStmt(); + bool isVirtual = false; + bool isAbstract = false; + checkModifiers(isVirtual, isAbstract); + Import_Utils::insertMethod(m_klass, op, m_currentAccess, returnType, + !isVirtual, isAbstract, false, false, m_comment); + return true; + } + if (m_section != sect_TYPE) { + skipStmt(); + return true; + } + if (m_klass == NULL) { + const QString& name = m_source[m_srcIndex]; + QString nextToken = advance(); + if (nextToken != "=") { + kDebug() << "PascalImport::parseStmt(" << name << "): " + << "expecting '=' at " << nextToken << endl; + return false; + } + keyword = advance().lower(); + if (keyword == "(") { + // enum type + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Enum, + name, m_scope[m_scopeIndex], m_comment); + UMLEnum *enumType = static_cast<UMLEnum*>(ns); + while (++m_srcIndex < srcLength && m_source[m_srcIndex] != ")") { + Import_Utils::addEnumLiteral(enumType, m_source[m_srcIndex]); + if (advance() != ",") + break; + } + skipStmt(); + return true; + } + if (keyword == "set") { // @todo implement Pascal set types + skipStmt(); + return true; + } + if (keyword == "array") { // @todo implement Pascal array types + skipStmt(); + return true; + } + if (keyword == "file") { // @todo implement Pascal file types + skipStmt(); + return true; + } + if (keyword == "^") { // @todo implement Pascal pointer types + skipStmt(); + return true; + } + if (keyword == "class" || keyword == "interface") { + Uml::Object_Type t = (keyword == "class" ? Uml::ot_Class : Uml::ot_Interface); + UMLObject *ns = Import_Utils::createUMLObject(t, name, + m_scope[m_scopeIndex], m_comment); + UMLClassifier *klass = static_cast<UMLClassifier*>(ns); + m_comment = QString(); + QString lookAhead = m_source[m_srcIndex + 1]; + if (lookAhead == "(") { + advance(); + do { + QString base = advance(); + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Class, base, NULL); + UMLClassifier *parent = static_cast<UMLClassifier*>(ns); + m_comment = QString(); + Import_Utils::createGeneralization(klass, parent); + } while (advance() == ","); + if (m_source[m_srcIndex] != ")") { + kError() << "PascalImport: expecting \")\" at " + << m_source[m_srcIndex] << endl; + return false; + } + lookAhead = m_source[m_srcIndex + 1]; + } + if (lookAhead == ";") { + skipStmt(); + return true; + } + if (lookAhead == "of") { + // @todo implement class-reference type + return false; + } + m_klass = klass; + m_currentAccess = Uml::Visibility::Public; + return true; + } + if (keyword == "record") { + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Class, name, + m_scope[m_scopeIndex], m_comment); + ns->setStereotype("record"); + m_klass = static_cast<UMLClassifier*>(ns); + return true; + } + if (keyword == "function" || keyword == "procedure") { + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Datatype, name, + m_scope[m_scopeIndex], m_comment); + if (m_source[m_srcIndex + 1] == "(") + skipToClosing('('); + skipStmt(); + return true; + } + // Datatypes: TO BE DONE + return false; + } + // At this point we need a class because we're expecting its member attributes. + if (m_klass == NULL) { + kDebug() << "importPascal: skipping " << m_source[m_srcIndex] << endl; + skipStmt(); + return true; + } + QString name, stereotype; + if (keyword == "property") { + stereotype = keyword; + name = advance(); + } else { + name = m_source[m_srcIndex]; + } + if (advance() != ":") { + kError() << "PascalImport: expecting \":\" at " << name << " " + << m_source[m_srcIndex] << endl; + skipStmt(); + return true; + } + QString typeName = advance(); + QString initialValue; + if (advance() == "=") { + initialValue = advance(); + QString token; + while ((token = advance()) != ";") { + initialValue.append(' ' + token); + } + } + UMLObject *o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name, + typeName, m_comment); + UMLAttribute *attr = static_cast<UMLAttribute*>(o); + attr->setStereotype(stereotype); + attr->setInitialValue(initialValue); + skipStmt(); + return true; +} + + diff --git a/umbrello/umbrello/codeimport/pascalimport.h b/umbrello/umbrello/codeimport/pascalimport.h new file mode 100644 index 00000000..975dc423 --- /dev/null +++ b/umbrello/umbrello/codeimport/pascalimport.h @@ -0,0 +1,66 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +#ifndef PASCALIMPORT_H +#define PASCALIMPORT_H + +#include "nativeimportbase.h" + +/** + * Pascal code import + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class PascalImport : public NativeImportBase { +public: + PascalImport(); + virtual ~PascalImport(); + +protected: + /** + * Reimplement operation from NativeImportBase. + */ + void initVars(); + + /** + * Implement abstract operation from NativeImportBase. + */ + bool parseStmt(); + + /** + * Implement abstract operation from NativeImportBase. + */ + void fillSource(const QString& word); + + /** + * Check for, and skip over, all modifiers following a method. + * Set the output arguments on encountering abstract and/or virtual. + * + * @param isVirtual return value, set to true when "virtual" seen + * @param isAbstract return value, set to true when "abstract" seen + */ + void checkModifiers(bool& isVirtual, bool& isAbstract); + + /** + * Auxiliary variable, becomes true when keyword "interface" is seen + */ + bool m_inInterface; + + enum Section_Type { sect_NONE, sect_LABEL, sect_CONST, sect_RESOURCESTRING, + sect_TYPE, sect_VAR, sect_THREADVAR }; + /** + * Auxiliary variable, contains the current section + */ + Section_Type m_section; +}; + +#endif + diff --git a/umbrello/umbrello/codeimport/pythonimport.cpp b/umbrello/umbrello/codeimport/pythonimport.cpp new file mode 100644 index 00000000..af59cf8a --- /dev/null +++ b/umbrello/umbrello/codeimport/pythonimport.cpp @@ -0,0 +1,190 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +// own header +#include "pythonimport.h" + +// qt/kde includes +#include <qstringlist.h> +#include <qregexp.h> +#include <kdebug.h> +// app includes +#include "import_utils.h" +#include "../uml.h" +#include "../umldoc.h" +#include "../umlpackagelist.h" +#include "../package.h" +#include "../classifier.h" +#include "../enum.h" +#include "../operation.h" +#include "../attribute.h" + +PythonImport::PythonImport() : NativeImportBase("#") { + setMultiLineComment("\"\"\"", "\"\"\""); + initVars(); +} + +PythonImport::~PythonImport() { +} + +void PythonImport::initVars() { + m_srcIndentIndex = 0; + m_srcIndent[m_srcIndentIndex] = 0; + m_braceWasOpened = false; +} + +bool PythonImport::preprocess(QString& line) { + if (NativeImportBase::preprocess(line)) + return true; + // Handle single line comment + int pos = line.find(m_singleLineCommentIntro); + if (pos != -1) { + QString cmnt = line.mid(pos); + m_source.append(cmnt); + m_srcIndex++; + if (pos == 0) + return true; + line = line.left(pos); + line.remove( QRegExp("\\s+$") ); + } + // Transform changes in indentation into braces a la C++/Java/Perl/... + pos = line.find( QRegExp("\\S") ); + if (pos == -1) + return true; + bool isContinuation = false; + int leadingWhite = line.left(pos).contains( QRegExp("\\s") ); + if (leadingWhite > m_srcIndent[m_srcIndentIndex]) { + if (m_srcIndex == 0) { + kError() << "PythonImport::preprocess(): internal error 1" << endl; + return true; + } + if (m_braceWasOpened) { + m_srcIndent[++m_srcIndentIndex] = leadingWhite; + m_braceWasOpened = false; + } else { + isContinuation = true; + } + } else { + while (m_srcIndentIndex > 0 && leadingWhite < m_srcIndent[m_srcIndentIndex]) { + m_srcIndentIndex--; + m_source.append("}"); + m_srcIndex++; + } + } + if (line.endsWith(":")) { + line.replace( QRegExp(":$"), "{" ); + m_braceWasOpened = true; + } else { + m_braceWasOpened = false; + } + if (!isContinuation && !m_braceWasOpened) + line += ';'; + return false; // The input was not completely consumed by preprocessing. +} + +void PythonImport::fillSource(const QString& word) { + QString lexeme; + const uint len = word.length(); + for (uint i = 0; i < len; i++) { + const QChar& c = word[i]; + if (c.isLetterOrNumber() || c == '_' || c == '.') { + lexeme += c; + } else { + if (!lexeme.isEmpty()) { + m_source.append(lexeme); + m_srcIndex++; + lexeme = QString(); + } + m_source.append(QString(c)); + m_srcIndex++; + } + } + if (!lexeme.isEmpty()) { + m_source.append(lexeme); + m_srcIndex++; + } +} + +void PythonImport::skipBody() { + if (m_source[m_srcIndex] != "{") + skipStmt("{"); + int braceNesting = 0; + QString token; + while (!(token = advance()).isNull()) { + if (token == "}") { + if (braceNesting <= 0) + break; + braceNesting--; + } else if (token == "{") { + braceNesting++; + } + } +} + +bool PythonImport::parseStmt() { + const uint srcLength = m_source.count(); + const QString& keyword = m_source[m_srcIndex]; + if (keyword == "class") { + const QString& name = advance(); + UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Class, + name, m_scope[m_scopeIndex], m_comment); + m_scope[++m_scopeIndex] = m_klass = static_cast<UMLClassifier*>(ns); + m_comment = QString(); + if (advance() == "(") { + while (m_srcIndex < srcLength - 1 && advance() != ")") { + const QString& baseName = m_source[m_srcIndex]; + Import_Utils::createGeneralization(m_klass, baseName); + if (advance() != ",") + break; + } + } + if (m_source[m_srcIndex] != "{") { + skipStmt("{"); + } + return true; + } + if (keyword == "def") { + if (m_klass == NULL) { + // skip functions outside of a class + skipBody(); + return true; + } + const QString& name = advance(); + // operation + UMLOperation *op = Import_Utils::makeOperation(m_klass, name); + if (advance() != "(") { + kError() << "importPython def " << name << ": expecting \"(\"" << endl; + skipBody(); + return true; + } + while (m_srcIndex < srcLength && advance() != ")") { + const QString& parName = m_source[m_srcIndex]; + UMLAttribute *att = Import_Utils::addMethodParameter(op, "string", parName); + if (advance() != ",") + break; + } + Import_Utils::insertMethod(m_klass, op, Uml::Visibility::Public, "string", + false /*isStatic*/, false /*isAbstract*/, false /*isFriend*/, + false /*isConstructor*/, m_comment); + skipBody(); + return true; + } + if (keyword == "}") { + if (m_scopeIndex) + m_klass = dynamic_cast<UMLClassifier*>(m_scope[--m_scopeIndex]); + else + kError() << "importPython: too many }" << endl; + return true; + } + return false; // @todo parsing of attributes +} + + diff --git a/umbrello/umbrello/codeimport/pythonimport.h b/umbrello/umbrello/codeimport/pythonimport.h new file mode 100644 index 00000000..41fea0d4 --- /dev/null +++ b/umbrello/umbrello/codeimport/pythonimport.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * copyright (C) 2006 * + * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + ***************************************************************************/ + +#ifndef PYTHONIMPORT_H +#define PYTHONIMPORT_H + +#include "nativeimportbase.h" + +/** + * Python code import + * @author Oliver Kellogg + * Bugs and comments to uml-devel@lists.sf.net or http://bugs.kde.org + */ +class PythonImport : public NativeImportBase { +public: + PythonImport(); + virtual ~PythonImport(); + +protected: + /** + * Reimplement operation from NativeImportBase. + */ + void initVars(); + + /** + * Implement abstract operation from NativeImportBase. + */ + bool parseStmt(); + + /** + * Implement abstract operation from NativeImportBase. + */ + void fillSource(const QString& line); + + /** + * Reimplement operation from NativeImportBase. + * In addition to handling multiline comments, this method transforms + * changes in leading indentation into braces (opening brace for increase + * in indentation, closing brace for decrease in indentation) in m_source. + * Removal of Python's indentation sensitivity simplifies subsequent + * processing using Umbrello's native import framework. + */ + bool preprocess(QString& line); + + /** + * Skip ahead to outermost closing brace + */ + void skipBody(); + + /** + * Buffer for number of indentation characters (whitespace, + * i.e. tabs or spaces) at beginning of input line. + */ + int m_srcIndent[100]; + + /** + * Index for m_srcIndent[]. Index 0 is reserved and contains 0. + */ + int m_srcIndentIndex; + + /** + * Auxiliary flag denoting the opening of a block + */ + bool m_braceWasOpened; +}; + +#endif + |