diff --git a/include/cubescript/cubescript.hh b/include/cubescript/cubescript.hh index dbf0ce9..4ef36f0 100644 --- a/include/cubescript/cubescript.hh +++ b/include/cubescript/cubescript.hh @@ -75,6 +75,7 @@ struct OSTD_EXPORT cs_strref { cs_strref() = delete; cs_strref(cs_shared_state &cs, ostd::string_range str); + cs_strref(cs_state &cs, ostd::string_range str); cs_strref(cs_strref const &ref); @@ -379,6 +380,7 @@ static inline void *cs_default_alloc(void *, void *p, size_t, size_t ns) { struct OSTD_EXPORT cs_state { friend struct cs_error; friend struct cs_strman; + friend struct cs_strref; friend struct cs_value; friend struct cs_gen_state; @@ -649,6 +651,19 @@ private: bool p_pushed; }; +struct cs_list_parse_state { + cs_list_parse_state(ostd::string_range s = ostd::string_range{}): input{s} {} + + ostd::string_range input{}; + ostd::string_range item{}; + ostd::string_range quoted_item{}; +}; + +OSTD_EXPORT bool list_parse(cs_list_parse_state &ps, cs_state &cs); +OSTD_EXPORT std::size_t list_count(cs_list_parse_state &ps, cs_state &cs); +OSTD_EXPORT cs_strref list_get_item(cs_list_parse_state &ps, cs_state &cs); +OSTD_EXPORT void list_find_item(cs_list_parse_state &ps); + namespace util { template inline R &&escape_string(R &&writer, ostd::string_range str) { @@ -743,90 +758,6 @@ namespace util { cs_state &cs, ostd::string_range str ); - struct list_range; - - struct OSTD_EXPORT list_parser { - list_parser() = delete; - list_parser(cs_state &cs, ostd::string_range src): - p_state(cs), p_input(src) - {} - - void skip(); - bool parse(); - size_t count(); - - template - R &&get_item(R &&writer) const { - if (!p_quote.empty() && (*p_quote == '"')) { - return unescape_string(std::forward(writer), p_item); - } else { - ostd::range_put_all(writer, p_item); - return std::forward(writer); - } - } - - cs_string get_item() const { - return std::move(get_item(ostd::appender()).get()); - } - - ostd::string_range &get_raw_item(bool quoted = false) { - return quoted ? p_quote : p_item; - } - - ostd::string_range const &get_raw_item(bool quoted = false) const { - return quoted ? p_quote : p_item; - } - - ostd::string_range &get_input() { - return p_input; - } - - list_range iter() noexcept; - -private: - ostd::string_range p_quote = ostd::string_range(); - ostd::string_range p_item = ostd::string_range(); - cs_state &p_state; - ostd::string_range p_input; - }; - - struct list_range: ostd::input_range { - using range_category = ostd::forward_range_tag; - using value_type = ostd::string_range; - using reference = ostd::string_range; - using size_type = std::size_t; - - list_range() = delete; - - list_range(list_parser &p) noexcept: p_parser(&p) { - pop_front(); - } - - bool empty() const noexcept { - return !bool(p_item); - } - - void pop_front() noexcept { - if (p_parser->parse()) { - p_item = p_parser->get_item(); - } else { - p_item.reset(); - } - } - - ostd::string_range front() const noexcept { - return *p_item; - } - - private: - list_parser *p_parser; - std::optional p_item{}; - }; - - inline list_range list_parser::iter() noexcept { - return list_range{*this}; - } - template inline void format_int(R &&writer, cs_int val) { try { diff --git a/src/cs_gen.cc b/src/cs_gen.cc index 17dcc4e..a61b2cb 100644 --- a/src/cs_gen.cc +++ b/src/cs_gen.cc @@ -17,14 +17,10 @@ ostd::string_range cs_gen_state::get_str() { return ret.slice(1, ret.size() - 1); } -cs_string cs_gen_state::get_str_dup(bool unescape) { +cs_string cs_gen_state::get_str_dup() { auto str = get_str(); auto app = ostd::appender(); - if (unescape) { - util::unescape_string(app, str); - } else { - app.get() = str; - } + util::unescape_string(app, str); return std::move(app.get()); } diff --git a/src/cs_util.cc b/src/cs_util.cc index 69c1d71..26015f3 100644 --- a/src/cs_util.cc +++ b/src/cs_util.cc @@ -357,107 +357,120 @@ end: } return str; } - - void list_parser::skip() { - for (;;) { - while (!p_input.empty()) { - char c = *p_input; - if ((c == ' ') || (c == '\t') || (c == '\r') || (c == '\n')) { - ++p_input; - } else { - break; - } - } - if ((p_input.size() < 2) || (p_input[0] != '/') || (p_input[1] != '/')) { - break; - } - p_input = ostd::find(p_input, '\n'); - } - } - - bool list_parser::parse() { - skip(); - if (p_input.empty()) { - return false; - } - switch (*p_input) { - case '"': - p_quote = p_input; - p_input = parse_string(p_state, p_input); - p_quote = p_quote.slice(0, &p_input[0] - &p_quote[0]); - p_item = p_quote.slice(1, p_quote.size() - 1); - break; - case '(': - case '[': { - p_quote = p_input; - ++p_input; - p_item = p_input; - char btype = *p_quote; - int brak = 1; - for (;;) { - p_input = ostd::find_one_of( - p_input, ostd::string_range("\"/;()[]") - ); - if (p_input.empty()) { - return true; - } - char c = *p_input; - ++p_input; - switch (c) { - case '"': - p_input = parse_string(p_state, p_input); - break; - case '/': - if (!p_input.empty() && (*p_input == '/')) { - p_input = ostd::find(p_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: - p_item = p_item.slice(0, &p_input[0] - &p_item[0]); - p_item.pop_back(); - p_quote = p_quote.slice(0, &p_input[0] - &p_quote[0]); - break; - } - case ')': - case ']': - return false; - default: { - ostd::string_range e = parse_word(p_state, p_input); - p_quote = p_item = p_input.slice(0, &e[0] - &p_input[0]); - p_input = e; - break; - } - } - skip(); - if (!p_input.empty() && (*p_input == ';')) { - ++p_input; - } - return true; - } - - size_t list_parser::count() { - size_t ret = 0; - while (parse()) { - ++ret; - } - return ret; - } } /* namespace util */ +OSTD_EXPORT bool list_parse(cs_list_parse_state &ps, cs_state &cs) { + list_find_item(ps); + if (ps.input.empty()) { + return false; + } + switch (*ps.input) { + case '"': + ps.quoted_item = ps.input; + ps.input = util::parse_string(cs, ps.input); + ps.quoted_item = ps.quoted_item.slice( + 0, &ps.input[0] - &ps.quoted_item[0] + ); + ps.item = ps.quoted_item.slice(1, ps.quoted_item.size() - 1); + break; + case '(': + case '[': { + ps.quoted_item = ps.input; + ++ps.input; + ps.item = ps.input; + char btype = *ps.quoted_item; + int brak = 1; + for (;;) { + ps.input = ostd::find_one_of( + ps.input, ostd::string_range("\"/;()[]") + ); + if (ps.input.empty()) { + return true; + } + char c = *ps.input; + ++ps.input; + switch (c) { + case '"': + ps.input = util::parse_string(cs, ps.input); + break; + case '/': + if (!ps.input.empty() && (*ps.input == '/')) { + ps.input = ostd::find(ps.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: + ps.item = ps.item.slice(0, &ps.input[0] - &ps.item[0]); + ps.item.pop_back(); + ps.quoted_item = ps.quoted_item.slice( + 0, &ps.input[0] - &ps.quoted_item[0] + ); + break; + } + case ')': + case ']': + return false; + default: { + ostd::string_range e = util::parse_word(cs, ps.input); + ps.quoted_item = ps.item = ps.input.slice(0, &e[0] - &ps.input[0]); + ps.input = e; + break; + } + } + list_find_item(ps); + if (!ps.input.empty() && (*ps.input == ';')) { + ++ps.input; + } + return true; +} + +OSTD_EXPORT std::size_t list_count(cs_list_parse_state &ps, cs_state &cs) { + size_t ret = 0; + while (list_parse(ps, cs)) { + ++ret; + } + return ret; +} + +OSTD_EXPORT cs_strref list_get_item(cs_list_parse_state &ps, cs_state &cs) { + if (!ps.quoted_item.empty() && (*ps.quoted_item == '"')) { + auto app = ostd::appender(); + util::unescape_string(app, ps.item); + return cs_strref{cs, app.get()}; + } + return cs_strref{cs, ps.item}; +} + +OSTD_EXPORT void list_find_item(cs_list_parse_state &ps) { + for (;;) { + while (!ps.input.empty()) { + char c = *ps.input; + if ((c == ' ') || (c == '\t') || (c == '\r') || (c == '\n')) { + ++ps.input; + } else { + break; + } + } + if ((ps.input.size() < 2) || (ps.input[0] != '/') || (ps.input[1] != '/')) { + break; + } + ps.input = ostd::find(ps.input, '\n'); + } +} + } /* namespace cscript */ diff --git a/src/cs_vm.cc b/src/cs_vm.cc index 4b39fd4..655502f 100644 --- a/src/cs_vm.cc +++ b/src/cs_vm.cc @@ -13,6 +13,12 @@ cs_strref::cs_strref(cs_shared_state &cs, ostd::string_range str): p_str = cs.strman->add(str); } +cs_strref::cs_strref(cs_state &cs, ostd::string_range str): + p_state{cs.p_state} +{ + p_str = p_state->strman->add(str); +} + cs_strref::cs_strref(cs_strref const &ref): p_state{ref.p_state}, p_str{ref.p_str} { p_state->strman->ref(p_str); diff --git a/src/cs_vm.hh b/src/cs_vm.hh index 7dba878..5c049c2 100644 --- a/src/cs_vm.hh +++ b/src/cs_vm.hh @@ -195,7 +195,7 @@ struct cs_gen_state { } ostd::string_range get_str(); - cs_string get_str_dup(bool unescape = true); + cs_string get_str_dup(); ostd::string_range get_word(); diff --git a/src/lib_list.cc b/src/lib_list.cc index 256c34a..1ffdacb 100644 --- a/src/lib_list.cc +++ b/src/lib_list.cc @@ -35,13 +35,13 @@ static inline void cs_list_find( ) { cs_int n = 0, skip = args[2].get_int(); T val = cs_arg_val::get(args[1]); - for (util::list_parser p(cs, args[0].get_str()); p.parse(); ++n) { + for (cs_list_parse_state p{args[0].get_str()}; list_parse(p, cs); ++n) { if (cmp(p, val)) { res.set_int(n); return; } for (int i = 0; i < skip; ++i) { - if (!p.parse()) { + if (!list_parse(p, cs)) { goto notfound; } ++n; @@ -56,14 +56,14 @@ static inline void cs_list_assoc( cs_state &cs, cs_value_r args, cs_value &res, F cmp ) { T val = cs_arg_val::get(args[1]); - for (util::list_parser p(cs, args[0].get_str()); p.parse();) { + for (cs_list_parse_state p{args[0].get_str()}; list_parse(p, cs);) { if (cmp(p, val)) { - if (p.parse()) { - res.set_str(p.get_item()); + if (list_parse(p, cs)) { + res.set_str(list_get_item(p, cs)); } return; } - if (!p.parse()) { + if (!list_parse(p, cs)) { break; } } @@ -79,8 +79,8 @@ static void cs_loop_list_conc( } cs_string r; int n = 0; - for (util::list_parser p(cs, list); p.parse(); ++n) { - idv.set_str(p.get_item()); + for (cs_list_parse_state p{list}; list_parse(p, cs); ++n) { + idv.set_str(list_get_item(p, cs)); idv.push(); if (n && space) { r += ' '; @@ -104,8 +104,8 @@ int cs_list_includes( cs_state &cs, ostd::string_range list, ostd::string_range needle ) { int offset = 0; - for (util::list_parser p(cs, list); p.parse();) { - if (p.get_raw_item() == needle) { + for (cs_list_parse_state p{list}; list_parse(p, cs);) { + if (p.item == needle) { return offset; } ++offset; @@ -126,12 +126,12 @@ static inline void cs_list_merge( if (Swap) { std::swap(list, elems); } - for (util::list_parser p(cs, list); p.parse();) { - if (cmp(cs_list_includes(cs, elems, p.get_raw_item()), 0)) { + for (cs_list_parse_state p{list}; list_parse(p, cs);) { + if (cmp(cs_list_includes(cs, elems, p.item), 0)) { if (!buf.empty()) { buf += ' '; } - buf += p.get_raw_item(true); + buf += p.quoted_item; } } res.set_str(buf); @@ -141,7 +141,8 @@ static void cs_init_lib_list_sort(cs_state &cs); void cs_init_lib_list(cs_state &gcs) { gcs.new_command("listlen", "s", [](auto &cs, auto args, auto &res) { - res.set_int(cs_int(util::list_parser(cs, args[0].get_str()).count())); + cs_list_parse_state p{args[0].get_str()}; + res.set_int(cs_int(list_count(p, cs))); }); gcs.new_command("at", "si1V", [](auto &cs, auto args, auto &res) { @@ -149,51 +150,51 @@ void cs_init_lib_list(cs_state &gcs) { return; } cs_strref str = args[0].get_str(); - util::list_parser p(cs, str); - p.get_raw_item() = str; + cs_list_parse_state p{str}; + p.item = str; for (size_t i = 1; i < args.size(); ++i) { - p.get_input() = str; + p.input = str; cs_int pos = args[i].get_int(); for (; pos > 0; --pos) { - if (!p.parse()) { + if (!list_parse(p, cs)) { break; } } - if (pos > 0 || !p.parse()) { - p.get_raw_item() = p.get_raw_item(true) = ostd::string_range(); + if (pos > 0 || !list_parse(p, cs)) { + p.item = p.quoted_item = ostd::string_range(); } } - res.set_str(p.get_item()); + res.set_str(list_get_item(p, cs)); }); gcs.new_command("sublist", "siiN", [](auto &cs, auto args, auto &res) { - cs_int skip = args[1].get_int(), + cs_int skip = args[1].get_int(), count = args[2].get_int(), numargs = args[3].get_int(); cs_int offset = std::max(skip, cs_int(0)), len = (numargs >= 3) ? std::max(count, cs_int(0)) : -1; - util::list_parser p(cs, args[0].get_str()); + cs_list_parse_state p{args[0].get_str()}; for (cs_int i = 0; i < offset; ++i) { - if (!p.parse()) break; + if (!list_parse(p, cs)) break; } if (len < 0) { if (offset > 0) { - p.skip(); + list_find_item(p); } - res.set_str(cs_string{p.get_input()}); + res.set_str(cs_string{p.input}); return; } - char const *list = p.get_input().data(); - p.get_raw_item(true) = ostd::string_range(); - if (len > 0 && p.parse()) { - while (--len > 0 && p.parse()); + char const *list = p.input.data(); + p.quoted_item = ostd::string_range(); + if (len > 0 && list_parse(p, cs)) { + while (--len > 0 && list_parse(p, cs)); } - ostd::string_range quote = p.get_raw_item(true); + ostd::string_range quote = p.quoted_item; char const *qend = !quote.empty() ? "e[quote.size()] : list; - res.set_str(cs_string{list, size_t(qend - list)}); + res.set_str(ostd::string_range{list, qend}); }); gcs.new_command("listfind", "rse", [](auto &cs, auto args, auto &res) { @@ -204,9 +205,9 @@ void cs_init_lib_list(cs_state &gcs) { } auto body = args[2].get_code(); int n = -1; - for (util::list_parser p(cs, args[1].get_str()); p.parse();) { + for (cs_list_parse_state p{args[1].get_str()}; list_parse(p, cs);) { ++n; - idv.set_str(cs_string{p.get_raw_item()}); + idv.set_str(cs_string{p.item}); idv.push(); if (cs.run_bool(body)) { res.set_int(cs_int(n)); @@ -223,17 +224,17 @@ void cs_init_lib_list(cs_state &gcs) { } auto body = args[2].get_code(); int n = -1; - for (util::list_parser p(cs, args[1].get_str()); p.parse();) { + for (cs_list_parse_state p{args[1].get_str()}; list_parse(p, cs);) { ++n; - idv.set_str(cs_string{p.get_raw_item()}); + idv.set_str(cs_string{p.item}); idv.push(); if (cs.run_bool(body)) { - if (p.parse()) { - res.set_str(p.get_item()); + if (list_parse(p, cs)) { + res.set_str(list_get_item(p, cs)); } break; } - if (!p.parse()) { + if (!list_parse(p, cs)) { break; } } @@ -241,44 +242,44 @@ void cs_init_lib_list(cs_state &gcs) { gcs.new_command("listfind=", "i", [](auto &cs, auto args, auto &res) { cs_list_find( - cs, args, res, [](const util::list_parser &p, cs_int val) { - return cs_parse_int(p.get_raw_item()) == val; + cs, args, res, [](cs_list_parse_state const &p, cs_int val) { + return cs_parse_int(p.item) == val; } ); }); gcs.new_command("listfind=f", "f", [](auto &cs, auto args, auto &res) { cs_list_find( - cs, args, res, [](const util::list_parser &p, cs_float val) { - return cs_parse_float(p.get_raw_item()) == val; + cs, args, res, [](cs_list_parse_state const &p, cs_float val) { + return cs_parse_float(p.item) == val; } ); }); gcs.new_command("listfind=s", "s", [](auto &cs, auto args, auto &res) { cs_list_find( - cs, args, res, [](const util::list_parser &p, ostd::string_range val) { - return p.get_raw_item() == val; + cs, args, res, [](cs_list_parse_state const &p, ostd::string_range val) { + return p.item == val; } ); }); gcs.new_command("listassoc=", "i", [](auto &cs, auto args, auto &res) { cs_list_assoc( - cs, args, res, [](const util::list_parser &p, cs_int val) { - return cs_parse_int(p.get_raw_item()) == val; + cs, args, res, [](cs_list_parse_state const &p, cs_int val) { + return cs_parse_int(p.item) == val; } ); }); gcs.new_command("listassoc=f", "f", [](auto &cs, auto args, auto &res) { cs_list_assoc( - cs, args, res, [](const util::list_parser &p, cs_float val) { - return cs_parse_float(p.get_raw_item()) == val; + cs, args, res, [](cs_list_parse_state const &p, cs_float val) { + return cs_parse_float(p.item) == val; } ); }); gcs.new_command("listassoc=s", "s", [](auto &cs, auto args, auto &res) { cs_list_assoc( - cs, args, res, [](const util::list_parser &p, ostd::string_range val) { - return p.get_raw_item() == val; + cs, args, res, [](cs_list_parse_state const &p, ostd::string_range val) { + return p.item == val; } ); }); @@ -290,8 +291,8 @@ void cs_init_lib_list(cs_state &gcs) { } auto body = args[2].get_code(); int n = 0; - for (util::list_parser p(cs, args[1].get_str()); p.parse(); ++n) { - idv.set_str(p.get_item()); + for (cs_list_parse_state p{args[1].get_str()}; list_parse(p, cs); ++n) { + idv.set_str(list_get_item(p, cs)); idv.push(); switch (cs.run_loop(body)) { case cs_loop_state::BREAK: @@ -312,10 +313,10 @@ end: } auto body = args[3].get_code(); int n = 0; - for (util::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()); + for (cs_list_parse_state p{args[2].get_str()}; list_parse(p, cs); n += 2) { + idv1.set_str(list_get_item(p, cs)); + if (list_parse(p, cs)) { + idv2.set_str(list_get_item(p, cs)); } else { idv2.set_str(""); } @@ -341,15 +342,15 @@ end: } auto body = args[4].get_code(); int n = 0; - for (util::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()); + for (cs_list_parse_state p{args[3].get_str()}; list_parse(p, cs); n += 3) { + idv1.set_str(list_get_item(p, cs)); + if (list_parse(p, cs)) { + idv2.set_str(list_get_item(p, cs)); } else { idv2.set_str(""); } - if (p.parse()) { - idv3.set_str(p.get_item()); + if (list_parse(p, cs)) { + idv3.set_str(list_get_item(p, cs)); } else { idv3.set_str(""); } @@ -391,14 +392,14 @@ end: auto body = args[2].get_code(); cs_string r; int n = 0; - for (util::list_parser p(cs, args[1].get_str()); p.parse(); ++n) { - idv.set_str(cs_string{p.get_raw_item()}); + for (cs_list_parse_state p{args[1].get_str()}; list_parse(p, cs); ++n) { + idv.set_str(p.item); idv.push(); if (cs.run_bool(body)) { if (r.size()) { r += ' '; } - r += p.get_raw_item(true); + r += p.quoted_item; } } res.set_str(r); @@ -411,8 +412,8 @@ end: } auto body = args[2].get_code(); int n = 0, r = 0; - for (util::list_parser p(cs, args[1].get_str()); p.parse(); ++n) { - idv.set_str(cs_string{p.get_raw_item()}); + for (cs_list_parse_state p{args[1].get_str()}; list_parse(p, cs); ++n) { + idv.set_str(p.item); idv.push(); if (cs.run_bool(body)) { r++; @@ -425,14 +426,14 @@ end: auto buf = ostd::appender(); ostd::string_range s = args[0].get_str(); ostd::string_range conj = args[1].get_str(); - size_t len = util::list_parser(cs, s).count(); + cs_list_parse_state p{s}; + size_t len = list_count(p, cs); size_t n = 0; - for (util::list_parser p(cs, s); p.parse(); ++n) { - if (!p.get_raw_item(true).empty() && - (p.get_raw_item(true).front() == '"')) { - util::unescape_string(buf, p.get_raw_item()); + for (p.input = s; list_parse(p, cs); ++n) { + if (!p.quoted_item.empty() && (p.quoted_item.front() == '"')) { + util::unescape_string(buf, p.item); } else { - ostd::range_put_all(buf, p.get_raw_item()); + ostd::range_put_all(buf, p.item); } if ((n + 1) < len) { if ((len > 2) || conj.empty()) { @@ -470,13 +471,13 @@ end: ostd::string_range s = args[0].get_str(); ostd::string_range vals = args[1].get_str(); char const *list = s.data(); - util::list_parser p(cs, s); + cs_list_parse_state p{s}; for (cs_int i = 0; i < offset; ++i) { - if (!p.parse()) { + if (!list_parse(p, cs)) { break; } } - ostd::string_range quote = p.get_raw_item(true); + ostd::string_range quote = p.quoted_item; char const *qend = !quote.empty() ? "e[quote.size()] : list; cs_string buf; if (qend > list) { @@ -489,13 +490,13 @@ end: buf += vals; } for (cs_int i = 0; i < len; ++i) { - if (!p.parse()) { + if (!list_parse(p, cs)) { break; } } - p.skip(); - if (!p.get_input().empty()) { - switch (p.get_input().front()) { + list_find_item(p); + if (!p.input.empty()) { + switch (p.input.front()) { case ')': case ']': break; @@ -503,7 +504,7 @@ end: if (!buf.empty()) { buf += ' '; } - buf += p.get_input(); + buf += p.input; break; } } @@ -545,14 +546,14 @@ static void cs_list_sort( cs_vector items; size_t total = 0; - for (util::list_parser p(cs, list); p.parse();) { - ListSortItem item = { p.get_raw_item(), p.get_raw_item(true) }; + for (cs_list_parse_state p{list}; list_parse(p, cs);) { + ListSortItem item = { p.item, p.quoted_item }; items.push_back(item); total += item.quote.size(); } if (items.empty()) { - res.set_str(cs_string{list}); + res.set_str(list); return; }