#include "cubescript.hh" #include "cs_util.hh" namespace cscript { char *cs_dup_ostr(ostd::ConstCharRange s); template struct CsArgVal; template<> struct CsArgVal { static CsInt get(CsValue &tv) { return tv.get_int(); } }; template<> struct CsArgVal { static CsFloat get(CsValue &tv) { return tv.get_float(); } }; template<> struct CsArgVal { static ostd::ConstCharRange get(CsValue &tv) { return tv.get_strr(); } }; template static inline void cs_list_find(CsValueRange args, CsValue &res, F cmp) { CsInt n = 0, skip = args[2].get_int(); T val = CsArgVal::get(args[1]); for (util::ListParser p(args[0].get_strr()); p.parse(); ++n) { if (cmp(p, val)) { res.set_int(n); return; } for (int i = 0; i < skip; ++i) { if (!p.parse()) { goto notfound; } ++n; } } notfound: res.set_int(-1); } template static inline void cs_list_assoc(CsValueRange args, CsValue &res, F cmp) { T val = CsArgVal::get(args[1]); for (util::ListParser p(args[0].get_strr()); p.parse();) { if (cmp(p, val)) { if (p.parse()) { auto elem = p.element(); auto er = elem.iter(); elem.disown(); res.set_mstr(er); } return; } if (!p.parse()) { break; } } } static void cs_loop_list_conc( CsState &cs, CsValue &res, CsIdent *id, ostd::ConstCharRange list, CsBytecode *body, bool space ) { CsStackedValue idv{id}; if (!idv.has_alias()) { return; } CsVector r; int n = 0; for (util::ListParser p(list); p.parse(); ++n) { char *val = p.element().disown(); idv.set_mstr(val); idv.push(); if (n && space) { r.push(' '); } CsValue v; cs.run_ret(body, v); CsString vstr = ostd::move(v.get_str()); r.push_n(vstr.data(), vstr.size()); v.cleanup(); } r.push('\0'); ostd::Size len = r.size(); res.set_mstr(ostd::CharRange(r.disown(), len - 1)); } int cs_list_includes(ostd::ConstCharRange list, ostd::ConstCharRange needle) { int offset = 0; for (util::ListParser p(list); p.parse();) { if (p.item == needle) { return offset; } ++offset; } return -1; } template static inline void cs_list_merge(CsValueRange args, CsValue &res, F cmp) { ostd::ConstCharRange list = args[0].get_strr(); ostd::ConstCharRange elems = args[1].get_strr(); CsVector buf; if (PushList) { buf.push_n(list.data(), list.size()); } if (Swap) { ostd::swap(list, elems); } for (util::ListParser p(list); p.parse();) { if (cmp(cs_list_includes(elems, p.item), 0)) { if (!buf.empty()) { buf.push(' '); } buf.push_n(p.quote.data(), p.quote.size()); } } buf.push('\0'); ostd::Size len = buf.size() - 1; res.set_mstr(ostd::CharRange(buf.disown(), len)); } static void cs_init_lib_list_sort(CsState &cs); void cs_init_lib_list(CsState &cs) { cs.add_command("listlen", "s", [](CsValueRange args, CsValue &res) { res.set_int(CsInt(util::list_length(args[0].get_strr()))); }); cs.add_command("at", "si1V", [](CsValueRange args, CsValue &res) { if (args.empty()) { return; } CsString str = ostd::move(args[0].get_str()); util::ListParser p(str); p.item = str; for (ostd::Size i = 1; i < args.size(); ++i) { p.input = str; CsInt 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(); res.set_mstr(er); }); cs.add_command("sublist", "siiN", [](CsValueRange args, CsValue &res) { CsInt skip = args[1].get_int(), count = args[2].get_int(), numargs = args[2].get_int(); CsInt offset = ostd::max(skip, 0), len = (numargs >= 3) ? ostd::max(count, 0) : -1; util::ListParser p(args[0].get_strr()); for (CsInt i = 0; i < offset; ++i) { if (!p.parse()) break; } if (len < 0) { if (offset > 0) { p.skip(); } res.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; res.set_str(ostd::ConstCharRange(list, qend - list)); }); cs.add_command("listfind", "rse", [&cs](CsValueRange args, CsValue &res) { CsStackedValue idv{args[0].get_ident()}; if (!idv.has_alias()) { res.set_int(-1); return; } auto body = args[2].get_code(); int n = -1; for (util::ListParser p(args[1].get_strr()); p.parse();) { ++n; idv.set_mstr(cs_dup_ostr(p.item)); idv.push(); if (cs.run_bool(body)) { res.set_int(CsInt(n)); return; } } res.set_int(-1); }); cs.add_command("listassoc", "rse", [&cs](CsValueRange args, CsValue &res) { CsStackedValue idv{args[0].get_ident()}; if (!idv.has_alias()) { return; } auto body = args[2].get_code(); int n = -1; for (util::ListParser p(args[1].get_strr()); p.parse();) { ++n; idv.set_mstr(cs_dup_ostr(p.item)); idv.push(); if (cs.run_bool(body)) { if (p.parse()) { auto elem = p.element(); auto er = elem.iter(); elem.disown(); res.set_mstr(er); } break; } if (!p.parse()) { break; } } }); cs.add_command("listfind=", "i", [](CsValueRange args, CsValue &res) { cs_list_find( args, res, [](const util::ListParser &p, CsInt val) { return cs_parse_int(p.item) == val; } ); }); cs.add_command("listfind=f", "f", [](CsValueRange args, CsValue &res) { cs_list_find( args, res, [](const util::ListParser &p, CsFloat val) { return cs_parse_float(p.item) == val; } ); }); cs.add_command("listfind=s", "s", [](CsValueRange args, CsValue &res) { cs_list_find( args, res, [](const util::ListParser &p, ostd::ConstCharRange val) { return p.item == val; } ); }); cs.add_command("listassoc=", "i", [](CsValueRange args, CsValue &res) { cs_list_assoc( args, res, [](const util::ListParser &p, CsInt val) { return cs_parse_int(p.item) == val; } ); }); cs.add_command("listassoc=f", "f", [](CsValueRange args, CsValue &res) { cs_list_assoc( args, res, [](const util::ListParser &p, CsFloat val) { return cs_parse_float(p.item) == val; } ); }); cs.add_command("listassoc=s", "s", [](CsValueRange args, CsValue &res) { cs_list_assoc( args, res, [](const util::ListParser &p, ostd::ConstCharRange val) { return p.item == val; } ); }); cs.add_command("looplist", "rse", [&cs](CsValueRange args, CsValue &) { CsStackedValue idv{args[0].get_ident()}; if (!idv.has_alias()) { return; } auto body = args[2].get_code(); int n = 0; for (util::ListParser p(args[1].get_strr()); p.parse(); ++n) { idv.set_mstr(p.element().disown()); idv.push(); cs.run_int(body); } }); cs.add_command("looplist2", "rrse", [&cs](CsValueRange args, CsValue &) { CsStackedValue idv1{args[0].get_ident()}, idv2{args[1].get_ident()}; if (!idv1.has_alias() || !idv2.has_alias()) { return; } auto body = args[3].get_code(); int n = 0; for (util::ListParser p(args[2].get_strr()); p.parse(); n += 2) { idv1.set_mstr(p.element().disown()); idv2.set_mstr(p.parse() ? p.element().disown() : cs_dup_ostr("")); idv1.push(); idv2.push(); cs.run_int(body); } }); cs.add_command("looplist3", "rrrse", [&cs](CsValueRange args, CsValue &) { CsStackedValue idv1{args[0].get_ident()}; CsStackedValue idv2{args[1].get_ident()}; CsStackedValue idv3{args[2].get_ident()}; if (!idv1.has_alias() || !idv2.has_alias() || !idv3.has_alias()) { return; } auto body = args[4].get_code(); int n = 0; for (util::ListParser p(args[3].get_strr()); p.parse(); n += 3) { idv1.set_mstr(p.element().disown()); idv2.set_mstr(p.parse() ? p.element().disown() : cs_dup_ostr("")); idv3.set_mstr(p.parse() ? p.element().disown() : cs_dup_ostr("")); idv1.push(); idv2.push(); idv3.push(); cs.run_int(body); } }); cs.add_command("looplistconcat", "rse", [&cs]( CsValueRange args, CsValue &res ) { cs_loop_list_conc( cs, res, args[0].get_ident(), args[1].get_strr(), args[2].get_code(), true ); }); cs.add_command("looplistconcatword", "rse", [&cs]( CsValueRange args, CsValue &res ) { cs_loop_list_conc( cs, res, args[0].get_ident(), args[1].get_strr(), args[2].get_code(), false ); }); cs.add_command("listfilter", "rse", [&cs](CsValueRange args, CsValue &res) { CsStackedValue idv{args[0].get_ident()}; if (!idv.has_alias()) { return; } auto body = args[2].get_code(); CsVector r; int n = 0; for (util::ListParser p(args[1].get_strr()); p.parse(); ++n) { char *val = cs_dup_ostr(p.item); idv.set_mstr(val); idv.push(); if (cs.run_bool(body)) { if (r.size()) { r.push(' '); } r.push_n(p.quote.data(), p.quote.size()); } } r.push('\0'); ostd::Size len = r.size() - 1; res.set_mstr(ostd::CharRange(r.disown(), len)); }); cs.add_command("listcount", "rse", [&cs](CsValueRange args, CsValue &res) { CsStackedValue idv{args[0].get_ident()}; if (!idv.has_alias()) { return; } auto body = args[2].get_code(); int n = 0, r = 0; for (util::ListParser p(args[1].get_strr()); p.parse(); ++n) { char *val = cs_dup_ostr(p.item); idv.set_mstr(val); idv.push(); if (cs.run_bool(body)) { r++; } } res.set_int(r); }); cs.add_command("prettylist", "ss", [](CsValueRange args, CsValue &res) { CsVector 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 (util::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; res.set_mstr(ostd::CharRange(buf.disown(), slen)); }); cs.add_command("indexof", "ss", [](CsValueRange args, CsValue &res) { res.set_int( cs_list_includes(args[0].get_strr(), args[1].get_strr()) ); }); cs.add_command("listdel", "ss", [](CsValueRange args, CsValue &res) { cs_list_merge(args, res, ostd::Less()); }); cs.add_command("listintersect", "ss", [](CsValueRange args, CsValue &res) { cs_list_merge(args, res, ostd::GreaterEqual()); }); cs.add_command("listunion", "ss", [](CsValueRange args, CsValue &res) { cs_list_merge(args, res, ostd::Less()); }); cs.add_command("listsplice", "ssii", [](CsValueRange args, CsValue &res) { CsInt offset = ostd::max(args[2].get_int(), 0); CsInt 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(); util::ListParser p(s); for (CsInt i = 0; i < offset; ++i) { if (!p.parse()) { break; } } char const *qend = !p.quote.empty() ? &p.quote[p.quote.size()] : list; CsVector 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 (CsInt 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; res.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; CsStackedValue &xv, &yv; CsBytecode *body; bool operator()(ListSortItem const &xval, ListSortItem const &yval) { xv.set_cstr(xval.str); yv.set_cstr(yval.str); xv.push(); yv.push(); return cs.run_bool(body); } }; static void cs_list_sort( CsState &cs, CsValue &res, ostd::ConstCharRange list, CsIdent *x, CsIdent *y, CsBytecode *body, CsBytecode *unique ) { if (x == y || !x->is_alias() || !y->is_alias()) { return; } CsAlias *xa = static_cast(x), *ya = static_cast(y); CsVector items; ostd::Size clen = list.size(); ostd::Size total = 0; char *cstr = cs_dup_ostr(list); for (util::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()) { res.set_mstr(cstr); return; } CsStackedValue xval{xa}, yval{ya}; xval.set_null(); yval.set_null(); xval.push(); yval.push(); ostd::Size totaluniq = total; ostd::Size nuniq = items.size(); if (body) { ListSortFun f = { cs, xval, yval, body }; ostd::sort_cmp(items.iter(), f); if (!cs_code_is_empty(unique)) { 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, xval, yval, 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; } } } xval.pop(); yval.pop(); 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'; res.set_mstr(sorted); } static void cs_init_lib_list_sort(CsState &cs) { cs.add_command("sortlist", "srree", [&cs]( CsValueRange args, CsValue &res ) { cs_list_sort( cs, res, 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]( CsValueRange args, CsValue &res ) { cs_list_sort( cs, res, args[0].get_strr(), args[1].get_ident(), args[2].get_ident(), nullptr, args[3].get_code() ); }); } } /* namespace cscript */