umbrello API Documentation

pascalimport.cpp

00001 /***************************************************************************
00002  *                                                                         *
00003  *   This program is free software; you can redistribute it and/or modify  *
00004  *   it under the terms of the GNU General Public License as published by  *
00005  *   the Free Software Foundation; either version 2 of the License, or     *
00006  *   (at your option) any later version.                                   *
00007  *                                                                         *
00008  *   copyright (C) 2006-2007                                               *
00009  *   Umbrello UML Modeller Authors <uml-devel@uml.sf.net>                  *
00010  ***************************************************************************/
00011 
00012 // own header
00013 #include "pascalimport.h"
00014 
00015 #include <stdio.h>
00016 // qt/kde includes
00017 #include <qregexp.h>
00018 #include <kdebug.h>
00019 // app includes
00020 #include "import_utils.h"
00021 #include "../uml.h"
00022 #include "../umldoc.h"
00023 #include "../package.h"
00024 #include "../classifier.h"
00025 #include "../enum.h"
00026 #include "../operation.h"
00027 #include "../attribute.h"
00028 
00029 PascalImport::PascalImport() : NativeImportBase("//") {
00030     setMultiLineComment("(*", "*)");
00031     setMultiLineAltComment("{", "}");
00032     initVars();
00033 }
00034 
00035 PascalImport::~PascalImport() {
00036 }
00037 
00038 void PascalImport::initVars() {
00039     m_inInterface = false;
00040     m_section = sect_NONE;
00041     NativeImportBase::m_currentAccess = Uml::Visibility::Public;
00042 }
00043 
00044 void PascalImport::fillSource(const QString& word) {
00045     QString lexeme;
00046     const uint len = word.length();
00047     for (uint i = 0; i < len; i++) {
00048         QChar c = word[i];
00049         if (c.isLetterOrNumber() || c == '_' || c == '.' || c == '#') {
00050             lexeme += c;
00051         } else {
00052             if (!lexeme.isEmpty()) {
00053                 m_source.append(lexeme);
00054                 lexeme = QString();
00055             }
00056             if (c == ':' && word[i + 1] == '=') {
00057                 m_source.append(":=");
00058                 i++;
00059             } else {
00060                 m_source.append(QString(c));
00061             }
00062         }
00063     }
00064     if (!lexeme.isEmpty())
00065         m_source.append(lexeme);
00066 }
00067 
00068 void PascalImport::checkModifiers(bool& isVirtual, bool& isAbstract) {
00069     const uint srcLength = m_source.count();
00070     while (m_srcIndex < srcLength - 1) {
00071         QString lookAhead = m_source[m_srcIndex + 1].lower();
00072         if (lookAhead != "virtual" && lookAhead != "abstract" &&
00073             lookAhead != "override" &&
00074             lookAhead != "register" && lookAhead != "cdecl" &&
00075             lookAhead != "pascal" && lookAhead != "stdcall" &&
00076             lookAhead != "safecall" && lookAhead != "saveregisters" &&
00077             lookAhead != "popstack")
00078             break;
00079         if (lookAhead == "abstract")
00080             isAbstract = true;
00081         else if (lookAhead == "virtual")
00082             isVirtual = true;
00083         advance();
00084         skipStmt();
00085     }
00086 }
00087 
00088 bool PascalImport::parseStmt() {
00089     const uint srcLength = m_source.count();
00090     QString keyword = m_source[m_srcIndex].lower();
00091     //kDebug() << '"' << keyword << '"' << endl;
00092     if (keyword == "uses") {
00093         while (m_srcIndex < srcLength - 1) {
00094             QString unit = advance();
00095             const QString& prefix = unit.lower();
00096             if (prefix == "sysutils" || prefix == "types" || prefix == "classes" ||
00097                 prefix == "graphics" || prefix == "controls" || prefix == "strings" ||
00098                 prefix == "forms" || prefix == "windows" || prefix == "messages" ||
00099                 prefix == "variants" || prefix == "stdctrls" || prefix == "extctrls" ||
00100                 prefix == "activex" || prefix == "comobj" || prefix == "registry" ||
00101                 prefix == "classes" || prefix == "dialogs") {
00102                 if (advance() != ",")
00103                     break;
00104                 continue;
00105             }
00106             QString filename = unit + ".pas";
00107             if (! m_parsedFiles.contains(unit)) {
00108                 // Save current m_source and m_srcIndex.
00109                 QStringList source(m_source);
00110                 uint srcIndex = m_srcIndex;
00111                 m_source.clear();
00112                 parseFile(filename);
00113                 // Restore m_source and m_srcIndex.
00114                 m_source = source;
00115                 m_srcIndex = srcIndex;
00116                 // Also reset m_currentAccess.
00117                 // CHECK: need to reset more stuff?
00118                 m_currentAccess = Uml::Visibility::Public;
00119             }
00120             if (advance() != ",")
00121                 break;
00122         }
00123         return true;
00124     }
00125     if (keyword == "unit") {
00126         const QString& name = advance();
00127         UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Package, name,
00128                                                       m_scope[m_scopeIndex], m_comment);
00129         m_scope[++m_scopeIndex] = static_cast<UMLPackage*>(ns);
00130         skipStmt();
00131         return true;
00132     }
00133     if (keyword == "interface") {
00134         m_inInterface = true;
00135         return true;
00136     }
00137     if (keyword == "initialization" || keyword == "implementation") {
00138         m_inInterface = false;
00139         return true;
00140     }
00141     if (! m_inInterface) {
00142         // @todo parseStmt() should support a notion for "quit parsing, close file immediately"
00143         return false;
00144     }
00145     if (keyword == "label") {
00146         m_section = sect_LABEL;
00147         return true;
00148     }
00149     if (keyword == "const") {
00150         m_section = sect_CONST;
00151         return true;
00152     }
00153     if (keyword == "resourcestring") {
00154         m_section = sect_RESOURCESTRING;
00155         return true;
00156     }
00157     if (keyword == "type") {
00158         m_section = sect_TYPE;
00159         return true;
00160     }
00161     if (keyword == "var") {
00162         m_section = sect_VAR;
00163         return true;
00164     }
00165     if (keyword == "threadvar") {
00166         m_section = sect_THREADVAR;
00167         return true;
00168     }
00169     if (keyword == "automated" || keyword == "published"  // no concept in UML
00170      || keyword == "public") {
00171         m_currentAccess = Uml::Visibility::Public;
00172         return true;
00173     }
00174     if (keyword == "protected") {
00175         m_currentAccess = Uml::Visibility::Protected;
00176         return true;
00177     }
00178     if (keyword == "private") {
00179         m_currentAccess = Uml::Visibility::Private;
00180         return true;
00181     }
00182     if (keyword == "packed") {
00183         return true;  // TBC: perhaps this could be stored in a TaggedValue
00184     }
00185     if (keyword == "[") {
00186         skipStmt("]");
00187         return true;
00188     }
00189     if (keyword == "end") {
00190         if (m_klass) {
00191             m_klass = NULL;
00192         } else if (m_scopeIndex) {
00193             m_scopeIndex--;
00194             m_currentAccess = Uml::Visibility::Public;
00195         } else {
00196             kError() << "importPascal: too many \"end\"" << endl;
00197         }
00198         skipStmt();
00199         return true;
00200     }
00201     if (keyword == "function" || keyword == "procedure" ||
00202         keyword == "constructor" || keyword == "destructor") {
00203         if (m_klass == NULL) {
00204             // Unlike a Pascal unit, a UML package does not support subprograms.
00205             // In order to map those, we would need to create a UML class with
00206             // stereotype <<utility>> for the unit, http://bugs.kde.org/89167
00207             bool dummyVirtual = false;
00208             bool dummyAbstract = false;
00209             checkModifiers(dummyVirtual, dummyAbstract);
00210             return true;
00211         }
00212         const QString& name = advance();
00213         UMLOperation *op = Import_Utils::makeOperation(m_klass, name);
00214         if (m_source[m_srcIndex + 1] == "(") {
00215             advance();
00216             const uint MAX_PARNAMES = 16;
00217             while (m_srcIndex < srcLength && m_source[m_srcIndex] != ")") {
00218                 QString nextToken = m_source[m_srcIndex + 1].lower();
00219                 Uml::Parameter_Direction dir = Uml::pd_In;
00220                 if (nextToken == "var") {
00221                     dir = Uml::pd_InOut;
00222                     advance();
00223                 } else if (nextToken == "const") {
00224                     advance();
00225                 } else if (nextToken == "out") {
00226                     dir = Uml::pd_Out;
00227                     advance();
00228                 }
00229                 QString parName[MAX_PARNAMES];
00230                 uint parNameCount = 0;
00231                 do {
00232                     if (parNameCount >= MAX_PARNAMES) {
00233                         kError() << "MAX_PARNAMES is exceeded at " << name << endl;
00234                         break;
00235                     }
00236                     parName[parNameCount++] = advance();
00237                 } while (advance() == ",");
00238                 if (m_source[m_srcIndex] != ":") {
00239                     kError() << "importPascal: expecting ':' at " << m_source[m_srcIndex] << endl;
00240                     skipStmt();
00241                     break;
00242                 }
00243                 nextToken = advance();
00244                 if (nextToken.lower() == "array") {
00245                     nextToken = advance().lower();
00246                     if (nextToken != "of") {
00247                         kError() << "importPascal(" << name << "): expecting 'array OF' at "
00248                                   << nextToken << endl;
00249                         skipStmt();
00250                         return false;
00251                     }
00252                     nextToken = advance();
00253                 }
00254                 for (uint i = 0; i < parNameCount; i++) {
00255                     UMLAttribute *att = Import_Utils::addMethodParameter(op, nextToken, parName[i]);
00256                     att->setParmKind(dir);
00257                 }
00258                 if (advance() != ";")
00259                     break;
00260             }
00261         }
00262         QString returnType;
00263         if (keyword == "function") {
00264             if (advance() != ":") {
00265                 kError() << "importPascal: expecting \":\" at function "
00266                         << name << endl;
00267                 return false;
00268             }
00269             returnType = advance();
00270         } else if (keyword == "constructor" || keyword == "destructor") {
00271             op->setStereotype(keyword);
00272         }
00273         skipStmt();
00274         bool isVirtual = false;
00275         bool isAbstract = false;
00276         checkModifiers(isVirtual, isAbstract);
00277         Import_Utils::insertMethod(m_klass, op, m_currentAccess, returnType,
00278                                    !isVirtual, isAbstract, false, false, m_comment);
00279         return true;
00280     }
00281     if (m_section != sect_TYPE) {
00282         skipStmt();
00283         return true;
00284     }
00285     if (m_klass == NULL) {
00286         const QString& name = m_source[m_srcIndex];
00287         QString nextToken = advance();
00288         if (nextToken != "=") {
00289             kDebug() << "PascalImport::parseStmt(" << name << "): "
00290                 << "expecting '=' at " << nextToken << endl;
00291             return false;
00292         }
00293         keyword = advance().lower();
00294         if (keyword == "(") {
00295             // enum type
00296             UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Enum,
00297                             name, m_scope[m_scopeIndex], m_comment);
00298             UMLEnum *enumType = static_cast<UMLEnum*>(ns);
00299             while (++m_srcIndex < srcLength && m_source[m_srcIndex] != ")") {
00300                 Import_Utils::addEnumLiteral(enumType, m_source[m_srcIndex]);
00301                 if (advance() != ",")
00302                     break;
00303             }
00304             skipStmt();
00305             return true;
00306         }
00307         if (keyword == "set") {  // @todo implement Pascal set types
00308             skipStmt();
00309             return true;
00310         }
00311         if (keyword == "array") {  // @todo implement Pascal array types
00312             skipStmt();
00313             return true;
00314         }
00315         if (keyword == "file") {  // @todo implement Pascal file types
00316             skipStmt();
00317             return true;
00318         }
00319         if (keyword == "^") {  // @todo implement Pascal pointer types
00320             skipStmt();
00321             return true;
00322         }
00323         if (keyword == "class" || keyword == "interface") {
00324             Uml::Object_Type t = (keyword == "class" ? Uml::ot_Class : Uml::ot_Interface);
00325             UMLObject *ns = Import_Utils::createUMLObject(t, name,
00326                                                           m_scope[m_scopeIndex], m_comment);
00327             UMLClassifier *klass = static_cast<UMLClassifier*>(ns);
00328             m_comment = QString();
00329             QString lookAhead = m_source[m_srcIndex + 1];
00330             if (lookAhead == "(") {
00331                 advance();
00332                 do {
00333                     QString base = advance();
00334                     UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Class, base, NULL);
00335                     UMLClassifier *parent = static_cast<UMLClassifier*>(ns);
00336                     m_comment = QString();
00337                     Import_Utils::createGeneralization(klass, parent);
00338                 } while (advance() == ",");
00339                 if (m_source[m_srcIndex] != ")") {
00340                     kError() << "PascalImport: expecting \")\" at "
00341                         << m_source[m_srcIndex] << endl;
00342                     return false;
00343                 }
00344                 lookAhead = m_source[m_srcIndex + 1];
00345             }
00346             if (lookAhead == ";") {
00347                 skipStmt();
00348                 return true;
00349             }
00350             if (lookAhead == "of") {
00351                 // @todo implement class-reference type
00352                 return false;
00353             }
00354             m_klass = klass;
00355             m_currentAccess = Uml::Visibility::Public;
00356             return true;
00357         }
00358         if (keyword == "record") {
00359             UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Class, name,
00360                                                           m_scope[m_scopeIndex], m_comment);
00361             ns->setStereotype("record");
00362             m_klass = static_cast<UMLClassifier*>(ns);
00363             return true;
00364         }
00365         if (keyword == "function" || keyword == "procedure") {
00366             UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Datatype, name,
00367                                                           m_scope[m_scopeIndex], m_comment);
00368             if (m_source[m_srcIndex + 1] == "(")
00369                 skipToClosing('(');
00370             skipStmt();
00371             return true;
00372         }
00373         // Datatypes: TO BE DONE
00374         return false;
00375     }
00376     // At this point we need a class because we're expecting its member attributes.
00377     if (m_klass == NULL) {
00378         kDebug() << "importPascal: skipping " << m_source[m_srcIndex] << endl;
00379         skipStmt();
00380         return true;
00381     }
00382     QString name, stereotype;
00383     if (keyword == "property") {
00384         stereotype = keyword;
00385         name = advance();
00386     } else {
00387         name = m_source[m_srcIndex];
00388     }
00389     if (advance() != ":") {
00390         kError() << "PascalImport: expecting \":\" at " << name << " "
00391                   << m_source[m_srcIndex] << endl;
00392         skipStmt();
00393         return true;
00394     }
00395     QString typeName = advance();
00396     QString initialValue;
00397     if (advance() == "=") {
00398         initialValue = advance();
00399         QString token;
00400         while ((token = advance()) != ";") {
00401             initialValue.append(' ' + token);
00402         }
00403     }
00404     UMLObject *o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name,
00405                                                  typeName, m_comment);
00406     UMLAttribute *attr = static_cast<UMLAttribute*>(o);
00407     attr->setStereotype(stereotype);
00408     attr->setInitialValue(initialValue);
00409     skipStmt();
00410     return true;
00411 }
00412 
00413 
KDE Logo
This file is part of the documentation for umbrello Version 3.1.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Jun 26 08:07:59 2007 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003