diff --git a/parser/test/parsertest.h b/parser/test/parsertest.h --- a/parser/test/parsertest.h +++ b/parser/test/parsertest.h @@ -24,6 +24,9 @@ namespace go { +class Lexer; +class AstNode; + class ParserTest : public QObject { Q_OBJECT @@ -34,15 +37,23 @@ void testKeyWords(); void testOperators(); void testRunes(); + void testNumbers(); + void testNumbers_data(); + void testCommentsAreIgnored(); + void testCommentsAreIgnored_data(); void testBasicTypes(); void testIfClause(); void testFuncTypes(); void testForRangeLoop(); + void testForSingleConditionalLoop(); + void testForWithForClauseLoop(); + void testForWithEmptySingleConditionLoop(); void testEmptyLabeledStmt(); void testMapKeyLiteralValue(); //Go 1.5 feature - +private: + QByteArray getCodeFromNode(const QByteArray &code, go::Lexer *lexer, go::AstNode *node); }; } #endif \ No newline at end of file diff --git a/parser/test/parsertest.cpp b/parser/test/parsertest.cpp --- a/parser/test/parsertest.cpp +++ b/parser/test/parsertest.cpp @@ -138,14 +138,74 @@ } +void ParserTest::testNumbers() +{ + QFETCH(QByteArray, code); + QFETCH(int, tokenType); + KDevPG::QByteArrayIterator iter(code); + Lexer lexer(iter); + auto token = lexer.read(); + QCOMPARE((TokenType)token.kind, (TokenType)tokenType); +} + +void ParserTest::testNumbers_data() +{ + QTest::addColumn("code"); + QTest::addColumn("tokenType"); + + QTest::newRow("integer 42") << QByteArray("42") << (int)TokenTypeWrapper::Token_INTEGER; + QTest::newRow("integer 0600") << QByteArray("0600") << (int)TokenTypeWrapper::Token_INTEGER; + QTest::newRow("integer 0xDEADBEEF") << QByteArray("0xDEADBEEF") << (int)TokenTypeWrapper::Token_INTEGER; + QTest::newRow("float 0.") << QByteArray("0.") << (int)TokenTypeWrapper::Token_FLOAT; + QTest::newRow("float 1.12") << QByteArray("1.12") << (int)TokenTypeWrapper::Token_FLOAT; + QTest::newRow("float 1.e+2") << QByteArray("1.e+2") << (int)TokenTypeWrapper::Token_FLOAT; + QTest::newRow("float 1.e-2") << QByteArray("1.e-2") << (int)TokenTypeWrapper::Token_FLOAT; + QTest::newRow("float 1E5") << QByteArray("1E5") << (int)TokenTypeWrapper::Token_FLOAT; + QTest::newRow("float .123") << QByteArray(".123") << (int)TokenTypeWrapper::Token_FLOAT; + QTest::newRow("complex number 0i") << QByteArray("0i") << (int)TokenTypeWrapper::Token_COMPLEX; + QTest::newRow("complex number 011i") << QByteArray("011i") << (int)TokenTypeWrapper::Token_COMPLEX; + QTest::newRow("complex number 0.i") << QByteArray("0.i") << (int)TokenTypeWrapper::Token_COMPLEX; + QTest::newRow("complex number 2.3i") << QByteArray("2.3i") << (int)TokenTypeWrapper::Token_COMPLEX; + QTest::newRow("complex number 1.e+0i") << QByteArray("1.e+0i") << (int)TokenTypeWrapper::Token_COMPLEX; + QTest::newRow("complex number 5.5e-3i") << QByteArray("5.5e-3i") << (int)TokenTypeWrapper::Token_COMPLEX; + QTest::newRow("complex number 1E6i") << QByteArray("1E6i") << (int)TokenTypeWrapper::Token_COMPLEX; + QTest::newRow("complex number .23i") << QByteArray(".23i") << (int)TokenTypeWrapper::Token_COMPLEX; + QTest::newRow("complex number .123E+4i") << QByteArray(".123E+4i") << (int)TokenTypeWrapper::Token_COMPLEX; +} + void prepareParser(const QByteArray& code, go::Parser** parser, go::Lexer** lexer) { KDevPG::TokenStream tokenStream; *parser = new go::Parser(); (*parser)->setMemoryPool(new KDevPG::MemoryPool()); KDevPG::QByteArrayIterator iter(code); *lexer = new go::Lexer(iter); (*parser)->setTokenStream(*lexer); + (*parser)->rewind(0); +} + +QByteArray ParserTest::getCodeFromNode(const QByteArray &code, go::Lexer *lexer, go::AstNode *node) +{ + auto startToken = lexer->at(node->startToken); + auto endToken = lexer->at(node->endToken); + return code.mid(startToken.begin, endToken.end - startToken.begin+1); +} + +void ParserTest::testCommentsAreIgnored() +{ + QFETCH(QByteArray, code); + go::Lexer *lexer; + lexer = new go::Lexer(go::Lexer::ByteStringIterator(code)); + QCOMPARE(lexer->read().kind, (int)TokenType::Token_EOF); +} + +void ParserTest::testCommentsAreIgnored_data() +{ + QTest::addColumn("code"); + + QTest::newRow("Single-lined comment") << QByteArray("//test\n"); + QTest::newRow("Multiple single-lined comments") << QByteArray("//test\n//test\n"); + QTest::newRow("Multi-lined comment") << QByteArray("/*\ntest\n*/\n"); } void ParserTest::testBasicTypes() @@ -175,9 +235,9 @@ QString content = code; const KDevPG::ListNode *node = ast->complexType->structType->fieldDeclSequence->front(); //qDebug() << code.mid(lexer.at(node->element->startToken).begin, lexer.at(node->element->endToken).end - lexer.at(node->element->startToken).begin+1); - QVERIFY( code.mid(lexer.at(node->element->startToken).begin, lexer.at(node->element->endToken).end - lexer.at(node->element->startToken).begin+1) - == "array [5][2]string"); - + auto actual = getCodeFromNode(code, &lexer, node->element); + QByteArray expected = "array [5][2]string"; + QCOMPARE(actual, expected); } @@ -237,6 +297,96 @@ QVERIFY(session.startParsing()); } +void ParserTest::testForSingleConditionalLoop() +{ + QByteArray code = "for i < 1 { i += 1 }\n"; + QByteArray expectedConditionCode = "i < 1"; + QByteArray expectedBlockCode = "{ i += 1 }"; + go::Parser *parser; + go::Lexer *lexer; + go::StatementsAst* ast; + prepareParser(code, &parser, &lexer); + + bool result = parser->parseStatements(&ast); + QVERIFY(result); + + auto forStmt = ast->statementSequence->front()->element->forStmt; + auto condition = forStmt->initStmtOrCondition; + auto block = forStmt->block; + bool foundCStyleCondition = forStmt->condition; + bool foundPostStmt = forStmt->postStmt; + QVERIFY(condition); + QVERIFY(block); + QVERIFY(!foundCStyleCondition); + QVERIFY(!foundPostStmt); + + auto conditionCode = getCodeFromNode(code, lexer, condition); + auto blockCode = getCodeFromNode(code, lexer, block); + QCOMPARE(conditionCode, expectedConditionCode); + QCOMPARE(blockCode, expectedBlockCode); +} + +void ParserTest::testForWithForClauseLoop() +{ + QByteArray code = "for i := 0; i < 10; i++ { f(i) }\n"; + QByteArray expectedInitStmtCode = "i := 0"; + QByteArray expectedConditionCode = "i < 10"; + QByteArray expectedPostStmtCode = "i++"; + QByteArray expectedBlockCode = "{ f(i) }"; + go::Parser *parser; + go::Lexer *lexer; + go::StatementsAst* ast; + prepareParser(code, &parser, &lexer); + + bool result = parser->parseStatements(&ast); + QVERIFY(result); + + auto forStmt = ast->statementSequence->front()->element->forStmt; + auto initStmt = forStmt->initStmtOrCondition; + auto condition = forStmt->condition; + auto postStmt = forStmt->postStmt; + auto block = forStmt->block; + QVERIFY(initStmt); + QVERIFY(condition); + QVERIFY(postStmt); + QVERIFY(block); + + auto initStmtCode = getCodeFromNode(code, lexer, initStmt); + auto conditionCode = getCodeFromNode(code, lexer, condition); + auto postStmtCode = getCodeFromNode(code, lexer, postStmt); + auto blockCode = getCodeFromNode(code, lexer, block); + QCOMPARE(initStmtCode, expectedInitStmtCode); + QCOMPARE(conditionCode, expectedConditionCode); + QCOMPARE(postStmtCode, expectedPostStmtCode); + QCOMPARE(blockCode, expectedBlockCode); +} + +void ParserTest::testForWithEmptySingleConditionLoop() +{ + QByteArray code = "for { i += 1 }\n"; + QByteArray expectedBlockCode = "{ i += 1 }"; + go::Parser *parser; + go::Lexer *lexer; + go::StatementsAst* ast; + prepareParser(code, &parser, &lexer); + + bool result = parser->parseStatements(&ast); + QVERIFY(result); + + auto forStmt = ast->statementSequence->front()->element->forStmt; + auto conditionNode = forStmt->initStmtOrCondition; + auto block = forStmt->block; + bool foundCStyleCondition = forStmt->condition; + bool foundPostStmt = forStmt->postStmt; + QVERIFY(!conditionNode); + QVERIFY(!foundCStyleCondition); + QVERIFY(!foundPostStmt); + QVERIFY(block); + + auto blockCode = getCodeFromNode(code, lexer, block); + QCOMPARE(blockCode, expectedBlockCode); +} + void ParserTest::testEmptyLabeledStmt() { QString code("package main; func main() { for i:=1; i<5; i++ { a := i; emptylabel: \n } }");