#include #include #include "BXParser.h" #include "catch.hpp" static BXVariables parse(const char *str) { bufio::ISStream stream(str); BXParser parser(stream); BXVariables vars; parser.parse(vars); return vars; } std::string stringify(std::vector &vec) { if (vec.size() == 0) { return "(empty)"; } std::string s; for (auto &val: vec) { if (s.size() == 0) { s += '\'' + val + '\''; } else { s += " '" + val + '\''; } } return s; } static void expecteq( const char *str, const std::initializer_list>> list) { INFO("Source: " << str); bufio::ISStream stream(str); BXParser parser(stream); BXVariables vars; parser.parse(vars); std::unordered_set keyset; for (auto &expected: list) { keyset.insert(expected.first); auto it = vars.find(expected.first); if (it == vars.end()) { FAIL("Missing key '" << expected.first << "'!"); } if (it->second != expected.second) { INFO("Value: " << stringify(it->second)); FAIL("Value of key '" << expected.first << "' doesn't match!"); } } for (auto &pair: vars) { if (keyset.find(pair.first) == keyset.end()) { FAIL("Unexpected key '" << pair.first << "'!"); } } } static void lexeq(BXVariables &vars, const char *str, std::initializer_list list) { INFO("Source: " << str); bufio::ISStream stream(str); BXParser parser(stream); parser.readToken(vars); size_t i = 1; for (auto &expected: list) { BXParser::Token tok = parser.readToken(vars); if (tok.kind != expected.kind) { INFO("Expected: " << (int)expected.kind); INFO("Actual: " << (int)tok.kind); FAIL("Token " << i << " doesn't match"); } if (tok.str != expected.str) { INFO("Expected: '" << expected.str << '\''); INFO("Actual: '" << tok.str << '\''); FAIL("Token " << i << "'s string doesn't match"); } if (tok.kind == BXParser::TokenKind::E_O_F) { return; } i += 1; } } static void lexeq(const char *str, std::initializer_list list) { BXVariables vars; lexeq(vars, str, list); } TEST_CASE("BXParser lex", "[BXParser][lex]") { lexeq("hello world", { { BXParser::TokenKind::STRING, "hello" }, { BXParser::TokenKind::STRING, "world" }, { BXParser::TokenKind::E_O_F }, }); lexeq("hello := what's up", { { BXParser::TokenKind::STRING, "hello" }, { BXParser::TokenKind::COLON_EQUALS }, { BXParser::TokenKind::STRING, "what's" }, { BXParser::TokenKind::STRING, "up" }, { BXParser::TokenKind::E_O_F }, }); lexeq("\t\t \t, := += =+\n\n\r\nhello", { { BXParser::TokenKind::INDENTATION }, { BXParser::TokenKind::COMMA }, { BXParser::TokenKind::COLON_EQUALS }, { BXParser::TokenKind::PLUS_EQUALS }, { BXParser::TokenKind::EQUALS_PLUS }, { BXParser::TokenKind::NEWLINE }, { BXParser::TokenKind::STRING, "hello" }, { BXParser::TokenKind::E_O_F }, }); } TEST_CASE("BXParser lex string interpolation", "[BXParser][lex]") { BXVariables vars = { { "foo", { "hello"} }, }; lexeq(vars, "hey \"$foo\" lol", { { BXParser::TokenKind::STRING, "hey" }, { BXParser::TokenKind::STRING, "hello" }, { BXParser::TokenKind::STRING, "lol" }, }); lexeq(vars, "hey \"no$foo\" lol", { { BXParser::TokenKind::STRING, "hey" }, { BXParser::TokenKind::STRING, "nohello" }, { BXParser::TokenKind::STRING, "lol" }, }); lexeq(vars, "hey \"Xx${foo}xX\" lol", { { BXParser::TokenKind::STRING, "hey" }, { BXParser::TokenKind::STRING, "XxhelloxX" }, { BXParser::TokenKind::STRING, "lol" }, }); } TEST_CASE("BXParser lex string interpolation without quotes", "[BXParser][lex]") { BXVariables vars = { { "foo", { "hello"} }, }; lexeq(vars, "hey Xx$foo lol", { { BXParser::TokenKind::STRING, "hey" }, { BXParser::TokenKind::STRING, "Xxhello" }, { BXParser::TokenKind::STRING, "lol" }, }); lexeq(vars, "hey Xx${ foo }xX lol", { { BXParser::TokenKind::STRING, "hey" }, { BXParser::TokenKind::STRING, "XxhelloxX" }, { BXParser::TokenKind::STRING, "lol" }, }); } TEST_CASE("BXParser lex array expansion", "[BXParser][lex]") { lexeq("hey $foo lol", { { BXParser::TokenKind::STRING, "hey" }, { BXParser::TokenKind::EXPANSION, "foo" }, { BXParser::TokenKind::STRING, "lol" }, }); lexeq("hey ${foo} lol", { { BXParser::TokenKind::STRING, "hey" }, { BXParser::TokenKind::EXPANSION, "foo" }, { BXParser::TokenKind::STRING, "lol" }, }); lexeq("hey ${ \tfoo\n \n} lol", { { BXParser::TokenKind::STRING, "hey" }, { BXParser::TokenKind::EXPANSION, "foo" }, { BXParser::TokenKind::STRING, "lol" }, }); } TEST_CASE("BXParser parsing", "[BXParser][parse]") { expecteq("hello := 10, world := 20", { { "hello", { "10" } }, { "world", { "20" } }, }); expecteq("foo := hello world, bar := 10 20 30, baz := 0", { { "foo", { "hello", "world" } }, { "bar", { "10", "20", "30" } }, { "baz", { "0" } }, }); } TEST_CASE("BXParser parsing variables", "[BXParser][parse]") { expecteq("foo := \"hello world\", bar := $foo", { { "foo", { "hello world" } }, { "bar", { "hello world" } }, }); expecteq("foo := hello world, bar := hey $foo what's up", { { "foo", { "hello", "world" } }, { "bar", { "hey", "hello", "world", "what's", "up" } }, }); } TEST_CASE("BXParser append/prepend", "[BXParser][parse]") { expecteq("foo := 10, foo += 20, foo += 30", { { "foo", { "10", "20", "30" } }, }); expecteq("foo := 10, foo =+ 20, foo =+ 30", { { "foo", { "30", "20", "10" } }, }); } TEST_CASE("BXParser |=", "[BXParser][parse]") { expecteq("foo := 10, foo |= 10, foo |= 20", { { "foo", { "10", "20" } }, }); } TEST_CASE("BXParser newline separation", "[BXParser][parse]") { expecteq( "foo := 10 20\n" "bar := 30 40\n", { { "foo", { "10", "20" } }, { "bar", { "30", "40" } }, }); } TEST_CASE("BXParser newline continuation", "[BXParser][parse]") { expecteq( "foo := 10 20\n" "\t30 40\n" "bar := hello world\n" "\tbaz \":=\" 30\n", { { "foo", { "10", "20", "30", "40" } }, { "bar", { "hello", "world", "baz", ":=", "30" } }, }); }