From 395615e2cb5f3b506ae010afc2843747ac5c0db7 Mon Sep 17 00:00:00 2001 From: q66 Date: Tue, 2 Aug 2016 01:21:36 +0100 Subject: [PATCH] start splitting into multiple source files --- Makefile | 20 +- cs_private.hh | 75 +++ cubescript.cc | 1543 +------------------------------------------------ cubescript.hh | 2 + lib_base.cc | 366 ++++++++++++ lib_list.cc | 571 ++++++++++++++++++ lib_list.hh | 129 +++++ lib_math.cc | 196 +++++++ lib_str.cc | 216 +++++++ 9 files changed, 1595 insertions(+), 1523 deletions(-) create mode 100644 cs_private.hh create mode 100644 lib_base.cc create mode 100644 lib_list.cc create mode 100644 lib_list.hh create mode 100644 lib_math.cc create mode 100644 lib_str.cc diff --git a/Makefile b/Makefile index 2616d74..e941e19 100644 --- a/Makefile +++ b/Makefile @@ -2,15 +2,18 @@ OSTD_PATH = ../octastd LIBCS_CXXFLAGS = \ -std=c++14 -Wall -Wextra -Wshadow -Wold-style-cast -I. \ - -fPIC -fvisibility=hidden \ - -I$(OSTD_PATH) + -fvisibility=hidden -I$(OSTD_PATH) LIBCS_LDFLAGS = -shared LIBCS_OBJ = \ - cubescript.o + cubescript.o \ + lib_str.o \ + lib_math.o \ + lib_list.o \ + lib_base.o -LIBCS_LIB = libcubescript.so +LIBCS_LIB = libcubescript.a .cc.o: $(CXX) $(CXXFLAGS) $(LIBCS_CXXFLAGS) -c -o $@ $< @@ -20,10 +23,13 @@ all: library library: $(LIBCS_LIB) $(LIBCS_LIB): $(LIBCS_OBJ) - $(CXX) $(CXXFLAGS) $(LIBCS_CXXFLAGS) \ - $(LDFLAGS) $(LIBCS_LDFLAGS) -o $@ $(LIBCS_OBJ) + ar rcs $(LIBCS_LIB) $(LIBCS_OBJ) clean: rm -f $(LIBCS_LIB) $(LIBCS_OBJ) -cubescript.o: cubescript.hh \ No newline at end of file +cubescript.o: cubescript.hh lib_list.hh cs_private.hh +lib_str.o: cubescript.hh +lib_math.o: cubescript.hh +lib_list.o: cubescript.hh lib_list.hh cs_private.hh +lib_base.o: cubescript.hh cs_private.hh \ No newline at end of file diff --git a/cs_private.hh b/cs_private.hh new file mode 100644 index 0000000..6fc73a3 --- /dev/null +++ b/cs_private.hh @@ -0,0 +1,75 @@ +#ifndef CS_PRIVATE_HH +#define CS_PRIVATE_HH + +#include "cubescript.hh" + +namespace cscript { + +static constexpr int MaxArguments = 25; +static constexpr int MaxResults = 7; +static constexpr int MaxComargs = 12; + +enum { + CODE_START = 0, + CODE_OFFSET, + CODE_NULL, CODE_TRUE, CODE_FALSE, CODE_NOT, + CODE_POP, + CODE_ENTER, CODE_ENTER_RESULT, + CODE_EXIT, CODE_RESULT_ARG, + CODE_VAL, CODE_VALI, + CODE_DUP, + CODE_MACRO, + CODE_BOOL, + CODE_BLOCK, CODE_EMPTY, + CODE_COMPILE, CODE_COND, + CODE_FORCE, + CODE_RESULT, + CODE_IDENT, CODE_IDENTU, CODE_IDENTARG, + CODE_COM, CODE_COMD, CODE_COMC, CODE_COMV, + CODE_CONC, CODE_CONCW, CODE_CONCM, CODE_DOWN, + CODE_SVAR, CODE_SVARM, CODE_SVAR1, + CODE_IVAR, CODE_IVAR1, CODE_IVAR2, CODE_IVAR3, + CODE_FVAR, CODE_FVAR1, + CODE_LOOKUP, CODE_LOOKUPU, CODE_LOOKUPARG, + CODE_LOOKUPM, CODE_LOOKUPMU, CODE_LOOKUPMARG, + CODE_ALIAS, CODE_ALIASU, CODE_ALIASARG, + CODE_CALL, CODE_CALLU, CODE_CALLARG, + CODE_PRINT, + CODE_LOCAL, + CODE_DO, CODE_DOARGS, + CODE_JUMP, CODE_JUMP_TRUE, CODE_JUMP_FALSE, + CODE_JUMP_RESULT_TRUE, CODE_JUMP_RESULT_FALSE, + + CODE_OP_MASK = 0x3F, + CODE_RET = 6, + CODE_RET_MASK = 0xC0, + + /* return type flags */ + RET_NULL = VAL_NULL << CODE_RET, + RET_STR = VAL_STR << CODE_RET, + RET_INT = VAL_INT << CODE_RET, + RET_FLOAT = VAL_FLOAT << CODE_RET, +}; + +template +static void cs_do_args(CsState &cs, F body) { + IdentStack argstack[MaxArguments]; + int argmask1 = cs.stack->usedargs; + for (int i = 0; argmask1; argmask1 >>= 1, ++i) if(argmask1 & 1) + cs.identmap[i]->undo_arg(argstack[i]); + IdentLink *prevstack = cs.stack->next; + IdentLink aliaslink = { + cs.stack->id, cs.stack, prevstack->usedargs, prevstack->argstack + }; + cs.stack = &aliaslink; + body(); + prevstack->usedargs = aliaslink.usedargs; + cs.stack = aliaslink.next; + int argmask2 = cs.stack->usedargs; + for(int i = 0; argmask2; argmask2 >>= 1, ++i) if(argmask2 & 1) + cs.identmap[i]->redo_arg(argstack[i]); +} + +} /*namespace cscript */ + +#endif diff --git a/cubescript.cc b/cubescript.cc index d8357df..522783b 100644 --- a/cubescript.cc +++ b/cubescript.cc @@ -1,4 +1,6 @@ #include "cubescript.hh" +#include "cs_private.hh" +#include "lib_list.hh" #include #include @@ -10,57 +12,11 @@ namespace cscript { -static constexpr int MaxArguments = 25; -static constexpr int MaxResults = 7; -static constexpr int MaxComargs = 12; - -enum { - CODE_START = 0, - CODE_OFFSET, - CODE_NULL, CODE_TRUE, CODE_FALSE, CODE_NOT, - CODE_POP, - CODE_ENTER, CODE_ENTER_RESULT, - CODE_EXIT, CODE_RESULT_ARG, - CODE_VAL, CODE_VALI, - CODE_DUP, - CODE_MACRO, - CODE_BOOL, - CODE_BLOCK, CODE_EMPTY, - CODE_COMPILE, CODE_COND, - CODE_FORCE, - CODE_RESULT, - CODE_IDENT, CODE_IDENTU, CODE_IDENTARG, - CODE_COM, CODE_COMD, CODE_COMC, CODE_COMV, - CODE_CONC, CODE_CONCW, CODE_CONCM, CODE_DOWN, - CODE_SVAR, CODE_SVARM, CODE_SVAR1, - CODE_IVAR, CODE_IVAR1, CODE_IVAR2, CODE_IVAR3, - CODE_FVAR, CODE_FVAR1, - CODE_LOOKUP, CODE_LOOKUPU, CODE_LOOKUPARG, - CODE_LOOKUPM, CODE_LOOKUPMU, CODE_LOOKUPMARG, - CODE_ALIAS, CODE_ALIASU, CODE_ALIASARG, - CODE_CALL, CODE_CALLU, CODE_CALLARG, - CODE_PRINT, - CODE_LOCAL, - CODE_DO, CODE_DOARGS, - CODE_JUMP, CODE_JUMP_TRUE, CODE_JUMP_FALSE, - CODE_JUMP_RESULT_TRUE, CODE_JUMP_RESULT_FALSE, - - CODE_OP_MASK = 0x3F, - CODE_RET = 6, - CODE_RET_MASK = 0xC0, - - /* return type flags */ - RET_NULL = VAL_NULL << CODE_RET, - RET_STR = VAL_STR << CODE_RET, - RET_INT = VAL_INT << CODE_RET, - RET_FLOAT = VAL_FLOAT << CODE_RET, -}; - static inline int parseint(char const *s) { return int(strtoul(s, nullptr, 0)); } -static inline int cs_parse_int(ostd::ConstCharRange s) { +int cs_parse_int(ostd::ConstCharRange s) { if (s.empty()) return 0; return parseint(s.data()); } @@ -72,7 +28,7 @@ static inline float parsefloat(char const *s) { return val || end==s || (*end!='x' && *end!='X') ? float(val) : float(parseint(s)); } -static inline float cs_parse_float(ostd::ConstCharRange s) { +float cs_parse_float(ostd::ConstCharRange s) { if (s.empty()) return 0.0f; return parsefloat(s.data()); } @@ -96,7 +52,7 @@ ostd::String floatstr(float v) { return buf; } -inline char *cs_dup_ostr(ostd::ConstCharRange s) { +char *cs_dup_ostr(ostd::ConstCharRange s) { char *r = new char[s.size() + 1]; memcpy(r, s.data(), s.size()); r[s.size()] = 0; @@ -775,25 +731,6 @@ void Ident::set_alias(CsState &cs, TaggedValue &v) { flags = (flags & cs.identflags) | cs.identflags; } -template -static void cs_do_args(CsState &cs, F body) { - IdentStack argstack[MaxArguments]; - int argmask1 = cs.stack->usedargs; - for (int i = 0; argmask1; argmask1 >>= 1, ++i) if(argmask1 & 1) - cs.identmap[i]->undo_arg(argstack[i]); - IdentLink *prevstack = cs.stack->next; - IdentLink aliaslink = { - cs.stack->id, cs.stack, prevstack->usedargs, prevstack->argstack - }; - cs.stack = &aliaslink; - body(); - prevstack->usedargs = aliaslink.usedargs; - cs.stack = aliaslink.next; - int argmask2 = cs.stack->usedargs; - for(int i = 0; argmask2; argmask2 >>= 1, ++i) if(argmask2 & 1) - cs.identmap[i]->redo_arg(argstack[i]); -} - template bool cs_override_var(CsState &cs, Ident *id, SF sf, RF rf, CF cf) { if ((cs.identflags & IDF_OVERRIDDEN) || (id->flags & IDF_OVERRIDE)) { @@ -1066,58 +1003,6 @@ bool CsState::add_command(ostd::ConstCharRange name, ostd::ConstCharRange args, return true; } -static void cs_init_lib_base_var(CsState &cs) { - cs.add_command("nodebug", "e", [&cs](TvalRange args) { - ++cs.nodebug; - cs.run_ret(args[0].get_code()); - --cs.nodebug; - }); - - cs.add_command("push", "rTe", [&cs](TvalRange args) { - Ident *id = args[0].get_ident(); - if (id->type != ID_ALIAS || id->index < MaxArguments) return; - IdentStack stack; - TaggedValue &v = args[1]; - id->push_arg(v, stack); - v.set_null(); - cs.run_ret(args[2].get_code()); - id->pop_arg(); - }); - - cs.add_command("local", nullptr, nullptr, ID_LOCAL); - - cs.add_command("resetvar", "s", [&cs](TvalRange args) { - cs.result->set_int(cs.reset_var(args[0].get_strr())); - }); - - cs.add_command("alias", "sT", [&cs](TvalRange args) { - TaggedValue &v = args[1]; - cs.set_alias(args[0].get_strr(), v); - v.set_null(); - }); - - cs.add_command("getvarmin", "s", [&cs](TvalRange args) { - cs.result->set_int(cs.get_var_min_int(args[0].get_strr()).value_or(0)); - }); - cs.add_command("getvarmax", "s", [&cs](TvalRange args) { - cs.result->set_int(cs.get_var_max_int(args[0].get_strr()).value_or(0)); - }); - cs.add_command("getfvarmin", "s", [&cs](TvalRange args) { - cs.result->set_float(cs.get_var_min_float(args[0].get_strr()).value_or(0.0f)); - }); - cs.add_command("getfvarmax", "s", [&cs](TvalRange args) { - cs.result->set_float(cs.get_var_max_float(args[0].get_strr()).value_or(0.0f)); - }); - - cs.add_command("identexists", "s", [&cs](TvalRange args) { - cs.result->set_int(cs.have_ident(args[0].get_strr())); - }); - - cs.add_command("getalias", "s", [&cs](TvalRange args) { - cs.result->set_str(ostd::move(cs.get_alias(args[0].get_strr()).value_or(""))); - }); -} - char const *parsestring(char const *p) { for (; *p; p++) switch (*p) { case '\r': @@ -1229,7 +1114,7 @@ overflow: return buf; } -static inline char *conc(TvalRange v, bool space) { +char *conc(TvalRange v, bool space) { return conc(v, space, nullptr, 0); } @@ -1273,7 +1158,7 @@ static inline char *cutstring(char const *&p) { return buf; } -static inline char const *parseword(char const *p) { +char const *parseword(char const *p) { constexpr int maxbrak = 100; static char brakstack[maxbrak]; int brakdepth = 0; @@ -1550,16 +1435,16 @@ static inline bool cs_get_bool(ostd::ConstCharRange s) { return true; } -static inline bool cs_get_bool(TaggedValue const &v) { - switch (v.get_type()) { +bool TaggedValue::get_bool() const { + switch (get_type()) { case VAL_FLOAT: - return v.f != 0; + return f != 0; case VAL_INT: - return v.i != 0; + return i != 0; case VAL_STR: case VAL_MACRO: case VAL_CSTR: - return cs_get_bool(v.s); + return cs_get_bool(ostd::ConstCharRange(s, len)); default: return false; } @@ -2732,10 +2617,10 @@ static ostd::Uint32 const *runcode(CsState &cs, ostd::Uint32 const *code, Tagged #define RETPOP(op, val) \ RETOP(op, { --numargs; val; args[numargs].cleanup(); }) - RETPOP(CODE_NOT | RET_STR, result.set_str(cs_get_bool(args[numargs]) ? "0" : "1")) + RETPOP(CODE_NOT | RET_STR, result.set_str(args[numargs].get_bool() ? "0" : "1")) case CODE_NOT|RET_NULL: - RETPOP(CODE_NOT | RET_INT, result.set_int(cs_get_bool(args[numargs]) ? 0 : 1)) - RETPOP(CODE_NOT | RET_FLOAT, result.set_float(cs_get_bool(args[numargs]) ? 0.0f : 1.0f)) + RETPOP(CODE_NOT | RET_INT, result.set_int(args[numargs].get_bool() ? 0 : 1)) + RETPOP(CODE_NOT | RET_FLOAT, result.set_float(args[numargs].get_bool() ? 0.0f : 1.0f)) case CODE_POP: args[--numargs].cleanup(); @@ -2808,13 +2693,13 @@ static ostd::Uint32 const *runcode(CsState &cs, ostd::Uint32 const *code, Tagged } case CODE_JUMP_TRUE: { ostd::Uint32 len = op >> 8; - if (cs_get_bool(args[--numargs])) code += len; + if (args[--numargs].get_bool()) code += len; args[numargs].cleanup(); continue; } case CODE_JUMP_FALSE: { ostd::Uint32 len = op >> 8; - if (!cs_get_bool(args[--numargs])) code += len; + if (!args[--numargs].get_bool()) code += len; args[numargs].cleanup(); continue; } @@ -2826,7 +2711,7 @@ static ostd::Uint32 const *runcode(CsState &cs, ostd::Uint32 const *code, Tagged runcode(cs, args[numargs].code, result); args[numargs].cleanup(); } else result = args[numargs]; - if (cs_get_bool(result)) code += len; + if (result.get_bool()) code += len; continue; } case CODE_JUMP_RESULT_FALSE: { @@ -2837,7 +2722,7 @@ static ostd::Uint32 const *runcode(CsState &cs, ostd::Uint32 const *code, Tagged runcode(cs, args[numargs].code, result); args[numargs].cleanup(); } else result = args[numargs]; - if (!cs_get_bool(result)) code += len; + if (!result.get_bool()) code += len; continue; } @@ -3538,7 +3423,7 @@ float CsState::run_float(Ident *id, TvalRange args) { bool CsState::run_bool(ostd::Uint32 const *code) { TaggedValue ret; runcode(*this, code, ret); - bool b = cs_get_bool(ret); + bool b = ret.get_bool(); ret.cleanup(); return b; } @@ -3546,7 +3431,7 @@ bool CsState::run_bool(ostd::Uint32 const *code) { bool CsState::run_bool(ostd::ConstCharRange code) { TaggedValue ret; run_ret(code, ret); - bool b = cs_get_bool(ret); + bool b = ret.get_bool(); ret.cleanup(); return b; } @@ -3554,7 +3439,7 @@ bool CsState::run_bool(ostd::ConstCharRange code) { bool CsState::run_bool(Ident *id, TvalRange args) { TaggedValue ret; run_ret(id, args, ret); - bool b = cs_get_bool(ret); + bool b = ret.get_bool(); ret.cleanup(); return b; } @@ -3585,430 +3470,6 @@ bool CsState::run_file(ostd::ConstCharRange fname) { return true; } -static void cs_init_lib_io(CsState &cs) { - cs.add_command("exec", "sb", [&cs](TvalRange args) { - auto file = args[0].get_strr(); - bool ret = cs.run_file(file); - if (!ret) { - if (args[1].get_int()) - ostd::err.writefln("could not run file \"%s\"", file); - cs.result->set_int(0); - } else - cs.result->set_int(1); - }); - - cs.add_command("echo", "C", [](TvalRange args) { - ostd::writeln(args[0].get_strr()); - }); -} - -void cs_init_lib_base_loops(CsState &cs); - -static void cs_init_lib_base(CsState &cs) { - cs.add_command("do", "e", [&cs](TvalRange args) { - cs.run_ret(args[0].get_code()); - }, ID_DO); - - cs.add_command("doargs", "e", [&cs](TvalRange args) { - if (cs.stack != &cs.noalias) - cs_do_args(cs, [&]() { cs.run_ret(args[0].get_code()); }); - else - cs.run_ret(args[0].get_code()); - }, ID_DOARGS); - - cs.add_command("if", "tee", [&cs](TvalRange args) { - cs.run_ret((cs_get_bool(args[0]) ? args[1] : args[2]).get_code()); - }, ID_IF); - - cs.add_command("result", "T", [&cs](TvalRange args) { - TaggedValue &v = args[0]; - *cs.result = v; - v.set_null(); - }, ID_RESULT); - - cs.add_command("!", "t", [&cs](TvalRange args) { - cs.result->set_int(!cs_get_bool(args[0])); - }, ID_NOT); - - cs.add_command("&&", "E1V", [&cs](TvalRange args) { - if (args.empty()) - cs.result->set_int(1); - else for (ostd::Size i = 0; i < args.size(); ++i) { - if (i) cs.result->cleanup(); - if (args[i].get_type() == VAL_CODE) - cs.run_ret(args[i].code); - else - *cs.result = args[i]; - if (!cs_get_bool(*cs.result)) break; - } - }, ID_AND); - - cs.add_command("||", "E1V", [&cs](TvalRange args) { - if (args.empty()) - cs.result->set_int(0); - else for (ostd::Size i = 0; i < args.size(); ++i) { - if (i) cs.result->cleanup(); - if (args[i].get_type() == VAL_CODE) - cs.run_ret(args[i].code); - else - *cs.result = args[i]; - if (cs_get_bool(*cs.result)) break; - } - }, ID_OR); - - cs.add_command("?", "tTT", [&cs](TvalRange args) { - cs.result->set(cs_get_bool(args[0]) ? args[1] : args[2]); - }); - - cs.add_command("cond", "ee2V", [&cs](TvalRange args) { - for (ostd::Size i = 0; i < args.size(); i += 2) { - if ((i + 1) < args.size()) { - if (cs.run_bool(args[i].code)) { - cs.run_ret(args[i + 1].code); - break; - } - } else { - cs.run_ret(args[i].code); - break; - } - } - }); - -#define CS_CMD_CASE(name, fmt, type, acc, compare) \ - cs.add_command(name, fmt "te2V", [&cs](TvalRange args) { \ - type val = ostd::move(acc); \ - ostd::Size i; \ - for (i = 1; (i + 1) < args.size(); i += 2) { \ - if (compare) { \ - cs.run_ret(args[i + 1].code); \ - return; \ - } \ - } \ - }); - - CS_CMD_CASE("case", "i", int, args[0].get_int(), - ((args[i].get_type() == VAL_NULL) || - (args[i].get_int() == val))); - - CS_CMD_CASE("casef", "f", float, args[0].get_float(), - ((args[i].get_type() == VAL_NULL) || - (args[i].get_float() == val))); - - CS_CMD_CASE("cases", "s", ostd::String, args[0].get_str(), - ((args[i].get_type() == VAL_NULL) || - (args[i].get_str() == val))); - -#undef CS_CMD_CASE - - cs.add_command("pushif", "rTe", [&cs](TvalRange args) { - Ident *id = args[0].get_ident(); - TaggedValue &v = args[1]; - ostd::Uint32 *code = args[2].get_code(); - if ((id->type != ID_ALIAS) || (id->index < MaxArguments)) - return; - if (cs_get_bool(v)) { - IdentStack stack; - id->push_arg(v, stack); - v.set_null(); - cs.run_ret(code); - id->pop_arg(); - } - }); - - cs_init_lib_base_loops(cs); - cs_init_lib_base_var(cs); -} - -static inline void cs_set_iter(Ident &id, int i, IdentStack &stack) { - if (id.stack == &stack) { - if (id.get_valtype() != VAL_INT) { - if (id.get_valtype() == VAL_STR) { - delete[] id.val.s; - id.val.s = nullptr; - id.val.len = 0; - } - id.clean_code(); - id.valtype = VAL_INT; - } - id.val.i = i; - return; - } - TaggedValue v; - v.set_int(i); - id.push_arg(v, stack); -} - -static inline void cs_do_loop(CsState &cs, Ident &id, int offset, int n, - int step, ostd::Uint32 *cond, ostd::Uint32 *body) { - if (n <= 0 || (id.type != ID_ALIAS)) - return; - IdentStack stack; - for (int i = 0; i < n; ++i) { - cs_set_iter(id, offset + i * step, stack); - if (cond && !cs.run_bool(cond)) break; - cs.run_int(body); - } - id.pop_arg(); -} - -static inline void cs_loop_conc(CsState &cs, Ident &id, int offset, int n, - int step, ostd::Uint32 *body, bool space) { - if (n <= 0 || id.type != ID_ALIAS) - return; - IdentStack stack; - ostd::Vector s; - for (int i = 0; i < n; ++i) { - cs_set_iter(id, offset + i * step, stack); - TaggedValue v; - cs.run_ret(body, v); - ostd::String vstr = ostd::move(v.get_str()); - if (space && i) s.push(' '); - s.push_n(vstr.data(), vstr.size()); - v.cleanup(); - } - if (n > 0) id.pop_arg(); - s.push('\0'); - ostd::Size len = s.size() - 1; - cs.result->set_mstr(ostd::CharRange(s.disown(), len)); -} - -void cs_init_lib_base_loops(CsState &cs) { - cs.add_command("loop", "rie", [&cs](TvalRange args) { - cs_do_loop( - cs, *args[0].get_ident(), 0, args[1].get_int(), 1, nullptr, - args[2].get_code() - ); - }); - - cs.add_command("loop+", "riie", [&cs](TvalRange args) { - cs_do_loop( - cs, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1, - nullptr, args[3].get_code() - ); - }); - - cs.add_command("loop*", "riie", [&cs](TvalRange args) { - cs_do_loop( - cs, *args[0].get_ident(), 0, args[1].get_int(), args[2].get_int(), - nullptr, args[3].get_code() - ); - }); - - cs.add_command("loop+*", "riiie", [&cs](TvalRange args) { - cs_do_loop( - cs, *args[0].get_ident(), args[1].get_int(), args[3].get_int(), - args[2].get_int(), nullptr, args[4].get_code() - ); - }); - - cs.add_command("loopwhile", "riee", [&cs](TvalRange args) { - cs_do_loop( - cs, *args[0].get_ident(), 0, args[1].get_int(), 1, - args[2].get_code(), args[3].get_code() - ); - }); - - cs.add_command("loopwhile+", "riiee", [&cs](TvalRange args) { - cs_do_loop( - cs, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1, - args[3].get_code(), args[4].get_code() - ); - }); - - cs.add_command("loopwhile*", "riiee", [&cs](TvalRange args) { - cs_do_loop( - cs, *args[0].get_ident(), 0, args[2].get_int(), args[1].get_int(), - args[3].get_code(), args[4].get_code() - ); - }); - - cs.add_command("loopwhile+*", "riiiee", [&cs](TvalRange args) { - cs_do_loop( - cs, *args[0].get_ident(), args[1].get_int(), args[3].get_int(), - args[2].get_int(), args[4].get_code(), args[5].get_code() - ); - }); - - cs.add_command("while", "ee", [&cs](TvalRange args) { - ostd::Uint32 *cond = args[0].get_code(), *body = args[1].get_code(); - while (cs.run_bool(cond)) { - cs.run_int(body); - } - }); - - cs.add_command("loopconcat", "rie", [&cs](TvalRange args) { - cs_loop_conc( - cs, *args[0].get_ident(), 0, args[1].get_int(), 1, - args[2].get_code(), true - ); - }); - - cs.add_command("loopconcat+", "riie", [&cs](TvalRange args) { - cs_loop_conc( - cs, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1, - args[3].get_code(), true - ); - }); - - cs.add_command("loopconcat*", "riie", [&cs](TvalRange args) { - cs_loop_conc( - cs, *args[0].get_ident(), 0, args[2].get_int(), args[1].get_int(), - args[3].get_code(), true - ); - }); - - cs.add_command("loopconcat+*", "riiie", [&cs](TvalRange args) { - cs_loop_conc( - cs, *args[0].get_ident(), args[1].get_int(), args[3].get_int(), - args[2].get_int(), args[4].get_code(), true - ); - }); - - cs.add_command("loopconcatword", "rie", [&cs](TvalRange args) { - cs_loop_conc( - cs, *args[0].get_ident(), 0, args[1].get_int(), 1, - args[2].get_code(), false - ); - }); - - cs.add_command("loopconcatword+", "riie", [&cs](TvalRange args) { - cs_loop_conc( - cs, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1, - args[3].get_code(), false - ); - }); - - cs.add_command("loopconcatword*", "riie", [&cs](TvalRange args) { - cs_loop_conc( - cs, *args[0].get_ident(), 0, args[2].get_int(), args[1].get_int(), - args[3].get_code(), false - ); - }); - - cs.add_command("loopconcatword+*", "riiie", [&cs](TvalRange args) { - cs_loop_conc( - cs, *args[0].get_ident(), args[1].get_int(), args[3].get_int(), - args[2].get_int(), args[4].get_code(), false - ); - }); -} - -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() { - for (;;) { - while (!input.empty()) { - char c = input.front(); - if ((c == ' ') || (c == '\t') || (c == '\r') || (c == '\n')) - input.pop_front(); - else - break; - } - if ((input.size() < 2) || (input[0] != '/') || (input[1] != '/')) - break; - input = ostd::find(input, '\n'); - } - } - - bool parse() { - skip(); - if (input.empty()) - return false; - switch (input.front()) { - case '"': - quote = input; - input.pop_front(); - item = input; - input = cs_parse_str(input); - item = ostd::slice_until(item, input); - if (!input.empty() && (input.front() == '"')) - input.pop_front(); - quote = ostd::slice_until(quote, input); - break; - case '(': - case '[': { - quote = input; - input.pop_front(); - item = input; - char btype = quote.front(); - int brak = 1; - for (;;) { - input = ostd::find_one_of(input, - ostd::ConstCharRange("\"/;()[]")); - if (input.empty()) - return true; - char c = input.front(); - input.pop_front(); - switch (c) { - case '"': - input = cs_parse_str(input); - if (!input.empty() && (input.front() == '"')) - input.pop_front(); - break; - case '/': - if (!input.empty() && (input.front() == '/')) - input = ostd::find(input, '\n'); - break; - case '(': - case '[': - brak += (c == btype); - break; - case ')': - if ((btype == '(') && (--brak <= 0)) - goto endblock; - break; - case ']': - if ((btype == '[') && (--brak <= 0)) - goto endblock; - break; - } - } -endblock: - item = ostd::slice_until(item, input); - item.pop_back(); - quote = ostd::slice_until(quote, input); - break; - } - case ')': - case ']': - return false; - default: { - char const *e = parseword(input.data()); - item = input; - input.pop_front_n(e - input.data()); - item = ostd::slice_until(item, input); - quote = item; - break; - } - } - skip(); - if (!input.empty() && (input.front() == ';')) - input.pop_front(); - return true; - } - - ostd::String element() { - ostd::String s; - s.reserve(item.size()); - if (!quote.empty() && (quote.front() == '"')) { - auto writer = s.iter_cap(); - util::unescape_string(writer, item); - writer.put('\0'); - } else { - memcpy(s.data(), item.data(), item.size()); - s[item.size()] = '\0'; - } - s.advance(item.size()); - return s; - } -}; - namespace util { ostd::Size list_length(ostd::ConstCharRange s) { ListParser p(s); @@ -4037,961 +3498,11 @@ namespace util { } } -static inline void cs_set_iter(Ident &id, char *val, IdentStack &stack) { - if (id.stack == &stack) { - if (id.get_valtype() == VAL_STR) { - delete[] id.val.s; - } else { - id.valtype = VAL_STR; - } - id.clean_code(); - id.val.s = val; - id.val.len = strlen(val); - return; - } - TaggedValue v; - v.set_mstr(val); - id.push_arg(v, stack); -} - -static void cs_loop_list_conc(CsState &cs, Ident *id, ostd::ConstCharRange list, - ostd::Uint32 const *body, bool space) { - if (id->type != ID_ALIAS) - return; - IdentStack stack; - ostd::Vector r; - int n = 0; - for (ListParser p(list); p.parse(); ++n) { - char *val = p.element().disown(); - cs_set_iter(*id, val, stack); - if (n && space) - r.push(' '); - TaggedValue v; - cs.run_ret(body, v); - ostd::String vstr = ostd::move(v.get_str()); - r.push_n(vstr.data(), vstr.size()); - v.cleanup(); - } - if (n >= 0) - id->pop_arg(); - r.push('\0'); - ostd::Size len = r.size(); - cs.result->set_mstr(ostd::CharRange(r.disown(), len - 1)); -} - -int cs_list_includes(ostd::ConstCharRange list, ostd::ConstCharRange needle) { - int offset = 0; - for (ListParser p(list); p.parse();) { - if (p.item == needle) - return offset; - ++offset; - } - return -1; -} - -static void cs_init_lib_list_sort(CsState &cs); - -static void cs_init_lib_list(CsState &cs) { - cs.add_command("listlen", "s", [&cs](TvalRange args) { - cs.result->set_int(int(util::list_length(args[0].get_strr()))); - }); - - cs.add_command("at", "si1V", [&cs](TvalRange args) { - if (args.empty()) - return; - ostd::String str = ostd::move(args[0].get_str()); - ListParser p(str); - p.item = str; - for (ostd::Size i = 1; i < args.size(); ++i) { - p.input = str; - int pos = args[i].get_int(); - for (; pos > 0; --pos) - if (!p.parse()) break; - if (pos > 0 || !p.parse()) - p.item = p.quote = ostd::ConstCharRange(); - } - auto elem = p.element(); - auto er = p.element().iter(); - elem.disown(); - cs.result->set_mstr(er); - }); - - cs.add_command("sublist", "siiN", [&cs](TvalRange args) { - int skip = args[1].get_int(), - count = args[2].get_int(), - numargs = args[2].get_int(); - - int offset = ostd::max(skip, 0), - len = (numargs >= 3) ? ostd::max(count, 0) : -1; - - ListParser p(args[0].get_strr()); - for (int i = 0; i < offset; ++i) - if (!p.parse()) break; - if (len < 0) { - if (offset > 0) - p.skip(); - cs.result->set_str(p.input); - return; - } - - char const *list = p.input.data(); - p.quote = ostd::ConstCharRange(); - if (len > 0 && p.parse()) - while (--len > 0 && p.parse()); - char const *qend = !p.quote.empty() ? &p.quote[p.quote.size()] : list; - cs.result->set_str(ostd::ConstCharRange(list, qend - list)); - }); - - cs.add_command("listfind", "rse", [&cs](TvalRange args) { - Ident *id = args[0].get_ident(); - auto body = args[2].get_code(); - if (id->type != ID_ALIAS) { - cs.result->set_int(-1); - return; - } - IdentStack stack; - int n = -1; - for (ListParser p(args[1].get_strr()); p.parse();) { - ++n; - cs_set_iter(*id, cs_dup_ostr(p.item), stack); - if (cs.run_bool(body)) { - cs.result->set_int(n); - goto found; - } - } - cs.result->set_int(-1); -found: - if (n >= 0) - id->pop_arg(); - }); - - cs.add_command("listassoc", "rse", [&cs](TvalRange args) { - Ident *id = args[0].get_ident(); - auto body = args[2].get_code(); - if (id->type != ID_ALIAS) - return; - IdentStack stack; - int n = -1; - for (ListParser p(args[1].get_strr()); p.parse();) { - ++n; - cs_set_iter(*id, cs_dup_ostr(p.item), stack); - if (cs.run_bool(body)) { - if (p.parse()) { - auto elem = p.element(); - auto er = elem.iter(); - elem.disown(); - cs.result->set_mstr(er); - } - break; - } - if (!p.parse()) - break; - } - if (n >= 0) - id->pop_arg(); - }); - -#define CS_CMD_LIST_FIND(name, fmt, gmeth, cmp) \ - cs.add_command(name, "s" fmt "i", [&cs](TvalRange args) { \ - int n = 0, skip = args[2].get_int(); \ - auto val = args[1].gmeth(); \ - for (ListParser p(args[0].get_strr()); p.parse(); ++n) { \ - if (cmp) { \ - cs.result->set_int(n); \ - return; \ - } \ - for (int i = 0; i < skip; ++i) { \ - if (!p.parse()) \ - goto notfound; \ - ++n; \ - } \ - } \ - notfound: \ - cs.result->set_int(-1); \ - }); - - CS_CMD_LIST_FIND("listfind=", "i", get_int, cs_parse_int(p.item) == val); - CS_CMD_LIST_FIND("listfind=f", "f", get_float, cs_parse_float(p.item) == val); - CS_CMD_LIST_FIND("listfind=s", "s", get_strr, p.item == val); - -#undef CS_CMD_LIST_FIND - -#define CS_CMD_LIST_ASSOC(name, fmt, gmeth, cmp) \ - cs.add_command(name, "s" fmt, [&cs](TvalRange args) { \ - auto val = args[1].gmeth(); \ - for (ListParser p(args[0].get_strr()); p.parse();) { \ - if (cmp) { \ - if (p.parse()) { \ - auto elem = p.element(); \ - auto er = elem.iter(); \ - elem.disown(); \ - cs.result->set_mstr(er); \ - } \ - return; \ - } \ - if (!p.parse()) \ - break; \ - } \ - }); - - CS_CMD_LIST_ASSOC("listassoc=", "i", get_int, cs_parse_int(p.item) == val); - CS_CMD_LIST_ASSOC("listassoc=f", "f", get_float, cs_parse_float(p.item) == val); - CS_CMD_LIST_ASSOC("listassoc=s", "s", get_strr, p.item == val); - -#undef CS_CMD_LIST_ASSOC - - cs.add_command("looplist", "rse", [&cs](TvalRange args) { - Ident *id = args[0].get_ident(); - auto body = args[2].get_code(); - if (id->type != ID_ALIAS) - return; - IdentStack stack; - int n = 0; - for (ListParser p(args[1].get_strr()); p.parse(); ++n) { - cs_set_iter(*id, p.element().disown(), stack); - cs.run_int(body); - } - if (n >= 0) - id->pop_arg(); - }); - - cs.add_command("looplist2", "rrse", [&cs](TvalRange args) { - Ident *id = args[0].get_ident(), *id2 = args[1].get_ident(); - auto body = args[3].get_code(); - if (id->type != ID_ALIAS || id2->type != ID_ALIAS) - return; - IdentStack stack, stack2; - int n = 0; - for (ListParser p(args[2].get_strr()); p.parse(); n += 2) { - cs_set_iter(*id, p.element().disown(), stack); - cs_set_iter(*id2, p.parse() ? p.element().disown() - : cs_dup_ostr(""), stack2); - cs.run_int(body); - } - if (n >= 0) { - id->pop_arg(); - id2->pop_arg(); - } - }); - - cs.add_command("looplist3", "rrrse", [&cs](TvalRange args) { - Ident *id = args[0].get_ident(); - Ident *id2 = args[1].get_ident(); - Ident *id3 = args[2].get_ident(); - auto body = args[4].get_code(); - if (id->type != ID_ALIAS) - return; - if (id2->type != ID_ALIAS || id3->type != ID_ALIAS) - return; - IdentStack stack, stack2, stack3; - int n = 0; - for (ListParser p(args[3].get_strr()); p.parse(); n += 3) { - cs_set_iter(*id, p.element().disown(), stack); - cs_set_iter(*id2, p.parse() ? p.element().disown() - : cs_dup_ostr(""), stack2); - cs_set_iter(*id3, p.parse() ? p.element().disown() - : cs_dup_ostr(""), stack3); - cs.run_int(body); - } - if (n >= 0) { - id->pop_arg(); - id2->pop_arg(); - id3->pop_arg(); - } - }); - - cs.add_command("looplistconcat", "rse", [&cs](TvalRange args) { - cs_loop_list_conc( - cs, args[0].get_ident(), args[1].get_strr(), - args[2].get_code(), true - ); - }); - - cs.add_command("looplistconcatword", "rse", [&cs](TvalRange args) { - cs_loop_list_conc( - cs, args[0].get_ident(), args[1].get_strr(), - args[2].get_code(), false - ); - }); - - cs.add_command("listfilter", "rse", [&cs](TvalRange args) { - Ident *id = args[0].get_ident(); - auto body = args[2].get_code(); - if (id->type != ID_ALIAS) - return; - IdentStack stack; - ostd::Vector r; - int n = 0; - for (ListParser p(args[1].get_strr()); p.parse(); ++n) { - char *val = cs_dup_ostr(p.item); - cs_set_iter(*id, val, stack); - if (cs.run_bool(body)) { - if (r.size()) r.push(' '); - r.push_n(p.quote.data(), p.quote.size()); - } - } - if (n >= 0) - id->pop_arg(); - r.push('\0'); - ostd::Size len = r.size() - 1; - cs.result->set_mstr(ostd::CharRange(r.disown(), len)); - }); - - cs.add_command("listcount", "rse", [&cs](TvalRange args) { - Ident *id = args[0].get_ident(); - auto body = args[2].get_code(); - if (id->type != ID_ALIAS) - return; - IdentStack stack; - int n = 0, r = 0; - for (ListParser p(args[1].get_strr()); p.parse(); ++n) { - char *val = cs_dup_ostr(p.item); - cs_set_iter(*id, val, stack); - if (cs.run_bool(body)) - r++; - } - if (n >= 0) - id->pop_arg(); - cs.result->set_int(r); - }); - - cs.add_command("prettylist", "ss", [&cs](TvalRange args) { - ostd::Vector buf; - ostd::ConstCharRange s = args[0].get_strr(); - ostd::ConstCharRange conj = args[1].get_strr(); - ostd::Size len = util::list_length(s); - ostd::Size n = 0; - for (ListParser p(s); p.parse(); ++n) { - if (!p.quote.empty() && (p.quote.front() == '"')) { - buf.reserve(buf.size() + p.item.size()); - auto writer = ostd::CharRange(&buf[buf.size()], - buf.capacity() - buf.size()); - ostd::Size adv = util::unescape_string(writer, p.item); - writer.put('\0'); - buf.advance(adv); - } else { - buf.push_n(p.item.data(), p.item.size()); - } - if ((n + 1) < len) { - if ((len > 2) || conj.empty()) - buf.push(','); - if ((n + 2 == len) && !conj.empty()) { - buf.push(' '); - buf.push_n(conj.data(), conj.size()); - } - buf.push(' '); - } - } - buf.push('\0'); - ostd::Size slen = buf.size() - 1; - cs.result->set_mstr(ostd::CharRange(buf.disown(), slen)); - }); - - cs.add_command("indexof", "ss", [&cs](TvalRange args) { - cs.result->set_int( - cs_list_includes(args[0].get_strr(), args[1].get_strr()) - ); - }); - -#define CS_CMD_LIST_MERGE(name, init, iter, filter, dir) \ - cs.add_command(name, "ss", [&cs](TvalRange args) { \ - ostd::ConstCharRange list = args[0].get_strr(); \ - ostd::ConstCharRange elems = args[1].get_strr(); \ - ostd::Vector buf; \ - init; \ - for (ListParser p(iter); p.parse();) { \ - if (cs_list_includes(filter, p.item) dir 0) { \ - if (!buf.empty()) \ - buf.push(' '); \ - buf.push_n(p.quote.data(), p.quote.size()); \ - } \ - } \ - buf.push('\0'); \ - ostd::Size len = buf.size() - 1; \ - cs.result->set_mstr(ostd::CharRange(buf.disown(), len)); \ - }); - - CS_CMD_LIST_MERGE("listdel", {}, list, elems, <); - CS_CMD_LIST_MERGE("listintersect", {}, list, elems, >=); - CS_CMD_LIST_MERGE("listunion", buf.push_n(list.data(), list.size()), elems, - list, <); - -#undef CS_CMD_LIST_MERGE - - cs.add_command("listsplice", "ssii", [&cs](TvalRange args) { - int offset = ostd::max(args[2].get_int(), 0); - int len = ostd::max(args[3].get_int(), 0); - ostd::ConstCharRange s = args[0].get_strr(); - ostd::ConstCharRange vals = args[1].get_strr(); - char const *list = s.data(); - ListParser p(s); - for (int i = 0; i < offset; ++i) - if (!p.parse()) - break; - char const *qend = !p.quote.empty() ? &p.quote[p.quote.size()] : list; - ostd::Vector buf; - if (qend > list) - buf.push_n(list, qend - list); - if (!vals.empty()) { - if (!buf.empty()) - buf.push(' '); - buf.push_n(vals.data(), vals.size()); - } - for (int i = 0; i < len; ++i) - if (!p.parse()) - break; - p.skip(); - if (!p.input.empty()) switch (p.input.front()) { - case ')': - case ']': - break; - default: - if (!buf.empty()) - buf.push(' '); - buf.push_n(p.input.data(), p.input.size()); - break; - } - buf.push('\0'); - ostd::Size slen = buf.size() - 1; - cs.result->set_mstr(ostd::CharRange(buf.disown(), slen)); - }); - - cs_init_lib_list_sort(cs); -} - -struct ListSortItem { - char const *str; - ostd::ConstCharRange quote; -}; - -struct ListSortFun { - CsState &cs; - Ident *x, *y; - ostd::Uint32 *body; - - bool operator()(ListSortItem const &xval, ListSortItem const &yval) { - x->clean_code(); - if (x->get_valtype() != VAL_CSTR) { - x->valtype = VAL_CSTR; - } - x->val.cstr = xval.str; - x->val.len = strlen(xval.str); - y->clean_code(); - if (y->get_valtype() != VAL_CSTR) { - y->valtype = VAL_CSTR; - } - y->val.cstr = yval.str; - y->val.len = strlen(yval.str); - return cs.run_bool(body); - } -}; - -static void cs_list_sort( - CsState &cs, ostd::ConstCharRange list, Ident *x, Ident *y, - ostd::Uint32 *body, ostd::Uint32 *unique -) { - if (x == y || x->type != ID_ALIAS || y->type != ID_ALIAS) - return; - - ostd::Vector items; - ostd::Size clen = list.size(); - ostd::Size total = 0; - - char *cstr = cs_dup_ostr(list); - for (ListParser p(list); p.parse();) { - cstr[&p.item[p.item.size()] - list.data()] = '\0'; - ListSortItem item = { &cstr[p.item.data() - list.data()], p.quote }; - items.push(item); - total += item.quote.size(); - } - - if (items.empty()) { - cs.result->set_mstr(cstr); - return; - } - - IdentStack xstack, ystack; - x->push_arg(null_value, xstack); - y->push_arg(null_value, ystack); - - ostd::Size totaluniq = total; - ostd::Size nuniq = items.size(); - if (body) { - ListSortFun f = { cs, x, y, body }; - ostd::sort_cmp(items.iter(), f); - if ((*unique & CODE_OP_MASK) != CODE_EXIT) { - f.body = unique; - totaluniq = items[0].quote.size(); - nuniq = 1; - for (ostd::Size i = 1; i < items.size(); i++) { - ListSortItem &item = items[i]; - if (f(items[i - 1], item)) - item.quote = nullptr; - else { - totaluniq += item.quote.size(); - ++nuniq; - } - } - } - } else { - ListSortFun f = { cs, x, y, unique }; - totaluniq = items[0].quote.size(); - nuniq = 1; - for (ostd::Size i = 1; i < items.size(); i++) { - ListSortItem &item = items[i]; - for (ostd::Size j = 0; j < i; ++j) { - ListSortItem &prev = items[j]; - if (!prev.quote.empty() && f(item, prev)) { - item.quote = nullptr; - break; - } - } - if (!item.quote.empty()) { - totaluniq += item.quote.size(); - ++nuniq; - } - } - } - - x->pop_arg(); - y->pop_arg(); - - char *sorted = cstr; - ostd::Size sortedlen = totaluniq + ostd::max(nuniq - 1, ostd::Size(0)); - if (clen < sortedlen) { - delete[] cstr; - sorted = new char[sortedlen + 1]; - } - - ostd::Size offset = 0; - for (ostd::Size i = 0; i < items.size(); ++i) { - ListSortItem &item = items[i]; - if (item.quote.empty()) - continue; - if (i) - sorted[offset++] = ' '; - memcpy(&sorted[offset], item.quote.data(), item.quote.size()); - offset += item.quote.size(); - } - sorted[offset] = '\0'; - - cs.result->set_mstr(sorted); -} - -static void cs_init_lib_list_sort(CsState &cs) { - cs.add_command("sortlist", "srree", [&cs](TvalRange args) { - cs_list_sort( - cs, args[0].get_strr(), args[1].get_ident(), args[2].get_ident(), - args[3].get_code(), args[4].get_code() - ); - }); - cs.add_command("uniquelist", "srre", [&cs](TvalRange args) { - cs_list_sort( - cs, args[0].get_strr(), args[1].get_ident(), args[2].get_ident(), - nullptr, args[3].get_code() - ); - }); -} - -static constexpr float PI = 3.14159265358979f; -static constexpr float RAD = PI / 180.0f; - -static void cs_init_lib_math(CsState &cs) { - cs.add_command("sin", "f", [&cs](TvalRange args) { - cs.result->set_float(sin(args[0].get_float() * RAD)); - }); - cs.add_command("cos", "f", [&cs](TvalRange args) { - cs.result->set_float(cos(args[0].get_float() * RAD)); - }); - cs.add_command("tan", "f", [&cs](TvalRange args) { - cs.result->set_float(tan(args[0].get_float() * RAD)); - }); - - cs.add_command("asin", "f", [&cs](TvalRange args) { - cs.result->set_float(asin(args[0].get_float()) / RAD); - }); - cs.add_command("acos", "f", [&cs](TvalRange args) { - cs.result->set_float(acos(args[0].get_float()) / RAD); - }); - cs.add_command("atan", "f", [&cs](TvalRange args) { - cs.result->set_float(atan(args[0].get_float()) / RAD); - }); - cs.add_command("atan2", "ff", [&cs](TvalRange args) { - cs.result->set_float(atan2(args[0].get_float(), args[1].get_float()) / RAD); - }); - - cs.add_command("sqrt", "f", [&cs](TvalRange args) { - cs.result->set_float(sqrt(args[0].get_float())); - }); - cs.add_command("loge", "f", [&cs](TvalRange args) { - cs.result->set_float(log(args[0].get_float())); - }); - cs.add_command("log2", "f", [&cs](TvalRange args) { - cs.result->set_float(log(args[0].get_float()) / M_LN2); - }); - cs.add_command("log10", "f", [&cs](TvalRange args) { - cs.result->set_float(log10(args[0].get_float())); - }); - - cs.add_command("exp", "f", [&cs](TvalRange args) { - cs.result->set_float(exp(args[0].get_float())); - }); - -#define CS_CMD_MIN_MAX(name, fmt, type, op) \ - cs.add_command(#name, #fmt "1V", [&cs](TvalRange args) { \ - type v = !args.empty() ? args[0].fmt : 0; \ - for (ostd::Size i = 1; i < args.size(); ++i) v = op(v, args[i].fmt); \ - cs.result->set_##type(v); \ - }) - - CS_CMD_MIN_MAX(min, i, int, ostd::min); - CS_CMD_MIN_MAX(max, i, int, ostd::max); - CS_CMD_MIN_MAX(minf, f, float, ostd::min); - CS_CMD_MIN_MAX(maxf, f, float, ostd::max); - -#undef CS_CMD_MIN_MAX - - cs.add_command("abs", "i", [&cs](TvalRange args) { - cs.result->set_int(abs(args[0].get_int())); - }); - cs.add_command("absf", "f", [&cs](TvalRange args) { - cs.result->set_float(fabs(args[0].get_float())); - }); - - cs.add_command("floor", "f", [&cs](TvalRange args) { - cs.result->set_float(floor(args[0].get_float())); - }); - cs.add_command("ceil", "f", [&cs](TvalRange args) { - cs.result->set_float(ceil(args[0].get_float())); - }); - - cs.add_command("round", "ff", [&cs](TvalRange args) { - double step = args[1].get_float(); - double r = args[0].get_float(); - if (step > 0) { - r += step * ((r < 0) ? -0.5 : 0.5); - r -= fmod(r, step); - } else { - r = (r < 0) ? ceil(r - 0.5) : floor(r + 0.5); - } - cs.result->set_float(float(r)); - }); - -#define CS_CMD_MATH(name, fmt, type, op, initval, unaryop) \ - cs.add_command(name, #fmt "1V", [&cs](TvalRange args) { \ - type val; \ - if (args.size() >= 2) { \ - val = args[0].fmt; \ - type val2 = args[1].fmt; \ - op; \ - for (ostd::Size i = 2; i < args.size(); ++i) { \ - val2 = args[i].fmt; \ - op; \ - } \ - } else { \ - val = (args.size() > 0) ? args[0].fmt : initval; \ - unaryop; \ - } \ - cs.result->set_##type(val); \ - }); - -#define CS_CMD_MATHIN(name, op, initval, unaryop) \ - CS_CMD_MATH(#name, i, int, val = val op val2, initval, unaryop) - -#define CS_CMD_MATHI(name, initval, unaryop) \ - CS_CMD_MATHIN(name, name, initval, unaryop) - -#define CS_CMD_MATHFN(name, op, initval, unaryop) \ - CS_CMD_MATH(#name "f", f, float, val = val op val2, initval, unaryop) - -#define CS_CMD_MATHF(name, initval, unaryop) \ - CS_CMD_MATHFN(name, name, initval, unaryop) - - CS_CMD_MATHI(+, 0, {}); - CS_CMD_MATHI(*, 1, {}); - CS_CMD_MATHI(-, 0, val = -val); - - CS_CMD_MATHI(^, 0, val = ~val); - CS_CMD_MATHIN(~, ^, 0, val = ~val); - CS_CMD_MATHI(&, 0, {}); - CS_CMD_MATHI(|, 0, {}); - CS_CMD_MATHI(^~, 0, {}); - CS_CMD_MATHI(&~, 0, {}); - CS_CMD_MATHI(|~, 0, {}); - - CS_CMD_MATH("<<", i, int, { - val = (val2 < 32) ? (val << ostd::max(val2, 0)) : 0; - }, 0, {}); - CS_CMD_MATH(">>", i, int, val >>= ostd::clamp(val2, 0, 31), 0, {}); - - CS_CMD_MATHF(+, 0, {}); - CS_CMD_MATHF(*, 1, {}); - CS_CMD_MATHF(-, 0, val = -val); - -#define CS_CMD_DIV(name, fmt, type, op) \ - CS_CMD_MATH(#name, fmt, type, { if (val2) op; else val = 0; }, 0, {}) - - CS_CMD_DIV(div, i, int, val /= val2); - CS_CMD_DIV(mod, i, int, val %= val2); - CS_CMD_DIV(divf, f, float, val /= val2); - CS_CMD_DIV(modf, f, float, val = fmod(val, val2)); - -#undef CS_CMD_DIV - - CS_CMD_MATH("pow", f, float, val = pow(val, val2), 0, {}); - -#undef CS_CMD_MATHF -#undef CS_CMD_MATHFN -#undef CS_CMD_MATHI -#undef CS_CMD_MATHIN -#undef CS_CMD_MATH - -#define CS_CMD_CMP(name, fmt, type, op) \ - cs.add_command(name, #fmt "1V", [&cs](TvalRange args) { \ - bool val; \ - if (args.size() >= 2) { \ - val = args[0].fmt op args[1].fmt; \ - for (ostd::Size i = 2; i < args.size() && val; ++i) \ - val = args[i-1].fmt op args[i].fmt; \ - } else \ - val = ((args.size() > 0) ? args[0].fmt : 0) op 0; \ - cs.result->set_int(int(val)); \ - }) - -#define CS_CMD_CMPIN(name, op) CS_CMD_CMP(#name, i, int, op) -#define CS_CMD_CMPI(name) CS_CMD_CMPIN(name, name) -#define CS_CMD_CMPFN(name, op) CS_CMD_CMP(#name "f", f, float, op) -#define CS_CMD_CMPF(name) CS_CMD_CMPFN(name, name) - - CS_CMD_CMPIN(=, ==); - CS_CMD_CMPI(!=); - CS_CMD_CMPI(<); - CS_CMD_CMPI(>); - CS_CMD_CMPI(<=); - CS_CMD_CMPI(>=); - - CS_CMD_CMPFN(=, ==); - CS_CMD_CMPF(!=); - CS_CMD_CMPF(<); - CS_CMD_CMPF(>); - CS_CMD_CMPF(<=); - CS_CMD_CMPF(>=); - -#undef CS_CMD_CMPF -#undef CS_CMD_CMPFN -#undef CS_CMD_CMPI -#undef CS_CMD_CMPIN -#undef CS_CMD_CMP -} - -static void cs_init_lib_string(CsState &cs) { - cs.add_command("strstr", "ss", [&cs](TvalRange args) { - ostd::ConstCharRange a = args[0].get_strr(), b = args[1].get_strr(); - ostd::ConstCharRange s = a; - for (int i = 0; b.size() <= s.size(); ++i) { - if (b == s.slice(0, b.size())) { - cs.result->set_int(i); - return; - } - s.pop_front(); - } - cs.result->set_int(-1); - }); - - cs.add_command("strlen", "s", [&cs](TvalRange args) { - cs.result->set_int(int(args[0].get_strr().size())); - }); - - cs.add_command("strcode", "si", [&cs](TvalRange args) { - ostd::ConstCharRange str = args[0].get_strr(); - int i = args[1].get_int(); - if (i >= int(str.size())) { - cs.result->set_int(0); - } else { - cs.result->set_int(ostd::byte(str[i])); - } - }); - - cs.add_command("codestr", "i", [&cs](TvalRange args) { - char *s = new char[2]; - s[0] = char(args[0].get_int()); - s[1] = '\0'; - cs.result->set_mstr(s); - }); - - cs.add_command("strlower", "s", [&cs](TvalRange args) { - ostd::ConstCharRange s = args[0].get_strr(); - char *buf = new char[s.size() + 1]; - for (auto i: ostd::range(s.size())) - buf[i] = tolower(s[i]); - buf[s.size()] = '\0'; - cs.result->set_mstr(ostd::CharRange(buf, s.size())); - }); - - cs.add_command("strupper", "s", [&cs](TvalRange args) { - ostd::ConstCharRange s = args[0].get_strr(); - char *buf = new char[s.size() + 1]; - for (auto i: ostd::range(s.size())) - buf[i] = toupper(s[i]); - buf[s.size()] = '\0'; - cs.result->set_mstr(ostd::CharRange(buf, s.size())); - }); - - cs.add_command("escape", "s", [&cs](TvalRange args) { - auto x = ostd::appender(); - util::escape_string(x, args[0].get_strr()); - ostd::Size len = x.size(); - cs.result->set_mstr(ostd::CharRange(x.get().disown(), len)); - }); - - cs.add_command("unescape", "s", [&cs](TvalRange args) { - ostd::ConstCharRange s = args[0].get_strr(); - char *buf = new char[s.size() + 1]; - auto writer = ostd::CharRange(buf, s.size() + 1); - util::unescape_string(writer, s); - writer.put('\0'); - cs.result->set_mstr(ostd::CharRange(buf, s.size())); - }); - - cs.add_command("concat", "V", [&cs](TvalRange args) { - cs.result->set_mstr(conc(args, true)); - }); - - cs.add_command("concatworld", "V", [&cs](TvalRange args) { - cs.result->set_mstr(conc(args, false)); - }); - - cs.add_command("format", "V", [&cs](TvalRange args) { - if (args.empty()) - return; - ostd::Vector s; - ostd::String fs = ostd::move(args[0].get_str()); - ostd::ConstCharRange f = fs.iter(); - while (!f.empty()) { - char c = f.front(); - f.pop_front(); - if ((c == '%') && !f.empty()) { - char ic = f.front(); - f.pop_front(); - if (ic >= '1' && ic <= '9') { - int i = ic - '0'; - ostd::String sub = ostd::move((i < int(args.size())) - ? args[i].get_str() : ostd::String("")); - s.push_n(sub.data(), sub.size()); - } else s.push(ic); - } else s.push(c); - } - s.push('\0'); - ostd::Size len = s.size() - 1; - cs.result->set_mstr(ostd::CharRange(s.disown(), len)); - }); - - cs.add_command("tohex", "ii", [&cs](TvalRange args) { - auto r = ostd::appender>(); - ostd::format(r, "0x%.*X", ostd::max(args[1].get_int(), 1), args[0].get_int()); - r.put('\0'); - ostd::Size len = r.size() - 1; - cs.result->set_mstr(ostd::CharRange(r.get().disown(), len)); - }); - - cs.add_command("substr", "siiN", [&cs](TvalRange args) { - ostd::ConstCharRange s = args[0].get_strr(); - int start = args[1].get_int(), count = args[2].get_int(); - int numargs = args[3].get_int(); - int len = int(s.size()), offset = ostd::clamp(start, 0, len); - cs.result->set_str(ostd::ConstCharRange( - &s[offset], - (numargs >= 3) ? ostd::clamp(count, 0, len - offset) - : (len - offset) - )); - }); - -#define CS_CMD_CMPS(name, op) \ - cs.add_command(#name, "s1V", [&cs](TvalRange args) { \ - bool val; \ - if (args.size() >= 2) { \ - val = strcmp(args[0].s, args[1].s) op 0; \ - for (ostd::Size i = 2; i < args.size() && val; ++i) \ - val = strcmp(args[i-1].s, args[i].s) op 0; \ - } else \ - val = (!args.empty() ? args[0].s[0] : 0) op 0; \ - cs.result->set_int(int(val)); \ - }) - - CS_CMD_CMPS(strcmp, ==); - CS_CMD_CMPS(=s, ==); - CS_CMD_CMPS(!=s, !=); - CS_CMD_CMPS(s, >); - CS_CMD_CMPS(<=s, <=); - CS_CMD_CMPS(>=s, >=); - -#undef CS_CMD_CMPS - - cs.add_command("strreplace", "ssss", [&cs](TvalRange args) { - ostd::ConstCharRange s = args[0].get_strr(); - ostd::ConstCharRange oldval = args[1].get_strr(), - newval = args[2].get_strr(), - newval2 = args[3].get_strr(); - if (newval2.empty()) { - newval2 = newval; - } - ostd::Vector buf; - if (!oldval.size()) { - cs.result->set_str(s); - return; - } - for (ostd::Size i = 0;; ++i) { - ostd::ConstCharRange found; - ostd::ConstCharRange trys = s; - for (; oldval.size() <= trys.size(); trys.pop_front()) { - if (trys.slice(0, oldval.size()) == oldval) { - found = trys; - break; - } - } - if (!found.empty()) { - auto bef = ostd::slice_until(s, found); - for (; !bef.empty(); bef.pop_front()) { - buf.push(bef.front()); - } - auto use = (i & 1) ? newval2 : newval; - for (; !use.empty(); use.pop_front()) { - buf.push(use.front()); - } - s = found + oldval.size(); - } else { - for (; !s.empty(); s.pop_front()) { - buf.push(s.front()); - } - buf.push('\0'); - ostd::Size len = buf.size() - 1; - cs.result->set_mstr(ostd::CharRange(buf.disown(), len)); - return; - } - } - }); - - cs.add_command("strsplice", "ssii", [&cs](TvalRange args) { - ostd::ConstCharRange s = args[0].get_strr(); - ostd::ConstCharRange vals = args[1].get_strr(); - int skip = args[2].get_int(), - count = args[3].get_int(); - int slen = int(s.size()), - vlen = int(vals.size()); - int offset = ostd::clamp(skip, 0, slen), - len = ostd::clamp(count, 0, slen - offset); - char *p = new char[slen - len + vlen + 1]; - if (offset) - memcpy(p, s.data(), offset); - if (vlen) - memcpy(&p[offset], vals.data(), vlen); - if (offset + len < slen) - memcpy(&p[offset + vlen], &s[offset + len], slen - (offset + len)); - p[slen - len + vlen] = '\0'; - cs.result->set_mstr(ostd::CharRange(p, slen - len + vlen)); - }); -} +void cs_init_lib_math(CsState &cs); +void cs_init_lib_string(CsState &cs); +void cs_init_lib_list(CsState &cs); +void cs_init_lib_io(CsState &cs); +void cs_init_lib_base(CsState &cs); OSTD_EXPORT void init_libs(CsState &cs, int libs) { if (libs & CS_LIB_BASE ) cs_init_lib_base(cs); diff --git a/cubescript.hh b/cubescript.hh index d5023af..63e9d8d 100644 --- a/cubescript.hh +++ b/cubescript.hh @@ -135,6 +135,8 @@ struct OSTD_EXPORT TaggedValue: IdentValue { Ident *get_ident() const; void get_val(TaggedValue &r) const; + bool get_bool() const; + void force_null(); float force_float(); int force_int(); diff --git a/lib_base.cc b/lib_base.cc new file mode 100644 index 0000000..2fdeaa7 --- /dev/null +++ b/lib_base.cc @@ -0,0 +1,366 @@ +#include "cubescript.hh" +#include "cs_private.hh" + +namespace cscript { + +static void cs_init_lib_base_var(CsState &cs) { + cs.add_command("nodebug", "e", [&cs](TvalRange args) { + ++cs.nodebug; + cs.run_ret(args[0].get_code()); + --cs.nodebug; + }); + + cs.add_command("push", "rTe", [&cs](TvalRange args) { + Ident *id = args[0].get_ident(); + if (id->type != ID_ALIAS || id->index < MaxArguments) return; + IdentStack stack; + TaggedValue &v = args[1]; + id->push_arg(v, stack); + v.set_null(); + cs.run_ret(args[2].get_code()); + id->pop_arg(); + }); + + cs.add_command("local", nullptr, nullptr, ID_LOCAL); + + cs.add_command("resetvar", "s", [&cs](TvalRange args) { + cs.result->set_int(cs.reset_var(args[0].get_strr())); + }); + + cs.add_command("alias", "sT", [&cs](TvalRange args) { + TaggedValue &v = args[1]; + cs.set_alias(args[0].get_strr(), v); + v.set_null(); + }); + + cs.add_command("getvarmin", "s", [&cs](TvalRange args) { + cs.result->set_int(cs.get_var_min_int(args[0].get_strr()).value_or(0)); + }); + cs.add_command("getvarmax", "s", [&cs](TvalRange args) { + cs.result->set_int(cs.get_var_max_int(args[0].get_strr()).value_or(0)); + }); + cs.add_command("getfvarmin", "s", [&cs](TvalRange args) { + cs.result->set_float(cs.get_var_min_float(args[0].get_strr()).value_or(0.0f)); + }); + cs.add_command("getfvarmax", "s", [&cs](TvalRange args) { + cs.result->set_float(cs.get_var_max_float(args[0].get_strr()).value_or(0.0f)); + }); + + cs.add_command("identexists", "s", [&cs](TvalRange args) { + cs.result->set_int(cs.have_ident(args[0].get_strr())); + }); + + cs.add_command("getalias", "s", [&cs](TvalRange args) { + cs.result->set_str(ostd::move(cs.get_alias(args[0].get_strr()).value_or(""))); + }); +} + +void cs_init_lib_io(CsState &cs) { + cs.add_command("exec", "sb", [&cs](TvalRange args) { + auto file = args[0].get_strr(); + bool ret = cs.run_file(file); + if (!ret) { + if (args[1].get_int()) + ostd::err.writefln("could not run file \"%s\"", file); + cs.result->set_int(0); + } else + cs.result->set_int(1); + }); + + cs.add_command("echo", "C", [](TvalRange args) { + ostd::writeln(args[0].get_strr()); + }); +} + +static void cs_init_lib_base_loops(CsState &cs); + +void cs_init_lib_base(CsState &cs) { + cs.add_command("do", "e", [&cs](TvalRange args) { + cs.run_ret(args[0].get_code()); + }, ID_DO); + + cs.add_command("doargs", "e", [&cs](TvalRange args) { + if (cs.stack != &cs.noalias) + cs_do_args(cs, [&]() { cs.run_ret(args[0].get_code()); }); + else + cs.run_ret(args[0].get_code()); + }, ID_DOARGS); + + cs.add_command("if", "tee", [&cs](TvalRange args) { + cs.run_ret((args[0].get_bool() ? args[1] : args[2]).get_code()); + }, ID_IF); + + cs.add_command("result", "T", [&cs](TvalRange args) { + TaggedValue &v = args[0]; + *cs.result = v; + v.set_null(); + }, ID_RESULT); + + cs.add_command("!", "t", [&cs](TvalRange args) { + cs.result->set_int(!args[0].get_bool()); + }, ID_NOT); + + cs.add_command("&&", "E1V", [&cs](TvalRange args) { + if (args.empty()) + cs.result->set_int(1); + else for (ostd::Size i = 0; i < args.size(); ++i) { + if (i) cs.result->cleanup(); + if (args[i].get_type() == VAL_CODE) + cs.run_ret(args[i].code); + else + *cs.result = args[i]; + if (!cs.result->get_bool()) break; + } + }, ID_AND); + + cs.add_command("||", "E1V", [&cs](TvalRange args) { + if (args.empty()) + cs.result->set_int(0); + else for (ostd::Size i = 0; i < args.size(); ++i) { + if (i) cs.result->cleanup(); + if (args[i].get_type() == VAL_CODE) + cs.run_ret(args[i].code); + else + *cs.result = args[i]; + if (cs.result->get_bool()) break; + } + }, ID_OR); + + cs.add_command("?", "tTT", [&cs](TvalRange args) { + cs.result->set(args[0].get_bool() ? args[1] : args[2]); + }); + + cs.add_command("cond", "ee2V", [&cs](TvalRange args) { + for (ostd::Size i = 0; i < args.size(); i += 2) { + if ((i + 1) < args.size()) { + if (cs.run_bool(args[i].code)) { + cs.run_ret(args[i + 1].code); + break; + } + } else { + cs.run_ret(args[i].code); + break; + } + } + }); + +#define CS_CMD_CASE(name, fmt, type, acc, compare) \ + cs.add_command(name, fmt "te2V", [&cs](TvalRange args) { \ + type val = ostd::move(acc); \ + ostd::Size i; \ + for (i = 1; (i + 1) < args.size(); i += 2) { \ + if (compare) { \ + cs.run_ret(args[i + 1].code); \ + return; \ + } \ + } \ + }); + + CS_CMD_CASE("case", "i", int, args[0].get_int(), + ((args[i].get_type() == VAL_NULL) || + (args[i].get_int() == val))); + + CS_CMD_CASE("casef", "f", float, args[0].get_float(), + ((args[i].get_type() == VAL_NULL) || + (args[i].get_float() == val))); + + CS_CMD_CASE("cases", "s", ostd::String, args[0].get_str(), + ((args[i].get_type() == VAL_NULL) || + (args[i].get_str() == val))); + +#undef CS_CMD_CASE + + cs.add_command("pushif", "rTe", [&cs](TvalRange args) { + Ident *id = args[0].get_ident(); + TaggedValue &v = args[1]; + ostd::Uint32 *code = args[2].get_code(); + if ((id->type != ID_ALIAS) || (id->index < MaxArguments)) + return; + if (v.get_bool()) { + IdentStack stack; + id->push_arg(v, stack); + v.set_null(); + cs.run_ret(code); + id->pop_arg(); + } + }); + + cs_init_lib_base_loops(cs); + cs_init_lib_base_var(cs); +} + +static inline void cs_set_iter(Ident &id, int i, IdentStack &stack) { + if (id.stack == &stack) { + if (id.get_valtype() != VAL_INT) { + if (id.get_valtype() == VAL_STR) { + delete[] id.val.s; + id.val.s = nullptr; + id.val.len = 0; + } + id.clean_code(); + id.valtype = VAL_INT; + } + id.val.i = i; + return; + } + TaggedValue v; + v.set_int(i); + id.push_arg(v, stack); +} + +static inline void cs_do_loop(CsState &cs, Ident &id, int offset, int n, + int step, ostd::Uint32 *cond, ostd::Uint32 *body) { + if (n <= 0 || (id.type != ID_ALIAS)) + return; + IdentStack stack; + for (int i = 0; i < n; ++i) { + cs_set_iter(id, offset + i * step, stack); + if (cond && !cs.run_bool(cond)) break; + cs.run_int(body); + } + id.pop_arg(); +} + +static inline void cs_loop_conc(CsState &cs, Ident &id, int offset, int n, + int step, ostd::Uint32 *body, bool space) { + if (n <= 0 || id.type != ID_ALIAS) + return; + IdentStack stack; + ostd::Vector s; + for (int i = 0; i < n; ++i) { + cs_set_iter(id, offset + i * step, stack); + TaggedValue v; + cs.run_ret(body, v); + ostd::String vstr = ostd::move(v.get_str()); + if (space && i) s.push(' '); + s.push_n(vstr.data(), vstr.size()); + v.cleanup(); + } + if (n > 0) id.pop_arg(); + s.push('\0'); + ostd::Size len = s.size() - 1; + cs.result->set_mstr(ostd::CharRange(s.disown(), len)); +} + +static void cs_init_lib_base_loops(CsState &cs) { + cs.add_command("loop", "rie", [&cs](TvalRange args) { + cs_do_loop( + cs, *args[0].get_ident(), 0, args[1].get_int(), 1, nullptr, + args[2].get_code() + ); + }); + + cs.add_command("loop+", "riie", [&cs](TvalRange args) { + cs_do_loop( + cs, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1, + nullptr, args[3].get_code() + ); + }); + + cs.add_command("loop*", "riie", [&cs](TvalRange args) { + cs_do_loop( + cs, *args[0].get_ident(), 0, args[1].get_int(), args[2].get_int(), + nullptr, args[3].get_code() + ); + }); + + cs.add_command("loop+*", "riiie", [&cs](TvalRange args) { + cs_do_loop( + cs, *args[0].get_ident(), args[1].get_int(), args[3].get_int(), + args[2].get_int(), nullptr, args[4].get_code() + ); + }); + + cs.add_command("loopwhile", "riee", [&cs](TvalRange args) { + cs_do_loop( + cs, *args[0].get_ident(), 0, args[1].get_int(), 1, + args[2].get_code(), args[3].get_code() + ); + }); + + cs.add_command("loopwhile+", "riiee", [&cs](TvalRange args) { + cs_do_loop( + cs, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1, + args[3].get_code(), args[4].get_code() + ); + }); + + cs.add_command("loopwhile*", "riiee", [&cs](TvalRange args) { + cs_do_loop( + cs, *args[0].get_ident(), 0, args[2].get_int(), args[1].get_int(), + args[3].get_code(), args[4].get_code() + ); + }); + + cs.add_command("loopwhile+*", "riiiee", [&cs](TvalRange args) { + cs_do_loop( + cs, *args[0].get_ident(), args[1].get_int(), args[3].get_int(), + args[2].get_int(), args[4].get_code(), args[5].get_code() + ); + }); + + cs.add_command("while", "ee", [&cs](TvalRange args) { + ostd::Uint32 *cond = args[0].get_code(), *body = args[1].get_code(); + while (cs.run_bool(cond)) { + cs.run_int(body); + } + }); + + cs.add_command("loopconcat", "rie", [&cs](TvalRange args) { + cs_loop_conc( + cs, *args[0].get_ident(), 0, args[1].get_int(), 1, + args[2].get_code(), true + ); + }); + + cs.add_command("loopconcat+", "riie", [&cs](TvalRange args) { + cs_loop_conc( + cs, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1, + args[3].get_code(), true + ); + }); + + cs.add_command("loopconcat*", "riie", [&cs](TvalRange args) { + cs_loop_conc( + cs, *args[0].get_ident(), 0, args[2].get_int(), args[1].get_int(), + args[3].get_code(), true + ); + }); + + cs.add_command("loopconcat+*", "riiie", [&cs](TvalRange args) { + cs_loop_conc( + cs, *args[0].get_ident(), args[1].get_int(), args[3].get_int(), + args[2].get_int(), args[4].get_code(), true + ); + }); + + cs.add_command("loopconcatword", "rie", [&cs](TvalRange args) { + cs_loop_conc( + cs, *args[0].get_ident(), 0, args[1].get_int(), 1, + args[2].get_code(), false + ); + }); + + cs.add_command("loopconcatword+", "riie", [&cs](TvalRange args) { + cs_loop_conc( + cs, *args[0].get_ident(), args[1].get_int(), args[2].get_int(), 1, + args[3].get_code(), false + ); + }); + + cs.add_command("loopconcatword*", "riie", [&cs](TvalRange args) { + cs_loop_conc( + cs, *args[0].get_ident(), 0, args[2].get_int(), args[1].get_int(), + args[3].get_code(), false + ); + }); + + cs.add_command("loopconcatword+*", "riiie", [&cs](TvalRange args) { + cs_loop_conc( + cs, *args[0].get_ident(), args[1].get_int(), args[3].get_int(), + args[2].get_int(), args[4].get_code(), false + ); + }); +} + +} /* namespace cscript */ diff --git a/lib_list.cc b/lib_list.cc new file mode 100644 index 0000000..27d9378 --- /dev/null +++ b/lib_list.cc @@ -0,0 +1,571 @@ +#include "cubescript.hh" +#include "cs_private.hh" +#include "lib_list.hh" + +namespace cscript { + +char *cs_dup_ostr(ostd::ConstCharRange s); +int cs_parse_int(ostd::ConstCharRange s); +float cs_parse_float(ostd::ConstCharRange s); + +struct NullValue: TaggedValue { + NullValue() { set_null(); } +} const null_value; + +static inline void cs_set_iter(Ident &id, char *val, IdentStack &stack) { + if (id.stack == &stack) { + if (id.get_valtype() == VAL_STR) { + delete[] id.val.s; + } else { + id.valtype = VAL_STR; + } + id.clean_code(); + id.val.s = val; + id.val.len = strlen(val); + return; + } + TaggedValue v; + v.set_mstr(val); + id.push_arg(v, stack); +} + +static void cs_loop_list_conc(CsState &cs, Ident *id, ostd::ConstCharRange list, + ostd::Uint32 const *body, bool space) { + if (id->type != ID_ALIAS) + return; + IdentStack stack; + ostd::Vector r; + int n = 0; + for (ListParser p(list); p.parse(); ++n) { + char *val = p.element().disown(); + cs_set_iter(*id, val, stack); + if (n && space) + r.push(' '); + TaggedValue v; + cs.run_ret(body, v); + ostd::String vstr = ostd::move(v.get_str()); + r.push_n(vstr.data(), vstr.size()); + v.cleanup(); + } + if (n >= 0) + id->pop_arg(); + r.push('\0'); + ostd::Size len = r.size(); + cs.result->set_mstr(ostd::CharRange(r.disown(), len - 1)); +} + +int cs_list_includes(ostd::ConstCharRange list, ostd::ConstCharRange needle) { + int offset = 0; + for (ListParser p(list); p.parse();) { + if (p.item == needle) + return offset; + ++offset; + } + return -1; +} + +static void cs_init_lib_list_sort(CsState &cs); + +void cs_init_lib_list(CsState &cs) { + cs.add_command("listlen", "s", [&cs](TvalRange args) { + cs.result->set_int(int(util::list_length(args[0].get_strr()))); + }); + + cs.add_command("at", "si1V", [&cs](TvalRange args) { + if (args.empty()) + return; + ostd::String str = ostd::move(args[0].get_str()); + ListParser p(str); + p.item = str; + for (ostd::Size i = 1; i < args.size(); ++i) { + p.input = str; + int pos = args[i].get_int(); + for (; pos > 0; --pos) + if (!p.parse()) break; + if (pos > 0 || !p.parse()) + p.item = p.quote = ostd::ConstCharRange(); + } + auto elem = p.element(); + auto er = p.element().iter(); + elem.disown(); + cs.result->set_mstr(er); + }); + + cs.add_command("sublist", "siiN", [&cs](TvalRange args) { + int skip = args[1].get_int(), + count = args[2].get_int(), + numargs = args[2].get_int(); + + int offset = ostd::max(skip, 0), + len = (numargs >= 3) ? ostd::max(count, 0) : -1; + + ListParser p(args[0].get_strr()); + for (int i = 0; i < offset; ++i) + if (!p.parse()) break; + if (len < 0) { + if (offset > 0) + p.skip(); + cs.result->set_str(p.input); + return; + } + + char const *list = p.input.data(); + p.quote = ostd::ConstCharRange(); + if (len > 0 && p.parse()) + while (--len > 0 && p.parse()); + char const *qend = !p.quote.empty() ? &p.quote[p.quote.size()] : list; + cs.result->set_str(ostd::ConstCharRange(list, qend - list)); + }); + + cs.add_command("listfind", "rse", [&cs](TvalRange args) { + Ident *id = args[0].get_ident(); + auto body = args[2].get_code(); + if (id->type != ID_ALIAS) { + cs.result->set_int(-1); + return; + } + IdentStack stack; + int n = -1; + for (ListParser p(args[1].get_strr()); p.parse();) { + ++n; + cs_set_iter(*id, cs_dup_ostr(p.item), stack); + if (cs.run_bool(body)) { + cs.result->set_int(n); + goto found; + } + } + cs.result->set_int(-1); +found: + if (n >= 0) + id->pop_arg(); + }); + + cs.add_command("listassoc", "rse", [&cs](TvalRange args) { + Ident *id = args[0].get_ident(); + auto body = args[2].get_code(); + if (id->type != ID_ALIAS) + return; + IdentStack stack; + int n = -1; + for (ListParser p(args[1].get_strr()); p.parse();) { + ++n; + cs_set_iter(*id, cs_dup_ostr(p.item), stack); + if (cs.run_bool(body)) { + if (p.parse()) { + auto elem = p.element(); + auto er = elem.iter(); + elem.disown(); + cs.result->set_mstr(er); + } + break; + } + if (!p.parse()) + break; + } + if (n >= 0) + id->pop_arg(); + }); + +#define CS_CMD_LIST_FIND(name, fmt, gmeth, cmp) \ + cs.add_command(name, "s" fmt "i", [&cs](TvalRange args) { \ + int n = 0, skip = args[2].get_int(); \ + auto val = args[1].gmeth(); \ + for (ListParser p(args[0].get_strr()); p.parse(); ++n) { \ + if (cmp) { \ + cs.result->set_int(n); \ + return; \ + } \ + for (int i = 0; i < skip; ++i) { \ + if (!p.parse()) \ + goto notfound; \ + ++n; \ + } \ + } \ + notfound: \ + cs.result->set_int(-1); \ + }); + + CS_CMD_LIST_FIND("listfind=", "i", get_int, cs_parse_int(p.item) == val); + CS_CMD_LIST_FIND("listfind=f", "f", get_float, cs_parse_float(p.item) == val); + CS_CMD_LIST_FIND("listfind=s", "s", get_strr, p.item == val); + +#undef CS_CMD_LIST_FIND + +#define CS_CMD_LIST_ASSOC(name, fmt, gmeth, cmp) \ + cs.add_command(name, "s" fmt, [&cs](TvalRange args) { \ + auto val = args[1].gmeth(); \ + for (ListParser p(args[0].get_strr()); p.parse();) { \ + if (cmp) { \ + if (p.parse()) { \ + auto elem = p.element(); \ + auto er = elem.iter(); \ + elem.disown(); \ + cs.result->set_mstr(er); \ + } \ + return; \ + } \ + if (!p.parse()) \ + break; \ + } \ + }); + + CS_CMD_LIST_ASSOC("listassoc=", "i", get_int, cs_parse_int(p.item) == val); + CS_CMD_LIST_ASSOC("listassoc=f", "f", get_float, cs_parse_float(p.item) == val); + CS_CMD_LIST_ASSOC("listassoc=s", "s", get_strr, p.item == val); + +#undef CS_CMD_LIST_ASSOC + + cs.add_command("looplist", "rse", [&cs](TvalRange args) { + Ident *id = args[0].get_ident(); + auto body = args[2].get_code(); + if (id->type != ID_ALIAS) + return; + IdentStack stack; + int n = 0; + for (ListParser p(args[1].get_strr()); p.parse(); ++n) { + cs_set_iter(*id, p.element().disown(), stack); + cs.run_int(body); + } + if (n >= 0) + id->pop_arg(); + }); + + cs.add_command("looplist2", "rrse", [&cs](TvalRange args) { + Ident *id = args[0].get_ident(), *id2 = args[1].get_ident(); + auto body = args[3].get_code(); + if (id->type != ID_ALIAS || id2->type != ID_ALIAS) + return; + IdentStack stack, stack2; + int n = 0; + for (ListParser p(args[2].get_strr()); p.parse(); n += 2) { + cs_set_iter(*id, p.element().disown(), stack); + cs_set_iter(*id2, p.parse() ? p.element().disown() + : cs_dup_ostr(""), stack2); + cs.run_int(body); + } + if (n >= 0) { + id->pop_arg(); + id2->pop_arg(); + } + }); + + cs.add_command("looplist3", "rrrse", [&cs](TvalRange args) { + Ident *id = args[0].get_ident(); + Ident *id2 = args[1].get_ident(); + Ident *id3 = args[2].get_ident(); + auto body = args[4].get_code(); + if (id->type != ID_ALIAS) + return; + if (id2->type != ID_ALIAS || id3->type != ID_ALIAS) + return; + IdentStack stack, stack2, stack3; + int n = 0; + for (ListParser p(args[3].get_strr()); p.parse(); n += 3) { + cs_set_iter(*id, p.element().disown(), stack); + cs_set_iter(*id2, p.parse() ? p.element().disown() + : cs_dup_ostr(""), stack2); + cs_set_iter(*id3, p.parse() ? p.element().disown() + : cs_dup_ostr(""), stack3); + cs.run_int(body); + } + if (n >= 0) { + id->pop_arg(); + id2->pop_arg(); + id3->pop_arg(); + } + }); + + cs.add_command("looplistconcat", "rse", [&cs](TvalRange args) { + cs_loop_list_conc( + cs, args[0].get_ident(), args[1].get_strr(), + args[2].get_code(), true + ); + }); + + cs.add_command("looplistconcatword", "rse", [&cs](TvalRange args) { + cs_loop_list_conc( + cs, args[0].get_ident(), args[1].get_strr(), + args[2].get_code(), false + ); + }); + + cs.add_command("listfilter", "rse", [&cs](TvalRange args) { + Ident *id = args[0].get_ident(); + auto body = args[2].get_code(); + if (id->type != ID_ALIAS) + return; + IdentStack stack; + ostd::Vector r; + int n = 0; + for (ListParser p(args[1].get_strr()); p.parse(); ++n) { + char *val = cs_dup_ostr(p.item); + cs_set_iter(*id, val, stack); + if (cs.run_bool(body)) { + if (r.size()) r.push(' '); + r.push_n(p.quote.data(), p.quote.size()); + } + } + if (n >= 0) + id->pop_arg(); + r.push('\0'); + ostd::Size len = r.size() - 1; + cs.result->set_mstr(ostd::CharRange(r.disown(), len)); + }); + + cs.add_command("listcount", "rse", [&cs](TvalRange args) { + Ident *id = args[0].get_ident(); + auto body = args[2].get_code(); + if (id->type != ID_ALIAS) + return; + IdentStack stack; + int n = 0, r = 0; + for (ListParser p(args[1].get_strr()); p.parse(); ++n) { + char *val = cs_dup_ostr(p.item); + cs_set_iter(*id, val, stack); + if (cs.run_bool(body)) + r++; + } + if (n >= 0) + id->pop_arg(); + cs.result->set_int(r); + }); + + cs.add_command("prettylist", "ss", [&cs](TvalRange args) { + ostd::Vector buf; + ostd::ConstCharRange s = args[0].get_strr(); + ostd::ConstCharRange conj = args[1].get_strr(); + ostd::Size len = util::list_length(s); + ostd::Size n = 0; + for (ListParser p(s); p.parse(); ++n) { + if (!p.quote.empty() && (p.quote.front() == '"')) { + buf.reserve(buf.size() + p.item.size()); + auto writer = ostd::CharRange(&buf[buf.size()], + buf.capacity() - buf.size()); + ostd::Size adv = util::unescape_string(writer, p.item); + writer.put('\0'); + buf.advance(adv); + } else { + buf.push_n(p.item.data(), p.item.size()); + } + if ((n + 1) < len) { + if ((len > 2) || conj.empty()) + buf.push(','); + if ((n + 2 == len) && !conj.empty()) { + buf.push(' '); + buf.push_n(conj.data(), conj.size()); + } + buf.push(' '); + } + } + buf.push('\0'); + ostd::Size slen = buf.size() - 1; + cs.result->set_mstr(ostd::CharRange(buf.disown(), slen)); + }); + + cs.add_command("indexof", "ss", [&cs](TvalRange args) { + cs.result->set_int( + cs_list_includes(args[0].get_strr(), args[1].get_strr()) + ); + }); + +#define CS_CMD_LIST_MERGE(name, init, iter, filter, dir) \ + cs.add_command(name, "ss", [&cs](TvalRange args) { \ + ostd::ConstCharRange list = args[0].get_strr(); \ + ostd::ConstCharRange elems = args[1].get_strr(); \ + ostd::Vector buf; \ + init; \ + for (ListParser p(iter); p.parse();) { \ + if (cs_list_includes(filter, p.item) dir 0) { \ + if (!buf.empty()) \ + buf.push(' '); \ + buf.push_n(p.quote.data(), p.quote.size()); \ + } \ + } \ + buf.push('\0'); \ + ostd::Size len = buf.size() - 1; \ + cs.result->set_mstr(ostd::CharRange(buf.disown(), len)); \ + }); + + CS_CMD_LIST_MERGE("listdel", {}, list, elems, <); + CS_CMD_LIST_MERGE("listintersect", {}, list, elems, >=); + CS_CMD_LIST_MERGE("listunion", buf.push_n(list.data(), list.size()), elems, + list, <); + +#undef CS_CMD_LIST_MERGE + + cs.add_command("listsplice", "ssii", [&cs](TvalRange args) { + int offset = ostd::max(args[2].get_int(), 0); + int len = ostd::max(args[3].get_int(), 0); + ostd::ConstCharRange s = args[0].get_strr(); + ostd::ConstCharRange vals = args[1].get_strr(); + char const *list = s.data(); + ListParser p(s); + for (int i = 0; i < offset; ++i) + if (!p.parse()) + break; + char const *qend = !p.quote.empty() ? &p.quote[p.quote.size()] : list; + ostd::Vector buf; + if (qend > list) + buf.push_n(list, qend - list); + if (!vals.empty()) { + if (!buf.empty()) + buf.push(' '); + buf.push_n(vals.data(), vals.size()); + } + for (int i = 0; i < len; ++i) + if (!p.parse()) + break; + p.skip(); + if (!p.input.empty()) switch (p.input.front()) { + case ')': + case ']': + break; + default: + if (!buf.empty()) + buf.push(' '); + buf.push_n(p.input.data(), p.input.size()); + break; + } + buf.push('\0'); + ostd::Size slen = buf.size() - 1; + cs.result->set_mstr(ostd::CharRange(buf.disown(), slen)); + }); + + cs_init_lib_list_sort(cs); +} + +struct ListSortItem { + char const *str; + ostd::ConstCharRange quote; +}; + +struct ListSortFun { + CsState &cs; + Ident *x, *y; + ostd::Uint32 *body; + + bool operator()(ListSortItem const &xval, ListSortItem const &yval) { + x->clean_code(); + if (x->get_valtype() != VAL_CSTR) { + x->valtype = VAL_CSTR; + } + x->val.cstr = xval.str; + x->val.len = strlen(xval.str); + y->clean_code(); + if (y->get_valtype() != VAL_CSTR) { + y->valtype = VAL_CSTR; + } + y->val.cstr = yval.str; + y->val.len = strlen(yval.str); + return cs.run_bool(body); + } +}; + +static void cs_list_sort( + CsState &cs, ostd::ConstCharRange list, Ident *x, Ident *y, + ostd::Uint32 *body, ostd::Uint32 *unique +) { + if (x == y || x->type != ID_ALIAS || y->type != ID_ALIAS) + return; + + ostd::Vector items; + ostd::Size clen = list.size(); + ostd::Size total = 0; + + char *cstr = cs_dup_ostr(list); + for (ListParser p(list); p.parse();) { + cstr[&p.item[p.item.size()] - list.data()] = '\0'; + ListSortItem item = { &cstr[p.item.data() - list.data()], p.quote }; + items.push(item); + total += item.quote.size(); + } + + if (items.empty()) { + cs.result->set_mstr(cstr); + return; + } + + IdentStack xstack, ystack; + x->push_arg(null_value, xstack); + y->push_arg(null_value, ystack); + + ostd::Size totaluniq = total; + ostd::Size nuniq = items.size(); + if (body) { + ListSortFun f = { cs, x, y, body }; + ostd::sort_cmp(items.iter(), f); + if ((*unique & CODE_OP_MASK) != CODE_EXIT) { + f.body = unique; + totaluniq = items[0].quote.size(); + nuniq = 1; + for (ostd::Size i = 1; i < items.size(); i++) { + ListSortItem &item = items[i]; + if (f(items[i - 1], item)) + item.quote = nullptr; + else { + totaluniq += item.quote.size(); + ++nuniq; + } + } + } + } else { + ListSortFun f = { cs, x, y, unique }; + totaluniq = items[0].quote.size(); + nuniq = 1; + for (ostd::Size i = 1; i < items.size(); i++) { + ListSortItem &item = items[i]; + for (ostd::Size j = 0; j < i; ++j) { + ListSortItem &prev = items[j]; + if (!prev.quote.empty() && f(item, prev)) { + item.quote = nullptr; + break; + } + } + if (!item.quote.empty()) { + totaluniq += item.quote.size(); + ++nuniq; + } + } + } + + x->pop_arg(); + y->pop_arg(); + + char *sorted = cstr; + ostd::Size sortedlen = totaluniq + ostd::max(nuniq - 1, ostd::Size(0)); + if (clen < sortedlen) { + delete[] cstr; + sorted = new char[sortedlen + 1]; + } + + ostd::Size offset = 0; + for (ostd::Size i = 0; i < items.size(); ++i) { + ListSortItem &item = items[i]; + if (item.quote.empty()) + continue; + if (i) + sorted[offset++] = ' '; + memcpy(&sorted[offset], item.quote.data(), item.quote.size()); + offset += item.quote.size(); + } + sorted[offset] = '\0'; + + cs.result->set_mstr(sorted); +} + +static void cs_init_lib_list_sort(CsState &cs) { + cs.add_command("sortlist", "srree", [&cs](TvalRange args) { + cs_list_sort( + cs, args[0].get_strr(), args[1].get_ident(), args[2].get_ident(), + args[3].get_code(), args[4].get_code() + ); + }); + cs.add_command("uniquelist", "srre", [&cs](TvalRange args) { + cs_list_sort( + cs, args[0].get_strr(), args[1].get_ident(), args[2].get_ident(), + nullptr, args[3].get_code() + ); + }); +} + +} /* namespace cscript */ diff --git a/lib_list.hh b/lib_list.hh new file mode 100644 index 0000000..ce3a8bf --- /dev/null +++ b/lib_list.hh @@ -0,0 +1,129 @@ +#ifndef LIB_LIST_HH +#define LIB_LIST_HH + +#include "cubescript.hh" + +namespace cscript { + +ostd::ConstCharRange cs_parse_str(ostd::ConstCharRange str); +char const *parseword(char const *p); + +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() { + for (;;) { + while (!input.empty()) { + char c = input.front(); + if ((c == ' ') || (c == '\t') || (c == '\r') || (c == '\n')) + input.pop_front(); + else + break; + } + if ((input.size() < 2) || (input[0] != '/') || (input[1] != '/')) + break; + input = ostd::find(input, '\n'); + } + } + + bool parse() { + skip(); + if (input.empty()) + return false; + switch (input.front()) { + case '"': + quote = input; + input.pop_front(); + item = input; + input = cs_parse_str(input); + item = ostd::slice_until(item, input); + if (!input.empty() && (input.front() == '"')) + input.pop_front(); + quote = ostd::slice_until(quote, input); + break; + case '(': + case '[': { + quote = input; + input.pop_front(); + item = input; + char btype = quote.front(); + int brak = 1; + for (;;) { + input = ostd::find_one_of(input, + ostd::ConstCharRange("\"/;()[]")); + if (input.empty()) + return true; + char c = input.front(); + input.pop_front(); + switch (c) { + case '"': + input = cs_parse_str(input); + if (!input.empty() && (input.front() == '"')) + input.pop_front(); + break; + case '/': + if (!input.empty() && (input.front() == '/')) + input = ostd::find(input, '\n'); + break; + case '(': + case '[': + brak += (c == btype); + break; + case ')': + if ((btype == '(') && (--brak <= 0)) + goto endblock; + break; + case ']': + if ((btype == '[') && (--brak <= 0)) + goto endblock; + break; + } + } +endblock: + item = ostd::slice_until(item, input); + item.pop_back(); + quote = ostd::slice_until(quote, input); + break; + } + case ')': + case ']': + return false; + default: { + char const *e = parseword(input.data()); + item = input; + input.pop_front_n(e - input.data()); + item = ostd::slice_until(item, input); + quote = item; + break; + } + } + skip(); + if (!input.empty() && (input.front() == ';')) + input.pop_front(); + return true; + } + + ostd::String element() { + ostd::String s; + s.reserve(item.size()); + if (!quote.empty() && (quote.front() == '"')) { + auto writer = s.iter_cap(); + util::unescape_string(writer, item); + writer.put('\0'); + } else { + memcpy(s.data(), item.data(), item.size()); + s[item.size()] = '\0'; + } + s.advance(item.size()); + return s; + } +}; + +} /*namespace cscript */ + +#endif diff --git a/lib_math.cc b/lib_math.cc new file mode 100644 index 0000000..1f0ed42 --- /dev/null +++ b/lib_math.cc @@ -0,0 +1,196 @@ +#include "cubescript.hh" + +namespace cscript { + +static constexpr float PI = 3.14159265358979f; +static constexpr float RAD = PI / 180.0f; + +void cs_init_lib_math(CsState &cs) { + cs.add_command("sin", "f", [&cs](TvalRange args) { + cs.result->set_float(sin(args[0].get_float() * RAD)); + }); + cs.add_command("cos", "f", [&cs](TvalRange args) { + cs.result->set_float(cos(args[0].get_float() * RAD)); + }); + cs.add_command("tan", "f", [&cs](TvalRange args) { + cs.result->set_float(tan(args[0].get_float() * RAD)); + }); + + cs.add_command("asin", "f", [&cs](TvalRange args) { + cs.result->set_float(asin(args[0].get_float()) / RAD); + }); + cs.add_command("acos", "f", [&cs](TvalRange args) { + cs.result->set_float(acos(args[0].get_float()) / RAD); + }); + cs.add_command("atan", "f", [&cs](TvalRange args) { + cs.result->set_float(atan(args[0].get_float()) / RAD); + }); + cs.add_command("atan2", "ff", [&cs](TvalRange args) { + cs.result->set_float(atan2(args[0].get_float(), args[1].get_float()) / RAD); + }); + + cs.add_command("sqrt", "f", [&cs](TvalRange args) { + cs.result->set_float(sqrt(args[0].get_float())); + }); + cs.add_command("loge", "f", [&cs](TvalRange args) { + cs.result->set_float(log(args[0].get_float())); + }); + cs.add_command("log2", "f", [&cs](TvalRange args) { + cs.result->set_float(log(args[0].get_float()) / M_LN2); + }); + cs.add_command("log10", "f", [&cs](TvalRange args) { + cs.result->set_float(log10(args[0].get_float())); + }); + + cs.add_command("exp", "f", [&cs](TvalRange args) { + cs.result->set_float(exp(args[0].get_float())); + }); + +#define CS_CMD_MIN_MAX(name, fmt, type, op) \ + cs.add_command(#name, #fmt "1V", [&cs](TvalRange args) { \ + type v = !args.empty() ? args[0].fmt : 0; \ + for (ostd::Size i = 1; i < args.size(); ++i) v = op(v, args[i].fmt); \ + cs.result->set_##type(v); \ + }) + + CS_CMD_MIN_MAX(min, i, int, ostd::min); + CS_CMD_MIN_MAX(max, i, int, ostd::max); + CS_CMD_MIN_MAX(minf, f, float, ostd::min); + CS_CMD_MIN_MAX(maxf, f, float, ostd::max); + +#undef CS_CMD_MIN_MAX + + cs.add_command("abs", "i", [&cs](TvalRange args) { + cs.result->set_int(abs(args[0].get_int())); + }); + cs.add_command("absf", "f", [&cs](TvalRange args) { + cs.result->set_float(fabs(args[0].get_float())); + }); + + cs.add_command("floor", "f", [&cs](TvalRange args) { + cs.result->set_float(floor(args[0].get_float())); + }); + cs.add_command("ceil", "f", [&cs](TvalRange args) { + cs.result->set_float(ceil(args[0].get_float())); + }); + + cs.add_command("round", "ff", [&cs](TvalRange args) { + double step = args[1].get_float(); + double r = args[0].get_float(); + if (step > 0) { + r += step * ((r < 0) ? -0.5 : 0.5); + r -= fmod(r, step); + } else { + r = (r < 0) ? ceil(r - 0.5) : floor(r + 0.5); + } + cs.result->set_float(float(r)); + }); + +#define CS_CMD_MATH(name, fmt, type, op, initval, unaryop) \ + cs.add_command(name, #fmt "1V", [&cs](TvalRange args) { \ + type val; \ + if (args.size() >= 2) { \ + val = args[0].fmt; \ + type val2 = args[1].fmt; \ + op; \ + for (ostd::Size i = 2; i < args.size(); ++i) { \ + val2 = args[i].fmt; \ + op; \ + } \ + } else { \ + val = (args.size() > 0) ? args[0].fmt : initval; \ + unaryop; \ + } \ + cs.result->set_##type(val); \ + }); + +#define CS_CMD_MATHIN(name, op, initval, unaryop) \ + CS_CMD_MATH(#name, i, int, val = val op val2, initval, unaryop) + +#define CS_CMD_MATHI(name, initval, unaryop) \ + CS_CMD_MATHIN(name, name, initval, unaryop) + +#define CS_CMD_MATHFN(name, op, initval, unaryop) \ + CS_CMD_MATH(#name "f", f, float, val = val op val2, initval, unaryop) + +#define CS_CMD_MATHF(name, initval, unaryop) \ + CS_CMD_MATHFN(name, name, initval, unaryop) + + CS_CMD_MATHI(+, 0, {}); + CS_CMD_MATHI(*, 1, {}); + CS_CMD_MATHI(-, 0, val = -val); + + CS_CMD_MATHI(^, 0, val = ~val); + CS_CMD_MATHIN(~, ^, 0, val = ~val); + CS_CMD_MATHI(&, 0, {}); + CS_CMD_MATHI(|, 0, {}); + CS_CMD_MATHI(^~, 0, {}); + CS_CMD_MATHI(&~, 0, {}); + CS_CMD_MATHI(|~, 0, {}); + + CS_CMD_MATH("<<", i, int, { + val = (val2 < 32) ? (val << ostd::max(val2, 0)) : 0; + }, 0, {}); + CS_CMD_MATH(">>", i, int, val >>= ostd::clamp(val2, 0, 31), 0, {}); + + CS_CMD_MATHF(+, 0, {}); + CS_CMD_MATHF(*, 1, {}); + CS_CMD_MATHF(-, 0, val = -val); + +#define CS_CMD_DIV(name, fmt, type, op) \ + CS_CMD_MATH(#name, fmt, type, { if (val2) op; else val = 0; }, 0, {}) + + CS_CMD_DIV(div, i, int, val /= val2); + CS_CMD_DIV(mod, i, int, val %= val2); + CS_CMD_DIV(divf, f, float, val /= val2); + CS_CMD_DIV(modf, f, float, val = fmod(val, val2)); + +#undef CS_CMD_DIV + + CS_CMD_MATH("pow", f, float, val = pow(val, val2), 0, {}); + +#undef CS_CMD_MATHF +#undef CS_CMD_MATHFN +#undef CS_CMD_MATHI +#undef CS_CMD_MATHIN +#undef CS_CMD_MATH + +#define CS_CMD_CMP(name, fmt, type, op) \ + cs.add_command(name, #fmt "1V", [&cs](TvalRange args) { \ + bool val; \ + if (args.size() >= 2) { \ + val = args[0].fmt op args[1].fmt; \ + for (ostd::Size i = 2; i < args.size() && val; ++i) \ + val = args[i-1].fmt op args[i].fmt; \ + } else \ + val = ((args.size() > 0) ? args[0].fmt : 0) op 0; \ + cs.result->set_int(int(val)); \ + }) + +#define CS_CMD_CMPIN(name, op) CS_CMD_CMP(#name, i, int, op) +#define CS_CMD_CMPI(name) CS_CMD_CMPIN(name, name) +#define CS_CMD_CMPFN(name, op) CS_CMD_CMP(#name "f", f, float, op) +#define CS_CMD_CMPF(name) CS_CMD_CMPFN(name, name) + + CS_CMD_CMPIN(=, ==); + CS_CMD_CMPI(!=); + CS_CMD_CMPI(<); + CS_CMD_CMPI(>); + CS_CMD_CMPI(<=); + CS_CMD_CMPI(>=); + + CS_CMD_CMPFN(=, ==); + CS_CMD_CMPF(!=); + CS_CMD_CMPF(<); + CS_CMD_CMPF(>); + CS_CMD_CMPF(<=); + CS_CMD_CMPF(>=); + +#undef CS_CMD_CMPF +#undef CS_CMD_CMPFN +#undef CS_CMD_CMPI +#undef CS_CMD_CMPIN +#undef CS_CMD_CMP +} + +} /* namespace cscript */ diff --git a/lib_str.cc b/lib_str.cc new file mode 100644 index 0000000..f0c2625 --- /dev/null +++ b/lib_str.cc @@ -0,0 +1,216 @@ +#include "cubescript.hh" + +namespace cscript { + +char *conc(TvalRange v, bool space); + +void cs_init_lib_string(CsState &cs) { + cs.add_command("strstr", "ss", [&cs](TvalRange args) { + ostd::ConstCharRange a = args[0].get_strr(), b = args[1].get_strr(); + ostd::ConstCharRange s = a; + for (int i = 0; b.size() <= s.size(); ++i) { + if (b == s.slice(0, b.size())) { + cs.result->set_int(i); + return; + } + s.pop_front(); + } + cs.result->set_int(-1); + }); + + cs.add_command("strlen", "s", [&cs](TvalRange args) { + cs.result->set_int(int(args[0].get_strr().size())); + }); + + cs.add_command("strcode", "si", [&cs](TvalRange args) { + ostd::ConstCharRange str = args[0].get_strr(); + int i = args[1].get_int(); + if (i >= int(str.size())) { + cs.result->set_int(0); + } else { + cs.result->set_int(ostd::byte(str[i])); + } + }); + + cs.add_command("codestr", "i", [&cs](TvalRange args) { + char *s = new char[2]; + s[0] = char(args[0].get_int()); + s[1] = '\0'; + cs.result->set_mstr(s); + }); + + cs.add_command("strlower", "s", [&cs](TvalRange args) { + ostd::ConstCharRange s = args[0].get_strr(); + char *buf = new char[s.size() + 1]; + for (auto i: ostd::range(s.size())) + buf[i] = tolower(s[i]); + buf[s.size()] = '\0'; + cs.result->set_mstr(ostd::CharRange(buf, s.size())); + }); + + cs.add_command("strupper", "s", [&cs](TvalRange args) { + ostd::ConstCharRange s = args[0].get_strr(); + char *buf = new char[s.size() + 1]; + for (auto i: ostd::range(s.size())) + buf[i] = toupper(s[i]); + buf[s.size()] = '\0'; + cs.result->set_mstr(ostd::CharRange(buf, s.size())); + }); + + cs.add_command("escape", "s", [&cs](TvalRange args) { + auto x = ostd::appender(); + util::escape_string(x, args[0].get_strr()); + ostd::Size len = x.size(); + cs.result->set_mstr(ostd::CharRange(x.get().disown(), len)); + }); + + cs.add_command("unescape", "s", [&cs](TvalRange args) { + ostd::ConstCharRange s = args[0].get_strr(); + char *buf = new char[s.size() + 1]; + auto writer = ostd::CharRange(buf, s.size() + 1); + util::unescape_string(writer, s); + writer.put('\0'); + cs.result->set_mstr(ostd::CharRange(buf, s.size())); + }); + + cs.add_command("concat", "V", [&cs](TvalRange args) { + cs.result->set_mstr(conc(args, true)); + }); + + cs.add_command("concatworld", "V", [&cs](TvalRange args) { + cs.result->set_mstr(conc(args, false)); + }); + + cs.add_command("format", "V", [&cs](TvalRange args) { + if (args.empty()) + return; + ostd::Vector s; + ostd::String fs = ostd::move(args[0].get_str()); + ostd::ConstCharRange f = fs.iter(); + while (!f.empty()) { + char c = f.front(); + f.pop_front(); + if ((c == '%') && !f.empty()) { + char ic = f.front(); + f.pop_front(); + if (ic >= '1' && ic <= '9') { + int i = ic - '0'; + ostd::String sub = ostd::move((i < int(args.size())) + ? args[i].get_str() : ostd::String("")); + s.push_n(sub.data(), sub.size()); + } else s.push(ic); + } else s.push(c); + } + s.push('\0'); + ostd::Size len = s.size() - 1; + cs.result->set_mstr(ostd::CharRange(s.disown(), len)); + }); + + cs.add_command("tohex", "ii", [&cs](TvalRange args) { + auto r = ostd::appender>(); + ostd::format(r, "0x%.*X", ostd::max(args[1].get_int(), 1), args[0].get_int()); + r.put('\0'); + ostd::Size len = r.size() - 1; + cs.result->set_mstr(ostd::CharRange(r.get().disown(), len)); + }); + + cs.add_command("substr", "siiN", [&cs](TvalRange args) { + ostd::ConstCharRange s = args[0].get_strr(); + int start = args[1].get_int(), count = args[2].get_int(); + int numargs = args[3].get_int(); + int len = int(s.size()), offset = ostd::clamp(start, 0, len); + cs.result->set_str(ostd::ConstCharRange( + &s[offset], + (numargs >= 3) ? ostd::clamp(count, 0, len - offset) + : (len - offset) + )); + }); + +#define CS_CMD_CMPS(name, op) \ + cs.add_command(#name, "s1V", [&cs](TvalRange args) { \ + bool val; \ + if (args.size() >= 2) { \ + val = strcmp(args[0].s, args[1].s) op 0; \ + for (ostd::Size i = 2; i < args.size() && val; ++i) \ + val = strcmp(args[i-1].s, args[i].s) op 0; \ + } else \ + val = (!args.empty() ? args[0].s[0] : 0) op 0; \ + cs.result->set_int(int(val)); \ + }) + + CS_CMD_CMPS(strcmp, ==); + CS_CMD_CMPS(=s, ==); + CS_CMD_CMPS(!=s, !=); + CS_CMD_CMPS(s, >); + CS_CMD_CMPS(<=s, <=); + CS_CMD_CMPS(>=s, >=); + +#undef CS_CMD_CMPS + + cs.add_command("strreplace", "ssss", [&cs](TvalRange args) { + ostd::ConstCharRange s = args[0].get_strr(); + ostd::ConstCharRange oldval = args[1].get_strr(), + newval = args[2].get_strr(), + newval2 = args[3].get_strr(); + if (newval2.empty()) { + newval2 = newval; + } + ostd::Vector buf; + if (!oldval.size()) { + cs.result->set_str(s); + return; + } + for (ostd::Size i = 0;; ++i) { + ostd::ConstCharRange found; + ostd::ConstCharRange trys = s; + for (; oldval.size() <= trys.size(); trys.pop_front()) { + if (trys.slice(0, oldval.size()) == oldval) { + found = trys; + break; + } + } + if (!found.empty()) { + auto bef = ostd::slice_until(s, found); + for (; !bef.empty(); bef.pop_front()) { + buf.push(bef.front()); + } + auto use = (i & 1) ? newval2 : newval; + for (; !use.empty(); use.pop_front()) { + buf.push(use.front()); + } + s = found + oldval.size(); + } else { + for (; !s.empty(); s.pop_front()) { + buf.push(s.front()); + } + buf.push('\0'); + ostd::Size len = buf.size() - 1; + cs.result->set_mstr(ostd::CharRange(buf.disown(), len)); + return; + } + } + }); + + cs.add_command("strsplice", "ssii", [&cs](TvalRange args) { + ostd::ConstCharRange s = args[0].get_strr(); + ostd::ConstCharRange vals = args[1].get_strr(); + int skip = args[2].get_int(), + count = args[3].get_int(); + int slen = int(s.size()), + vlen = int(vals.size()); + int offset = ostd::clamp(skip, 0, slen), + len = ostd::clamp(count, 0, slen - offset); + char *p = new char[slen - len + vlen + 1]; + if (offset) + memcpy(p, s.data(), offset); + if (vlen) + memcpy(&p[offset], vals.data(), vlen); + if (offset + len < slen) + memcpy(&p[offset + vlen], &s[offset + len], slen - (offset + len)); + p[slen - len + vlen] = '\0'; + cs.result->set_mstr(ostd::CharRange(p, slen - len + vlen)); + }); +} + +} /* namespace cscript */