// Copyright 2021 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // See https://github.com/danielaparker/jsoncons for latest version #ifndef JSONCONS_JSONPATH_JSONPATH_SELECTOR_HPP #define JSONCONS_JSONPATH_JSONPATH_SELECTOR_HPP #include #include #include #include // std::is_const #include // std::numeric_limits #include // std::move #include #include #include #include namespace jsoncons { namespace jsonpath { namespace detail { struct slice { jsoncons::optional start_; jsoncons::optional stop_; int64_t step_; slice() : start_(), stop_(), step_(1) { } slice(const jsoncons::optional& start, const jsoncons::optional& end, int64_t step) : start_(start), stop_(end), step_(step) { } slice(const slice& other) : start_(other.start_), stop_(other.stop_), step_(other.step_) { } slice& operator=(const slice& rhs) { if (this != &rhs) { if (rhs.start_) { start_ = rhs.start_; } else { start_.reset(); } if (rhs.stop_) { stop_ = rhs.stop_; } else { stop_.reset(); } step_ = rhs.step_; } return *this; } int64_t get_start(std::size_t size) const { if (start_) { auto len = *start_ >= 0 ? *start_ : (static_cast(size) + *start_); return len <= static_cast(size) ? len : static_cast(size); } else { if (step_ >= 0) { return 0; } else { return static_cast(size); } } } int64_t get_stop(std::size_t size) const { if (stop_) { auto len = *stop_ >= 0 ? *stop_ : (static_cast(size) + *stop_); return len <= static_cast(size) ? len : static_cast(size); } else { return step_ >= 0 ? static_cast(size) : -1; } } int64_t step() const { return step_; // Allow negative } }; template class json_array_receiver : public node_receiver { public: using reference = JsonReference; using char_type = typename Json::char_type; using json_location_node_type = json_location_node; Json* val; json_array_receiver(Json* ptr) : val(ptr) { } void add(const json_location_node_type&, reference value) override { val->emplace_back(value); } }; template struct path_generator { using char_type = typename Json::char_type; using json_location_node_type = json_location_node; using string_type = std::basic_string; static const json_location_node_type& generate(dynamic_resources& resources, const json_location_node_type& last, std::size_t index, result_options options) { const result_options require_path = result_options::path | result_options::nodups | result_options::sort; if ((options & require_path) != result_options()) { return *resources.create_path_node(&last, index); } else { return last; } } static const json_location_node_type& generate(dynamic_resources& resources, const json_location_node_type& last, const string_type& identifier, result_options options) { const result_options require_path = result_options::path | result_options::nodups | result_options::sort; if ((options & require_path) != result_options()) { return *resources.create_path_node(&last, identifier); } else { return last; } } }; template class base_selector : public jsonpath_selector { using supertype = jsonpath_selector; supertype* tail_; public: using value_type = typename supertype::value_type; using reference = typename supertype::reference; using pointer = typename supertype::pointer; using path_value_pair_type = typename supertype::path_value_pair_type; using json_location_node_type = typename supertype::json_location_node_type; using json_location_type = typename supertype::json_location_type; using node_receiver_type = typename supertype::node_receiver_type; using selector_type = typename supertype::selector_type; base_selector() : supertype(true, 11), tail_(nullptr) { } base_selector(bool is_path, std::size_t precedence_level) : supertype(is_path, precedence_level), tail_(nullptr) { } void append_selector(selector_type* expr) override { if (!tail_) { tail_ = expr; } else { tail_->append_selector(expr); } } void tail_select(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, node_receiver_type& receiver, result_options options) const { if (!tail_) { receiver.add(last, current); } else { tail_->select(resources, root, last, current, receiver, options); } } reference evaluate_tail(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, result_options options, std::error_code& ec) const { if (!tail_) { return current; } else { return tail_->evaluate(resources, root, last, current, options, ec); } } std::string to_string(int level = 0) const override { std::string s; if (level > 0) { s.append("\n"); s.append(level*2, ' '); } if (tail_) { s.append(tail_->to_string(level)); } return s; } }; template class identifier_selector final : public base_selector { using supertype = base_selector; using path_generator_type = path_generator; public: using value_type = typename supertype::value_type; using reference = typename supertype::reference; using pointer = typename supertype::pointer; using path_value_pair_type = typename supertype::path_value_pair_type; using json_location_node_type = typename supertype::json_location_node_type; using char_type = typename Json::char_type; using string_type = std::basic_string; using string_view_type = basic_string_view; using node_receiver_type = typename supertype::node_receiver_type; private: string_type identifier_; public: identifier_selector(const string_view_type& identifier) : base_selector(), identifier_(identifier) { } void select(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, node_receiver_type& receiver, result_options options) const override { //std::string buf; //buf.append("identifier selector: "); //unicode_traits::convert(identifier_.data(),identifier_.size(),buf); static const char_type length_name[] = {'l', 'e', 'n', 'g', 't', 'h', 0}; if (current.is_object()) { auto it = current.find(identifier_); if (it != current.object_range().end()) { this->tail_select(resources, root, path_generator_type::generate(resources, last, identifier_, options), it->value(), receiver, options); } } else if (current.is_array()) { int64_t n{0}; auto r = jsoncons::detail::to_integer_decimal(identifier_.data(), identifier_.size(), n); if (r) { std::size_t index = (n >= 0) ? static_cast(n) : static_cast(static_cast(current.size()) + n); if (index < current.size()) { this->tail_select(resources, root, path_generator_type::generate(resources, last, index, options), current[index], receiver, options); } } else if (identifier_ == length_name && current.size() > 0) { pointer ptr = resources.create_json(current.size()); this->tail_select(resources, root, path_generator_type::generate(resources, last, identifier_, options), *ptr, receiver, options); } } else if (current.is_string() && identifier_ == length_name) { string_view_type sv = current.as_string_view(); std::size_t count = unicode_traits::count_codepoints(sv.data(), sv.size()); pointer ptr = resources.create_json(count); this->tail_select(resources, root, path_generator_type::generate(resources, last, identifier_, options), *ptr, receiver, options); } //std::cout << "end identifier_selector\n"; } reference evaluate(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, result_options options, std::error_code& ec) const override { static const char_type length_name[] = {'l', 'e', 'n', 'g', 't', 'h', 0}; if (current.is_object()) { auto it = current.find(identifier_); if (it != current.object_range().end()) { return this->evaluate_tail(resources, root, path_generator_type::generate(resources, last, identifier_, options), it->value(), options, ec); } else { return resources.null_value(); } } else if (current.is_array()) { int64_t n{0}; auto r = jsoncons::detail::to_integer_decimal(identifier_.data(), identifier_.size(), n); if (r) { std::size_t index = (n >= 0) ? static_cast(n) : static_cast(static_cast(current.size()) + n); if (index < current.size()) { return this->evaluate_tail(resources, root, path_generator_type::generate(resources, last, index, options), current[index], options, ec); } else { return resources.null_value(); } } else if (identifier_ == length_name && current.size() > 0) { pointer ptr = resources.create_json(current.size()); return this->evaluate_tail(resources, root, path_generator_type::generate(resources, last, identifier_, options), *ptr, options, ec); } else { return resources.null_value(); } } else if (current.is_string() && identifier_ == length_name) { string_view_type sv = current.as_string_view(); std::size_t count = unicode_traits::count_codepoints(sv.data(), sv.size()); pointer ptr = resources.create_json(count); return this->evaluate_tail(resources, root, path_generator_type::generate(resources, last, identifier_, options), *ptr, options, ec); } else { return resources.null_value(); } } std::string to_string(int level = 0) const override { std::string s; if (level > 0) { s.append("\n"); s.append(level*2, ' '); } s.append("identifier selector "); unicode_traits::convert(identifier_.data(),identifier_.size(),s); s.append(base_selector::to_string(level+1)); //s.append("\n"); return s; } }; template class root_selector final : public base_selector { using supertype = base_selector; using path_generator_type = path_generator; std::size_t id_; public: using value_type = typename supertype::value_type; using reference = typename supertype::reference; using pointer = typename supertype::pointer; using path_value_pair_type = typename supertype::path_value_pair_type; using json_location_node_type = typename supertype::json_location_node_type; using node_receiver_type = typename supertype::node_receiver_type; root_selector(std::size_t id) : base_selector(), id_(id) { } void select(dynamic_resources& resources, reference root, const json_location_node_type& last, reference, node_receiver_type& receiver, result_options options) const override { this->tail_select(resources, root, last, root, receiver, options); } reference evaluate(dynamic_resources& resources, reference root, const json_location_node_type& last, reference, result_options options, std::error_code& ec) const override { if (resources.is_cached(id_)) { return resources.retrieve_from_cache(id_); } else { auto& ref = this->evaluate_tail(resources, root, last, root, options, ec); if (!ec) { resources.add_to_cache(id_, ref); } return ref; } } std::string to_string(int level = 0) const override { std::string s; if (level > 0) { s.append("\n"); s.append(level*2, ' '); } s.append("root_selector "); s.append(base_selector::to_string(level+1)); return s; } }; template class current_node_selector final : public base_selector { using supertype = base_selector; public: using value_type = typename supertype::value_type; using reference = typename supertype::reference; using pointer = typename supertype::pointer; using path_value_pair_type = typename supertype::path_value_pair_type; using json_location_node_type = typename supertype::json_location_node_type; using path_generator_type = path_generator; using node_receiver_type = typename supertype::node_receiver_type; current_node_selector() { } void select(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, node_receiver_type& receiver, result_options options) const override { this->tail_select(resources, root, last, current, receiver, options); } reference evaluate(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, result_options options, std::error_code& ec) const override { //std::cout << "current_node_selector: " << current << "\n"; return this->evaluate_tail(resources, root, last, current, options, ec); } std::string to_string(int level = 0) const override { std::string s; if (level > 0) { s.append("\n"); s.append(level*2, ' '); } s.append("current_node_selector"); s.append(base_selector::to_string(level+1)); return s; } }; template class parent_node_selector final : public base_selector { using supertype = base_selector; int ancestor_depth_; public: using value_type = typename supertype::value_type; using reference = typename supertype::reference; using pointer = typename supertype::pointer; using path_value_pair_type = typename supertype::path_value_pair_type; using json_location_node_type = typename supertype::json_location_node_type; using json_location_type = typename supertype::json_location_type; using path_generator_type = path_generator; using node_receiver_type = typename supertype::node_receiver_type; parent_node_selector(int ancestor_depth) { ancestor_depth_ = ancestor_depth; } void select(dynamic_resources& resources, reference root, const json_location_node_type& last, reference, node_receiver_type& receiver, result_options options) const override { const json_location_node_type* ancestor = std::addressof(last); int index = 0; while (ancestor != nullptr && index < ancestor_depth_) { ancestor = ancestor->parent(); ++index; } if (ancestor != nullptr) { json_location_type path(*ancestor); pointer ptr = jsoncons::jsonpath::select(root,path); if (ptr != nullptr) { this->tail_select(resources, root, path.last(), *ptr, receiver, options); } } } reference evaluate(dynamic_resources& resources, reference root, const json_location_node_type& last, reference, result_options options, std::error_code& ec) const override { const json_location_node_type* ancestor = std::addressof(last); int index = 0; while (ancestor != nullptr && index < ancestor_depth_) { ancestor = ancestor->parent(); ++index; } if (ancestor != nullptr) { json_location_type path(*ancestor); pointer ptr = jsoncons::jsonpath::select(root,path); if (ptr != nullptr) { return this->evaluate_tail(resources, root, path.last(), *ptr, options, ec); } else { return resources.null_value(); } } else { return resources.null_value(); } } std::string to_string(int level = 0) const override { std::string s; if (level > 0) { s.append("\n"); s.append(level*2, ' '); } s.append("parent_node_selector"); s.append(base_selector::to_string(level+1)); return s; } }; template class index_selector final : public base_selector { using supertype = base_selector; int64_t index_; public: using value_type = typename supertype::value_type; using reference = typename supertype::reference; using pointer = typename supertype::pointer; using path_value_pair_type = typename supertype::path_value_pair_type; using json_location_node_type = typename supertype::json_location_node_type; using path_generator_type = path_generator; using node_receiver_type = typename supertype::node_receiver_type; index_selector(int64_t index) : base_selector(), index_(index) { } void select(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, node_receiver_type& receiver, result_options options) const override { if (current.is_array()) { int64_t slen = static_cast(current.size()); if (index_ >= 0 && index_ < slen) { std::size_t i = static_cast(index_); this->tail_select(resources, root, path_generator_type::generate(resources, last, i, options), current.at(i), receiver, options); } else { int64_t index = slen + index_; if (index >= 0 && index < slen) { std::size_t i = static_cast(index); this->tail_select(resources, root, path_generator_type::generate(resources, last, i, options), current.at(i), receiver, options); } } } } reference evaluate(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, result_options options, std::error_code& ec) const override { if (current.is_array()) { int64_t slen = static_cast(current.size()); if (index_ >= 0 && index_ < slen) { std::size_t i = static_cast(index_); return this->evaluate_tail(resources, root, path_generator_type::generate(resources, last, i, options), current.at(i), options, ec); } else { int64_t index = slen + index_; if (index >= 0 && index < slen) { std::size_t i = static_cast(index); return this->evaluate_tail(resources, root, path_generator_type::generate(resources, last, i, options), current.at(i), options, ec); } else { return resources.null_value(); } } } else { return resources.null_value(); } } }; template class wildcard_selector final : public base_selector { using supertype = base_selector; public: using value_type = typename supertype::value_type; using reference = typename supertype::reference; using pointer = typename supertype::pointer; using path_value_pair_type = typename supertype::path_value_pair_type; using json_location_node_type = typename supertype::json_location_node_type; using path_generator_type = path_generator; using node_receiver_type = typename supertype::node_receiver_type; wildcard_selector() : base_selector() { } void select(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, node_receiver_type& receiver, result_options options) const override { if (current.is_array()) { for (std::size_t i = 0; i < current.size(); ++i) { this->tail_select(resources, root, path_generator_type::generate(resources, last, i, options), current[i], receiver, options); } } else if (current.is_object()) { for (auto& member : current.object_range()) { this->tail_select(resources, root, path_generator_type::generate(resources, last, member.key(), options), member.value(), receiver, options); } } //std::cout << "end wildcard_selector\n"; } reference evaluate(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, result_options options, std::error_code&) const override { auto jptr = resources.create_json(json_array_arg); json_array_receiver receiver(jptr); select(resources, root, last, current, receiver, options); return *jptr; } std::string to_string(int level = 0) const override { std::string s; if (level > 0) { s.append("\n"); s.append(level*2, ' '); } s.append("wildcard selector"); s.append(base_selector::to_string(level)); return s; } }; template class recursive_selector final : public base_selector { using supertype = base_selector; public: using value_type = typename supertype::value_type; using reference = typename supertype::reference; using pointer = typename supertype::pointer; using path_value_pair_type = typename supertype::path_value_pair_type; using json_location_node_type = typename supertype::json_location_node_type; using path_generator_type = path_generator; using node_receiver_type = typename supertype::node_receiver_type; recursive_selector() : base_selector() { } void select(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, node_receiver_type& receiver, result_options options) const override { if (current.is_array()) { this->tail_select(resources, root, last, current, receiver, options); for (std::size_t i = 0; i < current.size(); ++i) { select(resources, root, path_generator_type::generate(resources, last, i, options), current[i], receiver, options); } } else if (current.is_object()) { this->tail_select(resources, root, last, current, receiver, options); for (auto& item : current.object_range()) { select(resources, root, path_generator_type::generate(resources, last, item.key(), options), item.value(), receiver, options); } } //std::cout << "end wildcard_selector\n"; } reference evaluate(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, result_options options, std::error_code&) const override { auto jptr = resources.create_json(json_array_arg); json_array_receiver receiver(jptr); select(resources, root, last, current, receiver, options); return *jptr; } std::string to_string(int level = 0) const override { std::string s; if (level > 0) { s.append("\n"); s.append(level*2, ' '); } s.append("wildcard selector"); s.append(base_selector::to_string(level)); return s; } }; template class union_selector final : public jsonpath_selector { using supertype = jsonpath_selector; public: using value_type = typename supertype::value_type; using reference = typename supertype::reference; using pointer = typename supertype::pointer; using path_value_pair_type = typename supertype::path_value_pair_type; using json_location_node_type = typename supertype::json_location_node_type; using json_location_type = typename supertype::json_location_type; using path_expression_type = path_expression; using path_generator_type = path_generator; using node_receiver_type = typename supertype::node_receiver_type; using selector_type = typename supertype::selector_type; private: std::vector selectors_; selector_type* tail_; public: union_selector(std::vector&& selectors) : supertype(true, 11), selectors_(std::move(selectors)), tail_(nullptr) { } void append_selector(selector_type* tail) override { if (tail_ == nullptr) { tail_ = tail; for (auto& selector : selectors_) { selector->append_selector(tail); } } else { tail_->append_selector(tail); } } void select(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, node_receiver_type& receiver, result_options options) const override { for (auto& selector : selectors_) { selector->select(resources, root, last, current, receiver, options); } } reference evaluate(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, result_options options, std::error_code&) const override { auto jptr = resources.create_json(json_array_arg); json_array_receiver receiver(jptr); select(resources,root,last,current,receiver,options); return *jptr; } std::string to_string(int level = 0) const override { std::string s; if (level > 0) { s.append("\n"); s.append(level*2, ' '); } s.append("union selector "); for (auto& selector : selectors_) { s.append(selector->to_string(level+1)); //s.push_back('\n'); } return s; } }; template class filter_selector final : public base_selector { using supertype = base_selector; expression expr_; public: using value_type = typename supertype::value_type; using reference = typename supertype::reference; using pointer = typename supertype::pointer; using path_value_pair_type = typename supertype::path_value_pair_type; using json_location_node_type = typename supertype::json_location_node_type; using path_generator_type = path_generator; using node_receiver_type = typename supertype::node_receiver_type; filter_selector(expression&& expr) : base_selector(), expr_(std::move(expr)) { } void select(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, node_receiver_type& receiver, result_options options) const override { if (current.is_array()) { for (std::size_t i = 0; i < current.size(); ++i) { std::error_code ec; value_type r = expr_.evaluate(resources, root, current[i], options, ec); bool t = ec ? false : detail::is_true(r); if (t) { this->tail_select(resources, root, path_generator_type::generate(resources, last, i, options), current[i], receiver, options); } } } else if (current.is_object()) { for (auto& member : current.object_range()) { std::error_code ec; value_type r = expr_.evaluate(resources, root, member.value(), options, ec); bool t = ec ? false : detail::is_true(r); if (t) { this->tail_select(resources, root, path_generator_type::generate(resources, last, member.key(), options), member.value(), receiver, options); } } } } reference evaluate(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, result_options options, std::error_code&) const override { auto jptr = resources.create_json(json_array_arg); json_array_receiver receiver(jptr); select(resources, root, last, current, receiver, options); return *jptr; } std::string to_string(int level = 0) const override { std::string s; if (level > 0) { s.append("\n"); s.append(level*2, ' '); } s.append("filter selector "); s.append(expr_.to_string(level+1)); return s; } }; template class index_expression_selector final : public base_selector { using supertype = base_selector; expression expr_; public: using value_type = typename supertype::value_type; using reference = typename supertype::reference; using pointer = typename supertype::pointer; using path_value_pair_type = typename supertype::path_value_pair_type; using json_location_node_type = typename supertype::json_location_node_type; using path_generator_type = path_generator; using node_receiver_type = typename supertype::node_receiver_type; index_expression_selector(expression&& expr) : base_selector(), expr_(std::move(expr)) { } void select(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, node_receiver_type& receiver, result_options options) const override { std::error_code ec; value_type j = expr_.evaluate(resources, root, current, options, ec); if (!ec) { if (j.template is() && current.is_array()) { std::size_t start = j.template as(); this->tail_select(resources, root, path_generator_type::generate(resources, last, start, options), current.at(start), receiver, options); } else if (j.is_string() && current.is_object()) { this->tail_select(resources, root, path_generator_type::generate(resources, last, j.as_string(), options), current.at(j.as_string_view()), receiver, options); } } } reference evaluate(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, result_options options, std::error_code& ec) const override { //std::cout << "index_expression_selector current: " << current << "\n"; value_type j = expr_.evaluate(resources, root, current, options, ec); if (!ec) { if (j.template is() && current.is_array()) { std::size_t start = j.template as(); return this->evaluate_tail(resources, root, last, current.at(start), options, ec); } else if (j.is_string() && current.is_object()) { return this->evaluate_tail(resources, root, last, current.at(j.as_string_view()), options, ec); } else { return resources.null_value(); } } else { return resources.null_value(); } } std::string to_string(int level = 0) const override { std::string s; if (level > 0) { s.append("\n"); s.append(level*2, ' '); } s.append("bracket expression selector "); s.append(expr_.to_string(level+1)); s.append(base_selector::to_string(level+1)); return s; } }; template class slice_selector final : public base_selector { using supertype = base_selector; using path_generator_type = path_generator; slice slice_; public: using value_type = typename supertype::value_type; using reference = typename supertype::reference; using pointer = typename supertype::pointer; using path_value_pair_type = typename supertype::path_value_pair_type; using json_location_node_type = typename supertype::json_location_node_type; using node_receiver_type = typename supertype::node_receiver_type; slice_selector(const slice& slic) : base_selector(), slice_(slic) { } void select(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, node_receiver_type& receiver, result_options options) const override { if (current.is_array()) { auto start = slice_.get_start(current.size()); auto end = slice_.get_stop(current.size()); auto step = slice_.step(); if (step > 0) { if (start < 0) { start = 0; } if (end > static_cast(current.size())) { end = current.size(); } for (int64_t i = start; i < end; i += step) { std::size_t j = static_cast(i); this->tail_select(resources, root, path_generator_type::generate(resources, last, j, options), current[j], receiver, options); } } else if (step < 0) { if (start >= static_cast(current.size())) { start = static_cast(current.size()) - 1; } if (end < -1) { end = -1; } for (int64_t i = start; i > end; i += step) { std::size_t j = static_cast(i); if (j < current.size()) { this->tail_select(resources, root, path_generator_type::generate(resources, last,j,options), current[j], receiver, options); } } } } } reference evaluate(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, result_options options, std::error_code&) const override { auto jptr = resources.create_json(json_array_arg); json_array_receiver accum(jptr); select(resources, root, last, current, accum, options); return *jptr; } }; template class function_selector final : public base_selector { using supertype = base_selector; expression expr_; public: using value_type = typename supertype::value_type; using reference = typename supertype::reference; using pointer = typename supertype::pointer; using path_value_pair_type = typename supertype::path_value_pair_type; using json_location_node_type = typename supertype::json_location_node_type; using path_generator_type = path_generator; using node_receiver_type = typename supertype::node_receiver_type; function_selector(expression&& expr) : base_selector(), expr_(std::move(expr)) { } void select(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, node_receiver_type& receiver, result_options options) const override { std::error_code ec; value_type ref = expr_.evaluate(resources, root, current, options, ec); if (!ec) { this->tail_select(resources, root, last, *resources.create_json(std::move(ref)), receiver, options); } } reference evaluate(dynamic_resources& resources, reference root, const json_location_node_type& last, reference current, result_options options, std::error_code& ec) const override { value_type ref = expr_.evaluate(resources, root, current, options, ec); if (!ec) { return this->evaluate_tail(resources, root, last, *resources.create_json(std::move(ref)), options, ec); } else { return resources.null_value(); } } std::string to_string(int level = 0) const override { std::string s; if (level > 0) { s.append("\n"); s.append(level*2, ' '); } s.append("function_selector "); s.append(expr_.to_string(level+1)); return s; } }; } // namespace detail } // namespace jsonpath } // namespace jsoncons #endif