rework API for by-name lookups/assignments, simplify VM for that

master
Daniel Kolesa 2021-04-28 03:20:36 +02:00
parent acdb9a4a0e
commit a9afa89af6
4 changed files with 134 additions and 219 deletions

View File

@ -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;

View File

@ -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",

View File

@ -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;
}

View File

@ -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) {