Remove interpreter
This commit is contained in:
@@ -13,7 +13,6 @@ set(LIB_SOURCES
|
|||||||
src/lexer.cpp
|
src/lexer.cpp
|
||||||
src/parser.cpp
|
src/parser.cpp
|
||||||
src/value.cpp
|
src/value.cpp
|
||||||
src/interpreter.cpp
|
|
||||||
src/state.cpp
|
src/state.cpp
|
||||||
src/chunk.cpp
|
src/chunk.cpp
|
||||||
src/compiler.cpp
|
src/compiler.cpp
|
||||||
@@ -27,7 +26,6 @@ set(LIB_HEADERS
|
|||||||
src/parser.h
|
src/parser.h
|
||||||
src/ast.h
|
src/ast.h
|
||||||
src/value.h
|
src/value.h
|
||||||
src/interpreter.h
|
|
||||||
src/state.h
|
src/state.h
|
||||||
src/chunk.h
|
src/chunk.h
|
||||||
src/compiler.h
|
src/compiler.h
|
||||||
|
|||||||
204
src/state.cpp
204
src/state.cpp
@@ -4,171 +4,143 @@
|
|||||||
|
|
||||||
namespace camellya {
|
namespace camellya {
|
||||||
|
|
||||||
State::State(ExecutionMode mode)
|
State::State()
|
||||||
: execution_mode_(mode),
|
: vm_(std::make_unique<VM>()), compiler_(std::make_unique<Compiler>()) {}
|
||||||
interpreter_(std::make_unique<Interpreter>()),
|
|
||||||
vm_(std::make_unique<VM>()),
|
|
||||||
compiler_(std::make_unique<Compiler>()) {}
|
|
||||||
|
|
||||||
bool State::do_string(const std::string& script) {
|
bool State::do_string(const std::string &script) {
|
||||||
try {
|
try {
|
||||||
Lexer lexer(script);
|
Lexer lexer(script);
|
||||||
auto tokens = lexer.tokenize();
|
auto tokens = lexer.tokenize();
|
||||||
|
|
||||||
Parser parser(std::move(tokens));
|
Parser parser(std::move(tokens));
|
||||||
Program program = parser.parse();
|
Program program = parser.parse();
|
||||||
|
|
||||||
bool success = execute_program(program);
|
bool success = execute_program(program);
|
||||||
if (success) {
|
if (success) {
|
||||||
last_error_.clear();
|
last_error_.clear();
|
||||||
}
|
|
||||||
return success;
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
last_error_ = e.what();
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return success;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
last_error_ = e.what();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool State::do_file(const std::string& filename) {
|
bool State::do_file(const std::string &filename) {
|
||||||
std::ifstream file(filename);
|
std::ifstream file(filename);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
last_error_ = "Failed to open file: " + filename;
|
last_error_ = "Failed to open file: " + filename;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream buffer;
|
std::stringstream buffer;
|
||||||
buffer << file.rdbuf();
|
buffer << file.rdbuf();
|
||||||
return do_string(buffer.str());
|
return do_string(buffer.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void State::register_function(const std::string& name, NativeFunction func) {
|
void State::register_function(const std::string &name, NativeFunction func) {
|
||||||
auto func_value = std::make_shared<FunctionValue>(name, func);
|
auto func_value = std::make_shared<FunctionValue>(name, func);
|
||||||
|
vm_->register_native_function(name, func);
|
||||||
if (execution_mode_ == ExecutionMode::INTERPRETER) {
|
|
||||||
interpreter_->global_environment->define(name, func_value);
|
|
||||||
} else {
|
|
||||||
vm_->register_native_function(name, func);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ValuePtr State::get_global(const std::string& name) {
|
ValuePtr State::get_global(const std::string &name) {
|
||||||
if (execution_mode_ == ExecutionMode::INTERPRETER) {
|
return vm_->get_global(name);
|
||||||
return interpreter_->global_environment->get(name);
|
|
||||||
} else {
|
|
||||||
return vm_->get_global(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void State::set_global(const std::string& name, ValuePtr value) {
|
void State::set_global(const std::string &name, ValuePtr value) {
|
||||||
if (execution_mode_ == ExecutionMode::INTERPRETER) {
|
vm_->set_global(name, value);
|
||||||
interpreter_->global_environment->define(name, value);
|
|
||||||
} else {
|
|
||||||
vm_->set_global(name, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void State::push_number(double value) {
|
void State::push_number(double value) {
|
||||||
stack_.push_back(std::make_shared<NumberValue>(value));
|
stack_.push_back(std::make_shared<NumberValue>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void State::push_string(const std::string& value) {
|
void State::push_string(const std::string &value) {
|
||||||
stack_.push_back(std::make_shared<StringValue>(value));
|
stack_.push_back(std::make_shared<StringValue>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void State::push_bool(bool value) {
|
void State::push_bool(bool value) {
|
||||||
stack_.push_back(std::make_shared<BoolValue>(value));
|
stack_.push_back(std::make_shared<BoolValue>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void State::push_nil() {
|
void State::push_nil() { stack_.push_back(std::make_shared<NilValue>()); }
|
||||||
stack_.push_back(std::make_shared<NilValue>());
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::push_value(ValuePtr value) {
|
void State::push_value(ValuePtr value) { stack_.push_back(std::move(value)); }
|
||||||
stack_.push_back(std::move(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
double State::to_number(int index) {
|
double State::to_number(int index) {
|
||||||
ValuePtr val = get_stack_value(index);
|
ValuePtr val = get_stack_value(index);
|
||||||
if (val && val->type() == Type::NUMBER) {
|
if (val && val->type() == Type::NUMBER) {
|
||||||
return std::dynamic_pointer_cast<NumberValue>(val)->value;
|
return std::dynamic_pointer_cast<NumberValue>(val)->value;
|
||||||
}
|
}
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string State::to_string(int index) {
|
std::string State::to_string(int index) {
|
||||||
ValuePtr val = get_stack_value(index);
|
ValuePtr val = get_stack_value(index);
|
||||||
if (val) {
|
if (val) {
|
||||||
return val->to_string();
|
return val->to_string();
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool State::to_bool(int index) {
|
bool State::to_bool(int index) {
|
||||||
ValuePtr val = get_stack_value(index);
|
ValuePtr val = get_stack_value(index);
|
||||||
return is_truthy(val);
|
return is_truthy(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValuePtr State::to_value(int index) {
|
ValuePtr State::to_value(int index) { return get_stack_value(index); }
|
||||||
return get_stack_value(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::set_top(int index) {
|
void State::set_top(int index) {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
index = static_cast<int>(stack_.size()) + index + 1;
|
index = static_cast<int>(stack_.size()) + index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
stack_.clear();
|
stack_.clear();
|
||||||
} else if (static_cast<size_t>(index) < stack_.size()) {
|
} else if (static_cast<size_t>(index) < stack_.size()) {
|
||||||
stack_.resize(index);
|
stack_.resize(index);
|
||||||
} else {
|
} else {
|
||||||
while (static_cast<size_t>(index) > stack_.size()) {
|
while (static_cast<size_t>(index) > stack_.size()) {
|
||||||
stack_.push_back(std::make_shared<NilValue>());
|
stack_.push_back(std::make_shared<NilValue>());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void State::pop(int n) {
|
void State::pop(int n) {
|
||||||
if (n > static_cast<int>(stack_.size())) {
|
if (n > static_cast<int>(stack_.size())) {
|
||||||
stack_.clear();
|
stack_.clear();
|
||||||
} else {
|
} else {
|
||||||
stack_.resize(stack_.size() - n);
|
stack_.resize(stack_.size() - n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ValuePtr State::get_stack_value(int index) {
|
ValuePtr State::get_stack_value(int index) {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
index = static_cast<int>(stack_.size()) + index;
|
index = static_cast<int>(stack_.size()) + index;
|
||||||
} else {
|
} else {
|
||||||
index -= 1; // Convert to 0-based
|
index -= 1; // Convert to 0-based
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index < 0 || static_cast<size_t>(index) >= stack_.size()) {
|
if (index < 0 || static_cast<size_t>(index) >= stack_.size()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return stack_[index];
|
return stack_[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool State::execute_program(const Program& program) {
|
bool State::execute_program(const Program &program) {
|
||||||
if (execution_mode_ == ExecutionMode::INTERPRETER) {
|
// Use VM
|
||||||
// Use tree-walking interpreter
|
auto chunk = compiler_->compile(program);
|
||||||
interpreter_->execute(program);
|
if (!chunk) {
|
||||||
return true;
|
last_error_ = compiler_->get_error();
|
||||||
} else {
|
return false;
|
||||||
// Use VM
|
}
|
||||||
auto chunk = compiler_->compile(program);
|
|
||||||
if (!chunk) {
|
|
||||||
last_error_ = compiler_->get_error();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = vm_->execute(chunk);
|
bool success = vm_->execute(chunk);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
last_error_ = vm_->get_error();
|
last_error_ = vm_->get_error();
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace camellya
|
} // namespace camellya
|
||||||
|
|||||||
87
src/state.h
87
src/state.h
@@ -1,79 +1,66 @@
|
|||||||
#ifndef CAMELLYA_STATE_H
|
#ifndef CAMELLYA_STATE_H
|
||||||
#define CAMELLYA_STATE_H
|
#define CAMELLYA_STATE_H
|
||||||
|
|
||||||
|
#include "compiler.h"
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "interpreter.h"
|
|
||||||
#include "vm.h"
|
|
||||||
#include "compiler.h"
|
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
#include <string>
|
#include "vm.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace camellya {
|
namespace camellya {
|
||||||
|
|
||||||
// Execution mode
|
|
||||||
enum class ExecutionMode {
|
|
||||||
INTERPRETER, // Tree-walking interpreter
|
|
||||||
VM // Bytecode VM
|
|
||||||
};
|
|
||||||
|
|
||||||
// Main state class - similar to lua_State
|
// Main state class - similar to lua_State
|
||||||
class State {
|
class State {
|
||||||
public:
|
public:
|
||||||
State(ExecutionMode mode = ExecutionMode::VM);
|
State();
|
||||||
~State() = default;
|
~State() = default;
|
||||||
|
|
||||||
// Set execution mode
|
// Execute script from string
|
||||||
void set_execution_mode(ExecutionMode mode) { execution_mode_ = mode; }
|
bool do_string(const std::string &script);
|
||||||
ExecutionMode get_execution_mode() const { return execution_mode_; }
|
|
||||||
|
|
||||||
// Execute script from string
|
// Execute script from file
|
||||||
bool do_string(const std::string& script);
|
bool do_file(const std::string &filename);
|
||||||
|
|
||||||
// Execute script from file
|
// Register native function
|
||||||
bool do_file(const std::string& filename);
|
void register_function(const std::string &name, NativeFunction func);
|
||||||
|
|
||||||
// Register native function
|
// Get global variable
|
||||||
void register_function(const std::string& name, NativeFunction func);
|
ValuePtr get_global(const std::string &name);
|
||||||
|
|
||||||
// Get global variable
|
// Set global variable
|
||||||
ValuePtr get_global(const std::string& name);
|
void set_global(const std::string &name, ValuePtr value);
|
||||||
|
|
||||||
// Set global variable
|
// Stack operations (Lua-like API)
|
||||||
void set_global(const std::string& name, ValuePtr value);
|
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);
|
||||||
|
|
||||||
// Stack operations (Lua-like API)
|
double to_number(int index);
|
||||||
void push_number(double value);
|
std::string to_string(int index);
|
||||||
void push_string(const std::string& value);
|
bool to_bool(int index);
|
||||||
void push_bool(bool value);
|
ValuePtr to_value(int index);
|
||||||
void push_nil();
|
|
||||||
void push_value(ValuePtr value);
|
|
||||||
|
|
||||||
double to_number(int index);
|
int get_top() const { return static_cast<int>(stack_.size()); }
|
||||||
std::string to_string(int index);
|
void set_top(int index);
|
||||||
bool to_bool(int index);
|
void pop(int n = 1);
|
||||||
ValuePtr to_value(int index);
|
|
||||||
|
|
||||||
int get_top() const { return static_cast<int>(stack_.size()); }
|
// Error handling
|
||||||
void set_top(int index);
|
const std::string &get_error() const { return last_error_; }
|
||||||
void pop(int n = 1);
|
|
||||||
|
|
||||||
// Error handling
|
|
||||||
const std::string& get_error() const { return last_error_; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ExecutionMode execution_mode_;
|
std::unique_ptr<VM> vm_;
|
||||||
std::unique_ptr<Interpreter> interpreter_;
|
std::unique_ptr<Compiler> compiler_;
|
||||||
std::unique_ptr<VM> vm_;
|
std::vector<ValuePtr> stack_;
|
||||||
std::unique_ptr<Compiler> compiler_;
|
std::string last_error_;
|
||||||
std::vector<ValuePtr> stack_;
|
|
||||||
std::string last_error_;
|
|
||||||
|
|
||||||
ValuePtr get_stack_value(int index);
|
ValuePtr get_stack_value(int index);
|
||||||
|
|
||||||
// Helper for execution
|
// Helper for execution
|
||||||
bool execute_program(const Program& program);
|
bool execute_program(const Program &program);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace camellya
|
} // namespace camellya
|
||||||
|
|||||||
@@ -1,82 +1,82 @@
|
|||||||
#include <catch2/catch_test_macros.hpp>
|
|
||||||
#include <catch2/benchmark/catch_benchmark.hpp>
|
|
||||||
#include "camellya.h"
|
#include "camellya.h"
|
||||||
|
#include <catch2/benchmark/catch_benchmark.hpp>
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
using namespace camellya;
|
using namespace camellya;
|
||||||
|
|
||||||
TEST_CASE("basic arithmetic", "[script]") {
|
TEST_CASE("basic arithmetic", "[script]") {
|
||||||
State state;
|
State state;
|
||||||
const char* script = R"(
|
const char *script = R"(
|
||||||
var x = 10;
|
var x = 10;
|
||||||
var y = 20;
|
var y = 20;
|
||||||
var z = x + y;
|
var z = x + y;
|
||||||
)";
|
)";
|
||||||
|
|
||||||
REQUIRE(state.do_string(script));
|
REQUIRE(state.do_string(script));
|
||||||
|
|
||||||
auto z = state.get_global("z");
|
auto z = state.get_global("z");
|
||||||
REQUIRE(z);
|
REQUIRE(z);
|
||||||
REQUIRE(z->type() == Type::NUMBER);
|
REQUIRE(z->type() == Type::NUMBER);
|
||||||
|
|
||||||
auto nz = std::dynamic_pointer_cast<NumberValue>(z);
|
auto nz = std::dynamic_pointer_cast<NumberValue>(z);
|
||||||
REQUIRE(nz);
|
REQUIRE(nz);
|
||||||
REQUIRE(nz->value == 30.0);
|
REQUIRE(nz->value == 30.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("basic function", "[script][func]") {
|
TEST_CASE("basic function", "[script][func]") {
|
||||||
State state;
|
State state;
|
||||||
const char* script = R"(
|
const char *script = R"(
|
||||||
func add(number x, number y) -> number {
|
func add(number x, number y) -> number {
|
||||||
return x + y;
|
return x + y;
|
||||||
}
|
}
|
||||||
var z = add(10, 20);
|
var z = add(10, 20);
|
||||||
)";
|
)";
|
||||||
|
|
||||||
REQUIRE(state.do_string(script));
|
REQUIRE(state.do_string(script));
|
||||||
|
|
||||||
auto z = state.get_global("z");
|
auto z = state.get_global("z");
|
||||||
REQUIRE(z);
|
REQUIRE(z);
|
||||||
REQUIRE(z->type() == Type::NUMBER);
|
REQUIRE(z->type() == Type::NUMBER);
|
||||||
|
|
||||||
auto nz = std::dynamic_pointer_cast<NumberValue>(z);
|
auto nz = std::dynamic_pointer_cast<NumberValue>(z);
|
||||||
REQUIRE(nz);
|
REQUIRE(nz);
|
||||||
REQUIRE(nz->value == 30.0);
|
REQUIRE(nz->value == 30.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("list indexing is 0-based", "[list]") {
|
TEST_CASE("list indexing is 0-based", "[list]") {
|
||||||
State state;
|
State state;
|
||||||
const char* script = R"(
|
const char *script = R"(
|
||||||
var numbers = [10, 20, 30];
|
var numbers = [10, 20, 30];
|
||||||
)";
|
)";
|
||||||
|
|
||||||
REQUIRE(state.do_string(script));
|
REQUIRE(state.do_string(script));
|
||||||
|
|
||||||
auto list_val = state.get_global("numbers");
|
auto list_val = state.get_global("numbers");
|
||||||
REQUIRE(list_val);
|
REQUIRE(list_val);
|
||||||
REQUIRE(list_val->type() == Type::LIST);
|
REQUIRE(list_val->type() == Type::LIST);
|
||||||
|
|
||||||
auto list = std::dynamic_pointer_cast<ListValue>(list_val);
|
auto list = std::dynamic_pointer_cast<ListValue>(list_val);
|
||||||
REQUIRE(list);
|
REQUIRE(list);
|
||||||
REQUIRE(list->size() == 3);
|
REQUIRE(list->size() == 3);
|
||||||
|
|
||||||
auto first = list->get(0);
|
auto first = list->get(0);
|
||||||
auto third = list->get(2);
|
auto third = list->get(2);
|
||||||
|
|
||||||
REQUIRE(first->type() == Type::NUMBER);
|
REQUIRE(first->type() == Type::NUMBER);
|
||||||
REQUIRE(third->type() == Type::NUMBER);
|
REQUIRE(third->type() == Type::NUMBER);
|
||||||
|
|
||||||
auto first_n = std::dynamic_pointer_cast<NumberValue>(first);
|
auto first_n = std::dynamic_pointer_cast<NumberValue>(first);
|
||||||
auto third_n = std::dynamic_pointer_cast<NumberValue>(third);
|
auto third_n = std::dynamic_pointer_cast<NumberValue>(third);
|
||||||
|
|
||||||
REQUIRE(first_n->value == 10.0);
|
REQUIRE(first_n->value == 10.0);
|
||||||
REQUIRE(third_n->value == 30.0);
|
REQUIRE(third_n->value == 30.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("class init is called on declaration", "[class][init]") {
|
TEST_CASE("class init is called on declaration", "[class][init]") {
|
||||||
State state;
|
State state;
|
||||||
const char* script = R"(
|
const char *script = R"(
|
||||||
class Person {
|
class Person {
|
||||||
var age : number;
|
var age : number;
|
||||||
var name : string;
|
var name : string;
|
||||||
@@ -95,38 +95,37 @@ TEST_CASE("class init is called on declaration", "[class][init]") {
|
|||||||
var a = p.getAge();
|
var a = p.getAge();
|
||||||
)";
|
)";
|
||||||
|
|
||||||
REQUIRE(state.do_string(script));
|
REQUIRE(state.do_string(script));
|
||||||
|
|
||||||
auto p_val = state.get_global("p");
|
auto p_val = state.get_global("p");
|
||||||
REQUIRE(p_val);
|
REQUIRE(p_val);
|
||||||
REQUIRE(p_val->type() == Type::INSTANCE);
|
REQUIRE(p_val->type() == Type::INSTANCE);
|
||||||
|
|
||||||
auto instance = std::dynamic_pointer_cast<InstanceValue>(p_val);
|
auto instance = std::dynamic_pointer_cast<InstanceValue>(p_val);
|
||||||
REQUIRE(instance);
|
REQUIRE(instance);
|
||||||
|
|
||||||
auto age_val = instance->get("age");
|
auto age_val = instance->get("age");
|
||||||
auto name_val = instance->get("name");
|
auto name_val = instance->get("name");
|
||||||
|
|
||||||
REQUIRE(age_val->type() == Type::NUMBER);
|
REQUIRE(age_val->type() == Type::NUMBER);
|
||||||
REQUIRE(name_val->type() == Type::STRING);
|
REQUIRE(name_val->type() == Type::STRING);
|
||||||
|
|
||||||
auto age_num = std::dynamic_pointer_cast<NumberValue>(age_val);
|
auto age_num = std::dynamic_pointer_cast<NumberValue>(age_val);
|
||||||
auto name_str = std::dynamic_pointer_cast<StringValue>(name_val);
|
auto name_str = std::dynamic_pointer_cast<StringValue>(name_val);
|
||||||
|
|
||||||
REQUIRE(age_num->value == 18.0);
|
REQUIRE(age_num->value == 18.0);
|
||||||
REQUIRE(name_str->value == "Default");
|
REQUIRE(name_str->value == "Default");
|
||||||
|
|
||||||
auto a_val = state.get_global("a");
|
auto a_val = state.get_global("a");
|
||||||
REQUIRE(a_val);
|
REQUIRE(a_val);
|
||||||
REQUIRE(a_val->type() == Type::NUMBER);
|
REQUIRE(a_val->type() == Type::NUMBER);
|
||||||
auto a_num = std::dynamic_pointer_cast<NumberValue>(a_val);
|
auto a_num = std::dynamic_pointer_cast<NumberValue>(a_val);
|
||||||
REQUIRE(a_num->value == 18.0);
|
REQUIRE(a_num->value == 18.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("interpreter performance: simple loop", "[perf][script]") {
|
TEST_CASE("interpreter performance: simple loop", "[perf][script]") {
|
||||||
State state;
|
State state;
|
||||||
State state_vm(ExecutionMode::VM);
|
const char *script = R"(
|
||||||
const char* script = R"(
|
|
||||||
func sum_to(number n) -> number {
|
func sum_to(number n) -> number {
|
||||||
var s = 0;
|
var s = 0;
|
||||||
for (var i = 0; i < n; i = i + 1) {
|
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);
|
var r = sum_to(1000);
|
||||||
)";
|
)";
|
||||||
|
|
||||||
BENCHMARK("sum_to(1000)") {
|
BENCHMARK("sum_to(1000)") {
|
||||||
if (!state.do_string(script)) {
|
if (!state.do_string(script)) {
|
||||||
auto last_error = state.get_error();
|
auto last_error = state.get_error();
|
||||||
REQUIRE(last_error.empty());
|
REQUIRE(last_error.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto r_val = state.get_global("r");
|
auto r_val = state.get_global("r");
|
||||||
REQUIRE(r_val);
|
REQUIRE(r_val);
|
||||||
REQUIRE(r_val->type() == Type::NUMBER);
|
REQUIRE(r_val->type() == Type::NUMBER);
|
||||||
auto r_num = std::dynamic_pointer_cast<NumberValue>(r_val);
|
auto r_num = std::dynamic_pointer_cast<NumberValue>(r_val);
|
||||||
REQUIRE(r_num->value == 499500.0);
|
REQUIRE(r_num->value == 499500.0);
|
||||||
};
|
};
|
||||||
|
|
||||||
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<NumberValue>(r_val);
|
|
||||||
REQUIRE(r_num->value == 499500.0);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("loop break", "[script][loop]") {
|
TEST_CASE("loop break", "[script][loop]") {
|
||||||
State state;
|
State state;
|
||||||
const char* script = R"(
|
const char *script = R"(
|
||||||
var sum = 0;
|
var sum = 0;
|
||||||
for (var i = 0; i < 10; i = i + 1) {
|
for (var i = 0; i < 10; i = i + 1) {
|
||||||
if (i == 5) {
|
if (i == 5) {
|
||||||
@@ -175,16 +162,16 @@ TEST_CASE("loop break", "[script][loop]") {
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
REQUIRE(state.do_string(script));
|
REQUIRE(state.do_string(script));
|
||||||
auto sum_val = state.get_global("sum");
|
auto sum_val = state.get_global("sum");
|
||||||
REQUIRE(sum_val);
|
REQUIRE(sum_val);
|
||||||
auto sum_num = std::dynamic_pointer_cast<NumberValue>(sum_val);
|
auto sum_num = std::dynamic_pointer_cast<NumberValue>(sum_val);
|
||||||
REQUIRE(sum_num->value == 10.0); // 0+1+2+3+4 = 10
|
REQUIRE(sum_num->value == 10.0); // 0+1+2+3+4 = 10
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("loop continue", "[script][loop]") {
|
TEST_CASE("loop continue", "[script][loop]") {
|
||||||
State state;
|
State state;
|
||||||
const char* script = R"(
|
const char *script = R"(
|
||||||
var sum = 0;
|
var sum = 0;
|
||||||
for (var i = 0; i < 5; i = i + 1) {
|
for (var i = 0; i < 5; i = i + 1) {
|
||||||
if (i == 2) {
|
if (i == 2) {
|
||||||
@@ -194,16 +181,16 @@ TEST_CASE("loop continue", "[script][loop]") {
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
REQUIRE(state.do_string(script));
|
REQUIRE(state.do_string(script));
|
||||||
auto sum_val = state.get_global("sum");
|
auto sum_val = state.get_global("sum");
|
||||||
REQUIRE(sum_val);
|
REQUIRE(sum_val);
|
||||||
auto sum_num = std::dynamic_pointer_cast<NumberValue>(sum_val);
|
auto sum_num = std::dynamic_pointer_cast<NumberValue>(sum_val);
|
||||||
REQUIRE(sum_num->value == 8.0); // 0+1+3+4 = 8
|
REQUIRE(sum_num->value == 8.0); // 0+1+3+4 = 8
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("while break and continue", "[script][loop]") {
|
TEST_CASE("while break and continue", "[script][loop]") {
|
||||||
State state;
|
State state;
|
||||||
const char* script = R"(
|
const char *script = R"(
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var sum = 0;
|
var sum = 0;
|
||||||
while (i < 10) {
|
while (i < 10) {
|
||||||
@@ -218,16 +205,16 @@ TEST_CASE("while break and continue", "[script][loop]") {
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
REQUIRE(state.do_string(script));
|
REQUIRE(state.do_string(script));
|
||||||
auto sum_val = state.get_global("sum");
|
auto sum_val = state.get_global("sum");
|
||||||
REQUIRE(sum_val);
|
REQUIRE(sum_val);
|
||||||
auto sum_num = std::dynamic_pointer_cast<NumberValue>(sum_val);
|
auto sum_num = std::dynamic_pointer_cast<NumberValue>(sum_val);
|
||||||
REQUIRE(sum_num->value == 12.0);
|
REQUIRE(sum_num->value == 12.0);
|
||||||
// 1st iter: i=1, sum=1
|
// 1st iter: i=1, sum=1
|
||||||
// 2nd iter: i=2, sum=1+2=3
|
// 2nd iter: i=2, sum=1+2=3
|
||||||
// 3rd iter: i=3, continue
|
// 3rd iter: i=3, continue
|
||||||
// 4th iter: i=4, sum=3+4=7
|
// 4th iter: i=4, sum=3+4=7
|
||||||
// 5th iter: i=5, sum=7+5=12
|
// 5th iter: i=5, sum=7+5=12
|
||||||
// 6th iter: i=6, break
|
// 6th iter: i=6, break
|
||||||
// Result should be 12.0
|
// Result should be 12.0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,154 +1,155 @@
|
|||||||
#include <catch2/catch_test_macros.hpp>
|
|
||||||
#include "src/camellya.h"
|
#include "src/camellya.h"
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
using namespace camellya;
|
using namespace camellya;
|
||||||
|
|
||||||
TEST_CASE("VM - Basic arithmetic", "[vm]") {
|
TEST_CASE("VM - Basic arithmetic", "[vm]") {
|
||||||
State state(ExecutionMode::VM);
|
State state;
|
||||||
|
|
||||||
SECTION("Addition") {
|
SECTION("Addition") {
|
||||||
REQUIRE(state.do_string("var x = 10 + 20;"));
|
REQUIRE(state.do_string("var x = 10 + 20;"));
|
||||||
auto x = state.get_global("x");
|
auto x = state.get_global("x");
|
||||||
REQUIRE(x != nullptr);
|
REQUIRE(x != nullptr);
|
||||||
REQUIRE(x->type() == Type::NUMBER);
|
REQUIRE(x->type() == Type::NUMBER);
|
||||||
REQUIRE(std::dynamic_pointer_cast<NumberValue>(x)->value == 30.0);
|
REQUIRE(std::dynamic_pointer_cast<NumberValue>(x)->value == 30.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Subtraction") {
|
SECTION("Subtraction") {
|
||||||
REQUIRE(state.do_string("var y = 50 - 15;"));
|
REQUIRE(state.do_string("var y = 50 - 15;"));
|
||||||
auto y = state.get_global("y");
|
auto y = state.get_global("y");
|
||||||
REQUIRE(y != nullptr);
|
REQUIRE(y != nullptr);
|
||||||
REQUIRE(std::dynamic_pointer_cast<NumberValue>(y)->value == 35.0);
|
REQUIRE(std::dynamic_pointer_cast<NumberValue>(y)->value == 35.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Multiplication") {
|
SECTION("Multiplication") {
|
||||||
REQUIRE(state.do_string("var z = 7 * 8;"));
|
REQUIRE(state.do_string("var z = 7 * 8;"));
|
||||||
auto z = state.get_global("z");
|
auto z = state.get_global("z");
|
||||||
REQUIRE(z != nullptr);
|
REQUIRE(z != nullptr);
|
||||||
REQUIRE(std::dynamic_pointer_cast<NumberValue>(z)->value == 56.0);
|
REQUIRE(std::dynamic_pointer_cast<NumberValue>(z)->value == 56.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Division") {
|
SECTION("Division") {
|
||||||
REQUIRE(state.do_string("var w = 100 / 4;"));
|
REQUIRE(state.do_string("var w = 100 / 4;"));
|
||||||
auto w = state.get_global("w");
|
auto w = state.get_global("w");
|
||||||
REQUIRE(w != nullptr);
|
REQUIRE(w != nullptr);
|
||||||
REQUIRE(std::dynamic_pointer_cast<NumberValue>(w)->value == 25.0);
|
REQUIRE(std::dynamic_pointer_cast<NumberValue>(w)->value == 25.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("VM - Variables and assignment", "[vm]") {
|
TEST_CASE("VM - Variables and assignment", "[vm]") {
|
||||||
State state(ExecutionMode::VM);
|
State state;
|
||||||
|
|
||||||
SECTION("Variable declaration and initialization") {
|
SECTION("Variable declaration and initialization") {
|
||||||
REQUIRE(state.do_string("var a = 42;"));
|
REQUIRE(state.do_string("var a = 42;"));
|
||||||
auto a = state.get_global("a");
|
auto a = state.get_global("a");
|
||||||
REQUIRE(a != nullptr);
|
REQUIRE(a != nullptr);
|
||||||
REQUIRE(std::dynamic_pointer_cast<NumberValue>(a)->value == 42.0);
|
REQUIRE(std::dynamic_pointer_cast<NumberValue>(a)->value == 42.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Variable assignment") {
|
SECTION("Variable assignment") {
|
||||||
REQUIRE(state.do_string(R"(
|
REQUIRE(state.do_string(R"(
|
||||||
var b = 10;
|
var b = 10;
|
||||||
b = 20;
|
b = 20;
|
||||||
)"));
|
)"));
|
||||||
auto b = state.get_global("b");
|
auto b = state.get_global("b");
|
||||||
REQUIRE(std::dynamic_pointer_cast<NumberValue>(b)->value == 20.0);
|
REQUIRE(std::dynamic_pointer_cast<NumberValue>(b)->value == 20.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("VM - String operations", "[vm]") {
|
TEST_CASE("VM - String operations", "[vm]") {
|
||||||
State state(ExecutionMode::VM);
|
State state;
|
||||||
|
|
||||||
SECTION("String concatenation") {
|
SECTION("String concatenation") {
|
||||||
REQUIRE(state.do_string(R"(var greeting = "Hello" + " " + "World";)"));
|
REQUIRE(state.do_string(R"(var greeting = "Hello" + " " + "World";)"));
|
||||||
auto greeting = state.get_global("greeting");
|
auto greeting = state.get_global("greeting");
|
||||||
REQUIRE(greeting != nullptr);
|
REQUIRE(greeting != nullptr);
|
||||||
REQUIRE(greeting->type() == Type::STRING);
|
REQUIRE(greeting->type() == Type::STRING);
|
||||||
REQUIRE(std::dynamic_pointer_cast<StringValue>(greeting)->value == "Hello World");
|
REQUIRE(std::dynamic_pointer_cast<StringValue>(greeting)->value ==
|
||||||
}
|
"Hello World");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("VM - Comparison operators", "[vm]") {
|
TEST_CASE("VM - Comparison operators", "[vm]") {
|
||||||
State state(ExecutionMode::VM);
|
State state;
|
||||||
|
|
||||||
SECTION("Equality") {
|
SECTION("Equality") {
|
||||||
REQUIRE(state.do_string("var eq = 10 == 10;"));
|
REQUIRE(state.do_string("var eq = 10 == 10;"));
|
||||||
auto eq = state.get_global("eq");
|
auto eq = state.get_global("eq");
|
||||||
REQUIRE(std::dynamic_pointer_cast<BoolValue>(eq)->value == true);
|
REQUIRE(std::dynamic_pointer_cast<BoolValue>(eq)->value == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Greater than") {
|
SECTION("Greater than") {
|
||||||
REQUIRE(state.do_string("var gt = 20 > 10;"));
|
REQUIRE(state.do_string("var gt = 20 > 10;"));
|
||||||
auto gt = state.get_global("gt");
|
auto gt = state.get_global("gt");
|
||||||
REQUIRE(std::dynamic_pointer_cast<BoolValue>(gt)->value == true);
|
REQUIRE(std::dynamic_pointer_cast<BoolValue>(gt)->value == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Less than") {
|
SECTION("Less than") {
|
||||||
REQUIRE(state.do_string("var lt = 5 < 10;"));
|
REQUIRE(state.do_string("var lt = 5 < 10;"));
|
||||||
auto lt = state.get_global("lt");
|
auto lt = state.get_global("lt");
|
||||||
REQUIRE(std::dynamic_pointer_cast<BoolValue>(lt)->value == true);
|
REQUIRE(std::dynamic_pointer_cast<BoolValue>(lt)->value == true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("VM - Lists", "[vm]") {
|
TEST_CASE("VM - Lists", "[vm]") {
|
||||||
State state(ExecutionMode::VM);
|
State state;
|
||||||
|
|
||||||
SECTION("Create list") {
|
SECTION("Create list") {
|
||||||
REQUIRE(state.do_string("var numbers = [1, 2, 3, 4, 5];"));
|
REQUIRE(state.do_string("var numbers = [1, 2, 3, 4, 5];"));
|
||||||
auto numbers = state.get_global("numbers");
|
auto numbers = state.get_global("numbers");
|
||||||
REQUIRE(numbers != nullptr);
|
REQUIRE(numbers != nullptr);
|
||||||
REQUIRE(numbers->type() == Type::LIST);
|
REQUIRE(numbers->type() == Type::LIST);
|
||||||
auto list = std::dynamic_pointer_cast<ListValue>(numbers);
|
auto list = std::dynamic_pointer_cast<ListValue>(numbers);
|
||||||
REQUIRE(list->size() == 5);
|
REQUIRE(list->size() == 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("List indexing") {
|
SECTION("List indexing") {
|
||||||
REQUIRE(state.do_string(R"(
|
REQUIRE(state.do_string(R"(
|
||||||
var arr = [10, 20, 30];
|
var arr = [10, 20, 30];
|
||||||
var item = arr[1];
|
var item = arr[1];
|
||||||
)"));
|
)"));
|
||||||
auto item = state.get_global("item");
|
auto item = state.get_global("item");
|
||||||
REQUIRE(std::dynamic_pointer_cast<NumberValue>(item)->value == 20.0);
|
REQUIRE(std::dynamic_pointer_cast<NumberValue>(item)->value == 20.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("VM - Maps", "[vm]") {
|
TEST_CASE("VM - Maps", "[vm]") {
|
||||||
State state(ExecutionMode::VM);
|
State state;
|
||||||
|
|
||||||
SECTION("Create map") {
|
SECTION("Create map") {
|
||||||
REQUIRE(state.do_string(R"(var person = {"name": "Alice", "age": "30"};)"));
|
REQUIRE(state.do_string(R"(var person = {"name": "Alice", "age": "30"};)"));
|
||||||
auto person = state.get_global("person");
|
auto person = state.get_global("person");
|
||||||
REQUIRE(person != nullptr);
|
REQUIRE(person != nullptr);
|
||||||
REQUIRE(person->type() == Type::MAP);
|
REQUIRE(person->type() == Type::MAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Map access") {
|
SECTION("Map access") {
|
||||||
REQUIRE(state.do_string(R"(
|
REQUIRE(state.do_string(R"(
|
||||||
var data = {"key": "value"};
|
var data = {"key": "value"};
|
||||||
var val = data["key"];
|
var val = data["key"];
|
||||||
)"));
|
)"));
|
||||||
auto val = state.get_global("val");
|
auto val = state.get_global("val");
|
||||||
REQUIRE(val->type() == Type::STRING);
|
REQUIRE(val->type() == Type::STRING);
|
||||||
REQUIRE(std::dynamic_pointer_cast<StringValue>(val)->value == "value");
|
REQUIRE(std::dynamic_pointer_cast<StringValue>(val)->value == "value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("VM - If statements", "[vm]") {
|
TEST_CASE("VM - If statements", "[vm]") {
|
||||||
State state(ExecutionMode::VM);
|
State state;
|
||||||
|
|
||||||
SECTION("If branch taken") {
|
SECTION("If branch taken") {
|
||||||
REQUIRE(state.do_string(R"(
|
REQUIRE(state.do_string(R"(
|
||||||
var x = 10;
|
var x = 10;
|
||||||
if (x > 5) {
|
if (x > 5) {
|
||||||
x = 100;
|
x = 100;
|
||||||
}
|
}
|
||||||
)"));
|
)"));
|
||||||
auto x = state.get_global("x");
|
auto x = state.get_global("x");
|
||||||
REQUIRE(std::dynamic_pointer_cast<NumberValue>(x)->value == 100.0);
|
REQUIRE(std::dynamic_pointer_cast<NumberValue>(x)->value == 100.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Else branch taken") {
|
SECTION("Else branch taken") {
|
||||||
REQUIRE(state.do_string(R"(
|
REQUIRE(state.do_string(R"(
|
||||||
var y = 3;
|
var y = 3;
|
||||||
if (y > 5) {
|
if (y > 5) {
|
||||||
y = 100;
|
y = 100;
|
||||||
@@ -156,16 +157,16 @@ TEST_CASE("VM - If statements", "[vm]") {
|
|||||||
y = 200;
|
y = 200;
|
||||||
}
|
}
|
||||||
)"));
|
)"));
|
||||||
auto y = state.get_global("y");
|
auto y = state.get_global("y");
|
||||||
REQUIRE(std::dynamic_pointer_cast<NumberValue>(y)->value == 200.0);
|
REQUIRE(std::dynamic_pointer_cast<NumberValue>(y)->value == 200.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("VM - While loops", "[vm]") {
|
TEST_CASE("VM - While loops", "[vm]") {
|
||||||
State state(ExecutionMode::VM);
|
State state;
|
||||||
|
|
||||||
SECTION("While loop") {
|
SECTION("While loop") {
|
||||||
REQUIRE(state.do_string(R"(
|
REQUIRE(state.do_string(R"(
|
||||||
var counter = 0;
|
var counter = 0;
|
||||||
var sum = 0;
|
var sum = 0;
|
||||||
while (counter < 5) {
|
while (counter < 5) {
|
||||||
@@ -173,97 +174,20 @@ TEST_CASE("VM - While loops", "[vm]") {
|
|||||||
counter = counter + 1;
|
counter = counter + 1;
|
||||||
}
|
}
|
||||||
)"));
|
)"));
|
||||||
auto sum = state.get_global("sum");
|
auto sum = state.get_global("sum");
|
||||||
REQUIRE(std::dynamic_pointer_cast<NumberValue>(sum)->value == 10.0);
|
REQUIRE(std::dynamic_pointer_cast<NumberValue>(sum)->value == 10.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("VM - Native functions", "[vm]") {
|
TEST_CASE("VM - Native functions", "[vm]") {
|
||||||
State state(ExecutionMode::VM);
|
State state;
|
||||||
|
|
||||||
SECTION("len function") {
|
SECTION("len function") {
|
||||||
REQUIRE(state.do_string(R"(
|
REQUIRE(state.do_string(R"(
|
||||||
var arr = [1, 2, 3, 4];
|
var arr = [1, 2, 3, 4];
|
||||||
var size = len(arr);
|
var size = len(arr);
|
||||||
)"));
|
)"));
|
||||||
auto size = state.get_global("size");
|
auto size = state.get_global("size");
|
||||||
REQUIRE(std::dynamic_pointer_cast<NumberValue>(size)->value == 4.0);
|
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);
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user