#ifndef LIBCUBESCRIPT_CUBESCRIPT_HH #define LIBCUBESCRIPT_CUBESCRIPT_HH #include #include #include "cubescript_conf.hh" #include #include #include #include #include #include #include #include #include #include #include namespace cscript { enum { VAL_NULL = 0, VAL_INT, VAL_FLOAT, VAL_STR, VAL_ANY, VAL_CODE, VAL_MACRO, VAL_IDENT, VAL_CSTR, VAL_CANY, VAL_WORD, VAL_POP, VAL_COND }; enum { IDF_PERSIST = 1 << 0, IDF_OVERRIDE = 1 << 1, IDF_HEX = 1 << 2, IDF_READONLY = 1 << 3, IDF_OVERRIDDEN = 1 << 4, IDF_UNKNOWN = 1 << 5, IDF_ARG = 1 << 6 }; struct Bytecode; struct OSTD_EXPORT BytecodeRef { BytecodeRef(): p_code(nullptr) {} BytecodeRef(Bytecode *v); BytecodeRef(BytecodeRef const &v); BytecodeRef(BytecodeRef &&v): p_code(v.p_code) { v.p_code = nullptr; } ~BytecodeRef(); BytecodeRef &operator=(BytecodeRef const &v); BytecodeRef &operator=(BytecodeRef &&v); operator bool() const { return p_code != nullptr; } operator Bytecode *() const { return p_code; } private: Bytecode *p_code; }; OSTD_EXPORT bool code_is_empty(Bytecode const *code); struct Ident; struct Alias; struct IdentValue { union { CsInt i; /* ID_IVAR, VAL_INT */ CsFloat f; /* ID_FVAR, VAL_FLOAT */ Bytecode const *code; /* VAL_CODE */ Ident *id; /* VAL_IDENT */ char *s; /* ID_SVAR, VAL_STR */ char const *cstr; /* VAL_CSTR */ }; ostd::Size len; }; struct OSTD_EXPORT TaggedValue: IdentValue { friend struct Alias; int get_type() const { return p_type; } void set_int(CsInt val) { p_type = VAL_INT; i = val; } void set_float(CsFloat val) { p_type = VAL_FLOAT; f = val; } void set_str(ostd::String val) { ostd::CharRange cr = val.iter(); val.disown(); set_mstr(cr); } void set_null() { p_type = VAL_NULL; i = 0; } void set_code(Bytecode const *val) { p_type = VAL_CODE; code = val; } void set_cstr(ostd::ConstCharRange val) { p_type = VAL_CSTR; len = val.size(); cstr = val.data(); } void set_mstr(ostd::CharRange val) { p_type = VAL_STR; len = val.size(); s = val.data(); } void set_ident(Ident *val) { p_type = VAL_IDENT; id = val; } void set_macro(Bytecode const *val, ostd::Size ln) { p_type = VAL_MACRO; len = ln; code = val; } void set(TaggedValue &tv) { *this = tv; tv.p_type = VAL_NULL; } ostd::String get_str() const; ostd::ConstCharRange get_strr() const; CsInt get_int() const; CsFloat get_float() const; Bytecode *get_code() const; Ident *get_ident() const; void get_val(TaggedValue &r) const; bool get_bool() const; void force_null(); CsFloat force_float(); CsInt force_int(); ostd::ConstCharRange force_str(); bool code_is_empty() const; void cleanup(); void copy_arg(TaggedValue &r) const; private: int p_type; }; using TvalRange = ostd::PointerRange; struct IdentStack { IdentValue val; int valtype; IdentStack *next; }; union IdentValuePtr { CsInt *ip; /* ID_IVAR */ CsFloat *fp; /* ID_FVAR */ char **sp; /* ID_SVAR */ }; struct CsState; using VarCb = ostd::Function; enum class IdentType { unknown = -1, ivar, fvar, svar, command, alias }; struct OSTD_EXPORT Ident { ostd::byte type; /* ID_something */ int valtype; /* ID_ALIAS */ ostd::ushort flags; int index; ostd::String name; union { struct { /* ID_IVAR, ID_FVAR, ID_SVAR */ union { struct { /* ID_IVAR */ CsInt minval, maxval; }; struct { /* ID_FVAR */ CsFloat minvalf, maxvalf; }; }; IdentValuePtr storage; IdentValue overrideval; }; struct { /* ID_ALIAS */ Bytecode *code; IdentValue val; IdentStack *stack; }; }; VarCb cb_var; void changed() { if (cb_var) { cb_var(*this); } } void set_value(TaggedValue const &v) { valtype = v.get_type(); val = v; } void set_value(IdentStack const &v) { valtype = v.valtype; val = v.val; } CsFloat get_float() const; CsInt get_int() const; ostd::String get_str() const; ostd::ConstCharRange get_strr() const; void get_val(TaggedValue &r) const; void get_cstr(TaggedValue &v) const; void get_cval(TaggedValue &v) const; void clean_code(); void push_arg(TaggedValue const &v, IdentStack &st, bool um = true); void pop_arg(); void undo_arg(IdentStack &st); void redo_arg(IdentStack const &st); void push_alias(IdentStack &st); void pop_alias(); void set_arg(CsState &cs, TaggedValue &v); void set_alias(CsState &cs, TaggedValue &v); int get_valtype() const { return valtype; } IdentType get_type() const; bool is_alias() const { return get_type() == IdentType::alias; } bool is_command() const { return get_type() == IdentType::command; } bool is_var() const { IdentType tp = get_type(); return (tp >= IdentType::ivar) && (tp <= IdentType::svar); } bool is_ivar() const { return get_type() == IdentType::ivar; } bool is_fvar() const { return get_type() == IdentType::fvar; } bool is_svar() const { return get_type() == IdentType::svar; } protected: Ident(); }; struct Var: Ident { }; struct Ivar: Var { Ivar( ostd::ConstCharRange n, CsInt m, CsInt x, CsInt *s, VarCb f = VarCb(), int flags = 0 ); }; struct Fvar: Var { Fvar( ostd::ConstCharRange n, CsFloat m, CsFloat x, CsFloat *s, VarCb f = VarCb(), int flags = 0 ); }; struct Svar: Var { Svar( ostd::ConstCharRange n, char **s, VarCb f = VarCb(), int flags = 0 ); }; struct Alias: Ident { Alias(ostd::ConstCharRange n, char *a, int flags); Alias(ostd::ConstCharRange n, CsInt a, int flags); Alias(ostd::ConstCharRange n, CsFloat a, int flags); Alias(ostd::ConstCharRange n, int flags); Alias(ostd::ConstCharRange n, TaggedValue const &v, int flags); void force_null() { if (valtype == VAL_STR) { delete[] val.s; val.s = nullptr; val.len = 0; } valtype = VAL_NULL; } }; using CmdFunc = ostd::Function; struct Command: Ident { char *cargs; ostd::Uint32 argmask; int numargs; CmdFunc cb_cftv; Command( int t, ostd::ConstCharRange n, ostd::ConstCharRange args, ostd::Uint32 argmask, int numargs, CmdFunc f = CmdFunc() ); }; struct IdentLink { Ident *id; IdentLink *next; int usedargs; IdentStack *argstack; }; struct OSTD_EXPORT CsState { ostd::Map idents; ostd::Vector identmap; Ident *dummy = nullptr; IdentLink noalias; IdentLink *stack = &noalias; ostd::ConstCharRange src_file; ostd::ConstCharRange src_str; int identflags = 0; int nodebug = 0; CsInt numargs = 0; CsInt dbgalias = 4; CsState(); ~CsState(); void clear_override(Ident &id); void clear_overrides(); Ident *add_ident(Ident *id) { if (!id) { return nullptr; } idents[id->name] = id; id->index = identmap.size(); return identmap.push(id); } Ident *new_ident(ostd::ConstCharRange name, int flags = IDF_UNKNOWN); Ident *force_ident(TaggedValue &v); Ident *get_ident(ostd::ConstCharRange name) { Ident **id = idents.at(name); if (!id) { return nullptr; } return *id; } Alias *get_alias(ostd::ConstCharRange name) { Ident *id = get_ident(name); if (!id->is_alias()) { return nullptr; } return static_cast(id); } bool have_ident(ostd::ConstCharRange name) { return idents.at(name) != nullptr; } bool reset_var(ostd::ConstCharRange name); void touch_var(ostd::ConstCharRange name); bool add_command( ostd::ConstCharRange name, ostd::ConstCharRange args, CmdFunc func ); ostd::String run_str(Bytecode const *code); ostd::String run_str(ostd::ConstCharRange code); ostd::String run_str(Ident *id, TvalRange args); CsInt run_int(Bytecode const *code); CsInt run_int(ostd::ConstCharRange code); CsInt run_int(Ident *id, TvalRange args); CsFloat run_float(Bytecode const *code); CsFloat run_float(ostd::ConstCharRange code); CsFloat run_float(Ident *id, TvalRange args); bool run_bool(Bytecode const *code); bool run_bool(ostd::ConstCharRange code); bool run_bool(Ident *id, TvalRange args); void run_ret(Bytecode const *code, TaggedValue &ret); void run_ret(ostd::ConstCharRange code, TaggedValue &ret); void run_ret(Ident *id, TvalRange args, TaggedValue &ret); bool run_file(ostd::ConstCharRange fname); void set_alias(ostd::ConstCharRange name, TaggedValue &v); void set_var_int( ostd::ConstCharRange name, CsInt v, bool dofunc = true, bool doclamp = true ); void set_var_float( ostd::ConstCharRange name, CsFloat v, bool dofunc = true, bool doclamp = true ); void set_var_str( ostd::ConstCharRange name, ostd::ConstCharRange v, bool dofunc = true ); void set_var_int_checked(Ident *id, CsInt v); void set_var_int_checked(Ident *id, TvalRange args); void set_var_float_checked(Ident *id, CsFloat v); void set_var_str_checked(Ident *id, ostd::ConstCharRange v); ostd::Maybe get_var_int(ostd::ConstCharRange name); ostd::Maybe get_var_float(ostd::ConstCharRange name); ostd::Maybe get_var_str(ostd::ConstCharRange name); ostd::Maybe get_var_min_int(ostd::ConstCharRange name); ostd::Maybe get_var_max_int(ostd::ConstCharRange name); ostd::Maybe get_var_min_float(ostd::ConstCharRange name); ostd::Maybe get_var_max_float(ostd::ConstCharRange name); ostd::Maybe get_alias_val(ostd::ConstCharRange name); void print_var(Ident *id); void print_var_int(Ident *id, CsInt i); void print_var_float(Ident *id, CsFloat f); void print_var_str(Ident *id, ostd::ConstCharRange s); }; enum { CS_LIB_IO = 1 << 0, CS_LIB_MATH = 1 << 1, CS_LIB_STRING = 1 << 2, CS_LIB_LIST = 1 << 3, CS_LIB_ALL = 0b1111 }; OSTD_EXPORT void init_libs(CsState &cs, int libs = CS_LIB_ALL); struct OSTD_EXPORT StackedValue: TaggedValue { StackedValue(Ident *id = nullptr): TaggedValue(), p_id(nullptr), p_stack(), p_pushed(false) { set_id(id); } ~StackedValue() { pop(); } bool set_id(Ident *id) { if (!id || !id->is_alias()) { return false; } p_id = id; return true; } Ident *get_id() const { return p_id; } bool has_id() const { return p_id != nullptr; } bool push() { if (p_pushed || !p_id) { return false; } p_id->push_arg(*this, p_stack); p_pushed = true; return true; } bool pop() { if (!p_pushed || !p_id) { return false; } p_id->pop_arg(); p_pushed = false; return true; } private: Ident *p_id; IdentStack p_stack; bool p_pushed; }; namespace util { template inline ostd::Size escape_string(R &&writer, ostd::ConstCharRange str) { ostd::Size ret = 2; writer.put('"'); for (; !str.empty(); str.pop_front()) { switch (str.front()) { case '\n': ret += writer.put_n("^n", 2); break; case '\t': ret += writer.put_n("^t", 2); break; case '\f': ret += writer.put_n("^f", 2); break; case '"': ret += writer.put_n("^\"", 2); break; case '^': ret += writer.put_n("^^", 2); break; default: ret += writer.put(str.front()); break; } } writer.put('"'); return ret; } template inline ostd::Size unescape_string(R &&writer, ostd::ConstCharRange str) { ostd::Size ret = 0; for (; !str.empty(); str.pop_front()) { if (str.front() == '^') { str.pop_front(); if (str.empty()) { break; } switch (str.front()) { case 'n': ret += writer.put('\n'); break; case 't': ret += writer.put('\r'); break; case 'f': ret += writer.put('\f'); break; case '"': ret += writer.put('"'); break; case '^': ret += writer.put('^'); break; default: ret += writer.put(str.front()); break; } } else { ret += writer.put(str.front()); } } return ret; } struct ListParser { ostd::ConstCharRange input; ostd::ConstCharRange quote = ostd::ConstCharRange(); ostd::ConstCharRange item = ostd::ConstCharRange(); ListParser() = delete; ListParser(ostd::ConstCharRange src): input(src) {} void skip(); bool parse(); ostd::String element(); }; ostd::Size list_length(ostd::ConstCharRange s); ostd::Maybe list_index( ostd::ConstCharRange s, ostd::Size idx ); ostd::Vector list_explode( ostd::ConstCharRange s, ostd::Size limit = -1 ); template inline ostd::Ptrdiff format_int(R &&writer, CsInt val) { return ostd::format(ostd::forward(writer), IntFormat, val); } template inline ostd::Ptrdiff format_float(R &&writer, CsFloat val) { return ostd::format( ostd::forward(writer), (val == CsInt(val)) ? RoundFloatFormat : FloatFormat, val ); } template inline ostd::Size tvals_concat( R &&writer, TvalRange vals, ostd::ConstCharRange sep = ostd::ConstCharRange() ) { ostd::Size ret = 0; for (ostd::Size i = 0; i < vals.size(); ++i) { auto s = ostd::appender(); switch (vals[i].get_type()) { case VAL_INT: { auto r = format_int(ostd::forward(writer), vals[i].i); if (r > 0) { ret += ostd::Size(r); } break; } case VAL_FLOAT: { auto r = format_float(ostd::forward(writer), vals[i].f); if (r > 0) { ret += ostd::Size(r); } break; } case VAL_STR: case VAL_CSTR: case VAL_MACRO: ret += writer.put_n(vals[i].s, vals[i].len); break; default: break; } if (i == (vals.size() - 1)) { break; } ret += writer.put_n(sep.data(), sep.size()); } return ret; } } /* namespace util */ } /* namespace cscript */ #endif /* LIBCUBESCRIPT_CUBESCRIPT_HH */