You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdesdk/umbrello/umbrello/codeimport/pascalimport.cpp

414 lines
15 KiB

/***************************************************************************
* *
* 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 <tqregexp.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 TQString& word) {
TQString lexeme;
const uint len = word.length();
for (uint i = 0; i < len; i++) {
TQChar c = word[i];
if (c.isLetterOrNumber() || c == '_' || c == '.' || c == '#') {
lexeme += c;
} else {
if (!lexeme.isEmpty()) {
m_source.append(lexeme);
lexeme = TQString();
}
if (c == ':' && word[i + 1] == '=') {
m_source.append(":=");
i++;
} else {
m_source.append(TQString(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) {
TQString 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();
TQString keyword = m_source[m_srcIndex].lower();
//kDebug() << '"' << keyword << '"' << endl;
if (keyword == "uses") {
while (m_srcIndex < srcLength - 1) {
TQString unit = advance();
const TQString& 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;
}
TQString filename = unit + ".pas";
if (! m_parsedFiles.contains(unit)) {
// Save current m_source and m_srcIndex.
TQStringList 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 TQString& 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 TQString& 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] != ")") {
TQString 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();
}
TQString 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;
}
}
TQString 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 TQString& name = m_source[m_srcIndex];
TQString 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 = TQString();
TQString lookAhead = m_source[m_srcIndex + 1];
if (lookAhead == "(") {
advance();
do {
TQString base = advance();
UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Class, base, NULL);
UMLClassifier *tqparent = static_cast<UMLClassifier*>(ns);
m_comment = TQString();
Import_Utils::createGeneralization(klass, tqparent);
} 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;
}
TQString 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;
}
TQString typeName = advance();
TQString initialValue;
if (advance() == "=") {
initialValue = advance();
TQString 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;
}