diff --git a/src/qmljsc/symboltable.cpp b/src/qmljsc/symboltable.cpp index ac2dc69..a6a0f57 100644 --- a/src/qmljsc/symboltable.cpp +++ b/src/qmljsc/symboltable.cpp @@ -1,137 +1,183 @@ /* * Qml.js Compiler - a QML to JS compiler bringing QML's power to the web. * * Copyright (C) 2015 Anton Kreuzkamp * * 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 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "symboltable.h" #include "module.h" using namespace QmlJSc; +ShortSymbolName::ShortSymbolName(char first) + : QString(first) +{} + +ShortSymbolName::ShortSymbolName(QString first) + : QString(first) +{} + +ShortSymbolName &ShortSymbolName::operator++() +{ + Q_ASSERT(!isEmpty()); + + iterator i = end(); + i--; + + char borrow = 1; + + while (i != begin()) { + if ((*i) == '9') { + *i = 'A'; + borrow = 0; + break; + } else if ((*i) == 'Z') { + *i = 'a'; + borrow = 0; + break; + } else if ((*i) == 'z') { + // We need to add a borrow of 1 to the previous digit + *i = '0'; + i--; + borrow = 1; + continue; + } else { + *i = i->toLatin1() + 1; + borrow = 0; + break; + } + } + + if (borrow == 1) { + if (*i <= 'Z') { // the first letter is a capital one, so it should remain so. + if (*i == 'Z') { // We need to prepend a new digit + *i = '0'; + prepend('A'); + } else { + *i = i->toLatin1() + 1; + } + } else { // the first letter is a small one, so it should remain so. + if (*i == 'z') { // We need to prepend a new digit + *i = '0'; + prepend('a'); + } else { + *i = i->toLatin1() + 1; + } + } + } + + return *this; +} + SymbolTable::SymbolTable(QObject* parent) : QObject(parent) - , m_prefixCount(0) + , m_prefix('A' - 1) // we want the prefix to be 'A' after the first preincrement { } SymbolTable::~SymbolTable() { } void SymbolTable::loadModule(ModuleImport import) { if (m_modules.contains(import)) return; if (Module *module = Module::loadedModules.value(import)) { - m_modules.insert(import, { module, getNewPrefix(true) }); + m_modules.insert(import, { module, ++m_prefix }); return; } Module *module = new Module(import); Module::loadedModules.insert(import, module); - m_modules.insert(import, { module, getNewPrefix(true) }); + m_modules.insert(import, { module, ++m_prefix }); connect(module, SIGNAL(importError(QmlJSc::Error)), this, SIGNAL(importError(QmlJSc::Error))); QMetaObject::invokeMethod(module, "doLoad"); } Type *SymbolTable::type(const QString &typeName) { const ModuleData *data = moduleForType(typeName); if (data && data->module) return data->module->type(typeName); return 0; } QString SymbolTable::fullyQualifiedName(const QString &typeName) { return QStringLiteral("%1.%2").arg(moduleForType(typeName)->localPrefix, typeName); } const SymbolTable::ModuleData *SymbolTable::moduleForType(const QString &typeName) { Type *foundType = 0; const ModuleData *moduleData = 0; for (auto i = m_modules.constBegin(); i != m_modules.constEnd(); i++) { if (Type * type = i->module->type(typeName)) { if (foundType) { emit symbolLookupError({ Error::SymbolLookupError, QString("Ambitious type name. Type %1 was defined by module %2 and %3.") .arg(typeName, moduleData->module->name(), i->module->name()) }); return 0; } else { foundType = type; moduleData = &(*i); } } } return moduleData; } -QString SymbolTable::getNewPrefix(bool capital) -{ - QString prefix; - int number = ++m_prefixCount; - - do { - number -= 1; - prefix.prepend((capital ? 'A' : 'a') + (number % 26)); - number = number / 26; - } while(number > 0); - - return prefix; -} - // QString SymbolTable::findType(ModuleImport module, QString typeName) // { // if (!m_modules.contains(module)) // return QString(); // // Module *data = m_modules.value(module); // if (data->status() == Module::Loading) { // Wait for data to be available. // data->waitForLoaded(); // // if (data->status() == Module::ErrorState) // If an error occurred, the module won't exist anymore // return QString(); // } // // // return data->typeToFQIHash.value(typeName); // } // // QString SymbolTable::findType(ModuleImports modules, QString typeName) // { // if (!modules.contains({"QtQml", 2, 0})) { // modules.append({"QtQml", 2, 0}); // } // foreach (ModuleImport module, modules) { // QString result = findType(module, typeName); // if (!result.isEmpty()) { // return result; // } // } // return QString(); // } void SymbolTable::doLoadModule(ModuleImport import) { } diff --git a/src/qmljsc/symboltable.h b/src/qmljsc/symboltable.h index 1491624..1f32a91 100644 --- a/src/qmljsc/symboltable.h +++ b/src/qmljsc/symboltable.h @@ -1,92 +1,112 @@ /* * Qml.js Compiler - a QML to JS compiler bringing QML's power to the web. * * Copyright (C) 2015 Anton Kreuzkamp * * 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 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef SYMBOLTABLE_H #define SYMBOLTABLE_H #include "error.h" #include #include #include #include #include namespace QmlJSc { struct ModuleImport { QString name; int versionMajor; int versionMinor; inline bool operator==(const ModuleImport& other) const { return name == other.name && versionMajor == other.versionMajor && versionMinor == other.versionMinor; } }; class Module; struct Type; inline uint qHash(const ModuleImport &key, uint seed) { return qHash(key.name, seed) ^ key.versionMajor ^ key.versionMinor; } typedef QList ModuleImports; +/** + * This class represents a minified symbol name like "a", "b", "aa", "A8",... + * + * Initialize it with the first character you want to use as symbol name MINUS + * ONE, as you normally will use the preincrement operator before accessing it. + * If you won't, feel free to initialize it with the first symbol name directly, + * of course. + * + * To get the next valid symbol name, use the preincrement operator. + * + * This is a subclass of QString, so you can just use it as a string. + */ +class ShortSymbolName : public QString +{ +public: + ShortSymbolName(char first); + ShortSymbolName(QString first); + + ShortSymbolName &operator++(); +}; + class SymbolTable : public QObject { Q_OBJECT struct ModuleData { Module *module; QString localPrefix; }; public: explicit SymbolTable(QObject *parent = 0); virtual ~SymbolTable(); void loadModule(ModuleImport import); Type* type(const QString &typeName); QString fullyQualifiedName(const QString &typeName); signals: void importError(QmlJSc::Error error); void symbolLookupError(QmlJSc::Error error); private slots: void doLoadModule(ModuleImport import); private: const ModuleData *moduleForType(const QString &typeName); - QString getNewPrefix(bool capital = false); QHash m_modules; - int m_prefixCount; + ShortSymbolName m_prefix; }; } Q_DECLARE_METATYPE(QmlJSc::ModuleImport); #endif // SYMBOLTABLE_H diff --git a/tests/auto/qmljsc/testsymboltable.cpp b/tests/auto/qmljsc/testsymboltable.cpp index d9a7f57..9421eab 100644 --- a/tests/auto/qmljsc/testsymboltable.cpp +++ b/tests/auto/qmljsc/testsymboltable.cpp @@ -1,69 +1,156 @@ /* * Qml.js Compiler - a QML to JS compiler bringing QML's power to the web. * * Copyright (C) 2015 Anton Kreuzkamp * * 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 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include "../../../src/qmljsc/compiler.h" #include "../../../src/qmljsc/symboltable.h" class TestSymbolTable : public QObject { Q_OBJECT private slots: void loadModule(); + void testShortSymbolName(); }; using namespace QmlJSc; void TestSymbolTable::loadModule() { Compiler c; SymbolTable symbolTable; const ModuleImport testModuleImport = {"TestModule", 0, 1}; QSignalSpy spy(&symbolTable, SIGNAL(importError(QmlJSc::Error))); compiler->addIncludePath(":/test/"); symbolTable.loadModule(testModuleImport); if (spy.count()) { QList arguments = spy.takeFirst(); qDebug() << arguments.at(0).value().what(); } QCOMPARE(spy.count(), 0); QVERIFY(symbolTable.type("Pastry")); QCOMPARE(symbolTable.fullyQualifiedName("Pastry"), QStringLiteral("A.Pastry")); QVERIFY(symbolTable.type("Cake")); QCOMPARE(symbolTable.fullyQualifiedName("Cake"), QStringLiteral("A.Cake")); QVERIFY(symbolTable.type("Pizza")); QCOMPARE(symbolTable.fullyQualifiedName("Pizza"), QStringLiteral("A.Pizza")); QVERIFY(!symbolTable.type("Printer")); } +void TestSymbolTable::testShortSymbolName() +{ + ShortSymbolName nA('A'); + ShortSymbolName nB('B'); + ShortSymbolName nZ('Z'); + ShortSymbolName na('a'); + ShortSymbolName nb('b'); + ShortSymbolName nz('z'); + + ShortSymbolName nA0("A0"); + ShortSymbolName nA1("A1"); + ShortSymbolName nA9("A9"); + ShortSymbolName nAA("AA"); + ShortSymbolName nAB("AB"); + ShortSymbolName nAZ("AZ"); + ShortSymbolName nAa("Aa"); + ShortSymbolName nAb("Ab"); + ShortSymbolName nAz("Az"); + + ShortSymbolName na0("a0"); + ShortSymbolName na1("a1"); + ShortSymbolName na9("a9"); + ShortSymbolName naA("aA"); + ShortSymbolName naB("aB"); + ShortSymbolName naZ("aZ"); + ShortSymbolName naa("aa"); + ShortSymbolName nab("ab"); + ShortSymbolName naz("az"); + + ShortSymbolName nZ9("Z9"); + ShortSymbolName nZZ("ZZ"); + ShortSymbolName nZz("Zz"); + ShortSymbolName nz9("z9"); + ShortSymbolName nzZ("zZ"); + ShortSymbolName nzz("zz"); + + + ShortSymbolName nA5dh("A5dh"); + ShortSymbolName nA5dz("A5dz"); + ShortSymbolName nA5zz("A5zz"); + ShortSymbolName nAzzz("Azzz"); + ShortSymbolName nZzzz("Zzzz"); + ShortSymbolName nzzzz("zzzz"); + + QCOMPARE(static_cast(++nA), QStringLiteral("B")); + QCOMPARE(static_cast(++nB), QStringLiteral("C")); + QCOMPARE(static_cast(++nZ), QStringLiteral("A0")); + QCOMPARE(static_cast(++na), QStringLiteral("b")); + QCOMPARE(static_cast(++nb), QStringLiteral("c")); + QCOMPARE(static_cast(++nz), QStringLiteral("a0")); + + QCOMPARE(static_cast(++nA0), QStringLiteral("A1")); + QCOMPARE(static_cast(++nA1), QStringLiteral("A2")); + QCOMPARE(static_cast(++nA9), QStringLiteral("AA")); + QCOMPARE(static_cast(++nAA), QStringLiteral("AB")); + QCOMPARE(static_cast(++nAB), QStringLiteral("AC")); + QCOMPARE(static_cast(++nAZ), QStringLiteral("Aa")); + QCOMPARE(static_cast(++nAa), QStringLiteral("Ab")); + QCOMPARE(static_cast(++nAb), QStringLiteral("Ac")); + QCOMPARE(static_cast(++nAz), QStringLiteral("B0")); + + QCOMPARE(static_cast(++na0), QStringLiteral("a1")); + QCOMPARE(static_cast(++na1), QStringLiteral("a2")); + QCOMPARE(static_cast(++na9), QStringLiteral("aA")); + QCOMPARE(static_cast(++naA), QStringLiteral("aB")); + QCOMPARE(static_cast(++naB), QStringLiteral("aC")); + QCOMPARE(static_cast(++naZ), QStringLiteral("aa")); + QCOMPARE(static_cast(++naa), QStringLiteral("ab")); + QCOMPARE(static_cast(++nab), QStringLiteral("ac")); + QCOMPARE(static_cast(++naz), QStringLiteral("b0")); + + QCOMPARE(static_cast(++nZ9), QStringLiteral("ZA")); + QCOMPARE(static_cast(++nZZ), QStringLiteral("Za")); + QCOMPARE(static_cast(++nZz), QStringLiteral("A00")); + QCOMPARE(static_cast(++nz9), QStringLiteral("zA")); + QCOMPARE(static_cast(++nzZ), QStringLiteral("za")); + QCOMPARE(static_cast(++nzz), QStringLiteral("a00")); + + QCOMPARE(static_cast(++nA5dh), QStringLiteral("A5di")); + QCOMPARE(static_cast(++nA5dz), QStringLiteral("A5e0")); + QCOMPARE(static_cast(++nA5zz), QStringLiteral("A600")); + QCOMPARE(static_cast(++nAzzz), QStringLiteral("B000")); + QCOMPARE(static_cast(++nZzzz), QStringLiteral("A0000")); + QCOMPARE(static_cast(++nzzzz), QStringLiteral("a0000")); +} + QTEST_MAIN(TestSymbolTable) #include "testsymboltable.moc"