#include "parser/parser.hpp" #include "ast/ast.hpp" #include "ast/statements/let.hpp" #include "lexer/lexer.hpp" #include #include #include void test_let_statement(ast::statement* stmt, const std::string name) { REQUIRE(stmt->token_literal() == "let"); ast::let* let_stmt; REQUIRE_NOTHROW(let_stmt = dynamic_cast(stmt)); REQUIRE_MESSAGE( let_stmt != nullptr, "Couldn't cast statement to a let statement" ); REQUIRE(let_stmt->name->value == name); REQUIRE(let_stmt->name->token_literal() == name); } void test_failing_let_parsing( std::string input_s, std::vector expected_types, int n_good_statements = 0 ) { std::stringstream input(input_s); lexer::lexer l{input}; parser::parser p{l}; ast::program* program = p.parse_program(); // Check for errors REQUIRE(p.errors.size() == expected_types.size()); int i = 0; for (auto& e : p.errors) { ast::error::expected_next* en; REQUIRE_NOTHROW(en = dynamic_cast(e)); REQUIRE_MESSAGE( en != nullptr, "Couldn't cast the error to an 'expected_next'" ); REQUIRE(en->expected_type == expected_types[i++]); } // normal program check REQUIRE_MESSAGE( program != nullptr, "parse_program() returned a null pointer" ); REQUIRE(program->statements.size() == n_good_statements); delete program; } void check_parser_errors(const std::vector& errors) { if (errors.empty()) return; std::cerr << "parser has " << errors.size() << " errors:\n"; for (const auto& error : errors) std::cerr << '\t' << error->what() << "\n"; // Use doctest's FAIL macro to immediately stop FAIL_CHECK("Parser had errors. See stderr for details."); } 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 ); } } TEST_CASE("Parse let statement") { std::stringstream input("\ let x = 5;\ let y = 10;\ let foobar = 103213;\ "); lexer::lexer l{input}; parser::parser p{l}; 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); struct test { std::string expected_identifier; }; test tests[]{ "x", "y", "foobar", }; int i = 0; for (const auto& t : tests) { ast::statement* stmt = program->statements[i++]; test_let_statement(stmt, t.expected_identifier); } delete program; }