00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "adaimport.h"
00014
00015 #include <stdio.h>
00016
00017 #include <qregexp.h>
00018 #include <kdebug.h>
00019
00020 #include "import_utils.h"
00021 #include "../uml.h"
00022 #include "../umldoc.h"
00023 #include "../package.h"
00024 #include "../folder.h"
00025 #include "../classifier.h"
00026 #include "../enum.h"
00027 #include "../operation.h"
00028 #include "../attribute.h"
00029 #include "../association.h"
00030
00031 AdaImport::AdaImport() : NativeImportBase("--") {
00032 initVars();
00033 }
00034
00035 AdaImport::~AdaImport() {
00036 }
00037
00038 void AdaImport::initVars() {
00039 m_inGenericFormalPart = false;
00040 m_classesDefinedInThisScope.clear();
00041 m_renaming.clear();
00042 }
00043
00046 QStringList AdaImport::split(const QString& lin) {
00047 QStringList list;
00048 QString listElement;
00049 bool inString = false;
00050 bool seenSpace = false;
00051 QString line = lin.stripWhiteSpace();
00052 uint len = line.length();
00053 for (uint i = 0; i < len; i++) {
00054 const QChar& c = line[i];
00055 if (inString) {
00056 listElement += c;
00057 if (c == '"') {
00058 if (i < len - 1 && line[i + 1] == '"') {
00059 i++;
00060 continue;
00061 }
00062 list.append(listElement);
00063 listElement = QString();
00064 inString = false;
00065 }
00066 } else if (c == '"') {
00067 inString = true;
00068 if (!listElement.isEmpty())
00069 list.append(listElement);
00070 listElement = QString(c);
00071 seenSpace = false;
00072 } else if (c == '\'') {
00073 if (i < len - 2 && line[i + 2] == '\'') {
00074
00075 if (!listElement.isEmpty())
00076 list.append(listElement);
00077 listElement = line.mid(i, 3);
00078 i += 2;
00079 list.append(listElement);
00080 listElement = QString();
00081 continue;
00082 }
00083 listElement += c;
00084 seenSpace = false;
00085 } else if (c.isSpace()) {
00086 if (seenSpace)
00087 continue;
00088 seenSpace = true;
00089 if (!listElement.isEmpty()) {
00090 list.append(listElement);
00091 listElement = QString();
00092 }
00093 } else {
00094 listElement += c;
00095 seenSpace = false;
00096 }
00097 }
00098 if (!listElement.isEmpty())
00099 list.append(listElement);
00100 return list;
00101 }
00102
00103 void AdaImport::fillSource(const QString& word) {
00104 QString lexeme;
00105 const uint len = word.length();
00106 for (uint i = 0; i < len; i++) {
00107 QChar c = word[i];
00108 if (c.isLetterOrNumber() || c == '_' || c == '.' || c == '#') {
00109 lexeme += c;
00110 } else {
00111 if (!lexeme.isEmpty()) {
00112 m_source.append(lexeme);
00113 lexeme = QString();
00114 }
00115 if (c == ':' && word[i + 1] == '=') {
00116 m_source.append(":=");
00117 i++;
00118 } else {
00119 m_source.append(QString(c));
00120 }
00121 }
00122 }
00123 if (!lexeme.isEmpty())
00124 m_source.append(lexeme);
00125 }
00126
00127 QString AdaImport::expand(const QString& name) {
00128 QRegExp pfxRegExp("^(\\w+)\\.");
00129 pfxRegExp.setCaseSensitive(false);
00130 int pos = pfxRegExp.search(name);
00131 if (pos == -1)
00132 return name;
00133 QString result = name;
00134 QString pfx = pfxRegExp.cap(1);
00135 if (m_renaming.contains(pfx)) {
00136 result.remove(pfxRegExp);
00137 result.prepend(m_renaming[pfx] + '.');
00138 }
00139 return result;
00140 }
00141
00142 void AdaImport::parseStems(const QStringList& stems) {
00143 if (stems.isEmpty())
00144 return;
00145 QString base = stems.first();
00146 uint i = 0;
00147 while (1) {
00148 QString filename = base + ".ads";
00149 if (! m_parsedFiles.contains(filename)) {
00150
00151 QStringList source(m_source);
00152 uint srcIndex = m_srcIndex;
00153 m_source.clear();
00154 parseFile(filename);
00155
00156 m_source = source;
00157 m_srcIndex = srcIndex;
00158
00159
00160 m_currentAccess = Uml::Visibility::Public;
00161 }
00162 if (++i >= stems.count())
00163 break;
00164 base += '-' + stems[i];
00165 }
00166 }
00167
00168 bool AdaImport::parseStmt() {
00169 const uint srcLength = m_source.count();
00170 QString keyword = m_source[m_srcIndex];
00171 UMLDoc *umldoc = UMLApp::app()->getDocument();
00172
00173 if (keyword == "with") {
00174 if (m_inGenericFormalPart) {
00175
00176 return false;
00177 }
00178 while (++m_srcIndex < srcLength && m_source[m_srcIndex] != ";") {
00179 QStringList components = QStringList::split(".", m_source[m_srcIndex].lower());
00180 const QString& prefix = components.first();
00181 if (prefix == "system" || prefix == "ada" || prefix == "gnat" ||
00182 prefix == "interfaces" || prefix == "text_io" ||
00183 prefix == "unchecked_conversion" ||
00184 prefix == "unchecked_deallocation") {
00185 if (advance() != ",")
00186 break;
00187 continue;
00188 }
00189 parseStems(components);
00190 if (advance() != ",")
00191 break;
00192 }
00193 return true;
00194 }
00195 if (keyword == "generic") {
00196 m_inGenericFormalPart = true;
00197 return true;
00198 }
00199 if (keyword == "package") {
00200 const QString& name = advance();
00201 QStringList parentPkgs = QStringList::split(".", name.lower());
00202 parentPkgs.pop_back();
00203 parseStems(parentPkgs);
00204 UMLObject *ns = NULL;
00205 if (advance() == "is") {
00206 ns = Import_Utils::createUMLObject(Uml::ot_Package, name,
00207 m_scope[m_scopeIndex], m_comment);
00208 if (m_source[m_srcIndex + 1] == "new") {
00209 m_srcIndex++;
00210 QString pkgName = advance();
00211 UMLObject *gp = Import_Utils::createUMLObject(Uml::ot_Package, pkgName,
00212 m_scope[m_scopeIndex]);
00213 gp->setStereotype("generic");
00214
00215 UMLAssociation *assoc = new UMLAssociation(Uml::at_Dependency, ns, gp);
00216 assoc->setUMLPackage(umldoc->getRootFolder(Uml::mt_Logical));
00217 assoc->setStereotype("bind");
00218
00219 assoc->setName(assoc->getStereotype(true));
00220 umldoc->addAssociation(assoc);
00221 skipStmt();
00222 } else {
00223 m_scope[++m_scopeIndex] = static_cast<UMLPackage*>(ns);
00224 }
00225 } else if (m_source[m_srcIndex] == "renames") {
00226 m_renaming[name] = advance();
00227 } else {
00228 kError() << "AdaImport::parseStmt: unexpected: " << m_source[m_srcIndex] << endl;
00229 skipStmt("is");
00230 }
00231 if (m_inGenericFormalPart) {
00232 ns->setStereotype("generic");
00233 m_inGenericFormalPart = false;
00234 }
00235 return true;
00236 }
00237 if (m_inGenericFormalPart)
00238 return false;
00239 if (keyword == "subtype") {
00240 QString name = advance();
00241 advance();
00242 QString base = expand(advance());
00243 base.remove("Standard.", false);
00244 UMLObject *type = umldoc->findUMLObject(base, Uml::ot_UMLObject, m_scope[m_scopeIndex]);
00245 if (type == NULL) {
00246 type = Import_Utils::createUMLObject(Uml::ot_Datatype, base, m_scope[m_scopeIndex]);
00247 }
00248 UMLObject *subtype = Import_Utils::createUMLObject(type->getBaseType(), name,
00249 m_scope[m_scopeIndex], m_comment);
00250 UMLAssociation *assoc = new UMLAssociation(Uml::at_Dependency, subtype, type);
00251 assoc->setUMLPackage(umldoc->getRootFolder(Uml::mt_Logical));
00252 assoc->setStereotype("subtype");
00253
00254 assoc->setName(assoc->getStereotype(true));
00255 umldoc->addAssociation(assoc);
00256 skipStmt();
00257 return true;
00258 }
00259 if (keyword == "type") {
00260 QString name = advance();
00261 QString next = advance();
00262 if (next == "(") {
00263 kDebug() << "AdaImport::parseStmt(" << name << "): "
00264 << "discriminant handling is not yet implemented" << endl;
00265
00266
00267 Import_Utils::createUMLObject(Uml::ot_Class, name, m_scope[m_scopeIndex],
00268 m_comment, "record");
00269 skipStmt("end");
00270 if ((next = advance()) == "case")
00271 m_srcIndex += 2;
00272 skipStmt();
00273 return true;
00274 }
00275 if (next == ";") {
00276
00277 Import_Utils::createUMLObject(Uml::ot_Class, name, m_scope[m_scopeIndex],
00278 m_comment);
00279 return true;
00280 }
00281 if (next != "is") {
00282 kError() << "AdaImport::parseStmt: expecting \"is\"" << endl;
00283 return false;
00284 }
00285 next = advance();
00286 if (next == "(") {
00287
00288 UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Enum,
00289 name, m_scope[m_scopeIndex], m_comment);
00290 UMLEnum *enumType = static_cast<UMLEnum*>(ns);
00291 while ((next = advance()) != ")") {
00292 Import_Utils::addEnumLiteral(enumType, next, m_comment);
00293 m_comment = QString();
00294 if (advance() != ",")
00295 break;
00296 }
00297 skipStmt();
00298 return true;
00299 }
00300 bool isTaggedType = false;
00301 if (next == "abstract") {
00302 m_isAbstract = true;
00303 next = advance();
00304 }
00305 if (next == "tagged") {
00306 isTaggedType = true;
00307 next = advance();
00308 }
00309 if (next == "limited" ||
00310 next == "task" ||
00311 next == "protected" ||
00312 next == "synchronized") {
00313 next = advance();
00314 }
00315 if (next == "private" ||
00316 next == "interface" ||
00317 next == "record" ||
00318 (next == "null" &&
00319 m_source[m_srcIndex+1] == "record")) {
00320 Uml::Object_Type t = (next == "interface" ? Uml::ot_Interface : Uml::ot_Class);
00321 UMLObject *ns = Import_Utils::createUMLObject(t, name, m_scope[m_scopeIndex], m_comment);
00322 if (t == Uml::ot_Interface) {
00323 while ((next = advance()) == "and") {
00324 UMLClassifier *klass = static_cast<UMLClassifier*>(ns);
00325 QString base = expand(advance());
00326 UMLObject *p = Import_Utils::createUMLObject(Uml::ot_Interface, base, m_scope[m_scopeIndex]);
00327 UMLClassifier *parent = static_cast<UMLClassifier*>(p);
00328 Import_Utils::createGeneralization(klass, parent);
00329 }
00330 } else {
00331 ns->setAbstract(m_isAbstract);
00332 }
00333 m_isAbstract = false;
00334 if (isTaggedType) {
00335 if (! m_classesDefinedInThisScope.contains(ns))
00336 m_classesDefinedInThisScope.append(ns);
00337 } else {
00338 ns->setStereotype("record");
00339 }
00340 if (next == "record")
00341 m_klass = static_cast<UMLClassifier*>(ns);
00342 else
00343 skipStmt();
00344 return true;
00345 }
00346 if (next == "new") {
00347 QString base = expand(advance());
00348 QStringList baseInterfaces;
00349 while ((next = advance()) == "and") {
00350 baseInterfaces.append(expand(advance()));
00351 }
00352 const bool isExtension = (next == "with");
00353 Uml::Object_Type t;
00354 if (isExtension || m_isAbstract) {
00355 t = Uml::ot_Class;
00356 } else {
00357 base.remove("Standard.", false);
00358 UMLObject *known = umldoc->findUMLObject(base, Uml::ot_UMLObject, m_scope[m_scopeIndex]);
00359 t = (known ? known->getBaseType() : Uml::ot_Datatype);
00360 }
00361 UMLObject *ns = Import_Utils::createUMLObject(t, base, NULL);
00362 UMLClassifier *parent = static_cast<UMLClassifier*>(ns);
00363 ns = Import_Utils::createUMLObject(t, name, m_scope[m_scopeIndex], m_comment);
00364 if (isExtension) {
00365 next = advance();
00366 if (next == "null" || next == "record") {
00367 UMLClassifier *klass = static_cast<UMLClassifier*>(ns);
00368 Import_Utils::createGeneralization(klass, parent);
00369 if (next == "record") {
00370
00371 m_klass = klass;
00372 }
00373 if (baseInterfaces.count()) {
00374 t = Uml::ot_Interface;
00375 QStringList::Iterator end(baseInterfaces.end());
00376 for (QStringList::Iterator bi(baseInterfaces.begin()); bi != end; ++bi) {
00377 ns = Import_Utils::createUMLObject(t, *bi, m_scope[m_scopeIndex]);
00378 parent = static_cast<UMLClassifier*>(ns);
00379 Import_Utils::createGeneralization(klass, parent);
00380 }
00381 }
00382 }
00383 }
00384 skipStmt();
00385 return true;
00386 }
00387
00388 return false;
00389 }
00390 if (keyword == "private") {
00391 m_currentAccess = Uml::Visibility::Private;
00392 return true;
00393 }
00394 if (keyword == "end") {
00395 if (m_klass) {
00396 if (advance() != "record") {
00397 kError() << "end: expecting \"record\" at "
00398 << m_source[m_srcIndex] << endl;
00399 }
00400 m_klass = NULL;
00401 } else if (m_scopeIndex) {
00402 if (advance() != ";") {
00403 QString scopeName = m_scope[m_scopeIndex]->getFullyQualifiedName();
00404 if (scopeName.lower() != m_source[m_srcIndex].lower())
00405 kError() << "end: expecting " << scopeName << ", found "
00406 << m_source[m_srcIndex] << endl;
00407 }
00408 m_scopeIndex--;
00409 m_currentAccess = Uml::Visibility::Public;
00410 } else {
00411 kError() << "importAda: too many \"end\"" << endl;
00412 }
00413 skipStmt();
00414 return true;
00415 }
00416
00417 if (keyword == "not")
00418 keyword = advance();
00419 if (keyword == "overriding")
00420 keyword = advance();
00421 if (keyword == "function" || keyword == "procedure") {
00422 const QString& name = advance();
00423 QString returnType;
00424 if (advance() != "(") {
00425
00426
00427
00428
00429 kDebug() << "ignoring parameterless " << keyword << " " << name << endl;
00430 skipStmt();
00431 return true;
00432 }
00433 UMLClassifier *klass = NULL;
00434 UMLOperation *op = NULL;
00435 const uint MAX_PARNAMES = 16;
00436 while (m_srcIndex < srcLength && m_source[m_srcIndex] != ")") {
00437 QString parName[MAX_PARNAMES];
00438 uint parNameCount = 0;
00439 do {
00440 if (parNameCount >= MAX_PARNAMES) {
00441 kError() << "MAX_PARNAMES is exceeded at " << name << endl;
00442 break;
00443 }
00444 parName[parNameCount++] = advance();
00445 } while (advance() == ",");
00446 if (m_source[m_srcIndex] != ":") {
00447 kError() << "importAda: expecting ':'" << endl;
00448 skipStmt();
00449 break;
00450 }
00451 const QString &direction = advance();
00452 QString typeName;
00453 Uml::Parameter_Direction dir = Uml::pd_In;
00454 if (direction == "access") {
00455
00456
00457
00458
00459 dir = Uml::pd_InOut;
00460 typeName = advance();
00461 } else if (direction == "in") {
00462 if (m_source[m_srcIndex + 1] == "out") {
00463 dir = Uml::pd_InOut;
00464 m_srcIndex++;
00465 }
00466 typeName = advance();
00467 } else if (direction == "out") {
00468 dir = Uml::pd_Out;
00469 typeName = advance();
00470 } else {
00471 typeName = direction;
00472 }
00473 typeName.remove("Standard.", false);
00474 typeName = expand(typeName);
00475 if (op == NULL) {
00476
00477 UMLObject *type = Import_Utils::createUMLObject(Uml::ot_Class, typeName, m_scope[m_scopeIndex]);
00478 Uml::Object_Type t = type->getBaseType();
00479 if ((t != Uml::ot_Interface &&
00480 (t != Uml::ot_Class || type->getStereotype() == "record")) ||
00481 !m_classesDefinedInThisScope.contains(type)) {
00482
00483 skipStmt(")");
00484 break;
00485 }
00486 klass = static_cast<UMLClassifier*>(type);
00487 op = Import_Utils::makeOperation(klass, name);
00488
00489 parNameCount--;
00490 if (parNameCount) {
00491 for (uint i = 0; i < parNameCount; i++)
00492 parName[i] = parName[i + 1];
00493 }
00494 }
00495 for (uint i = 0; i < parNameCount; i++) {
00496 UMLAttribute *att = Import_Utils::addMethodParameter(op, typeName, parName[i]);
00497 att->setParmKind(dir);
00498 }
00499 if (advance() != ";")
00500 break;
00501 }
00502 if (keyword == "function") {
00503 if (advance() != "return") {
00504 if (klass)
00505 kError() << "importAda: expecting \"return\" at function "
00506 << name << endl;
00507 return false;
00508 }
00509 returnType = expand(advance());
00510 returnType.remove("Standard.", false);
00511 }
00512 bool isAbstract = false;
00513 if (advance() == "is" && advance() == "abstract")
00514 isAbstract = true;
00515 if (klass != NULL && op != NULL)
00516 Import_Utils::insertMethod(klass, op, m_currentAccess, returnType,
00517 false, isAbstract, false, false, m_comment);
00518 skipStmt();
00519 return true;
00520 }
00521 if (keyword == "task" || keyword == "protected") {
00522
00523 bool isType = false;
00524 QString name = advance();
00525 if (name == "type") {
00526 isType = true;
00527 name = advance();
00528 }
00529 QString next = advance();
00530 if (next == "(") {
00531 skipStmt(")");
00532 next = advance();
00533 }
00534 if (next == "is")
00535 skipStmt("end");
00536 skipStmt();
00537 return true;
00538 }
00539 if (keyword == "for") {
00540 QString typeName = advance();
00541 QString next = advance();
00542 if (next == "'") {
00543 advance();
00544 next = advance();
00545 }
00546 if (next == "use") {
00547 if (advance() == "record")
00548 skipStmt("end");
00549 } else {
00550 kError() << "importAda: expecting \"use\" at rep spec of "
00551 << typeName << endl;
00552 }
00553 skipStmt();
00554 return true;
00555 }
00556
00557 if (m_klass == NULL || keyword == "null") {
00558 skipStmt();
00559 return true;
00560 }
00561 const QString& name = keyword;
00562 if (advance() != ":") {
00563 kError() << "adaImport: expecting \":\" at " << name << " "
00564 << m_source[m_srcIndex] << endl;
00565 skipStmt();
00566 return true;
00567 }
00568 QString nextToken = advance();
00569 if (nextToken == "aliased")
00570 nextToken = advance();
00571 QString typeName = expand(nextToken);
00572 QString initialValue;
00573 if (advance() == ":=") {
00574 initialValue = advance();
00575 QString token;
00576 while ((token = advance()) != ";") {
00577 initialValue.append(' ' + token);
00578 }
00579 }
00580 UMLObject *o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name,
00581 typeName, m_comment);
00582 UMLAttribute *attr = static_cast<UMLAttribute*>(o);
00583 attr->setInitialValue(initialValue);
00584 skipStmt();
00585 return true;
00586 }
00587
00588