2021-03-23 01:11:21 +01:00
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
#include "cs_bcode.hh"
|
|
|
|
#include "cs_state.hh"
|
2021-03-24 20:33:20 +01:00
|
|
|
#include "cs_thread.hh"
|
2021-03-23 01:25:47 +01:00
|
|
|
#include "cs_strman.hh"
|
2021-03-27 00:26:59 +01:00
|
|
|
#include "cs_gen.hh"
|
2021-03-24 02:21:27 +01:00
|
|
|
#include "cs_vm.hh" // break/continue, call_with_args
|
2021-03-23 22:17:25 +01:00
|
|
|
#include "cs_parser.hh"
|
2021-03-23 01:11:21 +01:00
|
|
|
|
2021-03-23 23:32:25 +01:00
|
|
|
namespace cubescript {
|
2021-03-23 01:11:21 +01:00
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
internal_state::internal_state(alloc_func af, void *data):
|
2021-03-23 01:11:21 +01:00
|
|
|
allocf{af}, aptr{data},
|
|
|
|
idents{allocator_type{this}},
|
|
|
|
identmap{allocator_type{this}},
|
|
|
|
varprintf{},
|
2021-03-23 23:29:32 +01:00
|
|
|
strman{create<string_pool>(this)},
|
2021-03-23 01:11:21 +01:00
|
|
|
empty{bcode_init_empty(this)}
|
|
|
|
{}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
internal_state::~internal_state() {
|
2021-03-23 01:11:21 +01:00
|
|
|
bcode_free_empty(this, empty);
|
|
|
|
destroy(strman);
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
void *internal_state::alloc(void *ptr, size_t os, size_t ns) {
|
2021-03-23 01:11:21 +01:00
|
|
|
void *p = allocf(aptr, ptr, os, ns);
|
|
|
|
if (!p && ns) {
|
|
|
|
throw std::bad_alloc{};
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
static void *default_alloc(void *, void *p, size_t, size_t ns) {
|
2021-03-23 02:43:00 +01:00
|
|
|
if (!ns) {
|
|
|
|
std::free(p);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return std::realloc(p, ns);
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
void init_lib_base(state &cs);
|
|
|
|
void init_lib_math(state &cs);
|
|
|
|
void init_lib_string(state &cs);
|
|
|
|
void init_lib_list(state &cs);
|
2021-03-23 02:43:00 +01:00
|
|
|
|
|
|
|
/* public interfaces */
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
state::state(): state{default_alloc, nullptr} {}
|
2021-03-23 02:43:00 +01:00
|
|
|
|
2021-03-25 01:37:13 +01:00
|
|
|
state::state(alloc_func func, void *data) {
|
2021-03-23 23:29:32 +01:00
|
|
|
command *p;
|
2021-03-23 02:43:00 +01:00
|
|
|
|
|
|
|
if (!func) {
|
2021-03-23 23:29:32 +01:00
|
|
|
func = default_alloc;
|
2021-03-23 02:43:00 +01:00
|
|
|
}
|
|
|
|
/* allocator is not set up yet, use func directly */
|
2021-03-25 01:37:13 +01:00
|
|
|
auto *statep = static_cast<internal_state *>(
|
2021-03-23 23:29:32 +01:00
|
|
|
func(data, nullptr, 0, sizeof(internal_state))
|
2021-03-23 02:43:00 +01:00
|
|
|
);
|
|
|
|
/* allocator will be set up in the constructor */
|
2021-03-25 01:37:13 +01:00
|
|
|
new (statep) internal_state{func, data};
|
|
|
|
|
|
|
|
try {
|
|
|
|
p_tstate = statep->create<thread_state>(statep);
|
|
|
|
} catch (...) {
|
|
|
|
statep->destroy(statep);
|
|
|
|
throw;
|
|
|
|
}
|
2021-03-23 02:43:00 +01:00
|
|
|
|
2021-03-25 01:37:13 +01:00
|
|
|
p_tstate->pstate = this;
|
|
|
|
p_tstate->istate = statep;
|
2021-03-25 01:55:47 +01:00
|
|
|
p_tstate->owner = true;
|
2021-03-23 02:43:00 +01:00
|
|
|
|
2021-03-24 02:21:27 +01:00
|
|
|
for (int i = 0; i < MAX_ARGUMENTS; ++i) {
|
2021-03-23 02:43:00 +01:00
|
|
|
char buf[32];
|
|
|
|
snprintf(buf, sizeof(buf), "arg%d", i + 1);
|
2021-03-23 23:29:32 +01:00
|
|
|
new_ident(static_cast<char const *>(buf), IDENT_FLAG_ARG);
|
2021-03-23 02:43:00 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
ident *id = new_ident("//dummy");
|
2021-03-24 02:21:27 +01:00
|
|
|
if (id->get_index() != ID_IDX_DUMMY) {
|
2021-03-23 23:29:32 +01:00
|
|
|
throw internal_error{"invalid dummy index"};
|
2021-03-23 02:43:00 +01:00
|
|
|
}
|
|
|
|
|
2021-03-24 02:21:27 +01:00
|
|
|
id = new_ivar("numargs", MAX_ARGUMENTS, 0, 0);
|
|
|
|
if (id->get_index() != ID_IDX_NUMARGS) {
|
2021-03-23 23:29:32 +01:00
|
|
|
throw internal_error{"invalid numargs index"};
|
2021-03-23 02:43:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
id = new_ivar("dbgalias", 0, 1000, 4);
|
2021-03-24 02:21:27 +01:00
|
|
|
if (id->get_index() != ID_IDX_DBGALIAS) {
|
2021-03-23 23:29:32 +01:00
|
|
|
throw internal_error{"invalid dbgalias index"};
|
2021-03-23 02:43:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
p = new_command("do", "e", [](auto &cs, auto args, auto &res) {
|
|
|
|
cs.run(args[0].get_code(), res);
|
|
|
|
});
|
2021-03-23 23:29:32 +01:00
|
|
|
static_cast<command_impl *>(p)->p_type = ID_DO;
|
2021-03-23 02:43:00 +01:00
|
|
|
|
|
|
|
p = new_command("doargs", "e", [](auto &cs, auto args, auto &res) {
|
2021-03-26 03:05:14 +01:00
|
|
|
call_with_args(*cs.thread_pointer(), [&cs, &res, &args]() {
|
2021-03-23 02:43:00 +01:00
|
|
|
cs.run(args[0].get_code(), res);
|
|
|
|
});
|
|
|
|
});
|
2021-03-23 23:29:32 +01:00
|
|
|
static_cast<command_impl *>(p)->p_type = ID_DOARGS;
|
2021-03-23 02:43:00 +01:00
|
|
|
|
|
|
|
p = new_command("if", "tee", [](auto &cs, auto args, auto &res) {
|
|
|
|
cs.run((args[0].get_bool() ? args[1] : args[2]).get_code(), res);
|
|
|
|
});
|
2021-03-23 23:29:32 +01:00
|
|
|
static_cast<command_impl *>(p)->p_type = ID_IF;
|
2021-03-23 02:43:00 +01:00
|
|
|
|
|
|
|
p = new_command("result", "t", [](auto &, auto args, auto &res) {
|
|
|
|
res = std::move(args[0]);
|
|
|
|
});
|
2021-03-23 23:29:32 +01:00
|
|
|
static_cast<command_impl *>(p)->p_type = ID_RESULT;
|
2021-03-23 02:43:00 +01:00
|
|
|
|
|
|
|
p = new_command("!", "t", [](auto &, auto args, auto &res) {
|
|
|
|
res.set_int(!args[0].get_bool());
|
|
|
|
});
|
2021-03-23 23:29:32 +01:00
|
|
|
static_cast<command_impl *>(p)->p_type = ID_NOT;
|
2021-03-23 02:43:00 +01:00
|
|
|
|
|
|
|
p = new_command("&&", "E1V", [](auto &cs, auto args, auto &res) {
|
|
|
|
if (args.empty()) {
|
|
|
|
res.set_int(1);
|
|
|
|
} else {
|
|
|
|
for (size_t i = 0; i < args.size(); ++i) {
|
2021-03-23 23:29:32 +01:00
|
|
|
bcode *code = args[i].get_code();
|
2021-03-23 02:43:00 +01:00
|
|
|
if (code) {
|
|
|
|
cs.run(code, res);
|
|
|
|
} else {
|
|
|
|
res = std::move(args[i]);
|
|
|
|
}
|
|
|
|
if (!res.get_bool()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2021-03-23 23:29:32 +01:00
|
|
|
static_cast<command_impl *>(p)->p_type = ID_AND;
|
2021-03-23 02:43:00 +01:00
|
|
|
|
|
|
|
p = new_command("||", "E1V", [](auto &cs, auto args, auto &res) {
|
|
|
|
if (args.empty()) {
|
|
|
|
res.set_int(0);
|
|
|
|
} else {
|
|
|
|
for (size_t i = 0; i < args.size(); ++i) {
|
2021-03-23 23:29:32 +01:00
|
|
|
bcode *code = args[i].get_code();
|
2021-03-23 02:43:00 +01:00
|
|
|
if (code) {
|
|
|
|
cs.run(code, res);
|
|
|
|
} else {
|
|
|
|
res = std::move(args[i]);
|
|
|
|
}
|
|
|
|
if (res.get_bool()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2021-03-23 23:29:32 +01:00
|
|
|
static_cast<command_impl *>(p)->p_type = ID_OR;
|
2021-03-23 02:43:00 +01:00
|
|
|
|
|
|
|
p = new_command("local", "", nullptr);
|
2021-03-23 23:29:32 +01:00
|
|
|
static_cast<command_impl *>(p)->p_type = ID_LOCAL;
|
2021-03-23 02:43:00 +01:00
|
|
|
|
|
|
|
p = new_command("break", "", [](auto &cs, auto, auto &) {
|
|
|
|
if (cs.is_in_loop()) {
|
2021-03-24 02:21:27 +01:00
|
|
|
throw break_exception{};
|
2021-03-23 02:43:00 +01:00
|
|
|
} else {
|
2021-03-23 23:29:32 +01:00
|
|
|
throw error{cs, "no loop to break"};
|
2021-03-23 02:43:00 +01:00
|
|
|
}
|
|
|
|
});
|
2021-03-23 23:29:32 +01:00
|
|
|
static_cast<command_impl *>(p)->p_type = ID_BREAK;
|
2021-03-23 02:43:00 +01:00
|
|
|
|
|
|
|
p = new_command("continue", "", [](auto &cs, auto, auto &) {
|
|
|
|
if (cs.is_in_loop()) {
|
2021-03-24 02:21:27 +01:00
|
|
|
throw continue_exception{};
|
2021-03-23 02:43:00 +01:00
|
|
|
} else {
|
2021-03-23 23:29:32 +01:00
|
|
|
throw error{cs, "no loop to continue"};
|
2021-03-23 02:43:00 +01:00
|
|
|
}
|
|
|
|
});
|
2021-03-23 23:29:32 +01:00
|
|
|
static_cast<command_impl *>(p)->p_type = ID_CONTINUE;
|
2021-03-23 02:43:00 +01:00
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
init_lib_base(*this);
|
2021-03-23 02:43:00 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT state::~state() {
|
2021-03-23 02:48:14 +01:00
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::destroy() {
|
2021-03-25 01:55:47 +01:00
|
|
|
if (!p_tstate || !p_tstate->owner) {
|
2021-03-23 02:48:14 +01:00
|
|
|
return;
|
|
|
|
}
|
2021-03-25 01:37:13 +01:00
|
|
|
auto *sp = p_tstate->istate;
|
|
|
|
for (auto &p: sp->idents) {
|
2021-03-23 23:29:32 +01:00
|
|
|
ident *i = p.second;
|
|
|
|
alias *a = i->get_alias();
|
2021-03-23 02:48:14 +01:00
|
|
|
if (a) {
|
|
|
|
a->get_value().force_none();
|
2021-03-23 23:29:32 +01:00
|
|
|
static_cast<alias_impl *>(a)->clean_code();
|
2021-03-23 02:48:14 +01:00
|
|
|
}
|
2021-03-25 01:37:13 +01:00
|
|
|
sp->destroy(i->p_impl);
|
2021-03-23 02:48:14 +01:00
|
|
|
}
|
2021-03-25 01:37:13 +01:00
|
|
|
sp->destroy(p_tstate);
|
|
|
|
sp->destroy(sp);
|
2021-03-23 02:48:14 +01:00
|
|
|
}
|
|
|
|
|
2021-03-25 01:55:47 +01:00
|
|
|
state::state(internal_state *s) {
|
2021-03-25 01:37:13 +01:00
|
|
|
p_tstate = s->create<thread_state>(s);
|
|
|
|
p_tstate->istate = s;
|
2021-03-24 20:33:20 +01:00
|
|
|
}
|
2021-03-23 02:48:14 +01:00
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT state state::new_thread() {
|
2021-03-25 01:37:13 +01:00
|
|
|
return state{p_tstate->istate};
|
2021-03-23 02:48:14 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT hook_func state::set_call_hook(hook_func func) {
|
2021-03-24 20:33:20 +01:00
|
|
|
return p_tstate->set_hook(std::move(func));
|
2021-03-23 02:48:14 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT hook_func const &state::get_call_hook() const {
|
2021-03-24 20:33:20 +01:00
|
|
|
return p_tstate->get_hook();
|
2021-03-23 02:48:14 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT hook_func &state::get_call_hook() {
|
2021-03-24 20:33:20 +01:00
|
|
|
return p_tstate->get_hook();
|
2021-03-23 02:48:14 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT var_print_func state::set_var_printer(
|
|
|
|
var_print_func func
|
2021-03-23 02:48:14 +01:00
|
|
|
) {
|
2021-03-25 01:37:13 +01:00
|
|
|
auto fn = std::move(p_tstate->istate->varprintf);
|
|
|
|
p_tstate->istate->varprintf = std::move(func);
|
2021-03-23 02:48:14 +01:00
|
|
|
return fn;
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT var_print_func const &state::get_var_printer() const {
|
2021-03-25 01:37:13 +01:00
|
|
|
return p_tstate->istate->varprintf;
|
2021-03-23 02:48:14 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::print_var(global_var const &v) const {
|
2021-03-25 01:37:13 +01:00
|
|
|
if (p_tstate->istate->varprintf) {
|
|
|
|
p_tstate->istate->varprintf(*this, v);
|
2021-03-23 22:17:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void *state::alloc(void *ptr, size_t os, size_t ns) {
|
2021-03-25 01:37:13 +01:00
|
|
|
return p_tstate->istate->alloc(ptr, os, ns);
|
2021-03-23 02:48:14 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT ident *state::add_ident(
|
|
|
|
ident *id, ident_impl *impl
|
2021-03-23 22:17:25 +01:00
|
|
|
) {
|
|
|
|
if (!id) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
id->p_impl = impl;
|
2021-03-25 01:37:13 +01:00
|
|
|
p_tstate->istate->idents[id->get_name()] = id;
|
|
|
|
static_cast<ident_impl *>(impl)->p_index = p_tstate->istate->identmap.size();
|
|
|
|
p_tstate->istate->identmap.push_back(id);
|
|
|
|
return p_tstate->istate->identmap.back();
|
2021-03-23 22:17:25 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT ident *state::new_ident(
|
2021-03-23 22:17:25 +01:00
|
|
|
std::string_view name, int flags
|
|
|
|
) {
|
2021-03-23 23:29:32 +01:00
|
|
|
ident *id = get_ident(name);
|
2021-03-23 22:17:25 +01:00
|
|
|
if (!id) {
|
|
|
|
if (!is_valid_name(name)) {
|
2021-03-23 23:29:32 +01:00
|
|
|
throw error{
|
2021-03-23 22:17:25 +01:00
|
|
|
*this, "number %s is not a valid identifier name", name.data()
|
|
|
|
};
|
|
|
|
}
|
2021-03-25 01:37:13 +01:00
|
|
|
auto *inst = p_tstate->istate->create<alias_impl>(
|
|
|
|
*this, string_ref{p_tstate->istate, name}, flags
|
2021-03-23 22:17:25 +01:00
|
|
|
);
|
|
|
|
id = add_ident(inst, inst);
|
|
|
|
}
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT ident *state::get_ident(std::string_view name) {
|
2021-03-25 01:37:13 +01:00
|
|
|
auto id = p_tstate->istate->idents.find(name);
|
|
|
|
if (id != p_tstate->istate->idents.end()) {
|
2021-03-23 22:17:25 +01:00
|
|
|
return id->second;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT alias *state::get_alias(std::string_view name) {
|
2021-03-23 22:17:25 +01:00
|
|
|
auto id = get_ident(name);
|
|
|
|
if (!id || !id->is_alias()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
return static_cast<alias *>(id);
|
2021-03-23 22:17:25 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT bool state::have_ident(std::string_view name) {
|
2021-03-25 01:37:13 +01:00
|
|
|
return p_tstate->istate->idents.find(name) != p_tstate->istate->idents.end();
|
2021-03-23 22:17:25 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT std::span<ident *> state::get_idents() {
|
|
|
|
return std::span<ident *>{
|
2021-03-25 01:37:13 +01:00
|
|
|
p_tstate->istate->identmap.data(),
|
|
|
|
p_tstate->istate->identmap.size()
|
2021-03-23 22:17:25 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT std::span<ident const *> state::get_idents() const {
|
2021-03-25 01:37:13 +01:00
|
|
|
auto ptr = const_cast<ident const **>(p_tstate->istate->identmap.data());
|
|
|
|
return std::span<ident const *>{ptr, p_tstate->istate->identmap.size()};
|
2021-03-23 22:17:25 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT integer_var *state::new_ivar(
|
|
|
|
std::string_view n, integer_type m, integer_type x, integer_type v,
|
|
|
|
var_cb_func f, int flags
|
2021-03-23 22:17:25 +01:00
|
|
|
) {
|
2021-03-25 01:37:13 +01:00
|
|
|
auto *iv = p_tstate->istate->create<ivar_impl>(
|
|
|
|
string_ref{p_tstate->istate, n}, m, x, v, std::move(f), flags
|
2021-03-23 22:17:25 +01:00
|
|
|
);
|
|
|
|
add_ident(iv, iv);
|
|
|
|
return iv;
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT float_var *state::new_fvar(
|
|
|
|
std::string_view n, float_type m, float_type x, float_type v,
|
|
|
|
var_cb_func f, int flags
|
2021-03-23 22:17:25 +01:00
|
|
|
) {
|
2021-03-25 01:37:13 +01:00
|
|
|
auto *fv = p_tstate->istate->create<fvar_impl>(
|
|
|
|
string_ref{p_tstate->istate, n}, m, x, v, std::move(f), flags
|
2021-03-23 22:17:25 +01:00
|
|
|
);
|
|
|
|
add_ident(fv, fv);
|
|
|
|
return fv;
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT string_var *state::new_svar(
|
|
|
|
std::string_view n, std::string_view v, var_cb_func f, int flags
|
2021-03-23 22:17:25 +01:00
|
|
|
) {
|
2021-03-25 01:37:13 +01:00
|
|
|
auto *sv = p_tstate->istate->create<svar_impl>(
|
|
|
|
string_ref{p_tstate->istate, n}, string_ref{p_tstate->istate, v},
|
|
|
|
string_ref{p_tstate->istate, ""}, std::move(f), flags
|
2021-03-23 22:17:25 +01:00
|
|
|
);
|
|
|
|
add_ident(sv, sv);
|
|
|
|
return sv;
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::reset_var(std::string_view name) {
|
|
|
|
ident *id = get_ident(name);
|
2021-03-23 22:17:25 +01:00
|
|
|
if (!id) {
|
2021-03-23 23:29:32 +01:00
|
|
|
throw error{*this, "variable %s does not exist", name.data()};
|
2021-03-23 22:17:25 +01:00
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
if (id->get_flags() & IDENT_FLAG_READONLY) {
|
|
|
|
throw error{*this, "variable %s is read only", name.data()};
|
2021-03-23 22:17:25 +01:00
|
|
|
}
|
|
|
|
clear_override(*id);
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::touch_var(std::string_view name) {
|
|
|
|
ident *id = get_ident(name);
|
2021-03-23 22:17:25 +01:00
|
|
|
if (id && id->is_var()) {
|
2021-03-23 23:29:32 +01:00
|
|
|
static_cast<var_impl *>(id->p_impl)->changed(*this);
|
2021-03-23 22:17:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::set_alias(
|
|
|
|
std::string_view name, any_value v
|
2021-03-23 22:17:25 +01:00
|
|
|
) {
|
2021-03-23 23:29:32 +01:00
|
|
|
ident *id = get_ident(name);
|
2021-03-23 22:17:25 +01:00
|
|
|
if (id) {
|
|
|
|
switch (id->get_type()) {
|
2021-03-23 23:29:32 +01:00
|
|
|
case ident_type::ALIAS: {
|
|
|
|
alias_impl *a = static_cast<alias_impl *>(id);
|
2021-03-24 02:21:27 +01:00
|
|
|
if (a->get_index() < MAX_ARGUMENTS) {
|
2021-03-26 02:29:54 +01:00
|
|
|
a->set_arg(*p_tstate, v);
|
2021-03-23 22:17:25 +01:00
|
|
|
} else {
|
2021-03-26 02:29:54 +01:00
|
|
|
a->set_alias(*p_tstate, v);
|
2021-03-23 22:17:25 +01:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
case ident_type::IVAR:
|
|
|
|
set_var_int_checked(static_cast<integer_var *>(id), v.get_int());
|
2021-03-23 22:17:25 +01:00
|
|
|
break;
|
2021-03-23 23:29:32 +01:00
|
|
|
case ident_type::FVAR:
|
|
|
|
set_var_float_checked(static_cast<float_var *>(id), v.get_float());
|
2021-03-23 22:17:25 +01:00
|
|
|
break;
|
2021-03-23 23:29:32 +01:00
|
|
|
case ident_type::SVAR:
|
|
|
|
set_var_str_checked(static_cast<string_var *>(id), v.get_str());
|
2021-03-23 22:17:25 +01:00
|
|
|
break;
|
|
|
|
default:
|
2021-03-23 23:29:32 +01:00
|
|
|
throw error{
|
2021-03-23 22:17:25 +01:00
|
|
|
*this, "cannot redefine builtin %s with an alias",
|
|
|
|
id->get_name().data()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
} else if (!is_valid_name(name)) {
|
2021-03-23 23:29:32 +01:00
|
|
|
throw error{*this, "cannot alias invalid name '%s'", name.data()};
|
2021-03-23 22:17:25 +01:00
|
|
|
} else {
|
2021-03-25 01:37:13 +01:00
|
|
|
auto *a = p_tstate->istate->create<alias_impl>(
|
|
|
|
*this, string_ref{p_tstate->istate, name}, std::move(v), identflags
|
2021-03-23 22:17:25 +01:00
|
|
|
);
|
|
|
|
add_ident(a, a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT command *state::new_command(
|
|
|
|
std::string_view name, std::string_view args, command_func func
|
2021-03-23 22:17:25 +01:00
|
|
|
) {
|
|
|
|
int nargs = 0;
|
|
|
|
for (auto fmt = args.begin(); fmt != args.end(); ++fmt) {
|
|
|
|
switch (*fmt) {
|
|
|
|
case 'i':
|
|
|
|
case 'b':
|
|
|
|
case 'f':
|
|
|
|
case 'F':
|
|
|
|
case 't':
|
|
|
|
case 'T':
|
|
|
|
case 'E':
|
|
|
|
case 'N':
|
|
|
|
case 's':
|
|
|
|
case 'e':
|
|
|
|
case 'r':
|
|
|
|
case '$':
|
2021-03-24 02:21:27 +01:00
|
|
|
if (nargs < MAX_ARGUMENTS) {
|
2021-03-23 22:17:25 +01:00
|
|
|
++nargs;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
if (nargs < (*fmt - '0')) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if ((args.end() - fmt) != 2) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if ((fmt[1] != 'C') && (fmt[1] != 'V')) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2021-03-24 02:21:27 +01:00
|
|
|
if (nargs < MAX_ARGUMENTS) {
|
2021-03-23 22:17:25 +01:00
|
|
|
fmt -= *fmt - '0' + 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
case 'V':
|
|
|
|
if ((fmt + 1) != args.end()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
2021-03-25 01:37:13 +01:00
|
|
|
auto *cmd = p_tstate->istate->create<command_impl>(
|
|
|
|
string_ref{p_tstate->istate, name},
|
|
|
|
string_ref{p_tstate->istate, args},
|
|
|
|
nargs, std::move(func)
|
2021-03-23 22:17:25 +01:00
|
|
|
);
|
|
|
|
add_ident(cmd, cmd);
|
|
|
|
return cmd;
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::clear_override(ident &id) {
|
|
|
|
if (!(id.get_flags() & IDENT_FLAG_OVERRIDDEN)) {
|
2021-03-23 22:17:25 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch (id.get_type()) {
|
2021-03-23 23:29:32 +01:00
|
|
|
case ident_type::ALIAS: {
|
|
|
|
alias_impl &a = static_cast<alias_impl &>(id);
|
2021-03-23 22:17:25 +01:00
|
|
|
a.clean_code();
|
|
|
|
a.get_value().set_str("");
|
|
|
|
break;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
case ident_type::IVAR: {
|
|
|
|
ivar_impl &iv = static_cast<ivar_impl &>(id);
|
2021-03-23 22:17:25 +01:00
|
|
|
iv.set_value(iv.p_overrideval);
|
|
|
|
iv.changed(*this);
|
|
|
|
break;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
case ident_type::FVAR: {
|
|
|
|
fvar_impl &fv = static_cast<fvar_impl &>(id);
|
2021-03-23 22:17:25 +01:00
|
|
|
fv.set_value(fv.p_overrideval);
|
|
|
|
fv.changed(*this);
|
|
|
|
break;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
case ident_type::SVAR: {
|
|
|
|
svar_impl &sv = static_cast<svar_impl &>(id);
|
2021-03-23 22:17:25 +01:00
|
|
|
sv.set_value(sv.p_overrideval);
|
|
|
|
sv.changed(*this);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
id.p_impl->p_flags &= ~IDENT_FLAG_OVERRIDDEN;
|
2021-03-23 22:17:25 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::clear_overrides() {
|
2021-03-25 01:37:13 +01:00
|
|
|
for (auto &p: p_tstate->istate->idents) {
|
2021-03-23 22:17:25 +01:00
|
|
|
clear_override(*(p.second));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 21:55:19 +01:00
|
|
|
template<typename SF>
|
2021-03-23 23:29:32 +01:00
|
|
|
inline void override_var(state &cs, global_var *v, int &vflags, SF sf) {
|
|
|
|
if ((cs.identflags & IDENT_FLAG_OVERRIDDEN) || (vflags & IDENT_FLAG_OVERRIDE)) {
|
|
|
|
if (vflags & IDENT_FLAG_PERSIST) {
|
|
|
|
throw error{
|
2021-03-23 21:55:19 +01:00
|
|
|
cs, "cannot override persistent variable '%s'",
|
|
|
|
v->get_name().data()
|
|
|
|
};
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
if (!(vflags & IDENT_FLAG_OVERRIDDEN)) {
|
2021-03-23 21:55:19 +01:00
|
|
|
sf();
|
2021-03-23 23:29:32 +01:00
|
|
|
vflags |= IDENT_FLAG_OVERRIDDEN;
|
2021-03-23 21:55:19 +01:00
|
|
|
}
|
|
|
|
} else {
|
2021-03-23 23:29:32 +01:00
|
|
|
if (vflags & IDENT_FLAG_OVERRIDDEN) {
|
|
|
|
vflags &= ~IDENT_FLAG_OVERRIDDEN;
|
2021-03-23 21:55:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::set_var_int(
|
|
|
|
std::string_view name, integer_type v, bool dofunc, bool doclamp
|
2021-03-23 21:55:19 +01:00
|
|
|
) {
|
2021-03-23 23:29:32 +01:00
|
|
|
ident *id = get_ident(name);
|
2021-03-23 21:55:19 +01:00
|
|
|
if (!id || id->is_ivar()) {
|
|
|
|
return;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
ivar_impl *iv = static_cast<ivar_impl *>(id);
|
|
|
|
override_var(
|
2021-03-23 21:55:19 +01:00
|
|
|
*this, iv, iv->p_flags,
|
|
|
|
[&iv]() { iv->p_overrideval = iv->get_value(); }
|
|
|
|
);
|
|
|
|
if (doclamp) {
|
|
|
|
iv->set_value(std::clamp(v, iv->get_val_min(), iv->get_val_max()));
|
|
|
|
} else {
|
|
|
|
iv->set_value(v);
|
|
|
|
}
|
|
|
|
if (dofunc) {
|
|
|
|
iv->changed(*this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::set_var_float(
|
|
|
|
std::string_view name, float_type v, bool dofunc, bool doclamp
|
2021-03-23 21:55:19 +01:00
|
|
|
) {
|
2021-03-23 23:29:32 +01:00
|
|
|
ident *id = get_ident(name);
|
2021-03-23 21:55:19 +01:00
|
|
|
if (!id || id->is_fvar()) {
|
|
|
|
return;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
fvar_impl *fv = static_cast<fvar_impl *>(id);
|
|
|
|
override_var(
|
2021-03-23 21:55:19 +01:00
|
|
|
*this, fv, fv->p_flags,
|
|
|
|
[&fv]() { fv->p_overrideval = fv->get_value(); }
|
|
|
|
);
|
|
|
|
if (doclamp) {
|
|
|
|
fv->set_value(std::clamp(v, fv->get_val_min(), fv->get_val_max()));
|
|
|
|
} else {
|
|
|
|
fv->set_value(v);
|
|
|
|
}
|
|
|
|
if (dofunc) {
|
|
|
|
fv->changed(*this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::set_var_str(
|
2021-03-23 21:55:19 +01:00
|
|
|
std::string_view name, std::string_view v, bool dofunc
|
|
|
|
) {
|
2021-03-23 23:29:32 +01:00
|
|
|
ident *id = get_ident(name);
|
2021-03-23 21:55:19 +01:00
|
|
|
if (!id || id->is_svar()) {
|
|
|
|
return;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
svar_impl *sv = static_cast<svar_impl *>(id);
|
|
|
|
override_var(
|
2021-03-23 21:55:19 +01:00
|
|
|
*this, sv, sv->p_flags,
|
|
|
|
[&sv]() { sv->p_overrideval = sv->get_value(); }
|
|
|
|
);
|
2021-03-25 01:37:13 +01:00
|
|
|
sv->set_value(string_ref{p_tstate->istate, v});
|
2021-03-23 21:55:19 +01:00
|
|
|
if (dofunc) {
|
|
|
|
sv->changed(*this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT std::optional<integer_type>
|
|
|
|
state::get_var_int(std::string_view name) {
|
|
|
|
ident *id = get_ident(name);
|
2021-03-23 21:55:19 +01:00
|
|
|
if (!id || id->is_ivar()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
return static_cast<integer_var *>(id)->get_value();
|
2021-03-23 21:55:19 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT std::optional<float_type>
|
|
|
|
state::get_var_float(std::string_view name) {
|
|
|
|
ident *id = get_ident(name);
|
2021-03-23 21:55:19 +01:00
|
|
|
if (!id || id->is_fvar()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
return static_cast<float_var *>(id)->get_value();
|
2021-03-23 21:55:19 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT std::optional<string_ref>
|
|
|
|
state::get_var_str(std::string_view name) {
|
|
|
|
ident *id = get_ident(name);
|
2021-03-23 21:55:19 +01:00
|
|
|
if (!id || id->is_svar()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2021-03-25 01:37:13 +01:00
|
|
|
return string_ref{
|
|
|
|
p_tstate->istate, static_cast<string_var *>(id)->get_value()
|
|
|
|
};
|
2021-03-23 21:55:19 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT std::optional<integer_type>
|
|
|
|
state::get_var_min_int(std::string_view name) {
|
|
|
|
ident *id = get_ident(name);
|
2021-03-23 21:55:19 +01:00
|
|
|
if (!id || id->is_ivar()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
return static_cast<integer_var *>(id)->get_val_min();
|
2021-03-23 21:55:19 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT std::optional<integer_type>
|
|
|
|
state::get_var_max_int(std::string_view name) {
|
|
|
|
ident *id = get_ident(name);
|
2021-03-23 21:55:19 +01:00
|
|
|
if (!id || id->is_ivar()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
return static_cast<integer_var *>(id)->get_val_max();
|
2021-03-23 21:55:19 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT std::optional<float_type>
|
|
|
|
state::get_var_min_float(std::string_view name) {
|
|
|
|
ident *id = get_ident(name);
|
2021-03-23 21:55:19 +01:00
|
|
|
if (!id || id->is_fvar()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
return static_cast<float_var *>(id)->get_val_min();
|
2021-03-23 21:55:19 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT std::optional<float_type>
|
|
|
|
state::get_var_max_float(std::string_view name) {
|
|
|
|
ident *id = get_ident(name);
|
2021-03-23 21:55:19 +01:00
|
|
|
if (!id || id->is_fvar()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
return static_cast<float_var *>(id)->get_val_max();
|
2021-03-23 21:55:19 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT std::optional<string_ref>
|
|
|
|
state::get_alias_val(std::string_view name) {
|
|
|
|
alias *a = get_alias(name);
|
2021-03-23 21:55:19 +01:00
|
|
|
if (!a) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2021-03-26 02:29:54 +01:00
|
|
|
if ((a->get_index() < MAX_ARGUMENTS) && !ident_is_used_arg(a, *p_tstate)) {
|
2021-03-23 21:55:19 +01:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
return a->get_value().get_str();
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
integer_type clamp_var(state &cs, integer_var *iv, integer_type v) {
|
2021-03-23 21:55:19 +01:00
|
|
|
if (v < iv->get_val_min()) {
|
|
|
|
v = iv->get_val_min();
|
|
|
|
} else if (v > iv->get_val_max()) {
|
|
|
|
v = iv->get_val_max();
|
|
|
|
} else {
|
|
|
|
return v;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
throw error{
|
2021-03-23 21:55:19 +01:00
|
|
|
cs,
|
2021-03-23 23:29:32 +01:00
|
|
|
(iv->get_flags() & IDENT_FLAG_HEX)
|
2021-03-23 21:55:19 +01:00
|
|
|
? (
|
|
|
|
(iv->get_val_min() <= 255)
|
|
|
|
? "valid range for '%s' is %d..0x%X"
|
|
|
|
: "valid range for '%s' is 0x%X..0x%X"
|
|
|
|
)
|
|
|
|
: "valid range for '%s' is %d..%d",
|
|
|
|
iv->get_name().data(), iv->get_val_min(), iv->get_val_max()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::set_var_int_checked(
|
|
|
|
integer_var *iv, integer_type v
|
|
|
|
) {
|
|
|
|
if (iv->get_flags() & IDENT_FLAG_READONLY) {
|
|
|
|
throw error{
|
2021-03-23 21:55:19 +01:00
|
|
|
*this, "variable '%s' is read only", iv->get_name().data()
|
|
|
|
};
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
ivar_impl *ivp = static_cast<ivar_impl *>(iv);
|
|
|
|
override_var(
|
2021-03-23 21:55:19 +01:00
|
|
|
*this, iv, ivp->p_flags,
|
|
|
|
[&ivp]() { ivp->p_overrideval = ivp->p_storage; }
|
|
|
|
);
|
|
|
|
if ((v < iv->get_val_min()) || (v > iv->get_val_max())) {
|
2021-03-23 23:29:32 +01:00
|
|
|
v = clamp_var(*this, iv, v);
|
2021-03-23 21:55:19 +01:00
|
|
|
}
|
|
|
|
iv->set_value(v);
|
|
|
|
ivp->changed(*this);
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::set_var_int_checked(
|
|
|
|
integer_var *iv, std::span<any_value> args
|
2021-03-23 21:55:19 +01:00
|
|
|
) {
|
2021-03-23 23:29:32 +01:00
|
|
|
integer_type v = args[0].force_int();
|
|
|
|
if ((iv->get_flags() & IDENT_FLAG_HEX) && (args.size() > 1)) {
|
2021-03-23 21:55:19 +01:00
|
|
|
v = (v << 16) | (args[1].force_int() << 8);
|
|
|
|
if (args.size() > 2) {
|
|
|
|
v |= args[2].force_int();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
set_var_int_checked(iv, v);
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
float_type clamp_fvar(state &cs, float_var *fv, float_type v) {
|
2021-03-23 21:55:19 +01:00
|
|
|
if (v < fv->get_val_min()) {
|
|
|
|
v = fv->get_val_min();
|
|
|
|
} else if (v > fv->get_val_max()) {
|
|
|
|
v = fv->get_val_max();
|
|
|
|
} else {
|
|
|
|
return v;
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
any_value vmin{cs}, vmax{cs};
|
2021-03-23 21:55:19 +01:00
|
|
|
vmin.set_float(fv->get_val_min());
|
|
|
|
vmax.set_float(fv->get_val_max());
|
2021-03-23 23:29:32 +01:00
|
|
|
throw error{
|
2021-03-23 21:55:19 +01:00
|
|
|
cs, "valid range for '%s' is %s..%s", fv->get_name().data(),
|
|
|
|
vmin.force_str(), vmax.force_str()
|
2021-03-23 22:17:25 +01:00
|
|
|
};
|
2021-03-23 21:55:19 +01:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::set_var_float_checked(
|
|
|
|
float_var *fv, float_type v
|
2021-03-23 21:55:19 +01:00
|
|
|
) {
|
2021-03-23 23:29:32 +01:00
|
|
|
if (fv->get_flags() & IDENT_FLAG_READONLY) {
|
|
|
|
throw error{
|
2021-03-23 21:55:19 +01:00
|
|
|
*this, "variable '%s' is read only", fv->get_name().data()
|
|
|
|
};
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
fvar_impl *fvp = static_cast<fvar_impl *>(fv);
|
|
|
|
override_var(
|
2021-03-23 21:55:19 +01:00
|
|
|
*this, fv, fvp->p_flags,
|
|
|
|
[&fvp]() { fvp->p_overrideval = fvp->p_storage; }
|
|
|
|
);
|
|
|
|
if ((v < fv->get_val_min()) || (v > fv->get_val_max())) {
|
2021-03-23 23:29:32 +01:00
|
|
|
v = clamp_fvar(*this, fv, v);
|
2021-03-23 21:55:19 +01:00
|
|
|
}
|
|
|
|
fv->set_value(v);
|
|
|
|
fvp->changed(*this);
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::set_var_str_checked(
|
|
|
|
string_var *sv, std::string_view v
|
2021-03-23 21:55:19 +01:00
|
|
|
) {
|
2021-03-23 23:29:32 +01:00
|
|
|
if (sv->get_flags() & IDENT_FLAG_READONLY) {
|
|
|
|
throw error{
|
2021-03-23 21:55:19 +01:00
|
|
|
*this, "variable '%s' is read only", sv->get_name().data()
|
|
|
|
};
|
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
svar_impl *svp = static_cast<svar_impl *>(sv);
|
|
|
|
override_var(
|
2021-03-23 21:55:19 +01:00
|
|
|
*this, sv, svp->p_flags,
|
|
|
|
[&svp]() { svp->p_overrideval = svp->p_storage; }
|
|
|
|
);
|
2021-03-25 01:37:13 +01:00
|
|
|
sv->set_value(string_ref{p_tstate->istate, v});
|
2021-03-23 21:55:19 +01:00
|
|
|
svp->changed(*this);
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:29:32 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::init_libs(int libs) {
|
|
|
|
if (libs & LIB_MATH) {
|
|
|
|
init_lib_math(*this);
|
2021-03-23 02:43:00 +01:00
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
if (libs & LIB_STRING) {
|
|
|
|
init_lib_string(*this);
|
2021-03-23 02:43:00 +01:00
|
|
|
}
|
2021-03-23 23:29:32 +01:00
|
|
|
if (libs & LIB_LIST) {
|
|
|
|
init_lib_list(*this);
|
2021-03-23 02:43:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 00:26:59 +01:00
|
|
|
LIBCUBESCRIPT_EXPORT void state::run(bcode *code, any_value &ret) {
|
|
|
|
vm_exec(*p_tstate, reinterpret_cast<std::uint32_t *>(code), ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void do_run(
|
|
|
|
thread_state &ts, std::string_view file, std::string_view code,
|
|
|
|
any_value &ret
|
|
|
|
) {
|
|
|
|
codegen_state gs{ts};
|
|
|
|
gs.src_name = file;
|
|
|
|
gs.code.reserve(64);
|
|
|
|
gs.gen_main(code, VAL_ANY);
|
|
|
|
gs.done();
|
|
|
|
std::uint32_t *cbuf = bcode_alloc(ts.istate, gs.code.size());
|
|
|
|
std::memcpy(cbuf, gs.code.data(), gs.code.size() * sizeof(std::uint32_t));
|
|
|
|
bcode_incr(cbuf);
|
|
|
|
call_with_cleanup([&ts, cbuf, &ret]() {
|
|
|
|
vm_exec(ts, cbuf + 1, ret);
|
|
|
|
}, [cbuf]() {
|
|
|
|
bcode_decr(cbuf);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
LIBCUBESCRIPT_EXPORT void state::run(std::string_view code, any_value &ret) {
|
|
|
|
do_run(*p_tstate, std::string_view{}, code, ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
LIBCUBESCRIPT_EXPORT void state::run(
|
|
|
|
std::string_view code, any_value &ret, std::string_view source
|
|
|
|
) {
|
|
|
|
do_run(*p_tstate, source, code, ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
LIBCUBESCRIPT_EXPORT void state::run(
|
|
|
|
ident *id, std::span<any_value> args, any_value &ret
|
|
|
|
) {
|
|
|
|
std::size_t nargs = args.size();
|
|
|
|
ret.set_none();
|
|
|
|
run_depth_guard level{*p_tstate}; /* incr and decr on scope exit */
|
|
|
|
if (id) {
|
|
|
|
switch (id->get_type()) {
|
|
|
|
default:
|
|
|
|
if (!ident_is_callable(id)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* fallthrough */
|
|
|
|
case ident_type::COMMAND: {
|
|
|
|
auto *cimpl = static_cast<command_impl *>(id);
|
|
|
|
if (nargs < std::size_t(cimpl->get_num_args())) {
|
|
|
|
stack_guard s{*p_tstate}; /* restore after call */
|
|
|
|
auto &targs = p_tstate->vmstack;
|
|
|
|
auto osz = targs.size();
|
|
|
|
targs.resize(osz + cimpl->get_num_args(), any_value{*this});
|
|
|
|
for (std::size_t i = 0; i < nargs; ++i) {
|
|
|
|
targs[osz + i] = args[i];
|
|
|
|
}
|
|
|
|
exec_command(
|
|
|
|
*p_tstate, cimpl, &targs[osz], ret, nargs, false
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
exec_command(
|
|
|
|
*p_tstate, cimpl, &args[0], ret, nargs, false
|
|
|
|
);
|
|
|
|
}
|
|
|
|
nargs = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ident_type::IVAR:
|
|
|
|
if (args.empty()) {
|
|
|
|
print_var(*static_cast<global_var *>(id));
|
|
|
|
} else {
|
|
|
|
set_var_int_checked(static_cast<integer_var *>(id), args);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ident_type::FVAR:
|
|
|
|
if (args.empty()) {
|
|
|
|
print_var(*static_cast<global_var *>(id));
|
|
|
|
} else {
|
|
|
|
set_var_float_checked(
|
|
|
|
static_cast<float_var *>(id), args[0].force_float()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ident_type::SVAR:
|
|
|
|
if (args.empty()) {
|
|
|
|
print_var(*static_cast<global_var *>(id));
|
|
|
|
} else {
|
|
|
|
set_var_str_checked(
|
|
|
|
static_cast<string_var *>(id), args[0].force_str()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ident_type::ALIAS: {
|
|
|
|
alias *a = static_cast<alias *>(id);
|
|
|
|
if (
|
|
|
|
(a->get_index() < MAX_ARGUMENTS) &&
|
|
|
|
!ident_is_used_arg(a, *p_tstate)
|
|
|
|
) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (a->get_value().get_type() == value_type::NONE) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
exec_alias(
|
|
|
|
*p_tstate, a, &args[0], ret, nargs, nargs, 0, 0, BC_RET_NULL
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LIBCUBESCRIPT_EXPORT any_value state::run(bcode *code) {
|
|
|
|
any_value ret{*this};
|
|
|
|
run(code, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
LIBCUBESCRIPT_EXPORT any_value state::run(std::string_view code) {
|
|
|
|
any_value ret{*this};
|
|
|
|
run(code, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
LIBCUBESCRIPT_EXPORT any_value state::run(
|
|
|
|
std::string_view code, std::string_view source
|
|
|
|
) {
|
|
|
|
any_value ret{*this};
|
|
|
|
run(code, ret, source);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
LIBCUBESCRIPT_EXPORT any_value state::run(
|
|
|
|
ident *id, std::span<any_value> args
|
|
|
|
) {
|
|
|
|
any_value ret{*this};
|
|
|
|
run(id, args, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
LIBCUBESCRIPT_EXPORT loop_state state::run_loop(bcode *code, any_value &ret) {
|
|
|
|
++p_tstate->loop_level;
|
|
|
|
try {
|
|
|
|
run(code, ret);
|
|
|
|
} catch (break_exception) {
|
|
|
|
--p_tstate->loop_level;
|
|
|
|
return loop_state::BREAK;
|
|
|
|
} catch (continue_exception) {
|
|
|
|
--p_tstate->loop_level;
|
|
|
|
return loop_state::CONTINUE;
|
|
|
|
} catch (...) {
|
|
|
|
--p_tstate->loop_level;
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
return loop_state::NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
LIBCUBESCRIPT_EXPORT loop_state state::run_loop(bcode *code) {
|
|
|
|
any_value ret{*this};
|
|
|
|
return run_loop(code, ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
LIBCUBESCRIPT_EXPORT bool state::is_in_loop() const {
|
|
|
|
return !!p_tstate->loop_level;
|
|
|
|
}
|
|
|
|
|
2021-03-23 23:32:25 +01:00
|
|
|
} /* namespace cubescript */
|