summaryrefslogtreecommitdiffstats
path: root/umbrello/umbrello/codeimport/pascalimport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'umbrello/umbrello/codeimport/pascalimport.cpp')
-rw-r--r--umbrello/umbrello/codeimport/pascalimport.cpp413
1 files changed, 413 insertions, 0 deletions
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;
+}
+
+