rework API for by-name lookups/assignments, simplify VM for that
parent
acdb9a4a0e
commit
a9afa89af6
|
@ -230,22 +230,61 @@ struct LIBCUBESCRIPT_EXPORT state {
|
||||||
std::string_view name
|
std::string_view name
|
||||||
);
|
);
|
||||||
|
|
||||||
/** @brief Reset a variable or alias
|
/** @brief Assign a value to a name
|
||||||
|
*
|
||||||
|
* This will set something of the given name to the given value. The
|
||||||
|
* something may be a variable or an alias.
|
||||||
|
*
|
||||||
|
* If no ident of such name exists, a new alias will be created and
|
||||||
|
* set.
|
||||||
|
*
|
||||||
|
* @throw cubescript::error if `name` is a builtin ident (a registered
|
||||||
|
* command or similar) or if it is invalid
|
||||||
|
*
|
||||||
|
* @see lookup_value()
|
||||||
|
* @see reset_value()
|
||||||
|
* @see touch_value()
|
||||||
|
*/
|
||||||
|
void assign_value(std::string_view name, any_value v);
|
||||||
|
|
||||||
|
/** @brief Lookup a value by name
|
||||||
|
*
|
||||||
|
* This will lookup an ident of the given name and return its value.
|
||||||
|
* The ident may be a variable or an alias.
|
||||||
|
*
|
||||||
|
* @throw cubescript::error if `name` does not exist or belongs to an
|
||||||
|
* ident that doesn't support lookups
|
||||||
|
*
|
||||||
|
* @see assign_value()
|
||||||
|
* @see reset_value()
|
||||||
|
* @see touch_value()
|
||||||
|
*/
|
||||||
|
any_value lookup_value(std::string_view name);
|
||||||
|
|
||||||
|
/** @brief Reset a value by name
|
||||||
*
|
*
|
||||||
* This is like clear_override() except it works by name and performs
|
* This is like clear_override() except it works by name and performs
|
||||||
* extra checks.
|
* extra checks.
|
||||||
*
|
*
|
||||||
* @throw cubescript::error if non-existent or read only
|
* @throw cubescript::error if non-existent or read only
|
||||||
|
*
|
||||||
|
* @see assign_value()
|
||||||
|
* @see lookup_value()
|
||||||
|
* @see touch_value()
|
||||||
*/
|
*/
|
||||||
void reset_var(std::string_view name);
|
void reset_value(std::string_view name);
|
||||||
|
|
||||||
/** @brief Touch a variable
|
/** @brief Touch a value by name
|
||||||
*
|
*
|
||||||
* If an ident with the given name exists and is a global variable,
|
* If an ident with the given name exists and is a global variable,
|
||||||
* a changed hook will be triggered with it, acting like if a new
|
* a changed hook will be triggered with it, acting like if a new
|
||||||
* value was set, but without actually setting it.
|
* value was set, but without actually setting it.
|
||||||
|
*
|
||||||
|
* @see assign_value()
|
||||||
|
* @see lookup_value()
|
||||||
|
* @see reset_value()
|
||||||
*/
|
*/
|
||||||
void touch_var(std::string_view name);
|
void touch_value(std::string_view name);
|
||||||
|
|
||||||
/** @brief Register a command
|
/** @brief Register a command
|
||||||
*
|
*
|
||||||
|
@ -449,19 +488,6 @@ struct LIBCUBESCRIPT_EXPORT state {
|
||||||
*/
|
*/
|
||||||
std::size_t set_max_run_depth(std::size_t v);
|
std::size_t set_max_run_depth(std::size_t v);
|
||||||
|
|
||||||
/** @brief Set a variable
|
|
||||||
*
|
|
||||||
* This will set something of the given name to the given value. The
|
|
||||||
* something may be a variable or an alias.
|
|
||||||
*
|
|
||||||
* If no ident of such name exists, a new alias will be created and
|
|
||||||
* set.
|
|
||||||
*
|
|
||||||
* @throw cubescript::error if `name` is a builtin ident (a registered
|
|
||||||
* command or similar) or if it is invalid
|
|
||||||
*/
|
|
||||||
void set_alias(std::string_view name, any_value v);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct state_p;
|
friend struct state_p;
|
||||||
|
|
||||||
|
|
101
src/cs_state.cc
101
src/cs_state.cc
|
@ -472,27 +472,7 @@ LIBCUBESCRIPT_EXPORT ident &state::new_ident(std::string_view n) {
|
||||||
return p_tstate->istate->new_ident(*this, n, IDENT_FLAG_UNKNOWN);
|
return p_tstate->istate->new_ident(*this, n, IDENT_FLAG_UNKNOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
LIBCUBESCRIPT_EXPORT void state::reset_var(std::string_view name) {
|
LIBCUBESCRIPT_EXPORT void state::assign_value(
|
||||||
auto id = get_ident(name);
|
|
||||||
if (!id) {
|
|
||||||
throw error{*this, "variable '%s' does not exist", name.data()};
|
|
||||||
}
|
|
||||||
if (id->get().is_var()) {
|
|
||||||
if (static_cast<global_var &>(id->get()).is_read_only()) {
|
|
||||||
throw error{*this, "variable '%s' is read only", name.data()};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clear_override(id->get());
|
|
||||||
}
|
|
||||||
|
|
||||||
LIBCUBESCRIPT_EXPORT void state::touch_var(std::string_view name) {
|
|
||||||
auto id = get_ident(name);
|
|
||||||
if (id && id->get().is_var()) {
|
|
||||||
var_changed(*p_tstate, &id->get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LIBCUBESCRIPT_EXPORT void state::set_alias(
|
|
||||||
std::string_view name, any_value v
|
std::string_view name, any_value v
|
||||||
) {
|
) {
|
||||||
auto id = get_ident(name);
|
auto id = get_ident(name);
|
||||||
|
@ -524,6 +504,85 @@ LIBCUBESCRIPT_EXPORT void state::set_alias(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LIBCUBESCRIPT_EXPORT any_value state::lookup_value(std::string_view name) {
|
||||||
|
ident *id = nullptr;
|
||||||
|
auto idopt = get_ident(name);
|
||||||
|
if (!idopt) {
|
||||||
|
id = nullptr;
|
||||||
|
} else {
|
||||||
|
id = &idopt->get();
|
||||||
|
}
|
||||||
|
alias_stack *ast;
|
||||||
|
if (id) {
|
||||||
|
switch(id->get_type()) {
|
||||||
|
case ident_type::ALIAS: {
|
||||||
|
auto *a = static_cast<alias_impl *>(id);
|
||||||
|
ast = &p_tstate->get_astack(static_cast<alias *>(id));
|
||||||
|
if (ast->flags & IDENT_FLAG_UNKNOWN) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (a->is_arg() && !ident_is_used_arg(id, *p_tstate)) {
|
||||||
|
return any_value{};
|
||||||
|
}
|
||||||
|
return ast->node->val_s.get_plain();
|
||||||
|
}
|
||||||
|
case ident_type::SVAR: {
|
||||||
|
any_value val{};
|
||||||
|
val.set_string(static_cast<string_var *>(id)->get_value());
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
case ident_type::IVAR: {
|
||||||
|
any_value val{};
|
||||||
|
val.set_integer(static_cast<integer_var *>(id)->get_value());
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
case ident_type::FVAR: {
|
||||||
|
any_value val{};
|
||||||
|
val.set_float(static_cast<float_var *>(id)->get_value());
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
case ident_type::COMMAND: {
|
||||||
|
any_value val{};
|
||||||
|
/* make sure value stack gets restored */
|
||||||
|
stack_guard s{*p_tstate};
|
||||||
|
auto *cimpl = static_cast<command_impl *>(id);
|
||||||
|
auto &args = p_tstate->vmstack;
|
||||||
|
auto osz = args.size();
|
||||||
|
/* pad with as many empty values as we need */
|
||||||
|
args.resize(osz + cimpl->get_num_args());
|
||||||
|
exec_command(
|
||||||
|
*p_tstate, cimpl, cimpl, &args[osz], val, 0, true
|
||||||
|
);
|
||||||
|
args.resize(osz);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return any_value{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw error{*this, "unknown alias lookup: %s", name.data()};
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBCUBESCRIPT_EXPORT void state::reset_value(std::string_view name) {
|
||||||
|
auto id = get_ident(name);
|
||||||
|
if (!id) {
|
||||||
|
throw error{*this, "variable '%s' does not exist", name.data()};
|
||||||
|
}
|
||||||
|
if (id->get().is_var()) {
|
||||||
|
if (static_cast<global_var &>(id->get()).is_read_only()) {
|
||||||
|
throw error{*this, "variable '%s' is read only", name.data()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clear_override(id->get());
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBCUBESCRIPT_EXPORT void state::touch_value(std::string_view name) {
|
||||||
|
auto id = get_ident(name);
|
||||||
|
if (id && id->get().is_var()) {
|
||||||
|
var_changed(*p_tstate, &id->get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static char const *allowed_builtins[] = {
|
static char const *allowed_builtins[] = {
|
||||||
"//ivar", "//fvar", "//svar", "//var_changed",
|
"//ivar", "//fvar", "//svar", "//var_changed",
|
||||||
"//ivar_builtin", "//fvar_builtin", "//svar_builtin",
|
"//ivar_builtin", "//fvar_builtin", "//svar_builtin",
|
||||||
|
|
188
src/cs_vm.cc
188
src/cs_vm.cc
|
@ -308,61 +308,6 @@ static inline alias *get_lookup_id(
|
||||||
return static_cast<alias *>(id);
|
return static_cast<alias *>(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int get_lookupu_type(
|
|
||||||
thread_state &ts, any_value &arg, ident *&id, std::uint32_t op,
|
|
||||||
alias_stack *&ast
|
|
||||||
) {
|
|
||||||
if (arg.get_type() != value_type::STRING) {
|
|
||||||
return -2; /* default case */
|
|
||||||
}
|
|
||||||
auto idopt = ts.pstate->get_ident(arg.get_string(*ts.pstate));
|
|
||||||
if (!idopt) {
|
|
||||||
id = nullptr;
|
|
||||||
} else {
|
|
||||||
id = &idopt->get();
|
|
||||||
}
|
|
||||||
if (id) {
|
|
||||||
switch(id->get_type()) {
|
|
||||||
case ident_type::ALIAS: {
|
|
||||||
auto *a = static_cast<alias_impl *>(id);
|
|
||||||
ast = &ts.get_astack(static_cast<alias *>(id));
|
|
||||||
if (ast->flags & IDENT_FLAG_UNKNOWN) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (a->is_arg() && !ident_is_used_arg(id, ts)) {
|
|
||||||
return ID_UNKNOWN;
|
|
||||||
}
|
|
||||||
return ID_ALIAS;
|
|
||||||
}
|
|
||||||
case ident_type::SVAR:
|
|
||||||
return ID_SVAR;
|
|
||||||
case ident_type::IVAR:
|
|
||||||
return ID_IVAR;
|
|
||||||
case ident_type::FVAR:
|
|
||||||
return ID_FVAR;
|
|
||||||
case ident_type::COMMAND: {
|
|
||||||
/* make sure value stack gets restored */
|
|
||||||
stack_guard s{ts};
|
|
||||||
auto *cimpl = static_cast<command_impl *>(id);
|
|
||||||
auto &args = ts.vmstack;
|
|
||||||
auto osz = args.size();
|
|
||||||
/* pad with as many empty values as we need */
|
|
||||||
args.resize(osz + cimpl->get_num_args());
|
|
||||||
arg.set_none();
|
|
||||||
exec_command(ts, cimpl, cimpl, &args[osz], arg, 0, true);
|
|
||||||
force_arg(*ts.pstate, arg, op & BC_INST_RET_MASK);
|
|
||||||
return -2; /* ignore */
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return ID_UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw error{
|
|
||||||
*ts.pstate, "unknown alias lookup: %s",
|
|
||||||
arg.get_string(*ts.pstate).data()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t *vm_exec(
|
std::uint32_t *vm_exec(
|
||||||
thread_state &ts, std::uint32_t *code, any_value &result
|
thread_state &ts, std::uint32_t *code, any_value &result
|
||||||
) {
|
) {
|
||||||
|
@ -759,39 +704,13 @@ std::uint32_t *vm_exec(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
case BC_INST_LOOKUP_U | BC_RET_STRING: {
|
case BC_INST_LOOKUP_U | BC_RET_STRING:
|
||||||
ident *id = nullptr;
|
case BC_INST_LOOKUP_U | BC_RET_INT:
|
||||||
alias_stack *ast;
|
case BC_INST_LOOKUP_U | BC_RET_FLOAT:
|
||||||
any_value &arg = args.back();
|
case BC_INST_LOOKUP_U | BC_RET_NULL:
|
||||||
switch (get_lookupu_type(ts, arg, id, op, ast)) {
|
args.back() = cs.lookup_value(args.back().get_string(cs));
|
||||||
case ID_ALIAS:
|
force_arg(cs, args.back(), op & BC_INST_RET_MASK);
|
||||||
arg = ast->node->val_s;
|
continue;
|
||||||
arg.force_string(cs);
|
|
||||||
continue;
|
|
||||||
case ID_SVAR:
|
|
||||||
arg.set_string(
|
|
||||||
static_cast<string_var *>(id)->get_value()
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
case ID_IVAR:
|
|
||||||
arg.set_integer(
|
|
||||||
static_cast<integer_var *>(id)->get_value()
|
|
||||||
);
|
|
||||||
arg.force_string(cs);
|
|
||||||
continue;
|
|
||||||
case ID_FVAR:
|
|
||||||
arg.set_float(
|
|
||||||
static_cast<float_var *>(id)->get_value()
|
|
||||||
);
|
|
||||||
arg.force_string(cs);
|
|
||||||
continue;
|
|
||||||
case ID_UNKNOWN:
|
|
||||||
arg.set_string("", cs);
|
|
||||||
continue;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case BC_INST_LOOKUP | BC_RET_STRING: {
|
case BC_INST_LOOKUP | BC_RET_STRING: {
|
||||||
alias_stack *ast;
|
alias_stack *ast;
|
||||||
|
@ -806,36 +725,6 @@ std::uint32_t *vm_exec(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
case BC_INST_LOOKUP_U | BC_RET_INT: {
|
|
||||||
ident *id = nullptr;
|
|
||||||
alias_stack *ast;
|
|
||||||
any_value &arg = args.back();
|
|
||||||
switch (get_lookupu_type(ts, arg, id, op, ast)) {
|
|
||||||
case ID_ALIAS:
|
|
||||||
arg.set_integer(ast->node->val_s.get_integer());
|
|
||||||
continue;
|
|
||||||
case ID_SVAR:
|
|
||||||
arg.set_integer(parse_int(
|
|
||||||
static_cast<string_var *>(id)->get_value()
|
|
||||||
));
|
|
||||||
continue;
|
|
||||||
case ID_IVAR:
|
|
||||||
arg.set_integer(
|
|
||||||
static_cast<integer_var *>(id)->get_value()
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
case ID_FVAR:
|
|
||||||
arg.set_integer(integer_type(
|
|
||||||
static_cast<float_var *>(id)->get_value()
|
|
||||||
));
|
|
||||||
continue;
|
|
||||||
case ID_UNKNOWN:
|
|
||||||
arg.set_integer(0);
|
|
||||||
continue;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case BC_INST_LOOKUP | BC_RET_INT: {
|
case BC_INST_LOOKUP | BC_RET_INT: {
|
||||||
alias_stack *ast;
|
alias_stack *ast;
|
||||||
alias *a = get_lookup_id(ts, op, ast);
|
alias *a = get_lookup_id(ts, op, ast);
|
||||||
|
@ -848,36 +737,7 @@ std::uint32_t *vm_exec(
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
case BC_INST_LOOKUP_U | BC_RET_FLOAT: {
|
|
||||||
ident *id = nullptr;
|
|
||||||
alias_stack *ast;
|
|
||||||
any_value &arg = args.back();
|
|
||||||
switch (get_lookupu_type(ts, arg, id, op, ast)) {
|
|
||||||
case ID_ALIAS:
|
|
||||||
arg.set_float(ast->node->val_s.get_float());
|
|
||||||
continue;
|
|
||||||
case ID_SVAR:
|
|
||||||
arg.set_float(parse_float(
|
|
||||||
static_cast<string_var *>(id)->get_value()
|
|
||||||
));
|
|
||||||
continue;
|
|
||||||
case ID_IVAR:
|
|
||||||
arg.set_float(float_type(
|
|
||||||
static_cast<integer_var *>(id)->get_value()
|
|
||||||
));
|
|
||||||
continue;
|
|
||||||
case ID_FVAR:
|
|
||||||
arg.set_float(
|
|
||||||
static_cast<float_var *>(id)->get_value()
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
case ID_UNKNOWN:
|
|
||||||
arg.set_float(float_type(0));
|
|
||||||
continue;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case BC_INST_LOOKUP | BC_RET_FLOAT: {
|
case BC_INST_LOOKUP | BC_RET_FLOAT: {
|
||||||
alias_stack *ast;
|
alias_stack *ast;
|
||||||
alias *a = get_lookup_id(ts, op, ast);
|
alias *a = get_lookup_id(ts, op, ast);
|
||||||
|
@ -890,36 +750,6 @@ std::uint32_t *vm_exec(
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
case BC_INST_LOOKUP_U | BC_RET_NULL: {
|
|
||||||
ident *id = nullptr;
|
|
||||||
alias_stack *ast;
|
|
||||||
any_value &arg = args.back();
|
|
||||||
switch (get_lookupu_type(ts, arg, id, op, ast)) {
|
|
||||||
case ID_ALIAS:
|
|
||||||
arg = ast->node->val_s.get_plain();
|
|
||||||
continue;
|
|
||||||
case ID_SVAR:
|
|
||||||
arg.set_string(
|
|
||||||
static_cast<string_var *>(id)->get_value()
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
case ID_IVAR:
|
|
||||||
arg.set_integer(
|
|
||||||
static_cast<integer_var *>(id)->get_value()
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
case ID_FVAR:
|
|
||||||
arg.set_float(
|
|
||||||
static_cast<float_var *>(id)->get_value()
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
case ID_UNKNOWN:
|
|
||||||
arg.set_none();
|
|
||||||
continue;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case BC_INST_LOOKUP | BC_RET_NULL: {
|
case BC_INST_LOOKUP | BC_RET_NULL: {
|
||||||
alias_stack *ast;
|
alias_stack *ast;
|
||||||
alias *a = get_lookup_id(ts, op, ast);
|
alias *a = get_lookup_id(ts, op, ast);
|
||||||
|
@ -1032,7 +862,7 @@ std::uint32_t *vm_exec(
|
||||||
case BC_INST_ALIAS_U: {
|
case BC_INST_ALIAS_U: {
|
||||||
auto v = std::move(args.back());
|
auto v = std::move(args.back());
|
||||||
args.pop_back();
|
args.pop_back();
|
||||||
cs.set_alias(args.back().get_string(cs), std::move(v));
|
cs.assign_value(args.back().get_string(cs), std::move(v));
|
||||||
args.pop_back();
|
args.pop_back();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -329,11 +329,11 @@ end:
|
||||||
});
|
});
|
||||||
|
|
||||||
new_cmd_quiet(gcs, "resetvar", "s", [](auto &cs, auto args, auto &) {
|
new_cmd_quiet(gcs, "resetvar", "s", [](auto &cs, auto args, auto &) {
|
||||||
cs.reset_var(args[0].get_string(cs));
|
cs.reset_value(args[0].get_string(cs));
|
||||||
});
|
});
|
||||||
|
|
||||||
new_cmd_quiet(gcs, "alias", "st", [](auto &cs, auto args, auto &) {
|
new_cmd_quiet(gcs, "alias", "st", [](auto &cs, auto args, auto &) {
|
||||||
cs.set_alias(args[0].get_string(cs), args[1]);
|
cs.assign_value(args[0].get_string(cs), args[1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
new_cmd_quiet(gcs, "identexists", "s", [](auto &cs, auto args, auto &res) {
|
new_cmd_quiet(gcs, "identexists", "s", [](auto &cs, auto args, auto &res) {
|
||||||
|
|
Loading…
Reference in New Issue