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