#include "../../extern/doctest.hpp" #include "../../src/Parser/Parser.hpp" using namespace MalachScript; #define PARSER_TEST(name, tokens, asserts) \ TEST_CASE(name) { \ std::vector vec = { \ tokens, \ new Parser::LexTokenImpl(TextSpan(0, 0)), \ }; \ for (size_t i = 0; i < vec.size() - 1; i++) { \ vec[i]->SetNext(vec[i + 1]); \ } \ Diagnostics::Logger diags; \ auto* script = Parser::Parser::Parse(vec.front(), &diags); \ REQUIRE(diags.GetMessages().empty()); \ asserts; \ delete vec[0]; \ delete script; \ } #define PARSER_TEST_TOKENS(...) __VA_ARGS__ #define TextSpan(a, b) ScriptTextSpan(a, b, "") PARSER_TEST("Parse ``void foobar();``", PARSER_TEST_TOKENS(new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "foobar"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0))), { REQUIRE(script->GetStatements().size() == 1); REQUIRE(script->GetStatements()[0].get()->GetKind() == Parser::ParsedStatementKind::Func); auto funcStat = (const MalachScript::Parser::ParsedFuncStatement*)script->GetStatements()[0].get(); CHECK_FALSE(funcStat->IsShared()); CHECK_FALSE(funcStat->IsExternal()); CHECK(funcStat->GetAccess() == MalachScript::AccessModifier::Public); auto type = (const MalachScript::Parser::ParsedTypeStatement*)funcStat->GetTypeStatement().get(); CHECK_FALSE(type->IsConst()); CHECK_FALSE(type->IsArray()); CHECK_FALSE(type->IsHandle()); auto& id = type->GetScopedIdentifier(); CHECK(id.GetIdentifier().GetString() == "void"); CHECK_FALSE(funcStat->ReturnsReference()); CHECK(funcStat->GetIdentifier().GetString() == "foobar"); auto paramList = (const MalachScript::Parser::ParsedParamListStatement*)funcStat->GetParamList().get(); CHECK(paramList->GetParameters().empty()); CHECK_FALSE(funcStat->IsConst()); CHECK(funcStat->GetFuncAttr() == MalachScript::FuncAttr::None); CHECK(funcStat->GetStatBlock() == nullptr); }) PARSER_TEST("Parse scoped function without body.", PARSER_TEST_TOKENS(new Parser::IdentifierToken(TextSpan(0, 0), "foo"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "bar"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "baz"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "foobar"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0))), { REQUIRE(script->GetStatements().size() == 1); REQUIRE(script->GetStatements()[0].get()->GetKind() == Parser::ParsedStatementKind::Func); auto funcStat = (const MalachScript::Parser::ParsedFuncStatement*)script->GetStatements()[0].get(); CHECK_FALSE(funcStat->IsShared()); CHECK_FALSE(funcStat->IsExternal()); CHECK(funcStat->GetAccess() == MalachScript::AccessModifier::Public); auto type = (const MalachScript::Parser::ParsedTypeStatement*)funcStat->GetTypeStatement().get(); CHECK_FALSE(type->IsConst()); CHECK_FALSE(type->IsArray()); CHECK_FALSE(type->IsHandle()); auto& id = type->GetScopedIdentifier(); CHECK(id.GetIdentifier().GetString() == "baz"); CHECK(id.GetScope()[1].GetString() == "bar"); CHECK(id.GetScope()[0].GetString() == "foo"); CHECK_FALSE(funcStat->ReturnsReference()); CHECK(funcStat->GetIdentifier().GetString() == "foobar"); auto paramList = (const MalachScript::Parser::ParsedParamListStatement*)funcStat->GetParamList().get(); CHECK(paramList->GetParameters().empty()); CHECK_FALSE(funcStat->IsConst()); CHECK(funcStat->GetFuncAttr() == MalachScript::FuncAttr::None); CHECK(funcStat->GetStatBlock() == nullptr); }) PARSER_TEST("Parse scoped function with parameters without body.", PARSER_TEST_TOKENS(new Parser::IdentifierToken(TextSpan(0, 0), "foo"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "bar"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "baz"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "foobar"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "par1"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "par2"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0))), { REQUIRE(script->GetStatements().size() == 1); REQUIRE(script->GetStatements()[0].get()->GetKind() == Parser::ParsedStatementKind::Func); auto funcStat = (const MalachScript::Parser::ParsedFuncStatement*)script->GetStatements()[0].get(); CHECK_FALSE(funcStat->IsShared()); CHECK_FALSE(funcStat->IsExternal()); CHECK(funcStat->GetAccess() == MalachScript::AccessModifier::Public); auto type = (const MalachScript::Parser::ParsedTypeStatement*)funcStat->GetTypeStatement().get(); CHECK_FALSE(type->IsConst()); CHECK_FALSE(type->IsArray()); CHECK_FALSE(type->IsHandle()); auto& id = type->GetScopedIdentifier(); CHECK(id.GetIdentifier().GetString() == "baz"); CHECK(id.GetScope()[1].GetString() == "bar"); CHECK(id.GetScope()[0].GetString() == "foo"); CHECK_FALSE(funcStat->ReturnsReference()); CHECK(funcStat->GetIdentifier().GetString() == "foobar"); auto paramList = (const MalachScript::Parser::ParsedParamListStatement*)funcStat->GetParamList().get(); CHECK(paramList->GetParameters().size() == 2); auto& par1 = *paramList->GetParameters()[0]; CHECK_FALSE(par1.GetTypeStatement()->IsConst()); CHECK_FALSE(par1.GetTypeStatement()->IsArray()); CHECK_FALSE(par1.GetTypeStatement()->IsHandle()); auto& par1TypeId = par1.GetTypeStatement()->GetScopedIdentifier(); CHECK(par1TypeId.GetIdentifier().GetString() == "int"); CHECK(par1.GetTypeMod() == TypeMod::None); CHECK(par1.GetIdentifier().GetString() == "par1"); CHECK(par1.GetDefaultExpression() == nullptr); auto& par2 = *paramList->GetParameters()[1]; CHECK_FALSE(par2.GetTypeStatement()->IsConst()); CHECK_FALSE(par2.GetTypeStatement()->IsArray()); CHECK_FALSE(par2.GetTypeStatement()->IsHandle()); auto& par2TypeId = par2.GetTypeStatement()->GetScopedIdentifier(); CHECK(par2TypeId.GetIdentifier().GetString() == "bool"); CHECK(par2.GetTypeMod() == TypeMod::None); CHECK(par2.GetIdentifier().GetString() == "par2"); CHECK(par2.GetDefaultExpression() == nullptr); CHECK_FALSE(funcStat->IsConst()); CHECK(funcStat->GetFuncAttr() == MalachScript::FuncAttr::None); CHECK(funcStat->GetStatBlock() == nullptr); }) PARSER_TEST("Parse scoped function with reference parameters without body.", PARSER_TEST_TOKENS(new Parser::IdentifierToken(TextSpan(0, 0), "foo"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "bar"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "baz"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "foobar"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "par1"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::IdentifierToken(TextSpan(0, 0), "par2"), new Parser::LexTokenImpl(TextSpan(0, 0)), new Parser::LexTokenImpl(TextSpan(0, 0))), { REQUIRE(script->GetStatements().size() == 1); REQUIRE(script->GetStatements()[0].get()->GetKind() == Parser::ParsedStatementKind::Func); auto funcStat = (const MalachScript::Parser::ParsedFuncStatement*)script->GetStatements()[0].get(); CHECK_FALSE(funcStat->IsShared()); CHECK_FALSE(funcStat->IsExternal()); CHECK(funcStat->GetAccess() == MalachScript::AccessModifier::Public); auto type = (const MalachScript::Parser::ParsedTypeStatement*)funcStat->GetTypeStatement().get(); CHECK_FALSE(type->IsConst()); CHECK_FALSE(type->IsArray()); CHECK_FALSE(type->IsHandle()); auto& id = type->GetScopedIdentifier(); CHECK(id.GetIdentifier().GetString() == "baz"); CHECK(id.GetScope()[1].GetString() == "bar"); CHECK(id.GetScope()[0].GetString() == "foo"); CHECK_FALSE(funcStat->ReturnsReference()); CHECK(funcStat->GetIdentifier().GetString() == "foobar"); auto paramList = (const MalachScript::Parser::ParsedParamListStatement*)funcStat->GetParamList().get(); CHECK(paramList->GetParameters().size() == 2); auto& par1 = *paramList->GetParameters()[0]; CHECK_FALSE(par1.GetTypeStatement()->IsConst()); CHECK_FALSE(par1.GetTypeStatement()->IsArray()); CHECK_FALSE(par1.GetTypeStatement()->IsHandle()); auto& par1TypeId = par1.GetTypeStatement()->GetScopedIdentifier(); CHECK(par1TypeId.GetIdentifier().GetString() == "int"); CHECK(par1.GetTypeMod() == TypeMod::RefIn); CHECK(par1.GetIdentifier().GetString() == "par1"); CHECK(par1.GetDefaultExpression() == nullptr); auto& par2 = *paramList->GetParameters()[1]; CHECK_FALSE(par2.GetTypeStatement()->IsConst()); CHECK_FALSE(par2.GetTypeStatement()->IsArray()); CHECK_FALSE(par2.GetTypeStatement()->IsHandle()); auto& par2TypeId = par2.GetTypeStatement()->GetScopedIdentifier(); CHECK(par2TypeId.GetIdentifier().GetString() == "bool"); CHECK(par2.GetTypeMod() == TypeMod::RefOut); CHECK(par2.GetIdentifier().GetString() == "par2"); CHECK(par2.GetDefaultExpression() == nullptr); CHECK_FALSE(funcStat->IsConst()); CHECK(funcStat->GetFuncAttr() == MalachScript::FuncAttr::None); CHECK(funcStat->GetStatBlock() == nullptr); })