269 lines
8.0 KiB
C++
269 lines
8.0 KiB
C++
#include <catch2/catch_test_macros.hpp>
|
|
#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<NumberValue>(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<NumberValue>(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<NumberValue>(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<NumberValue>(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<NumberValue>(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<NumberValue>(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<StringValue>(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<BoolValue>(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<BoolValue>(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<BoolValue>(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<ListValue>(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<NumberValue>(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<StringValue>(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<NumberValue>(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<NumberValue>(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<NumberValue>(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<NumberValue>(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<NumberValue>(vm_sum)->value ==
|
|
std::dynamic_pointer_cast<NumberValue>(interp_sum)->value);
|
|
|
|
auto vm_product = vm_state.get_global("product");
|
|
auto interp_product = interp_state.get_global("product");
|
|
REQUIRE(std::dynamic_pointer_cast<NumberValue>(vm_product)->value ==
|
|
std::dynamic_pointer_cast<NumberValue>(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<InstanceValue>(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<NumberValue>(age_val);
|
|
auto name_str = std::dynamic_pointer_cast<StringValue>(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<NumberValue>(a_val);
|
|
REQUIRE(a_num->value == 18.0);
|
|
} |