#include #include "cs_vm.hh" #include namespace cscript { bool cs_check_num(std::string_view s) { if (isdigit(s[0])) { return true; } switch (s[0]) { case '+': case '-': return isdigit(s[1]) || ((s[1] == '.') && isdigit(s[2])); case '.': return isdigit(s[1]) != 0; default: return false; } } LIBCUBESCRIPT_EXPORT void cs_state::clear_override(cs_ident &id) { if (!(id.get_flags() & CS_IDF_OVERRIDDEN)) { return; } switch (id.get_type()) { case cs_ident_type::ALIAS: { cs_alias_impl &a = static_cast(id); a.clean_code(); a.get_value().set_str(""); break; } case cs_ident_type::IVAR: { cs_ivar_impl &iv = static_cast(id); iv.set_value(iv.p_overrideval); iv.changed(*this); break; } case cs_ident_type::FVAR: { cs_fvar_impl &fv = static_cast(id); fv.set_value(fv.p_overrideval); fv.changed(*this); break; } case cs_ident_type::SVAR: { cs_svar_impl &sv = static_cast(id); sv.set_value(sv.p_overrideval); sv.changed(*this); break; } default: break; } id.p_impl->p_flags &= ~CS_IDF_OVERRIDDEN; } LIBCUBESCRIPT_EXPORT void cs_state::clear_overrides() { for (auto &p: p_state->idents) { clear_override(*(p.second)); } } LIBCUBESCRIPT_EXPORT cs_ident *cs_state::add_ident( cs_ident *id, cs_ident_impl *impl ) { if (!id) { return nullptr; } id->p_impl = impl; p_state->idents[id->get_name()] = id; static_cast(impl)->p_index = p_state->identmap.size(); p_state->identmap.push_back(id); return p_state->identmap.back(); } LIBCUBESCRIPT_EXPORT cs_ident *cs_state::new_ident(std::string_view name, int flags) { cs_ident *id = get_ident(name); if (!id) { if (cs_check_num(name)) { throw cs_error( *this, "number %s is not a valid identifier name", name.data() ); } auto *inst = p_state->create( *this, cs_strref{p_state, name}, flags ); id = add_ident(inst, inst); } return id; } LIBCUBESCRIPT_EXPORT cs_ident *cs_state::force_ident(cs_value &v) { switch (v.get_type()) { case cs_value_type::IDENT: return v.get_ident(); case cs_value_type::STRING: { cs_ident *id = new_ident(v.get_str()); v.set_ident(id); return id; } default: break; } v.set_ident(p_state->identmap[DummyIdx]); return p_state->identmap[DummyIdx]; } LIBCUBESCRIPT_EXPORT cs_ident *cs_state::get_ident(std::string_view name) { auto id = p_state->idents.find(name); if (id != p_state->idents.end()) { return id->second; } return nullptr; } LIBCUBESCRIPT_EXPORT cs_alias *cs_state::get_alias(std::string_view name) { auto id = get_ident(name); if (!id || !id->is_alias()) { return nullptr; } return static_cast(id); } LIBCUBESCRIPT_EXPORT bool cs_state::have_ident(std::string_view name) { return p_state->idents.find(name) != p_state->idents.end(); } LIBCUBESCRIPT_EXPORT std::span cs_state::get_idents() { return std::span{ p_state->identmap.data(), p_state->identmap.size() }; } LIBCUBESCRIPT_EXPORT std::span cs_state::get_idents() const { auto ptr = const_cast(p_state->identmap.data()); return std::span{ptr, p_state->identmap.size()}; } LIBCUBESCRIPT_EXPORT cs_ivar *cs_state::new_ivar( std::string_view n, cs_int m, cs_int x, cs_int v, cs_var_cb f, int flags ) { auto *iv = p_state->create( cs_strref{p_state, n}, m, x, v, std::move(f), flags ); add_ident(iv, iv); return iv; } LIBCUBESCRIPT_EXPORT cs_fvar *cs_state::new_fvar( std::string_view n, cs_float m, cs_float x, cs_float v, cs_var_cb f, int flags ) { auto *fv = p_state->create( cs_strref{p_state, n}, m, x, v, std::move(f), flags ); add_ident(fv, fv); return fv; } LIBCUBESCRIPT_EXPORT cs_svar *cs_state::new_svar( std::string_view n, std::string_view v, cs_var_cb f, int flags ) { auto *sv = p_state->create( cs_strref{p_state, n}, cs_strref{p_state, v}, cs_strref{p_state, ""}, std::move(f), flags ); add_ident(sv, sv); return sv; } LIBCUBESCRIPT_EXPORT void cs_state::reset_var(std::string_view name) { cs_ident *id = get_ident(name); if (!id) { throw cs_error(*this, "variable %s does not exist", name.data()); } if (id->get_flags() & CS_IDF_READONLY) { throw cs_error(*this, "variable %s is read only", name.data()); } clear_override(*id); } LIBCUBESCRIPT_EXPORT void cs_state::touch_var(std::string_view name) { cs_ident *id = get_ident(name); if (id && id->is_var()) { static_cast(id->p_impl)->changed(*this); } } LIBCUBESCRIPT_EXPORT void cs_state::set_alias(std::string_view name, cs_value v) { cs_ident *id = get_ident(name); if (id) { switch (id->get_type()) { case cs_ident_type::ALIAS: { cs_alias_impl *a = static_cast(id); if (a->get_index() < MaxArguments) { a->set_arg(*this, v); } else { a->set_alias(*this, v); } return; } case cs_ident_type::IVAR: set_var_int_checked(static_cast(id), v.get_int()); break; case cs_ident_type::FVAR: set_var_float_checked(static_cast(id), v.get_float()); break; case cs_ident_type::SVAR: set_var_str_checked(static_cast(id), v.get_str()); break; default: throw cs_error( *this, "cannot redefine builtin %s with an alias", id->get_name().data() ); } } else if (cs_check_num(name)) { throw cs_error(*this, "cannot alias number %s", name.data()); } else { auto *a = p_state->create( *this, cs_strref{p_state, name}, std::move(v), identflags ); add_ident(a, a); } } LIBCUBESCRIPT_EXPORT void cs_state::print_var(cs_var const &v) const { if (p_state->varprintf) { p_state->varprintf(*this, v); } } LIBCUBESCRIPT_EXPORT cs_value cs_alias::get_value() const { return static_cast(this)->p_val; } void cs_alias::get_cval(cs_value &v) const { auto *imp = static_cast(this); switch (imp->p_val.get_type()) { case cs_value_type::STRING: v = imp->p_val; break; case cs_value_type::INT: v.set_int(imp->p_val.get_int()); break; case cs_value_type::FLOAT: v.set_float(imp->p_val.get_float()); break; default: v.set_none(); break; } } int cs_ident::get_raw_type() const { return p_impl->p_type; } cs_ident_type cs_ident::get_type() const { if (p_impl->p_type > ID_ALIAS) { return cs_ident_type::SPECIAL; } return cs_ident_type(p_impl->p_type); } std::string_view cs_ident::get_name() const { return p_impl->p_name; } int cs_ident::get_flags() const { return p_impl->p_flags; } int cs_ident::get_index() const { return p_impl->p_index; } template static inline void cs_override_var(cs_state &cs, cs_var *v, int &vflags, SF sf) { if ((cs.identflags & CS_IDF_OVERRIDDEN) || (vflags & CS_IDF_OVERRIDE)) { if (vflags & CS_IDF_PERSIST) { throw cs_error( cs, "cannot override persistent variable '%s'", v->get_name().data() ); } if (!(vflags & CS_IDF_OVERRIDDEN)) { sf(); vflags |= CS_IDF_OVERRIDDEN; } } else { if (vflags & CS_IDF_OVERRIDDEN) { vflags &= ~CS_IDF_OVERRIDDEN; } } } LIBCUBESCRIPT_EXPORT void cs_state::set_var_int( std::string_view name, cs_int v, bool dofunc, bool doclamp ) { cs_ident *id = get_ident(name); if (!id || id->is_ivar()) { return; } cs_ivar_impl *iv = static_cast(id); cs_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 cs_state::set_var_float( std::string_view name, cs_float v, bool dofunc, bool doclamp ) { cs_ident *id = get_ident(name); if (!id || id->is_fvar()) { return; } cs_fvar_impl *fv = static_cast(id); cs_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 cs_state::set_var_str( std::string_view name, std::string_view v, bool dofunc ) { cs_ident *id = get_ident(name); if (!id || id->is_svar()) { return; } cs_svar_impl *sv = static_cast(id); cs_override_var( *this, sv, sv->p_flags, [&sv]() { sv->p_overrideval = sv->get_value(); } ); sv->set_value(cs_strref{p_state, v}); if (dofunc) { sv->changed(*this); } } LIBCUBESCRIPT_EXPORT std::optional cs_state::get_var_int(std::string_view name) { cs_ident *id = get_ident(name); if (!id || id->is_ivar()) { return std::nullopt; } return static_cast(id)->get_value(); } LIBCUBESCRIPT_EXPORT std::optional cs_state::get_var_float(std::string_view name) { cs_ident *id = get_ident(name); if (!id || id->is_fvar()) { return std::nullopt; } return static_cast(id)->get_value(); } LIBCUBESCRIPT_EXPORT std::optional cs_state::get_var_str(std::string_view name) { cs_ident *id = get_ident(name); if (!id || id->is_svar()) { return std::nullopt; } return cs_strref{p_state, static_cast(id)->get_value()}; } LIBCUBESCRIPT_EXPORT std::optional cs_state::get_var_min_int(std::string_view name) { cs_ident *id = get_ident(name); if (!id || id->is_ivar()) { return std::nullopt; } return static_cast(id)->get_val_min(); } LIBCUBESCRIPT_EXPORT std::optional cs_state::get_var_max_int(std::string_view name) { cs_ident *id = get_ident(name); if (!id || id->is_ivar()) { return std::nullopt; } return static_cast(id)->get_val_max(); } LIBCUBESCRIPT_EXPORT std::optional cs_state::get_var_min_float(std::string_view name) { cs_ident *id = get_ident(name); if (!id || id->is_fvar()) { return std::nullopt; } return static_cast(id)->get_val_min(); } LIBCUBESCRIPT_EXPORT std::optional cs_state::get_var_max_float(std::string_view name) { cs_ident *id = get_ident(name); if (!id || id->is_fvar()) { return std::nullopt; } return static_cast(id)->get_val_max(); } LIBCUBESCRIPT_EXPORT std::optional cs_state::get_alias_val(std::string_view name) { cs_alias *a = get_alias(name); if (!a) { return std::nullopt; } if ((a->get_index() < MaxArguments) && !ident_is_used_arg(a, *this)) { return std::nullopt; } return a->get_value().get_str(); } cs_int cs_clamp_var(cs_state &cs, cs_ivar *iv, cs_int 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 cs_error( cs, (iv->get_flags() & CS_IDF_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 cs_state::set_var_int_checked(cs_ivar *iv, cs_int v) { if (iv->get_flags() & CS_IDF_READONLY) { throw cs_error( *this, "variable '%s' is read only", iv->get_name().data() ); } cs_ivar_impl *ivp = static_cast(iv); cs_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 = cs_clamp_var(*this, iv, v); } iv->set_value(v); ivp->changed(*this); } LIBCUBESCRIPT_EXPORT void cs_state::set_var_int_checked( cs_ivar *iv, std::span args ) { cs_int v = args[0].force_int(); if ((iv->get_flags() & CS_IDF_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); } cs_float cs_clamp_fvar(cs_state &cs, cs_fvar *fv, cs_float 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; } cs_value vmin{cs}, vmax{cs}; vmin.set_float(fv->get_val_min()); vmax.set_float(fv->get_val_max()); throw cs_error( cs, "valid range for '%s' is %s..%s", fv->get_name().data(), vmin.force_str(), vmax.force_str() ); return v; } LIBCUBESCRIPT_EXPORT void cs_state::set_var_float_checked(cs_fvar *fv, cs_float v) { if (fv->get_flags() & CS_IDF_READONLY) { throw cs_error( *this, "variable '%s' is read only", fv->get_name().data() ); } cs_fvar_impl *fvp = static_cast(fv); cs_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 = cs_clamp_fvar(*this, fv, v); } fv->set_value(v); fvp->changed(*this); } LIBCUBESCRIPT_EXPORT void cs_state::set_var_str_checked( cs_svar *sv, std::string_view v ) { if (sv->get_flags() & CS_IDF_READONLY) { throw cs_error( *this, "variable '%s' is read only", sv->get_name().data() ); } cs_svar_impl *svp = static_cast(sv); cs_override_var( *this, sv, svp->p_flags, [&svp]() { svp->p_overrideval = svp->p_storage; } ); sv->set_value(cs_strref{p_state, v}); svp->changed(*this); } LIBCUBESCRIPT_EXPORT cs_command *cs_state::new_command( std::string_view name, std::string_view args, cs_command_cb func ) { int nargs = 0; for (auto fmt = args.begin(); fmt != args.end(); ++fmt) { switch (*fmt) { case 'i': case 'b': case 'f': case 'F': case 't': case 'T': case 'E': case 'N': case 's': case 'e': case 'r': case '$': if (nargs < MaxArguments) { ++nargs; } break; case '1': case '2': case '3': case '4': if (nargs < (*fmt - '0')) { return nullptr; } if ((args.end() - fmt) != 2) { return nullptr; } if ((fmt[1] != 'C') && (fmt[1] != 'V')) { return nullptr; } if (nargs < MaxArguments) { fmt -= *fmt - '0' + 1; } break; case 'C': case 'V': if ((fmt + 1) != args.end()) { return nullptr; } break; default: return nullptr; } } auto *cmd = p_state->create( cs_strref{p_state, name}, cs_strref{p_state, args}, nargs, std::move(func) ); add_ident(cmd, cmd); return cmd; } } /* namespace cscript */