#ifndef LIBCUBESCRIPT_CUBESCRIPT_HH #define LIBCUBESCRIPT_CUBESCRIPT_HH #include #include #include #include #include #include #include #include "cubescript_conf.hh" #include #include #include #include #include namespace cscript { using cs_string = std::string; static_assert(std::is_integral_v, "cs_int must be integral"); static_assert(std::is_signed_v, "cs_int must be signed"); static_assert(std::is_floating_point_v, "cs_float must be floating point"); struct cs_internal_error: std::runtime_error { using std::runtime_error::runtime_error; }; enum { CS_IDF_PERSIST = 1 << 0, CS_IDF_OVERRIDE = 1 << 1, CS_IDF_HEX = 1 << 2, CS_IDF_READONLY = 1 << 3, CS_IDF_OVERRIDDEN = 1 << 4, CS_IDF_UNKNOWN = 1 << 5, CS_IDF_ARG = 1 << 6 }; struct cs_bcode; struct OSTD_EXPORT cs_bcode_ref { cs_bcode_ref(): p_code(nullptr) {} cs_bcode_ref(cs_bcode *v); cs_bcode_ref(cs_bcode_ref const &v); cs_bcode_ref(cs_bcode_ref &&v): p_code(v.p_code) { v.p_code = nullptr; } ~cs_bcode_ref(); cs_bcode_ref &operator=(cs_bcode_ref const &v); cs_bcode_ref &operator=(cs_bcode_ref &&v); operator bool() const { return p_code != nullptr; } operator cs_bcode *() const { return p_code; } private: cs_bcode *p_code; }; OSTD_EXPORT bool cs_code_is_empty(cs_bcode *code); enum class cs_value_type { Null = 0, Int, Float, String, Cstring, Code, Macro, Ident }; struct OSTD_EXPORT cs_value { cs_value(); ~cs_value(); cs_value(cs_value const &); cs_value(cs_value &&); cs_value &operator=(cs_value const &v); cs_value &operator=(cs_value &&v); cs_value_type get_type() const; void set_int(cs_int val); void set_float(cs_float val); void set_str(cs_string val); void set_null(); void set_code(cs_bcode *val); void set_cstr(ostd::string_range val); void set_ident(cs_ident *val); void set_macro(ostd::string_range val); cs_string get_str() const; ostd::string_range get_strr() const; cs_int get_int() const; cs_float get_float() const; cs_bcode *get_code() const; cs_ident *get_ident() const; void get_val(cs_value &r) const; bool get_bool() const; void force_null(); cs_float force_float(); cs_int force_int(); ostd::string_range force_str(); bool code_is_empty() const; private: std::aligned_union_t<1, cs_int, cs_float, void *> p_stor; size_t p_len; cs_value_type p_type; }; struct cs_ident_stack { cs_value val_s; cs_ident_stack *next; }; struct cs_shared_state; struct cs_error; struct cs_gen_state; enum class cs_ident_type { Ivar = 0, Fvar, Svar, Command, Alias, Special }; struct cs_var; struct cs_ivar; struct cs_fvar; struct cs_svar; struct cs_alias; struct cs_command; struct OSTD_EXPORT cs_ident { friend struct cs_state; friend struct cs_shared_state; cs_ident() = delete; cs_ident(cs_ident const &) = delete; cs_ident(cs_ident &&) = delete; /* trigger destructors for all inherited members properly */ virtual ~cs_ident() {}; cs_ident &operator=(cs_ident const &) = delete; cs_ident &operator=(cs_ident &&) = delete; cs_ident_type get_type() const; ostd::string_range get_name() const; int get_flags() const; int get_index() const; bool is_alias() const; cs_alias *get_alias(); cs_alias const *get_alias() const; bool is_command() const; cs_command *get_command(); cs_command const *get_command() const; bool is_special() const; bool is_var() const; cs_var *get_var(); cs_var const *get_var() const; bool is_ivar() const; cs_ivar *get_ivar(); cs_ivar const *get_ivar() const; bool is_fvar() const; cs_fvar *get_fvar(); cs_fvar const *get_fvar() const; bool is_svar() const; cs_svar *get_svar(); cs_svar const *get_svar() const; int get_type_raw() const { return p_type; } protected: cs_ident(cs_ident_type tp, ostd::string_range name, int flags = 0); cs_string p_name; /* represents the cs_ident_type above, but internally it has a wider variety * of values, so it's an int here (maps to an internal enum) */ int p_type, p_flags; private: int p_index = -1; }; struct OSTD_EXPORT cs_var: cs_ident { friend struct cs_state; friend struct cs_shared_state; protected: cs_var(cs_ident_type tp, ostd::string_range name, cs_var_cb func, int flags = 0); private: cs_var_cb cb_var; virtual cs_string to_printable() const = 0; void changed(cs_state &cs) { if (cb_var) { cb_var(cs, *this); } } }; struct OSTD_EXPORT cs_ivar: cs_var { friend struct cs_state; friend struct cs_shared_state; cs_int get_val_min() const; cs_int get_val_max() const; cs_int get_value() const; void set_value(cs_int val); cs_string to_printable() const final; private: cs_ivar( ostd::string_range n, cs_int m, cs_int x, cs_int v, cs_var_cb f, int flags ); cs_int p_storage, p_minval, p_maxval, p_overrideval; }; struct OSTD_EXPORT cs_fvar: cs_var { friend struct cs_state; friend struct cs_shared_state; cs_float get_val_min() const; cs_float get_val_max() const; cs_float get_value() const; void set_value(cs_float val); cs_string to_printable() const final; private: cs_fvar( ostd::string_range n, cs_float m, cs_float x, cs_float v, cs_var_cb f, int flags ); cs_float p_storage, p_minval, p_maxval, p_overrideval; }; struct OSTD_EXPORT cs_svar: cs_var { friend struct cs_state; friend struct cs_shared_state; ostd::string_range get_value() const; void set_value(cs_string val); cs_string to_printable() const final; private: cs_svar(ostd::string_range n, cs_string v, cs_var_cb f, int flags); cs_string p_storage, p_overrideval; }; struct OSTD_EXPORT cs_alias: cs_ident { friend struct cs_state; friend struct cs_shared_state; friend struct cs_alias_internal; cs_value const &get_value() const { return p_val; } cs_value &get_value() { return p_val; } void get_cstr(cs_value &v) const; void get_cval(cs_value &v) const; private: cs_alias(ostd::string_range n, cs_string a, int flags); cs_alias(ostd::string_range n, cs_int a, int flags); cs_alias(ostd::string_range n, cs_float a, int flags); cs_alias(ostd::string_range n, int flags); cs_alias(ostd::string_range n, cs_value v, int flags); cs_bcode *p_acode; cs_ident_stack *p_astack; cs_value p_val; }; struct cs_command: cs_ident { friend struct cs_state; friend struct cs_shared_state; friend struct cs_cmd_internal; ostd::string_range get_args() const; int get_num_args() const; private: cs_command( ostd::string_range name, ostd::string_range args, int numargs, cs_command_cb func ); cs_string p_cargs; cs_command_cb p_cb_cftv; int p_numargs; }; struct cs_identLink; enum { CsLibMath = 1 << 0, CsLibString = 1 << 1, CsLibList = 1 << 2, CsLibAll = 0b111 }; enum class CsLoopState { Normal = 0, Break, Continue }; static inline void *cs_default_alloc(void *, void *p, size_t, size_t ns) { if (!ns) { delete[] static_cast(p); return nullptr; } return new unsigned char[ns]; } struct OSTD_EXPORT cs_state { friend struct cs_error; friend struct cs_gen_state; cs_shared_state *p_state; cs_identLink *p_callstack = nullptr; int identflags = 0; cs_state(cs_alloc_cb func = cs_default_alloc, void *data = nullptr); virtual ~cs_state(); cs_state(cs_state const &) = delete; cs_state(cs_state &&s) { swap(s); } cs_state &operator=(cs_state const &) = delete; cs_state &operator=(cs_state &&s) { swap(s); s.destroy(); return *this; } void destroy(); void swap(cs_state &s) { std::swap(p_state, s.p_state); std::swap(p_callstack, s.p_callstack); std::swap(identflags, s.identflags); std::swap(p_pstate, s.p_pstate); std::swap(p_inloop, s.p_inloop); std::swap(p_owner, s.p_owner); std::swap(p_callhook, s.p_callhook); } cs_state new_thread(); cs_hook_cb set_call_hook(cs_hook_cb func); cs_hook_cb const &get_call_hook() const; cs_hook_cb &get_call_hook(); void init_libs(int libs = CsLibAll); void clear_override(cs_ident &id); void clear_overrides(); cs_ident *new_ident(ostd::string_range name, int flags = CS_IDF_UNKNOWN); cs_ident *force_ident(cs_value &v); cs_ivar *new_ivar( ostd::string_range n, cs_int m, cs_int x, cs_int v, cs_var_cb f = cs_var_cb(), int flags = 0 ); cs_fvar *new_fvar( ostd::string_range n, cs_float m, cs_float x, cs_float v, cs_var_cb f = cs_var_cb(), int flags = 0 ); cs_svar *new_svar( ostd::string_range n, cs_string v, cs_var_cb f = cs_var_cb(), int flags = 0 ); cs_command *new_command( ostd::string_range name, ostd::string_range args, cs_command_cb func ); cs_ident *get_ident(ostd::string_range name); cs_alias *get_alias(ostd::string_range name); bool have_ident(ostd::string_range name); cs_ident_r get_idents(); cs_const_ident_r get_idents() const; void reset_var(ostd::string_range name); void touch_var(ostd::string_range name); cs_string run_str(cs_bcode *code); cs_string run_str(ostd::string_range code); cs_string run_str(cs_ident *id, cs_value_r args); cs_int run_int(cs_bcode *code); cs_int run_int(ostd::string_range code); cs_int run_int(cs_ident *id, cs_value_r args); cs_float run_float(cs_bcode *code); cs_float run_float(ostd::string_range code); cs_float run_float(cs_ident *id, cs_value_r args); bool run_bool(cs_bcode *code); bool run_bool(ostd::string_range code); bool run_bool(cs_ident *id, cs_value_r args); void run(cs_bcode *code, cs_value &ret); void run(ostd::string_range code, cs_value &ret); void run(cs_ident *id, cs_value_r args, cs_value &ret); void run(cs_bcode *code); void run(ostd::string_range code); void run(cs_ident *id, cs_value_r args); CsLoopState run_loop(cs_bcode *code, cs_value &ret); CsLoopState run_loop(cs_bcode *code); bool is_in_loop() const { return p_inloop; } std::optional run_file_str(ostd::string_range fname); std::optional run_file_int(ostd::string_range fname); std::optional run_file_float(ostd::string_range fname); std::optional run_file_bool(ostd::string_range fname); bool run_file(ostd::string_range fname, cs_value &ret); bool run_file(ostd::string_range fname); void set_alias(ostd::string_range name, cs_value v); void set_var_int( ostd::string_range name, cs_int v, bool dofunc = true, bool doclamp = true ); void set_var_float( ostd::string_range name, cs_float v, bool dofunc = true, bool doclamp = true ); void set_var_str( ostd::string_range name, ostd::string_range v, bool dofunc = true ); void set_var_int_checked(cs_ivar *iv, cs_int v); void set_var_int_checked(cs_ivar *iv, cs_value_r args); void set_var_float_checked(cs_fvar *fv, cs_float v); void set_var_str_checked(cs_svar *fv, ostd::string_range v); std::optional get_var_int(ostd::string_range name); std::optional get_var_float(ostd::string_range name); std::optional get_var_str(ostd::string_range name); std::optional get_var_min_int(ostd::string_range name); std::optional get_var_max_int(ostd::string_range name); std::optional get_var_min_float(ostd::string_range name); std::optional get_var_max_float(ostd::string_range name); std::optional get_alias_val(ostd::string_range name); virtual void print_var(cs_var *v); private: OSTD_LOCAL cs_state(cs_shared_state *s); cs_ident *add_ident(cs_ident *id); OSTD_LOCAL void *alloc(void *ptr, size_t olds, size_t news); cs_gen_state *p_pstate = nullptr; int p_inloop = 0; bool p_owner = false; char p_errbuf[512]; cs_hook_cb p_callhook; }; struct cs_stack_state_node { cs_stack_state_node const *next; cs_ident const *id; int index; }; struct cs_stack_state { cs_stack_state() = delete; cs_stack_state(cs_state &cs, cs_stack_state_node *nd = nullptr, bool gap = false); cs_stack_state(cs_stack_state const &) = delete; cs_stack_state(cs_stack_state &&st); ~cs_stack_state(); cs_stack_state &operator=(cs_stack_state const &) = delete; cs_stack_state &operator=(cs_stack_state &&); cs_stack_state_node const *get() const; bool gap() const; private: cs_state &p_state; cs_stack_state_node *p_node; bool p_gap; }; struct cs_error { friend struct cs_state; cs_error() = delete; cs_error(cs_error const &) = delete; cs_error(cs_error &&v): p_errmsg(v.p_errmsg), p_stack(std::move(v.p_stack)) {} ostd::string_range what() const { return p_errmsg; } cs_stack_state &get_stack() { return p_stack; } cs_stack_state const &get_stack() const { return p_stack; } cs_error(cs_state &cs, ostd::string_range msg): p_errmsg(), p_stack(cs) { p_errmsg = save_msg(cs, msg); p_stack = save_stack(cs); } template cs_error(cs_state &cs, ostd::string_range msg, A &&...args): p_errmsg(), p_stack(cs) { try { char fbuf[512]; auto ret = ostd::format( ostd::counting_sink(ostd::char_range(fbuf, fbuf + sizeof(fbuf))), msg, std::forward(args)... ).get_written(); p_errmsg = save_msg(cs, ostd::char_range(fbuf, fbuf + ret)); } catch (...) { p_errmsg = save_msg(cs, msg); } p_stack = save_stack(cs); } private: cs_stack_state save_stack(cs_state &cs); ostd::string_range save_msg(cs_state &cs, ostd::string_range v); ostd::string_range p_errmsg; cs_stack_state p_stack; }; struct OSTD_EXPORT cs_stacked_value: cs_value { cs_stacked_value(cs_ident *id = nullptr); ~cs_stacked_value(); cs_stacked_value(cs_stacked_value const &) = delete; cs_stacked_value(cs_stacked_value &&) = delete; cs_stacked_value &operator=(cs_stacked_value const &) = delete; cs_stacked_value &operator=(cs_stacked_value &&v) = delete; cs_stacked_value &operator=(cs_value const &v); cs_stacked_value &operator=(cs_value &&v); bool set_alias(cs_ident *id); cs_alias *get_alias() const; bool has_alias() const; bool push(); bool pop(); private: cs_alias *p_a; cs_ident_stack p_stack; bool p_pushed; }; namespace util { template inline R &&escape_string(R &&writer, ostd::string_range str) { using namespace ostd::string_literals; writer.put('"'); for (; !str.empty(); str.pop_front()) { switch (str.front()) { case '\n': ostd::range_put_all(writer, "^n"_sr); break; case '\t': ostd::range_put_all(writer, "^t"_sr); break; case '\f': ostd::range_put_all(writer, "^f"_sr); break; case '"': ostd::range_put_all(writer, "^\""_sr); break; case '^': ostd::range_put_all(writer, "^^"_sr); break; default: writer.put(str.front()); break; } } writer.put('"'); return std::forward(writer); } template inline R &&unescape_string(R &&writer, ostd::string_range str) { for (; !str.empty(); str.pop_front()) { if (str.front() == '^') { str.pop_front(); if (str.empty()) { break; } switch (str.front()) { case 'n': writer.put('\n'); break; case 't': writer.put('\r'); break; case 'f': writer.put('\f'); break; case '"': writer.put('"'); break; case '^': writer.put('^'); break; default: writer.put(str.front()); break; } } else if (str.front() == '\\') { str.pop_front(); if (str.empty()) { break; } char c = str.front(); if ((c == '\r') || (c == '\n')) { if (!str.empty() && (c == '\r') && (str.front() == '\n')) { str.pop_front(); } continue; } writer.put('\\'); } else { writer.put(str.front()); } } return std::forward(writer); } OSTD_EXPORT ostd::string_range parse_string( cs_state &cs, ostd::string_range str, size_t &nlines ); inline ostd::string_range parse_string( cs_state &cs, ostd::string_range str ) { size_t nlines; return parse_string(cs, str, nlines); } OSTD_EXPORT ostd::string_range parse_word( cs_state &cs, ostd::string_range str ); struct list_range; struct OSTD_EXPORT list_parser { list_parser() = delete; list_parser(cs_state &cs, ostd::string_range src): p_state(cs), p_input(src) {} void skip(); bool parse(); size_t count(); template R &&get_item(R &&writer) const { if (!p_quote.empty() && (*p_quote == '"')) { return unescape_string(std::forward(writer), p_item); } else { ostd::range_put_all(writer, p_item); return std::forward(writer); } } cs_string get_item() const { return std::move(get_item(ostd::appender()).get()); } ostd::string_range &get_raw_item(bool quoted = false) { return quoted ? p_quote : p_item; } ostd::string_range const &get_raw_item(bool quoted = false) const { return quoted ? p_quote : p_item; } ostd::string_range &get_input() { return p_input; } list_range iter() noexcept; private: ostd::string_range p_quote = ostd::string_range(); ostd::string_range p_item = ostd::string_range(); cs_state &p_state; ostd::string_range p_input; }; struct list_range: ostd::input_range { using range_category = ostd::forward_range_tag; using value_type = ostd::string_range; using reference = ostd::string_range; using size_type = std::size_t; list_range() = delete; list_range(list_parser &p) noexcept: p_parser(&p) { pop_front(); } bool empty() const noexcept { return !bool(p_item); } void pop_front() noexcept { if (p_parser->parse()) { p_item = p_parser->get_item(); } else { p_item.reset(); } } ostd::string_range front() const noexcept { return *p_item; } private: list_parser *p_parser; std::optional p_item{}; }; inline list_range list_parser::iter() noexcept { return list_range{*this}; } template inline void format_int(R &&writer, cs_int val) { try { ostd::format(std::forward(writer), IntFormat, val); } catch (ostd::format_error const &e) { throw cs_internal_error{e.what()}; } } template inline void format_float(R &&writer, cs_float val) { try { ostd::format( std::forward(writer), (val == cs_int(val)) ? RoundFloatFormat : FloatFormat, val ); } catch (ostd::format_error const &e) { throw cs_internal_error{e.what()}; } } template inline void tvals_concat( R &&writer, cs_value_r vals, ostd::string_range sep = ostd::string_range() ) { for (size_t i = 0; i < vals.size(); ++i) { switch (vals[i].get_type()) { case cs_value_type::Int: { format_int( std::forward(writer), vals[i].get_int() ); break; } case cs_value_type::Float: { format_float( std::forward(writer), vals[i].get_float() ); break; } case cs_value_type::String: case cs_value_type::Cstring: case cs_value_type::Macro: { ostd::range_put_all(writer, vals[i].get_strr()); break; } default: break; } if (i == (vals.size() - 1)) { break; } ostd::range_put_all(writer, sep); } } template inline void print_stack(R &&writer, cs_stack_state const &st) { auto nd = st.get(); while (nd) { try { ostd::format( std::forward(writer), ((nd->index == 1) && st.gap()) ? " ..%d) %s" : " %d) %s", nd->index, nd->id->get_name() ); } catch (ostd::format_error const &e) { throw cs_internal_error{e.what()}; } nd = nd->next; if (nd) { writer.put('\n'); } } } } /* namespace util */ } /* namespace cscript */ #endif /* LIBCUBESCRIPT_CUBESCRIPT_HH */