From 1d055261b4144dbf86b2658437015b15d4dd9bff Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 4 Sep 2022 00:32:56 +0100 Subject: initial --- include/jsoncons_ext/jsonpath/expression.hpp | 3329 ++++++++++++++++++++ include/jsoncons_ext/jsonpath/flatten.hpp | 432 +++ include/jsoncons_ext/jsonpath/json_location.hpp | 445 +++ include/jsoncons_ext/jsonpath/json_query.hpp | 115 + include/jsoncons_ext/jsonpath/jsonpath.hpp | 13 + include/jsoncons_ext/jsonpath/jsonpath_error.hpp | 240 ++ .../jsoncons_ext/jsonpath/jsonpath_expression.hpp | 2612 +++++++++++++++ .../jsoncons_ext/jsonpath/jsonpath_selector.hpp | 1322 ++++++++ 8 files changed, 8508 insertions(+) create mode 100644 include/jsoncons_ext/jsonpath/expression.hpp create mode 100644 include/jsoncons_ext/jsonpath/flatten.hpp create mode 100644 include/jsoncons_ext/jsonpath/json_location.hpp create mode 100644 include/jsoncons_ext/jsonpath/json_query.hpp create mode 100644 include/jsoncons_ext/jsonpath/jsonpath.hpp create mode 100644 include/jsoncons_ext/jsonpath/jsonpath_error.hpp create mode 100644 include/jsoncons_ext/jsonpath/jsonpath_expression.hpp create mode 100644 include/jsoncons_ext/jsonpath/jsonpath_selector.hpp (limited to 'include/jsoncons_ext/jsonpath') 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 // std::basic_string +#include // std::vector +#include // std::unordered_map +#include // std::unordered_set +#include // std::numeric_limits +#include // std::set +#include // std::move +#if defined(JSONCONS_HAS_STD_REGEX) +#include +#endif +#include +#include +#include + +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(~static_cast(a)); + } + + inline result_options operator&(result_options a, result_options b) + { + return static_cast(static_cast(a) & static_cast(b)); + } + + inline result_options operator^(result_options a, result_options b) + { + return static_cast(static_cast(a) ^ static_cast(b)); + } + + inline result_options operator|(result_options a, result_options b) + { + return static_cast(static_cast(a) | static_cast(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 parameter; + + template + class value_or_pointer + { + public: + friend class parameter; + using value_type = Json; + using reference = JsonReference; + using pointer = typename std::conditional::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 parameter + { + using value_type = Json; + using reference = const Json&; + using pointer = const Json*; + private: + value_or_pointer data_; + public: + template + parameter(value_or_pointer&& 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 custom_function + { + public: + using value_type = Json; + using char_type = typename Json::char_type; + using parameter_type = parameter; + using function_type = std::function, std::error_code& ec)>; + using string_type = std::basic_string; + + string_type function_name_; + optional arity_; + function_type f_; + + custom_function(const string_type& function_name, + const optional& arity, + const function_type& f) + : function_name_(function_name), + arity_(arity), + f_(f) + { + } + + custom_function(string_type&& function_name, + optional&& 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 arity() const + { + return arity_; + } + + const function_type& function() const + { + return f_; + } + }; + + template + class custom_functions + { + using char_type = typename Json::char_type; + using string_type = std::basic_string; + using value_type = Json; + using parameter_type = parameter; + using function_type = std::function, std::error_code& ec)>; + using const_iterator = typename std::vector>::const_iterator; + + std::vector> functions_; + public: + void register_function(const string_type& name, + jsoncons::optional 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 dynamic_resources; + + template + 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 + 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 + bool is_true(const Json& val) + { + return !is_false(val); + } + + template + class unary_not_operator final : public unary_operator + { + public: + unary_not_operator() + : unary_operator(1, true) + {} + + Json evaluate(JsonReference val, + std::error_code&) const override + { + return is_false(val) ? Json(true) : Json(false); + } + }; + + template + class unary_minus_operator final : public unary_operator + { + public: + unary_minus_operator() + : unary_operator(1, true) + {} + + Json evaluate(JsonReference val, + std::error_code&) const override + { + if (val.is_int64()) + { + return Json(-val.template as()); + } + else if (val.is_double()) + { + return Json(-val.as_double()); + } + else + { + return Json::null(); + } + } + }; + + template + class regex_operator final : public unary_operator + { + using char_type = typename Json::char_type; + using string_type = std::basic_string; + std::basic_regex pattern_; + public: + regex_operator(std::basic_regex&& pattern) + : unary_operator(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 + 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 or_operator final : public binary_operator + { + public: + or_operator() + : binary_operator(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 and_operator final : public binary_operator + { + public: + and_operator() + : binary_operator(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 eq_operator final : public binary_operator + { + public: + eq_operator() + : binary_operator(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 ne_operator final : public binary_operator + { + public: + ne_operator() + : binary_operator(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 lt_operator final : public binary_operator + { + public: + lt_operator() + : binary_operator(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 lte_operator final : public binary_operator + { + public: + lte_operator() + : binary_operator(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 gt_operator final : public binary_operator + { + public: + gt_operator() + : binary_operator(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 gte_operator final : public binary_operator + { + public: + gte_operator() + : binary_operator(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 plus_operator final : public binary_operator + { + public: + plus_operator() + : binary_operator(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() + rhs.template as()))); + } + else if (lhs.is_uint64() && rhs.is_uint64()) + { + return Json((lhs.template as() + rhs.template as())); + } + 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 minus_operator final : public binary_operator + { + public: + minus_operator() + : binary_operator(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() - rhs.template as()))); + } + else if (lhs.is_uint64() && rhs.is_uint64()) + { + return Json((lhs.template as() - rhs.template as())); + } + 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 mult_operator final : public binary_operator + { + public: + mult_operator() + : binary_operator(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() * rhs.template as()))); + } + else if (lhs.is_uint64() && rhs.is_uint64()) + { + return Json((lhs.template as() * rhs.template as())); + } + 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 div_operator final : public binary_operator + { + public: + div_operator() + : binary_operator(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() / rhs.template as()))); + } + else if (lhs.is_uint64() && rhs.is_uint64()) + { + return Json((lhs.template as() / rhs.template as())); + } + 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 modulus_operator final : public binary_operator + { + public: + modulus_operator() + : binary_operator(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() % rhs.template as()))); + } + else if (lhs.is_uint64() && rhs.is_uint64()) + { + return Json((lhs.template as() % rhs.template as())); + } + 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 function_base + { + jsoncons::optional arg_count_; + public: + using value_type = Json; + using parameter_type = parameter; + + function_base(jsoncons::optional arg_count) + : arg_count_(arg_count) + { + } + + virtual ~function_base() noexcept = default; + + jsoncons::optional arity() const + { + return arg_count_; + } + + virtual value_type evaluate(const std::vector& 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 decorator_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + using string_view_type = typename Json::string_view_type; + using function_type = std::function, std::error_code& ec)>; + private: + function_type f_; + public: + decorator_function(jsoncons::optional arity, + const function_type& f) + : function_base(arity), f_(f) + { + } + + value_type evaluate(const std::vector& args, + std::error_code& ec) const override + { + return f_(args, ec); + } + }; + + template + class contains_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + using string_view_type = typename Json::string_view_type; + + contains_function() + : function_base(2) + { + } + + value_type evaluate(const std::vector& 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(); + auto sv1 = arg1.template as(); + 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 ends_with_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + using string_view_type = typename Json::string_view_type; + + ends_with_function() + : function_base(2) + { + } + + value_type evaluate(const std::vector& 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(); + auto sv1 = arg1.template as(); + + 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 starts_with_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + using string_view_type = typename Json::string_view_type; + + starts_with_function() + : function_base(2) + { + } + + value_type evaluate(const std::vector& 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(); + auto sv1 = arg1.template as(); + + 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 sum_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + sum_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& 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(); + } + + 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 tokenize_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + using char_type = typename Json::char_type; + using string_type = std::basic_string; + + tokenize_function() + : function_base(2) + { + } + + value_type evaluate(const std::vector& 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(); + auto arg1 = args[1].value().template as(); + + std::regex::flag_type options = std::regex_constants::ECMAScript; + std::basic_regex pieces_regex(arg1, options); + + std::regex_token_iterator rit ( arg0.begin(), arg0.end(), pieces_regex, -1); + std::regex_token_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 ceil_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + ceil_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& 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()); + } + case json_type::double_value: + { + return value_type(std::ceil(arg0.template as())); + } + 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 floor_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + floor_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& 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()); + } + case json_type::double_value: + { + return value_type(std::floor(arg0.template as())); + } + 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 to_number_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + to_number_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& 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 prod_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + prod_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& 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(); + } + + 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 avg_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + avg_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& 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(); + } + + return value_type(sum / static_cast(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 min_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + min_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& 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 max_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + max_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& 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 abs_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + + abs_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& 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() >= 0 ? arg0 : value_type(std::abs(arg0.template as())); + } + case json_type::double_value: + { + return arg0.template as() >= 0 ? arg0 : value_type(std::abs(arg0.template as())); + } + 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 length_function : public function_base + { + public: + using value_type = Json; + using string_view_type = typename Json::string_view_type; + using parameter_type = parameter; + + length_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& 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(); + 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 keys_function : public function_base + { + public: + using value_type = Json; + using parameter_type = parameter; + using string_view_type = typename Json::string_view_type; + + keys_function() + : function_base(1) + { + } + + value_type evaluate(const std::vector& 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 + struct path_value_pair + { + using char_type = typename Json::char_type; + using string_type = std::basic_string>; + using value_type = Json; + using reference = JsonReference; + using value_pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + using json_location_node_type = json_location_node; + using json_location_type = json_location; + 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 + struct path_value_pair_less + { + bool operator()(const path_value_pair& lhs, + const path_value_pair& rhs) const noexcept + { + return lhs.path() < rhs.path(); + } + }; + + template + struct path_value_pair_equal + { + bool operator()(const path_value_pair& lhs, + const path_value_pair& rhs) const noexcept + { + return lhs.path() == rhs.path(); + } + }; + + template + struct path_component_value_pair + { + using char_type = typename Json::char_type; + using string_type = std::basic_string>; + using value_type = Json; + using reference = JsonReference; + using value_pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + using json_location_node_type = json_location_node; + using json_location_type = json_location; + 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 node_receiver + { + public: + using char_type = typename Json::char_type; + using reference = JsonReference; + using json_location_node_type = json_location_node; + + virtual ~node_receiver() noexcept = default; + + virtual void add(const json_location_node_type& path_tail, + reference value) = 0; + }; + + template + class path_value_receiver : public node_receiver + { + public: + using reference = JsonReference; + using char_type = typename Json::char_type; + using json_location_node_type = json_location_node; + using json_location_type = json_location; + using path_value_pair_type = path_value_pair; + + std::vector 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 path_stem_value_receiver : public node_receiver + { + public: + using reference = JsonReference; + using char_type = typename Json::char_type; + using json_location_node_type = json_location_node; + using path_stem_value_pair_type = path_component_value_pair; + + std::vector nodes; + + void add(const json_location_node_type& path_tail, + reference value) override + { + nodes.emplace_back(path_tail, value); + } + }; + + template + class dynamic_resources + { + using reference = JsonReference; + using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + using json_location_node_type = json_location_node; + using path_stem_value_pair_type = path_component_value_pair; + std::vector> temp_json_values_; + std::vector> temp_path_node_values_; + std::unordered_map 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 + Json* create_json(Args&& ... args) + { + auto temp = jsoncons::make_unique(std::forward(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 + const json_location_node_type* create_path_node(Args&& ... args) + { + auto temp = jsoncons::make_unique(std::forward(args)...); + json_location_node_type* ptr = temp.get(); + temp_path_node_values_.emplace_back(std::move(temp)); + return ptr; + } + }; + + template + struct node_less + { + bool operator()(const path_value_pair& a, const path_value_pair& b) const + { + return *(a.ptr) < *(b.ptr); + } + }; + + template + 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>; + using string_view_type = jsoncons::basic_string_view>; + using value_type = Json; + using reference = JsonReference; + using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + using path_value_pair_type = path_value_pair; + using json_location_node_type = json_location_node; + using json_location_type = json_location; + using node_receiver_type = node_receiver; + using selector_type = jsonpath_selector; + + 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& 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& 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 + struct static_resources + { + using char_type = typename Json::char_type; + using string_type = std::basic_string; + using value_type = Json; + using reference = JsonReference; + using function_base_type = function_base; + using selector_type = jsonpath_selector; + + std::vector> selectors_; + std::vector> temp_json_values_; + std::vector>> unary_operators_; + std::unordered_map> custom_functions_; + + static_resources() + { + } + + static_resources(const custom_functions& functions) + { + for (const auto& item : functions) + { + custom_functions_.emplace(item.name(), + jsoncons::make_unique>(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 abs_func; + static contains_function contains_func; + static starts_with_function starts_with_func; + static ends_with_function ends_with_func; + static ceil_function ceil_func; + static floor_function floor_func; + static to_number_function to_number_func; + static sum_function sum_func; + static prod_function prod_func; + static avg_function avg_func; + static min_function min_func; + static max_function max_func; + static length_function length_func; + static keys_function keys_func; +#if defined(JSONCONS_HAS_STD_REGEX) + static tokenize_function tokenize_func; +#endif + + static std::unordered_map 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* get_unary_not() const + { + static unary_not_operator oper; + return &oper; + } + + const unary_operator* get_unary_minus() const + { + static unary_minus_operator oper; + return &oper; + } + + const unary_operator* get_regex_operator(std::basic_regex&& pattern) + { + unary_operators_.push_back(jsoncons::make_unique>(std::move(pattern))); + return unary_operators_.back().get(); + } + + const binary_operator* get_or_operator() const + { + static or_operator oper; + + return &oper; + } + + const binary_operator* get_and_operator() const + { + static and_operator oper; + + return &oper; + } + + const binary_operator* get_eq_operator() const + { + static eq_operator oper; + return &oper; + } + + const binary_operator* get_ne_operator() const + { + static ne_operator oper; + return &oper; + } + + const binary_operator* get_lt_operator() const + { + static lt_operator oper; + return &oper; + } + + const binary_operator* get_lte_operator() const + { + static lte_operator oper; + return &oper; + } + + const binary_operator* get_gt_operator() const + { + static gt_operator oper; + return &oper; + } + + const binary_operator* get_gte_operator() const + { + static gte_operator oper; + return &oper; + } + + const binary_operator* get_plus_operator() const + { + static plus_operator oper; + return &oper; + } + + const binary_operator* get_minus_operator() const + { + static minus_operator oper; + return &oper; + } + + const binary_operator* get_mult_operator() const + { + static mult_operator oper; + return &oper; + } + + const binary_operator* get_div_operator() const + { + static div_operator oper; + return &oper; + } + + const binary_operator* get_modulus_operator() const + { + static modulus_operator oper; + return &oper; + } + + template + selector_type* new_selector(T&& val) + { + selectors_.emplace_back(jsoncons::make_unique(std::forward(val))); + return selectors_.back().get(); + } + + template + Json* create_json(Args&& ... args) + { + auto temp = jsoncons::make_unique(std::forward(args)...); + Json* ptr = temp.get(); + temp_json_values_.emplace_back(std::move(temp)); + return ptr; + } + }; + + template + class expression_base + { + public: + using char_type = typename Json::char_type; + using string_type = std::basic_string>; + using string_view_type = jsoncons::basic_string_view>; + using value_type = Json; + using reference = JsonReference; + using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + using path_value_pair_type = path_value_pair; + using json_location_node_type = json_location_node; + + virtual ~expression_base() noexcept = default; + + virtual value_type evaluate(dynamic_resources& 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 token + { + public: + using selector_type = jsonpath_selector; + using expression_base_type = expression_base; + + jsonpath_token_kind token_kind_; + + union + { + selector_type* selector_; + std::unique_ptr expression_; + const unary_operator* unary_operator_; + const binary_operator* binary_operator_; + const function_base* function_; + Json value_; + }; + public: + + token(const unary_operator* expr) noexcept + : token_kind_(jsonpath_token_kind::unary_operator), + unary_operator_(expr) + { + } + + token(const binary_operator* 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&& expr) + : token_kind_(jsonpath_token_kind::expression) + { + new (&expression_) std::unique_ptr(std::move(expr)); + } + + token(const function_base* 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(other)); + } + + const Json& get_value(const_reference_arg_t, dynamic_resources&) const + { + return value_; + } + + Json& get_value(reference_arg_t, dynamic_resources& 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(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 arity() const + { + return token_kind_ == jsonpath_token_kind::function ? function_->arity() : jsoncons::optional(); + } + + 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(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_receiver : public node_receiver + { + Callback& callback_; + public: + using reference = JsonReference; + using char_type = typename Json::char_type; + using json_location_node_type = json_location_node; + using json_location_type = json_location; + + 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 path_expression + { + public: + using char_type = typename Json::char_type; + using string_type = std::basic_string>; + using string_view_type = typename Json::string_view_type; + using path_value_pair_type = path_value_pair; + using path_value_pair_less_type = path_value_pair_less; + using path_value_pair_equal_type = path_value_pair_equal; + 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; + using reference_arg_type = typename std::conditional::type>::value, + const_reference_arg_t,reference_arg_t>::type; + using json_location_node_type = json_location_node; + using json_location_type = json_location; + using selector_type = jsonpath_selector; + 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& 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 + typename std::enable_if::value,void>::type + evaluate(dynamic_resources& 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 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 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 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 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 expression : public expression_base + { + public: + using path_value_pair_type = path_value_pair; + 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>; + using string_view_type = typename Json::string_view_type; + using path_value_pair_less_type = path_value_pair_less; + using path_value_pair_equal_type = path_value_pair_equal; + using parameter_type = parameter; + using token_type = token; + using reference_arg_type = typename std::conditional::type>::value, + const_reference_arg_t,reference_arg_t>::type; + using json_location_node_type = json_location_node; + using stack_item_type = value_or_pointer; + private: + std::vector token_list_; + public: + + expression() + { + } + + expression(expression&& expr) + : token_list_(std::move(expr.token_list_)) + { + } + + expression(std::vector&& token_stack) + : token_list_(std::move(token_stack)) + { + } + + expression& operator=(expression&& expr) = default; + + value_type evaluate(dynamic_resources& resources, + reference root, + reference current, + result_options options, + std::error_code& ec) const override + { + std::vector stack; + std::vector 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 +#include +#include +#include // std::is_const +#include // std::numeric_limits +#include // std::move +#include // std::copy +#include // std::back_inserter +#include + +namespace jsoncons { namespace jsonpath { + + template + 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 + void flatten_(const std::basic_string& parent_key, + const Json& parent_value, + Json& result) + { + using char_type = typename Json::char_type; + using string_type = std::basic_string; + + 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 + Json flatten(const Json& value) + { + Json result; + std::basic_string 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 + Json unflatten(const Json& value) + { + using char_type = typename Json::char_type; + using string_type = std::basic_string; + + 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 +#include +#include +#include // std::reverse +#include +#include +#include +#include + +namespace jsoncons { +namespace jsonpath { + + template + class json_location; + + enum class json_location_node_kind { root, index, name }; + + template + class json_location_node + { + friend class json_location; + public: + using char_type = CharT; + using string_type = std::basic_string; + 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{}(index_) : std::hash{}(name_); + + return h; + } + + int compare_node(const json_location_node& other) const + { + int diff = 0; + if (node_kind_ != other.node_kind_) + { + diff = static_cast(node_kind_) - static_cast(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 json_location_iterator + { + Iterator it_; + + public: + using iterator_category = std::random_access_iterator_tag; + + using value_type = typename std::remove_pointer::value_type>::type; + using difference_type = typename std::iterator_traits::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 ::value && std::is_convertible::value>::type> + json_location_iterator(const json_location_iterator& 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 operator+( + difference_type offset, json_location_iterator next) + { + return next += offset; + } + }; + + } // namespace detail + + template + class json_location + { + public: + using char_type = CharT; + using string_type = std::basic_string; + using json_location_node_type = json_location_node; + private: + std::vector nodes_; + public: + using iterator = typename detail::json_location_iterator::iterator>; + using const_iterator = typename detail::json_location_iterator::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 + Json* select(Json& root, const json_location& 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 +#include + +namespace jsoncons { +namespace jsonpath { + + template + Json json_query(const Json& instance, + const typename Json::string_view_type& path, + result_options options = result_options(), + const custom_functions& functions = custom_functions()) + { + auto expr = make_expression(path, functions); + return expr.evaluate(instance, options); + } + + template + typename std::enable_if&,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& functions = custom_functions()) + { + auto expr = make_expression(path, functions); + expr.evaluate(instance, callback, options); + } + + template + typename std::enable_if::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& funcs = custom_functions()) + { + using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator; + //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 static_resources(funcs); + evaluator_t e; + json_selector_t expr = e.compile(static_resources, path); + + jsoncons::jsonpath::detail::dynamic_resources resources; + auto callback = [&new_value](const json_location_type&, reference v) + { + v = std::forward(new_value); + }; + expr.evaluate(resources, instance, resources.root_path_node(), instance, callback, options); + } + + template + typename std::enable_if::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; + //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 static_resources; + evaluator_t e; + json_selector_t expr = e.compile(static_resources, path); + + jsoncons::jsonpath::detail::dynamic_resources 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 + typename std::enable_if&,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& funcs = custom_functions()) + { + using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator; + //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 static_resources(funcs); + evaluator_t e; + json_selector_t expr = e.compile(static_resources, path); + + jsoncons::jsonpath::detail::dynamic_resources 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 +#include + +#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 +#include + +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(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(result),jsonpath_error_category()); + } + +} // jsonpath +} // jsoncons + +namespace std { + template<> + struct is_error_code_enum : 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 +#include +#include +#include // std::is_const +#include // std::numeric_limits +#include // std::move +#include +#include // std::reverse +#include +#include +#include +#include + +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 jsonpath_evaluator : public ser_context + { + public: + using char_type = typename Json::char_type; + using string_type = std::basic_string>; + using string_view_type = typename Json::string_view_type; + using path_value_pair_type = path_value_pair; + using value_type = Json; + using reference = JsonReference; + using pointer = typename path_value_pair_type::value_pointer; + using token_type = token; + using path_expression_type = path_expression; + using expression_type = expression; + using json_location_type = json_location; + using json_location_node_type = json_location_node; + using selector_type = jsonpath_selector; + + 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; + std::vector function_stack_; + std::vector state_stack_; + std::vector output_stack_; + std::vector 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& 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& 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())), 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(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())), 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())), 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 decoder; + basic_json_parser 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 decoder; + basic_json_parser 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 decoder; + basic_json_parser 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())), 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(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::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(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 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(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())), 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())), 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(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())), 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(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(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(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(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(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(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(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(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(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(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(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())), 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(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())), 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())), 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(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(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(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& 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 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(expression_type(std::move(toks))))); + } + else + { + output_stack_.emplace_back(token_type(resources.new_selector(filter_selector(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 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(expression_type(std::move(toks))))); + } + else + { + output_stack_.emplace_back(token_type(resources.new_selector(index_expression_selector(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 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(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 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(std::move(expressions)))); + } + else + { + output_stack_.emplace_back(token_type(resources.new_selector(union_selector(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 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(expression_type(std::move(toks))))); + } + else + { + output_stack_.emplace_back(token_type(resources.new_selector(function_selector(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 jsonpath_expression + { + public: + using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator; + 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; + 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, std::error_code& ec)>; + private: + jsoncons::jsonpath::detail::static_resources static_resources_; + json_selector_t expr_; + public: + jsonpath_expression(jsoncons::jsonpath::detail::static_resources&& resources, + json_selector_t&& expr) + : static_resources_(std::move(resources)), + expr_(std::move(expr)) + { + } + + jsonpath_expression(jsoncons::jsonpath::detail::static_resources&& resources, + json_selector_t&& expr, std::vector&& custom_functions) + : static_resources_(std::move(resources)), + expr_(std::move(expr), std::move(custom_functions)) + { + } + + template + typename std::enable_if::value,void>::type + evaluate(reference instance, BinaryCallback callback, result_options options = result_options()) + { + jsoncons::jsonpath::detail::dynamic_resources 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 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 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 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 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& functions) + { + jsoncons::jsonpath::detail::static_resources 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& functions, + std::error_code& ec) + { + jsoncons::jsonpath::detail::static_resources resources(functions); + evaluator_t e; + json_selector_t expr = e.compile(resources, path, ec); + return jsonpath_expression(std::move(resources), std::move(expr)); + } + }; + + template + jsonpath_expression make_expression(const typename Json::string_view_type& expr, + const custom_functions& functions = custom_functions()) + { + return jsonpath_expression::compile(expr, functions); + } + + template + jsonpath_expression make_expression(const typename Json::string_view_type& expr, std::error_code& ec) + { + return jsonpath_expression::compile(expr, ec); + } + + template + jsonpath_expression make_expression(const typename Json::string_view_type& expr, + const custom_functions& functions, + std::error_code& ec) + { + return jsonpath_expression::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 +#include +#include +#include // std::is_const +#include // std::numeric_limits +#include // std::move +#include +#include +#include +#include + +namespace jsoncons { +namespace jsonpath { +namespace detail { + + struct slice + { + jsoncons::optional start_; + jsoncons::optional stop_; + int64_t step_; + + slice() + : start_(), stop_(), step_(1) + { + } + + slice(const jsoncons::optional& start, const jsoncons::optional& end, int64_t step) + : start_(start), stop_(end), step_(step) + { + } + + slice(const slice& other) + : start_(other.start_), stop_(other.stop_), step_(other.step_) + { + } + + slice& operator=(const slice& rhs) + { + if (this != &rhs) + { + if (rhs.start_) + { + start_ = rhs.start_; + } + else + { + start_.reset(); + } + if (rhs.stop_) + { + stop_ = rhs.stop_; + } + else + { + stop_.reset(); + } + step_ = rhs.step_; + } + return *this; + } + + int64_t get_start(std::size_t size) const + { + if (start_) + { + auto len = *start_ >= 0 ? *start_ : (static_cast(size) + *start_); + return len <= static_cast(size) ? len : static_cast(size); + } + else + { + if (step_ >= 0) + { + return 0; + } + else + { + return static_cast(size); + } + } + } + + int64_t get_stop(std::size_t size) const + { + if (stop_) + { + auto len = *stop_ >= 0 ? *stop_ : (static_cast(size) + *stop_); + return len <= static_cast(size) ? len : static_cast(size); + } + else + { + return step_ >= 0 ? static_cast(size) : -1; + } + } + + int64_t step() const + { + return step_; // Allow negative + } + }; + + template + class json_array_receiver : public node_receiver + { + public: + using reference = JsonReference; + using char_type = typename Json::char_type; + using json_location_node_type = json_location_node; + + Json* val; + + json_array_receiver(Json* ptr) + : val(ptr) + { + } + + void add(const json_location_node_type&, reference value) override + { + val->emplace_back(value); + } + }; + + template + struct path_generator + { + using char_type = typename Json::char_type; + using json_location_node_type = json_location_node; + using string_type = std::basic_string; + + static const json_location_node_type& generate(dynamic_resources& resources, + const json_location_node_type& last, + std::size_t index, + result_options options) + { + const result_options require_path = result_options::path | result_options::nodups | result_options::sort; + if ((options & require_path) != result_options()) + { + return *resources.create_path_node(&last, index); + } + else + { + return last; + } + } + + static const json_location_node_type& generate(dynamic_resources& resources, + const json_location_node_type& last, + const string_type& identifier, + result_options options) + { + const result_options require_path = result_options::path | result_options::nodups | result_options::sort; + if ((options & require_path) != result_options()) + { + return *resources.create_path_node(&last, identifier); + } + else + { + return last; + } + } + }; + + template + class base_selector : public jsonpath_selector + { + using supertype = jsonpath_selector; + + supertype* tail_; + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using json_location_type = typename supertype::json_location_type; + using node_receiver_type = typename supertype::node_receiver_type; + using selector_type = typename supertype::selector_type; + + base_selector() + : supertype(true, 11), tail_(nullptr) + { + } + + base_selector(bool is_path, std::size_t precedence_level) + : supertype(is_path, precedence_level), tail_(nullptr) + { + } + + void append_selector(selector_type* expr) override + { + if (!tail_) + { + tail_ = expr; + } + else + { + tail_->append_selector(expr); + } + } + + void tail_select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const + { + if (!tail_) + { + receiver.add(last, current); + } + else + { + tail_->select(resources, root, last, current, receiver, options); + } + } + + reference evaluate_tail(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code& ec) const + { + if (!tail_) + { + return current; + } + else + { + return tail_->evaluate(resources, root, last, current, options, ec); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + if (tail_) + { + s.append(tail_->to_string(level)); + } + return s; + } + }; + + template + class identifier_selector final : public base_selector + { + using supertype = base_selector; + using path_generator_type = path_generator; + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using char_type = typename Json::char_type; + using string_type = std::basic_string; + using string_view_type = basic_string_view; + using node_receiver_type = typename supertype::node_receiver_type; + private: + string_type identifier_; + public: + + identifier_selector(const string_view_type& identifier) + : base_selector(), identifier_(identifier) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + //std::string buf; + //buf.append("identifier selector: "); + //unicode_traits::convert(identifier_.data(),identifier_.size(),buf); + + static const char_type length_name[] = {'l', 'e', 'n', 'g', 't', 'h', 0}; + + if (current.is_object()) + { + auto it = current.find(identifier_); + if (it != current.object_range().end()) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, identifier_, options), + it->value(), receiver, options); + } + } + else if (current.is_array()) + { + int64_t n{0}; + auto r = jsoncons::detail::to_integer_decimal(identifier_.data(), identifier_.size(), n); + if (r) + { + std::size_t index = (n >= 0) ? static_cast(n) : static_cast(static_cast(current.size()) + n); + if (index < current.size()) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, index, options), + current[index], receiver, options); + } + } + else if (identifier_ == length_name && current.size() > 0) + { + pointer ptr = resources.create_json(current.size()); + this->tail_select(resources, root, + path_generator_type::generate(resources, last, identifier_, options), + *ptr, + receiver, options); + } + } + else if (current.is_string() && identifier_ == length_name) + { + string_view_type sv = current.as_string_view(); + std::size_t count = unicode_traits::count_codepoints(sv.data(), sv.size()); + pointer ptr = resources.create_json(count); + this->tail_select(resources, root, + path_generator_type::generate(resources, last, identifier_, options), + *ptr, receiver, options); + } + //std::cout << "end identifier_selector\n"; + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code& ec) const override + { + static const char_type length_name[] = {'l', 'e', 'n', 'g', 't', 'h', 0}; + + if (current.is_object()) + { + auto it = current.find(identifier_); + if (it != current.object_range().end()) + { + return this->evaluate_tail(resources, root, + path_generator_type::generate(resources, last, identifier_, options), + it->value(), options, ec); + } + else + { + return resources.null_value(); + } + } + else if (current.is_array()) + { + int64_t n{0}; + auto r = jsoncons::detail::to_integer_decimal(identifier_.data(), identifier_.size(), n); + if (r) + { + std::size_t index = (n >= 0) ? static_cast(n) : static_cast(static_cast(current.size()) + n); + if (index < current.size()) + { + return this->evaluate_tail(resources, root, + path_generator_type::generate(resources, last, index, options), + current[index], options, ec); + } + else + { + return resources.null_value(); + } + } + else if (identifier_ == length_name && current.size() > 0) + { + pointer ptr = resources.create_json(current.size()); + return this->evaluate_tail(resources, root, + path_generator_type::generate(resources, last, identifier_, options), + *ptr, + options, ec); + } + else + { + return resources.null_value(); + } + } + else if (current.is_string() && identifier_ == length_name) + { + string_view_type sv = current.as_string_view(); + std::size_t count = unicode_traits::count_codepoints(sv.data(), sv.size()); + pointer ptr = resources.create_json(count); + return this->evaluate_tail(resources, root, + path_generator_type::generate(resources, last, identifier_, options), + *ptr, options, ec); + } + else + { + return resources.null_value(); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("identifier selector "); + unicode_traits::convert(identifier_.data(),identifier_.size(),s); + s.append(base_selector::to_string(level+1)); + //s.append("\n"); + + return s; + } + }; + + template + class root_selector final : public base_selector + { + using supertype = base_selector; + using path_generator_type = path_generator; + + std::size_t id_; + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using node_receiver_type = typename supertype::node_receiver_type; + + root_selector(std::size_t id) + : base_selector(), id_(id) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference, + node_receiver_type& receiver, + result_options options) const override + { + this->tail_select(resources, root, last, root, receiver, options); + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference, + result_options options, + std::error_code& ec) const override + { + if (resources.is_cached(id_)) + { + return resources.retrieve_from_cache(id_); + } + else + { + auto& ref = this->evaluate_tail(resources, root, last, root, options, ec); + if (!ec) + { + resources.add_to_cache(id_, ref); + } + + return ref; + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("root_selector "); + s.append(base_selector::to_string(level+1)); + + return s; + } + }; + + template + class current_node_selector final : public base_selector + { + using supertype = base_selector; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + current_node_selector() + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + this->tail_select(resources, + root, last, current, receiver, options); + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code& ec) const override + { + //std::cout << "current_node_selector: " << current << "\n"; + return this->evaluate_tail(resources, + root, last, current, options, ec); + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("current_node_selector"); + s.append(base_selector::to_string(level+1)); + + return s; + } + }; + + template + class parent_node_selector final : public base_selector + { + using supertype = base_selector; + + int ancestor_depth_; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using json_location_type = typename supertype::json_location_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + parent_node_selector(int ancestor_depth) + { + ancestor_depth_ = ancestor_depth; + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference, + node_receiver_type& receiver, + result_options options) const override + { + const json_location_node_type* ancestor = std::addressof(last); + int index = 0; + while (ancestor != nullptr && index < ancestor_depth_) + { + ancestor = ancestor->parent(); + ++index; + } + + if (ancestor != nullptr) + { + json_location_type path(*ancestor); + pointer ptr = jsoncons::jsonpath::select(root,path); + if (ptr != nullptr) + { + this->tail_select(resources, root, path.last(), *ptr, receiver, options); + } + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference, + result_options options, + std::error_code& ec) const override + { + const json_location_node_type* ancestor = std::addressof(last); + int index = 0; + while (ancestor != nullptr && index < ancestor_depth_) + { + ancestor = ancestor->parent(); + ++index; + } + + if (ancestor != nullptr) + { + json_location_type path(*ancestor); + pointer ptr = jsoncons::jsonpath::select(root,path); + if (ptr != nullptr) + { + return this->evaluate_tail(resources, root, path.last(), *ptr, options, ec); + } + else + { + return resources.null_value(); + } + } + else + { + return resources.null_value(); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("parent_node_selector"); + s.append(base_selector::to_string(level+1)); + + return s; + } + }; + + template + class index_selector final : public base_selector + { + using supertype = base_selector; + + int64_t index_; + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + index_selector(int64_t index) + : base_selector(), index_(index) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + if (current.is_array()) + { + int64_t slen = static_cast(current.size()); + if (index_ >= 0 && index_ < slen) + { + std::size_t i = static_cast(index_); + this->tail_select(resources, root, + path_generator_type::generate(resources, last, i, options), + current.at(i), receiver, options); + } + else + { + int64_t index = slen + index_; + if (index >= 0 && index < slen) + { + std::size_t i = static_cast(index); + this->tail_select(resources, root, + path_generator_type::generate(resources, last, i, options), + current.at(i), receiver, options); + } + } + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code& ec) const override + { + if (current.is_array()) + { + int64_t slen = static_cast(current.size()); + if (index_ >= 0 && index_ < slen) + { + std::size_t i = static_cast(index_); + return this->evaluate_tail(resources, root, + path_generator_type::generate(resources, last, i, options), + current.at(i), options, ec); + } + else + { + int64_t index = slen + index_; + if (index >= 0 && index < slen) + { + std::size_t i = static_cast(index); + return this->evaluate_tail(resources, root, + path_generator_type::generate(resources, last, i, options), + current.at(i), options, ec); + } + else + { + return resources.null_value(); + } + } + } + else + { + return resources.null_value(); + } + } + }; + + template + class wildcard_selector final : public base_selector + { + using supertype = base_selector; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + wildcard_selector() + : base_selector() + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + if (current.is_array()) + { + for (std::size_t i = 0; i < current.size(); ++i) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, i, options), current[i], + receiver, options); + } + } + else if (current.is_object()) + { + for (auto& member : current.object_range()) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, member.key(), options), + member.value(), receiver, options); + } + } + //std::cout << "end wildcard_selector\n"; + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code&) const override + { + auto jptr = resources.create_json(json_array_arg); + json_array_receiver receiver(jptr); + select(resources, root, last, current, receiver, options); + return *jptr; + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("wildcard selector"); + s.append(base_selector::to_string(level)); + + return s; + } + }; + + template + class recursive_selector final : public base_selector + { + using supertype = base_selector; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + recursive_selector() + : base_selector() + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + if (current.is_array()) + { + this->tail_select(resources, root, last, current, receiver, options); + for (std::size_t i = 0; i < current.size(); ++i) + { + select(resources, root, + path_generator_type::generate(resources, last, i, options), current[i], receiver, options); + } + } + else if (current.is_object()) + { + this->tail_select(resources, root, last, current, receiver, options); + for (auto& item : current.object_range()) + { + select(resources, root, + path_generator_type::generate(resources, last, item.key(), options), item.value(), receiver, options); + } + } + //std::cout << "end wildcard_selector\n"; + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code&) const override + { + auto jptr = resources.create_json(json_array_arg); + json_array_receiver receiver(jptr); + select(resources, root, last, current, receiver, options); + return *jptr; + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("wildcard selector"); + s.append(base_selector::to_string(level)); + + return s; + } + }; + + template + class union_selector final : public jsonpath_selector + { + using supertype = jsonpath_selector; + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using json_location_type = typename supertype::json_location_type; + using path_expression_type = path_expression; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + using selector_type = typename supertype::selector_type; + private: + std::vector selectors_; + selector_type* tail_; + public: + union_selector(std::vector&& selectors) + : supertype(true, 11), selectors_(std::move(selectors)), tail_(nullptr) + { + } + + void append_selector(selector_type* tail) override + { + if (tail_ == nullptr) + { + tail_ = tail; + for (auto& selector : selectors_) + { + selector->append_selector(tail); + } + } + else + { + tail_->append_selector(tail); + } + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + for (auto& selector : selectors_) + { + selector->select(resources, root, last, current, receiver, options); + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code&) const override + { + auto jptr = resources.create_json(json_array_arg); + json_array_receiver receiver(jptr); + select(resources,root,last,current,receiver,options); + return *jptr; + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("union selector "); + for (auto& selector : selectors_) + { + s.append(selector->to_string(level+1)); + //s.push_back('\n'); + } + + return s; + } + }; + + template + class filter_selector final : public base_selector + { + using supertype = base_selector; + + expression expr_; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + filter_selector(expression&& expr) + : base_selector(), expr_(std::move(expr)) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + if (current.is_array()) + { + for (std::size_t i = 0; i < current.size(); ++i) + { + std::error_code ec; + value_type r = expr_.evaluate(resources, root, current[i], options, ec); + bool t = ec ? false : detail::is_true(r); + if (t) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, i, options), + current[i], receiver, options); + } + } + } + else if (current.is_object()) + { + for (auto& member : current.object_range()) + { + std::error_code ec; + value_type r = expr_.evaluate(resources, root, member.value(), options, ec); + bool t = ec ? false : detail::is_true(r); + if (t) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, member.key(), options), + member.value(), receiver, options); + } + } + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code&) const override + { + auto jptr = resources.create_json(json_array_arg); + json_array_receiver receiver(jptr); + select(resources, root, last, current, receiver, options); + return *jptr; + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("filter selector "); + s.append(expr_.to_string(level+1)); + + return s; + } + }; + + template + class index_expression_selector final : public base_selector + { + using supertype = base_selector; + + expression expr_; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + index_expression_selector(expression&& expr) + : base_selector(), expr_(std::move(expr)) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + std::error_code ec; + value_type j = expr_.evaluate(resources, root, current, options, ec); + + if (!ec) + { + if (j.template is() && current.is_array()) + { + std::size_t start = j.template as(); + this->tail_select(resources, root, + path_generator_type::generate(resources, last, start, options), + current.at(start), receiver, options); + } + else if (j.is_string() && current.is_object()) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last, j.as_string(), options), + current.at(j.as_string_view()), receiver, options); + } + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code& ec) const override + { + //std::cout << "index_expression_selector current: " << current << "\n"; + + value_type j = expr_.evaluate(resources, root, current, options, ec); + + if (!ec) + { + if (j.template is() && current.is_array()) + { + std::size_t start = j.template as(); + return this->evaluate_tail(resources, root, last, current.at(start), options, ec); + } + else if (j.is_string() && current.is_object()) + { + return this->evaluate_tail(resources, root, last, current.at(j.as_string_view()), options, ec); + } + else + { + return resources.null_value(); + } + } + else + { + return resources.null_value(); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("bracket expression selector "); + s.append(expr_.to_string(level+1)); + s.append(base_selector::to_string(level+1)); + + return s; + } + }; + + template + class slice_selector final : public base_selector + { + using supertype = base_selector; + using path_generator_type = path_generator; + + slice slice_; + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using node_receiver_type = typename supertype::node_receiver_type; + + slice_selector(const slice& slic) + : base_selector(), slice_(slic) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + if (current.is_array()) + { + auto start = slice_.get_start(current.size()); + auto end = slice_.get_stop(current.size()); + auto step = slice_.step(); + + if (step > 0) + { + if (start < 0) + { + start = 0; + } + if (end > static_cast(current.size())) + { + end = current.size(); + } + for (int64_t i = start; i < end; i += step) + { + std::size_t j = static_cast(i); + this->tail_select(resources, root, + path_generator_type::generate(resources, last, j, options), + current[j], receiver, options); + } + } + else if (step < 0) + { + if (start >= static_cast(current.size())) + { + start = static_cast(current.size()) - 1; + } + if (end < -1) + { + end = -1; + } + for (int64_t i = start; i > end; i += step) + { + std::size_t j = static_cast(i); + if (j < current.size()) + { + this->tail_select(resources, root, + path_generator_type::generate(resources, last,j,options), current[j], receiver, options); + } + } + } + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code&) const override + { + auto jptr = resources.create_json(json_array_arg); + json_array_receiver accum(jptr); + select(resources, root, last, current, accum, options); + return *jptr; + } + }; + + template + class function_selector final : public base_selector + { + using supertype = base_selector; + + expression expr_; + + public: + using value_type = typename supertype::value_type; + using reference = typename supertype::reference; + using pointer = typename supertype::pointer; + using path_value_pair_type = typename supertype::path_value_pair_type; + using json_location_node_type = typename supertype::json_location_node_type; + using path_generator_type = path_generator; + using node_receiver_type = typename supertype::node_receiver_type; + + function_selector(expression&& expr) + : base_selector(), expr_(std::move(expr)) + { + } + + void select(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + node_receiver_type& receiver, + result_options options) const override + { + std::error_code ec; + value_type ref = expr_.evaluate(resources, root, current, options, ec); + if (!ec) + { + this->tail_select(resources, root, last, *resources.create_json(std::move(ref)), receiver, options); + } + } + + reference evaluate(dynamic_resources& resources, + reference root, + const json_location_node_type& last, + reference current, + result_options options, + std::error_code& ec) const override + { + value_type ref = expr_.evaluate(resources, root, current, options, ec); + if (!ec) + { + return this->evaluate_tail(resources, root, last, *resources.create_json(std::move(ref)), + options, ec); + } + else + { + return resources.null_value(); + } + } + + std::string to_string(int level = 0) const override + { + std::string s; + if (level > 0) + { + s.append("\n"); + s.append(level*2, ' '); + } + s.append("function_selector "); + s.append(expr_.to_string(level+1)); + + return s; + } + }; + +} // namespace detail +} // namespace jsonpath +} // namespace jsoncons + +#endif -- cgit v1.2.3