remove most variable code and leave it to user-defined handlers

the user can now define commands '//ivar', '//fvar' and '//svar'
which take identity (which is the variable) as the first argument
followed by any number of user defined arguments with types (one
can use the N argument type to check argument count, and when no
value arguments are provided, print the variable); this allows us
to implement different styles of variable setting without the
interpreter itself knowing about it, as well as ditch all the
stuff with overridden vars and hex vars and whatnot since this is
all specific to the engine and has no place in here

there is still leftover code remaining, which will get cleaned up
afterwards...
master
Daniel Kolesa 2021-04-03 03:14:52 +02:00
parent 95e7ae320c
commit b00a08ea88
11 changed files with 271 additions and 660 deletions

View File

@ -58,7 +58,6 @@ struct global_var;
using hook_func = callable<void, state &>;
using var_cb_func = callable<void, state &, ident &>;
using var_print_func = callable<void, state const &, global_var const &>;
using command_func = callable<
void, state &, std::span<any_value>, any_value &
>;
@ -267,9 +266,6 @@ protected:
};
struct LIBCUBESCRIPT_EXPORT integer_var: global_var {
integer_type get_val_min() const;
integer_type get_val_max() const;
integer_type get_value() const;
void set_value(integer_type val);
@ -278,9 +274,6 @@ protected:
};
struct LIBCUBESCRIPT_EXPORT float_var: global_var {
float_type get_val_min() const;
float_type get_val_max() const;
float_type get_value() const;
void set_value(float_type val);
@ -357,14 +350,6 @@ struct LIBCUBESCRIPT_EXPORT state {
hook_func const &get_call_hook() const;
hook_func &get_call_hook();
template<typename F>
var_print_func set_var_printer(F &&f) {
return set_var_printer(
var_print_func{std::forward<F>(f), callable_alloc, this}
);
}
var_print_func const &get_var_printer() const;
void init_libs(int libs = LIB_ALL);
void clear_override(ident &id);
@ -454,33 +439,6 @@ struct LIBCUBESCRIPT_EXPORT state {
void set_alias(std::string_view name, any_value v);
void set_var_int(
std::string_view name, integer_type v,
bool dofunc = true, bool doclamp = true
);
void set_var_float(
std::string_view name, float_type v,
bool dofunc = true, bool doclamp = true
);
void set_var_str(
std::string_view name, std::string_view v, bool dofunc = true
);
void set_var_int_checked(integer_var *iv, integer_type v);
void set_var_int_checked(integer_var *iv, std::span<any_value> args);
void set_var_float_checked(float_var *fv, float_type v);
void set_var_str_checked(string_var *fv, std::string_view v);
std::optional<integer_type> get_var_int(std::string_view name);
std::optional<float_type> get_var_float(std::string_view name);
std::optional<string_ref> get_var_str(std::string_view name);
std::optional<integer_type> get_var_min_int(std::string_view name);
std::optional<integer_type> get_var_max_int(std::string_view name);
std::optional<float_type> get_var_min_float(std::string_view name);
std::optional<float_type> get_var_max_float(std::string_view name);
std::optional<string_ref> get_alias_val(std::string_view name);
void print_var(global_var const &v) const;
@ -495,7 +453,6 @@ struct LIBCUBESCRIPT_EXPORT state {
private:
hook_func set_call_hook(hook_func func);
var_print_func set_var_printer(var_print_func func);
integer_var *new_ivar(
std::string_view n, integer_type m, integer_type x, integer_type v,

View File

@ -80,8 +80,6 @@ enum {
* integral values between -0x800000 and 0x7FFFFF inclusive
*/
BC_INST_VAL_INT,
/* print builtin variable with index D using user provided call */
BC_INST_PRINT,
/* pop D aliases off the stack, push their values and recurse the VM
* pop their values afterwards (i.e. they are local to the execution)
*/
@ -144,17 +142,8 @@ enum {
BC_INST_CONC_W,
/* push the value of svar with index D on the stack according to M */
BC_INST_SVAR,
/* pop a value off the stack and set svar with index D to it */
BC_INST_SVAR1,
/* push the value of ivar with index D on the stack according to M */
BC_INST_IVAR,
/* pop up to 3 values off the stack and set ivar with index D to:
*
* ivar1: top
* ivar2: (top << 8) | ((top - 1) << 16)
* ivar3: top | ((top - 1) << 8) | ((top - 2) << 16)
*/
BC_INST_IVAR1, BC_INST_IVAR2, BC_INST_IVAR3,
/* push the value of fvar with index D on the stack according to M */
BC_INST_FVAR,
/* pop a value off the stack and set vvar with index D to it */

View File

@ -800,18 +800,19 @@ static bool compilearg(
}
static void compile_cmd(
codegen_state &gs, command_impl *id, bool &more, int rettype
codegen_state &gs, command_impl *id, ident *self, bool &more, int rettype,
std::uint32_t limit = 0
) {
std::uint32_t comtype = BC_INST_COM, numargs = 0, fakeargs = 0;
std::uint32_t comtype = BC_INST_COM, numargs = 0, numcargs = 0, fakeargs = 0;
bool rep = false;
auto fmt = id->get_args();
for (auto it = fmt.begin(); it != fmt.end(); ++it) {
switch (*it) {
case 's': /* string */
if (more) {
if (more && (!limit || (numcargs < limit))) {
more = compilearg(gs, VAL_STRING);
}
if (!more) {
if (!more || (limit && (numcargs >= limit))) {
if (rep) {
break;
}
@ -831,12 +832,13 @@ static void compile_cmd(
}
}
numargs++;
numcargs++;
break;
case 'i': /* integer */
if (more) {
if (more && (!limit || (numcargs < limit))) {
more = compilearg(gs, VAL_INT);
}
if (!more) {
if (!more || (limit && (numcargs >= limit))) {
if (rep) {
break;
}
@ -844,12 +846,13 @@ static void compile_cmd(
fakeargs++;
}
numargs++;
numcargs++;
break;
case 'b': /* integer, INT_MIN default */
if (more) {
if (more && (!limit || (numcargs < limit))) {
more = compilearg(gs, VAL_INT);
}
if (!more) {
if (!more || (limit && (numcargs >= limit))) {
if (rep) {
break;
}
@ -857,12 +860,13 @@ static void compile_cmd(
fakeargs++;
}
numargs++;
numcargs++;
break;
case 'f': /* float */
if (more) {
if (more && (!limit || (numcargs < limit))) {
more = compilearg(gs, VAL_FLOAT);
}
if (!more) {
if (!more || (limit && (numcargs >= limit))) {
if (rep) {
break;
}
@ -870,12 +874,13 @@ static void compile_cmd(
fakeargs++;
}
numargs++;
numcargs++;
break;
case 'F': /* float, prev-argument default */
if (more) {
if (more && (!limit || (numcargs < limit))) {
more = compilearg(gs, VAL_FLOAT);
}
if (!more) {
if (!more || (limit && (numcargs >= limit))) {
if (rep) {
break;
}
@ -883,12 +888,13 @@ static void compile_cmd(
fakeargs++;
}
numargs++;
numcargs++;
break;
case 't': /* any arg */
if (more) {
if (more && (!limit || (numcargs < limit))) {
more = compilearg(gs, VAL_ANY);
}
if (!more) {
if (!more || (limit && (numcargs >= limit))) {
if (rep) {
break;
}
@ -896,12 +902,13 @@ static void compile_cmd(
fakeargs++;
}
numargs++;
numcargs++;
break;
case 'E': /* condition */
if (more) {
if (more && (!limit || (numcargs < limit))) {
more = compilearg(gs, VAL_COND);
}
if (!more) {
if (!more || (limit && (numcargs >= limit))) {
if (rep) {
break;
}
@ -909,12 +916,13 @@ static void compile_cmd(
fakeargs++;
}
numargs++;
numcargs++;
break;
case 'e': /* code */
if (more) {
if (more && (!limit || (numcargs < limit))) {
more = compilearg(gs, VAL_CODE);
}
if (!more) {
if (!more || (limit && (numcargs >= limit))) {
if (rep) {
break;
}
@ -922,12 +930,13 @@ static void compile_cmd(
fakeargs++;
}
numargs++;
numcargs++;
break;
case 'r': /* ident */
if (more) {
if (more && (!limit || (numcargs < limit))) {
more = compilearg(gs, VAL_IDENT);
}
if (!more) {
if (!more || (limit && (numcargs >= limit))) {
if (rep) {
break;
}
@ -935,9 +944,10 @@ static void compile_cmd(
fakeargs++;
}
numargs++;
numcargs++;
break;
case '$': /* self */
gs.gen_ident(id);
gs.gen_ident(self);
numargs++;
break;
case 'N': /* number of arguments */
@ -946,25 +956,27 @@ static void compile_cmd(
break;
case 'C': /* concatenated string */
comtype = BC_INST_COM_C;
if (more) {
if (more && (!limit || (numcargs < limit))) {
for (;;) {
more = compilearg(gs, VAL_ANY);
if (!more) {
if (!more || (limit && (numcargs >= limit))) {
break;
}
numargs++;
numcargs++;
}
}
goto compilecomv;
case 'V': /* varargs */
comtype = BC_INST_COM_V;
if (more) {
if (more && (!limit || (numcargs < limit))) {
for(;;) {
more = compilearg(gs, VAL_ANY);
if (!more) {
if (!more || (limit && (numcargs >= limit))) {
break;
}
numargs++;
numcargs++;
}
}
goto compilecomv;
@ -972,7 +984,7 @@ static void compile_cmd(
case '2':
case '3':
case '4':
if (more) {
if (more && (!limit || (numcargs < limit))) {
int numrep = *it - '0' + 1;
it -= numrep;
rep = true;
@ -1191,33 +1203,39 @@ static void compilestatements(codegen_state &gs, int rettype, int brak) {
BC_INST_ALIAS | (id->get_index() << 8)
);
goto endstatement;
case ident_type::IVAR:
more = compilearg(gs, VAL_INT);
if (!more) {
gs.gen_int();
case ident_type::IVAR: {
auto *hid = gs.ts.pstate->get_ident("//ivar");
if (!hid || !hid->is_command()) {
throw error{gs.ts, "invalid ivar handler"};
}
gs.code.push_back(
BC_INST_IVAR1 | (id->get_index() << 8)
compile_cmd(
gs, static_cast<command_impl *>(hid),
id, more, rettype, 1
);
goto endstatement;
case ident_type::FVAR:
more = compilearg(gs, VAL_FLOAT);
if (!more) {
gs.gen_float();
}
case ident_type::FVAR: {
auto *hid = gs.ts.pstate->get_ident("//fvar");
if (!hid || !hid->is_command()) {
throw error{gs.ts, "invalid fvar handler"};
}
gs.code.push_back(
BC_INST_FVAR1 | (id->get_index() << 8)
compile_cmd(
gs, static_cast<command_impl *>(hid),
id, more, rettype, 1
);
goto endstatement;
case ident_type::SVAR:
more = compilearg(gs, VAL_STRING);
if (!more) {
gs.gen_str();
}
case ident_type::SVAR: {
auto *hid = gs.ts.pstate->get_ident("//svar");
if (!hid || !hid->is_command()) {
throw error{gs.ts, "invalid svar handler"};
}
gs.code.push_back(
BC_INST_SVAR1 | (id->get_index() << 8)
compile_cmd(
gs, static_cast<command_impl *>(hid),
id, more, rettype, 1
);
goto endstatement;
}
default:
break;
}
@ -1276,7 +1294,7 @@ noid:
break;
case ID_COMMAND:
compile_cmd(
gs, static_cast<command_impl *>(id), more,
gs, static_cast<command_impl *>(id), id, more,
rettype
);
break;
@ -1319,54 +1337,39 @@ noid:
case ID_OR:
compile_and_or(gs, id, more, rettype);
break;
case ID_IVAR:
more = compilearg(gs, VAL_INT); /* first arg */
if (!more) {
gs.code.push_back(BC_INST_PRINT | (id->get_index() << 8));
break;
case ID_IVAR: {
auto *hid = gs.ts.pstate->get_ident("//ivar");
if (!hid || !hid->is_command()) {
throw error{gs.ts, "invalid ivar handler"};
}
if (id->get_flags() & IDENT_FLAG_HEX) {
more = compilearg(gs, VAL_INT); /* second arg */
} else {
more = false;
}
if (!more) {
gs.code.push_back(BC_INST_IVAR1 | (id->get_index() << 8));
break;
}
more = compilearg(gs, VAL_INT); /* third arg */
if (!more) {
gs.code.push_back(BC_INST_IVAR2 | (id->get_index() << 8));
break;
}
gs.code.push_back(BC_INST_IVAR3 | (id->get_index() << 8));
compile_cmd(
gs, static_cast<command_impl *>(hid),
id, more, rettype
);
break;
case ID_FVAR:
more = compilearg(gs, VAL_FLOAT);
if (!more) {
gs.code.push_back(BC_INST_PRINT | (id->get_index() << 8));
} else {
gs.code.push_back(BC_INST_FVAR1 | (id->get_index() << 8));
}
case ID_FVAR: {
auto *hid = gs.ts.pstate->get_ident("//fvar");
if (!hid || !hid->is_command()) {
throw error{gs.ts, "invalid fvar handler"};
}
compile_cmd(
gs, static_cast<command_impl *>(hid),
id, more, rettype
);
break;
case ID_SVAR:
more = compilearg(gs, VAL_STRING);
if (!more) {
gs.code.push_back(BC_INST_PRINT | (id->get_index() << 8));
} else {
std::uint32_t numargs = 0;
do {
++numargs;
more = compilearg(gs, VAL_ANY);
} while (more);
if (numargs > 1) {
gs.code.push_back(
BC_INST_CONC | BC_RET_STRING | (numargs << 8)
);
}
gs.code.push_back(BC_INST_SVAR1 | (id->get_index() << 8));
}
case ID_SVAR: {
auto *hid = gs.ts.pstate->get_ident("//svar");
if (!hid || !hid->is_command()) {
throw error{gs.ts, "invalid svar handler"};
}
compile_cmd(
gs, static_cast<command_impl *>(hid),
id, more, rettype
);
break;
}
}
}
}

View File

@ -305,14 +305,6 @@ LIBCUBESCRIPT_EXPORT string_var const *ident::get_svar() const {
return static_cast<string_var const *>(this);
}
LIBCUBESCRIPT_EXPORT integer_type integer_var::get_val_min() const {
return static_cast<ivar_impl const *>(this)->p_minval;
}
LIBCUBESCRIPT_EXPORT integer_type integer_var::get_val_max() const {
return static_cast<ivar_impl const *>(this)->p_maxval;
}
LIBCUBESCRIPT_EXPORT integer_type integer_var::get_value() const {
return static_cast<ivar_impl const *>(this)->p_storage;
}
@ -321,14 +313,6 @@ LIBCUBESCRIPT_EXPORT void integer_var::set_value(integer_type val) {
static_cast<ivar_impl *>(this)->p_storage = val;
}
LIBCUBESCRIPT_EXPORT float_type float_var::get_val_min() const {
return static_cast<fvar_impl const *>(this)->p_minval;
}
LIBCUBESCRIPT_EXPORT float_type float_var::get_val_max() const {
return static_cast<fvar_impl const *>(this)->p_maxval;
}
LIBCUBESCRIPT_EXPORT float_type float_var::get_value() const {
return static_cast<fvar_impl const *>(this)->p_storage;
}

View File

@ -14,7 +14,6 @@ internal_state::internal_state(alloc_func af, void *data):
allocf{af}, aptr{data},
idents{allocator_type{this}},
identmap{allocator_type{this}},
varprintf{},
strman{create<string_pool>(this)},
empty{bcode_init_empty(this)}
{}
@ -220,24 +219,6 @@ LIBCUBESCRIPT_EXPORT hook_func &state::get_call_hook() {
return p_tstate->get_hook();
}
LIBCUBESCRIPT_EXPORT var_print_func state::set_var_printer(
var_print_func func
) {
auto fn = std::move(p_tstate->istate->varprintf);
p_tstate->istate->varprintf = std::move(func);
return fn;
}
LIBCUBESCRIPT_EXPORT var_print_func const &state::get_var_printer() const {
return p_tstate->istate->varprintf;
}
LIBCUBESCRIPT_EXPORT void state::print_var(global_var const &v) const {
if (p_tstate->istate->varprintf) {
p_tstate->istate->varprintf(*this, v);
}
}
LIBCUBESCRIPT_EXPORT void *state::alloc(void *ptr, size_t os, size_t ns) {
return p_tstate->istate->alloc(ptr, os, ns);
}
@ -340,17 +321,6 @@ LIBCUBESCRIPT_EXPORT string_var *state::new_svar(
return sv;
}
LIBCUBESCRIPT_EXPORT void state::reset_var(std::string_view name) {
ident *id = get_ident(name);
if (!id) {
throw error{*this, "variable %s does not exist", name.data()};
}
if (id->get_flags() & IDENT_FLAG_READONLY) {
throw error{*this, "variable %s is read only", name.data()};
}
clear_override(*id);
}
LIBCUBESCRIPT_EXPORT void state::touch_var(std::string_view name) {
ident *id = get_ident(name);
if (id && id->is_var()) {
@ -375,14 +345,12 @@ LIBCUBESCRIPT_EXPORT void state::set_alias(
return;
}
case ident_type::IVAR:
set_var_int_checked(static_cast<integer_var *>(id), v.get_int());
break;
case ident_type::FVAR:
set_var_float_checked(static_cast<float_var *>(id), v.get_float());
break;
case ident_type::SVAR:
set_var_str_checked(static_cast<string_var *>(id), v.get_str());
case ident_type::SVAR: {
any_value ret{*this};
run(id, std::span<any_value>{&v, 1}, ret);
break;
}
default:
throw error{
*this, "cannot redefine builtin %s with an alias",
@ -454,194 +422,6 @@ LIBCUBESCRIPT_EXPORT command *state::new_command(
return cmd;
}
LIBCUBESCRIPT_EXPORT void state::clear_override(ident &id) {
if (!(id.get_flags() & IDENT_FLAG_OVERRIDDEN)) {
return;
}
switch (id.get_type()) {
case ident_type::ALIAS: {
auto &ast = p_tstate->get_astack(static_cast<alias *>(&id));
ast.node->val_s.set_str("");
ast.node->code = bcode_ref{};
break;
}
case ident_type::IVAR: {
ivar_impl &iv = static_cast<ivar_impl &>(id);
iv.set_value(iv.p_overrideval);
iv.changed(*this);
break;
}
case ident_type::FVAR: {
fvar_impl &fv = static_cast<fvar_impl &>(id);
fv.set_value(fv.p_overrideval);
fv.changed(*this);
break;
}
case ident_type::SVAR: {
svar_impl &sv = static_cast<svar_impl &>(id);
sv.set_value(sv.p_overrideval);
sv.changed(*this);
break;
}
default:
break;
}
id.p_impl->p_flags &= ~IDENT_FLAG_OVERRIDDEN;
}
LIBCUBESCRIPT_EXPORT void state::clear_overrides() {
for (auto &p: p_tstate->istate->idents) {
clear_override(*(p.second));
}
}
template<typename SF>
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{
cs, "cannot override persistent variable '%s'",
v->get_name().data()
};
}
if (!(vflags & IDENT_FLAG_OVERRIDDEN)) {
sf();
vflags |= IDENT_FLAG_OVERRIDDEN;
}
} else {
if (vflags & IDENT_FLAG_OVERRIDDEN) {
vflags &= ~IDENT_FLAG_OVERRIDDEN;
}
}
}
LIBCUBESCRIPT_EXPORT void state::set_var_int(
std::string_view name, integer_type v, bool dofunc, bool doclamp
) {
ident *id = get_ident(name);
if (!id || id->is_ivar()) {
return;
}
ivar_impl *iv = static_cast<ivar_impl *>(id);
override_var(
*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);
}
}
LIBCUBESCRIPT_EXPORT void state::set_var_float(
std::string_view name, float_type v, bool dofunc, bool doclamp
) {
ident *id = get_ident(name);
if (!id || id->is_fvar()) {
return;
}
fvar_impl *fv = static_cast<fvar_impl *>(id);
override_var(
*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);
}
}
LIBCUBESCRIPT_EXPORT void state::set_var_str(
std::string_view name, std::string_view v, bool dofunc
) {
ident *id = get_ident(name);
if (!id || id->is_svar()) {
return;
}
svar_impl *sv = static_cast<svar_impl *>(id);
override_var(
*this, sv, sv->p_flags,
[&sv]() { sv->p_overrideval = sv->get_value(); }
);
sv->set_value(string_ref{p_tstate->istate, v});
if (dofunc) {
sv->changed(*this);
}
}
LIBCUBESCRIPT_EXPORT std::optional<integer_type>
state::get_var_int(std::string_view name) {
ident *id = get_ident(name);
if (!id || id->is_ivar()) {
return std::nullopt;
}
return static_cast<integer_var *>(id)->get_value();
}
LIBCUBESCRIPT_EXPORT std::optional<float_type>
state::get_var_float(std::string_view name) {
ident *id = get_ident(name);
if (!id || id->is_fvar()) {
return std::nullopt;
}
return static_cast<float_var *>(id)->get_value();
}
LIBCUBESCRIPT_EXPORT std::optional<string_ref>
state::get_var_str(std::string_view name) {
ident *id = get_ident(name);
if (!id || id->is_svar()) {
return std::nullopt;
}
return string_ref{
p_tstate->istate, static_cast<string_var *>(id)->get_value()
};
}
LIBCUBESCRIPT_EXPORT std::optional<integer_type>
state::get_var_min_int(std::string_view name) {
ident *id = get_ident(name);
if (!id || id->is_ivar()) {
return std::nullopt;
}
return static_cast<integer_var *>(id)->get_val_min();
}
LIBCUBESCRIPT_EXPORT std::optional<integer_type>
state::get_var_max_int(std::string_view name) {
ident *id = get_ident(name);
if (!id || id->is_ivar()) {
return std::nullopt;
}
return static_cast<integer_var *>(id)->get_val_max();
}
LIBCUBESCRIPT_EXPORT std::optional<float_type>
state::get_var_min_float(std::string_view name) {
ident *id = get_ident(name);
if (!id || id->is_fvar()) {
return std::nullopt;
}
return static_cast<float_var *>(id)->get_val_min();
}
LIBCUBESCRIPT_EXPORT std::optional<float_type>
state::get_var_max_float(std::string_view name) {
ident *id = get_ident(name);
if (!id || id->is_fvar()) {
return std::nullopt;
}
return static_cast<float_var *>(id)->get_val_max();
}
LIBCUBESCRIPT_EXPORT std::optional<string_ref>
state::get_alias_val(std::string_view name) {
alias *a = get_alias(name);
@ -654,114 +434,6 @@ state::get_alias_val(std::string_view name) {
return p_tstate->get_astack(a).node->val_s.get_str();
}
integer_type clamp_var(state &cs, integer_var *iv, integer_type v) {
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;
}
throw error{
cs,
(iv->get_flags() & IDENT_FLAG_HEX)
? (
(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()
};
}
LIBCUBESCRIPT_EXPORT void state::set_var_int_checked(
integer_var *iv, integer_type v
) {
if (iv->get_flags() & IDENT_FLAG_READONLY) {
throw error{
*this, "variable '%s' is read only", iv->get_name().data()
};
}
ivar_impl *ivp = static_cast<ivar_impl *>(iv);
override_var(
*this, iv, ivp->p_flags,
[&ivp]() { ivp->p_overrideval = ivp->p_storage; }
);
if ((v < iv->get_val_min()) || (v > iv->get_val_max())) {
v = clamp_var(*this, iv, v);
}
iv->set_value(v);
ivp->changed(*this);
}
LIBCUBESCRIPT_EXPORT void state::set_var_int_checked(
integer_var *iv, std::span<any_value> args
) {
integer_type v = args[0].force_int();
if ((iv->get_flags() & IDENT_FLAG_HEX) && (args.size() > 1)) {
v = (v << 16) | (args[1].force_int() << 8);
if (args.size() > 2) {
v |= args[2].force_int();
}
}
set_var_int_checked(iv, v);
}
float_type clamp_fvar(state &cs, float_var *fv, float_type v) {
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;
}
any_value vmin{cs}, vmax{cs};
vmin.set_float(fv->get_val_min());
vmax.set_float(fv->get_val_max());
throw error{
cs, "valid range for '%s' is %s..%s", fv->get_name().data(),
vmin.force_str(), vmax.force_str()
};
}
LIBCUBESCRIPT_EXPORT void state::set_var_float_checked(
float_var *fv, float_type v
) {
if (fv->get_flags() & IDENT_FLAG_READONLY) {
throw error{
*this, "variable '%s' is read only", fv->get_name().data()
};
}
fvar_impl *fvp = static_cast<fvar_impl *>(fv);
override_var(
*this, fv, fvp->p_flags,
[&fvp]() { fvp->p_overrideval = fvp->p_storage; }
);
if ((v < fv->get_val_min()) || (v > fv->get_val_max())) {
v = clamp_fvar(*this, fv, v);
}
fv->set_value(v);
fvp->changed(*this);
}
LIBCUBESCRIPT_EXPORT void state::set_var_str_checked(
string_var *sv, std::string_view v
) {
if (sv->get_flags() & IDENT_FLAG_READONLY) {
throw error{
*this, "variable '%s' is read only", sv->get_name().data()
};
}
svar_impl *svp = static_cast<svar_impl *>(sv);
override_var(
*this, sv, svp->p_flags,
[&svp]() { svp->p_overrideval = svp->p_storage; }
);
sv->set_value(string_ref{p_tstate->istate, v});
svp->changed(*this);
}
LIBCUBESCRIPT_EXPORT void state::init_libs(int libs) {
if (libs & LIB_MATH) {
init_lib_math(*this);
@ -829,41 +501,76 @@ LIBCUBESCRIPT_EXPORT void state::run(
targs[osz + i] = args[i];
}
exec_command(
*p_tstate, cimpl, &targs[osz], ret, nargs, false
*p_tstate, cimpl, id, &targs[osz], ret, nargs, false
);
} else {
exec_command(
*p_tstate, cimpl, &args[0], ret, nargs, false
*p_tstate, cimpl, id, &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);
case ident_type::IVAR: {
auto *hid = get_ident("//ivar");
if (!hid || !hid->is_command()) {
throw error{*p_tstate, "invalid ivar handler"};
}
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()
);
auto *cimp = static_cast<command_impl *>(hid);
auto &targs = p_tstate->vmstack;
auto osz = targs.size();
auto anargs = std::size_t(cimp->get_num_args());
targs.resize(
osz + std::max(args.size(), anargs + 1), any_value{*this}
);
for (std::size_t i = 0; i < nargs; ++i) {
targs[osz + i + 1] = args[i];
}
exec_command(
*p_tstate, cimp, id, &targs[osz], ret, nargs + 1, false
);
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()
);
}
case ident_type::FVAR: {
auto *hid = get_ident("//fvar");
if (!hid || !hid->is_command()) {
throw error{*p_tstate, "invalid fvar handler"};
}
auto *cimp = static_cast<command_impl *>(hid);
auto &targs = p_tstate->vmstack;
auto osz = targs.size();
auto anargs = std::size_t(cimp->get_num_args());
targs.resize(
osz + std::max(args.size(), anargs + 1), any_value{*this}
);
for (std::size_t i = 0; i < nargs; ++i) {
targs[osz + i + 1] = args[i];
}
exec_command(
*p_tstate, cimp, id, &targs[osz], ret, nargs + 1, false
);
break;
}
case ident_type::SVAR: {
auto *hid = get_ident("//svar");
if (!hid || !hid->is_command()) {
throw error{*p_tstate, "invalid svar handler"};
}
auto *cimp = static_cast<command_impl *>(hid);
auto &targs = p_tstate->vmstack;
auto osz = targs.size();
auto anargs = std::size_t(cimp->get_num_args());
targs.resize(
osz + std::max(args.size(), anargs + 1), any_value{*this}
);
for (std::size_t i = 0; i < nargs; ++i) {
targs[osz + i + 1] = args[i];
}
exec_command(
*p_tstate, cimp, id, &targs[osz], ret, nargs + 1, false
);
break;
}
case ident_type::ALIAS: {
alias *a = static_cast<alias *>(id);
if (

View File

@ -48,7 +48,6 @@ struct internal_state {
> idents;
std::vector<ident *, std_allocator<ident *>> identmap;
var_print_func varprintf;
string_pool *strman;
empty_block *empty;

View File

@ -37,6 +37,10 @@ struct valbuf {
buf.insert(buf.end(), beg, end);
}
void insert(std::size_t i, T const &it) {
buf.insert(buf.begin() + i, it);
}
template<typename ...A>
reference emplace_back(A &&...args) {
return buf.emplace_back(std::forward<A>(args)...);

View File

@ -44,8 +44,8 @@ static inline void force_arg(any_value &v, int type) {
}
void exec_command(
thread_state &ts, command_impl *id, any_value *args, any_value &res,
std::size_t nargs, bool lookup
thread_state &ts, command_impl *id, ident *self, any_value *args,
any_value &res, std::size_t nargs, bool lookup
) {
int i = -1, fakeargs = 0, numargs = int(nargs);
bool rep = false;
@ -159,7 +159,7 @@ void exec_command(
break;
case '$':
i += 1;
args[i].set_ident(id);
args[i].set_ident(self);
break;
case 'N':
i += 1;
@ -337,7 +337,7 @@ static inline int get_lookupu_type(
/* pad with as many empty values as we need */
args.resize(osz + cimpl->get_num_args(), any_value{*ts.pstate});
arg.set_none();
exec_command(ts, cimpl, &args[osz], arg, 0, true);
exec_command(ts, cimpl, cimpl, &args[osz], arg, 0, true);
force_arg(arg, op & BC_INST_RET_MASK);
return -2; /* ignore */
}
@ -530,10 +530,6 @@ std::uint32_t *vm_exec(
);
continue;
case BC_INST_PRINT:
cs.print_var(*static_cast<global_var *>(ts.istate->identmap[op >> 8]));
continue;
case BC_INST_LOCAL: {
std::size_t numlocals = op >> 8;
std::size_t offset = args.size() - numlocals;
@ -981,15 +977,6 @@ std::uint32_t *vm_exec(
)->get_value()
));
continue;
case BC_INST_SVAR1: {
auto v = std::move(args.back());
args.pop_back();
cs.set_var_str_checked(
static_cast<string_var *>(ts.istate->identmap[op >> 8]),
v.get_str()
);
continue;
}
case BC_INST_IVAR | BC_RET_INT:
case BC_INST_IVAR | BC_RET_NULL:
@ -1012,34 +999,6 @@ std::uint32_t *vm_exec(
)->get_value()
));
continue;
case BC_INST_IVAR1: {
auto v = std::move(args.back());
args.pop_back();
cs.set_var_int_checked(
static_cast<integer_var *>(ts.istate->identmap[op >> 8]),
v.get_int()
);
continue;
}
case BC_INST_IVAR2: {
auto v1 = std::move(args.back()); args.pop_back();
auto v2 = std::move(args.back()); args.pop_back();
cs.set_var_int_checked(
static_cast<integer_var *>(ts.istate->identmap[op >> 8]),
(v2.get_int() << 16) | (v1.get_int() << 8)
);
continue;
}
case BC_INST_IVAR3: {
auto v1 = std::move(args.back()); args.pop_back();
auto v2 = std::move(args.back()); args.pop_back();
auto v3 = std::move(args.back()); args.pop_back();
cs.set_var_int_checked(
static_cast<integer_var *>(ts.istate->identmap[op >> 8]),
(v3.get_int() << 16) | (v2.get_int() << 8) | (v1.get_int())
);
continue;
}
case BC_INST_FVAR | BC_RET_FLOAT:
case BC_INST_FVAR | BC_RET_NULL:
@ -1060,15 +1019,6 @@ std::uint32_t *vm_exec(
ts.istate->identmap[op >> 8]
)->get_value()));
continue;
case BC_INST_FVAR1: {
auto &v = args.back();
cs.set_var_float_checked(
static_cast<float_var *>(ts.istate->identmap[op >> 8]),
v.get_float()
);
args.pop_back();
continue;
}
case BC_INST_ALIAS: {
auto *a = static_cast<alias *>(
@ -1159,14 +1109,18 @@ noid:
continue;
}
/* fallthrough */
case ID_COMMAND:
case ID_COMMAND: {
auto *cimp = static_cast<command_impl *>(id);
args.resize(offset + std::max(
std::size_t(cimp->get_num_args()), callargs
), any_value{cs});
exec_command(
ts, static_cast<command_impl *>(id),
&args[offset], result, callargs
ts, cimp, cimp, &args[offset], result, callargs
);
force_arg(result, op & BC_INST_RET_MASK);
args.resize(offset - 1, any_value{cs});
continue;
}
case ID_LOCAL: {
std::size_t idstsz = ts.idstack.size();
for (size_t j = 0; j < size_t(callargs); ++j) {
@ -1190,42 +1144,60 @@ noid:
cleanup();
return code;
}
case ID_IVAR:
if (callargs <= 0) {
cs.print_var(*static_cast<global_var *>(id));
} else {
cs.set_var_int_checked(
static_cast<integer_var *>(id),
std::span{&args[offset], std::size_t(callargs)}
);
case ID_IVAR: {
auto *hid = cs.get_ident("//ivar");
if (!hid || !hid->is_command()) {
throw error{ts, "invalid ivar handler"};
}
args.resize(offset - 1, any_value{cs});
auto *cimp = static_cast<command_impl *>(hid);
/* the $ argument */
args.insert(offset, any_value{cs});
args.resize(offset + std::max(
std::size_t(cimp->get_num_args()), callargs
), any_value{cs});
exec_command(
ts, cimp, id, &args[offset], result, callargs
);
force_arg(result, op & BC_INST_RET_MASK);
args.resize(offset - 1, any_value{cs});
continue;
case ID_FVAR:
if (callargs <= 0) {
cs.print_var(*static_cast<global_var *>(id));
} else {
cs.set_var_float_checked(
static_cast<float_var *>(id),
args[offset].force_float()
);
}
case ID_FVAR: {
auto *hid = cs.get_ident("//fvar");
if (!hid || !hid->is_command()) {
throw error{ts, "invalid fvar handler"};
}
args.resize(offset - 1, any_value{cs});
auto *cimp = static_cast<command_impl *>(hid);
/* the $ argument */
args.insert(offset, any_value{cs});
args.resize(offset + std::max(
std::size_t(cimp->get_num_args()), callargs
), any_value{cs});
exec_command(
ts, cimp, id, &args[offset], result, callargs
);
force_arg(result, op & BC_INST_RET_MASK);
args.resize(offset - 1, any_value{cs});
continue;
case ID_SVAR:
if (callargs <= 0) {
cs.print_var(*static_cast<global_var *>(id));
} else {
cs.set_var_str_checked(
static_cast<string_var *>(id),
args[offset].force_str()
);
}
case ID_SVAR: {
auto *hid = cs.get_ident("//svar");
if (!hid || !hid->is_command()) {
throw error{ts, "invalid svar handler"};
}
args.resize(offset - 1, any_value{cs});
auto *cimp = static_cast<command_impl *>(hid);
/* the $ argument */
args.insert(offset, any_value{cs});
args.resize(offset + std::max(
std::size_t(cimp->get_num_args()), callargs
), any_value{cs});
exec_command(
ts, cimp, id, &args[offset], result, callargs
);
force_arg(result, op & BC_INST_RET_MASK);
args.resize(offset - 1, any_value{cs});
continue;
}
case ID_ALIAS: {
alias *a = static_cast<alias *>(id);
if (

View File

@ -95,8 +95,8 @@ static void call_with_args(thread_state &ts, F body) {
}
void exec_command(
thread_state &ts, command_impl *id, any_value *args, any_value &res,
std::size_t nargs, bool lookup = false
thread_state &ts, command_impl *id, ident *self, any_value *args,
any_value &res, std::size_t nargs, bool lookup = false
);
bool exec_alias(

View File

@ -312,27 +312,10 @@ end:
}
});
gcs.new_command("resetvar", "s", [](auto &cs, auto args, auto &) {
cs.reset_var(args[0].get_str());
});
gcs.new_command("alias", "st", [](auto &cs, auto args, auto &) {
cs.set_alias(args[0].get_str(), args[1]);
});
gcs.new_command("getvarmin", "s", [](auto &cs, auto args, auto &res) {
res.set_int(cs.get_var_min_int(args[0].get_str()).value_or(0));
});
gcs.new_command("getvarmax", "s", [](auto &cs, auto args, auto &res) {
res.set_int(cs.get_var_max_int(args[0].get_str()).value_or(0));
});
gcs.new_command("getfvarmin", "s", [](auto &cs, auto args, auto &res) {
res.set_float(cs.get_var_min_float(args[0].get_str()).value_or(0.0f));
});
gcs.new_command("getfvarmax", "s", [](auto &cs, auto args, auto &res) {
res.set_float(cs.get_var_max_float(args[0].get_str()).value_or(0.0f));
});
gcs.new_command("identexists", "s", [](auto &cs, auto args, auto &res) {
res.set_int(cs.have_ident(args[0].get_str()));
});

View File

@ -199,50 +199,6 @@ static void do_sigint(int n) {
});
}
/* an example of what var printer would look like in real usage */
static void repl_print_var(cs::state const &, cs::global_var const &var) {
switch (var.get_type()) {
case cs::ident_type::IVAR: {
auto &iv = static_cast<cs::integer_var const &>(var);
auto val = iv.get_value();
if (!(iv.get_flags() & cs::IDENT_FLAG_HEX) || (val < 0)) {
std::printf("%s = %d\n", iv.get_name().data(), val);
} else if (iv.get_val_max() == 0xFFFFFF) {
std::printf(
"%s = 0x%.6X (%d, %d, %d)\n",
iv.get_name().data(),
val, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF
);
} else {
std::printf("%s = 0x%X\n", iv.get_name().data(), val);
}
break;
}
case cs::ident_type::FVAR: {
auto &fv = static_cast<cs::float_var const &>(var);
auto val = fv.get_value();
if (std::floor(val) == val) {
std::printf("%s = %.1f", fv.get_name().data(), val);
} else {
std::printf("%s = %.7g", fv.get_name().data(), val);
}
break;
}
case cs::ident_type::SVAR: {
auto &sv = static_cast<cs::string_var const &>(var);
auto val = std::string_view{sv.get_value()};
if (val.find('"') == val.npos) {
std::printf("%s = \"%s\"", sv.get_name().data(), val.data());
} else {
std::printf("%s = [%s]", sv.get_name().data(), val.data());
}
break;
}
default:
break;
}
}
static bool do_run_file(
cs::state &cs, std::string_view fname, cs::any_value &ret
) {
@ -362,9 +318,66 @@ static void do_tty(cs::state &cs) {
int main(int argc, char **argv) {
cs::state gcs;
gcs.set_var_printer(repl_print_var);
gcs.init_libs();
gcs.new_command("//ivar", "$iiiN", [](auto &, auto args, auto &) {
auto *iv = args[0].get_ident()->get_ivar();
auto nargs = args[4].get_int();
if (nargs <= 1) {
auto val = iv->get_value();
if ((val >= 0) && (val < 0xFFFFFF)) {
std::printf(
"%s = %d (0x%.6X: %d, %d, %d)\n",
iv->get_name().data(), val, val,
(val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF
);
} else {
std::printf("%s = %d\n", iv->get_name().data(), val);
}
} else if (nargs == 2) {
iv->set_value(args[1].get_int());
} else if (nargs == 3) {
iv->set_value(
(args[1].get_int() << 8) | (args[2].get_int() << 16)
);
} else {
iv->set_value(
args[1].get_int() | (args[2].get_int() << 8) |
(args[3].get_int() << 16)
);
}
});
gcs.new_command("//fvar", "$fN", [](auto &, auto args, auto &) {
auto *fv = args[0].get_ident()->get_fvar();
auto nargs = args[2].get_int();
if (nargs <= 1) {
auto val = fv->get_value();
if (std::floor(val) == val) {
std::printf("%s = %.1f\n", fv->get_name().data(), val);
} else {
std::printf("%s = %.7g\n", fv->get_name().data(), val);
}
} else {
fv->set_value(args[1].get_float());
}
});
gcs.new_command("//svar", "$sN", [](auto &, auto args, auto &) {
auto sv = args[0].get_ident()->get_svar();
auto nargs = args[2].get_int();
if (nargs <= 1) {
auto val = std::string_view{sv->get_value()};
if (val.find('"') == val.npos) {
std::printf("%s = \"%s\"\n", sv->get_name().data(), val.data());
} else {
std::printf("%s = [%s]\n", sv->get_name().data(), val.data());
}
} else {
sv->set_value(args[1].get_str());
}
});
gcs.new_command("exec", "s", [](auto &css, auto args, auto &) {
auto file = args[0].get_str();
cs::any_value val{css};