diff options
Diffstat (limited to 'include/jsoncons_ext/jsonpath')
| -rw-r--r-- | include/jsoncons_ext/jsonpath/expression.hpp | 3329 | ||||
| -rw-r--r-- | include/jsoncons_ext/jsonpath/flatten.hpp | 432 | ||||
| -rw-r--r-- | include/jsoncons_ext/jsonpath/json_location.hpp | 445 | ||||
| -rw-r--r-- | include/jsoncons_ext/jsonpath/json_query.hpp | 115 | ||||
| -rw-r--r-- | include/jsoncons_ext/jsonpath/jsonpath.hpp | 13 | ||||
| -rw-r--r-- | include/jsoncons_ext/jsonpath/jsonpath_error.hpp | 240 | ||||
| -rw-r--r-- | include/jsoncons_ext/jsonpath/jsonpath_expression.hpp | 2612 | ||||
| -rw-r--r-- | include/jsoncons_ext/jsonpath/jsonpath_selector.hpp | 1322 | 
8 files changed, 8508 insertions, 0 deletions
| diff --git a/include/jsoncons_ext/jsonpath/expression.hpp b/include/jsoncons_ext/jsonpath/expression.hpp new file mode 100644 index 0000000..f655f2d --- /dev/null +++ b/include/jsoncons_ext/jsonpath/expression.hpp @@ -0,0 +1,3329 @@ +// 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_EXPRESSION_HPP +#define JSONCONS_JSONPATH_EXPRESSION_HPP + +#include <string> // std::basic_string +#include <vector> // std::vector +#include <unordered_map> // std::unordered_map +#include <unordered_set> // std::unordered_set +#include <limits> // std::numeric_limits +#include <set> // std::set +#include <utility> // std::move +#if defined(JSONCONS_HAS_STD_REGEX) +#include <regex> +#endif +#include <jsoncons/json_type.hpp> +#include <jsoncons_ext/jsonpath/json_location.hpp> +#include <jsoncons_ext/jsonpath/jsonpath_error.hpp> + +namespace jsoncons {  +namespace jsonpath { + +    struct reference_arg_t +    { +        explicit reference_arg_t() = default; +    }; +    constexpr reference_arg_t reference_arg{}; + +    struct const_reference_arg_t +    { +        explicit const_reference_arg_t() = default; +    }; +    constexpr const_reference_arg_t const_reference_arg{}; + +    struct literal_arg_t +    { +        explicit literal_arg_t() = default; +    }; +    constexpr literal_arg_t literal_arg{}; + +    struct end_of_expression_arg_t +    { +        explicit end_of_expression_arg_t() = default; +    }; +    constexpr end_of_expression_arg_t end_of_expression_arg{}; + +    struct separator_arg_t +    { +        explicit separator_arg_t() = default; +    }; +    constexpr separator_arg_t separator_arg{}; + +    struct lparen_arg_t +    { +        explicit lparen_arg_t() = default; +    }; +    constexpr lparen_arg_t lparen_arg{}; + +    struct rparen_arg_t +    { +        explicit rparen_arg_t() = default; +    }; +    constexpr rparen_arg_t rparen_arg{}; + +    struct begin_union_arg_t +    { +        explicit begin_union_arg_t() = default; +    }; +    constexpr begin_union_arg_t begin_union_arg{}; + +    struct end_union_arg_t +    { +        explicit end_union_arg_t() = default; +    }; +    constexpr end_union_arg_t end_union_arg{}; + +    struct begin_filter_arg_t +    { +        explicit begin_filter_arg_t() = default; +    }; +    constexpr begin_filter_arg_t begin_filter_arg{}; + +    struct end_filter_arg_t +    { +        explicit end_filter_arg_t() = default; +    }; +    constexpr end_filter_arg_t end_filter_arg{}; + +    struct begin_expression_arg_t +    { +        explicit begin_expression_arg_t() = default; +    }; +    constexpr begin_expression_arg_t begin_expression_arg{}; + +    struct end_index_expression_arg_t +    { +        explicit end_index_expression_arg_t() = default; +    }; +    constexpr end_index_expression_arg_t end_index_expression_arg{}; + +    struct end_argument_expression_arg_t +    { +        explicit end_argument_expression_arg_t() = default; +    }; +    constexpr end_argument_expression_arg_t end_argument_expression_arg{}; + +    struct current_node_arg_t +    { +        explicit current_node_arg_t() = default; +    }; +    constexpr current_node_arg_t current_node_arg{}; + +    struct root_node_arg_t +    { +        explicit root_node_arg_t() = default; +    }; +    constexpr root_node_arg_t root_node_arg{}; + +    struct end_function_arg_t +    { +        explicit end_function_arg_t() = default; +    }; +    constexpr end_function_arg_t end_function_arg{}; + +    struct argument_arg_t +    { +        explicit argument_arg_t() = default; +    }; +    constexpr argument_arg_t argument_arg{}; + +    enum class result_options {value=0, nodups=1, sort=2, path=4}; + +    using result_type = result_options; + +    inline result_options operator~(result_options a) +    { +        return static_cast<result_options>(~static_cast<unsigned int>(a)); +    } + +    inline result_options operator&(result_options a, result_options b) +    { +        return static_cast<result_options>(static_cast<unsigned int>(a) & static_cast<unsigned int>(b)); +    } + +    inline result_options operator^(result_options a, result_options b) +    { +        return static_cast<result_options>(static_cast<unsigned int>(a) ^ static_cast<unsigned int>(b)); +    } + +    inline result_options operator|(result_options a, result_options b) +    { +        return static_cast<result_options>(static_cast<unsigned int>(a) | static_cast<unsigned int>(b)); +    } + +    inline result_options operator&=(result_options& a, result_options b) +    { +        a = a & b; +        return a; +    } + +    inline result_options operator^=(result_options& a, result_options b) +    { +        a = a ^ b; +        return a; +    } + +    inline result_options operator|=(result_options& a, result_options b) +    { +        a = a | b; +        return a; +    } + +    template <class Json> +    class parameter; + +    template <class Json,class JsonReference> +    class value_or_pointer +    { +    public: +        friend class parameter<Json>; +        using value_type = Json; +        using reference = JsonReference; +        using pointer = typename std::conditional<std::is_const<typename std::remove_reference<reference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type; +    private: +        bool is_value_; +        union +        { +            value_type val_; +            pointer ptr_; +        }; +    public: +        value_or_pointer(value_type&& val) +            : is_value_(true), val_(std::move(val)) +        { +        } + +        value_or_pointer(pointer ptr) +            : is_value_(false), ptr_(std::move(ptr)) +        { +        } + +        value_or_pointer(value_or_pointer&& other) noexcept +            : is_value_(other.is_value_) +        { +            if (is_value_) +            { +                new(&val_)value_type(std::move(other.val_)); +            } +            else +            { +                ptr_ = other.ptr_; +            } +        } + +        ~value_or_pointer() noexcept +        { +            if (is_value_) +            { +                val_.~value_type(); +            } +        } + +        value_or_pointer& operator=(value_or_pointer&& other) noexcept +        { +            if (is_value_) +            { +                val_.~value_type(); +            } +            is_value_ = other.is_value_; + +            if (is_value_) +            { +                new(&val_)value_type(std::move(other.val_)); +            } +            else +            { +                ptr_ = other.ptr_; +            } +            return *this; +        } + +        reference value()  +        { +            return is_value_ ? val_ : *ptr_; +        } + +        pointer ptr()  +        { +            return is_value_ ? &val_ : ptr_; +        } +    }; + +    template <class Json> +    class parameter +    { +        using value_type = Json; +        using reference = const Json&; +        using pointer = const Json*; +    private: +        value_or_pointer<Json,reference> data_; +    public: +        template <class JsonReference> +        parameter(value_or_pointer<Json,JsonReference>&& data) noexcept +            : data_(nullptr) +        { +            data_.is_value_ = data.is_value_; +            if (data.is_value_) +            { +                data_.val_ = std::move(data.val_); +            } +            else +            { +                data_.ptr_ = data.ptr_; +            } +        } + +        parameter(parameter&& other) noexcept = default; + +        parameter& operator=(parameter&& other) noexcept = default; + +        const Json& value() const +        { +            return data_.is_value_ ? data_.val_ : *data_.ptr_; +        } +    }; + +    template <class Json> +    class custom_function +    { +    public: +        using value_type = Json; +        using char_type = typename Json::char_type; +        using parameter_type = parameter<Json>; +        using function_type = std::function<value_type(jsoncons::span<const parameter_type>, std::error_code& ec)>; +        using string_type = std::basic_string<char_type>; + +        string_type function_name_; +        optional<std::size_t> arity_; +        function_type f_; + +        custom_function(const string_type& function_name, +                        const optional<std::size_t>& arity, +                        const function_type& f) +            : function_name_(function_name), +              arity_(arity), +              f_(f) +        { +        } + +        custom_function(string_type&& function_name, +                        optional<std::size_t>&& arity, +                        function_type&& f) +            : function_name_(std::move(function_name)), +              arity_(std::move(arity)), +              f_(std::move(f)) +        { +        } + +        custom_function(const custom_function&) = default; + +        custom_function(custom_function&&) = default; + +        const string_type& name() const  +        { +            return function_name_; +        } + +        optional<std::size_t> arity() const  +        { +            return arity_; +        } + +        const function_type& function() const  +        { +            return f_; +        } +    }; + +    template <class Json> +    class custom_functions +    { +        using char_type = typename Json::char_type; +        using string_type = std::basic_string<char_type>; +        using value_type = Json; +        using parameter_type = parameter<Json>; +        using function_type = std::function<value_type(jsoncons::span<const parameter_type>, std::error_code& ec)>; +        using const_iterator = typename std::vector<custom_function<Json>>::const_iterator; + +        std::vector<custom_function<Json>> functions_; +    public: +        void register_function(const string_type& name, +                               jsoncons::optional<std::size_t> arity, +                               const function_type& f) +        { +            functions_.emplace_back(name, arity, f); +        } + +        const_iterator begin() const +        { +            return functions_.begin(); +        } + +        const_iterator end() const +        { +            return functions_.end(); +        } +    }; + +namespace detail { + +    template <class Json,class JsonReference> +    class dynamic_resources; + +    template <class Json,class JsonReference> +    struct unary_operator +    { +        std::size_t precedence_level_; +        bool is_right_associative_; + +        unary_operator(std::size_t precedence_level, +                       bool is_right_associative) +            : precedence_level_(precedence_level), +              is_right_associative_(is_right_associative) +        { +        } + +        virtual ~unary_operator() = default; + +        std::size_t precedence_level() const  +        { +            return precedence_level_; +        } +        bool is_right_associative() const +        { +            return is_right_associative_; +        } + +        virtual Json evaluate(JsonReference,  +                              std::error_code&) const = 0; +    }; + +    template <class Json> +    bool is_false(const Json& val) +    { +        return ((val.is_array() && val.empty()) || +                 (val.is_object() && val.empty()) || +                 (val.is_string() && val.as_string_view().empty()) || +                 (val.is_bool() && !val.as_bool()) || +                 val.is_null()); +    } + +    template <class Json> +    bool is_true(const Json& val) +    { +        return !is_false(val); +    } + +    template <class Json,class JsonReference> +    class unary_not_operator final : public unary_operator<Json,JsonReference> +    { +    public: +        unary_not_operator() +            : unary_operator<Json,JsonReference>(1, true) +        {} + +        Json evaluate(JsonReference val,  +                      std::error_code&) const override +        { +            return is_false(val) ? Json(true) : Json(false); +        } +    }; + +    template <class Json,class JsonReference> +    class unary_minus_operator final : public unary_operator<Json,JsonReference> +    { +    public: +        unary_minus_operator() +            : unary_operator<Json,JsonReference>(1, true) +        {} + +        Json evaluate(JsonReference val,  +                      std::error_code&) const override +        { +            if (val.is_int64()) +            { +                return Json(-val.template as<int64_t>()); +            } +            else if (val.is_double()) +            { +                return Json(-val.as_double()); +            } +            else +            { +                return Json::null(); +            } +        } +    }; + +    template <class Json,class JsonReference> +    class regex_operator final : public unary_operator<Json,JsonReference> +    { +        using char_type = typename Json::char_type; +        using string_type = std::basic_string<char_type>; +        std::basic_regex<char_type> pattern_; +    public: +        regex_operator(std::basic_regex<char_type>&& pattern) +            : unary_operator<Json,JsonReference>(2, true), +              pattern_(std::move(pattern)) +        { +        } + +        regex_operator(regex_operator&&) = default; +        regex_operator& operator=(regex_operator&&) = default; + +        Json evaluate(JsonReference val,  +                             std::error_code&) const override +        { +            if (!val.is_string()) +            { +                return Json::null(); +            } +            return std::regex_search(val.as_string(), pattern_) ? Json(true) : Json(false); +        } +    }; + +    template <class Json,class JsonReference> +    struct binary_operator +    { +        std::size_t precedence_level_; +        bool is_right_associative_; + +        binary_operator(std::size_t precedence_level, +                        bool is_right_associative = false) +            : precedence_level_(precedence_level), +              is_right_associative_(is_right_associative) +        { +        } + +        std::size_t precedence_level() const  +        { +            return precedence_level_; +        } +        bool is_right_associative() const +        { +            return is_right_associative_; +        } + +        virtual Json evaluate(JsonReference,  +                             JsonReference,  + +                             std::error_code&) const = 0; + +        virtual std::string to_string(int = 0) const +        { +            return "binary operator"; +        } + +    protected: +        ~binary_operator() = default; +    }; + +    // Implementations + +    template <class Json,class JsonReference> +    class or_operator final : public binary_operator<Json,JsonReference> +    { +    public: +        or_operator() +            : binary_operator<Json,JsonReference>(9) +        { +        } + +        Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override +        { +            if (lhs.is_null() && rhs.is_null()) +            { +                return Json::null(); +            } +            if (!is_false(lhs)) +            { +                return lhs; +            } +            else +            { +                return rhs; +            } +        } +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                //s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("or operator"); +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class and_operator final : public binary_operator<Json,JsonReference> +    { +    public: +        and_operator() +            : binary_operator<Json,JsonReference>(8) +        { +        } + +        Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override +        { +            if (is_true(lhs)) +            { +                return rhs; +            } +            else +            { +                return lhs; +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("and operator"); +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class eq_operator final : public binary_operator<Json,JsonReference> +    { +    public: +        eq_operator() +            : binary_operator<Json,JsonReference>(6) +        { +        } + +        Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override  +        { +            return lhs == rhs ? Json(true) : Json(false); +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("equal operator"); +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class ne_operator final : public binary_operator<Json,JsonReference> +    { +    public: +        ne_operator() +            : binary_operator<Json,JsonReference>(6) +        { +        } + +        Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override  +        { +            return lhs != rhs ? Json(true) : Json(false); +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("not equal operator"); +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class lt_operator final : public binary_operator<Json,JsonReference> +    { +    public: +        lt_operator() +            : binary_operator<Json,JsonReference>(5) +        { +        } + +        Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override  +        { +            if (lhs.is_number() && rhs.is_number()) +            { +                return lhs < rhs ? Json(true) : Json(false); +            } +            else if (lhs.is_string() && rhs.is_string()) +            { +                return lhs < rhs ? Json(true) : Json(false); +            } +            return Json::null(); +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("less than operator"); +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class lte_operator final : public binary_operator<Json,JsonReference> +    { +    public: +        lte_operator() +            : binary_operator<Json,JsonReference>(5) +        { +        } + +        Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override  +        { +            if (lhs.is_number() && rhs.is_number()) +            { +                return lhs <= rhs ? Json(true) : Json(false); +            } +            else if (lhs.is_string() && rhs.is_string()) +            { +                return lhs <= rhs ? Json(true) : Json(false); +            } +            return Json::null(); +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("less than or equal operator"); +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class gt_operator final : public binary_operator<Json,JsonReference> +    { +    public: +        gt_operator() +            : binary_operator<Json,JsonReference>(5) +        { +        } + +        Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override +        { +            //std::cout << "operator> lhs: " << lhs << ", rhs: " << rhs << "\n"; + +            if (lhs.is_number() && rhs.is_number()) +            { +                return lhs > rhs ? Json(true) : Json(false); +            } +            else if (lhs.is_string() && rhs.is_string()) +            { +                return lhs > rhs ? Json(true) : Json(false); +            } +            return Json::null(); +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("greater than operator"); +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class gte_operator final : public binary_operator<Json,JsonReference> +    { +    public: +        gte_operator() +            : binary_operator<Json,JsonReference>(5) +        { +        } + +        Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override +        { +            if (lhs.is_number() && rhs.is_number()) +            { +                return lhs >= rhs ? Json(true) : Json(false); +            } +            else if (lhs.is_string() && rhs.is_string()) +            { +                return lhs >= rhs ? Json(true) : Json(false); +            } +            return Json::null(); +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("greater than or equal operator"); +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class plus_operator final : public binary_operator<Json,JsonReference> +    { +    public: +        plus_operator() +            : binary_operator<Json,JsonReference>(4) +        { +        } + +        Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override +        { +            if (!(lhs.is_number() && rhs.is_number())) +            { +                return Json::null(); +            } +            else if (lhs.is_int64() && rhs.is_int64()) +            { +                return Json(((lhs.template as<int64_t>() + rhs.template as<int64_t>()))); +            } +            else if (lhs.is_uint64() && rhs.is_uint64()) +            { +                return Json((lhs.template as<uint64_t>() + rhs.template as<uint64_t>())); +            } +            else +            { +                return Json((lhs.as_double() + rhs.as_double())); +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("plus operator"); +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class minus_operator final : public binary_operator<Json,JsonReference> +    { +    public: +        minus_operator() +            : binary_operator<Json,JsonReference>(4) +        { +        } + +        Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override +        { +            if (!(lhs.is_number() && rhs.is_number())) +            { +                return Json::null(); +            } +            else if (lhs.is_int64() && rhs.is_int64()) +            { +                return Json(((lhs.template as<int64_t>() - rhs.template as<int64_t>()))); +            } +            else if (lhs.is_uint64() && rhs.is_uint64()) +            { +                return Json((lhs.template as<uint64_t>() - rhs.template as<uint64_t>())); +            } +            else +            { +                return Json((lhs.as_double() - rhs.as_double())); +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("minus operator"); +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class mult_operator final : public binary_operator<Json,JsonReference> +    { +    public: +        mult_operator() +            : binary_operator<Json,JsonReference>(3) +        { +        } + +        Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override +        { +            if (!(lhs.is_number() && rhs.is_number())) +            { +                return Json::null(); +            } +            else if (lhs.is_int64() && rhs.is_int64()) +            { +                return Json(((lhs.template as<int64_t>() * rhs.template as<int64_t>()))); +            } +            else if (lhs.is_uint64() && rhs.is_uint64()) +            { +                return Json((lhs.template as<uint64_t>() * rhs.template as<uint64_t>())); +            } +            else +            { +                return Json((lhs.as_double() * rhs.as_double())); +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("multiply operator"); +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class div_operator final : public binary_operator<Json,JsonReference> +    { +    public: +        div_operator() +            : binary_operator<Json,JsonReference>(3) +        { +        } + +        Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override +        { +            //std::cout << "operator/ lhs: " << lhs << ", rhs: " << rhs << "\n"; + +            if (!(lhs.is_number() && rhs.is_number())) +            { +                return Json::null(); +            } +            else if (lhs.is_int64() && rhs.is_int64()) +            { +                return Json(((lhs.template as<int64_t>() / rhs.template as<int64_t>()))); +            } +            else if (lhs.is_uint64() && rhs.is_uint64()) +            { +                return Json((lhs.template as<uint64_t>() / rhs.template as<uint64_t>())); +            } +            else +            { +                return Json((lhs.as_double() / rhs.as_double())); +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("divide operator"); +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class modulus_operator final : public binary_operator<Json,JsonReference> +    { +    public: +        modulus_operator() +            : binary_operator<Json,JsonReference>(3) +        { +        } + +        Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override +        { +            //std::cout << "operator/ lhs: " << lhs << ", rhs: " << rhs << "\n"; + +            if (!(lhs.is_number() && rhs.is_number())) +            { +                return Json::null(); +            } +            else if (lhs.is_int64() && rhs.is_int64()) +            { +                return Json(((lhs.template as<int64_t>() % rhs.template as<int64_t>()))); +            } +            else if (lhs.is_uint64() && rhs.is_uint64()) +            { +                return Json((lhs.template as<uint64_t>() % rhs.template as<uint64_t>())); +            } +            else +            { +                return Json(fmod(lhs.as_double(), rhs.as_double())); +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("modulus operator"); +            return s; +        } +    }; + +    // function_base +    template <class Json> +    class function_base +    { +        jsoncons::optional<std::size_t> arg_count_; +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; + +        function_base(jsoncons::optional<std::size_t> arg_count) +            : arg_count_(arg_count) +        { +        } + +        virtual ~function_base() noexcept = default; + +        jsoncons::optional<std::size_t> arity() const +        { +            return arg_count_; +        } + +        virtual value_type evaluate(const std::vector<parameter_type>& args,  +                                    std::error_code& ec) const = 0; + +        virtual std::string to_string(int level = 0) const +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("function"); +            return s; +        } +    };   + +    template <class Json> +    class decorator_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; +        using string_view_type = typename Json::string_view_type; +        using function_type = std::function<value_type(jsoncons::span<const parameter_type>, std::error_code& ec)>; +    private: +        function_type f_; +    public: +        decorator_function(jsoncons::optional<std::size_t> arity, +            const function_type& f) +            : function_base<Json>(arity), f_(f) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args, +            std::error_code& ec) const override +        { +            return f_(args, ec); +        } +    }; + +    template <class Json> +    class contains_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; +        using string_view_type = typename Json::string_view_type; + +        contains_function() +            : function_base<Json>(2) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            auto arg1= args[1].value(); + +            switch (arg0.type()) +            { +                case json_type::array_value: +                    for (auto& j : arg0.array_range()) +                    { +                        if (j == arg1) +                        { +                            return value_type(true); +                        } +                    } +                    return value_type(false); +                case json_type::string_value: +                { +                    if (!arg1.is_string()) +                    { +                        ec = jsonpath_errc::invalid_type; +                        return value_type::null(); +                    } +                    auto sv0 = arg0.template as<string_view_type>(); +                    auto sv1 = arg1.template as<string_view_type>(); +                    return sv0.find(sv1) != string_view_type::npos ? value_type(true) : value_type(false); +                } +                default: +                { +                    ec = jsonpath_errc::invalid_type; +                    return value_type::null(); +                } +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("contains function"); +            return s; +        } +    }; + +    template <class Json> +    class ends_with_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; +        using string_view_type = typename Json::string_view_type; + +        ends_with_function() +            : function_base<Json>(2) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            if (!arg0.is_string()) +            { +                ec = jsonpath_errc::invalid_type; +                return value_type::null(); +            } + +            auto arg1= args[1].value(); +            if (!arg1.is_string()) +            { +                ec = jsonpath_errc::invalid_type; +                return value_type::null(); +            } + +            auto sv0 = arg0.template as<string_view_type>(); +            auto sv1 = arg1.template as<string_view_type>(); + +            if (sv1.length() <= sv0.length() && sv1 == sv0.substr(sv0.length() - sv1.length())) +            { +                return value_type(true); +            } +            else +            { +                return value_type(false); +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("ends_with function"); +            return s; +        } +    }; + +    template <class Json> +    class starts_with_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; +        using string_view_type = typename Json::string_view_type; + +        starts_with_function() +            : function_base<Json>(2) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            if (!arg0.is_string()) +            { +                ec = jsonpath_errc::invalid_type; +                return value_type::null(); +            } + +            auto arg1= args[1].value(); +            if (!arg1.is_string()) +            { +                ec = jsonpath_errc::invalid_type; +                return value_type::null(); +            } + +            auto sv0 = arg0.template as<string_view_type>(); +            auto sv1 = arg1.template as<string_view_type>(); + +            if (sv1.length() <= sv0.length() && sv1 == sv0.substr(0, sv1.length())) +            { +                return value_type(true); +            } +            else +            { +                return value_type(false); +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("starts_with function"); +            return s; +        } +    }; + +    template <class Json> +    class sum_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; + +        sum_function() +            : function_base<Json>(1) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            if (!arg0.is_array()) +            { +                //std::cout << "arg: " << arg0 << "\n"; +                ec = jsonpath_errc::invalid_type; +                return value_type::null(); +            } +            //std::cout << "sum function arg: " << arg0 << "\n"; + +            double sum = 0; +            for (auto& j : arg0.array_range()) +            { +                if (!j.is_number()) +                { +                    ec = jsonpath_errc::invalid_type; +                    return value_type::null(); +                } +                sum += j.template as<double>(); +            } + +            return value_type(sum); +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("sum function"); +            return s; +        } +    }; + +#if defined(JSONCONS_HAS_STD_REGEX) + +    template <class Json> +    class tokenize_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; +        using char_type = typename Json::char_type; +        using string_type = std::basic_string<char_type>; + +        tokenize_function() +            : function_base<Json>(2) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            if (!args[0].value().is_string() || !args[1].value().is_string()) +            { +                //std::cout << "arg: " << arg0 << "\n"; +                ec = jsonpath_errc::invalid_type; +                return value_type::null(); +            } +            auto arg0 = args[0].value().template as<string_type>(); +            auto arg1 = args[1].value().template as<string_type>(); + +            std::regex::flag_type options = std::regex_constants::ECMAScript;  +            std::basic_regex<char_type> pieces_regex(arg1, options); + +            std::regex_token_iterator<typename string_type::const_iterator> rit ( arg0.begin(), arg0.end(), pieces_regex, -1); +            std::regex_token_iterator<typename string_type::const_iterator> rend; + +            value_type j(json_array_arg); +            while (rit != rend)  +            { +                j.emplace_back(rit->str()); +                ++rit; +            } +            return j; +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("tokenize function"); +            return s; +        } +    }; + +#endif // defined(JSONCONS_HAS_STD_REGEX) + +    template <class Json> +    class ceil_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; + +        ceil_function() +            : function_base<Json>(1) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            switch (arg0.type()) +            { +                case json_type::uint64_value: +                case json_type::int64_value: +                { +                    return value_type(arg0.template as<double>()); +                } +                case json_type::double_value: +                { +                    return value_type(std::ceil(arg0.template as<double>())); +                } +                default: +                    ec = jsonpath_errc::invalid_type; +                    return value_type::null(); +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("ceil function"); +            return s; +        } +    }; + +    template <class Json> +    class floor_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; + +        floor_function() +            : function_base<Json>(1) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            switch (arg0.type()) +            { +                case json_type::uint64_value: +                case json_type::int64_value: +                { +                    return value_type(arg0.template as<double>()); +                } +                case json_type::double_value: +                { +                    return value_type(std::floor(arg0.template as<double>())); +                } +                default: +                    ec = jsonpath_errc::invalid_type; +                    return value_type::null(); +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("floor function"); +            return s; +        } +    }; + +    template <class Json> +    class to_number_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; + +        to_number_function() +            : function_base<Json>(1) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            switch (arg0.type()) +            { +                case json_type::int64_value: +                case json_type::uint64_value: +                case json_type::double_value: +                    return arg0; +                case json_type::string_value: +                { +                    auto sv = arg0.as_string_view(); +                    uint64_t un{0}; +                    auto result1 = jsoncons::detail::to_integer(sv.data(), sv.length(), un); +                    if (result1) +                    { +                        return value_type(un); +                    } +                    int64_t sn{0}; +                    auto result2 = jsoncons::detail::to_integer(sv.data(), sv.length(), sn); +                    if (result2) +                    { +                        return value_type(sn); +                    } +                    jsoncons::detail::chars_to to_double; +                    try +                    { +                        auto s = arg0.as_string(); +                        double d = to_double(s.c_str(), s.length()); +                        return value_type(d); +                    } +                    catch (const std::exception&) +                    { +                        return value_type::null(); +                    } +                } +                default: +                    ec = jsonpath_errc::invalid_type; +                    return value_type::null(); +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("to_number function"); +            return s; +        } +    }; + +    template <class Json> +    class prod_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; + +        prod_function() +            : function_base<Json>(1) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            if (!arg0.is_array() || arg0.empty()) +            { +                //std::cout << "arg: " << arg0 << "\n"; +                ec = jsonpath_errc::invalid_type; +                return value_type::null(); +            } +            double prod = 1; +            for (auto& j : arg0.array_range()) +            { +                if (!j.is_number()) +                { +                    ec = jsonpath_errc::invalid_type; +                    return value_type::null(); +                } +                prod *= j.template as<double>(); +            } + +            return value_type(prod); +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("prod function"); +            return s; +        } +    }; + +    template <class Json> +    class avg_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; + +        avg_function() +            : function_base<Json>(1) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            if (!arg0.is_array()) +            { +                ec = jsonpath_errc::invalid_type; +                return value_type::null(); +            } +            if (arg0.empty()) +            { +                return value_type::null(); +            } +            double sum = 0; +            for (auto& j : arg0.array_range()) +            { +                if (!j.is_number()) +                { +                    ec = jsonpath_errc::invalid_type; +                    return value_type::null(); +                } +                sum += j.template as<double>(); +            } + +            return value_type(sum / static_cast<double>(arg0.size())); +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("to_string function"); +            return s; +        } +    }; + +    template <class Json> +    class min_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; + +        min_function() +            : function_base<Json>(1) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            if (!arg0.is_array()) +            { +                //std::cout << "arg: " << arg0 << "\n"; +                ec = jsonpath_errc::invalid_type; +                return value_type::null(); +            } +            if (arg0.empty()) +            { +                return value_type::null(); +            } +            bool is_number = arg0.at(0).is_number(); +            bool is_string = arg0.at(0).is_string(); +            if (!is_number && !is_string) +            { +                ec = jsonpath_errc::invalid_type; +                return value_type::null(); +            } + +            std::size_t index = 0; +            for (std::size_t i = 1; i < arg0.size(); ++i) +            { +                if (!(arg0.at(i).is_number() == is_number && arg0.at(i).is_string() == is_string)) +                { +                    ec = jsonpath_errc::invalid_type; +                    return value_type::null(); +                } +                if (arg0.at(i) < arg0.at(index)) +                { +                    index = i; +                } +            } + +            return arg0.at(index); +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("min function"); +            return s; +        } +    }; + +    template <class Json> +    class max_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; + +        max_function() +            : function_base<Json>(1) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            if (!arg0.is_array()) +            { +                //std::cout << "arg: " << arg0 << "\n"; +                ec = jsonpath_errc::invalid_type; +                return value_type::null(); +            } +            if (arg0.empty()) +            { +                return value_type::null(); +            } + +            bool is_number = arg0.at(0).is_number(); +            bool is_string = arg0.at(0).is_string(); +            if (!is_number && !is_string) +            { +                ec = jsonpath_errc::invalid_type; +                return value_type::null(); +            } + +            std::size_t index = 0; +            for (std::size_t i = 1; i < arg0.size(); ++i) +            { +                if (!(arg0.at(i).is_number() == is_number && arg0.at(i).is_string() == is_string)) +                { +                    ec = jsonpath_errc::invalid_type; +                    return value_type::null(); +                } +                if (arg0.at(i) > arg0.at(index)) +                { +                    index = i; +                } +            } + +            return arg0.at(index); +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("max function"); +            return s; +        } +    }; + +    template <class Json> +    class abs_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; + +        abs_function() +            : function_base<Json>(1) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            switch (arg0.type()) +            { +                case json_type::uint64_value: +                    return arg0; +                case json_type::int64_value: +                { +                    return arg0.template as<int64_t>() >= 0 ? arg0 : value_type(std::abs(arg0.template as<int64_t>())); +                } +                case json_type::double_value: +                { +                    return arg0.template as<double>() >= 0 ? arg0 : value_type(std::abs(arg0.template as<double>())); +                } +                default: +                { +                    ec = jsonpath_errc::invalid_type; +                    return value_type::null(); +                } +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("abs function"); +            return s; +        } +    }; + +    template <class Json> +    class length_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using string_view_type = typename Json::string_view_type; +        using parameter_type = parameter<Json>; + +        length_function() +            : function_base<Json>(1) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            //std::cout << "length function arg: " << arg0 << "\n"; + +            switch (arg0.type()) +            { +                case json_type::object_value: +                case json_type::array_value: +                    return value_type(arg0.size()); +                case json_type::string_value: +                { +                    auto sv0 = arg0.template as<string_view_type>(); +                    auto length = unicode_traits::count_codepoints(sv0.data(), sv0.size()); +                    return value_type(length); +                } +                default: +                { +                    ec = jsonpath_errc::invalid_type; +                    return value_type::null(); +                } +            } +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("length function"); +            return s; +        } +    }; + +    template <class Json> +    class keys_function : public function_base<Json> +    { +    public: +        using value_type = Json; +        using parameter_type = parameter<Json>; +        using string_view_type = typename Json::string_view_type; + +        keys_function() +            : function_base<Json>(1) +        { +        } + +        value_type evaluate(const std::vector<parameter_type>& args,  +                            std::error_code& ec) const override +        { +            if (args.size() != *this->arity()) +            { +                ec = jsonpath_errc::invalid_arity; +                return value_type::null(); +            } + +            auto arg0= args[0].value(); +            if (!arg0.is_object()) +            { +                ec = jsonpath_errc::invalid_type; +                return value_type::null(); +            } + +            value_type result(json_array_arg); +            result.reserve(args.size()); + +            for (auto& item : arg0.object_range()) +            { +                result.emplace_back(item.key()); +            } +            return result; +        } + +        std::string to_string(int level = 0) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("keys function"); +            return s; +        } +    }; + +    enum class jsonpath_token_kind +    { +        root_node, +        current_node, +        expression, +        lparen, +        rparen, +        begin_union, +        end_union, +        begin_filter, +        end_filter, +        begin_expression, +        end_index_expression, +        end_argument_expression, +        separator, +        literal, +        selector, +        function, +        end_function, +        argument, +        unary_operator, +        binary_operator +    }; + +    inline +    std::string to_string(jsonpath_token_kind kind) +    { +        switch (kind) +        { +            case jsonpath_token_kind::root_node: +                return "root_node"; +            case jsonpath_token_kind::current_node: +                return "current_node"; +            case jsonpath_token_kind::lparen: +                return "lparen"; +            case jsonpath_token_kind::rparen: +                return "rparen"; +            case jsonpath_token_kind::begin_union: +                return "begin_union"; +            case jsonpath_token_kind::end_union: +                return "end_union"; +            case jsonpath_token_kind::begin_filter: +                return "begin_filter"; +            case jsonpath_token_kind::end_filter: +                return "end_filter"; +            case jsonpath_token_kind::begin_expression: +                return "begin_expression"; +            case jsonpath_token_kind::end_index_expression: +                return "end_index_expression"; +            case jsonpath_token_kind::end_argument_expression: +                return "end_argument_expression"; +            case jsonpath_token_kind::separator: +                return "separator"; +            case jsonpath_token_kind::literal: +                return "literal"; +            case jsonpath_token_kind::selector: +                return "selector"; +            case jsonpath_token_kind::function: +                return "function"; +            case jsonpath_token_kind::end_function: +                return "end_function"; +            case jsonpath_token_kind::argument: +                return "argument"; +            case jsonpath_token_kind::unary_operator: +                return "unary_operator"; +            case jsonpath_token_kind::binary_operator: +                return "binary_operator"; +            default: +                return ""; +        } +    } + +    template <class Json,class JsonReference> +    struct path_value_pair +    { +        using char_type = typename Json::char_type; +        using string_type = std::basic_string<char_type,std::char_traits<char_type>>; +        using value_type = Json; +        using reference = JsonReference; +        using value_pointer = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type; +        using json_location_node_type = json_location_node<char_type>; +        using json_location_type = json_location<char_type>; +        using path_pointer = const json_location_node_type*; + +        json_location_type path_; +        value_pointer value_ptr_; + +        path_value_pair(const json_location_type& path, reference value) noexcept +            : path_(path), value_ptr_(std::addressof(value)) +        { +        } + +        path_value_pair(json_location_type&& path, value_pointer valp) noexcept +            : path_(std::move(path)), value_ptr_(valp) +        { +        } + +        path_value_pair(const path_value_pair&) = default; +        path_value_pair(path_value_pair&& other) = default; +        path_value_pair& operator=(const path_value_pair&) = default; +        path_value_pair& operator=(path_value_pair&& other) = default; + +        json_location_type path() const +        { +            return path_; +        } + +        reference value()  +        { +            return *value_ptr_; +        } +    }; +  +    template <class Json,class JsonReference> +    struct path_value_pair_less +    { +        bool operator()(const path_value_pair<Json,JsonReference>& lhs, +                        const path_value_pair<Json,JsonReference>& rhs) const noexcept +        { +            return lhs.path() < rhs.path(); +        } +    }; + +    template <class Json,class JsonReference> +    struct path_value_pair_equal +    { +        bool operator()(const path_value_pair<Json,JsonReference>& lhs, +                        const path_value_pair<Json,JsonReference>& rhs) const noexcept +        { +            return lhs.path() == rhs.path(); +        } +    }; + +    template <class Json,class JsonReference> +    struct path_component_value_pair +    { +        using char_type = typename Json::char_type; +        using string_type = std::basic_string<char_type,std::char_traits<char_type>>; +        using value_type = Json; +        using reference = JsonReference; +        using value_pointer = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type; +        using json_location_node_type = json_location_node<char_type>; +        using json_location_type = json_location<char_type>; +        using path_pointer = const json_location_node_type*; +    private: +        const json_location_node_type* last_ptr_; +        value_pointer value_ptr_; +    public: +        path_component_value_pair(const json_location_node_type& last, reference value) noexcept +            : last_ptr_(std::addressof(last)), value_ptr_(std::addressof(value)) +        { +        } + +        const json_location_node_type& last() const +        { +            return *last_ptr_; +        } + +        reference value() const +        { +            return *value_ptr_; +        } +    }; + +    template <class Json,class JsonReference> +    class node_receiver +    { +    public: +        using char_type = typename Json::char_type; +        using reference = JsonReference; +        using json_location_node_type = json_location_node<char_type>; + +        virtual ~node_receiver() noexcept = default; + +        virtual void add(const json_location_node_type& path_tail,  +                         reference value) = 0; +    }; + +    template <class Json,class JsonReference> +    class path_value_receiver : public node_receiver<Json,JsonReference> +    { +    public: +        using reference = JsonReference; +        using char_type = typename Json::char_type; +        using json_location_node_type = json_location_node<char_type>; +        using json_location_type = json_location<char_type>; +        using path_value_pair_type = path_value_pair<Json,JsonReference>; + +        std::vector<path_value_pair_type> nodes; + +        void add(const json_location_node_type& path_tail,  +                 reference value) override +        { +            nodes.emplace_back(json_location_type(path_tail), std::addressof(value)); +        } +    }; + +    template <class Json,class JsonReference> +    class path_stem_value_receiver : public node_receiver<Json,JsonReference> +    { +    public: +        using reference = JsonReference; +        using char_type = typename Json::char_type; +        using json_location_node_type = json_location_node<char_type>; +        using path_stem_value_pair_type = path_component_value_pair<Json,JsonReference>; + +        std::vector<path_stem_value_pair_type> nodes; + +        void add(const json_location_node_type& path_tail,  +                 reference value) override +        { +            nodes.emplace_back(path_tail, value); +        } +    }; + +    template <class Json, class JsonReference> +    class dynamic_resources +    { +        using reference = JsonReference; +        using pointer = typename std::conditional<std::is_const<typename std::remove_reference<reference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type; +        using json_location_node_type = json_location_node<typename Json::char_type>; +        using path_stem_value_pair_type = path_component_value_pair<Json,JsonReference>; +        std::vector<std::unique_ptr<Json>> temp_json_values_; +        std::vector<std::unique_ptr<json_location_node_type>> temp_path_node_values_; +        std::unordered_map<std::size_t,pointer> cache_; +    public: +        bool is_cached(std::size_t id) const +        { +            return cache_.find(id) != cache_.end(); +        } +        void add_to_cache(std::size_t id, reference val)  +        { +            cache_.emplace(id, std::addressof(val)); +        } +        reference retrieve_from_cache(std::size_t id)  +        { +            return *cache_[id]; +        } + +        reference null_value() +        { +            static Json j{ null_type{} }; +            return j; +        } + +        template <typename... Args> +        Json* create_json(Args&& ... args) +        { +            auto temp = jsoncons::make_unique<Json>(std::forward<Args>(args)...); +            Json* ptr = temp.get(); +            temp_json_values_.emplace_back(std::move(temp)); +            return ptr; +        } + +        const json_location_node_type& root_path_node() const +        { +            static json_location_node_type root('$'); +            return root; +        } + +        const json_location_node_type& current_path_node() const +        { +            static json_location_node_type root('@'); +            return root; +        } + +        template <typename... Args> +        const json_location_node_type* create_path_node(Args&& ... args) +        { +            auto temp = jsoncons::make_unique<json_location_node_type>(std::forward<Args>(args)...); +            json_location_node_type* ptr = temp.get(); +            temp_path_node_values_.emplace_back(std::move(temp)); +            return ptr; +        } +    }; + +    template <class Json,class JsonReference> +    struct node_less +    { +        bool operator()(const path_value_pair<Json,JsonReference>& a, const path_value_pair<Json,JsonReference>& b) const +        { +            return *(a.ptr) < *(b.ptr); +        } +    }; + +    template <class Json,class JsonReference> +    class jsonpath_selector +    { +        bool is_path_; +        std::size_t precedence_level_; + +    public: +        using char_type = typename Json::char_type; +        using string_type = std::basic_string<char_type,std::char_traits<char_type>>; +        using string_view_type = jsoncons::basic_string_view<char_type, std::char_traits<char_type>>; +        using value_type = Json; +        using reference = JsonReference; +        using pointer = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type; +        using path_value_pair_type = path_value_pair<Json,JsonReference>; +        using json_location_node_type = json_location_node<char_type>; +        using json_location_type = json_location<char_type>; +        using node_receiver_type = node_receiver<Json,JsonReference>; +        using selector_type = jsonpath_selector<Json,JsonReference>; + +        jsonpath_selector(bool is_path, +                          std::size_t precedence_level = 0) +            : is_path_(is_path),  +              precedence_level_(precedence_level) +        { +        } + +        virtual ~jsonpath_selector() noexcept = default; + +        bool is_path() const  +        { +            return is_path_; +        } + +        std::size_t precedence_level() const +        { +            return precedence_level_; +        } + +        bool is_right_associative() const +        { +            return true; +        } + +        virtual void select(dynamic_resources<Json,JsonReference>& resources, +                            reference root, +                            const json_location_node_type& path_tail,  +                            reference val,  +                            node_receiver_type& receiver, +                            result_options options) const = 0; + +        virtual reference evaluate(dynamic_resources<Json,JsonReference>& resources, +                                   reference root, +                                   const json_location_node_type& path_tail,  +                                   reference current,  +                                   result_options options, +                                   std::error_code& ec) const = 0; + +        virtual void append_selector(jsonpath_selector*)  +        { +        } + +        virtual std::string to_string(int = 0) const +        { +            return std::string(); +        } +    }; + +    template <class Json, class JsonReference> +    struct static_resources +    { +        using char_type = typename Json::char_type; +        using string_type = std::basic_string<char_type>; +        using value_type = Json; +        using reference = JsonReference; +        using function_base_type = function_base<Json>; +        using selector_type = jsonpath_selector<Json,JsonReference>; + +        std::vector<std::unique_ptr<selector_type>> selectors_; +        std::vector<std::unique_ptr<Json>> temp_json_values_; +        std::vector<std::unique_ptr<unary_operator<Json,JsonReference>>> unary_operators_; +        std::unordered_map<string_type,std::unique_ptr<function_base_type>> custom_functions_; + +        static_resources() +        { +        } + +        static_resources(const custom_functions<Json>& functions) +        { +            for (const auto& item : functions) +            { +                custom_functions_.emplace(item.name(), +                                          jsoncons::make_unique<decorator_function<Json>>(item.arity(),item.function())); +            } +        } + +        static_resources(const static_resources&) = default; + +        static_resources(static_resources&& other) noexcept  +            : selectors_(std::move(other.selectors_)), +              temp_json_values_(std::move(other.temp_json_values_)), +              unary_operators_(std::move(other.unary_operators_)), +              custom_functions_(std::move(other.custom_functions_)) +        { +        } + +        const function_base_type* get_function(const string_type& name, std::error_code& ec) const +        { +            static abs_function<Json> abs_func; +            static contains_function<Json> contains_func; +            static starts_with_function<Json> starts_with_func; +            static ends_with_function<Json> ends_with_func; +            static ceil_function<Json> ceil_func; +            static floor_function<Json> floor_func; +            static to_number_function<Json> to_number_func; +            static sum_function<Json> sum_func; +            static prod_function<Json> prod_func; +            static avg_function<Json> avg_func; +            static min_function<Json> min_func; +            static max_function<Json> max_func; +            static length_function<Json> length_func; +            static keys_function<Json> keys_func; +#if defined(JSONCONS_HAS_STD_REGEX) +            static tokenize_function<Json> tokenize_func; +#endif + +            static std::unordered_map<string_type,const function_base_type*> functions = +            { +                {string_type{'a','b','s'}, &abs_func}, +                {string_type{'c','o','n','t','a','i','n','s'}, &contains_func}, +                {string_type{'s','t','a','r','t','s','_','w','i','t','h'}, &starts_with_func}, +                {string_type{'e','n','d','s','_','w','i','t','h'}, &ends_with_func}, +                {string_type{'c','e','i','l'}, &ceil_func}, +                {string_type{'f','l','o','o','r'}, &floor_func}, +                {string_type{'t','o','_','n','u','m','b','e','r'}, &to_number_func}, +                {string_type{'s','u','m'}, &sum_func}, +                {string_type{'p','r','o', 'd'}, &prod_func}, +                {string_type{'a','v','g'}, &avg_func}, +                {string_type{'m','i','n'}, &min_func}, +                {string_type{'m','a','x'}, &max_func}, +                {string_type{'l','e','n','g','t','h'}, &length_func}, +                {string_type{'k','e','y','s'}, &keys_func}, +#if defined(JSONCONS_HAS_STD_REGEX) +                {string_type{'t','o','k','e','n','i','z','e'}, &tokenize_func}, +#endif +                {string_type{'c','o','u','n','t'}, &length_func} +            }; + +            auto it = functions.find(name); +            if (it == functions.end()) +            { +                auto it2 = custom_functions_.find(name); +                if (it2 == custom_functions_.end()) +                { +                    ec = jsonpath_errc::unknown_function; +                    return nullptr; +                } +                else +                { +                    return it2->second.get(); +                } +            } +            else +            { +                return it->second; +            } +        } + +        const unary_operator<Json,JsonReference>* get_unary_not() const +        { +            static unary_not_operator<Json,JsonReference> oper; +            return &oper; +        } + +        const unary_operator<Json,JsonReference>* get_unary_minus() const +        { +            static unary_minus_operator<Json,JsonReference> oper; +            return &oper; +        } + +        const unary_operator<Json,JsonReference>* get_regex_operator(std::basic_regex<char_type>&& pattern)  +        { +            unary_operators_.push_back(jsoncons::make_unique<regex_operator<Json,JsonReference>>(std::move(pattern))); +            return unary_operators_.back().get(); +        } + +        const binary_operator<Json,JsonReference>* get_or_operator() const +        { +            static or_operator<Json,JsonReference> oper; + +            return &oper; +        } + +        const binary_operator<Json,JsonReference>* get_and_operator() const +        { +            static and_operator<Json,JsonReference> oper; + +            return &oper; +        } + +        const binary_operator<Json,JsonReference>* get_eq_operator() const +        { +            static eq_operator<Json,JsonReference> oper; +            return &oper; +        } + +        const binary_operator<Json,JsonReference>* get_ne_operator() const +        { +            static ne_operator<Json,JsonReference> oper; +            return &oper; +        } + +        const binary_operator<Json,JsonReference>* get_lt_operator() const +        { +            static lt_operator<Json,JsonReference> oper; +            return &oper; +        } + +        const binary_operator<Json,JsonReference>* get_lte_operator() const +        { +            static lte_operator<Json,JsonReference> oper; +            return &oper; +        } + +        const binary_operator<Json,JsonReference>* get_gt_operator() const +        { +            static gt_operator<Json,JsonReference> oper; +            return &oper; +        } + +        const binary_operator<Json,JsonReference>* get_gte_operator() const +        { +            static gte_operator<Json,JsonReference> oper; +            return &oper; +        } + +        const binary_operator<Json,JsonReference>* get_plus_operator() const +        { +            static plus_operator<Json,JsonReference> oper; +            return &oper; +        } + +        const binary_operator<Json,JsonReference>* get_minus_operator() const +        { +            static minus_operator<Json,JsonReference> oper; +            return &oper; +        } + +        const binary_operator<Json,JsonReference>* get_mult_operator() const +        { +            static mult_operator<Json,JsonReference> oper; +            return &oper; +        } + +        const binary_operator<Json,JsonReference>* get_div_operator() const +        { +            static div_operator<Json,JsonReference> oper; +            return &oper; +        } + +        const binary_operator<Json,JsonReference>* get_modulus_operator() const +        { +            static modulus_operator<Json,JsonReference> oper; +            return &oper; +        } + +        template <typename T> +        selector_type* new_selector(T&& val) +        { +            selectors_.emplace_back(jsoncons::make_unique<T>(std::forward<T>(val))); +            return selectors_.back().get(); +        } + +        template <typename... Args> +        Json* create_json(Args&& ... args) +        { +            auto temp = jsoncons::make_unique<Json>(std::forward<Args>(args)...); +            Json* ptr = temp.get(); +            temp_json_values_.emplace_back(std::move(temp)); +            return ptr; +        } +    }; + +    template <class Json, class JsonReference> +    class expression_base +    { +    public: +        using char_type = typename Json::char_type; +        using string_type = std::basic_string<char_type,std::char_traits<char_type>>; +        using string_view_type = jsoncons::basic_string_view<char_type, std::char_traits<char_type>>; +        using value_type = Json; +        using reference = JsonReference; +        using pointer = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type; +        using path_value_pair_type = path_value_pair<Json,JsonReference>; +        using json_location_node_type = json_location_node<char_type>; + +        virtual ~expression_base() noexcept = default; + +        virtual value_type evaluate(dynamic_resources<Json,JsonReference>& resources, +                                           reference root, +                                           //const json_location_node_type& path,  +                                           reference val,  +                                           result_options options, +                                           std::error_code& ec) const = 0; + +        virtual std::string to_string(int level = 0) const = 0; +    }; + +    template <class Json,class JsonReference> +    class token +    { +    public: +        using selector_type = jsonpath_selector<Json,JsonReference>; +        using expression_base_type = expression_base<Json,JsonReference>; + +        jsonpath_token_kind token_kind_; + +        union +        { +            selector_type* selector_; +            std::unique_ptr<expression_base_type> expression_; +            const unary_operator<Json,JsonReference>* unary_operator_; +            const binary_operator<Json,JsonReference>* binary_operator_; +            const function_base<Json>* function_; +            Json value_; +        }; +    public: + +        token(const unary_operator<Json,JsonReference>* expr) noexcept +            : token_kind_(jsonpath_token_kind::unary_operator), +              unary_operator_(expr) +        { +        } + +        token(const binary_operator<Json,JsonReference>* expr) noexcept +            : token_kind_(jsonpath_token_kind::binary_operator), +              binary_operator_(expr) +        { +        } + +        token(current_node_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::current_node) +        { +        } + +        token(root_node_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::root_node) +        { +        } + +        token(end_function_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::end_function) +        { +        } + +        token(separator_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::separator) +        { +        } + +        token(lparen_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::lparen) +        { +        } + +        token(rparen_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::rparen) +        { +        } + +        token(begin_union_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::begin_union) +        { +        } + +        token(end_union_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::end_union) +        { +        } + +        token(begin_filter_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::begin_filter) +        { +        } + +        token(end_filter_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::end_filter) +        { +        } + +        token(begin_expression_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::begin_expression) +        { +        } + +        token(end_index_expression_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::end_index_expression) +        { +        } + +        token(end_argument_expression_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::end_argument_expression) +        { +        } + +        token(selector_type* selector) +            : token_kind_(jsonpath_token_kind::selector), selector_(selector) +        { +        } + +        token(std::unique_ptr<expression_base_type>&& expr) +            : token_kind_(jsonpath_token_kind::expression) +        { +            new (&expression_) std::unique_ptr<expression_base_type>(std::move(expr)); +        } + +        token(const function_base<Json>* function) noexcept +            : token_kind_(jsonpath_token_kind::function), +              function_(function) +        { +        } + +        token(argument_arg_t) noexcept +            : token_kind_(jsonpath_token_kind::argument) +        { +        } + +        token(literal_arg_t, Json&& value) noexcept +            : token_kind_(jsonpath_token_kind::literal), value_(std::move(value)) +        { +        } + +        token(token&& other) noexcept +        { +            construct(std::forward<token>(other)); +        } + +        const Json& get_value(const_reference_arg_t, dynamic_resources<Json,JsonReference>&) const +        { +            return value_; +        } + +        Json& get_value(reference_arg_t, dynamic_resources<Json,JsonReference>& resources) const +        { +            return *resources.create_json(value_); +        } + +        token& operator=(token&& other) +        { +            if (&other != this) +            { +                if (token_kind_ == other.token_kind_) +                { +                    switch (token_kind_) +                    { +                        case jsonpath_token_kind::selector: +                            selector_ = other.selector_; +                            break; +                        case jsonpath_token_kind::expression: +                            expression_ = std::move(other.expression_); +                            break; +                        case jsonpath_token_kind::unary_operator: +                            unary_operator_ = other.unary_operator_; +                            break; +                        case jsonpath_token_kind::binary_operator: +                            binary_operator_ = other.binary_operator_; +                            break; +                        case jsonpath_token_kind::function: +                            function_ = other.function_; +                            break; +                        case jsonpath_token_kind::literal: +                            value_ = std::move(other.value_); +                            break; +                        default: +                            break; +                    } +                } +                else +                { +                    destroy(); +                    construct(std::forward<token>(other)); +                } +            } +            return *this; +        } + +        ~token() noexcept +        { +            destroy(); +        } + +        jsonpath_token_kind token_kind() const +        { +            return token_kind_; +        } + +        bool is_lparen() const +        { +            return token_kind_ == jsonpath_token_kind::lparen;  +        } + +        bool is_rparen() const +        { +            return token_kind_ == jsonpath_token_kind::rparen;  +        } + +        bool is_current_node() const +        { +            return token_kind_ == jsonpath_token_kind::current_node;  +        } + +        bool is_path() const +        { +            return token_kind_ == jsonpath_token_kind::selector && selector_->is_path();  +        } + +        bool is_operator() const +        { +            return token_kind_ == jsonpath_token_kind::unary_operator ||  +                   token_kind_ == jsonpath_token_kind::binary_operator;  +        } + +        std::size_t precedence_level() const +        { +            switch(token_kind_) +            { +                case jsonpath_token_kind::selector: +                    return selector_->precedence_level(); +                case jsonpath_token_kind::unary_operator: +                    return unary_operator_->precedence_level(); +                case jsonpath_token_kind::binary_operator: +                    return binary_operator_->precedence_level(); +                default: +                    return 0; +            } +        } + +        jsoncons::optional<std::size_t> arity() const +        { +            return token_kind_ == jsonpath_token_kind::function ? function_->arity() : jsoncons::optional<std::size_t>(); +        } + +        bool is_right_associative() const +        { +            switch(token_kind_) +            { +                case jsonpath_token_kind::selector: +                    return selector_->is_right_associative(); +                case jsonpath_token_kind::unary_operator: +                    return unary_operator_->is_right_associative(); +                case jsonpath_token_kind::binary_operator: +                    return binary_operator_->is_right_associative(); +                default: +                    return false; +            } +        } + +        void construct(token&& other) +        { +            token_kind_ = other.token_kind_; +            switch (token_kind_) +            { +                case jsonpath_token_kind::selector: +                    selector_ = other.selector_; +                    break; +                case jsonpath_token_kind::expression: +                    new (&expression_) std::unique_ptr<expression_base_type>(std::move(other.expression_)); +                    break; +                case jsonpath_token_kind::unary_operator: +                    unary_operator_ = other.unary_operator_; +                    break; +                case jsonpath_token_kind::binary_operator: +                    binary_operator_ = other.binary_operator_; +                    break; +                case jsonpath_token_kind::function: +                    function_ = other.function_; +                    break; +                case jsonpath_token_kind::literal: +                    new (&value_) Json(std::move(other.value_)); +                    break; +                default: +                    break; +            } +        } + +        void destroy() noexcept  +        { +            switch(token_kind_) +            { +                case jsonpath_token_kind::expression: +                    expression_.~unique_ptr(); +                    break; +                case jsonpath_token_kind::literal: +                    value_.~Json(); +                    break; +                default: +                    break; +            } +        } + +        std::string to_string(int level = 0) const +        { +            std::string s; +            switch (token_kind_) +            { +                case jsonpath_token_kind::root_node: +                    if (level > 0) +                    { +                        s.append("\n"); +                        s.append(level*2, ' '); +                    } +                    s.append("root node"); +                    break; +                case jsonpath_token_kind::current_node: +                    if (level > 0) +                    { +                        s.append("\n"); +                        s.append(level*2, ' '); +                    } +                    s.append("current node"); +                    break; +                case jsonpath_token_kind::argument: +                    if (level > 0) +                    { +                        s.append("\n"); +                        s.append(level*2, ' '); +                    } +                    s.append("argument"); +                    break; +                case jsonpath_token_kind::selector: +                    s.append(selector_->to_string(level)); +                    break; +                case jsonpath_token_kind::expression: +                    s.append(expression_->to_string(level)); +                    break; +                case jsonpath_token_kind::literal: +                { +                    if (level > 0) +                    { +                        s.append("\n"); +                        s.append(level*2, ' '); +                    } +                    auto sbuf = value_.to_string(); +                    unicode_traits::convert(sbuf.data(), sbuf.size(), s); +                    break; +                } +                case jsonpath_token_kind::binary_operator: +                    s.append(binary_operator_->to_string(level)); +                    break; +                case jsonpath_token_kind::function: +                    s.append(function_->to_string(level)); +                    break; +                default: +                    if (level > 0) +                    { +                        s.append("\n"); +                        s.append(level*2, ' '); +                    } +                    s.append("token kind: "); +                    s.append(jsoncons::jsonpath::detail::to_string(token_kind_)); +                    break; +            } +            //s.append("\n"); +            return s; +        } +    }; + +    template <class Callback, class Json,class JsonReference> +    class callback_receiver : public node_receiver<Json,JsonReference> +    { +        Callback& callback_; +    public: +        using reference = JsonReference; +        using char_type = typename Json::char_type; +        using json_location_node_type = json_location_node<char_type>; +        using json_location_type = json_location<char_type>; + +        callback_receiver(Callback& callback) +            : callback_(callback) +        { +        } + +        void add(const json_location_node_type& path_tail,  +                 reference value) override +        { +            callback_(json_location_type(path_tail), value); +        } +    }; + +    template <class Json,class JsonReference> +    class path_expression +    { +    public: +        using char_type = typename Json::char_type; +        using string_type = std::basic_string<char_type,std::char_traits<char_type>>; +        using string_view_type = typename Json::string_view_type; +        using path_value_pair_type = path_value_pair<Json,JsonReference>; +        using path_value_pair_less_type = path_value_pair_less<Json,JsonReference>; +        using path_value_pair_equal_type = path_value_pair_equal<Json,JsonReference>; +        using value_type = Json; +        using reference = typename path_value_pair_type::reference; +        using pointer = typename path_value_pair_type::value_pointer; +        using token_type = token<Json,JsonReference>; +        using reference_arg_type = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value, +            const_reference_arg_t,reference_arg_t>::type; +        using json_location_node_type = json_location_node<char_type>; +        using json_location_type = json_location<char_type>; +        using selector_type = jsonpath_selector<Json,JsonReference>; +    private: +        selector_type* selector_; +        result_options required_options_; +    public: + +        path_expression() +            : required_options_() +        { +        } + +        path_expression(path_expression&& expr) = default; + +        path_expression(selector_type* selector, bool paths_required) +            : selector_(selector), required_options_() +        { +            if (paths_required) +            { +                required_options_ |= result_options::path; +            } +        } + +        path_expression& operator=(path_expression&& expr) = default; + +        Json evaluate(dynamic_resources<Json,JsonReference>& resources,  +                      reference root, +                      const json_location_node_type& path,  +                      reference instance, +                      result_options options) const +        { +            Json result(json_array_arg); + +            if ((options & result_options::path) == result_options::path) +            { +                auto callback = [&result](const json_location_type& path, reference) +                { +                    result.emplace_back(path.to_string()); +                }; +                evaluate(resources, root, path, instance, callback, options); +            } +            else +            { +                auto callback = [&result](const json_location_type&, reference val) +                { +                    result.push_back(val); +                }; +                evaluate(resources, root, path, instance, callback, options); +            } + +            return result; +        } + +        template <class Callback> +        typename std::enable_if<type_traits::is_binary_function_object<Callback,const json_location_type&,reference>::value,void>::type +        evaluate(dynamic_resources<Json,JsonReference>& resources,  +                 reference root, +                 const json_location_node_type& path,  +                 reference current,  +                 Callback callback, +                 result_options options) const +        { +            std::error_code ec; + +            options |= required_options_; + +            const result_options require_more = result_options::nodups | result_options::sort; + +            if ((options & require_more) != result_options()) +            { +                path_value_receiver<Json,JsonReference> receiver; +                selector_->select(resources, root, path, current, receiver, options); + +                if (receiver.nodes.size() > 1 && (options & result_options::sort) == result_options::sort) +                { +                    std::sort(receiver.nodes.begin(), receiver.nodes.end(), path_value_pair_less_type()); +                } + +                if (receiver.nodes.size() > 1 && (options & result_options::nodups) == result_options::nodups) +                { +                    if ((options & result_options::sort) == result_options::sort) +                    { +                        auto last = std::unique(receiver.nodes.begin(),receiver.nodes.end(),path_value_pair_equal_type()); +                        receiver.nodes.erase(last,receiver.nodes.end()); +                        for (auto& node : receiver.nodes) +                        { +                            callback(node.path(), node.value()); +                        } +                    } +                    else +                    { +                        std::vector<path_value_pair_type> index(receiver.nodes); +                        std::sort(index.begin(), index.end(), path_value_pair_less_type()); +                        auto last = std::unique(index.begin(),index.end(),path_value_pair_equal_type()); +                        index.erase(last,index.end()); + +                        std::vector<path_value_pair_type> temp2; +                        temp2.reserve(index.size()); +                        for (auto&& node : receiver.nodes) +                        { +                            auto it = std::lower_bound(index.begin(),index.end(),node, path_value_pair_less_type()); +                            if (it != index.end() && it->path() == node.path())  +                            { +                                temp2.emplace_back(std::move(node)); +                                index.erase(it); +                            } +                        } +                        for (auto& node : temp2) +                        { +                            callback(node.path(), node.value()); +                        } +                    } +                } +                else +                { +                    for (auto& node : receiver.nodes) +                    { +                        callback(node.path(), node.value()); +                    } +                } +            } +            else +            { +                callback_receiver<Callback,Json,JsonReference> receiver(callback); +                selector_->select(resources, root, path, current, receiver, options); +            } +        } + +        std::string to_string(int level) const +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("expression "); +            s.append(selector_->to_string(level+1)); + +            return s; + +        } +    }; + +    template <class Json,class JsonReference> +    class expression : public expression_base<Json,JsonReference> +    { +    public: +        using path_value_pair_type = path_value_pair<Json,JsonReference>; +        using value_type = Json; +        using reference = typename path_value_pair_type::reference; +        using pointer = typename path_value_pair_type::value_pointer; +        using const_pointer = const value_type*; +        using char_type = typename Json::char_type; +        using string_type = std::basic_string<char_type,std::char_traits<char_type>>; +        using string_view_type = typename Json::string_view_type; +        using path_value_pair_less_type = path_value_pair_less<Json,reference>; +        using path_value_pair_equal_type = path_value_pair_equal<Json,reference>; +        using parameter_type = parameter<Json>; +        using token_type = token<Json,reference>; +        using reference_arg_type = typename std::conditional<std::is_const<typename std::remove_reference<reference>::type>::value, +            const_reference_arg_t,reference_arg_t>::type; +        using json_location_node_type = json_location_node<char_type>; +        using stack_item_type = value_or_pointer<Json,JsonReference>; +    private: +        std::vector<token_type> token_list_; +    public: + +        expression() +        { +        } + +        expression(expression&& expr) +            : token_list_(std::move(expr.token_list_)) +        { +        } + +        expression(std::vector<token_type>&& token_stack) +            : token_list_(std::move(token_stack)) +        { +        } + +        expression& operator=(expression&& expr) = default; + +        value_type evaluate(dynamic_resources<Json,reference>& resources,  +                                   reference root, +                                   reference current, +                                   result_options options, +                                   std::error_code& ec) const override +        { +            std::vector<stack_item_type> stack; +            std::vector<parameter_type> arg_stack; + +            //std::cout << "EVALUATE TOKENS\n"; +            //for (auto& tok : token_list_) +            //{ +            //    std::cout << tok.to_string() << "\n"; +            //} +            //std::cout << "\n"; + +            if (!token_list_.empty()) +            { +                for (auto& tok : token_list_) +                { +                    //std::cout << "Token: " << tok.to_string() << "\n"; +                    switch (tok.token_kind()) +                    {  +                        case jsonpath_token_kind::literal: +                        { +                            stack.emplace_back(std::addressof(tok.get_value(reference_arg_type(), resources))); +                            break; +                        } +                        case jsonpath_token_kind::unary_operator: +                        { +                            JSONCONS_ASSERT(stack.size() >= 1); +                            auto item = std::move(stack.back()); +                            stack.pop_back(); + +                            auto val = tok.unary_operator_->evaluate(item.value(), ec); +                            stack.emplace_back(std::move(val)); +                            break; +                        } +                        case jsonpath_token_kind::binary_operator: +                        { +                            //std::cout << "binary operator: " << stack.size() << "\n"; +                            JSONCONS_ASSERT(stack.size() >= 2); +                            auto rhs = std::move(stack.back()); +                            //std::cout << "rhs: " << *rhs << "\n"; +                            stack.pop_back(); +                            auto lhs = std::move(stack.back()); +                            //std::cout << "lhs: " << *lhs << "\n"; +                            stack.pop_back(); + +                            auto val = tok.binary_operator_->evaluate(lhs.value(), rhs.value(), ec); +                            //std::cout << "Evaluate binary expression: " << r << "\n"; +                            stack.emplace_back(std::move(val)); +                            break; +                        } +                        case jsonpath_token_kind::root_node: +                            //std::cout << "root: " << root << "\n"; +                            stack.emplace_back(std::addressof(root)); +                            break; +                        case jsonpath_token_kind::current_node: +                            //std::cout << "current: " << current << "\n"; +                            stack.emplace_back(std::addressof(current)); +                            break; +                        case jsonpath_token_kind::argument: +                            JSONCONS_ASSERT(!stack.empty()); +                            //std::cout << "argument stack items " << stack.size() << "\n"; +                            //for (auto& item : stack) +                            //{ +                            //    std::cout << *item.to_pointer(resources) << "\n"; +                            //} +                            //std::cout << "\n"; +                            arg_stack.emplace_back(std::move(stack.back())); +                            //for (auto& item : arg_stack) +                            //{ +                            //    std::cout << *item << "\n"; +                            //} +                            //std::cout << "\n"; +                            stack.pop_back(); +                            break; +                        case jsonpath_token_kind::function: +                        { +                            if (tok.function_->arity() && *(tok.function_->arity()) != arg_stack.size()) +                            { +                                ec = jsonpath_errc::invalid_arity; +                                return Json::null(); +                            } +                            //std::cout << "function arg stack:\n"; +                            //for (auto& item : arg_stack) +                            //{ +                            //    std::cout << *item << "\n"; +                            //} +                            //std::cout << "\n"; + +                            value_type val = tok.function_->evaluate(arg_stack, ec); +                            if (ec) +                            { +                                return Json::null(); +                            } +                            //std::cout << "function result: " << val << "\n"; +                            arg_stack.clear(); +                            stack.emplace_back(std::move(val)); +                            break; +                        } +                        case jsonpath_token_kind::expression: +                        { +                            value_type val = tok.expression_->evaluate(resources, root, current, options, ec); +                            stack.emplace_back(std::move(val)); +                            break; +                        } +                        case jsonpath_token_kind::selector: +                        { +                            JSONCONS_ASSERT(!stack.empty()); +                            auto item = std::move(stack.back()); +                            //for (auto& item : stack) +                            //{ +                                //std::cout << "selector stack input:\n"; +                                //switch (item.tag) +                                //{ +                                //    case node_set_tag::single: +                                //        std::cout << "single: " << *(item.node.ptr) << "\n"; +                                //        break; +                                //    case node_set_tag::multi: +                                //        for (auto& node : stack.back().ptr().nodes) +                                //        { +                                //            std::cout << "multi: " << *node.ptr << "\n"; +                                //        } +                                //        break; +                                //    default: +                                //        break; +                            //} +                            //std::cout << "\n"; +                            //} +                            //std::cout << "selector item: " << *ptr << "\n"; + +                            reference val = tok.selector_->evaluate(resources, root, resources.current_path_node(), item.value(), options, ec); + +                            stack.pop_back(); +                            stack.emplace_back(stack_item_type(std::addressof(val))); +                            break; +                        } +                        default: +                            break; +                    } +                } +            } + +            //if (stack.size() != 1) +            //{ +            //    std::cout << "Stack size: " << stack.size() << "\n"; +            //} +            return stack.empty() ? Json::null() : stack.back().value(); +        } +  +        std::string to_string(int level) const override +        { +            std::string s; +            if (level > 0) +            { +                s.append("\n"); +                s.append(level*2, ' '); +            } +            s.append("expression "); +            for (const auto& item : token_list_) +            { +                s.append(item.to_string(level+1)); +            } + +            return s; + +        } +    private: +    }; + +} // namespace detail +} // namespace jsonpath +} // namespace jsoncons + +#endif // JSONCONS_JSONPATH_JSONPATH_EXPRESSION_HPP diff --git a/include/jsoncons_ext/jsonpath/flatten.hpp b/include/jsoncons_ext/jsonpath/flatten.hpp new file mode 100644 index 0000000..938391f --- /dev/null +++ b/include/jsoncons_ext/jsonpath/flatten.hpp @@ -0,0 +1,432 @@ +// 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_FLATTEN_HPP +#define JSONCONS_JSONPATH_FLATTEN_HPP + +#include <string> +#include <vector> +#include <memory> +#include <type_traits> // std::is_const +#include <limits> // std::numeric_limits +#include <utility> // std::move +#include <algorithm> // std::copy +#include <iterator> // std::back_inserter +#include <jsoncons_ext/jsonpath/jsonpath.hpp> + +namespace jsoncons { namespace jsonpath { + +    template <class CharT, class Sink> +    std::size_t escape_string(const CharT* s, std::size_t length, +                              Sink& sink) +    { +        std::size_t count = 0; +        const CharT* begin = s; +        const CharT* end = s + length; +        for (const CharT* it = begin; it != end; ++it) +        { +            CharT c = *it; +            switch (c) +            { +                case '\\': +                    sink.push_back('\\'); +                    sink.push_back('\\'); +                    count += 2; +                    break; +                case '\'': +                    sink.push_back('\\'); +                    sink.push_back('\''); +                    count += 2; +                    break; +                case '\b': +                    sink.push_back('\\'); +                    sink.push_back('b'); +                    count += 2; +                    break; +                case '\f': +                    sink.push_back('\\'); +                    sink.push_back('f'); +                    count += 2; +                    break; +                case '\n': +                    sink.push_back('\\'); +                    sink.push_back('n'); +                    count += 2; +                    break; +                case '\r': +                    sink.push_back('\\'); +                    sink.push_back('r'); +                    count += 2; +                    break; +                case '\t': +                    sink.push_back('\\'); +                    sink.push_back('t'); +                    count += 2; +                    break; +                default: +                    sink.push_back(c); +                    ++count; +                    break; +            } +        } +        return count; +    } + +    template<class Json> +    void flatten_(const std::basic_string<typename Json::char_type>& parent_key, +                  const Json& parent_value, +                  Json& result) +    { +        using char_type = typename Json::char_type; +        using string_type = std::basic_string<char_type>; + +        switch (parent_value.type()) +        { +            case json_type::array_value: +            { +                if (parent_value.empty()) +                { +                    result.try_emplace(parent_key, parent_value); +                } +                else +                { +                    for (std::size_t i = 0; i < parent_value.size(); ++i) +                    { +                        string_type key(parent_key); +                        key.push_back('['); +                        jsoncons::detail::from_integer(i,key); +                        key.push_back(']'); +                        flatten_(key, parent_value.at(i), result); +                    } +                } +                break; +            } + +            case json_type::object_value: +            { +                if (parent_value.empty()) +                { +                    result.try_emplace(parent_key, Json()); +                } +                else +                { +                    for (const auto& item : parent_value.object_range()) +                    { +                        string_type key(parent_key); +                        key.push_back('['); +                        key.push_back('\''); +                        escape_string(item.key().data(), item.key().length(), key); +                        key.push_back('\''); +                        key.push_back(']'); +                        flatten_(key, item.value(), result); +                    } +                } +                break; +            } + +            default: +            { +                result[parent_key] = parent_value; +                break; +            } +        } +    } + +    template<class Json> +    Json flatten(const Json& value) +    { +        Json result; +        std::basic_string<typename Json::char_type> parent_key = {'$'}; +        flatten_(parent_key, value, result); +        return result; +    } + +    enum class unflatten_state  +    { +        start, +        expect_lbracket, +        lbracket, +        single_quoted_name_state, +        double_quoted_name_state, +        index_state, +        expect_rbracket, +        double_quoted_string_escape_char, +        single_quoted_string_escape_char +    }; + +    template<class Json> +    Json unflatten(const Json& value) +    { +        using char_type = typename Json::char_type; +        using string_type = std::basic_string<char_type>; + +        if (JSONCONS_UNLIKELY(!value.is_object())) +        { +            JSONCONS_THROW(jsonpath_error(jsonpath_errc::argument_to_unflatten_invalid)); +        } + +        Json result; + +        for (const auto& item : value.object_range()) +        { +            Json* part = &result; +            string_type buffer; +            unflatten_state state = unflatten_state::start; + +            auto it = item.key().begin(); +            auto last = item.key().end(); + +            for (; it != last; ++it) +            { +                switch (state) +                { +                    case unflatten_state::start: +                    { +                        switch (*it) +                        { +                            case '$': +                                state = unflatten_state::expect_lbracket; +                                break; +                            default: +                                break; +                        } +                        break; +                    } +                    case unflatten_state::expect_lbracket: +                    { +                        switch (*it) +                        { +                            case '[': +                                state = unflatten_state::lbracket; +                                break; +                            default: +                                JSONCONS_THROW(jsonpath_error(jsonpath_errc::invalid_flattened_key)); +                                break; +                        } +                        break; +                    } +                    case unflatten_state::lbracket: +                    { +                        switch (*it) +                        { +                            case '\'': +                                state = unflatten_state::single_quoted_name_state; +                                break; +                            case '\"': +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': +                                buffer.push_back(*it); +                                state = unflatten_state::index_state; +                                break; +                            default: +                                JSONCONS_THROW(jsonpath_error(jsonpath_errc::invalid_flattened_key)); +                                break; +                        } +                        break; +                    } +                    case unflatten_state::single_quoted_name_state: +                    { +                        switch (*it) +                        { +                            case '\'': +                                if (it != last-2) +                                { +                                    auto res = part->try_emplace(buffer,Json()); +                                    part = &(res.first->value()); +                                } +                                else +                                { +                                    auto res = part->try_emplace(buffer,item.value()); +                                    part = &(res.first->value()); +                                } +                                buffer.clear(); +                                state = unflatten_state::expect_rbracket; +                                break; +                            case '\\': +                                state = unflatten_state::single_quoted_string_escape_char; +                                break; +                            default: +                                buffer.push_back(*it); +                                break; +                        } +                        break; +                    } +                    case unflatten_state::double_quoted_name_state: +                    { +                        switch (*it) +                        { +                            case '\"': +                                if (it != last-2) +                                { +                                    auto res = part->try_emplace(buffer,Json()); +                                    part = &(res.first->value()); +                                } +                                else +                                { +                                    auto res = part->try_emplace(buffer,item.value()); +                                    part = &(res.first->value()); +                                } +                                buffer.clear(); +                                state = unflatten_state::expect_rbracket; +                                break; +                            case '\\': +                                state = unflatten_state::double_quoted_string_escape_char; +                                break; +                            default: +                                buffer.push_back(*it); +                                break; +                        } +                        break; +                    } +                    case unflatten_state::double_quoted_string_escape_char: +                        switch (*it) +                        { +                            case '\"': +                                buffer.push_back('\"'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case '\\':  +                                buffer.push_back('\\'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case '/': +                                buffer.push_back('/'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case 'b': +                                buffer.push_back('\b'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case 'f': +                                buffer.push_back('\f'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case 'n': +                                buffer.push_back('\n'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case 'r': +                                buffer.push_back('\r'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case 't': +                                buffer.push_back('\t'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            default: +                                break; +                        } +                        break; +                    case unflatten_state::single_quoted_string_escape_char: +                        switch (*it) +                        { +                            case '\'': +                                buffer.push_back('\''); +                                state = unflatten_state::single_quoted_name_state; +                                break; +                            case '\\':  +                                buffer.push_back('\\'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case '/': +                                buffer.push_back('/'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case 'b': +                                buffer.push_back('\b'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case 'f': +                                buffer.push_back('\f'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case 'n': +                                buffer.push_back('\n'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case 'r': +                                buffer.push_back('\r'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            case 't': +                                buffer.push_back('\t'); +                                state = unflatten_state::double_quoted_name_state; +                                break; +                            default: +                                break; +                        } +                        break; +                    case unflatten_state::index_state: +                    { +                        switch (*it) +                        { +                            case ']': +                            { +                                std::size_t n{0}; +                                auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); +                                if (r) +                                { +                                    if (!part->is_array()) +                                    { +                                        *part = Json(json_array_arg); +                                    } +                                    if (it != last-1) +                                    { +                                        if (n+1 > part->size()) +                                        { +                                            Json& ref = part->emplace_back(); +                                            part = std::addressof(ref); +                                        } +                                        else +                                        { +                                            part = &part->at(n); +                                        } +                                    } +                                    else +                                    { +                                        Json& ref = part->emplace_back(item.value()); +                                        part = std::addressof(ref); +                                    } +                                } +                                buffer.clear(); +                                state = unflatten_state::expect_lbracket; +                                break; +                            } +                            case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': +                                buffer.push_back(*it); +                                break; +                            default: +                                JSONCONS_THROW(jsonpath_error(jsonpath_errc::invalid_flattened_key)); +                                break; +                        } +                        break; +                    } +                    case unflatten_state::expect_rbracket: +                    { +                        switch (*it) +                        { +                            case ']': +                                state = unflatten_state::expect_lbracket; +                                break; +                            default: +                                JSONCONS_THROW(jsonpath_error(jsonpath_errc::invalid_flattened_key)); +                                break; +                        } +                        break; +                    } +                    default: +                        JSONCONS_UNREACHABLE(); +                        break; +                } +            } +        } + +        return result; +    } +}} + +#endif diff --git a/include/jsoncons_ext/jsonpath/json_location.hpp b/include/jsoncons_ext/jsonpath/json_location.hpp new file mode 100644 index 0000000..9105608 --- /dev/null +++ b/include/jsoncons_ext/jsonpath/json_location.hpp @@ -0,0 +1,445 @@ +// 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_JSON_LOCATION_HPP +#define JSONCONS_JSONPATH_JSON_LOCATION_HPP + +#include <string> +#include <vector> +#include <functional> +#include <algorithm> // std::reverse +#include <jsoncons/config/jsoncons_config.hpp> +#include <jsoncons/detail/write_number.hpp> +#include <jsoncons_ext/jsonpath/jsonpath_error.hpp> +#include <jsoncons/json_type.hpp> + +namespace jsoncons {  +namespace jsonpath { + +    template <class CharT> +    class json_location;  + +    enum class json_location_node_kind { root, index, name }; + +    template <class CharT> +    class json_location_node  +    { +        friend class json_location<CharT>; +    public: +        using char_type = CharT; +        using string_type = std::basic_string<CharT>; +    private: + +        const json_location_node* parent_; +        json_location_node_kind node_kind_; +        string_type name_; +        std::size_t index_; +    public: +        json_location_node(char_type c) +            : parent_(nullptr), node_kind_(json_location_node_kind::root), index_(0) +        { +            name_.push_back(c); +        } + +        json_location_node(const json_location_node* parent, const string_type& name) +            : parent_(parent), node_kind_(json_location_node_kind::name), name_(name), index_(0) +        { +        } + +        json_location_node(const json_location_node* parent, std::size_t index) +            : parent_(parent), node_kind_(json_location_node_kind::index), index_(index) +        { +        } + +        const json_location_node* parent() const { return parent_;} + +        json_location_node_kind node_kind() const +        { +            return node_kind_; +        } + +        const string_type& name() const +        { +            return name_; +        } + +        std::size_t index() const  +        { +            return index_; +        } + +        void swap(json_location_node& node) +        { +            std::swap(parent_, node.parent_); +            std::swap(node_kind_, node.node_kind_); +            std::swap(name_, node.name_); +            std::swap(index_, node.index_); +        } + +    private: + +        std::size_t node_hash() const +        { +            std::size_t h = node_kind_ == json_location_node_kind::index ? std::hash<std::size_t>{}(index_) : std::hash<string_type>{}(name_); + +            return h; +        } + +        int compare_node(const json_location_node& other) const +        { +            int diff = 0; +            if (node_kind_ != other.node_kind_) +            { +                diff = static_cast<int>(node_kind_) - static_cast<int>(other.node_kind_); +            } +            else +            { +                switch (node_kind_) +                { +                    case json_location_node_kind::root: +                        diff = name_.compare(other.name_); +                        break; +                    case json_location_node_kind::index: +                        diff = index_ < other.index_ ? -1 : index_ > other.index_ ? 1 : 0; +                        break; +                    case json_location_node_kind::name: +                        diff = name_.compare(other.name_); +                        break; +                } +            } +            return diff; +        } +    }; + +    namespace detail { + +        template <class Iterator> +        class json_location_iterator +        {  +            Iterator it_;  + +        public: +            using iterator_category = std::random_access_iterator_tag; + +            using value_type = typename std::remove_pointer<typename std::iterator_traits<Iterator>::value_type>::type; +            using difference_type = typename std::iterator_traits<Iterator>::difference_type; +            using pointer = const value_type*; +            using reference = const value_type&; + +            json_location_iterator() : it_() +            {  +            } + +            explicit json_location_iterator(Iterator ptr) : it_(ptr) +            { +            } + +            json_location_iterator(const json_location_iterator&) = default; +            json_location_iterator(json_location_iterator&&) = default; +            json_location_iterator& operator=(const json_location_iterator&) = default; +            json_location_iterator& operator=(json_location_iterator&&) = default; + +            template <class Iter, +                      class=typename std::enable_if<!std::is_same<Iter,Iterator>::value && std::is_convertible<Iter,Iterator>::value>::type> +            json_location_iterator(const json_location_iterator<Iter>& other) +                : it_(other.it_) +            { +            } + +            operator Iterator() const +            {  +                return it_;  +            } + +            reference operator*() const  +            { +                return *(*it_); +            } + +            pointer operator->() const  +            { +                return (*it_); +            } + +            json_location_iterator& operator++()  +            { +                ++it_; +                return *this; +            } + +            json_location_iterator operator++(int)  +            { +                json_location_iterator temp = *this; +                ++*this; +                return temp; +            } + +            json_location_iterator& operator--()  +            { +                --it_; +                return *this; +            } + +            json_location_iterator operator--(int)  +            { +                json_location_iterator temp = *this; +                --*this; +                return temp; +            } + +            json_location_iterator& operator+=(const difference_type offset)  +            { +                it_ += offset; +                return *this; +            } + +            json_location_iterator operator+(const difference_type offset) const  +            { +                json_location_iterator temp = *this; +                return temp += offset; +            } + +            json_location_iterator& operator-=(const difference_type offset)  +            { +                return *this += -offset; +            } + +            json_location_iterator operator-(const difference_type offset) const  +            { +                json_location_iterator temp = *this; +                return temp -= offset; +            } + +            difference_type operator-(const json_location_iterator& rhs) const noexcept +            { +                return it_ - rhs.it_; +            } + +            reference operator[](const difference_type offset) const noexcept +            { +                return *(*(*this + offset)); +            } + +            bool operator==(const json_location_iterator& rhs) const noexcept +            { +                return it_ == rhs.it_; +            } + +            bool operator!=(const json_location_iterator& rhs) const noexcept +            { +                return !(*this == rhs); +            } + +            bool operator<(const json_location_iterator& rhs) const noexcept +            { +                return it_ < rhs.it_; +            } + +            bool operator>(const json_location_iterator& rhs) const noexcept +            { +                return rhs < *this; +            } + +            bool operator<=(const json_location_iterator& rhs) const noexcept +            { +                return !(rhs < *this); +            } + +            bool operator>=(const json_location_iterator& rhs) const noexcept +            { +                return !(*this < rhs); +            } + +            inline  +            friend json_location_iterator<Iterator> operator+( +                difference_type offset, json_location_iterator<Iterator> next)  +            { +                return next += offset; +            } +        }; + +    } // namespace detail + +    template <class CharT> +    class json_location +    { +    public: +        using char_type = CharT; +        using string_type = std::basic_string<CharT>; +        using json_location_node_type = json_location_node<CharT>; +    private: +        std::vector<const json_location_node_type*> nodes_; +    public: +        using iterator = typename detail::json_location_iterator<typename std::vector<const json_location_node_type*>::iterator>; +        using const_iterator = typename detail::json_location_iterator<typename std::vector<const json_location_node_type*>::const_iterator>; + +        json_location(const json_location_node_type& node) +        { +            const json_location_node_type* p = std::addressof(node); +            do +            { +                nodes_.push_back(p); +                p = p->parent_; +            } +            while (p != nullptr); + +            std::reverse(nodes_.begin(), nodes_.end()); +        } + +        iterator begin() +        { +            return iterator(nodes_.begin()); +        } + +        iterator end() +        { +            return iterator(nodes_.end()); +        } + +        const_iterator begin() const +        { +            return const_iterator(nodes_.begin()); +        } + +        const_iterator end() const +        { +            return const_iterator(nodes_.end()); +        } + +        const json_location_node_type& last() const +        { +            return *nodes_.back(); +        } + +        string_type to_string() const +        { +            string_type buffer; + +            for (const auto& node : nodes_) +            { +                switch (node->node_kind()) +                { +                    case json_location_node_kind::root: +                        buffer.append(node->name()); +                        break; +                    case json_location_node_kind::name: +                        buffer.push_back('['); +                        buffer.push_back('\''); +                        for (auto c : node->name()) +                        { +                            if (c == '\'') +                            { +                                buffer.push_back('\\'); +                                buffer.push_back('\''); +                            } +                            else +                            { +                                buffer.push_back(c); +                            } +                        } +                        buffer.push_back('\''); +                        buffer.push_back(']'); +                        break; +                    case json_location_node_kind::index: +                        buffer.push_back('['); +                        jsoncons::detail::from_integer(node->index(), buffer); +                        buffer.push_back(']'); +                        break; +                } +            } + +            return buffer; +        } + +        int compare(const json_location& other) const +        { +            if (this == &other) +            { +               return 0; +            } + +            auto it1 = nodes_.begin(); +            auto it2 = other.nodes_.begin(); +            while (it1 != nodes_.end() && it2 != other.nodes_.end()) +            { +                int diff = (*it1)->compare_node(*(*it2)); +                if (diff != 0) +                { +                    return diff; +                } +                ++it1; +                ++it2; +            } +            return (nodes_.size() < other.nodes_.size()) ? -1 : (nodes_.size() == other.nodes_.size()) ? 0 : 1; +        } + +        std::size_t hash() const +        { + +            auto it = nodes_.begin(); +            std::size_t hash = (*it).hash(); +            ++it; + +            while (it != nodes_.end()) +            { +                hash += 17*(*it)->node_hash(); +                ++it; +            } + +            return hash; +        } + +        friend bool operator==(const json_location& lhs, const json_location& rhs)  +        { +            return lhs.compare(rhs) == 0; +        } + +        friend bool operator!=(const json_location& lhs, const json_location& rhs) +        { +            return !(lhs == rhs); +        } + +        friend bool operator<(const json_location& lhs, const json_location& rhs)  +        { +            return lhs.compare(rhs) < 0; +        } +    }; + +    template <class Json> +    Json* select(Json& root, const json_location<typename Json::char_type>& path) +    { +        Json* current = std::addressof(root); +        for (const auto& json_location_node : path) +        { +            if (json_location_node.node_kind() == json_location_node_kind::index) +            { +                if (current->type() != json_type::array_value || json_location_node.index() >= current->size()) +                { +                    return nullptr;  +                } +                current = std::addressof(current->at(json_location_node.index())); +            } +            else if (json_location_node.node_kind() == json_location_node_kind::name) +            { +                if (current->type() != json_type::object_value) +                { +                    return nullptr; +                } +                auto it = current->find(json_location_node.name()); +                if (it == current->object_range().end()) +                { +                    return nullptr; +                } +                current = std::addressof(it->value()); +            } +        } +        return current; +    } + +} // namespace jsonpath +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/jsonpath/json_query.hpp b/include/jsoncons_ext/jsonpath/json_query.hpp new file mode 100644 index 0000000..8facfa9 --- /dev/null +++ b/include/jsoncons_ext/jsonpath/json_query.hpp @@ -0,0 +1,115 @@ +// 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_JSON_QUERY_HPP +#define JSONCONS_JSONPATH_JSON_QUERY_HPP + +#include <jsoncons/json.hpp> +#include <jsoncons_ext/jsonpath/jsonpath_expression.hpp> + +namespace jsoncons {  +namespace jsonpath { +      +    template<class Json> +    Json json_query(const Json& instance, +                    const typename Json::string_view_type& path,  +                    result_options options = result_options(), +                    const custom_functions<Json>& functions = custom_functions<Json>()) +    { +        auto expr = make_expression<Json>(path, functions); +        return expr.evaluate(instance, options); +    } + +    template<class Json,class Callback> +    typename std::enable_if<type_traits::is_binary_function_object<Callback,const std::basic_string<typename Json::char_type>&,const Json&>::value,void>::type +    json_query(const Json& instance,  +               const typename Json::string_view_type& path,  +               Callback callback, +               result_options options = result_options(), +               const custom_functions<Json>& functions = custom_functions<Json>()) +    { +        auto expr = make_expression<Json>(path, functions); +        expr.evaluate(instance, callback, options); +    } + +    template<class Json, class T> +    typename std::enable_if<is_json_type_traits_specialized<Json,T>::value,void>::type +        json_replace(Json& instance, const typename Json::string_view_type& path, T&& new_value, +                     result_options options = result_options::nodups, +                     const custom_functions<Json>& funcs = custom_functions<Json>()) +    { +        using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator<Json, Json&>; +        //using string_type = typename evaluator_t::string_type; +        using value_type = typename evaluator_t::value_type; +        using reference = typename evaluator_t::reference; +        using json_selector_t = typename evaluator_t::path_expression_type; +        using json_location_type = typename evaluator_t::json_location_type; + +        jsoncons::jsonpath::detail::static_resources<value_type,reference> static_resources(funcs); +        evaluator_t e; +        json_selector_t expr = e.compile(static_resources, path); + +        jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources; +        auto callback = [&new_value](const json_location_type&, reference v) +        { +            v = std::forward<T>(new_value); +        }; +        expr.evaluate(resources, instance, resources.root_path_node(), instance, callback, options); +    } + +    template<class Json, class UnaryCallback> +    typename std::enable_if<type_traits::is_unary_function_object<UnaryCallback,Json>::value,void>::type +    json_replace(Json& instance, const typename Json::string_view_type& path , UnaryCallback callback) +    { +        using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator<Json, Json&>; +        //using string_type = typename evaluator_t::string_type; +        using value_type = typename evaluator_t::value_type; +        using reference = typename evaluator_t::reference; +        using json_selector_t = typename evaluator_t::path_expression_type; +        using json_location_type = typename evaluator_t::json_location_type; + +        jsoncons::jsonpath::detail::static_resources<value_type,reference> static_resources; +        evaluator_t e; +        json_selector_t expr = e.compile(static_resources, path); + +        jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources; +        auto f = [callback](const json_location_type&, reference v) +        { +            v = callback(v); +        }; +        expr.evaluate(resources, instance, resources.root_path_node(), instance, f, result_options::nodups); +    } + +    template<class Json, class BinaryCallback> +    typename std::enable_if<type_traits::is_binary_function_object<BinaryCallback,const std::basic_string<typename Json::char_type>&,Json&>::value,void>::type +    json_replace(Json& instance, const typename Json::string_view_type& path , BinaryCallback callback,  +                 result_options options = result_options::nodups, +                 const custom_functions<Json>& funcs = custom_functions<Json>()) +    { +        using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator<Json, Json&>; +        //using string_type = typename evaluator_t::string_type; +        using value_type = typename evaluator_t::value_type; +        using reference = typename evaluator_t::reference; +        using json_selector_t = typename evaluator_t::path_expression_type; +        using json_location_type = typename evaluator_t::json_location_type; + +        jsoncons::jsonpath::detail::static_resources<value_type,reference> static_resources(funcs); +        evaluator_t e; +        json_selector_t expr = e.compile(static_resources, path); + +        jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources; + +        auto f = [&callback](const json_location_type& path, reference val) +        { +            callback(path.to_string(), val); +        }; +        expr.evaluate(resources, instance, resources.root_path_node(), instance, f, options); +    } + +} // namespace jsonpath +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/jsonpath/jsonpath.hpp b/include/jsoncons_ext/jsonpath/jsonpath.hpp new file mode 100644 index 0000000..18500c5 --- /dev/null +++ b/include/jsoncons_ext/jsonpath/jsonpath.hpp @@ -0,0 +1,13 @@ +// Copyright 2013 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_HPP +#define JSONCONS_JSONPATH_JSONPATH_HPP + +#include <jsoncons_ext/jsonpath/json_query.hpp> +#include <jsoncons_ext/jsonpath/flatten.hpp> + +#endif diff --git a/include/jsoncons_ext/jsonpath/jsonpath_error.hpp b/include/jsoncons_ext/jsonpath/jsonpath_error.hpp new file mode 100644 index 0000000..8157bba --- /dev/null +++ b/include/jsoncons_ext/jsonpath/jsonpath_error.hpp @@ -0,0 +1,240 @@ +/// 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_ERROR_HPP +#define JSONCONS_JSONPATH_JSONPATH_ERROR_HPP + +#include <jsoncons/json_exception.hpp> +#include <system_error> + +namespace jsoncons { namespace jsonpath { + +    enum class jsonpath_errc  +    { +        success = 0, +        expected_root_or_function, +        expected_current_node, +        expected_rparen, +        expected_rbracket, +        expected_separator, +        expected_forward_slash, +        expected_slice_start, +        expected_slice_end, +        expected_slice_step, +        expected_bracket_specifier_or_union, +        unexpected_operator, +        invalid_function_name, +        invalid_argument, +        invalid_arity, +        function_name_not_found, +        parse_error_in_filter, +        argument_parse_error, +        unidentified_error, +        unexpected_eof, +        expected_colon_dot_left_bracket_comma_or_rbracket, +        argument_to_unflatten_invalid, +        invalid_flattened_key, +        step_cannot_be_zero, +        invalid_number, +        illegal_escaped_character, +        invalid_codepoint, +        unknown_function, +        invalid_type, +        unbalanced_parentheses, +        syntax_error, +        expected_comparator, +        expected_or, +        expected_and, +        expected_comma_or_rparen, +        expected_comma_or_rbracket, +        expected_relative_path +    }; + +    class jsonpath_error_category_impl +       : public std::error_category +    { +    public: +        const char* name() const noexcept override +        { +            return "jsoncons/jsonpath"; +        } +        std::string message(int ev) const override +        { +            switch (static_cast<jsonpath_errc>(ev)) +            { +                case jsonpath_errc::expected_root_or_function: +                    return "Expected '$' or function expression"; +                case jsonpath_errc::expected_current_node: +                    return "Expected @"; +                case jsonpath_errc::expected_rbracket: +                    return "Expected ]"; +                case jsonpath_errc::expected_rparen: +                    return "Expected )"; +                case jsonpath_errc::expected_slice_start: +                    return "Expected slice start"; +                case jsonpath_errc::expected_slice_end: +                    return "Expected slice end"; +                case jsonpath_errc::expected_slice_step: +                    return "Expected slice step"; +                case jsonpath_errc::expected_separator: +                    return "Expected dot or left bracket separator"; +                case jsonpath_errc::expected_forward_slash: +                    return "Invalid path filter, expected '/'"; +                case jsonpath_errc::expected_bracket_specifier_or_union: +                    return "Expected index, single or double quoted name, expression, filter, absolute ('$') path or relative ('@') path"; +                case jsonpath_errc::invalid_function_name: +                    return "Invalid function name"; +                case jsonpath_errc::invalid_argument: +                    return "Invalid argument type"; +                case jsonpath_errc::invalid_arity: +                    return "Incorrect number of arguments"; +                case jsonpath_errc::function_name_not_found: +                    return "Function name not found"; +                case jsonpath_errc::parse_error_in_filter: +                    return "Could not parse JSON expression in a JSONPath filter"; +                case jsonpath_errc::argument_parse_error: +                    return "Could not parse JSON expression passed to JSONPath function"; +                case jsonpath_errc::unidentified_error: +                    return "Unidentified error"; +                case jsonpath_errc::unexpected_eof: +                    return "Unexpected EOF while parsing jsonpath expression"; +                case jsonpath_errc::expected_colon_dot_left_bracket_comma_or_rbracket: +                    return "Expected ':', '.', '[', ',', or ']'"; +                case jsonpath_errc::argument_to_unflatten_invalid: +                    return "Argument to unflatten must be an object"; +                case jsonpath_errc::invalid_flattened_key: +                    return "Flattened key is invalid"; +                case jsonpath_errc::step_cannot_be_zero: +                    return "Slice step cannot be zero"; +                case jsonpath_errc::invalid_number: +                    return "Invalid number"; +                case jsonpath_errc::illegal_escaped_character: +                    return "Illegal escaped character"; +                case jsonpath_errc::invalid_codepoint: +                    return "Invalid codepoint"; +                case jsonpath_errc::unknown_function: +                    return "Unknown function"; +                case jsonpath_errc::invalid_type: +                    return "Invalid type"; +                case jsonpath_errc::unbalanced_parentheses: +                    return "Unbalanced parentheses"; +                case jsonpath_errc::syntax_error: +                    return "Syntax error"; +                case jsonpath_errc::expected_comparator: +                    return "Expected comparator"; +                case jsonpath_errc::expected_or: +                    return "Expected operator '||'"; +                case jsonpath_errc::expected_and: +                    return "Expected operator '&&'"; +                case jsonpath_errc::expected_comma_or_rparen: +                    return "Expected comma or right parenthesis"; +                case jsonpath_errc::expected_comma_or_rbracket: +                    return "Expected comma or right bracket"; +                case jsonpath_errc::expected_relative_path: +                    return "Expected unquoted string, or single or double quoted string, or index or '*'"; +                default: +                    return "Unknown jsonpath parser error"; +            } +        } +    }; + +    inline +    const std::error_category& jsonpath_error_category() +    { +      static jsonpath_error_category_impl instance; +      return instance; +    } + +    inline  +    std::error_code make_error_code(jsonpath_errc result) +    { +        return std::error_code(static_cast<int>(result),jsonpath_error_category()); +    } + +} // jsonpath +} // jsoncons + +namespace std { +    template<> +    struct is_error_code_enum<jsoncons::jsonpath::jsonpath_errc> : public true_type +    { +    }; +} + +namespace jsoncons { namespace jsonpath { + +    class jsonpath_error : public std::system_error, public virtual json_exception +    { +        std::size_t line_number_; +        std::size_t column_number_; +        mutable std::string what_; +    public: +        jsonpath_error(std::error_code ec) +            : std::system_error(ec), line_number_(0), column_number_(0) +        { +        } +        jsonpath_error(std::error_code ec, const std::string& what_arg) +            : std::system_error(ec, what_arg), line_number_(0), column_number_(0) +        { +        } +        jsonpath_error(std::error_code ec, std::size_t position) +            : std::system_error(ec), line_number_(0), column_number_(position) +        { +        } +        jsonpath_error(std::error_code ec, std::size_t line, std::size_t column) +            : std::system_error(ec), line_number_(line), column_number_(column) +        { +        } +        jsonpath_error(const jsonpath_error& other) = default; + +        jsonpath_error(jsonpath_error&& other) = default; + +        const char* what() const noexcept override +        { +            if (what_.empty()) +            { +                JSONCONS_TRY +                { +                    what_.append(std::system_error::what()); +                    if (line_number_ != 0 && column_number_ != 0) +                    { +                        what_.append(" at line "); +                        what_.append(std::to_string(line_number_)); +                        what_.append(" and column "); +                        what_.append(std::to_string(column_number_)); +                    } +                    else if (column_number_ != 0) +                    { +                        what_.append(" at position "); +                        what_.append(std::to_string(column_number_)); +                    } +                    return what_.c_str(); +                } +                JSONCONS_CATCH(...) +                { +                    return std::system_error::what(); +                } +            } +            else +            { +                return what_.c_str(); +            } +        } + +        std::size_t line() const noexcept +        { +            return line_number_; +        } + +        std::size_t column() const noexcept +        { +            return column_number_; +        } +    }; + +}} + +#endif diff --git a/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp b/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp new file mode 100644 index 0000000..a3537b3 --- /dev/null +++ b/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp @@ -0,0 +1,2612 @@ +// 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_EXPRESSION_HPP +#define JSONCONS_JSONPATH_JSONPATH_EXPRESSION_HPP + +#include <string> +#include <vector> +#include <memory> +#include <type_traits> // std::is_const +#include <limits> // std::numeric_limits +#include <utility> // std::move +#include <regex> +#include <algorithm> // std::reverse +#include <jsoncons/json.hpp> +#include <jsoncons_ext/jsonpath/jsonpath_error.hpp> +#include <jsoncons_ext/jsonpath/expression.hpp> +#include <jsoncons_ext/jsonpath/jsonpath_selector.hpp> + +namespace jsoncons {  +namespace jsonpath { +namespace detail { +      +    enum class path_state  +    { +        start, +        root_or_current_node, +        expect_function_expr, +        relative_path, +        relative_location, +        parent_operator, +        ancestor_depth, +        filter_expression, +        expression_rhs, +        recursive_descent_or_expression_lhs, +        path_or_literal_or_function, +        json_text_or_function, +        json_text_or_function_name, +        json_text_string, +        json_value, +        json_string, +        identifier_or_function_expr, +        name_or_lbracket, +        unquoted_string, +        anything, +        number, +        function_expression, +        argument, +        zero_or_one_arguments, +        one_or_more_arguments, +        identifier, +        single_quoted_string, +        double_quoted_string, +        bracketed_unquoted_name_or_union, +        union_expression, +        identifier_or_union, +        bracket_specifier_or_union, +        bracketed_wildcard, +        index_or_slice, +        wildcard_or_union, +        union_element, +        index_or_slice_or_union, +        integer, +        digit, +        slice_expression_stop, +        slice_expression_step, +        comma_or_rbracket, +        expect_rparen, +        expect_rbracket, +        quoted_string_escape_char, +        escape_u1,  +        escape_u2,  +        escape_u3,  +        escape_u4,  +        escape_expect_surrogate_pair1,  +        escape_expect_surrogate_pair2,  +        escape_u5,  +        escape_u6,  +        escape_u7,  +        escape_u8, +        expression, +        comparator_expression, +        eq_or_regex, +        expect_regex, +        regex, +        regex_options, +        regex_pattern, +        cmp_lt_or_lte, +        cmp_gt_or_gte, +        cmp_ne, +        expect_or, +        expect_and +    }; + +    template<class Json, +             class JsonReference> +    class jsonpath_evaluator : public ser_context +    { +    public: +        using char_type = typename Json::char_type; +        using string_type = std::basic_string<char_type,std::char_traits<char_type>>; +        using string_view_type = typename Json::string_view_type; +        using path_value_pair_type = path_value_pair<Json,JsonReference>; +        using value_type = Json; +        using reference = JsonReference; +        using pointer = typename path_value_pair_type::value_pointer; +        using token_type = token<Json,JsonReference>; +        using path_expression_type = path_expression<Json,JsonReference>; +        using expression_type = expression<Json,JsonReference>; +        using json_location_type = json_location<char_type>; +        using json_location_node_type = json_location_node<char_type>; +        using selector_type = jsonpath_selector<Json,JsonReference>; + +    private: + +        std::size_t line_; +        std::size_t column_; +        const char_type* begin_input_; +        const char_type* end_input_; +        const char_type* p_; + +        using argument_type = std::vector<pointer>; +        std::vector<argument_type> function_stack_; +        std::vector<path_state> state_stack_; +        std::vector<token_type> output_stack_; +        std::vector<token_type> operator_stack_; + +    public: +        jsonpath_evaluator() +            : line_(1), column_(1), +              begin_input_(nullptr), end_input_(nullptr), +              p_(nullptr) +        { +        } + +        jsonpath_evaluator(std::size_t line, std::size_t column) +            : line_(line), column_(column), +              begin_input_(nullptr), end_input_(nullptr), +              p_(nullptr) +        { +        } + +        std::size_t line() const +        { +            return line_; +        } + +        std::size_t column() const +        { +            return column_; +        } + +        path_expression_type compile(static_resources<value_type,reference>& resources, const string_view_type& path) +        { +            std::error_code ec; +            auto result = compile(resources, path, ec); +            if (ec) +            { +                JSONCONS_THROW(jsonpath_error(ec, line_, column_)); +            } +            return result; +        } + +        path_expression_type compile(static_resources<value_type,reference>& resources,  +                                     const string_view_type& path,  +                                     std::error_code& ec) +        { +            std::size_t selector_id = 0; + +            string_type buffer; +            string_type buffer2; +            uint32_t cp = 0; +            uint32_t cp2 = 0; + +            begin_input_ = path.data(); +            end_input_ = path.data() + path.length(); +            p_ = begin_input_; + +            slice slic; +            bool paths_required = false; +            int ancestor_depth = 0; + +            state_stack_.emplace_back(path_state::start); +            while (p_ < end_input_ && !state_stack_.empty()) +            { +                switch (state_stack_.back()) +                { +                    case path_state::start:  +                    { +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '$': +                            case '@': +                            { +                                push_token(resources, token_type(resources.new_selector(current_node_selector<Json,JsonReference>())), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.emplace_back(path_state::relative_location); +                                ++p_; +                                ++column_; +                                break; +                            } +                            default: +                            { +                                state_stack_.emplace_back(path_state::relative_location); +                                state_stack_.emplace_back(path_state::expect_function_expr); +                                state_stack_.emplace_back(path_state::unquoted_string); +                                break; +                            } +                        } +                        break; +                    } +                    case path_state::root_or_current_node:  +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '$': +                                push_token(resources, token_type(root_node_arg), ec); +                                push_token(resources, token_type(resources.new_selector(root_selector<Json,JsonReference>(selector_id++))), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back(); +                                ++p_; +                                ++column_; +                                break; +                            case '@': +                                push_token(resources, token_type(current_node_arg), ec); // ISSUE +                                push_token(resources, token_type(resources.new_selector(current_node_selector<Json,JsonReference>())), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back(); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                ec = jsonpath_errc::syntax_error; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::recursive_descent_or_expression_lhs: +                        switch (*p_) +                        { +                            case '.': +                                push_token(resources, token_type(resources.new_selector(recursive_selector<Json,JsonReference>())), ec); +                                if (ec) {return path_expression_type();} +                                ++p_; +                                ++column_; +                                state_stack_.back() = path_state::name_or_lbracket; +                                break; +                            default: +                                state_stack_.back() = path_state::relative_path; +                                break; +                        } +                        break; +                    case path_state::name_or_lbracket:  +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '[': // [ can follow .. +                                state_stack_.back() = path_state::bracket_specifier_or_union; +                                ++p_; +                                ++column_; +                                break; +                            default: +                                buffer.clear(); +                                state_stack_.back() = path_state::relative_path; +                                break; +                        } +                        break; +                    case path_state::json_string: +                    { +                        //std::cout << "literal: " << buffer << "\n"; +                        push_token(resources, token_type(literal_arg, Json(buffer)), ec); +                        if (ec) {return path_expression_type();} +                        buffer.clear(); +                        state_stack_.pop_back(); // json_value +                        break; +                    } +                    case path_state::path_or_literal_or_function:  +                    { +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '$': +                            case '@': +                                state_stack_.back() = path_state::relative_location; +                                state_stack_.push_back(path_state::root_or_current_node); +                                break; +                            case '(': +                            { +                                ++p_; +                                ++column_; +                                push_token(resources, lparen_arg, ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::expect_rparen; +                                state_stack_.emplace_back(path_state::expression_rhs); +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                break; +                            } +                            case '\'': +                                state_stack_.back() = path_state::json_string; +                                state_stack_.emplace_back(path_state::single_quoted_string); +                                ++p_; +                                ++column_; +                                break; +                            case '\"': +                                state_stack_.back() = path_state::json_string; +                                state_stack_.emplace_back(path_state::double_quoted_string); +                                ++p_; +                                ++column_; +                                break; +                            case '!': +                            { +                                ++p_; +                                ++column_; +                                push_token(resources, token_type(resources.get_unary_not()), ec); +                                if (ec) {return path_expression_type();} +                                break; +                            } +                            case '-': +                            { +                                ++p_; +                                ++column_; +                                push_token(resources, token_type(resources.get_unary_minus()), ec); +                                if (ec) {return path_expression_type();} +                                break; +                            } +                            case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': +                            { +                                state_stack_.back() = path_state::json_value; +                                state_stack_.emplace_back(path_state::number); +                                break; +                            } +                            default: +                            { +                                state_stack_.back() = path_state::json_text_or_function_name; +                                break; +                            } +                        } +                        break; +                    } +                    case path_state::json_text_or_function: +                    { +                        switch(*p_) +                        { +                            case '(': +                            { +                                auto f = resources.get_function(buffer, ec); +                                if (ec) +                                { +                                    return path_expression_type(); +                                } +                                buffer.clear(); +                                push_token(resources, current_node_arg, ec); +                                if (ec) {return path_expression_type();} +                                push_token(resources, token_type(f), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::function_expression; +                                state_stack_.emplace_back(path_state::zero_or_one_arguments); +                                ++p_; +                                ++column_; +                                break; +                            } +                            default: +                            { +                                json_decoder<Json> decoder; +                                basic_json_parser<char_type> parser; +                                parser.update(buffer.data(),buffer.size()); +                                parser.parse_some(decoder, ec); +                                if (ec) +                                { +                                    return path_expression_type(); +                                } +                                parser.finish_parse(decoder, ec); +                                if (ec) +                                { +                                    return path_expression_type(); +                                } +                                push_token(resources, token_type(literal_arg, decoder.get_result()), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                state_stack_.pop_back(); +                                break; +                            } +                        } +                        break; +                    } +                    case path_state::json_value: +                    { +                        json_decoder<Json> decoder; +                        basic_json_parser<char_type> parser; +                        parser.update(buffer.data(),buffer.size()); +                        parser.parse_some(decoder, ec); +                        if (ec) +                        { +                            return path_expression_type(); +                        } +                        parser.finish_parse(decoder, ec); +                        if (ec) +                        { +                            return path_expression_type(); +                        } +                        push_token(resources, token_type(literal_arg, decoder.get_result()), ec); +                        if (ec) {return path_expression_type();} +                        buffer.clear(); +                        state_stack_.pop_back(); +                        break; +                    } +                    case path_state::json_text_or_function_name: +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '{': +                            case '[': +                            { +                                json_decoder<Json> decoder; +                                basic_json_parser<char_type> parser; +                                parser.update(p_,end_input_ - p_); +                                parser.parse_some(decoder, ec); +                                if (ec) +                                { +                                    return path_expression_type(); +                                } +                                parser.finish_parse(decoder, ec); +                                if (ec) +                                { +                                    return path_expression_type(); +                                } +                                push_token(resources, token_type(literal_arg, decoder.get_result()), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                state_stack_.pop_back(); +                                p_ = parser.current(); +                                column_ = column_ + parser.column() - 1; +                                break; +                            } +                            case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': +                                state_stack_.back() = path_state::json_text_or_function; +                                state_stack_.emplace_back(path_state::number); +                                buffer.push_back(*p_); +                                ++p_; +                                ++column_; +                                break; +                            case '\"': +                                state_stack_.back() = path_state::json_text_or_function; +                                state_stack_.emplace_back(path_state::json_text_string); +                                buffer.push_back(*p_); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                state_stack_.back() = path_state::json_text_or_function; +                                state_stack_.emplace_back(path_state::unquoted_string); +                                buffer.push_back(*p_); +                                ++p_; +                                ++column_; +                                break; +                        }; +                        break; +                    case path_state::number:  +                        switch (*p_) +                        { +                            case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': +                            case 'e':case 'E':case '.': +                                buffer.push_back(*p_); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                state_stack_.pop_back(); // number +                                break; +                        }; +                        break; +                    case path_state::json_text_string:  +                        switch (*p_) +                        { +                            case '\\': +                                buffer.push_back(*p_); +                                ++p_; +                                ++column_; +                                if (p_ == end_input_) +                                { +                                    ec = jsonpath_errc::unexpected_eof; +                                    return path_expression_type(); +                                } +                                buffer.push_back(*p_); +                                ++p_; +                                ++column_; +                                break; +                            case '\"': +                                buffer.push_back(*p_); +                                state_stack_.pop_back();  +                                ++p_; +                                ++column_; +                                break; +                            default: +                                buffer.push_back(*p_); +                                ++p_; +                                ++column_; +                                break; +                        }; +                        break; +                    case path_state::relative_path:  +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '*': +                                push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back(); +                                ++p_; +                                ++column_; +                                break; +                            case '\'': +                                state_stack_.back() = path_state::identifier; +                                state_stack_.emplace_back(path_state::single_quoted_string); +                                ++p_; +                                ++column_; +                                break; +                            case '\"': +                                state_stack_.back() = path_state::identifier; +                                state_stack_.emplace_back(path_state::double_quoted_string); +                                ++p_; +                                ++column_; +                                break; +                            case '[': +                            case '.': +                                ec = jsonpath_errc::expected_relative_path; +                                return path_expression_type(); +                            default: +                                buffer.clear(); +                                state_stack_.back() = path_state::identifier_or_function_expr; +                                state_stack_.emplace_back(path_state::unquoted_string); +                                break; +                        } +                        break; +                    case path_state::identifier_or_function_expr: +                    { +                        switch(*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '(': +                            { +                                auto f = resources.get_function(buffer, ec); +                                if (ec) +                                { +                                    return path_expression_type(); +                                } +                                buffer.clear(); +                                push_token(resources, current_node_arg, ec); +                                push_token(resources, token_type(f), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::function_expression; +                                state_stack_.emplace_back(path_state::zero_or_one_arguments); +                                ++p_; +                                ++column_; +                                break; +                            } +                            default: +                            { +                                push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                state_stack_.pop_back();  +                                break; +                            } +                        } +                        break; +                    } +                    case path_state::expect_function_expr: +                    { +                        switch(*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '(': +                            { +                                auto f = resources.get_function(buffer, ec); +                                if (ec) +                                { +                                    return path_expression_type(); +                                } +                                buffer.clear(); +                                push_token(resources, current_node_arg, ec); +                                push_token(resources, token_type(f), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::function_expression; +                                state_stack_.emplace_back(path_state::zero_or_one_arguments); +                                ++p_; +                                ++column_; +                                break; +                            } +                            default: +                            { +                                ec = jsonpath_errc::expected_root_or_function; +                                return path_expression_type(); +                            } +                        } +                        break; +                    } +                    case path_state::function_expression: +                    { +                         +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ',': +                                push_token(resources, token_type(current_node_arg), ec); +                                if (ec) {return path_expression_type();} +                                push_token(resources, token_type(begin_expression_arg), ec); +                                if (ec) {return path_expression_type();} +                                if (ec) {return path_expression_type();} +                                state_stack_.emplace_back(path_state::argument); +                                state_stack_.emplace_back(path_state::expression_rhs); +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                ++p_; +                                ++column_; +                                break; +                            case ')': +                            { +                                push_token(resources, token_type(end_function_arg), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back();  +                                ++p_; +                                ++column_; +                                break; +                            } +                            default: +                                ec = jsonpath_errc::syntax_error; +                                return path_expression_type(); +                        } +                        break; +                    } +                    case path_state::zero_or_one_arguments: +                    { +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ')': +                                state_stack_.pop_back(); +                                break; +                            default: +                                push_token(resources, token_type(begin_expression_arg), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::one_or_more_arguments; +                                state_stack_.emplace_back(path_state::argument); +                                state_stack_.emplace_back(path_state::expression_rhs); +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                break; +                        } +                        break; +                    } +                    case path_state::one_or_more_arguments: +                    { +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ')': +                                state_stack_.pop_back(); +                                break; +                            case ',': +                                push_token(resources, token_type(begin_expression_arg), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.emplace_back(path_state::argument); +                                state_stack_.emplace_back(path_state::expression_rhs); +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                ++p_; +                                ++column_; +                                break; +                        } +                        break; +                    } +                    case path_state::argument: +                    { +                        switch(*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ',': +                            case ')': +                            { +                                push_token(resources, token_type(end_argument_expression_arg), ec); +                                push_token(resources, argument_arg, ec); +                                //push_token(resources, argument_arg, ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back(); +                                break; +                            } +                            default: +                                ec = jsonpath_errc::expected_comma_or_rparen; +                                return path_expression_type(); +                        } +                        break; +                    } +                    case path_state::unquoted_string:  +                        switch (*p_) +                        { +                            case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'g':case 'h':case 'i':case 'j':case 'k':case 'l':case 'm':case 'n':case 'o':case 'p':case 'q':case 'r':case 's':case 't':case 'u':case 'v':case 'w':case 'x':case 'y':case 'z': +                            case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':case 'G':case 'H':case 'I':case 'J':case 'K':case 'L':case 'M':case 'N':case 'O':case 'P':case 'Q':case 'R':case 'S':case 'T':case 'U':case 'V':case 'W':case 'X':case 'Y':case 'Z': +                            case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': +                            case '_': +                                buffer.push_back(*p_); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                if (typename std::make_unsigned<char_type>::type(*p_) > 127) +                                { +                                    buffer.push_back(*p_); +                                    ++p_; +                                    ++column_; +                                } +                                else +                                { +                                    state_stack_.pop_back(); // unquoted_string +                                } +                                break; +                        }; +                        break;                     +                    case path_state::relative_location:  +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '.': +                                state_stack_.emplace_back(path_state::recursive_descent_or_expression_lhs); +                                ++p_; +                                ++column_; +                                break; +                            case '[': +                                state_stack_.emplace_back(path_state::bracket_specifier_or_union); +                                ++p_; +                                ++column_; +                                break; +                            case '^': +                                ancestor_depth = 0; +                                state_stack_.emplace_back(path_state::parent_operator); +                                state_stack_.emplace_back(path_state::ancestor_depth); +                                break; +                            default: +                                state_stack_.pop_back(); +                                break; +                        }; +                        break; +                    case path_state::parent_operator:  +                    { +                        push_token(resources, token_type(resources.new_selector(parent_node_selector<Json,JsonReference>(ancestor_depth))), ec); +                        paths_required = true; +                        ancestor_depth = 0; +                        ++p_; +                        ++column_; +                        state_stack_.pop_back(); +                        break; +                    } +                    case path_state::ancestor_depth:  +                    { +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '^': +                            { +                                ++ancestor_depth; +                                ++p_; +                                ++column_; +                                break; +                            } +                            default: +                            { +                                state_stack_.pop_back(); +                                break; +                            } +                        } +                        break; +                    } +                    case path_state::expression_rhs:  +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '.': +                                state_stack_.emplace_back(path_state::recursive_descent_or_expression_lhs); +                                ++p_; +                                ++column_; +                                break; +                            case '[': +                                state_stack_.emplace_back(path_state::bracket_specifier_or_union); +                                ++p_; +                                ++column_; +                                break; +                            case ')': +                            { +                                state_stack_.pop_back(); +                                break; +                            } +                            case '|': +                                ++p_; +                                ++column_; +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                state_stack_.emplace_back(path_state::expect_or); +                                break; +                            case '&': +                                ++p_; +                                ++column_; +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                state_stack_.emplace_back(path_state::expect_and); +                                break; +                            case '<': +                            case '>': +                            { +                                state_stack_.emplace_back(path_state::comparator_expression); +                                break; +                            } +                            case '=': +                            { +                                state_stack_.emplace_back(path_state::eq_or_regex); +                                ++p_; +                                ++column_; +                                break; +                            } +                            case '!': +                            { +                                ++p_; +                                ++column_; +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                state_stack_.emplace_back(path_state::cmp_ne); +                                break; +                            } +                            case '+': +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                push_token(resources, token_type(resources.get_plus_operator()), ec); +                                if (ec) {return path_expression_type();} +                                ++p_; +                                ++column_; +                                break; +                            case '-': +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                push_token(resources, token_type(resources.get_minus_operator()), ec); +                                if (ec) {return path_expression_type();} +                                ++p_; +                                ++column_; +                                break; +                            case '*': +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                push_token(resources, token_type(resources.get_mult_operator()), ec); +                                if (ec) {return path_expression_type();} +                                ++p_; +                                ++column_; +                                break; +                            case '/': +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                push_token(resources, token_type(resources.get_div_operator()), ec); +                                if (ec) {return path_expression_type();} +                                ++p_; +                                ++column_; +                                break; +                            case '%': +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                push_token(resources, token_type(resources.get_modulus_operator()), ec); +                                if (ec) {return path_expression_type();} +                                ++p_; +                                ++column_; +                                break; +                            case ']': +                            case ',': +                                state_stack_.pop_back(); +                                break; +                            default: +                                ec = jsonpath_errc::expected_separator; +                                return path_expression_type(); +                        }; +                        break; +                    case path_state::expect_or: +                    { +                        switch (*p_) +                        { +                            case '|': +                                push_token(resources, token_type(resources.get_or_operator()), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back();  +                                ++p_; +                                ++column_; +                                break; +                            default: +                                ec = jsonpath_errc::expected_or; +                                return path_expression_type(); +                        } +                        break; +                    } +                    case path_state::expect_and: +                    { +                        switch(*p_) +                        { +                            case '&': +                                push_token(resources, token_type(resources.get_and_operator()), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back(); // expect_and +                                ++p_; +                                ++column_; +                                break; +                            default: +                                ec = jsonpath_errc::expected_and; +                                return path_expression_type(); +                        } +                        break; +                    } +                    case path_state::comparator_expression: +                        switch(*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '<': +                                ++p_; +                                ++column_; +                                state_stack_.back() = path_state::path_or_literal_or_function; +                                state_stack_.emplace_back(path_state::cmp_lt_or_lte); +                                break; +                            case '>': +                                ++p_; +                                ++column_; +                                state_stack_.back() = path_state::path_or_literal_or_function; +                                state_stack_.emplace_back(path_state::cmp_gt_or_gte); +                                break; +                            default: +                                if (state_stack_.size() > 1) +                                { +                                    state_stack_.pop_back(); +                                } +                                else +                                { +                                    ec = jsonpath_errc::syntax_error; +                                    return path_expression_type(); +                                } +                                break; +                        } +                        break; +                    case path_state::eq_or_regex: +                        switch(*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '=': +                            { +                                push_token(resources, token_type(resources.get_eq_operator()), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::path_or_literal_or_function; +                                ++p_; +                                ++column_; +                                break; +                            } +                            case '~': +                            { +                                ++p_; +                                ++column_; +                                state_stack_.emplace_back(path_state::expect_regex); +                                break; +                            } +                            default: +                                if (state_stack_.size() > 1) +                                { +                                    state_stack_.pop_back(); +                                } +                                else +                                { +                                    ec = jsonpath_errc::syntax_error; +                                    return path_expression_type(); +                                } +                                break; +                        } +                        break; +                    case path_state::expect_regex:  +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '/': +                                state_stack_.back() = path_state::regex; +                                state_stack_.emplace_back(path_state::regex_options); +                                state_stack_.emplace_back(path_state::regex_pattern); +                                ++p_; +                                ++column_; +                                break; +                            default:  +                                ec = jsonpath_errc::expected_forward_slash; +                                return path_expression_type(); +                        }; +                        break; +                    case path_state::regex:  +                    { +                        std::regex::flag_type options = std::regex_constants::ECMAScript;  +                        if (buffer2.find('i') != string_type::npos) +                        { +                            options |= std::regex_constants::icase; +                        } +                        std::basic_regex<char_type> pattern(buffer, options); +                        push_token(resources, resources.get_regex_operator(std::move(pattern)), ec); +                        if (ec) {return path_expression_type();} +                        buffer.clear(); +                        buffer2.clear(); +                        state_stack_.pop_back(); +                        break; +                    } +                    case path_state::regex_pattern:  +                    { +                        switch (*p_) +                        {                    +                            case '/': +                                { +                                    state_stack_.pop_back(); +                                    ++p_; +                                    ++column_; +                                } +                                break; + +                            default:  +                                buffer.push_back(*p_); +                                ++p_; +                                ++column_; +                                break; +                        } +                        break; +                    } +                    case path_state::regex_options:  +                    { +                        if (*p_ == 'i') +                        {            +                            buffer2.push_back(*p_); +                            ++p_; +                            ++column_; +                        } +                        else +                        { +                            state_stack_.pop_back(); +                        } +                        break; +                    } +                    case path_state::cmp_lt_or_lte: +                    { +                        switch(*p_) +                        { +                            case '=': +                                push_token(resources, token_type(resources.get_lte_operator()), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back(); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                push_token(resources, token_type(resources.get_lt_operator()), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back(); +                                break; +                        } +                        break; +                    } +                    case path_state::cmp_gt_or_gte: +                    { +                        switch(*p_) +                        { +                            case '=': +                                push_token(resources, token_type(resources.get_gte_operator()), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back();  +                                ++p_; +                                ++column_; +                                break; +                            default: +                                //std::cout << "Parse: gt_operator\n"; +                                push_token(resources, token_type(resources.get_gt_operator()), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back();  +                                break; +                        } +                        break; +                    } +                    case path_state::cmp_ne: +                    { +                        switch(*p_) +                        { +                            case '=': +                                push_token(resources, token_type(resources.get_ne_operator()), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back();  +                                ++p_; +                                ++column_; +                                break; +                            default: +                                ec = jsonpath_errc::expected_comparator; +                                return path_expression_type(); +                        } +                        break; +                    } +                    case path_state::identifier: +                        push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec); +                        if (ec) {return path_expression_type();} +                        buffer.clear(); +                        state_stack_.pop_back();  +                        break; +                    case path_state::single_quoted_string: +                        switch (*p_) +                        { +                            case '\'': +                                state_stack_.pop_back(); +                                ++p_; +                                ++column_; +                                break; +                            case '\\': +                                state_stack_.emplace_back(path_state::quoted_string_escape_char); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                buffer.push_back(*p_); +                                ++p_; +                                ++column_; +                                break; +                        }; +                        break; +                    case path_state::double_quoted_string:  +                        switch (*p_) +                        { +                            case '\"': +                                state_stack_.pop_back(); +                                ++p_; +                                ++column_; +                                break; +                            case '\\': +                                state_stack_.emplace_back(path_state::quoted_string_escape_char); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                buffer.push_back(*p_); +                                ++p_; +                                ++column_; +                                break; +                        }; +                        break; +                    case path_state::comma_or_rbracket: +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ',': +                                state_stack_.back() = path_state::bracket_specifier_or_union; +                                ++p_; +                                ++column_; +                                break; +                            case ']': +                                state_stack_.pop_back(); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                ec = jsonpath_errc::expected_comma_or_rbracket; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::expect_rbracket: +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ']': +                                state_stack_.pop_back(); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                ec = jsonpath_errc::expected_rbracket; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::expect_rparen: +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ')': +                                ++p_; +                                ++column_; +                                push_token(resources, rparen_arg, ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::expression_rhs; +                                break; +                            default: +                                ec = jsonpath_errc::expected_rparen; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::bracket_specifier_or_union: +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '(': +                            { +                                push_token(resources, token_type(begin_union_arg), ec); +                                push_token(resources, token_type(begin_expression_arg), ec); +                                push_token(resources, lparen_arg, ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::union_expression; // union +                                state_stack_.emplace_back(path_state::expression); +                                state_stack_.emplace_back(path_state::expect_rparen); +                                state_stack_.emplace_back(path_state::expression_rhs); +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                ++p_; +                                ++column_; +                                break; +                            } +                            case '?': +                            { +                                push_token(resources, token_type(begin_union_arg), ec); +                                push_token(resources, token_type(begin_filter_arg), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::union_expression; // union +                                state_stack_.emplace_back(path_state::filter_expression); +                                state_stack_.emplace_back(path_state::expression_rhs); +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                ++p_; +                                ++column_; +                                break; +                            } +                            case '*': +                                state_stack_.back() = path_state::wildcard_or_union; +                                ++p_; +                                ++column_; +                                break; +                            case '\'': +                                state_stack_.back() = path_state::identifier_or_union; +                                state_stack_.push_back(path_state::single_quoted_string); +                                ++p_; +                                ++column_; +                                break; +                            case '\"': +                                state_stack_.back() = path_state::identifier_or_union; +                                state_stack_.push_back(path_state::double_quoted_string); +                                ++p_; +                                ++column_; +                                break; +                            case ':': // slice_expression +                                state_stack_.back() = path_state::index_or_slice_or_union; +                                break; +                            case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': +                                state_stack_.back() = path_state::index_or_slice_or_union; +                                state_stack_.emplace_back(path_state::integer); +                                break; +                            case '$': +                                push_token(resources, token_type(begin_union_arg), ec); +                                push_token(resources, root_node_arg, ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::union_expression; // union +                                state_stack_.emplace_back(path_state::relative_location);                                 +                                ++p_; +                                ++column_; +                                break; +                            case '@': +                                push_token(resources, token_type(begin_union_arg), ec); +                                push_token(resources, token_type(current_node_arg), ec); // ISSUE +                                push_token(resources, token_type(resources.new_selector(current_node_selector<Json,JsonReference>())), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::union_expression; // union +                                state_stack_.emplace_back(path_state::relative_location); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                ec = jsonpath_errc::expected_bracket_specifier_or_union; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::union_element: +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ':': // slice_expression +                                state_stack_.back() = path_state::index_or_slice; +                                break; +                            case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': +                                state_stack_.back() = path_state::index_or_slice; +                                state_stack_.emplace_back(path_state::integer); +                                break; +                            case '(': +                            { +                                push_token(resources, token_type(begin_expression_arg), ec); +                                push_token(resources, lparen_arg, ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::expression; +                                state_stack_.emplace_back(path_state::expect_rparen); +                                state_stack_.emplace_back(path_state::expression_rhs); +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                ++p_; +                                ++column_; +                                break; +                            } +                            case '?': +                            { +                                push_token(resources, token_type(begin_filter_arg), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::filter_expression; +                                state_stack_.emplace_back(path_state::expression_rhs); +                                state_stack_.emplace_back(path_state::path_or_literal_or_function); +                                ++p_; +                                ++column_; +                                break; +                            } +                            case '*': +                                push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::relative_location; +                                ++p_; +                                ++column_; +                                break; +                            case '$': +                                push_token(resources, token_type(root_node_arg), ec); +                                push_token(resources, token_type(resources.new_selector(root_selector<Json,JsonReference>(selector_id++))), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::relative_location; +                                ++p_; +                                ++column_; +                                break; +                            case '@': +                                push_token(resources, token_type(current_node_arg), ec); // ISSUE +                                push_token(resources, token_type(resources.new_selector(current_node_selector<Json,JsonReference>())), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::relative_location; +                                ++p_; +                                ++column_; +                                break; +                            case '\'': +                                state_stack_.back() = path_state::identifier; +                                state_stack_.push_back(path_state::single_quoted_string); +                                ++p_; +                                ++column_; +                                break; +                            case '\"': +                                state_stack_.back() = path_state::identifier; +                                state_stack_.push_back(path_state::double_quoted_string); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                ec = jsonpath_errc::expected_bracket_specifier_or_union; +                                return path_expression_type(); +                        } +                        break; + +                    case path_state::integer: +                        switch(*p_) +                        { +                            case '-': +                                buffer.push_back(*p_); +                                state_stack_.back() = path_state::digit; +                                ++p_; +                                ++column_; +                                break; +                            default: +                                state_stack_.back() = path_state::digit; +                                break; +                        } +                        break; +                    case path_state::digit: +                        switch(*p_) +                        { +                            case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': +                                buffer.push_back(*p_); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                state_stack_.pop_back(); // digit +                                break; +                        } +                        break; +                    case path_state::index_or_slice_or_union: +                        switch(*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ']': +                            { +                                if (buffer.empty()) +                                { +                                    ec = jsonpath_errc::invalid_number; +                                    return path_expression_type(); +                                } +                                int64_t n{0}; +                                auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); +                                if (!r) +                                { +                                    ec = jsonpath_errc::invalid_number; +                                    return path_expression_type(); +                                } +                                push_token(resources, token_type(resources.new_selector(index_selector<Json,JsonReference>(n))), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                state_stack_.pop_back(); // index_or_slice_or_union +                                ++p_; +                                ++column_; +                                break; +                            } +                            case ',': +                            { +                                push_token(resources, token_type(begin_union_arg), ec); +                                if (ec) {return path_expression_type();} +                                if (buffer.empty()) +                                { +                                    ec = jsonpath_errc::invalid_number; +                                    return path_expression_type(); +                                } +                                else +                                { +                                    int64_t n{0}; +                                    auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); +                                    if (!r) +                                    { +                                        ec = jsonpath_errc::invalid_number; +                                        return path_expression_type(); +                                    } +                                    push_token(resources, token_type(resources.new_selector(index_selector<Json,JsonReference>(n))), ec); +                                    if (ec) {return path_expression_type();} + +                                    buffer.clear(); +                                } +                                push_token(resources, token_type(separator_arg), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                state_stack_.back() = path_state::union_expression; // union +                                state_stack_.emplace_back(path_state::union_element); +                                ++p_; +                                ++column_; +                                break; +                            } +                            case ':': +                            { +                                if (!buffer.empty()) +                                { +                                    int64_t n{0}; +                                    auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); +                                    if (!r) +                                    { +                                        ec = jsonpath_errc::invalid_number; +                                        return path_expression_type(); +                                    } +                                    slic.start_ = n; +                                    buffer.clear(); +                                } +                                push_token(resources, token_type(begin_union_arg), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::union_expression; // union +                                state_stack_.emplace_back(path_state::slice_expression_stop); +                                state_stack_.emplace_back(path_state::integer); +                                ++p_; +                                ++column_; +                                break; +                            } +                            default: +                                ec = jsonpath_errc::expected_rbracket; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::slice_expression_stop: +                    { +                        if (!buffer.empty()) +                        { +                            int64_t n{0}; +                            auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); +                            if (!r) +                            { +                                ec = jsonpath_errc::invalid_number; +                                return path_expression_type(); +                            } +                            slic.stop_ = jsoncons::optional<int64_t>(n); +                            buffer.clear(); +                        } +                        switch(*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ']': +                            case ',': +                                push_token(resources, token_type(resources.new_selector(slice_selector<Json,JsonReference>(slic))), ec); +                                if (ec) {return path_expression_type();} +                                slic = slice{}; +                                state_stack_.pop_back(); // bracket_specifier2 +                                break; +                            case ':': +                                state_stack_.back() = path_state::slice_expression_step; +                                state_stack_.emplace_back(path_state::integer); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                ec = jsonpath_errc::expected_rbracket; +                                return path_expression_type(); +                        } +                        break; +                    } +                    case path_state::slice_expression_step: +                    { +                        if (!buffer.empty()) +                        { +                            int64_t n{0}; +                            auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); +                            if (!r) +                            { +                                ec = jsonpath_errc::invalid_number; +                                return path_expression_type(); +                            } +                            if (n == 0) +                            { +                                ec = jsonpath_errc::step_cannot_be_zero; +                                return path_expression_type(); +                            } +                            slic.step_ = n; +                            buffer.clear(); +                        } +                        switch(*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ']': +                            case ',': +                                push_token(resources, token_type(resources.new_selector(slice_selector<Json,JsonReference>(slic))), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                slic = slice{}; +                                state_stack_.pop_back(); // slice_expression_step +                                break; +                            default: +                                ec = jsonpath_errc::expected_rbracket; +                                return path_expression_type(); +                        } +                        break; +                    } + +                    case path_state::bracketed_unquoted_name_or_union: +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ']':  +                                push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                state_stack_.pop_back(); +                                ++p_; +                                ++column_; +                                break; +                            case '.': +                                push_token(resources, token_type(begin_union_arg), ec); +                                push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                state_stack_.back() = path_state::union_expression; // union +                                state_stack_.emplace_back(path_state::relative_path);                                 +                                ++p_; +                                ++column_; +                                break; +                            case '[': +                                push_token(resources, token_type(begin_union_arg), ec); +                                push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.back() = path_state::union_expression; // union +                                state_stack_.emplace_back(path_state::relative_path);                                 +                                ++p_; +                                ++column_; +                                break; +                            case ',':  +                                push_token(resources, token_type(begin_union_arg), ec); +                                push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec); +                                push_token(resources, token_type(separator_arg), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                state_stack_.back() = path_state::union_expression; // union +                                state_stack_.emplace_back(path_state::relative_path);                                 +                                ++p_; +                                ++column_; +                                break; +                            default: +                                buffer.push_back(*p_); +                                ++p_; +                                ++column_; +                                break; +                        } +                        break; +                    case path_state::union_expression: +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '.': +                                state_stack_.emplace_back(path_state::relative_path); +                                ++p_; +                                ++column_; +                                break; +                            case '[': +                                state_stack_.emplace_back(path_state::bracket_specifier_or_union); +                                ++p_; +                                ++column_; +                                break; +                            case ',':  +                                push_token(resources, token_type(separator_arg), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.emplace_back(path_state::union_element); +                                ++p_; +                                ++column_; +                                break; +                            case ']':  +                                push_token(resources, token_type(end_union_arg), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back(); +                                ++p_; +                                ++column_; +                                break; +                            default: +                                ec = jsonpath_errc::expected_rbracket; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::identifier_or_union: +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ']':  +                                push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                state_stack_.pop_back(); +                                ++p_; +                                ++column_; +                                break; +                            case ',':  +                                push_token(resources, token_type(begin_union_arg), ec); +                                push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec); +                                push_token(resources, token_type(separator_arg), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                state_stack_.back() = path_state::union_expression; // union +                                state_stack_.emplace_back(path_state::union_element);                                 +                                ++p_; +                                ++column_; +                                break; +                            default: +                                ec = jsonpath_errc::expected_rbracket; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::bracketed_wildcard: +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case '[': +                            case ']': +                            case ',': +                            case '.': +                                push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                state_stack_.pop_back(); +                                break; +                            default: +                                ec = jsonpath_errc::expected_rbracket; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::index_or_slice: +                        switch(*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ',': +                            case ']': +                            { +                                if (buffer.empty()) +                                { +                                    ec = jsonpath_errc::invalid_number; +                                    return path_expression_type(); +                                } +                                else +                                { +                                    int64_t n{0}; +                                    auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); +                                    if (!r) +                                    { +                                        ec = jsonpath_errc::invalid_number; +                                        return path_expression_type(); +                                    } +                                    push_token(resources, token_type(resources.new_selector(index_selector<Json,JsonReference>(n))), ec); +                                    if (ec) {return path_expression_type();} + +                                    buffer.clear(); +                                } +                                state_stack_.pop_back(); // bracket_specifier +                                break; +                            } +                            case ':': +                            { +                                if (!buffer.empty()) +                                { +                                    int64_t n{0}; +                                    auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); +                                    if (!r) +                                    { +                                        ec = jsonpath_errc::invalid_number; +                                        return path_expression_type(); +                                    } +                                    slic.start_ = n; +                                    buffer.clear(); +                                } +                                state_stack_.back() = path_state::slice_expression_stop; +                                state_stack_.emplace_back(path_state::integer); +                                ++p_; +                                ++column_; +                                break; +                            } +                            default: +                                ec = jsonpath_errc::expected_rbracket; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::wildcard_or_union: +                        switch (*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ']':  +                                push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                state_stack_.pop_back(); +                                ++p_; +                                ++column_; +                                break; +                            case ',':  +                                push_token(resources, token_type(begin_union_arg), ec); +                                push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec); +                                push_token(resources, token_type(separator_arg), ec); +                                if (ec) {return path_expression_type();} +                                buffer.clear(); +                                state_stack_.back() = path_state::union_expression; // union +                                state_stack_.emplace_back(path_state::union_element);                                 +                                ++p_; +                                ++column_; +                                break; +                            default: +                                ec = jsonpath_errc::expected_rbracket; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::quoted_string_escape_char: +                        switch (*p_) +                        { +                            case '\"': +                                buffer.push_back('\"'); +                                ++p_; +                                ++column_; +                                state_stack_.pop_back(); +                                break; +                            case '\'': +                                buffer.push_back('\''); +                                ++p_; +                                ++column_; +                                state_stack_.pop_back(); +                                break; +                            case '\\':  +                                buffer.push_back('\\'); +                                ++p_; +                                ++column_; +                                state_stack_.pop_back(); +                                break; +                            case '/': +                                buffer.push_back('/'); +                                ++p_; +                                ++column_; +                                state_stack_.pop_back(); +                                break; +                            case 'b': +                                buffer.push_back('\b'); +                                ++p_; +                                ++column_; +                                state_stack_.pop_back(); +                                break; +                            case 'f': +                                buffer.push_back('\f'); +                                ++p_; +                                ++column_; +                                state_stack_.pop_back(); +                                break; +                            case 'n': +                                buffer.push_back('\n'); +                                ++p_; +                                ++column_; +                                state_stack_.pop_back(); +                                break; +                            case 'r': +                                buffer.push_back('\r'); +                                ++p_; +                                ++column_; +                                state_stack_.pop_back(); +                                break; +                            case 't': +                                buffer.push_back('\t'); +                                ++p_; +                                ++column_; +                                state_stack_.pop_back(); +                                break; +                            case 'u': +                                ++p_; +                                ++column_; +                                state_stack_.back() = path_state::escape_u1; +                                break; +                            default: +                                ec = jsonpath_errc::illegal_escaped_character; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::escape_u1: +                        cp = append_to_codepoint(0, *p_, ec); +                        if (ec) +                        { +                            return path_expression_type(); +                        } +                        ++p_; +                        ++column_; +                        state_stack_.back() = path_state::escape_u2; +                        break; +                    case path_state::escape_u2: +                        cp = append_to_codepoint(cp, *p_, ec); +                        if (ec) +                        { +                            return path_expression_type(); +                        } +                        ++p_; +                        ++column_; +                        state_stack_.back() = path_state::escape_u3; +                        break; +                    case path_state::escape_u3: +                        cp = append_to_codepoint(cp, *p_, ec); +                        if (ec) +                        { +                            return path_expression_type(); +                        } +                        ++p_; +                        ++column_; +                        state_stack_.back() = path_state::escape_u4; +                        break; +                    case path_state::escape_u4: +                        cp = append_to_codepoint(cp, *p_, ec); +                        if (ec) +                        { +                            return path_expression_type(); +                        } +                        if (unicode_traits::is_high_surrogate(cp)) +                        { +                            ++p_; +                            ++column_; +                            state_stack_.back() = path_state::escape_expect_surrogate_pair1; +                        } +                        else +                        { +                            unicode_traits::convert(&cp, 1, buffer); +                            ++p_; +                            ++column_; +                            state_stack_.pop_back(); +                        } +                        break; +                    case path_state::escape_expect_surrogate_pair1: +                        switch (*p_) +                        { +                            case '\\':  +                                ++p_; +                                ++column_; +                                state_stack_.back() = path_state::escape_expect_surrogate_pair2; +                                break; +                            default: +                                ec = jsonpath_errc::invalid_codepoint; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::escape_expect_surrogate_pair2: +                        switch (*p_) +                        { +                            case 'u':  +                                ++p_; +                                ++column_; +                                state_stack_.back() = path_state::escape_u5; +                                break; +                            default: +                                ec = jsonpath_errc::invalid_codepoint; +                                return path_expression_type(); +                        } +                        break; +                    case path_state::escape_u5: +                        cp2 = append_to_codepoint(0, *p_, ec); +                        if (ec) +                        { +                            return path_expression_type(); +                        } +                        ++p_; +                        ++column_; +                        state_stack_.back() = path_state::escape_u6; +                        break; +                    case path_state::escape_u6: +                        cp2 = append_to_codepoint(cp2, *p_, ec); +                        if (ec) +                        { +                            return path_expression_type(); +                        } +                        ++p_; +                        ++column_; +                        state_stack_.back() = path_state::escape_u7; +                        break; +                    case path_state::escape_u7: +                        cp2 = append_to_codepoint(cp2, *p_, ec); +                        if (ec) +                        { +                            return path_expression_type(); +                        } +                        ++p_; +                        ++column_; +                        state_stack_.back() = path_state::escape_u8; +                        break; +                    case path_state::escape_u8: +                    { +                        cp2 = append_to_codepoint(cp2, *p_, ec); +                        if (ec) +                        { +                            return path_expression_type(); +                        } +                        uint32_t codepoint = 0x10000 + ((cp & 0x3FF) << 10) + (cp2 & 0x3FF); +                        unicode_traits::convert(&codepoint, 1, buffer); +                        state_stack_.pop_back(); +                        ++p_; +                        ++column_; +                        break; +                    } +                    case path_state::filter_expression: +                    { +                        switch(*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ',': +                            case ']': +                            { +                                push_token(resources, token_type(end_filter_arg), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back(); +                                break; +                            } +                            default: +                                ec = jsonpath_errc::expected_comma_or_rbracket; +                                return path_expression_type(); +                        } +                        break; +                    } +                    case path_state::expression: +                    { +                        switch(*p_) +                        { +                            case ' ':case '\t':case '\r':case '\n': +                                advance_past_space_character(); +                                break; +                            case ',': +                            case ']': +                            { +                                push_token(resources, token_type(end_index_expression_arg), ec); +                                if (ec) {return path_expression_type();} +                                state_stack_.pop_back(); +                                break; +                            } +                            default: +                                ec = jsonpath_errc::expected_comma_or_rbracket; +                                return path_expression_type(); +                        } +                        break; +                    } +                    default: +                        ++p_; +                        ++column_; +                        break; +                } +            } + +            if (state_stack_.empty()) +            { +                ec = jsonpath_errc::syntax_error; +                return path_expression_type(); +            } + +            while (state_stack_.size() > 1) +            { +                switch (state_stack_.back()) +                { +                    case path_state::name_or_lbracket:  +                        state_stack_.back() = path_state::relative_path; +                        break; +                    case path_state::relative_path:  +                        state_stack_.back() = path_state::identifier_or_function_expr; +                        state_stack_.emplace_back(path_state::unquoted_string); +                        break; +                    case path_state::identifier_or_function_expr: +                        if (!buffer.empty()) // Can't be quoted string +                        { +                            push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec); +                            if (ec) {return path_expression_type();} +                        } +                        state_stack_.pop_back();  +                        break; +                    case path_state::unquoted_string:  +                        state_stack_.pop_back(); // unquoted_string +                        break;                     +                    case path_state::relative_location:  +                        state_stack_.pop_back(); +                        break; +                    case path_state::identifier: +                        if (!buffer.empty()) // Can't be quoted string +                        { +                            push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec); +                            if (ec) {return path_expression_type();} +                        } +                        state_stack_.pop_back();  +                        break; +                    case path_state::parent_operator:  +                    { +                        push_token(resources, token_type(resources.new_selector(parent_node_selector<Json,JsonReference>(ancestor_depth))), ec); +                        if (ec) { return path_expression_type(); } +                        paths_required = true; +                        state_stack_.pop_back();  +                        break; +                    } +                    case path_state::ancestor_depth:  +                        state_stack_.pop_back();  +                        break; +                    default: +                        ec = jsonpath_errc::syntax_error; +                        return path_expression_type(); +                } +            } + +            if (state_stack_.size() > 2) +            { +                ec = jsonpath_errc::unexpected_eof; +                return path_expression_type(); +            } + +            //std::cout << "\nTokens\n\n"; +            //for (const auto& tok : output_stack_) +            //{ +            //    std::cout << tok.to_string() << "\n"; +            //} +            //std::cout << "\n"; + +            if (output_stack_.empty() || !operator_stack_.empty()) +            { +                ec = jsonpath_errc::unexpected_eof; +                return path_expression_type(); +            } + +            return path_expression_type(output_stack_.back().selector_, paths_required); +        } + +        void advance_past_space_character() +        { +            switch (*p_) +            { +                case ' ':case '\t': +                    ++p_; +                    ++column_; +                    break; +                case '\r': +                    if (p_+1 < end_input_ && *(p_+1) == '\n') +                        ++p_; +                    ++line_; +                    column_ = 1; +                    ++p_; +                    break; +                case '\n': +                    ++line_; +                    column_ = 1; +                    ++p_; +                    break; +                default: +                    break; +            } +        } + +        void unwind_rparen(std::error_code& ec) +        { +            auto it = operator_stack_.rbegin(); +            while (it != operator_stack_.rend() && !it->is_lparen()) +            { +                output_stack_.emplace_back(std::move(*it)); +                ++it; +            } +            if (it == operator_stack_.rend()) +            { +                ec = jsonpath_errc::unbalanced_parentheses; +                return; +            } +            ++it; +            operator_stack_.erase(it.base(),operator_stack_.end()); +        } + +        void push_token(jsoncons::jsonpath::detail::static_resources<value_type,reference>& resources, token_type&& tok, std::error_code& ec) +        { +            //std::cout << tok.to_string() << "\n"; +            switch (tok.token_kind()) +            { +                case jsonpath_token_kind::begin_filter: +                    output_stack_.emplace_back(std::move(tok)); +                    operator_stack_.emplace_back(token_type(lparen_arg)); +                    break; +                case jsonpath_token_kind::end_filter: +                { +                    //std::cout << "push_token end_filter 1\n"; +                    //for (const auto& tok2 : output_stack_) +                    //{ +                    //    std::cout << tok2.to_string() << "\n"; +                    //} +                    //std::cout << "\n\n"; +                    unwind_rparen(ec); +                    if (ec) +                    { +                        return; +                    } +                    std::vector<token_type> toks; +                    auto it = output_stack_.rbegin(); +                    while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_filter) +                    { +                        toks.emplace_back(std::move(*it)); +                        ++it; +                    } +                    if (it == output_stack_.rend()) +                    { +                        ec = jsonpath_errc::unbalanced_parentheses; +                        return; +                    } +                    std::reverse(toks.begin(), toks.end()); +                    ++it; +                    output_stack_.erase(it.base(),output_stack_.end()); + +                    if (!output_stack_.empty() && output_stack_.back().is_path()) +                    { +                        output_stack_.back().selector_->append_selector(resources.new_selector(filter_selector<Json,JsonReference>(expression_type(std::move(toks))))); +                    } +                    else +                    { +                        output_stack_.emplace_back(token_type(resources.new_selector(filter_selector<Json,JsonReference>(expression_type(std::move(toks)))))); +                    } +                    //std::cout << "push_token end_filter 2\n"; +                    //for (const auto& tok2 : output_stack_) +                    //{ +                    //    std::cout << tok2.to_string() << "\n"; +                    //} +                    //std::cout << "\n\n"; +                    break; +                } +                case jsonpath_token_kind::begin_expression: +                    //std::cout << "begin_expression\n"; +                    output_stack_.emplace_back(std::move(tok)); +                    operator_stack_.emplace_back(token_type(lparen_arg)); +                    break; +                case jsonpath_token_kind::end_index_expression: +                { +                    //std::cout << "jsonpath_token_kind::end_index_expression\n"; +                    //for (const auto& t : output_stack_) +                    //{ +                    //    std::cout << t.to_string() << "\n"; +                    //} +                    //std::cout << "/jsonpath_token_kind::end_index_expression\n"; +                    unwind_rparen(ec); +                    if (ec) +                    { +                        return; +                    } +                    std::vector<token_type> toks; +                    auto it = output_stack_.rbegin(); +                    while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_expression) +                    { +                        toks.emplace_back(std::move(*it)); +                        ++it; +                    } +                    if (it == output_stack_.rend()) +                    { +                        ec = jsonpath_errc::unbalanced_parentheses; +                        return; +                    } +                    std::reverse(toks.begin(), toks.end()); +                    ++it; +                    output_stack_.erase(it.base(),output_stack_.end()); + +                    if (!output_stack_.empty() && output_stack_.back().is_path()) +                    { +                        output_stack_.back().selector_->append_selector(resources.new_selector(index_expression_selector<Json,JsonReference>(expression_type(std::move(toks))))); +                    } +                    else +                    { +                        output_stack_.emplace_back(token_type(resources.new_selector(index_expression_selector<Json,JsonReference>(expression_type(std::move(toks)))))); +                    } +                    break; +                } +                case jsonpath_token_kind::end_argument_expression: +                { +                    //std::cout << "jsonpath_token_kind::end_index_expression\n"; +                    //for (const auto& t : output_stack_) +                    //{ +                    //    std::cout << t.to_string() << "\n"; +                    //} +                    //std::cout << "/jsonpath_token_kind::end_index_expression\n"; +                    unwind_rparen(ec); +                    if (ec) +                    { +                        return; +                    } +                    std::vector<token_type> toks; +                    auto it = output_stack_.rbegin(); +                    while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_expression) +                    { +                        toks.emplace_back(std::move(*it)); +                        ++it; +                    } +                    if (it == output_stack_.rend()) +                    { +                        ec = jsonpath_errc::unbalanced_parentheses; +                        return; +                    } +                    std::reverse(toks.begin(), toks.end()); +                    ++it; +                    output_stack_.erase(it.base(),output_stack_.end()); +                    output_stack_.emplace_back(token_type(jsoncons::make_unique<expression_type>(std::move(toks)))); +                    break; +                } +                case jsonpath_token_kind::selector: +                { +                    if (!output_stack_.empty() && output_stack_.back().is_path()) +                    { +                        output_stack_.back().selector_->append_selector(std::move(tok.selector_)); +                    } +                    else +                    { +                        output_stack_.emplace_back(std::move(tok)); +                    } +                    break; +                } +                case jsonpath_token_kind::separator: +                    output_stack_.emplace_back(std::move(tok)); +                    break; +                case jsonpath_token_kind::begin_union: +                    output_stack_.emplace_back(std::move(tok)); +                    break; +                case jsonpath_token_kind::end_union: +                { +                    std::vector<selector_type*> expressions; +                    auto it = output_stack_.rbegin(); +                    while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_union) +                    { +                        if (it->token_kind() == jsonpath_token_kind::selector) +                        { +                            expressions.emplace_back(std::move(it->selector_)); +                        } +                        do +                        { +                            ++it; +                        }  +                        while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_union && it->token_kind() != jsonpath_token_kind::separator); +                        if (it->token_kind() == jsonpath_token_kind::separator) +                        { +                            ++it; +                        } +                    } +                    if (it == output_stack_.rend()) +                    { +                        ec = jsonpath_errc::unbalanced_parentheses; +                        return; +                    } +                    std::reverse(expressions.begin(), expressions.end()); +                    ++it; +                    output_stack_.erase(it.base(),output_stack_.end()); + +                    if (!output_stack_.empty() && output_stack_.back().is_path()) +                    { +                        output_stack_.back().selector_->append_selector(resources.new_selector(union_selector<Json,JsonReference>(std::move(expressions)))); +                    } +                    else +                    { +                        output_stack_.emplace_back(token_type(resources.new_selector(union_selector<Json,JsonReference>(std::move(expressions))))); +                    } +                    break; +                } +                case jsonpath_token_kind::lparen: +                    operator_stack_.emplace_back(std::move(tok)); +                    break; +                case jsonpath_token_kind::rparen: +                { +                    unwind_rparen(ec); +                    break; +                } +                case jsonpath_token_kind::end_function: +                { +                    //std::cout << "jsonpath_token_kind::end_function\n"; +                    unwind_rparen(ec); +                    if (ec) +                    { +                        return; +                    } +                    std::vector<token_type> toks; +                    auto it = output_stack_.rbegin(); +                    std::size_t arg_count = 0; +                    while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::function) +                    { +                        if (it->token_kind() == jsonpath_token_kind::argument) +                        { +                            ++arg_count; +                        } +                        toks.emplace_back(std::move(*it)); +                        ++it; +                    } +                    if (it == output_stack_.rend()) +                    { +                        ec = jsonpath_errc::unbalanced_parentheses; +                        return; +                    } +                    std::reverse(toks.begin(), toks.end()); +                    if (it->arity() && arg_count != *(it->arity())) +                    { +                        ec = jsonpath_errc::invalid_arity; +                        return; +                    } +                    toks.push_back(std::move(*it)); +                    ++it; +                    output_stack_.erase(it.base(),output_stack_.end()); + +                    if (!output_stack_.empty() && output_stack_.back().is_path()) +                    { +                        output_stack_.back().selector_->append_selector(resources.new_selector(function_selector<Json,JsonReference>(expression_type(std::move(toks))))); +                    } +                    else +                    { +                        output_stack_.emplace_back(token_type(resources.new_selector(function_selector<Json,JsonReference>(std::move(toks))))); +                    } +                    break; +                } +                case jsonpath_token_kind::literal: +                    if (!output_stack_.empty() && (output_stack_.back().token_kind() == jsonpath_token_kind::current_node || output_stack_.back().token_kind() == jsonpath_token_kind::root_node)) +                    { +                        output_stack_.back() = std::move(tok); +                    } +                    else +                    { +                        output_stack_.emplace_back(std::move(tok)); +                    } +                    break; +                case jsonpath_token_kind::function: +                    output_stack_.emplace_back(std::move(tok)); +                    operator_stack_.emplace_back(token_type(lparen_arg)); +                    break; +                case jsonpath_token_kind::argument: +                    output_stack_.emplace_back(std::move(tok)); +                    break; +                case jsonpath_token_kind::root_node: +                case jsonpath_token_kind::current_node: +                    output_stack_.emplace_back(std::move(tok)); +                    break; +                case jsonpath_token_kind::unary_operator: +                case jsonpath_token_kind::binary_operator: +                { +                    if (operator_stack_.empty() || operator_stack_.back().is_lparen()) +                    { +                        operator_stack_.emplace_back(std::move(tok)); +                    } +                    else if (tok.precedence_level() < operator_stack_.back().precedence_level() +                             || (tok.precedence_level() == operator_stack_.back().precedence_level() && tok.is_right_associative())) +                    { +                        operator_stack_.emplace_back(std::move(tok)); +                    } +                    else +                    { +                        auto it = operator_stack_.rbegin(); +                        while (it != operator_stack_.rend() && it->is_operator() +                               && (tok.precedence_level() > it->precedence_level() +                             || (tok.precedence_level() == it->precedence_level() && tok.is_right_associative()))) +                        { +                            output_stack_.emplace_back(std::move(*it)); +                            ++it; +                        } + +                        operator_stack_.erase(it.base(),operator_stack_.end()); +                        operator_stack_.emplace_back(std::move(tok)); +                    } +                    break; +                } +                default: +                    break; +            } +            //std::cout << "  " << "Output Stack\n"; +            //for (auto&& t : output_stack_) +            //{ +            //    std::cout << t.to_string(2) << "\n"; +            //} +            //if (!operator_stack_.empty()) +            //{ +            //    std::cout << "  " << "Operator Stack\n"; +            //    for (auto&& t : operator_stack_) +            //    { +            //        std::cout << t.to_string(2) << "\n"; +            //    } +            //} +        } + +        uint32_t append_to_codepoint(uint32_t cp, int c, std::error_code& ec) +        { +            cp *= 16; +            if (c >= '0'  &&  c <= '9') +            { +                cp += c - '0'; +            } +            else if (c >= 'a'  &&  c <= 'f') +            { +                cp += c - 'a' + 10; +            } +            else if (c >= 'A'  &&  c <= 'F') +            { +                cp += c - 'A' + 10; +            } +            else +            { +                ec = jsonpath_errc::invalid_codepoint; +            } +            return cp; +        } +    }; + +    } // namespace detail + +    template <class Json,class JsonReference = const Json&> +    class jsonpath_expression +    { +    public: +        using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator<Json, JsonReference>; +        using char_type = typename evaluator_t::char_type; +        using string_type = typename evaluator_t::string_type; +        using string_view_type = typename evaluator_t::string_view_type; +        using value_type = typename evaluator_t::value_type; +        using reference = typename evaluator_t::reference; +        using parameter_type = parameter<Json>; +        using json_selector_t = typename evaluator_t::path_expression_type; +        using path_value_pair_type = typename evaluator_t::path_value_pair_type; +        using json_location_type = typename evaluator_t::json_location_type; +        using function_type = std::function<value_type(jsoncons::span<const parameter_type>, std::error_code& ec)>; +    private: +        jsoncons::jsonpath::detail::static_resources<value_type,reference> static_resources_; +        json_selector_t expr_; +    public: +        jsonpath_expression(jsoncons::jsonpath::detail::static_resources<value_type,reference>&& resources, +                            json_selector_t&& expr) +            : static_resources_(std::move(resources)),  +              expr_(std::move(expr)) +        { +        } + +        jsonpath_expression(jsoncons::jsonpath::detail::static_resources<value_type,reference>&& resources, +                            json_selector_t&& expr, std::vector<function_type>&& custom_functions) +            : static_resources_(std::move(resources)),  +              expr_(std::move(expr), std::move(custom_functions)) +        { +        } + +        template <class BinaryCallback> +        typename std::enable_if<type_traits::is_binary_function_object<BinaryCallback,const string_type&,reference>::value,void>::type +        evaluate(reference instance, BinaryCallback callback, result_options options = result_options()) +        { +            jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources; +            auto f = [&callback](const json_location_type& path, reference val) +            { +                callback(path.to_string(), val); +            }; +            expr_.evaluate(resources, instance, resources.root_path_node(), instance, f, options); +        } + +        Json evaluate(reference instance, result_options options = result_options()) +        { +            if ((options & result_options::path) == result_options::path) +            { +                jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources; + +                Json result(json_array_arg); +                auto callback = [&result](const json_location_type& p, reference) +                { +                    result.emplace_back(p.to_string()); +                }; +                expr_.evaluate(resources, instance, resources.root_path_node(), instance, callback, options); +                return result; +            } +            else +            { +                jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources; +                return expr_.evaluate(resources, instance, resources.current_path_node(), instance, options); +            } +        } + +        static jsonpath_expression compile(const string_view_type& path) +        { +            jsoncons::jsonpath::detail::static_resources<value_type,reference> resources; + +            evaluator_t e; +            json_selector_t expr = e.compile(resources, path); +            return jsonpath_expression(std::move(resources), std::move(expr)); +        } + +        static jsonpath_expression compile(const string_view_type& path, std::error_code& ec) +        { +            jsoncons::jsonpath::detail::static_resources<value_type,reference> resources; +            evaluator_t e; +            json_selector_t expr = e.compile(resources, path, ec); +            return jsonpath_expression(std::move(resources), std::move(expr)); +        } + +        static jsonpath_expression compile(const string_view_type& path,  +                                           const custom_functions<Json>& functions) +        { +            jsoncons::jsonpath::detail::static_resources<value_type,reference> resources(functions); + +            evaluator_t e; +            json_selector_t expr = e.compile(resources, path); +            return jsonpath_expression(std::move(resources), std::move(expr)); +        } + +        static jsonpath_expression compile(const string_view_type& path,  +                                           const custom_functions<Json>& functions,  +                                           std::error_code& ec) +        { +            jsoncons::jsonpath::detail::static_resources<value_type,reference> resources(functions); +            evaluator_t e; +            json_selector_t expr = e.compile(resources, path, ec); +            return jsonpath_expression(std::move(resources), std::move(expr)); +        } +    }; + +    template <class Json> +    jsonpath_expression<Json> make_expression(const typename Json::string_view_type& expr,  +                                              const custom_functions<Json>& functions = custom_functions<Json>()) +    { +        return jsonpath_expression<Json>::compile(expr, functions); +    } + +    template <class Json> +    jsonpath_expression<Json> make_expression(const typename Json::string_view_type& expr, std::error_code& ec) +    { +        return jsonpath_expression<Json>::compile(expr, ec); +    } + +    template <class Json> +    jsonpath_expression<Json> make_expression(const typename Json::string_view_type& expr,  +                                              const custom_functions<Json>& functions,  +                                              std::error_code& ec) +    { +        return jsonpath_expression<Json>::compile(expr, functions, ec); +    } + +} // namespace jsonpath +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/jsonpath/jsonpath_selector.hpp b/include/jsoncons_ext/jsonpath/jsonpath_selector.hpp new file mode 100644 index 0000000..e06d37c --- /dev/null +++ b/include/jsoncons_ext/jsonpath/jsonpath_selector.hpp @@ -0,0 +1,1322 @@ +// 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 <string> +#include <vector> +#include <memory> +#include <type_traits> // std::is_const +#include <limits> // std::numeric_limits +#include <utility> // std::move +#include <regex> +#include <jsoncons/json.hpp> +#include <jsoncons_ext/jsonpath/jsonpath_error.hpp> +#include <jsoncons_ext/jsonpath/expression.hpp> + +namespace jsoncons {  +namespace jsonpath { +namespace detail { + +    struct slice +    { +        jsoncons::optional<int64_t> start_; +        jsoncons::optional<int64_t> stop_; +        int64_t step_; + +        slice() +            : start_(), stop_(), step_(1) +        { +        } + +        slice(const jsoncons::optional<int64_t>& start, const jsoncons::optional<int64_t>& 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<int64_t>(size) + *start_); +                return len <= static_cast<int64_t>(size) ? len : static_cast<int64_t>(size); +            } +            else +            { +                if (step_ >= 0) +                { +                    return 0; +                } +                else  +                { +                    return static_cast<int64_t>(size); +                } +            } +        } + +        int64_t get_stop(std::size_t size) const +        { +            if (stop_) +            { +                auto len = *stop_ >= 0 ? *stop_ : (static_cast<int64_t>(size) + *stop_); +                return len <= static_cast<int64_t>(size) ? len : static_cast<int64_t>(size); +            } +            else +            { +                return step_ >= 0 ? static_cast<int64_t>(size) : -1; +            } +        } + +        int64_t step() const +        { +            return step_; // Allow negative +        } +    }; + +    template <class Json,class JsonReference> +    class json_array_receiver : public node_receiver<Json,JsonReference> +    { +    public: +        using reference = JsonReference; +        using char_type = typename Json::char_type; +        using json_location_node_type = json_location_node<char_type>; + +        Json* val; + +        json_array_receiver(Json* ptr) +            : val(ptr) +        { +        } + +        void add(const json_location_node_type&, reference value) override +        { +            val->emplace_back(value); +        } +    }; + +    template <class Json,class JsonReference> +    struct path_generator +    { +        using char_type = typename Json::char_type; +        using json_location_node_type = json_location_node<char_type>; +        using string_type = std::basic_string<char_type>; + +        static const json_location_node_type& generate(dynamic_resources<Json,JsonReference>& 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<Json,JsonReference>& 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 Json,class JsonReference> +    class base_selector : public jsonpath_selector<Json,JsonReference> +    { +        using supertype = jsonpath_selector<Json,JsonReference>; + +        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<Json,JsonReference>& 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<Json,JsonReference>& 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 Json,class JsonReference> +    class identifier_selector final : public base_selector<Json,JsonReference> +    { +        using supertype = base_selector<Json,JsonReference>; +        using path_generator_type = path_generator<Json,JsonReference>; +    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<char_type>; +        using string_view_type = basic_string_view<char_type>; +        using node_receiver_type = typename supertype::node_receiver_type; +    private: +        string_type identifier_; +    public: + +        identifier_selector(const string_view_type& identifier) +            : base_selector<Json,JsonReference>(), identifier_(identifier) +        { +        } + +        void select(dynamic_resources<Json,JsonReference>& 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<std::size_t>(n) : static_cast<std::size_t>(static_cast<int64_t>(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<Json,JsonReference>& 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<std::size_t>(n) : static_cast<std::size_t>(static_cast<int64_t>(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<Json,JsonReference>::to_string(level+1)); +            //s.append("\n"); + +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class root_selector final : public base_selector<Json,JsonReference> +    { +        using supertype = base_selector<Json,JsonReference>; +        using path_generator_type = path_generator<Json,JsonReference>; + +        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<Json,JsonReference>(), id_(id) +        { +        } + +        void select(dynamic_resources<Json,JsonReference>& 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<Json,JsonReference>& 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<Json,JsonReference>::to_string(level+1)); + +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class current_node_selector final : public base_selector<Json,JsonReference> +    { +        using supertype = base_selector<Json,JsonReference>; + +    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<Json,JsonReference>; +        using node_receiver_type = typename supertype::node_receiver_type; + +        current_node_selector() +        { +        } + +        void select(dynamic_resources<Json,JsonReference>& 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<Json,JsonReference>& 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<Json,JsonReference>::to_string(level+1)); + +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class parent_node_selector final : public base_selector<Json,JsonReference> +    { +        using supertype = base_selector<Json,JsonReference>; + +        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<Json,JsonReference>; +        using node_receiver_type = typename supertype::node_receiver_type; + +        parent_node_selector(int ancestor_depth) +        { +            ancestor_depth_ = ancestor_depth; +        } + +        void select(dynamic_resources<Json,JsonReference>& 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<Json,JsonReference>& 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<Json,JsonReference>::to_string(level+1)); + +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class index_selector final : public base_selector<Json,JsonReference> +    { +        using supertype = base_selector<Json,JsonReference>; + +        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<Json,JsonReference>; +        using node_receiver_type = typename supertype::node_receiver_type; + +        index_selector(int64_t index) +            : base_selector<Json,JsonReference>(), index_(index) +        { +        } + +        void select(dynamic_resources<Json,JsonReference>& 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<int64_t>(current.size()); +                if (index_ >= 0 && index_ < slen) +                { +                    std::size_t i = static_cast<std::size_t>(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<std::size_t>(index); +                        this->tail_select(resources, root,  +                                            path_generator_type::generate(resources, last, i, options),  +                                            current.at(i), receiver, options); +                    } +                } +            } +        } + +        reference evaluate(dynamic_resources<Json,JsonReference>& 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<int64_t>(current.size()); +                if (index_ >= 0 && index_ < slen) +                { +                    std::size_t i = static_cast<std::size_t>(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<std::size_t>(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 Json,class JsonReference> +    class wildcard_selector final : public base_selector<Json,JsonReference> +    { +        using supertype = base_selector<Json,JsonReference>; + +    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<Json,JsonReference>; +        using node_receiver_type = typename supertype::node_receiver_type; + +        wildcard_selector() +            : base_selector<Json,JsonReference>() +        { +        } + +        void select(dynamic_resources<Json,JsonReference>& 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<Json,JsonReference>& 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<Json,JsonReference> 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<Json,JsonReference>::to_string(level)); + +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class recursive_selector final : public base_selector<Json,JsonReference> +    { +        using supertype = base_selector<Json,JsonReference>; + +    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<Json,JsonReference>; +        using node_receiver_type = typename supertype::node_receiver_type; + +        recursive_selector() +            : base_selector<Json,JsonReference>() +        { +        } + +        void select(dynamic_resources<Json,JsonReference>& 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<Json,JsonReference>& 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<Json,JsonReference> 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<Json,JsonReference>::to_string(level)); + +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class union_selector final : public jsonpath_selector<Json,JsonReference> +    { +        using supertype = jsonpath_selector<Json,JsonReference>; +    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<Json, JsonReference>; +        using path_generator_type = path_generator<Json,JsonReference>; +        using node_receiver_type = typename supertype::node_receiver_type; +        using selector_type = typename supertype::selector_type; +    private: +        std::vector<selector_type*> selectors_; +        selector_type* tail_; +    public: +        union_selector(std::vector<selector_type*>&& 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<Json,JsonReference>& 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<Json,JsonReference>& 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<Json,JsonReference> 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 Json,class JsonReference> +    class filter_selector final : public base_selector<Json,JsonReference> +    { +        using supertype = base_selector<Json,JsonReference>; + +        expression<Json,JsonReference> 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<Json,JsonReference>; +        using node_receiver_type = typename supertype::node_receiver_type; + +        filter_selector(expression<Json,JsonReference>&& expr) +            : base_selector<Json,JsonReference>(), expr_(std::move(expr)) +        { +        } + +        void select(dynamic_resources<Json,JsonReference>& 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<Json,JsonReference>& 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<Json,JsonReference> 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 Json,class JsonReference> +    class index_expression_selector final : public base_selector<Json,JsonReference> +    { +        using supertype = base_selector<Json,JsonReference>; + +        expression<Json,JsonReference> 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<Json,JsonReference>; +        using node_receiver_type = typename supertype::node_receiver_type; + +        index_expression_selector(expression<Json,JsonReference>&& expr) +            : base_selector<Json,JsonReference>(), expr_(std::move(expr)) +        { +        } + +        void select(dynamic_resources<Json,JsonReference>& 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<std::size_t>() && current.is_array()) +                { +                    std::size_t start = j.template as<std::size_t>(); +                    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<Json,JsonReference>& 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<std::size_t>() && current.is_array()) +                { +                    std::size_t start = j.template as<std::size_t>(); +                    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<Json,JsonReference>::to_string(level+1)); + +            return s; +        } +    }; + +    template <class Json,class JsonReference> +    class slice_selector final : public base_selector<Json,JsonReference> +    { +        using supertype = base_selector<Json,JsonReference>; +        using path_generator_type = path_generator<Json, JsonReference>; + +        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<Json,JsonReference>(), slice_(slic)  +        { +        } + +        void select(dynamic_resources<Json,JsonReference>& 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<int64_t>(current.size())) +                    { +                        end = current.size(); +                    } +                    for (int64_t i = start; i < end; i += step) +                    { +                        std::size_t j = static_cast<std::size_t>(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<int64_t>(current.size())) +                    { +                        start = static_cast<int64_t>(current.size()) - 1; +                    } +                    if (end < -1) +                    { +                        end = -1; +                    } +                    for (int64_t i = start; i > end; i += step) +                    { +                        std::size_t j = static_cast<std::size_t>(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<Json,JsonReference>& 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<Json,JsonReference> accum(jptr); +            select(resources, root, last, current, accum, options); +            return *jptr; +        } +    }; + +    template <class Json,class JsonReference> +    class function_selector final : public base_selector<Json,JsonReference> +    { +        using supertype = base_selector<Json,JsonReference>; + +        expression<Json,JsonReference> 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<Json,JsonReference>; +        using node_receiver_type = typename supertype::node_receiver_type; + +        function_selector(expression<Json,JsonReference>&& expr) +            : base_selector<Json,JsonReference>(), expr_(std::move(expr)) +        { +        } + +        void select(dynamic_resources<Json,JsonReference>& 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<Json,JsonReference>& 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 |