#include #include "src/camellya.h" using namespace camellya; TEST_CASE("VM - Basic arithmetic", "[vm]") { State state(ExecutionMode::VM); SECTION("Addition") { REQUIRE(state.do_string("var x = 10 + 20;")); auto x = state.get_global("x"); REQUIRE(x != nullptr); REQUIRE(x->type() == Type::NUMBER); REQUIRE(std::dynamic_pointer_cast(x)->value == 30.0); } SECTION("Subtraction") { REQUIRE(state.do_string("var y = 50 - 15;")); auto y = state.get_global("y"); REQUIRE(y != nullptr); REQUIRE(std::dynamic_pointer_cast(y)->value == 35.0); } SECTION("Multiplication") { REQUIRE(state.do_string("var z = 7 * 8;")); auto z = state.get_global("z"); REQUIRE(z != nullptr); REQUIRE(std::dynamic_pointer_cast(z)->value == 56.0); } SECTION("Division") { REQUIRE(state.do_string("var w = 100 / 4;")); auto w = state.get_global("w"); REQUIRE(w != nullptr); REQUIRE(std::dynamic_pointer_cast(w)->value == 25.0); } } TEST_CASE("VM - Variables and assignment", "[vm]") { State state(ExecutionMode::VM); SECTION("Variable declaration and initialization") { REQUIRE(state.do_string("var a = 42;")); auto a = state.get_global("a"); REQUIRE(a != nullptr); REQUIRE(std::dynamic_pointer_cast(a)->value == 42.0); } SECTION("Variable assignment") { REQUIRE(state.do_string(R"( var b = 10; b = 20; )")); auto b = state.get_global("b"); REQUIRE(std::dynamic_pointer_cast(b)->value == 20.0); } } TEST_CASE("VM - String operations", "[vm]") { State state(ExecutionMode::VM); SECTION("String concatenation") { REQUIRE(state.do_string(R"(var greeting = "Hello" + " " + "World";)")); auto greeting = state.get_global("greeting"); REQUIRE(greeting != nullptr); REQUIRE(greeting->type() == Type::STRING); REQUIRE(std::dynamic_pointer_cast(greeting)->value == "Hello World"); } } TEST_CASE("VM - Comparison operators", "[vm]") { State state(ExecutionMode::VM); SECTION("Equality") { REQUIRE(state.do_string("var eq = 10 == 10;")); auto eq = state.get_global("eq"); REQUIRE(std::dynamic_pointer_cast(eq)->value == true); } SECTION("Greater than") { REQUIRE(state.do_string("var gt = 20 > 10;")); auto gt = state.get_global("gt"); REQUIRE(std::dynamic_pointer_cast(gt)->value == true); } SECTION("Less than") { REQUIRE(state.do_string("var lt = 5 < 10;")); auto lt = state.get_global("lt"); REQUIRE(std::dynamic_pointer_cast(lt)->value == true); } } TEST_CASE("VM - Lists", "[vm]") { State state(ExecutionMode::VM); SECTION("Create list") { REQUIRE(state.do_string("var numbers = [1, 2, 3, 4, 5];")); auto numbers = state.get_global("numbers"); REQUIRE(numbers != nullptr); REQUIRE(numbers->type() == Type::LIST); auto list = std::dynamic_pointer_cast(numbers); REQUIRE(list->size() == 5); } SECTION("List indexing") { REQUIRE(state.do_string(R"( var arr = [10, 20, 30]; var item = arr[1]; )")); auto item = state.get_global("item"); REQUIRE(std::dynamic_pointer_cast(item)->value == 20.0); } } TEST_CASE("VM - Maps", "[vm]") { State state(ExecutionMode::VM); SECTION("Create map") { REQUIRE(state.do_string(R"(var person = {"name": "Alice", "age": "30"};)")); auto person = state.get_global("person"); REQUIRE(person != nullptr); REQUIRE(person->type() == Type::MAP); } SECTION("Map access") { REQUIRE(state.do_string(R"( var data = {"key": "value"}; var val = data["key"]; )")); auto val = state.get_global("val"); REQUIRE(val->type() == Type::STRING); REQUIRE(std::dynamic_pointer_cast(val)->value == "value"); } } TEST_CASE("VM - If statements", "[vm]") { State state(ExecutionMode::VM); SECTION("If branch taken") { REQUIRE(state.do_string(R"( var x = 10; if (x > 5) { x = 100; } )")); auto x = state.get_global("x"); REQUIRE(std::dynamic_pointer_cast(x)->value == 100.0); } SECTION("Else branch taken") { REQUIRE(state.do_string(R"( var y = 3; if (y > 5) { y = 100; } else { y = 200; } )")); auto y = state.get_global("y"); REQUIRE(std::dynamic_pointer_cast(y)->value == 200.0); } } TEST_CASE("VM - While loops", "[vm]") { State state(ExecutionMode::VM); SECTION("While loop") { REQUIRE(state.do_string(R"( var counter = 0; var sum = 0; while (counter < 5) { sum = sum + counter; counter = counter + 1; } )")); auto sum = state.get_global("sum"); REQUIRE(std::dynamic_pointer_cast(sum)->value == 10.0); } } TEST_CASE("VM - Native functions", "[vm]") { State state(ExecutionMode::VM); SECTION("len function") { REQUIRE(state.do_string(R"( var arr = [1, 2, 3, 4]; var size = len(arr); )")); auto size = state.get_global("size"); REQUIRE(std::dynamic_pointer_cast(size)->value == 4.0); } } TEST_CASE("VM vs Interpreter - Same results", "[vm][interpreter]") { const char* script = R"( var x = 10; var y = 20; var sum = x + y; var product = x * y; )"; State vm_state(ExecutionMode::VM); State interp_state(ExecutionMode::INTERPRETER); REQUIRE(vm_state.do_string(script)); REQUIRE(interp_state.do_string(script)); auto vm_sum = vm_state.get_global("sum"); auto interp_sum = interp_state.get_global("sum"); REQUIRE(std::dynamic_pointer_cast(vm_sum)->value == std::dynamic_pointer_cast(interp_sum)->value); auto vm_product = vm_state.get_global("product"); auto interp_product = interp_state.get_global("product"); REQUIRE(std::dynamic_pointer_cast(vm_product)->value == std::dynamic_pointer_cast(interp_product)->value); } TEST_CASE("class init is called on declaration", "[vm][class][init]") { State state(ExecutionMode::VM); const char* script = R"( class Person { var age : number; var name : string; func init() -> nil { age = 18; name = "Default"; } func getAge() -> number { return this.age; } } var p : Person; var a = p.getAge(); )"; auto ret = state.do_string(script); if(!ret) { REQUIRE(state.get_error() == ""); } auto p_val = state.get_global("p"); REQUIRE(p_val); REQUIRE(p_val->type() == Type::INSTANCE); auto instance = std::dynamic_pointer_cast(p_val); REQUIRE(instance); auto age_val = instance->get("age"); auto name_val = instance->get("name"); REQUIRE(age_val->type() == Type::NUMBER); REQUIRE(name_val->type() == Type::STRING); auto age_num = std::dynamic_pointer_cast(age_val); auto name_str = std::dynamic_pointer_cast(name_val); REQUIRE(age_num->value == 18.0); REQUIRE(name_str->value == "Default"); auto a_val = state.get_global("a"); REQUIRE(a_val); REQUIRE(a_val->type() == Type::NUMBER); auto a_num = std::dynamic_pointer_cast(a_val); REQUIRE(a_num->value == 18.0); }