extracted and abstracted function
This commit is contained in:
@@ -1,14 +1,10 @@
|
|||||||
#include "ast/statements/let.hpp"
|
#include "ast/statements/let.hpp"
|
||||||
|
|
||||||
#include "ast/ast.hpp"
|
#include "ast/ast.hpp"
|
||||||
#include "ast/errors/error.hpp"
|
|
||||||
#include "lexer/lexer.hpp"
|
|
||||||
#include "parser/parser.hpp"
|
|
||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
|
|
||||||
#include <doctest.h>
|
#include <doctest.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
void test_let_statement(ast::statement* stmt, const std::string name) {
|
void test_let_statement(ast::statement* stmt, const std::string name) {
|
||||||
REQUIRE(stmt->token_literal() == "let");
|
REQUIRE(stmt->token_literal() == "let");
|
||||||
@@ -18,52 +14,22 @@ void test_let_statement(ast::statement* stmt, const std::string name) {
|
|||||||
REQUIRE(let_stmt->name->token_literal() == name);
|
REQUIRE(let_stmt->name->token_literal() == name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_failing_let_parsing(
|
|
||||||
std::string input_s,
|
|
||||||
std::vector<token::type> expected_types,
|
|
||||||
int n_good_statements = 0
|
|
||||||
) {
|
|
||||||
std::stringstream input(input_s);
|
|
||||||
|
|
||||||
lexer::lexer l{input};
|
|
||||||
parser::parser p{l};
|
|
||||||
|
|
||||||
std::unique_ptr<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 = cast<ast::error::expected_next>(e);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_SUITE("Parser: let") {
|
TEST_SUITE("Parser: let") {
|
||||||
TEST_CASE("Malformed let statement (checking for memory leaks)") {
|
TEST_CASE("Malformed let statement (checking for memory leaks)") {
|
||||||
SUBCASE("Second token not identifier") {
|
SUBCASE("Second token not identifier") {
|
||||||
test_failing_let_parsing("let 5 = 5;", {token::type::IDENTIFIER});
|
test_failing_parsing("let 5 = 5;", {token::type::IDENTIFIER});
|
||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("Third token not '='") {
|
SUBCASE("Third token not '='") {
|
||||||
test_failing_let_parsing("let five ! 5;", {token::type::ASSIGN});
|
test_failing_parsing("let five ! 5;", {token::type::ASSIGN});
|
||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("Missing both identifier and '='") {
|
SUBCASE("Missing both identifier and '='") {
|
||||||
test_failing_let_parsing("let 5;", {token::type::IDENTIFIER});
|
test_failing_parsing("let 5;", {token::type::IDENTIFIER});
|
||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("Multiple parsing errors") {
|
SUBCASE("Multiple parsing errors") {
|
||||||
test_failing_let_parsing(
|
test_failing_parsing(
|
||||||
"let 5; let ! = 5; let five = 5; let five 5; let;",
|
"let 5; let ! = 5; let five = 5; let five 5; let;",
|
||||||
{token::type::IDENTIFIER,
|
{token::type::IDENTIFIER,
|
||||||
token::type::IDENTIFIER,
|
token::type::IDENTIFIER,
|
||||||
|
@@ -87,3 +87,41 @@ void test_infix_expression(
|
|||||||
CHECK(op_exp->op == op);
|
CHECK(op_exp->op == op);
|
||||||
test_literal_expression(op_exp->right, right);
|
test_literal_expression(op_exp->right, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_failing_parsing(
|
||||||
|
std::string input_s,
|
||||||
|
std::vector<token::type> expected_types,
|
||||||
|
int n_good_statements
|
||||||
|
) {
|
||||||
|
std::stringstream input(input_s);
|
||||||
|
|
||||||
|
lexer::lexer l{input};
|
||||||
|
parser::parser p{l};
|
||||||
|
|
||||||
|
std::unique_ptr<ast::program> program = p.parse_program();
|
||||||
|
|
||||||
|
// Check for errors
|
||||||
|
REQUIRE(p.errors.size() >= expected_types.size());
|
||||||
|
// ^^ because even though you were thinking
|
||||||
|
// about a specific token to be there, other `expect_next -> false` might be
|
||||||
|
// triggered for subexpressions due to the first one
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (auto& t : expected_types) {
|
||||||
|
ast::error::expected_next* en =
|
||||||
|
cast<ast::error::expected_next>(p.errors[i++]);
|
||||||
|
|
||||||
|
REQUIRE(en->expected_type == t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal program check
|
||||||
|
REQUIRE_MESSAGE(
|
||||||
|
program != nullptr,
|
||||||
|
"parse_program() returned a null pointer"
|
||||||
|
);
|
||||||
|
REQUIRE(program->statements.size() >= n_good_statements);
|
||||||
|
// ^^ because even though you were thinking
|
||||||
|
// about a specific number of statements to be there, it failing for
|
||||||
|
// `expect_next` might trigger a sub-expression to be triggered correctly
|
||||||
|
// and be parsed as the expression_stmt
|
||||||
|
}
|
||||||
|
@@ -67,3 +67,4 @@ void test_integer_literal(ast::expression*, int);
|
|||||||
void test_boolean_literal(ast::expression*, bool);
|
void test_boolean_literal(ast::expression*, bool);
|
||||||
void test_literal_expression(ast::expression*, std::any&);
|
void test_literal_expression(ast::expression*, std::any&);
|
||||||
void test_infix_expression(ast::expression*, std::any, std::string, std::any);
|
void test_infix_expression(ast::expression*, std::any, std::string, std::any);
|
||||||
|
void test_failing_parsing(std::string, std::vector<token::type>, int = 0);
|
||||||
|
Reference in New Issue
Block a user