#include #include #include #include "cs_std.hh" #include "cs_parser.hh" namespace cubescript { template struct arg_val; template<> struct arg_val { static integer_type get(any_value &tv) { return tv.get_int(); } }; template<> struct arg_val { static float_type get(any_value &tv) { return tv.get_float(); } }; template<> struct arg_val { static std::string_view get(any_value &tv) { return tv.get_str(); } }; template static inline void list_find( state &cs, std::span args, any_value &res, F cmp ) { integer_type n = 0, skip = args[2].get_int(); T val = arg_val::get(args[1]); for (list_parser p{cs, args[0].get_str()}; 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 list_assoc( state &cs, std::span args, any_value &res, F cmp ) { T val = arg_val::get(args[1]); for (list_parser p{cs, args[0].get_str()}; p.parse();) { if (cmp(p, val)) { if (p.parse()) { res.set_str(p.get_item()); } return; } if (!p.parse()) { break; } } } static void loop_list_conc( state &cs, any_value &res, ident *id, std::string_view list, bcode *body, bool space ) { stacked_value idv{cs, id}; if (!idv.has_alias()) { return; } charbuf r{cs}; int n = 0; for (list_parser p{cs, list}; p.parse(); ++n) { idv.set_str(p.get_item()); idv.push(); if (n && space) { r.push_back(' '); } any_value v{cs}; switch (cs.run_loop(body, v)) { case loop_state::BREAK: goto end; case loop_state::CONTINUE: continue; default: break; } r.append(v.get_str()); } end: res.set_str(r.str()); } int list_includes( state &cs, std::string_view list, std::string_view needle ) { int offset = 0; for (list_parser p{cs, list}; p.parse();) { if (p.get_raw_item() == needle) { return offset; } ++offset; } return -1; } template static inline void list_merge( state &cs, std::span args, any_value &res, F cmp ) { std::string_view list = args[0].get_str(); std::string_view elems = args[1].get_str(); charbuf buf{cs}; if (PushList) { buf.append(list); } if (Swap) { std::swap(list, elems); } for (list_parser p{cs, list}; p.parse();) { if (cmp(list_includes(cs, elems, p.get_raw_item()), 0)) { if (!buf.empty()) { buf.push_back(' '); } buf.append(p.get_quoted_item()); } } res.set_str(buf.str()); } static void init_lib_list_sort(state &cs); void init_lib_list(state &gcs) { gcs.new_command("listlen", "s", [](auto &cs, auto args, auto &res) { res.set_int(integer_type(list_parser{cs, args[0].get_str()}.count())); }); gcs.new_command("at", "si1V", [](auto &cs, auto args, auto &res) { if (args.empty()) { return; } if (args.size() <= 1) { res = args[0]; return; } auto str = args[0].get_str(); list_parser p{cs, str}; for (size_t i = 1; i < args.size(); ++i) { p.set_input(str); integer_type pos = args[i].get_int(); for (; pos > 0; --pos) { if (!p.parse()) { break; } } if (pos > 0 || !p.parse()) { p.set_input(""); } } res.set_str(p.get_item()); }); gcs.new_command("sublist", "siiN", [](auto &cs, auto args, auto &res) { integer_type skip = args[1].get_int(), count = args[2].get_int(), numargs = args[3].get_int(); integer_type offset = std::max(skip, integer_type(0)), len = (numargs >= 3) ? std::max(count, integer_type(0)) : -1; list_parser p{cs, args[0].get_str()}; for (integer_type i = 0; i < offset; ++i) { if (!p.parse()) break; } if (len < 0) { if (offset > 0) { p.skip_until_item(); } res.set_str(p.get_input()); return; } char const *list = p.get_input().data(); if (len > 0 && p.parse()) { while (--len > 0 && p.parse()); } else { res.set_str(""); return; } auto quote = p.get_quoted_item(); auto *qend = "e[quote.size()]; res.set_str(std::string_view{list, qend}); }); gcs.new_command("listfind", "rse", [](auto &cs, auto args, auto &res) { stacked_value idv{cs, args[0].get_ident()}; if (!idv.has_alias()) { res.set_int(-1); return; } auto body = args[2].get_code(); int n = -1; for (list_parser p{cs, args[1].get_str()}; p.parse();) { ++n; idv.set_str(p.get_raw_item()); idv.push(); if (cs.run(body).get_bool()) { res.set_int(integer_type(n)); return; } } res.set_int(-1); }); gcs.new_command("listassoc", "rse", [](auto &cs, auto args, auto &res) { stacked_value idv{cs, args[0].get_ident()}; if (!idv.has_alias()) { return; } auto body = args[2].get_code(); int n = -1; for (list_parser p{cs, args[1].get_str()}; p.parse();) { ++n; idv.set_str(p.get_raw_item()); idv.push(); if (cs.run(body).get_bool()) { if (p.parse()) { res.set_str(p.get_item()); } break; } if (!p.parse()) { break; } } }); gcs.new_command("listfind=", "i", [](auto &cs, auto args, auto &res) { list_find( cs, args, res, [](list_parser const &p, integer_type val) { return parse_int(p.get_raw_item()) == val; } ); }); gcs.new_command("listfind=f", "f", [](auto &cs, auto args, auto &res) { list_find( cs, args, res, [](list_parser const &p, float_type val) { return parse_float(p.get_raw_item()) == val; } ); }); gcs.new_command("listfind=s", "s", [](auto &cs, auto args, auto &res) { list_find( cs, args, res, [](list_parser const &p, std::string_view val) { return p.get_raw_item() == val; } ); }); gcs.new_command("listassoc=", "i", [](auto &cs, auto args, auto &res) { list_assoc( cs, args, res, [](list_parser const &p, integer_type val) { return parse_int(p.get_raw_item()) == val; } ); }); gcs.new_command("listassoc=f", "f", [](auto &cs, auto args, auto &res) { list_assoc( cs, args, res, [](list_parser const &p, float_type val) { return parse_float(p.get_raw_item()) == val; } ); }); gcs.new_command("listassoc=s", "s", [](auto &cs, auto args, auto &res) { list_assoc( cs, args, res, [](list_parser const &p, std::string_view val) { return p.get_raw_item() == val; } ); }); gcs.new_command("looplist", "rse", [](auto &cs, auto args, auto &) { stacked_value idv{cs, args[0].get_ident()}; if (!idv.has_alias()) { return; } auto body = args[2].get_code(); int n = 0; for (list_parser p{cs, args[1].get_str()}; p.parse(); ++n) { idv.set_str(p.get_item()); idv.push(); switch (cs.run_loop(body)) { case loop_state::BREAK: goto end; default: /* continue and normal */ break; } } end: return; }); gcs.new_command("looplist2", "rrse", [](auto &cs, auto args, auto &) { stacked_value idv1{cs, args[0].get_ident()}; stacked_value idv2{cs, args[1].get_ident()}; if (!idv1.has_alias() || !idv2.has_alias()) { return; } auto body = args[3].get_code(); int n = 0; for (list_parser p{cs, args[2].get_str()}; p.parse(); n += 2) { idv1.set_str(p.get_item()); if (p.parse()) { idv2.set_str(p.get_item()); } else { idv2.set_str(""); } idv1.push(); idv2.push(); switch (cs.run_loop(body)) { case loop_state::BREAK: goto end; default: /* continue and normal */ break; } } end: return; }); gcs.new_command("looplist3", "rrrse", [](auto &cs, auto args, auto &) { stacked_value idv1{cs, args[0].get_ident()}; stacked_value idv2{cs, args[1].get_ident()}; stacked_value idv3{cs, 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 (list_parser p{cs, args[3].get_str()}; p.parse(); n += 3) { idv1.set_str(p.get_item()); if (p.parse()) { idv2.set_str(p.get_item()); } else { idv2.set_str(""); } if (p.parse()) { idv3.set_str(p.get_item()); } else { idv3.set_str(""); } idv1.push(); idv2.push(); idv3.push(); switch (cs.run_loop(body)) { case loop_state::BREAK: goto end; default: /* continue and normal */ break; } } end: return; }); gcs.new_command("looplistconcat", "rse", [](auto &cs, auto args, auto &res) { loop_list_conc( cs, res, args[0].get_ident(), args[1].get_str(), args[2].get_code(), true ); }); gcs.new_command("looplistconcatword", "rse", []( auto &cs, auto args, auto &res ) { loop_list_conc( cs, res, args[0].get_ident(), args[1].get_str(), args[2].get_code(), false ); }); gcs.new_command("listfilter", "rse", [](auto &cs, auto args, auto &res) { stacked_value idv{cs, args[0].get_ident()}; if (!idv.has_alias()) { return; } auto body = args[2].get_code(); charbuf r{cs}; int n = 0; for (list_parser p{cs, args[1].get_str()}; p.parse(); ++n) { idv.set_str(p.get_raw_item()); idv.push(); if (cs.run(body).get_bool()) { if (r.size()) { r.push_back(' '); } r.append(p.get_quoted_item()); } } res.set_str(r.str()); }); gcs.new_command("listcount", "rse", [](auto &cs, auto args, auto &res) { stacked_value idv{cs, args[0].get_ident()}; if (!idv.has_alias()) { return; } auto body = args[2].get_code(); int n = 0, r = 0; for (list_parser p{cs, args[1].get_str()}; p.parse(); ++n) { idv.set_str(p.get_raw_item()); idv.push(); if (cs.run(body).get_bool()) { r++; } } res.set_int(r); }); gcs.new_command("prettylist", "ss", [](auto &cs, auto args, auto &res) { charbuf buf{cs}; std::string_view s = args[0].get_str(); std::string_view conj = args[1].get_str(); list_parser p{cs, s}; size_t len = p.count(); size_t n = 0; for (p.set_input(s); p.parse(); ++n) { auto qi = p.get_quoted_item(); if (!qi.empty() && (qi.front() == '"')) { unescape_string(std::back_inserter(buf), p.get_raw_item()); } else { buf.append(p.get_raw_item()); } if ((n + 1) < len) { if ((len > 2) || conj.empty()) { buf.push_back(','); } if ((n + 2 == len) && !conj.empty()) { buf.push_back(' '); buf.append(conj); } buf.push_back(' '); } } res.set_str(buf.str()); }); gcs.new_command("indexof", "ss", [](auto &cs, auto args, auto &res) { res.set_int( list_includes(cs, args[0].get_str(), args[1].get_str()) ); }); gcs.new_command("listdel", "ss", [](auto &cs, auto args, auto &res) { list_merge(cs, args, res, std::less()); }); gcs.new_command("listintersect", "ss", [](auto &cs, auto args, auto &res) { list_merge(cs, args, res, std::greater_equal()); }); gcs.new_command("listunion", "ss", [](auto &cs, auto args, auto &res) { list_merge(cs, args, res, std::less()); }); gcs.new_command("listsplice", "ssii", [](auto &cs, auto args, auto &res) { integer_type offset = std::max(args[2].get_int(), integer_type(0)); integer_type len = std::max(args[3].get_int(), integer_type(0)); std::string_view s = args[0].get_str(); std::string_view vals = args[1].get_str(); char const *list = s.data(); list_parser p{cs, s}; for (integer_type i = 0; i < offset; ++i) { if (!p.parse()) { break; } } std::string_view quote = p.get_quoted_item(); char const *qend = !quote.empty() ? "e[quote.size()] : list; charbuf buf{cs}; if (qend > list) { buf.append(list, qend); } if (!vals.empty()) { if (!buf.empty()) { buf.push_back(' '); } buf.append(vals); } for (integer_type i = 0; i < len; ++i) { if (!p.parse()) { break; } } p.skip_until_item(); if (!p.get_input().empty()) { switch (p.get_input().front()) { case ')': case ']': break; default: if (!buf.empty()) { buf.push_back(' '); } buf.append(p.get_input()); break; } } res.set_str(buf.str()); }); init_lib_list_sort(gcs); } struct ListSortItem { std::string_view str; std::string_view quote; }; struct ListSortFun { state &cs; stacked_value &xv, &yv; bcode *body; bool operator()(ListSortItem const &xval, ListSortItem const &yval) { xv.set_str(xval.str); yv.set_str(yval.str); xv.push(); yv.push(); return cs.run(body).get_bool(); } }; static void list_sort( state &cs, any_value &res, std::string_view list, ident *x, ident *y, bcode *body, bcode *unique ) { if (x == y || !x->is_alias() || !y->is_alias()) { return; } alias *xa = static_cast(x), *ya = static_cast(y); valbuf items{cs}; size_t total = 0; for (list_parser p{cs, list}; p.parse();) { ListSortItem item = { p.get_raw_item(), p.get_quoted_item() }; items.push_back(item); total += item.quote.size(); } if (items.empty()) { res.set_str(list); return; } stacked_value xval{cs, xa}, yval{cs, ya}; xval.set_none(); yval.set_none(); xval.push(); yval.push(); size_t totaluniq = total; size_t nuniq = items.size(); if (body) { ListSortFun f = { cs, xval, yval, body }; std::sort(items.buf.begin(), items.buf.end(), f); if (!code_is_empty(unique)) { f.body = unique; totaluniq = items[0].quote.size(); nuniq = 1; for (size_t i = 1; i < items.size(); i++) { ListSortItem &item = items[i]; if (f(items[i - 1], item)) { item.quote = std::string_view{}; } else { totaluniq += item.quote.size(); ++nuniq; } } } } else { ListSortFun f = { cs, xval, yval, unique }; totaluniq = items[0].quote.size(); nuniq = 1; for (size_t i = 1; i < items.size(); i++) { ListSortItem &item = items[i]; for (size_t j = 0; j < i; ++j) { ListSortItem &prev = items[j]; if (!prev.quote.empty() && f(item, prev)) { item.quote = std::string_view{}; break; } } if (!item.quote.empty()) { totaluniq += item.quote.size(); ++nuniq; } } } xval.pop(); yval.pop(); charbuf sorted{cs}; sorted.reserve(totaluniq + std::max(nuniq - 1, size_t(0))); for (size_t i = 0; i < items.size(); ++i) { ListSortItem &item = items[i]; if (item.quote.empty()) { continue; } if (i) { sorted.push_back(' '); } sorted.append(item.quote); } res.set_str(sorted.str()); } static void init_lib_list_sort(state &gcs) { gcs.new_command("sortlist", "srree", [](auto &cs, auto args, auto &res) { list_sort( cs, res, args[0].get_str(), args[1].get_ident(), args[2].get_ident(), args[3].get_code(), args[4].get_code() ); }); gcs.new_command("uniquelist", "srre", [](auto &cs, auto args, auto &res) { list_sort( cs, res, args[0].get_str(), args[1].get_ident(), args[2].get_ident(), nullptr, args[3].get_code() ); }); } } /* namespace cubescript */