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.
589 lines
22 KiB
589 lines
22 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) 2005-2007 *
|
|
* Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
|
|
***************************************************************************/
|
|
|
|
// own header
|
|
#include "adaimport.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 "../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.
|
|
TQStringList AdaImport::split(const TQString& lin) {
|
|
TQStringList list;
|
|
TQString listElement;
|
|
bool inString = false;
|
|
bool seenSpace = false;
|
|
TQString line = lin.stripWhiteSpace();
|
|
uint len = line.length();
|
|
for (uint i = 0; i < len; i++) {
|
|
const TQChar& 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 = TQString();
|
|
inString = false;
|
|
}
|
|
} else if (c == '"') {
|
|
inString = true;
|
|
if (!listElement.isEmpty())
|
|
list.append(listElement);
|
|
listElement = TQString(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 = TQString();
|
|
continue;
|
|
}
|
|
listElement += c;
|
|
seenSpace = false;
|
|
} else if (c.isSpace()) {
|
|
if (seenSpace)
|
|
continue;
|
|
seenSpace = true;
|
|
if (!listElement.isEmpty()) {
|
|
list.append(listElement);
|
|
listElement = TQString();
|
|
}
|
|
} else {
|
|
listElement += c;
|
|
seenSpace = false;
|
|
}
|
|
}
|
|
if (!listElement.isEmpty())
|
|
list.append(listElement);
|
|
return list;
|
|
}
|
|
|
|
void AdaImport::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);
|
|
}
|
|
|
|
TQString AdaImport::expand(const TQString& name) {
|
|
TQRegExp pfxRegExp("^(\\w+)\\.");
|
|
pfxRegExp.setCaseSensitive(false);
|
|
int pos = pfxRegExp.search(name);
|
|
if (pos == -1)
|
|
return name;
|
|
TQString result = name;
|
|
TQString pfx = pfxRegExp.cap(1);
|
|
if (m_renaming.contains(pfx)) {
|
|
result.remove(pfxRegExp);
|
|
result.prepend(m_renaming[pfx] + '.');
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void AdaImport::parseStems(const TQStringList& stems) {
|
|
if (stems.isEmpty())
|
|
return;
|
|
TQString base = stems.first();
|
|
uint i = 0;
|
|
while (1) {
|
|
TQString filename = base + ".ads";
|
|
if (! m_parsedFiles.contains(filename)) {
|
|
// 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 (++i >= stems.count())
|
|
break;
|
|
base += '-' + stems[i];
|
|
}
|
|
}
|
|
|
|
bool AdaImport::parseStmt() {
|
|
const uint srcLength = m_source.count();
|
|
TQString 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] != ";") {
|
|
TQStringList components = TQStringList::split(".", m_source[m_srcIndex].lower());
|
|
const TQString& 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 TQString& name = advance();
|
|
TQStringList parentPkgs = TQStringList::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++;
|
|
TQString 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") {
|
|
TQString name = advance();
|
|
advance(); // "is"
|
|
TQString 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") {
|
|
TQString name = advance();
|
|
TQString 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 = TQString();
|
|
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);
|
|
TQString 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") {
|
|
TQString base = expand(advance());
|
|
TQStringList 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;
|
|
TQStringList::Iterator end(baseInterfaces.end());
|
|
for (TQStringList::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() != ";") {
|
|
TQString 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 TQString& name = advance();
|
|
TQString 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] != ")") {
|
|
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() << "importAda: expecting ':'" << endl;
|
|
skipStmt();
|
|
break;
|
|
}
|
|
const TQString &direction = advance();
|
|
TQString 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;
|
|
TQString name = advance();
|
|
if (name == "type") {
|
|
isType = true;
|
|
name = advance();
|
|
}
|
|
TQString next = advance();
|
|
if (next == "(") {
|
|
skipStmt(")"); // skip discriminant
|
|
next = advance();
|
|
}
|
|
if (next == "is")
|
|
skipStmt("end");
|
|
skipStmt();
|
|
return true;
|
|
}
|
|
if (keyword == "for") { // rep spec
|
|
TQString typeName = advance();
|
|
TQString 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 TQString& name = keyword;
|
|
if (advance() != ":") {
|
|
kError() << "adaImport: expecting \":\" at " << name << " "
|
|
<< m_source[m_srcIndex] << endl;
|
|
skipStmt();
|
|
return true;
|
|
}
|
|
TQString nextToken = advance();
|
|
if (nextToken == "aliased")
|
|
nextToken = advance();
|
|
TQString typeName = expand(nextToken);
|
|
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->setInitialValue(initialValue);
|
|
skipStmt();
|
|
return true;
|
|
}
|
|
|
|
|