diff --git a/CMakeLists.txt b/CMakeLists.txt index 978d708..bac6e38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,6 @@ set(LIB_SOURCES src/lexer.cpp src/parser.cpp src/value.cpp - src/interpreter.cpp src/state.cpp src/chunk.cpp src/compiler.cpp @@ -27,7 +26,6 @@ set(LIB_HEADERS src/parser.h src/ast.h src/value.h - src/interpreter.h src/state.h src/chunk.h src/compiler.h diff --git a/src/state.cpp b/src/state.cpp index d842b3b..cfcfa22 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -4,171 +4,143 @@ namespace camellya { -State::State(ExecutionMode mode) - : execution_mode_(mode), - interpreter_(std::make_unique()), - vm_(std::make_unique()), - compiler_(std::make_unique()) {} +State::State() + : vm_(std::make_unique()), compiler_(std::make_unique()) {} -bool State::do_string(const std::string& script) { - try { - Lexer lexer(script); - auto tokens = lexer.tokenize(); - - Parser parser(std::move(tokens)); - Program program = parser.parse(); - - bool success = execute_program(program); - if (success) { - last_error_.clear(); - } - return success; - } catch (const std::exception& e) { - last_error_ = e.what(); - return false; +bool State::do_string(const std::string &script) { + try { + Lexer lexer(script); + auto tokens = lexer.tokenize(); + + Parser parser(std::move(tokens)); + Program program = parser.parse(); + + bool success = execute_program(program); + if (success) { + last_error_.clear(); } + return success; + } catch (const std::exception &e) { + last_error_ = e.what(); + return false; + } } -bool State::do_file(const std::string& filename) { - std::ifstream file(filename); - if (!file.is_open()) { - last_error_ = "Failed to open file: " + filename; - return false; - } - - std::stringstream buffer; - buffer << file.rdbuf(); - return do_string(buffer.str()); +bool State::do_file(const std::string &filename) { + std::ifstream file(filename); + if (!file.is_open()) { + last_error_ = "Failed to open file: " + filename; + return false; + } + + std::stringstream buffer; + buffer << file.rdbuf(); + return do_string(buffer.str()); } -void State::register_function(const std::string& name, NativeFunction func) { - auto func_value = std::make_shared(name, func); - - if (execution_mode_ == ExecutionMode::INTERPRETER) { - interpreter_->global_environment->define(name, func_value); - } else { - vm_->register_native_function(name, func); - } +void State::register_function(const std::string &name, NativeFunction func) { + auto func_value = std::make_shared(name, func); + vm_->register_native_function(name, func); } -ValuePtr State::get_global(const std::string& name) { - if (execution_mode_ == ExecutionMode::INTERPRETER) { - return interpreter_->global_environment->get(name); - } else { - return vm_->get_global(name); - } +ValuePtr State::get_global(const std::string &name) { + return vm_->get_global(name); } -void State::set_global(const std::string& name, ValuePtr value) { - if (execution_mode_ == ExecutionMode::INTERPRETER) { - interpreter_->global_environment->define(name, value); - } else { - vm_->set_global(name, value); - } +void State::set_global(const std::string &name, ValuePtr value) { + vm_->set_global(name, value); } void State::push_number(double value) { - stack_.push_back(std::make_shared(value)); + stack_.push_back(std::make_shared(value)); } -void State::push_string(const std::string& value) { - stack_.push_back(std::make_shared(value)); +void State::push_string(const std::string &value) { + stack_.push_back(std::make_shared(value)); } void State::push_bool(bool value) { - stack_.push_back(std::make_shared(value)); + stack_.push_back(std::make_shared(value)); } -void State::push_nil() { - stack_.push_back(std::make_shared()); -} +void State::push_nil() { stack_.push_back(std::make_shared()); } -void State::push_value(ValuePtr value) { - stack_.push_back(std::move(value)); -} +void State::push_value(ValuePtr value) { stack_.push_back(std::move(value)); } double State::to_number(int index) { - ValuePtr val = get_stack_value(index); - if (val && val->type() == Type::NUMBER) { - return std::dynamic_pointer_cast(val)->value; - } - return 0.0; + ValuePtr val = get_stack_value(index); + if (val && val->type() == Type::NUMBER) { + return std::dynamic_pointer_cast(val)->value; + } + return 0.0; } std::string State::to_string(int index) { - ValuePtr val = get_stack_value(index); - if (val) { - return val->to_string(); - } - return ""; + ValuePtr val = get_stack_value(index); + if (val) { + return val->to_string(); + } + return ""; } bool State::to_bool(int index) { - ValuePtr val = get_stack_value(index); - return is_truthy(val); + ValuePtr val = get_stack_value(index); + return is_truthy(val); } -ValuePtr State::to_value(int index) { - return get_stack_value(index); -} +ValuePtr State::to_value(int index) { return get_stack_value(index); } void State::set_top(int index) { - if (index < 0) { - index = static_cast(stack_.size()) + index + 1; - } - - if (index < 0) { - stack_.clear(); - } else if (static_cast(index) < stack_.size()) { - stack_.resize(index); - } else { - while (static_cast(index) > stack_.size()) { - stack_.push_back(std::make_shared()); - } + if (index < 0) { + index = static_cast(stack_.size()) + index + 1; + } + + if (index < 0) { + stack_.clear(); + } else if (static_cast(index) < stack_.size()) { + stack_.resize(index); + } else { + while (static_cast(index) > stack_.size()) { + stack_.push_back(std::make_shared()); } + } } void State::pop(int n) { - if (n > static_cast(stack_.size())) { - stack_.clear(); - } else { - stack_.resize(stack_.size() - n); - } + if (n > static_cast(stack_.size())) { + stack_.clear(); + } else { + stack_.resize(stack_.size() - n); + } } ValuePtr State::get_stack_value(int index) { - if (index < 0) { - index = static_cast(stack_.size()) + index; - } else { - index -= 1; // Convert to 0-based - } - - if (index < 0 || static_cast(index) >= stack_.size()) { - return nullptr; - } - - return stack_[index]; + if (index < 0) { + index = static_cast(stack_.size()) + index; + } else { + index -= 1; // Convert to 0-based + } + + if (index < 0 || static_cast(index) >= stack_.size()) { + return nullptr; + } + + return stack_[index]; } -bool State::execute_program(const Program& program) { - if (execution_mode_ == ExecutionMode::INTERPRETER) { - // Use tree-walking interpreter - interpreter_->execute(program); - return true; - } else { - // Use VM - auto chunk = compiler_->compile(program); - if (!chunk) { - last_error_ = compiler_->get_error(); - return false; - } - - bool success = vm_->execute(chunk); - if (!success) { - last_error_ = vm_->get_error(); - } - return success; - } +bool State::execute_program(const Program &program) { + // Use VM + auto chunk = compiler_->compile(program); + if (!chunk) { + last_error_ = compiler_->get_error(); + return false; + } + + bool success = vm_->execute(chunk); + if (!success) { + last_error_ = vm_->get_error(); + } + return success; } } // namespace camellya diff --git a/src/state.h b/src/state.h index 3f01278..b2841dd 100644 --- a/src/state.h +++ b/src/state.h @@ -1,79 +1,66 @@ #ifndef CAMELLYA_STATE_H #define CAMELLYA_STATE_H +#include "compiler.h" #include "lexer.h" #include "parser.h" -#include "interpreter.h" -#include "vm.h" -#include "compiler.h" #include "value.h" -#include +#include "vm.h" #include +#include namespace camellya { -// Execution mode -enum class ExecutionMode { - INTERPRETER, // Tree-walking interpreter - VM // Bytecode VM -}; - // Main state class - similar to lua_State class State { public: - State(ExecutionMode mode = ExecutionMode::VM); - ~State() = default; - - // Set execution mode - void set_execution_mode(ExecutionMode mode) { execution_mode_ = mode; } - ExecutionMode get_execution_mode() const { return execution_mode_; } - - // Execute script from string - bool do_string(const std::string& script); - - // Execute script from file - bool do_file(const std::string& filename); - - // Register native function - void register_function(const std::string& name, NativeFunction func); - - // Get global variable - ValuePtr get_global(const std::string& name); - - // Set global variable - void set_global(const std::string& name, ValuePtr value); - - // Stack operations (Lua-like API) - void push_number(double value); - void push_string(const std::string& value); - void push_bool(bool value); - void push_nil(); - void push_value(ValuePtr value); - - double to_number(int index); - std::string to_string(int index); - bool to_bool(int index); - ValuePtr to_value(int index); - - int get_top() const { return static_cast(stack_.size()); } - void set_top(int index); - void pop(int n = 1); - - // Error handling - const std::string& get_error() const { return last_error_; } - + State(); + ~State() = default; + + // Execute script from string + bool do_string(const std::string &script); + + // Execute script from file + bool do_file(const std::string &filename); + + // Register native function + void register_function(const std::string &name, NativeFunction func); + + // Get global variable + ValuePtr get_global(const std::string &name); + + // Set global variable + void set_global(const std::string &name, ValuePtr value); + + // Stack operations (Lua-like API) + void push_number(double value); + void push_string(const std::string &value); + void push_bool(bool value); + void push_nil(); + void push_value(ValuePtr value); + + double to_number(int index); + std::string to_string(int index); + bool to_bool(int index); + ValuePtr to_value(int index); + + int get_top() const { return static_cast(stack_.size()); } + void set_top(int index); + void pop(int n = 1); + + // Error handling + const std::string &get_error() const { return last_error_; } + private: - ExecutionMode execution_mode_; - std::unique_ptr interpreter_; - std::unique_ptr vm_; - std::unique_ptr compiler_; - std::vector stack_; - std::string last_error_; - - ValuePtr get_stack_value(int index); - - // Helper for execution - bool execute_program(const Program& program); + std::unique_ptr vm_; + std::unique_ptr compiler_; + std::vector stack_; + std::string last_error_; + + ValuePtr get_stack_value(int index); + + // Helper for execution + bool execute_program(const Program &program); }; } // namespace camellya diff --git a/tests/test_basic.cpp b/tests/test_basic.cpp index f4b3bdf..cba50f2 100644 --- a/tests/test_basic.cpp +++ b/tests/test_basic.cpp @@ -1,82 +1,82 @@ -#include -#include #include "camellya.h" +#include +#include #include using namespace camellya; TEST_CASE("basic arithmetic", "[script]") { - State state; - const char* script = R"( + State state; + const char *script = R"( var x = 10; var y = 20; var z = x + y; )"; - REQUIRE(state.do_string(script)); + REQUIRE(state.do_string(script)); - auto z = state.get_global("z"); - REQUIRE(z); - REQUIRE(z->type() == Type::NUMBER); + auto z = state.get_global("z"); + REQUIRE(z); + REQUIRE(z->type() == Type::NUMBER); - auto nz = std::dynamic_pointer_cast(z); - REQUIRE(nz); - REQUIRE(nz->value == 30.0); + auto nz = std::dynamic_pointer_cast(z); + REQUIRE(nz); + REQUIRE(nz->value == 30.0); } TEST_CASE("basic function", "[script][func]") { - State state; - const char* script = R"( + State state; + const char *script = R"( func add(number x, number y) -> number { return x + y; } var z = add(10, 20); )"; - REQUIRE(state.do_string(script)); + REQUIRE(state.do_string(script)); - auto z = state.get_global("z"); - REQUIRE(z); - REQUIRE(z->type() == Type::NUMBER); + auto z = state.get_global("z"); + REQUIRE(z); + REQUIRE(z->type() == Type::NUMBER); - auto nz = std::dynamic_pointer_cast(z); - REQUIRE(nz); - REQUIRE(nz->value == 30.0); + auto nz = std::dynamic_pointer_cast(z); + REQUIRE(nz); + REQUIRE(nz->value == 30.0); } TEST_CASE("list indexing is 0-based", "[list]") { - State state; - const char* script = R"( + State state; + const char *script = R"( var numbers = [10, 20, 30]; )"; - REQUIRE(state.do_string(script)); + REQUIRE(state.do_string(script)); - auto list_val = state.get_global("numbers"); - REQUIRE(list_val); - REQUIRE(list_val->type() == Type::LIST); + auto list_val = state.get_global("numbers"); + REQUIRE(list_val); + REQUIRE(list_val->type() == Type::LIST); - auto list = std::dynamic_pointer_cast(list_val); - REQUIRE(list); - REQUIRE(list->size() == 3); + auto list = std::dynamic_pointer_cast(list_val); + REQUIRE(list); + REQUIRE(list->size() == 3); - auto first = list->get(0); - auto third = list->get(2); + auto first = list->get(0); + auto third = list->get(2); - REQUIRE(first->type() == Type::NUMBER); - REQUIRE(third->type() == Type::NUMBER); + REQUIRE(first->type() == Type::NUMBER); + REQUIRE(third->type() == Type::NUMBER); - auto first_n = std::dynamic_pointer_cast(first); - auto third_n = std::dynamic_pointer_cast(third); + auto first_n = std::dynamic_pointer_cast(first); + auto third_n = std::dynamic_pointer_cast(third); - REQUIRE(first_n->value == 10.0); - REQUIRE(third_n->value == 30.0); + REQUIRE(first_n->value == 10.0); + REQUIRE(third_n->value == 30.0); } TEST_CASE("class init is called on declaration", "[class][init]") { - State state; - const char* script = R"( + State state; + const char *script = R"( class Person { var age : number; var name : string; @@ -95,38 +95,37 @@ TEST_CASE("class init is called on declaration", "[class][init]") { var a = p.getAge(); )"; - REQUIRE(state.do_string(script)); + REQUIRE(state.do_string(script)); - auto p_val = state.get_global("p"); - REQUIRE(p_val); - REQUIRE(p_val->type() == Type::INSTANCE); + 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 instance = std::dynamic_pointer_cast(p_val); + REQUIRE(instance); - auto age_val = instance->get("age"); - auto name_val = instance->get("name"); + auto age_val = instance->get("age"); + auto name_val = instance->get("name"); - REQUIRE(age_val->type() == Type::NUMBER); - REQUIRE(name_val->type() == Type::STRING); + 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); + 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"); + 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); + 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); } TEST_CASE("interpreter performance: simple loop", "[perf][script]") { - State state; - State state_vm(ExecutionMode::VM); - const char* script = R"( + State state; + const char *script = R"( func sum_to(number n) -> number { var s = 0; for (var i = 0; i < n; i = i + 1) { @@ -137,35 +136,23 @@ TEST_CASE("interpreter performance: simple loop", "[perf][script]") { var r = sum_to(1000); )"; - BENCHMARK("sum_to(1000)") { - if (!state.do_string(script)) { - auto last_error = state.get_error(); - REQUIRE(last_error.empty()); - } - - auto r_val = state.get_global("r"); - REQUIRE(r_val); - REQUIRE(r_val->type() == Type::NUMBER); - auto r_num = std::dynamic_pointer_cast(r_val); - REQUIRE(r_num->value == 499500.0); - }; + BENCHMARK("sum_to(1000)") { + if (!state.do_string(script)) { + auto last_error = state.get_error(); + REQUIRE(last_error.empty()); + } - BENCHMARK("state_vm sum_to(1000)") { - if (!state_vm.do_string(script)) { - auto last_error = state_vm.get_error(); - REQUIRE(last_error.empty()); - } - auto r_val = state_vm.get_global("r"); - REQUIRE(r_val); - REQUIRE(r_val->type() == Type::NUMBER); - auto r_num = std::dynamic_pointer_cast(r_val); - REQUIRE(r_num->value == 499500.0); - }; + auto r_val = state.get_global("r"); + REQUIRE(r_val); + REQUIRE(r_val->type() == Type::NUMBER); + auto r_num = std::dynamic_pointer_cast(r_val); + REQUIRE(r_num->value == 499500.0); + }; } TEST_CASE("loop break", "[script][loop]") { - State state; - const char* script = R"( + State state; + const char *script = R"( var sum = 0; for (var i = 0; i < 10; i = i + 1) { if (i == 5) { @@ -175,16 +162,16 @@ TEST_CASE("loop break", "[script][loop]") { } )"; - REQUIRE(state.do_string(script)); - auto sum_val = state.get_global("sum"); - REQUIRE(sum_val); - auto sum_num = std::dynamic_pointer_cast(sum_val); - REQUIRE(sum_num->value == 10.0); // 0+1+2+3+4 = 10 + REQUIRE(state.do_string(script)); + auto sum_val = state.get_global("sum"); + REQUIRE(sum_val); + auto sum_num = std::dynamic_pointer_cast(sum_val); + REQUIRE(sum_num->value == 10.0); // 0+1+2+3+4 = 10 } TEST_CASE("loop continue", "[script][loop]") { - State state; - const char* script = R"( + State state; + const char *script = R"( var sum = 0; for (var i = 0; i < 5; i = i + 1) { if (i == 2) { @@ -194,16 +181,16 @@ TEST_CASE("loop continue", "[script][loop]") { } )"; - REQUIRE(state.do_string(script)); - auto sum_val = state.get_global("sum"); - REQUIRE(sum_val); - auto sum_num = std::dynamic_pointer_cast(sum_val); - REQUIRE(sum_num->value == 8.0); // 0+1+3+4 = 8 + REQUIRE(state.do_string(script)); + auto sum_val = state.get_global("sum"); + REQUIRE(sum_val); + auto sum_num = std::dynamic_pointer_cast(sum_val); + REQUIRE(sum_num->value == 8.0); // 0+1+3+4 = 8 } TEST_CASE("while break and continue", "[script][loop]") { - State state; - const char* script = R"( + State state; + const char *script = R"( var i = 0; var sum = 0; while (i < 10) { @@ -218,16 +205,16 @@ TEST_CASE("while break and continue", "[script][loop]") { } )"; - REQUIRE(state.do_string(script)); - auto sum_val = state.get_global("sum"); - REQUIRE(sum_val); - auto sum_num = std::dynamic_pointer_cast(sum_val); - REQUIRE(sum_num->value == 12.0); - // 1st iter: i=1, sum=1 - // 2nd iter: i=2, sum=1+2=3 - // 3rd iter: i=3, continue - // 4th iter: i=4, sum=3+4=7 - // 5th iter: i=5, sum=7+5=12 - // 6th iter: i=6, break - // Result should be 12.0 + REQUIRE(state.do_string(script)); + auto sum_val = state.get_global("sum"); + REQUIRE(sum_val); + auto sum_num = std::dynamic_pointer_cast(sum_val); + REQUIRE(sum_num->value == 12.0); + // 1st iter: i=1, sum=1 + // 2nd iter: i=2, sum=1+2=3 + // 3rd iter: i=3, continue + // 4th iter: i=4, sum=3+4=7 + // 5th iter: i=5, sum=7+5=12 + // 6th iter: i=6, break + // Result should be 12.0 } diff --git a/tests/test_vm.cpp b/tests/test_vm.cpp index 48642f8..05aeaac 100644 --- a/tests/test_vm.cpp +++ b/tests/test_vm.cpp @@ -1,154 +1,155 @@ -#include #include "src/camellya.h" +#include 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); - } + State state; + + 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"( + State state; + + 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); - } + 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"); - } + State state; + + 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); - } + State state; + + 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"( + State state; + + 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); - } + 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"( + State state; + + 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"); - } + 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"( + State state; + + 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"( + 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; @@ -156,16 +157,16 @@ TEST_CASE("VM - If statements", "[vm]") { y = 200; } )")); - auto y = state.get_global("y"); - REQUIRE(std::dynamic_pointer_cast(y)->value == 200.0); - } + 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"( + State state; + + SECTION("While loop") { + REQUIRE(state.do_string(R"( var counter = 0; var sum = 0; while (counter < 5) { @@ -173,97 +174,20 @@ TEST_CASE("VM - While loops", "[vm]") { counter = counter + 1; } )")); - auto sum = state.get_global("sum"); - REQUIRE(std::dynamic_pointer_cast(sum)->value == 10.0); - } + 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"( + State state; + + 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); - } + 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); -} \ No newline at end of file