diff --git a/test/lexer.cpp b/test/lexer.cpp index 8966cef..7ff8e60 100644 --- a/test/lexer.cpp +++ b/test/lexer.cpp @@ -6,43 +6,44 @@ #include #include -TEST_CASE("Single character token") { - struct test { - token::type expectedType; - std::string expectedLiteral; +TEST_SUITE("Lexer") { + TEST_CASE("Single character token") { + struct test { + token::type expectedType; + std::string expectedLiteral; + }; + + std::string input = "=+(){},;"; + std::istringstream ss(input); + + lexer::lexer l{ss}; + + test tests[] = { + {token::type::ASSIGN, "="}, + {token::type::PLUS, "+"}, + {token::type::LPAREN, "("}, + {token::type::RPAREN, ")"}, + {token::type::LBRACE, "{"}, + {token::type::RBRACE, "}"}, + {token::type::COMMA, ","}, + {token::type::SEMICOLON, ";"}, + {token::type::END_OF_FILE, ""}, + }; + + for (const auto& t : tests) { + token::token tok = l.next_token(); + REQUIRE(tok.type == t.expectedType); + REQUIRE(tok.literal == t.expectedLiteral); + } }; - std::string input = "=+(){},;"; - std::istringstream ss(input); + TEST_CASE("More tokens") { + struct test { + token::type expectedType; + std::string expectedLiteral; + }; - lexer::lexer l{ss}; - - test tests[] = { - {token::type::ASSIGN, "="}, - {token::type::PLUS, "+"}, - {token::type::LPAREN, "("}, - {token::type::RPAREN, ")"}, - {token::type::LBRACE, "{"}, - {token::type::RBRACE, "}"}, - {token::type::COMMA, ","}, - {token::type::SEMICOLON, ";"}, - {token::type::END_OF_FILE, ""}, - }; - - for (const auto& t : tests) { - token::token tok = l.next_token(); - REQUIRE(tok.type == t.expectedType); - REQUIRE(tok.literal == t.expectedLiteral); - } -}; - -TEST_CASE("More tokens") { - struct test { - token::type expectedType; - std::string expectedLiteral; - }; - - std::istringstream ss("let five = 5;\ + std::istringstream ss("let five = 5;\ let ten = 10;\ let add = fn(x, y) {\ x + y;\ @@ -61,101 +62,102 @@ if (5 < 10) {\ 10 != 9;\ "); - lexer::lexer l{ss}; + lexer::lexer l{ss}; - test tests[] = { - // clang-format off - {token::type::LET, "let"}, - {token::type::IDENTIFIER, "five"}, - {token::type::ASSIGN, "="}, - {token::type::INT, "5"}, - {token::type::SEMICOLON, ";"}, + test tests[] = { + // clang-format off + {token::type::LET, "let"}, + {token::type::IDENTIFIER, "five"}, + {token::type::ASSIGN, "="}, + {token::type::INT, "5"}, + {token::type::SEMICOLON, ";"}, - {token::type::LET, "let"}, - {token::type::IDENTIFIER, "ten"}, - {token::type::ASSIGN, "="}, - {token::type::INT, "10"}, - {token::type::SEMICOLON, ";"}, + {token::type::LET, "let"}, + {token::type::IDENTIFIER, "ten"}, + {token::type::ASSIGN, "="}, + {token::type::INT, "10"}, + {token::type::SEMICOLON, ";"}, - {token::type::LET, "let"}, - {token::type::IDENTIFIER, "add"}, - {token::type::ASSIGN, "="}, - {token::type::FUNCTION, "fn"}, - {token::type::LPAREN, "("}, - {token::type::IDENTIFIER, "x"}, - {token::type::COMMA, ","}, - {token::type::IDENTIFIER, "y"}, - {token::type::RPAREN, ")"}, - {token::type::LBRACE, "{"}, - {token::type::IDENTIFIER, "x"}, - {token::type::PLUS, "+"}, - {token::type::IDENTIFIER, "y"}, - {token::type::SEMICOLON, ";"}, - {token::type::RBRACE, "}"}, - {token::type::SEMICOLON, ";"}, + {token::type::LET, "let"}, + {token::type::IDENTIFIER, "add"}, + {token::type::ASSIGN, "="}, + {token::type::FUNCTION, "fn"}, + {token::type::LPAREN, "("}, + {token::type::IDENTIFIER, "x"}, + {token::type::COMMA, ","}, + {token::type::IDENTIFIER, "y"}, + {token::type::RPAREN, ")"}, + {token::type::LBRACE, "{"}, + {token::type::IDENTIFIER, "x"}, + {token::type::PLUS, "+"}, + {token::type::IDENTIFIER, "y"}, + {token::type::SEMICOLON, ";"}, + {token::type::RBRACE, "}"}, + {token::type::SEMICOLON, ";"}, - {token::type::LET, "let"}, - {token::type::IDENTIFIER, "result"}, - {token::type::ASSIGN, "="}, - {token::type::IDENTIFIER, "add"}, - {token::type::LPAREN, "("}, - {token::type::IDENTIFIER, "five"}, - {token::type::COMMA, ","}, - {token::type::IDENTIFIER, "ten"}, - {token::type::RPAREN, ")"}, - {token::type::SEMICOLON, ";"}, + {token::type::LET, "let"}, + {token::type::IDENTIFIER, "result"}, + {token::type::ASSIGN, "="}, + {token::type::IDENTIFIER, "add"}, + {token::type::LPAREN, "("}, + {token::type::IDENTIFIER, "five"}, + {token::type::COMMA, ","}, + {token::type::IDENTIFIER, "ten"}, + {token::type::RPAREN, ")"}, + {token::type::SEMICOLON, ";"}, - {token::type::BANG, "!"}, - {token::type::MINUS, "-"}, - {token::type::SLASH, "/"}, - {token::type::ASTERISK, "*"}, - {token::type::INT, "5"}, - {token::type::SEMICOLON, ";"}, + {token::type::BANG, "!"}, + {token::type::MINUS, "-"}, + {token::type::SLASH, "/"}, + {token::type::ASTERISK, "*"}, + {token::type::INT, "5"}, + {token::type::SEMICOLON, ";"}, - {token::type::INT, "5"}, - {token::type::LT, "<"}, - {token::type::INT, "10"}, - {token::type::GT, ">"}, - {token::type::INT, "5"}, - {token::type::SEMICOLON, ";"}, + {token::type::INT, "5"}, + {token::type::LT, "<"}, + {token::type::INT, "10"}, + {token::type::GT, ">"}, + {token::type::INT, "5"}, + {token::type::SEMICOLON, ";"}, - {token::type::IF, "if"}, - {token::type::LPAREN, "("}, - {token::type::INT, "5"}, - {token::type::LT, "<"}, - {token::type::INT, "10"}, - {token::type::RPAREN, ")"}, - {token::type::LBRACE, "{"}, + {token::type::IF, "if"}, + {token::type::LPAREN, "("}, + {token::type::INT, "5"}, + {token::type::LT, "<"}, + {token::type::INT, "10"}, + {token::type::RPAREN, ")"}, + {token::type::LBRACE, "{"}, - {token::type::RETURN, "return"}, - {token::type::TRUE, "true"}, - {token::type::SEMICOLON, ";"}, + {token::type::RETURN, "return"}, + {token::type::TRUE, "true"}, + {token::type::SEMICOLON, ";"}, - {token::type::RBRACE, "}"}, - {token::type::ELSE, "else"}, - {token::type::LBRACE, "{"}, + {token::type::RBRACE, "}"}, + {token::type::ELSE, "else"}, + {token::type::LBRACE, "{"}, - {token::type::RETURN, "return"}, - {token::type::FALSE, "false"}, - {token::type::SEMICOLON, ";"}, + {token::type::RETURN, "return"}, + {token::type::FALSE, "false"}, + {token::type::SEMICOLON, ";"}, - {token::type::RBRACE, "}"}, + {token::type::RBRACE, "}"}, - {token::type::INT, "10"}, - {token::type::EQ, "=="}, - {token::type::INT, "10"}, - {token::type::SEMICOLON, ";"}, + {token::type::INT, "10"}, + {token::type::EQ, "=="}, + {token::type::INT, "10"}, + {token::type::SEMICOLON, ";"}, - {token::type::INT, "10"}, - {token::type::NEQ, "!="}, - {token::type::INT, "9"}, - {token::type::SEMICOLON, ";"}, - // clang-format on + {token::type::INT, "10"}, + {token::type::NEQ, "!="}, + {token::type::INT, "9"}, + {token::type::SEMICOLON, ";"}, + // clang-format on + }; + + for (const auto& t : tests) { + token::token tok = l.next_token(); + REQUIRE(tok.type == t.expectedType); + REQUIRE(tok.literal == t.expectedLiteral); + } }; - - for (const auto& t : tests) { - token::token tok = l.next_token(); - REQUIRE(tok.type == t.expectedType); - REQUIRE(tok.literal == t.expectedLiteral); - } -}; +} diff --git a/test/parser/let.cpp b/test/parser/let.cpp index b0f8743..4794743 100644 --- a/test/parser/let.cpp +++ b/test/parser/let.cpp @@ -57,66 +57,68 @@ void test_failing_let_parsing( delete program; } -TEST_CASE("Malformed let statement (checking for memory leaks)") { - SUBCASE("Second token not identifier") { - test_failing_let_parsing("let 5 = 5;", {token::type::IDENTIFIER}); +TEST_SUITE("Parser: let") { + TEST_CASE("Malformed let statement (checking for memory leaks)") { + SUBCASE("Second token not identifier") { + test_failing_let_parsing("let 5 = 5;", {token::type::IDENTIFIER}); + } + + SUBCASE("Third token not '='") { + test_failing_let_parsing("let five ! 5;", {token::type::ASSIGN}); + } + + SUBCASE("Missing both identifier and '='") { + test_failing_let_parsing("let 5;", {token::type::IDENTIFIER}); + } + + SUBCASE("Multiple parsing errors") { + test_failing_let_parsing( + "let 5; let ! = 5; let five = 5; let five 5; let;", + {token::type::IDENTIFIER, + token::type::IDENTIFIER, + token::type::ASSIGN, + token::type::IDENTIFIER}, + 1 + ); + } } - SUBCASE("Third token not '='") { - test_failing_let_parsing("let five ! 5;", {token::type::ASSIGN}); - } - - SUBCASE("Missing both identifier and '='") { - test_failing_let_parsing("let 5;", {token::type::IDENTIFIER}); - } - - SUBCASE("Multiple parsing errors") { - test_failing_let_parsing( - "let 5; let ! = 5; let five = 5; let five 5; let;", - {token::type::IDENTIFIER, - token::type::IDENTIFIER, - token::type::ASSIGN, - token::type::IDENTIFIER}, - 1 - ); - } -} - -TEST_CASE("Parse let statement") { - std::stringstream input("\ + TEST_CASE("Parse well formed let statements") { + std::stringstream input("\ let x = 5;\ let y = 10;\ let foobar = 103213;\ "); - lexer::lexer l{input}; - parser::parser p{l}; + lexer::lexer l{input}; + parser::parser p{l}; - ast::program* program = p.parse_program(); - check_parser_errors(p.errors); + ast::program* program = p.parse_program(); + check_parser_errors(p.errors); - REQUIRE_MESSAGE( - program != nullptr, - "parse_program() returned a null pointer" - ); - REQUIRE(program->statements.size() == 3); + REQUIRE_MESSAGE( + program != nullptr, + "parse_program() returned a null pointer" + ); + REQUIRE(program->statements.size() == 3); - struct test { - std::string expected_identifier; - }; + struct test { + std::string expected_identifier; + }; - test tests[]{ - "x", - "y", - "foobar", - }; + test tests[]{ + "x", + "y", + "foobar", + }; - int i = 0; - for (const auto& t : tests) { - ast::statement* stmt = program->statements[i++]; + int i = 0; + for (const auto& t : tests) { + ast::statement* stmt = program->statements[i++]; - test_let_statement(stmt, t.expected_identifier); + test_let_statement(stmt, t.expected_identifier); + } + + delete program; } - - delete program; } diff --git a/test/parser/return.cpp b/test/parser/return.cpp index 6a8136b..1ecb606 100644 --- a/test/parser/return.cpp +++ b/test/parser/return.cpp @@ -6,34 +6,36 @@ #include #include -TEST_CASE("Parse return statement") { - std::stringstream input("\ +TEST_SUITE("Parser: return") { + TEST_CASE("Parse return statement") { + std::stringstream input("\ return 5;\ return 10;\ return 103213;\ "); - lexer::lexer l{input}; - parser::parser p{l}; + lexer::lexer l{input}; + parser::parser p{l}; - ast::program* program = p.parse_program(); - check_parser_errors(p.errors); + ast::program* program = p.parse_program(); + check_parser_errors(p.errors); - REQUIRE_MESSAGE( - program != nullptr, - "parse_program() returned a null pointer" - ); - REQUIRE(program->statements.size() == 3); - - for (const auto stmt : program->statements) { - REQUIRE(stmt->token_literal() == "return"); - ast::return_stmt* let_stmt; - REQUIRE_NOTHROW(let_stmt = dynamic_cast(stmt)); REQUIRE_MESSAGE( - let_stmt != nullptr, - "Couldn't cast statement to a return statement" + program != nullptr, + "parse_program() returned a null pointer" ); - } + REQUIRE(program->statements.size() == 3); - delete program; + for (const auto stmt : program->statements) { + REQUIRE(stmt->token_literal() == "return"); + ast::return_stmt* let_stmt; + REQUIRE_NOTHROW(let_stmt = dynamic_cast(stmt)); + REQUIRE_MESSAGE( + let_stmt != nullptr, + "Couldn't cast statement to a return statement" + ); + } + + delete program; + } }