From a9afa89af65bebaa57b1d84e8964c43ab0102d13 Mon Sep 17 00:00:00 2001 From: Daniel Kolesa Date: Wed, 28 Apr 2021 03:20:36 +0200 Subject: [PATCH] rework API for by-name lookups/assignments, simplify VM for that --- include/cubescript/cubescript/state.hh | 60 +++++--- src/cs_state.cc | 101 ++++++++++--- src/cs_vm.cc | 188 ++----------------------- src/lib_base.cc | 4 +- 4 files changed, 134 insertions(+), 219 deletions(-) diff --git a/include/cubescript/cubescript/state.hh b/include/cubescript/cubescript/state.hh index 73c9c5b..682d70c 100644 --- a/include/cubescript/cubescript/state.hh +++ b/include/cubescript/cubescript/state.hh @@ -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; diff --git a/src/cs_state.cc b/src/cs_state.cc index 192e7cf..523b3a2 100644 --- a/src/cs_state.cc +++ b/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(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(id); + ast = &p_tstate->get_astack(static_cast(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(id)->get_value()); + return val; + } + case ident_type::IVAR: { + any_value val{}; + val.set_integer(static_cast(id)->get_value()); + return val; + } + case ident_type::FVAR: { + any_value val{}; + val.set_float(static_cast(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(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(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", diff --git a/src/cs_vm.cc b/src/cs_vm.cc index 84bbc70..913bcd4 100644 --- a/src/cs_vm.cc +++ b/src/cs_vm.cc @@ -308,61 +308,6 @@ static inline alias *get_lookup_id( return static_cast(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(id); - ast = &ts.get_astack(static_cast(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(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(id)->get_value() - ); - continue; - case ID_IVAR: - arg.set_integer( - static_cast(id)->get_value() - ); - arg.force_string(cs); - continue; - case ID_FVAR: - arg.set_float( - static_cast(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(id)->get_value() - )); - continue; - case ID_IVAR: - arg.set_integer( - static_cast(id)->get_value() - ); - continue; - case ID_FVAR: - arg.set_integer(integer_type( - static_cast(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(id)->get_value() - )); - continue; - case ID_IVAR: - arg.set_float(float_type( - static_cast(id)->get_value() - )); - continue; - case ID_FVAR: - arg.set_float( - static_cast(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(id)->get_value() - ); - continue; - case ID_IVAR: - arg.set_integer( - static_cast(id)->get_value() - ); - continue; - case ID_FVAR: - arg.set_float( - static_cast(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; } diff --git a/src/lib_base.cc b/src/lib_base.cc index 74596ab..d36b0aa 100644 --- a/src/lib_base.cc +++ b/src/lib_base.cc @@ -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) {