diff -r 000000000000 -r 1918ee327afb util/lexgen/tests/tst_lexgen.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/lexgen/tests/tst_lexgen.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the utils of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#define AUTOTEST +#include "../main.cpp" + +class tst_LexGen : public QObject +{ + Q_OBJECT +private slots: + void nfa_singleInput(); + void nfa_alternating(); + void nfa_concatenating(); + void nfa_optional(); + void nfa_toDFA_data(); + void nfa_toDFA(); + void lexgen_data(); + void lexgen(); +}; + +void tst_LexGen::nfa_singleInput() +{ + NFA nfa = NFA::createSingleInputNFA('a'); + + QCOMPARE(nfa.initialState, 0); + QCOMPARE(nfa.finalState, 1); + + QCOMPARE(nfa.states.count(), 2); + + QCOMPARE(nfa.states.at(0).transitions.count(), 1); + QVERIFY(nfa.states.at(0).transitions.contains('a')); + QCOMPARE(nfa.states.at(0).transitions.values('a').count(), 1); + QCOMPARE(nfa.states.at(0).transitions.value('a'), nfa.finalState); + + QVERIFY(nfa.states.at(1).transitions.isEmpty()); +} + +void tst_LexGen::nfa_alternating() +{ + NFA a = NFA::createSingleInputNFA('a'); + NFA b = NFA::createSingleInputNFA('b'); + NFA nfa = NFA::createAlternatingNFA(a, b); + + const int initialA = 1; + const int finalA = 2; + + const int initialB = 3; + const int finalB = 4; + + QCOMPARE(nfa.states.count(), 6); + + QCOMPARE(nfa.initialState, 0); + QCOMPARE(nfa.finalState, 5); + + QList initialTransitions = nfa.states.at(0).transitions.values(Epsilon); + QCOMPARE(initialTransitions.count(), 2); + QVERIFY(initialTransitions.contains(initialA)); + QVERIFY(initialTransitions.contains(initialB)); + + // no need to test the individual a and b NFAs, the other + // autotest already takes care of that. Just check whether + // the epsilon transitions to the final state exist. + + QCOMPARE(nfa.states.at(finalA).transitions.count(), 1); + QCOMPARE(nfa.states.at(finalA).transitions.values(Epsilon).count(), 1); + QCOMPARE(nfa.states.at(finalA).transitions.value(Epsilon), nfa.finalState); + + QCOMPARE(nfa.states.at(finalB).transitions.count(), 1); + QCOMPARE(nfa.states.at(finalB).transitions.values(Epsilon).count(), 1); + QCOMPARE(nfa.states.at(finalB).transitions.value(Epsilon), nfa.finalState); +} + +void tst_LexGen::nfa_concatenating() +{ + NFA a = NFA::createSingleInputNFA('a'); + NFA b = NFA::createSingleInputNFA('b'); + NFA nfa = NFA::createConcatenatingNFA(a, b); + + const int initialA = 1; + const int finalA = 2; + + const int initialB = 3; + const int finalB = 4; + + QCOMPARE(nfa.states.count(), 6); + + QCOMPARE(nfa.initialState, 0); + QCOMPARE(nfa.finalState, 5); + + QCOMPARE(nfa.states.at(0).transitions.count(), 1); + QCOMPARE(nfa.states.at(0).transitions.values(Epsilon).count(), 1); + QCOMPARE(nfa.states.at(0).transitions.value(Epsilon), initialA); + + QCOMPARE(nfa.states.at(finalA).transitions.values(Epsilon).count(), 1); + QCOMPARE(nfa.states.at(finalA).transitions.value(Epsilon), initialB); + + QCOMPARE(nfa.states.at(finalB).transitions.values(Epsilon).count(), 1); + QCOMPARE(nfa.states.at(finalB).transitions.value(Epsilon), nfa.finalState); +} + +void tst_LexGen::nfa_optional() +{ + NFA a = NFA::createSingleInputNFA('a'); + NFA nfa = NFA::createOptionalNFA(a); + + const int initialA = 1; + const int finalA = 2; + + QCOMPARE(nfa.states.count(), 4); + + QCOMPARE(nfa.initialState, 0); + QCOMPARE(nfa.finalState, 3); + + QCOMPARE(nfa.states.at(0).transitions.count(), 2); + QList initialTransitions = nfa.states.at(0).transitions.values(Epsilon); + QVERIFY(initialTransitions.contains(nfa.finalState)); + QVERIFY(initialTransitions.contains(initialA)); + + QList finalEpsilonATransitions = nfa.states.at(finalA).transitions.values(Epsilon); + QVERIFY(finalEpsilonATransitions.contains(initialA)); + QVERIFY(finalEpsilonATransitions.contains(nfa.finalState)); +} + +Q_DECLARE_METATYPE(NFA); +Q_DECLARE_METATYPE(DFA); + +void tst_LexGen::nfa_toDFA_data() +{ + QTest::addColumn("nfa"); + QTest::addColumn("expectedDFA"); + + NFA a = NFA::createSingleInputNFA('a'); + NFA b = NFA::createSingleInputNFA('b'); + NFA c = NFA::createSingleInputNFA('c'); + + NFA nfa; + DFA dfa; + + dfa.clear(); + dfa.resize(3); + dfa[0].transitions.insert('a', 1); + dfa[1].transitions.insert('b', 2); + + nfa = NFA::createConcatenatingNFA(a, b); + + QTest::newRow("simple concat") << nfa << dfa; + + dfa.clear(); + dfa.resize(3); + dfa[0].transitions.insert('a', 1); + dfa[0].transitions.insert('b', 2); + + nfa = NFA::createAlternatingNFA(a, b); + + QTest::newRow("simple alternate") << nfa << dfa; + +} + +void tst_LexGen::nfa_toDFA() +{ + QFETCH(NFA, nfa); + QFETCH(DFA, expectedDFA); + + DFA dfa = nfa.toDFA(); + + QCOMPARE(dfa.count(), expectedDFA.count()); + for (int i = 0; i < dfa.count(); ++i) { + if (dfa.at(i).transitions != expectedDFA.at(i).transitions) { + qDebug() << "DFAs differ in state" << i; + qDebug() << "NFA:"; + nfa.debug(); + qDebug() << "Actual DFA:"; + dfa.debug(); + qDebug() << "Expected DFA:"; + expectedDFA.debug(); + QVERIFY(false); + } + } +} + +void tst_LexGen::lexgen_data() +{ + QTest::addColumn("ruleFile"); + QTest::addColumn("input"); + QTest::addColumn("expectedOutput"); + + QDir d(QString(SRCDIR)); + d.cd("testdata"); + foreach (QString test, d.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { + QString dir = d.absoluteFilePath(test) + '/'; + QTest::newRow(qPrintable(test)) + << dir + "rules.lexgen" + << dir + "input" + << dir + "output" + ; + } +} + +void tst_LexGen::lexgen() +{ + QFETCH(QString, ruleFile); + QFETCH(QString, input); + QFETCH(QString, expectedOutput); + + Config conf; + QVERIFY(loadConfig(ruleFile, &conf)); + DFA dfa = generateMachine(conf); + QVERIFY(!dfa.isEmpty()); + conf.debug = true; + + QFile f(input); + QVERIFY(f.open(QIODevice::ReadOnly)); + input = QString::fromUtf8(f.readAll()); + f.close(); + if (input.endsWith(QLatin1Char('\n'))) + input.chop(1); +// machine.debug(); + bool ok = false; + QList symbols = tokenize(dfa, input, &conf, &ok); + QVERIFY(ok); + f.setFileName(expectedOutput); + QVERIFY(f.open(QIODevice::ReadOnly)); + QStringList lines; + while (!f.atEnd()) { + QString line = QString::fromUtf8(f.readLine()); + if (line.endsWith(QLatin1Char('\n'))) + line.chop(1); + lines << line; + } + f.close(); + +// dfa.debug(); + QCOMPARE(lines.count(), symbols.count()); + + for (int i = 0; i < lines.count(); ++i) { + QStringList l = lines.at(i).split(QChar::fromLatin1('|')); + QCOMPARE(l.count(), 2); + QString expectedToken = l.at(0); + QString expectedLexem = l.at(1); + QCOMPARE(symbols.at(i).token, expectedToken); + QCOMPARE(symbols.at(i).lexem, expectedLexem); + } +} + +QTEST_MAIN(tst_LexGen) +#include "tst_lexgen.moc"