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
|
||||
);
|
||||
|
||||
/** @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
|
||||
* extra checks.
|
||||
*
|
||||
* @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,
|
||||
* a changed hook will be triggered with it, acting like if a new
|
||||
* 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
|
||||
*
|
||||
|
@ -449,19 +488,6 @@ struct LIBCUBESCRIPT_EXPORT state {
|
|||
*/
|
||||
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:
|
||||
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);
|
||||
}
|
||||
|
||||
LIBCUBESCRIPT_EXPORT void state::reset_var(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_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(
|
||||
LIBCUBESCRIPT_EXPORT void state::assign_value(
|
||||
std::string_view name, any_value v
|
||||
) {
|
||||
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[] = {
|
||||
"//ivar", "//fvar", "//svar", "//var_changed",
|
||||
"//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);
|
||||
}
|
||||
|
||||
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(
|
||||
thread_state &ts, std::uint32_t *code, any_value &result
|
||||
) {
|
||||
|
@ -759,39 +704,13 @@ std::uint32_t *vm_exec(
|
|||
continue;
|
||||
}
|
||||
|
||||
case BC_INST_LOOKUP_U | BC_RET_STRING: {
|
||||
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;
|
||||
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_U | BC_RET_STRING:
|
||||
case BC_INST_LOOKUP_U | BC_RET_INT:
|
||||
case BC_INST_LOOKUP_U | BC_RET_FLOAT:
|
||||
case BC_INST_LOOKUP_U | BC_RET_NULL:
|
||||
args.back() = cs.lookup_value(args.back().get_string(cs));
|
||||
force_arg(cs, args.back(), op & BC_INST_RET_MASK);
|
||||
continue;
|
||||
|
||||
case BC_INST_LOOKUP | BC_RET_STRING: {
|
||||
alias_stack *ast;
|
||||
|
@ -806,36 +725,6 @@ std::uint32_t *vm_exec(
|
|||
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: {
|
||||
alias_stack *ast;
|
||||
alias *a = get_lookup_id(ts, op, ast);
|
||||
|
@ -848,36 +737,7 @@ std::uint32_t *vm_exec(
|
|||
}
|
||||
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: {
|
||||
alias_stack *ast;
|
||||
alias *a = get_lookup_id(ts, op, ast);
|
||||
|
@ -890,36 +750,6 @@ std::uint32_t *vm_exec(
|
|||
}
|
||||
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: {
|
||||
alias_stack *ast;
|
||||
alias *a = get_lookup_id(ts, op, ast);
|
||||
|
@ -1032,7 +862,7 @@ std::uint32_t *vm_exec(
|
|||
case BC_INST_ALIAS_U: {
|
||||
auto v = std::move(args.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();
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -329,11 +329,11 @@ end:
|
|||
});
|
||||
|
||||
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 &) {
|
||||
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) {
|
||||
|
|
Loading…
Reference in New Issue