diff options
author | Richard <q@1bpm.net> | 2022-09-04 00:32:56 +0100 |
---|---|---|
committer | Richard <q@1bpm.net> | 2022-09-04 00:32:56 +0100 |
commit | 1d055261b4144dbf86b2658437015b15d4dd9bff (patch) | |
tree | 6049b19d1bf953a650383de1a5e438b8b82679f6 /include/jsoncons_ext/jmespath | |
download | csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.gz csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.bz2 csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.zip |
initial
Diffstat (limited to 'include/jsoncons_ext/jmespath')
-rw-r--r-- | include/jsoncons_ext/jmespath/jmespath.hpp | 5215 | ||||
-rw-r--r-- | include/jsoncons_ext/jmespath/jmespath_error.hpp | 215 |
2 files changed, 5430 insertions, 0 deletions
diff --git a/include/jsoncons_ext/jmespath/jmespath.hpp b/include/jsoncons_ext/jmespath/jmespath.hpp new file mode 100644 index 0000000..69458cd --- /dev/null +++ b/include/jsoncons_ext/jmespath/jmespath.hpp @@ -0,0 +1,5215 @@ +// Copyright 2020 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_JMESPATH_JMESPATH_HPP +#define JSONCONS_JMESPATH_JMESPATH_HPP + +#include <string> +#include <vector> +#include <unordered_map> // std::unordered_map +#include <memory> +#include <type_traits> // std::is_const +#include <limits> // std::numeric_limits +#include <utility> // std::move +#include <functional> // +#include <algorithm> // std::stable_sort, std::reverse +#include <cmath> // std::abs +#include <jsoncons/json.hpp> +#include <jsoncons_ext/jmespath/jmespath_error.hpp> + +namespace jsoncons { +namespace jmespath { + + enum class operator_kind + { + default_op, // Identifier, CurrentNode, Index, MultiSelectList, MultiSelectHash, FunctionExpression + projection_op, + flatten_projection_op, // FlattenProjection + or_op, + and_op, + eq_op, + ne_op, + lt_op, + lte_op, + gt_op, + gte_op, + not_op + }; + + struct operator_table final + { + static int precedence_level(operator_kind oper) + { + switch (oper) + { + case operator_kind::projection_op: + return 11; + case operator_kind::flatten_projection_op: + return 11; + case operator_kind::or_op: + return 9; + case operator_kind::and_op: + return 8; + case operator_kind::eq_op: + case operator_kind::ne_op: + return 6; + case operator_kind::lt_op: + case operator_kind::lte_op: + case operator_kind::gt_op: + case operator_kind::gte_op: + return 5; + case operator_kind::not_op: + return 1; + default: + return 1; + } + } + + static bool is_right_associative(operator_kind oper) + { + switch (oper) + { + case operator_kind::not_op: + return true; + case operator_kind::projection_op: + return true; + case operator_kind::flatten_projection_op: + return false; + case operator_kind::or_op: + case operator_kind::and_op: + case operator_kind::eq_op: + case operator_kind::ne_op: + case operator_kind::lt_op: + case operator_kind::lte_op: + case operator_kind::gt_op: + case operator_kind::gte_op: + return false; + default: + return false; + } + } + }; + + enum class token_kind + { + current_node, + lparen, + rparen, + begin_multi_select_hash, + end_multi_select_hash, + begin_multi_select_list, + end_multi_select_list, + begin_filter, + end_filter, + pipe, + separator, + key, + literal, + expression, + binary_operator, + unary_operator, + function, + end_function, + argument, + begin_expression_type, + end_expression_type, + end_of_expression + }; + + struct literal_arg_t + { + explicit literal_arg_t() = default; + }; + constexpr literal_arg_t literal_arg{}; + + struct begin_expression_type_arg_t + { + explicit begin_expression_type_arg_t() = default; + }; + constexpr begin_expression_type_arg_t begin_expression_type_arg{}; + + struct end_expression_type_arg_t + { + explicit end_expression_type_arg_t() = default; + }; + constexpr end_expression_type_arg_t end_expression_type_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 key_arg_t + { + explicit key_arg_t() = default; + }; + constexpr key_arg_t key_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_multi_select_hash_arg_t + { + explicit begin_multi_select_hash_arg_t() = default; + }; + constexpr begin_multi_select_hash_arg_t begin_multi_select_hash_arg{}; + + struct end_multi_select_hash_arg_t + { + explicit end_multi_select_hash_arg_t() = default; + }; + constexpr end_multi_select_hash_arg_t end_multi_select_hash_arg{}; + + struct begin_multi_select_list_arg_t + { + explicit begin_multi_select_list_arg_t() = default; + }; + constexpr begin_multi_select_list_arg_t begin_multi_select_list_arg{}; + + struct end_multi_select_list_arg_t + { + explicit end_multi_select_list_arg_t() = default; + }; + constexpr end_multi_select_list_arg_t end_multi_select_list_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 pipe_arg_t + { + explicit pipe_arg_t() = default; + }; + constexpr pipe_arg_t pipe_arg{}; + + struct current_node_arg_t + { + explicit current_node_arg_t() = default; + }; + constexpr current_node_arg_t current_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{}; + + 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 + } + }; + + namespace detail { + + enum class path_state + { + start, + lhs_expression, + rhs_expression, + sub_expression, + expression_type, + comparator_expression, + function_expression, + argument, + expression_or_expression_type, + quoted_string, + raw_string, + raw_string_escape_char, + 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, + literal, + key_expr, + val_expr, + identifier_or_function_expr, + unquoted_string, + key_val_expr, + number, + digit, + index_or_slice_expression, + bracket_specifier, + bracket_specifier_or_multi_select_list, + filter, + multi_select_list, + multi_select_hash, + rhs_slice_expression_stop, + rhs_slice_expression_step, + expect_rbracket, + expect_rparen, + expect_dot, + expect_rbrace, + expect_colon, + expect_multi_select_list, + cmp_lt_or_lte, + cmp_eq, + cmp_gt_or_gte, + cmp_ne, + expect_pipe_or_or, + expect_and + }; + + // dynamic_resources + + template<class Json, class JsonReference> + class dynamic_resources + { + typedef typename Json::char_type char_type; + typedef typename Json::char_traits_type char_traits_type; + typedef std::basic_string<char_type,char_traits_type> string_type; + typedef typename Json::string_view_type string_view_type; + typedef JsonReference reference; + using pointer = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + typedef typename Json::const_pointer const_pointer; + + std::vector<std::unique_ptr<Json>> temp_storage_; + + public: + ~dynamic_resources() + { + } + + reference number_type_name() + { + static Json number_type_name(JSONCONS_STRING_CONSTANT(char_type, "number")); + + return number_type_name; + } + + reference boolean_type_name() + { + static Json boolean_type_name(JSONCONS_STRING_CONSTANT(char_type, "boolean")); + + return boolean_type_name; + } + + reference string_type_name() + { + static Json string_type_name(JSONCONS_STRING_CONSTANT(char_type, "string")); + + return string_type_name; + } + + reference object_type_name() + { + static Json object_type_name(JSONCONS_STRING_CONSTANT(char_type, "object")); + + return object_type_name; + } + + reference array_type_name() + { + static Json array_type_name(JSONCONS_STRING_CONSTANT(char_type, "array")); + + return array_type_name; + } + + reference null_type_name() + { + static Json null_type_name(JSONCONS_STRING_CONSTANT(char_type, "null")); + + return null_type_name; + } + + reference true_value() const + { + static const Json true_value(true, semantic_tag::none); + return true_value; + } + + reference false_value() const + { + static const Json false_value(false, semantic_tag::none); + return false_value; + } + + reference null_value() const + { + static const Json null_value(null_type(), semantic_tag::none); + return null_value; + } + + template <typename... Args> + Json* create_json(Args&& ... args) + { + auto temp = jsoncons::make_unique<Json>(std::forward<Args>(args)...); + Json* ptr = temp.get(); + temp_storage_.emplace_back(std::move(temp)); + return ptr; + } + }; + + template<class Json, class JsonReference> + class jmespath_evaluator + { + public: + typedef typename Json::char_type char_type; + typedef typename Json::char_traits_type char_traits_type; + typedef std::basic_string<char_type,char_traits_type> string_type; + typedef typename Json::string_view_type string_view_type; + typedef JsonReference reference; + using pointer = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + typedef typename Json::const_pointer const_pointer; + + static bool is_false(reference ref) + { + return (ref.is_array() && ref.empty()) || + (ref.is_object() && ref.empty()) || + (ref.is_string() && ref.as_string_view().size() == 0) || + (ref.is_bool() && !ref.as_bool()) || + ref.is_null(); + } + + static bool is_true(reference ref) + { + return !is_false(ref); + } + + class unary_operator + { + std::size_t precedence_level_; + bool is_right_associative_; + + protected: + ~unary_operator() = default; // virtual destructor not needed + public: + unary_operator(operator_kind oper) + : precedence_level_(operator_table::precedence_level(oper)), + is_right_associative_(operator_table::is_right_associative(oper)) + { + } + + std::size_t precedence_level() const + { + return precedence_level_; + } + bool is_right_associative() const + { + return is_right_associative_; + } + + virtual reference evaluate(reference val, dynamic_resources<Json,JsonReference>&, std::error_code& ec) const = 0; + }; + + class not_expression final : public unary_operator + { + public: + not_expression() + : unary_operator(operator_kind::not_op) + {} + + reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override + { + return is_false(val) ? resources.true_value() : resources.false_value(); + } + }; + + class binary_operator + { + std::size_t precedence_level_; + bool is_right_associative_; + protected: + ~binary_operator() = default; // virtual destructor not needed + public: + binary_operator(operator_kind oper) + : precedence_level_(operator_table::precedence_level(oper)), + is_right_associative_(operator_table::is_right_associative(oper)) + { + } + + + std::size_t precedence_level() const + { + return precedence_level_; + } + bool is_right_associative() const + { + return is_right_associative_; + } + + virtual reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>&, std::error_code& ec) const = 0; + + virtual std::string to_string(std::size_t indent = 0) const + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("to_string not implemented\n"); + return s; + } + }; + + // expression_base + class expression_base + { + std::size_t precedence_level_; + bool is_right_associative_; + bool is_projection_; + public: + expression_base(operator_kind oper, bool is_projection) + : precedence_level_(operator_table::precedence_level(oper)), + is_right_associative_(operator_table::is_right_associative(oper)), + is_projection_(is_projection) + { + } + + std::size_t precedence_level() const + { + return precedence_level_; + } + + bool is_right_associative() const + { + return is_right_associative_; + } + + bool is_projection() const + { + return is_projection_; + } + + virtual ~expression_base() = default; + + virtual reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const = 0; + + virtual void add_expression(std::unique_ptr<expression_base>&& expressions) = 0; + + virtual std::string to_string(std::size_t = 0) const + { + return std::string("to_string not implemented"); + } + }; + + // parameter + + enum class parameter_kind{value, expression}; + + class parameter + { + parameter_kind type_; + + union + { + expression_base* expression_; + pointer value_; + }; + + public: + + parameter(const parameter& other) noexcept + : type_(other.type_) + { + switch (type_) + { + case parameter_kind::expression: + expression_ = other.expression_; + break; + case parameter_kind::value: + value_ = other.value_; + break; + default: + break; + } + } + + parameter(reference value) noexcept + : type_(parameter_kind::value), value_(std::addressof(value)) + { + } + + parameter(expression_base* expression) noexcept + : type_(parameter_kind::expression), expression_(expression) + { + } + + parameter& operator=(const parameter& other) + { + if (&other != this) + { + type_ = other.type_; + switch (type_) + { + case parameter_kind::expression: + expression_ = other.expression_; + break; + case parameter_kind::value: + value_ = other.value_; + break; + default: + break; + } + } + return *this; + } + + bool is_value() const + { + return type_ == parameter_kind::value; + } + + bool is_expression() const + { + return type_ == parameter_kind::expression; + } + + const Json& value() const + { + return *value_; + } + + const expression_base& expression() const + { + return *expression_; + } + }; + + // function_base + class function_base + { + jsoncons::optional<std::size_t> arg_count_; + public: + function_base(jsoncons::optional<std::size_t> arg_count) + : arg_count_(arg_count) + { + } + + jsoncons::optional<std::size_t> arity() const + { + return arg_count_; + } + + virtual ~function_base() = default; + + virtual reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>&, std::error_code& ec) const = 0; + + virtual std::string to_string(std::size_t = 0) const + { + return std::string("to_string not implemented"); + } + }; + + class abs_function : public function_base + { + public: + abs_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference 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 : *resources.create_json(std::abs(arg0.template as<int64_t>())); + } + case json_type::double_value: + { + return arg0.template as<double>() >= 0 ? arg0 : *resources.create_json(std::abs(arg0.template as<double>())); + } + default: + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + } + }; + + class avg_function : public function_base + { + public: + avg_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.empty()) + { + return resources.null_value(); + } + + double sum = 0; + for (auto& j : arg0.array_range()) + { + if (!j.is_number()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + sum += j.template as<double>(); + } + + return sum == 0 ? resources.null_value() : *resources.create_json(sum/arg0.size()); + } + }; + + class ceil_function : public function_base + { + public: + ceil_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + switch (arg0.type()) + { + case json_type::uint64_value: + case json_type::int64_value: + { + return *resources.create_json(arg0.template as<double>()); + } + case json_type::double_value: + { + return *resources.create_json(std::ceil(arg0.template as<double>())); + } + default: + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + }; + + class contains_function : public function_base + { + public: + contains_function() + : function_base(2) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_value())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + + reference arg0 = args[0].value(); + reference arg1 = args[1].value(); + + switch (arg0.type()) + { + case json_type::array_value: + for (auto& j : arg0.array_range()) + { + if (j == arg1) + { + return resources.true_value(); + } + } + return resources.false_value(); + case json_type::string_value: + { + if (!arg1.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + auto sv0 = arg0.template as<string_view_type>(); + auto sv1 = arg1.template as<string_view_type>(); + return sv0.find(sv1) != string_view_type::npos ? resources.true_value() : resources.false_value(); + } + default: + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + } + }; + + class ends_with_function : public function_base + { + public: + ends_with_function() + : function_base(2) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_value())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg1 = args[1].value(); + if (!arg1.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + 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 resources.true_value(); + } + else + { + return resources.false_value(); + } + } + }; + + class floor_function : public function_base + { + public: + floor_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + switch (arg0.type()) + { + case json_type::uint64_value: + case json_type::int64_value: + { + return *resources.create_json(arg0.template as<double>()); + } + case json_type::double_value: + { + return *resources.create_json(std::floor(arg0.template as<double>())); + } + default: + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + }; + + class join_function : public function_base + { + public: + join_function() + : function_base(2) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + reference arg0 = args[0].value(); + reference arg1 = args[1].value(); + + if (!(args[0].is_value() && args[1].is_value())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + if (!arg0.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (!arg1.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + string_type sep = arg0.template as<string_type>(); + string_type buf; + for (auto& j : arg1.array_range()) + { + if (!j.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (!buf.empty()) + { + buf.append(sep); + } + auto sv = j.template as<string_view_type>(); + buf.append(sv.begin(), sv.end()); + } + return *resources.create_json(buf); + } + }; + + class length_function : public function_base + { + public: + length_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + + switch (arg0.type()) + { + case json_type::object_value: + case json_type::array_value: + return *resources.create_json(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 *resources.create_json(length); + } + default: + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + } + }; + + class max_function : public function_base + { + public: + max_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.empty()) + { + return resources.null_value(); + } + + bool is_number = arg0.at(0).is_number(); + bool is_string = arg0.at(0).is_string(); + if (!is_number && !is_string) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + 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 = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.at(i) > arg0.at(index)) + { + index = i; + } + } + + return arg0.at(index); + } + }; + + class max_by_function : public function_base + { + public: + max_by_function() + : function_base(2) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_expression())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.empty()) + { + return resources.null_value(); + } + + const auto& expr = args[1].expression(); + + std::error_code ec2; + Json key1 = expr.evaluate(arg0.at(0), resources, ec2); + + bool is_number = key1.is_number(); + bool is_string = key1.is_string(); + if (!(is_number || is_string)) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + std::size_t index = 0; + for (std::size_t i = 1; i < arg0.size(); ++i) + { + reference key2 = expr.evaluate(arg0.at(i), resources, ec2); + if (!(key2.is_number() == is_number && key2.is_string() == is_string)) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (key2 > key1) + { + key1 = key2; + index = i; + } + } + + return arg0.at(index); + } + }; + + class map_function : public function_base + { + public: + map_function() + : function_base(2) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_expression() && args[1].is_value())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + const auto& expr = args[0].expression(); + + reference arg0 = args[1].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + + for (auto& item : arg0.array_range()) + { + auto& j = expr.evaluate(item, resources, ec); + if (ec) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + + return *result; + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("map_function\n"); + } + }; + + class min_function : public function_base + { + public: + min_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.empty()) + { + return resources.null_value(); + } + + bool is_number = arg0.at(0).is_number(); + bool is_string = arg0.at(0).is_string(); + if (!is_number && !is_string) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + 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 = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.at(i) < arg0.at(index)) + { + index = i; + } + } + + return arg0.at(index); + } + }; + + class min_by_function : public function_base + { + public: + min_by_function() + : function_base(2) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_expression())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.empty()) + { + return resources.null_value(); + } + + const auto& expr = args[1].expression(); + + std::error_code ec2; + Json key1 = expr.evaluate(arg0.at(0), resources, ec2); + + bool is_number = key1.is_number(); + bool is_string = key1.is_string(); + if (!(is_number || is_string)) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + std::size_t index = 0; + for (std::size_t i = 1; i < arg0.size(); ++i) + { + reference key2 = expr.evaluate(arg0.at(i), resources, ec2); + if (!(key2.is_number() == is_number && key2.is_string() == is_string)) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (key2 < key1) + { + key1 = key2; + index = i; + } + } + + return arg0.at(index); + } + }; + + class merge_function : public function_base + { + public: + merge_function() + : function_base(jsoncons::optional<std::size_t>()) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + if (args.empty()) + { + ec = jmespath_errc::invalid_arity; + return resources.null_value(); + } + + for (auto& param : args) + { + if (!param.is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + + reference arg0 = args[0].value(); + if (!arg0.is_object()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (args.size() == 1) + { + return arg0; + } + + auto result = resources.create_json(arg0); + for (std::size_t i = 1; i < args.size(); ++i) + { + reference argi = args[i].value(); + if (!argi.is_object()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + for (auto& item : argi.object_range()) + { + result->insert_or_assign(item.key(),item.value()); + } + } + + return *result; + } + }; + + class type_function : public function_base + { + public: + type_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + + switch (arg0.type()) + { + case json_type::int64_value: + case json_type::uint64_value: + case json_type::double_value: + return resources.number_type_name(); + case json_type::bool_value: + return resources.boolean_type_name(); + case json_type::string_value: + return resources.string_type_name(); + case json_type::object_value: + return resources.object_type_name(); + case json_type::array_value: + return resources.array_type_name(); + default: + return resources.null_type_name(); + break; + + } + } + }; + + class sort_function : public function_base + { + public: + sort_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.size() <= 1) + { + return arg0; + } + + bool is_number = arg0.at(0).is_number(); + bool is_string = arg0.at(0).is_string(); + if (!is_number && !is_string) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + 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 = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + + auto v = resources.create_json(arg0); + std::stable_sort((v->array_range()).begin(), (v->array_range()).end()); + return *v; + } + }; + + class sort_by_function : public function_base + { + public: + sort_by_function() + : function_base(2) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_expression())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + if (arg0.size() <= 1) + { + return arg0; + } + + const auto& expr = args[1].expression(); + + auto v = resources.create_json(arg0); + std::stable_sort((v->array_range()).begin(), (v->array_range()).end(), + [&expr,&resources,&ec](reference lhs, reference rhs) -> bool + { + std::error_code ec2; + reference key1 = expr.evaluate(lhs, resources, ec2); + bool is_number = key1.is_number(); + bool is_string = key1.is_string(); + if (!(is_number || is_string)) + { + ec = jmespath_errc::invalid_type; + } + + reference key2 = expr.evaluate(rhs, resources, ec2); + if (!(key2.is_number() == is_number && key2.is_string() == is_string)) + { + ec = jmespath_errc::invalid_type; + } + + return key1 < key2; + }); + return ec ? resources.null_value() : *v; + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("sort_by_function\n"); + } + }; + + class keys_function final : public function_base + { + public: + keys_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_object()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + result->reserve(args.size()); + + for (auto& item : arg0.object_range()) + { + result->emplace_back(item.key()); + } + return *result; + } + }; + + class values_function final : public function_base + { + public: + values_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_object()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + result->reserve(args.size()); + + for (auto& item : arg0.object_range()) + { + result->emplace_back(item.value()); + } + return *result; + } + }; + + class reverse_function final : public function_base + { + public: + reverse_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + switch (arg0.type()) + { + case json_type::string_value: + { + string_view_type sv = arg0.as_string_view(); + std::basic_string<char32_t> buf; + unicode_traits::convert(sv.data(), sv.size(), buf); + std::reverse(buf.begin(), buf.end()); + string_type s; + unicode_traits::convert(buf.data(), buf.size(), s); + return *resources.create_json(s); + } + case json_type::array_value: + { + auto result = resources.create_json(arg0); + std::reverse(result->array_range().begin(),result->array_range().end()); + return *result; + } + default: + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + } + }; + + class starts_with_function : public function_base + { + public: + starts_with_function() + : function_base(2) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_value())) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg1 = args[1].value(); + if (!arg1.is_string()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + 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 resources.true_value(); + } + else + { + return resources.false_value(); + } + } + }; + + class sum_function : public function_base + { + public: + sum_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + double sum = 0; + for (auto& j : arg0.array_range()) + { + if (!j.is_number()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + sum += j.template as<double>(); + } + + return *resources.create_json(sum); + } + }; + + class to_array_function final : public function_base + { + public: + to_array_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (arg0.is_array()) + { + return arg0; + } + else + { + auto result = resources.create_json(json_array_arg); + result->push_back(arg0); + return *result; + } + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("to_array_function\n"); + } + }; + + class to_number_function final : public function_base + { + public: + to_number_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference 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 uval{ 0 }; + auto result1 = jsoncons::detail::to_integer(sv.data(), sv.length(), uval); + if (result1) + { + return *resources.create_json(uval); + } + int64_t sval{ 0 }; + auto result2 = jsoncons::detail::to_integer(sv.data(), sv.length(), sval); + if (result2) + { + return *resources.create_json(sval); + } + jsoncons::detail::chars_to to_double; + try + { + auto s = arg0.as_string(); + double d = to_double(s.c_str(), s.length()); + return *resources.create_json(d); + } + catch (const std::exception&) + { + return resources.null_value(); + } + } + default: + return resources.null_value(); + } + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("to_number_function\n"); + } + }; + + class to_string_function final : public function_base + { + public: + to_string_function() + : function_base(1) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jmespath_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + return *resources.create_json(arg0.template as<string_type>()); + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("to_string_function\n"); + } + }; + + class not_null_function final : public function_base + { + public: + not_null_function() + : function_base(jsoncons::optional<std::size_t>()) + { + } + + reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override + { + for (auto& param : args) + { + if (param.is_value() && !param.value().is_null()) + { + return param.value(); + } + } + return resources.null_value(); + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("to_string_function\n"); + } + }; + + // token + + class token + { + public: + token_kind type_; + + union + { + std::unique_ptr<expression_base> expression_; + const unary_operator* unary_operator_; + const binary_operator* binary_operator_; + const function_base* function_; + Json value_; + string_type key_; + }; + public: + + token(current_node_arg_t) noexcept + : type_(token_kind::current_node) + { + } + + token(end_function_arg_t) noexcept + : type_(token_kind::end_function) + { + } + + token(separator_arg_t) noexcept + : type_(token_kind::separator) + { + } + + token(lparen_arg_t) noexcept + : type_(token_kind::lparen) + { + } + + token(rparen_arg_t) noexcept + : type_(token_kind::rparen) + { + } + + token(end_of_expression_arg_t) noexcept + : type_(token_kind::end_of_expression) + { + } + + token(begin_multi_select_hash_arg_t) noexcept + : type_(token_kind::begin_multi_select_hash) + { + } + + token(end_multi_select_hash_arg_t) noexcept + : type_(token_kind::end_multi_select_hash) + { + } + + token(begin_multi_select_list_arg_t) noexcept + : type_(token_kind::begin_multi_select_list) + { + } + + token(end_multi_select_list_arg_t) noexcept + : type_(token_kind::end_multi_select_list) + { + } + + token(begin_filter_arg_t) noexcept + : type_(token_kind::begin_filter) + { + } + + token(end_filter_arg_t) noexcept + : type_(token_kind::end_filter) + { + } + + token(pipe_arg_t) noexcept + : type_(token_kind::pipe) + { + } + + token(key_arg_t, const string_type& key) + : type_(token_kind::key) + { + new (&key_) string_type(key); + } + + token(std::unique_ptr<expression_base>&& expression) + : type_(token_kind::expression) + { + new (&expression_) std::unique_ptr<expression_base>(std::move(expression)); + } + + token(const unary_operator* expression) noexcept + : type_(token_kind::unary_operator), + unary_operator_(expression) + { + } + + token(const binary_operator* expression) noexcept + : type_(token_kind::binary_operator), + binary_operator_(expression) + { + } + + token(const function_base* function) noexcept + : type_(token_kind::function), + function_(function) + { + } + + token(argument_arg_t) noexcept + : type_(token_kind::argument) + { + } + + token(begin_expression_type_arg_t) noexcept + : type_(token_kind::begin_expression_type) + { + } + + token(end_expression_type_arg_t) noexcept + : type_(token_kind::end_expression_type) + { + } + + token(literal_arg_t, Json&& value) noexcept + : type_(token_kind::literal), value_(std::move(value)) + { + } + + token(token&& other) noexcept + { + construct(std::forward<token>(other)); + } + + token& operator=(token&& other) + { + if (&other != this) + { + if (type_ == other.type_) + { + switch (type_) + { + case token_kind::expression: + expression_ = std::move(other.expression_); + break; + case token_kind::key: + key_ = std::move(other.key_); + break; + case token_kind::unary_operator: + unary_operator_ = other.unary_operator_; + break; + case token_kind::binary_operator: + binary_operator_ = other.binary_operator_; + break; + case token_kind::function: + function_ = other.function_; + break; + case token_kind::literal: + value_ = std::move(other.value_); + break; + default: + break; + } + } + else + { + destroy(); + construct(std::forward<token>(other)); + } + } + return *this; + } + + ~token() noexcept + { + destroy(); + } + + token_kind type() const + { + return type_; + } + + bool is_lparen() const + { + return type_ == token_kind::lparen; + } + + bool is_lbrace() const + { + return type_ == token_kind::begin_multi_select_hash; + } + + bool is_key() const + { + return type_ == token_kind::key; + } + + bool is_rparen() const + { + return type_ == token_kind::rparen; + } + + bool is_current_node() const + { + return type_ == token_kind::current_node; + } + + bool is_projection() const + { + return type_ == token_kind::expression && expression_->is_projection(); + } + + bool is_expression() const + { + return type_ == token_kind::expression; + } + + bool is_operator() const + { + return type_ == token_kind::unary_operator || + type_ == token_kind::binary_operator; + } + + std::size_t precedence_level() const + { + switch(type_) + { + case token_kind::unary_operator: + return unary_operator_->precedence_level(); + case token_kind::binary_operator: + return binary_operator_->precedence_level(); + case token_kind::expression: + return expression_->precedence_level(); + default: + return 0; + } + } + + jsoncons::optional<std::size_t> arity() const + { + return type_ == token_kind::function ? function_->arity() : jsoncons::optional<std::size_t>(); + } + + bool is_right_associative() const + { + switch(type_) + { + case token_kind::unary_operator: + return unary_operator_->is_right_associative(); + case token_kind::binary_operator: + return binary_operator_->is_right_associative(); + case token_kind::expression: + return expression_->is_right_associative(); + default: + return false; + } + } + + void construct(token&& other) + { + type_ = other.type_; + switch (type_) + { + case token_kind::expression: + new (&expression_) std::unique_ptr<expression_base>(std::move(other.expression_)); + break; + case token_kind::key: + new (&key_) string_type(std::move(other.key_)); + break; + case token_kind::unary_operator: + unary_operator_ = other.unary_operator_; + break; + case token_kind::binary_operator: + binary_operator_ = other.binary_operator_; + break; + case token_kind::function: + function_ = other.function_; + break; + case token_kind::literal: + new (&value_) Json(std::move(other.value_)); + break; + default: + break; + } + } + + void destroy() noexcept + { + switch(type_) + { + case token_kind::expression: + expression_.~unique_ptr(); + break; + case token_kind::key: + key_.~basic_string(); + break; + case token_kind::literal: + value_.~Json(); + break; + default: + break; + } + } + + std::string to_string(std::size_t indent = 0) const + { + switch(type_) + { + case token_kind::expression: + return expression_->to_string(indent); + break; + case token_kind::unary_operator: + return std::string("unary_operator"); + break; + case token_kind::binary_operator: + return binary_operator_->to_string(indent); + break; + case token_kind::current_node: + return std::string("current_node"); + break; + case token_kind::end_function: + return std::string("end_function"); + break; + case token_kind::separator: + return std::string("separator"); + break; + case token_kind::literal: + return std::string("literal"); + break; + case token_kind::key: + return std::string("key") + key_; + break; + case token_kind::begin_multi_select_hash: + return std::string("begin_multi_select_hash"); + break; + case token_kind::begin_multi_select_list: + return std::string("begin_multi_select_list"); + break; + case token_kind::begin_filter: + return std::string("begin_filter"); + break; + case token_kind::pipe: + return std::string("pipe"); + break; + case token_kind::lparen: + return std::string("lparen"); + break; + case token_kind::function: + return function_->to_string(); + case token_kind::argument: + return std::string("argument"); + break; + case token_kind::begin_expression_type: + return std::string("begin_expression_type"); + break; + case token_kind::end_expression_type: + return std::string("end_expression_type"); + break; + default: + return std::string("default"); + break; + } + } + }; + + static pointer evaluate_tokens(reference doc, const std::vector<token>& output_stack, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) + { + pointer root_ptr = std::addressof(doc); + std::vector<parameter> stack; + std::vector<parameter> arg_stack; + for (std::size_t i = 0; i < output_stack.size(); ++i) + { + auto& t = output_stack[i]; + switch (t.type()) + { + case token_kind::literal: + { + stack.emplace_back(t.value_); + break; + } + case token_kind::begin_expression_type: + { + JSONCONS_ASSERT(i+1 < output_stack.size()); + ++i; + JSONCONS_ASSERT(output_stack[i].is_expression()); + JSONCONS_ASSERT(!stack.empty()); + stack.pop_back(); + stack.emplace_back(output_stack[i].expression_.get()); + break; + } + case token_kind::pipe: + { + JSONCONS_ASSERT(!stack.empty()); + root_ptr = std::addressof(stack.back().value()); + break; + } + case token_kind::current_node: + stack.emplace_back(*root_ptr); + break; + case token_kind::expression: + { + JSONCONS_ASSERT(!stack.empty()); + pointer ptr = std::addressof(stack.back().value()); + stack.pop_back(); + auto& ref = t.expression_->evaluate(*ptr, resources, ec); + stack.emplace_back(ref); + break; + } + case token_kind::unary_operator: + { + JSONCONS_ASSERT(stack.size() >= 1); + pointer ptr = std::addressof(stack.back().value()); + stack.pop_back(); + reference r = t.unary_operator_->evaluate(*ptr, resources, ec); + stack.emplace_back(r); + break; + } + case token_kind::binary_operator: + { + JSONCONS_ASSERT(stack.size() >= 2); + pointer rhs = std::addressof(stack.back().value()); + stack.pop_back(); + pointer lhs = std::addressof(stack.back().value()); + stack.pop_back(); + reference r = t.binary_operator_->evaluate(*lhs,*rhs, resources, ec); + stack.emplace_back(r); + break; + } + case token_kind::argument: + { + JSONCONS_ASSERT(!stack.empty()); + arg_stack.push_back(std::move(stack.back())); + stack.pop_back(); + break; + } + case token_kind::function: + { + if (t.function_->arity() && *(t.function_->arity()) != arg_stack.size()) + { + ec = jmespath_errc::invalid_arity; + return std::addressof(resources.null_value()); + } + + reference r = t.function_->evaluate(arg_stack, resources, ec); + if (ec) + { + return std::addressof(resources.null_value()); + } + arg_stack.clear(); + stack.emplace_back(r); + break; + } + default: + break; + } + } + JSONCONS_ASSERT(stack.size() == 1); + return std::addressof(stack.back().value()); + } + + // Implementations + + class or_operator final : public binary_operator + { + public: + or_operator() + : binary_operator(operator_kind::or_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override + { + if (lhs.is_null() && rhs.is_null()) + { + return resources.null_value(); + } + if (!is_false(lhs)) + { + return lhs; + } + else + { + return rhs; + } + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("or_operator\n"); + return s; + } + }; + + class and_operator final : public binary_operator + { + public: + and_operator() + : binary_operator(operator_kind::and_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>&, std::error_code&) const override + { + if (is_true(lhs)) + { + return rhs; + } + else + { + return lhs; + } + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("and_operator\n"); + return s; + } + }; + + class eq_operator final : public binary_operator + { + public: + eq_operator() + : binary_operator(operator_kind::eq_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override + { + return lhs == rhs ? resources.true_value() : resources.false_value(); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("eq_operator\n"); + return s; + } + }; + + class ne_operator final : public binary_operator + { + public: + ne_operator() + : binary_operator(operator_kind::ne_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override + { + return lhs != rhs ? resources.true_value() : resources.false_value(); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("ne_operator\n"); + return s; + } + }; + + class lt_operator final : public binary_operator + { + public: + lt_operator() + : binary_operator(operator_kind::lt_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override + { + if (!(lhs.is_number() && rhs.is_number())) + { + return resources.null_value(); + } + return lhs < rhs ? resources.true_value() : resources.false_value(); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("lt_operator\n"); + return s; + } + }; + + class lte_operator final : public binary_operator + { + public: + lte_operator() + : binary_operator(operator_kind::lte_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override + { + if (!(lhs.is_number() && rhs.is_number())) + { + return resources.null_value(); + } + return lhs <= rhs ? resources.true_value() : resources.false_value(); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("lte_operator\n"); + return s; + } + }; + + class gt_operator final : public binary_operator + { + public: + gt_operator() + : binary_operator(operator_kind::gt_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override + { + if (!(lhs.is_number() && rhs.is_number())) + { + return resources.null_value(); + } + return lhs > rhs ? resources.true_value() : resources.false_value(); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("gt_operator\n"); + return s; + } + }; + + class gte_operator final : public binary_operator + { + public: + gte_operator() + : binary_operator(operator_kind::gte_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override + { + if (!(lhs.is_number() && rhs.is_number())) + { + return resources.null_value(); + } + return lhs >= rhs ? resources.true_value() : resources.false_value(); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("gte_operator\n"); + return s; + } + }; + + // basic_expression + class basic_expression : public expression_base + { + public: + basic_expression() + : expression_base(operator_kind::default_op, false) + { + } + + void add_expression(std::unique_ptr<expression_base>&&) override + { + } + }; + + class identifier_selector final : public basic_expression + { + private: + string_type identifier_; + public: + identifier_selector(const string_view_type& name) + : identifier_(name) + { + } + + reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override + { + //std::cout << "(identifier_selector " << identifier_ << " ) " << pretty_print(val) << "\n"; + if (val.is_object() && val.contains(identifier_)) + { + return val.at(identifier_); + } + else + { + return resources.null_value(); + } + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("identifier_selector "); + s.append(identifier_); + return s; + } + }; + + class current_node final : public basic_expression + { + public: + current_node() + { + } + + reference evaluate(reference val, dynamic_resources<Json,JsonReference>&, std::error_code&) const override + { + return val; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("current_node "); + return s; + } + }; + + class index_selector final : public basic_expression + { + int64_t index_; + public: + index_selector(int64_t index) + : index_(index) + { + } + + reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override + { + if (!val.is_array()) + { + return resources.null_value(); + } + int64_t slen = static_cast<int64_t>(val.size()); + if (index_ >= 0 && index_ < slen) + { + std::size_t index = static_cast<std::size_t>(index_); + return val.at(index); + } + else if ((slen + index_) >= 0 && (slen+index_) < slen) + { + std::size_t index = static_cast<std::size_t>(slen + index_); + return val.at(index); + } + else + { + return resources.null_value(); + } + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("index_selector "); + s.append(std::to_string(index_)); + return s; + } + }; + + // projection_base + class projection_base : public expression_base + { + protected: + std::vector<std::unique_ptr<expression_base>> expressions_; + public: + projection_base(operator_kind oper) + : expression_base(oper, true) + { + } + + void add_expression(std::unique_ptr<expression_base>&& expr) override + { + if (!expressions_.empty() && expressions_.back()->is_projection() && + (expr->precedence_level() < expressions_.back()->precedence_level() || + (expr->precedence_level() == expressions_.back()->precedence_level() && expr->is_right_associative()))) + { + expressions_.back()->add_expression(std::move(expr)); + } + else + { + expressions_.emplace_back(std::move(expr)); + } + } + + reference apply_expressions(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const + { + pointer ptr = std::addressof(val); + for (auto& expression : expressions_) + { + ptr = std::addressof(expression->evaluate(*ptr, resources, ec)); + } + return *ptr; + } + }; + + class object_projection final : public projection_base + { + public: + object_projection() + : projection_base(operator_kind::projection_op) + { + } + + reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + if (!val.is_object()) + { + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + for (auto& item : val.object_range()) + { + if (!item.value().is_null()) + { + reference j = this->apply_expressions(item.value(), resources, ec); + if (!j.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + } + } + return *result; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("object_projection\n"); + for (auto& expr : this->expressions_) + { + std::string sss = expr->to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + return s; + } + }; + + class list_projection final : public projection_base + { + public: + list_projection() + : projection_base(operator_kind::projection_op) + { + } + + reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + if (!val.is_array()) + { + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + for (reference item : val.array_range()) + { + if (!item.is_null()) + { + reference j = this->apply_expressions(item, resources, ec); + if (!j.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + } + } + return *result; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("list_projection\n"); + for (auto& expr : this->expressions_) + { + std::string sss = expr->to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + return s; + } + }; + + class slice_projection final : public projection_base + { + slice slice_; + public: + slice_projection(const slice& s) + : projection_base(operator_kind::projection_op), slice_(s) + { + } + + reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + if (!val.is_array()) + { + return resources.null_value(); + } + + auto start = slice_.get_start(val.size()); + auto end = slice_.get_stop(val.size()); + auto step = slice_.step(); + + if (step == 0) + { + ec = jmespath_errc::step_cannot_be_zero; + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + if (step > 0) + { + if (start < 0) + { + start = 0; + } + if (end > static_cast<int64_t>(val.size())) + { + end = val.size(); + } + for (int64_t i = start; i < end; i += step) + { + reference j = this->apply_expressions(val.at(static_cast<std::size_t>(i)), resources, ec); + if (!j.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + } + } + else + { + if (start >= static_cast<int64_t>(val.size())) + { + start = static_cast<int64_t>(val.size()) - 1; + } + if (end < -1) + { + end = -1; + } + for (int64_t i = start; i > end; i += step) + { + reference j = this->apply_expressions(val.at(static_cast<std::size_t>(i)), resources, ec); + if (!j.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + } + } + + return *result; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("slice_projection\n"); + for (auto& expr : this->expressions_) + { + std::string sss = expr->to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + return s; + } + }; + + class filter_expression final : public projection_base + { + std::vector<token> token_list_; + public: + filter_expression(std::vector<token>&& token_list) + : projection_base(operator_kind::projection_op), token_list_(std::move(token_list)) + { + } + + reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + if (!val.is_array()) + { + return resources.null_value(); + } + auto result = resources.create_json(json_array_arg); + + for (auto& item : val.array_range()) + { + Json j(json_const_pointer_arg, evaluate_tokens(item, token_list_, resources, ec)); + if (is_true(j)) + { + reference jj = this->apply_expressions(item, resources, ec); + if (!jj.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(jj)); + } + } + } + return *result; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("filter_expression\n"); + for (auto& item : token_list_) + { + std::string sss = item.to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + return s; + } + }; + + class flatten_projection final : public projection_base + { + public: + flatten_projection() + : projection_base(operator_kind::flatten_projection_op) + { + } + + reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + if (!val.is_array()) + { + return resources.null_value(); + } + + auto result = resources.create_json(json_array_arg); + for (reference current_elem : val.array_range()) + { + if (current_elem.is_array()) + { + for (reference elem : current_elem.array_range()) + { + if (!elem.is_null()) + { + reference j = this->apply_expressions(elem, resources, ec); + if (!j.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + } + } + } + else + { + if (!current_elem.is_null()) + { + reference j = this->apply_expressions(current_elem, resources, ec); + if (!j.is_null()) + { + result->emplace_back(json_const_pointer_arg, std::addressof(j)); + } + } + } + } + return *result; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("flatten_projection\n"); + for (auto& expr : this->expressions_) + { + std::string sss = expr->to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + return s; + } + }; + + class multi_select_list final : public basic_expression + { + std::vector<std::vector<token>> token_lists_; + public: + multi_select_list(std::vector<std::vector<token>>&& token_lists) + : token_lists_(std::move(token_lists)) + { + } + + reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + if (val.is_null()) + { + return val; + } + auto result = resources.create_json(json_array_arg); + result->reserve(token_lists_.size()); + + for (auto& list : token_lists_) + { + result->emplace_back(json_const_pointer_arg, evaluate_tokens(val, list, resources, ec)); + } + return *result; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("multi_select_list\n"); + for (auto& list : token_lists_) + { + for (auto& item : list) + { + std::string sss = item.to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + s.append("---\n"); + } + return s; + } + }; + + struct key_tokens + { + string_type key; + std::vector<token> tokens; + + key_tokens(string_type&& key, std::vector<token>&& tokens) noexcept + : key(std::move(key)), tokens(std::move(tokens)) + { + } + }; + + class multi_select_hash final : public basic_expression + { + public: + std::vector<key_tokens> key_toks_; + + multi_select_hash(std::vector<key_tokens>&& key_toks) + : key_toks_(std::move(key_toks)) + { + } + + reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + if (val.is_null()) + { + return val; + } + auto resultp = resources.create_json(json_object_arg); + resultp->reserve(key_toks_.size()); + for (auto& item : key_toks_) + { + resultp->try_emplace(item.key, json_const_pointer_arg, evaluate_tokens(val, item.tokens, resources, ec)); + } + + return *resultp; + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("multi_select_list\n"); + return s; + } + }; + + class function_expression final : public basic_expression + { + public: + std::vector<token> toks_; + + function_expression(std::vector<token>&& toks) + : toks_(std::move(toks)) + { + } + + reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override + { + return *evaluate_tokens(val, toks_, resources, ec); + } + + std::string to_string(std::size_t indent = 0) const override + { + std::string s; + for (std::size_t i = 0; i <= indent; ++i) + { + s.push_back(' '); + } + s.append("function_expression\n"); + for (auto& tok : toks_) + { + for (std::size_t i = 0; i <= indent+2; ++i) + { + s.push_back(' '); + } + std::string sss = tok.to_string(indent+2); + s.insert(s.end(), sss.begin(), sss.end()); + s.push_back('\n'); + } + return s; + } + }; + + class static_resources + { + std::vector<std::unique_ptr<Json>> temp_storage_; + + public: + + static_resources() = default; + static_resources(const static_resources& expr) = delete; + static_resources& operator=(const static_resources& expr) = delete; + static_resources(static_resources&& expr) = default; + static_resources& operator=(static_resources&& expr) = default; + + const function_base* get_function(const string_type& name, std::error_code& ec) const + { + static abs_function abs_func; + static avg_function avg_func; + static ceil_function ceil_func; + static contains_function contains_func; + static ends_with_function ends_with_func; + static floor_function floor_func; + static join_function join_func; + static length_function length_func; + static max_function max_func; + static max_by_function max_by_func; + static map_function map_func; + static merge_function merge_func; + static min_function min_func; + static min_by_function min_by_func; + static type_function type_func; + static sort_function sort_func; + static sort_by_function sort_by_func; + static keys_function keys_func; + static values_function values_func; + static reverse_function reverse_func; + static starts_with_function starts_with_func; + static const sum_function sum_func; + static to_array_function to_array_func; + static to_number_function to_number_func; + static to_string_function to_string_func; + static not_null_function not_null_func; + + using function_dictionary = std::unordered_map<string_type,const function_base*>; + static const function_dictionary functions_ = + { + {string_type{'a','b','s'}, &abs_func}, + {string_type{'a','v','g'}, &avg_func}, + {string_type{'c','e','i', 'l'}, &ceil_func}, + {string_type{'c','o','n', 't', 'a', 'i', 'n', 's'}, &contains_func}, + {string_type{'e','n','d', 's', '_', 'w', 'i', 't', 'h'}, &ends_with_func}, + {string_type{'f','l','o', 'o', 'r'}, &floor_func}, + {string_type{'j','o','i', 'n'}, &join_func}, + {string_type{'l','e','n', 'g', 't', 'h'}, &length_func}, + {string_type{'m','a','x'}, &max_func}, + {string_type{'m','a','x','_','b','y'}, &max_by_func}, + {string_type{'m','a','p'}, &map_func}, + {string_type{'m','i','n'}, &min_func}, + {string_type{'m','i','n','_','b','y'}, &min_by_func}, + {string_type{'m','e','r', 'g', 'e'}, &merge_func}, + {string_type{'t','y','p', 'e'}, &type_func}, + {string_type{'s','o','r', 't'}, &sort_func}, + {string_type{'s','o','r', 't','_','b','y'}, &sort_by_func}, + {string_type{'k','e','y', 's'}, &keys_func}, + {string_type{'v','a','l', 'u','e','s'}, &values_func}, + {string_type{'r','e','v', 'e', 'r', 's','e'}, &reverse_func}, + {string_type{'s','t','a', 'r','t','s','_','w','i','t','h'}, &starts_with_func}, + {string_type{'s','u','m'}, &sum_func}, + {string_type{'t','o','_','a','r','r','a','y',}, &to_array_func}, + {string_type{'t','o','_', 'n', 'u', 'm','b','e','r'}, &to_number_func}, + {string_type{'t','o','_', 's', 't', 'r','i','n','g'}, &to_string_func}, + {string_type{'n','o','t', '_', 'n', 'u','l','l'}, ¬_null_func} + }; + auto it = functions_.find(name); + if (it == functions_.end()) + { + ec = jmespath_errc::unknown_function; + return nullptr; + } + return it->second; + } + + const unary_operator* get_not_operator() const + { + static const not_expression not_oper; + + return ¬_oper; + } + + const binary_operator* get_or_operator() const + { + static const or_operator or_oper; + + return &or_oper; + } + + const binary_operator* get_and_operator() const + { + static const and_operator and_oper; + + return &and_oper; + } + + const binary_operator* get_eq_operator() const + { + static const eq_operator eq_oper; + return &eq_oper; + } + + const binary_operator* get_ne_operator() const + { + static const ne_operator ne_oper; + return &ne_oper; + } + + const binary_operator* get_lt_operator() const + { + static const lt_operator lt_oper; + return <_oper; + } + + const binary_operator* get_lte_operator() const + { + static const lte_operator lte_oper; + return <e_oper; + } + + const binary_operator* get_gt_operator() const + { + static const gt_operator gt_oper; + return >_oper; + } + + const binary_operator* get_gte_operator() const + { + static const gte_operator gte_oper; + return >e_oper; + } + }; + + class jmespath_expression + { + static_resources resources_; + std::vector<token> output_stack_; + public: + jmespath_expression() + { + } + + jmespath_expression(const jmespath_expression& expr) = delete; + jmespath_expression& operator=(const jmespath_expression& expr) = delete; + + jmespath_expression(jmespath_expression&& expr) + : resources_(std::move(expr.resources_)), + output_stack_(std::move(expr.output_stack_)) + { + } + + jmespath_expression(static_resources&& resources, + std::vector<token>&& output_stack) + : resources_(std::move(resources)), output_stack_(std::move(output_stack)) + { + } + + Json evaluate(reference doc) + { + if (output_stack_.empty()) + { + return Json::null(); + } + std::error_code ec; + Json result = evaluate(doc, ec); + if (ec) + { + JSONCONS_THROW(jmespath_error(ec)); + } + return result; + } + + Json evaluate(reference doc, std::error_code& ec) + { + if (output_stack_.empty()) + { + return Json::null(); + } + dynamic_resources<Json,JsonReference> dynamic_storage; + return deep_copy(*evaluate_tokens(doc, output_stack_, dynamic_storage, ec)); + } + + static jmespath_expression compile(const string_view_type& expr) + { + jsoncons::jmespath::detail::jmespath_evaluator<Json,const Json&> evaluator; + std::error_code ec; + jmespath_expression result = evaluator.compile(expr.data(), expr.size(), ec); + if (ec) + { + JSONCONS_THROW(jmespath_error(ec, evaluator.line(), evaluator.column())); + } + return result; + } + + static jmespath_expression compile(const string_view_type& expr, + std::error_code& ec) + { + jsoncons::jmespath::detail::jmespath_evaluator<Json,const Json&> evaluator; + return evaluator.compile(expr.data(), expr.size(), ec); + } + }; + private: + std::size_t line_; + std::size_t column_; + const char_type* begin_input_; + const char_type* end_input_; + const char_type* p_; + + static_resources resources_; + std::vector<path_state> state_stack_; + + std::vector<token> output_stack_; + std::vector<token> operator_stack_; + + public: + jmespath_evaluator() + : line_(1), column_(1), + begin_input_(nullptr), end_input_(nullptr), + p_(nullptr) + { + } + + std::size_t line() const + { + return line_; + } + + std::size_t column() const + { + return column_; + } + + jmespath_expression compile(const char_type* path, + std::size_t length, + std::error_code& ec) + { + push_token(current_node_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.emplace_back(path_state::start); + + string_type buffer; + uint32_t cp = 0; + uint32_t cp2 = 0; + + begin_input_ = path; + end_input_ = path + length; + p_ = begin_input_; + + slice slic{}; + + while (p_ < end_input_) + { + switch (state_stack_.back()) + { + case path_state::start: + { + state_stack_.back() = path_state::rhs_expression; + state_stack_.emplace_back(path_state::lhs_expression); + break; + } + case path_state::rhs_expression: + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '.': + ++p_; + ++column_; + state_stack_.emplace_back(path_state::sub_expression); + break; + case '|': + ++p_; + ++column_; + state_stack_.emplace_back(path_state::lhs_expression); + state_stack_.emplace_back(path_state::expect_pipe_or_or); + break; + case '&': + ++p_; + ++column_; + state_stack_.emplace_back(path_state::lhs_expression); + state_stack_.emplace_back(path_state::expect_and); + break; + case '<': + case '>': + case '=': + { + state_stack_.emplace_back(path_state::comparator_expression); + break; + } + case '!': + { + ++p_; + ++column_; + state_stack_.emplace_back(path_state::lhs_expression); + state_stack_.emplace_back(path_state::cmp_ne); + break; + } + case ')': + { + state_stack_.pop_back(); + break; + } + case '[': + state_stack_.emplace_back(path_state::bracket_specifier); + ++p_; + ++column_; + break; + default: + if (state_stack_.size() > 1) + { + state_stack_.pop_back(); + } + else + { + ec = jmespath_errc::syntax_error; + return jmespath_expression(); + } + break; + } + break; + case path_state::comparator_expression: + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '<': + ++p_; + ++column_; + state_stack_.back() = path_state::lhs_expression; + state_stack_.emplace_back(path_state::cmp_lt_or_lte); + break; + case '>': + ++p_; + ++column_; + state_stack_.back() = path_state::lhs_expression; + state_stack_.emplace_back(path_state::cmp_gt_or_gte); + break; + case '=': + { + ++p_; + ++column_; + state_stack_.back() = path_state::lhs_expression; + state_stack_.emplace_back(path_state::cmp_eq); + break; + } + default: + if (state_stack_.size() > 1) + { + state_stack_.pop_back(); + } + else + { + ec = jmespath_errc::syntax_error; + return jmespath_expression(); + } + break; + } + break; + case path_state::lhs_expression: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '\"': + state_stack_.back() = path_state::val_expr; + state_stack_.emplace_back(path_state::quoted_string); + ++p_; + ++column_; + break; + case '\'': + state_stack_.back() = path_state::raw_string; + ++p_; + ++column_; + break; + case '`': + state_stack_.back() = path_state::literal; + ++p_; + ++column_; + break; + case '{': + push_token(begin_multi_select_hash_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::multi_select_hash; + ++p_; + ++column_; + break; + case '*': // wildcard + push_token(token(jsoncons::make_unique<object_projection>()), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + case '(': + { + ++p_; + ++column_; + push_token(lparen_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::expect_rparen; + state_stack_.emplace_back(path_state::rhs_expression); + state_stack_.emplace_back(path_state::lhs_expression); + break; + } + case '!': + { + ++p_; + ++column_; + push_token(token(resources_.get_not_operator()), ec); + if (ec) {return jmespath_expression();} + break; + } + case '@': + ++p_; + ++column_; + push_token(token(jsoncons::make_unique<current_node>()), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + case '[': + state_stack_.back() = path_state::bracket_specifier_or_multi_select_list; + ++p_; + ++column_; + break; + default: + if ((*p_ >= 'A' && *p_ <= 'Z') || (*p_ >= 'a' && *p_ <= 'z') || (*p_ == '_')) + { + state_stack_.back() = path_state::identifier_or_function_expr; + state_stack_.emplace_back(path_state::unquoted_string); + buffer.push_back(*p_); + ++p_; + ++column_; + } + else + { + ec = jmespath_errc::expected_identifier; + return jmespath_expression(); + } + break; + }; + break; + } + case path_state::sub_expression: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '\"': + state_stack_.back() = path_state::val_expr; + state_stack_.emplace_back(path_state::quoted_string); + ++p_; + ++column_; + break; + case '{': + push_token(begin_multi_select_hash_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::multi_select_hash; + ++p_; + ++column_; + break; + case '*': + push_token(token(jsoncons::make_unique<object_projection>()), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + case '[': + state_stack_.back() = path_state::expect_multi_select_list; + ++p_; + ++column_; + break; + default: + if ((*p_ >= 'A' && *p_ <= 'Z') || (*p_ >= 'a' && *p_ <= 'z') || (*p_ == '_')) + { + state_stack_.back() = path_state::identifier_or_function_expr; + state_stack_.emplace_back(path_state::unquoted_string); + buffer.push_back(*p_); + ++p_; + ++column_; + } + else + { + ec = jmespath_errc::expected_identifier; + return jmespath_expression(); + } + break; + }; + break; + } + case path_state::key_expr: + push_token(token(key_arg, buffer), ec); + if (ec) {return jmespath_expression();} + buffer.clear(); + state_stack_.pop_back(); + break; + case path_state::val_expr: + push_token(token(jsoncons::make_unique<identifier_selector>(buffer)), ec); + if (ec) {return jmespath_expression();} + buffer.clear(); + state_stack_.pop_back(); + break; + case path_state::expression_or_expression_type: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '&': + push_token(token(begin_expression_type_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::expression_type; + state_stack_.emplace_back(path_state::rhs_expression); + state_stack_.emplace_back(path_state::lhs_expression); + ++p_; + ++column_; + break; + default: + state_stack_.back() = path_state::argument; + state_stack_.emplace_back(path_state::rhs_expression); + state_stack_.emplace_back(path_state::lhs_expression); + break; + } + break; + case path_state::identifier_or_function_expr: + switch(*p_) + { + case '(': + { + auto f = resources_.get_function(buffer, ec); + if (ec) + { + return jmespath_expression(); + } + buffer.clear(); + push_token(token(f), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::function_expression; + state_stack_.emplace_back(path_state::expression_or_expression_type); + ++p_; + ++column_; + break; + } + default: + { + push_token(token(jsoncons::make_unique<identifier_selector>(buffer)), ec); + if (ec) {return jmespath_expression();} + buffer.clear(); + state_stack_.pop_back(); + break; + } + } + break; + + case path_state::function_expression: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case ',': + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.emplace_back(path_state::expression_or_expression_type); + ++p_; + ++column_; + break; + case ')': + { + push_token(token(end_function_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + } + default: + break; + } + break; + + case path_state::argument: + push_token(argument_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + + case path_state::expression_type: + push_token(end_expression_type_arg, ec); + push_token(argument_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + + case path_state::quoted_string: + switch (*p_) + { + case '\"': + state_stack_.pop_back(); // quoted_string + ++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::unquoted_string: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + state_stack_.pop_back(); // unquoted_string + advance_past_space_character(ec); + break; + default: + if ((*p_ >= '0' && *p_ <= '9') || (*p_ >= 'A' && *p_ <= 'Z') || (*p_ >= 'a' && *p_ <= 'z') || (*p_ == '_')) + { + buffer.push_back(*p_); + ++p_; + ++column_; + } + else + { + state_stack_.pop_back(); // unquoted_string + } + break; + }; + break; + case path_state::raw_string_escape_char: + switch (*p_) + { + case '\'': + buffer.push_back(*p_); + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + buffer.push_back('\\'); + buffer.push_back(*p_); + state_stack_.pop_back(); + ++p_; + ++column_; + break; + } + 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 '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 = jmespath_errc::illegal_escaped_character; + return jmespath_expression(); + } + break; + case path_state::escape_u1: + cp = append_to_codepoint(0, *p_, ec); + if (ec) + { + return jmespath_expression(); + } + ++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 jmespath_expression(); + } + ++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 jmespath_expression(); + } + ++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 jmespath_expression(); + } + 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 = jmespath_errc::invalid_codepoint; + return jmespath_expression(); + } + break; + case path_state::escape_expect_surrogate_pair2: + switch (*p_) + { + case 'u': + ++p_; + ++column_; + state_stack_.back() = path_state::escape_u5; + break; + default: + ec = jmespath_errc::invalid_codepoint; + return jmespath_expression(); + } + break; + case path_state::escape_u5: + cp2 = append_to_codepoint(0, *p_, ec); + if (ec) + { + return jmespath_expression(); + } + ++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 jmespath_expression(); + } + ++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 jmespath_expression(); + } + ++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 jmespath_expression(); + } + 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::raw_string: + switch (*p_) + { + case '\'': + { + push_token(token(literal_arg, Json(buffer)), ec); + if (ec) {return jmespath_expression();} + buffer.clear(); + state_stack_.pop_back(); // raw_string + ++p_; + ++column_; + break; + } + case '\\': + state_stack_.emplace_back(path_state::raw_string_escape_char); + ++p_; + ++column_; + break; + default: + buffer.push_back(*p_); + ++p_; + ++column_; + break; + }; + break; + case path_state::literal: + switch (*p_) + { + case '`': + { + json_decoder<Json> decoder; + basic_json_reader<char_type,string_source<char_type>> reader(buffer, decoder); + std::error_code parse_ec; + reader.read(parse_ec); + if (parse_ec) + { + ec = jmespath_errc::invalid_literal; + return jmespath_expression(); + } + auto j = decoder.get_result(); + + push_token(token(literal_arg, std::move(j)), ec); + if (ec) {return jmespath_expression();} + buffer.clear(); + state_stack_.pop_back(); // json_value + ++p_; + ++column_; + break; + } + case '\\': + if (p_+1 < end_input_) + { + ++p_; + ++column_; + if (*p_ != '`') + { + buffer.push_back('\\'); + } + buffer.push_back(*p_); + } + else + { + ec = jmespath_errc::unexpected_end_of_input; + return jmespath_expression(); + } + ++p_; + ++column_; + break; + default: + buffer.push_back(*p_); + ++p_; + ++column_; + break; + }; + break; + case path_state::number: + 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::bracket_specifier: + switch(*p_) + { + case '*': + push_token(token(jsoncons::make_unique<list_projection>()), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::expect_rbracket; + ++p_; + ++column_; + break; + case ']': // [] + push_token(token(jsoncons::make_unique<flatten_projection>()), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); // bracket_specifier + ++p_; + ++column_; + break; + case '?': + push_token(token(begin_filter_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::filter; + state_stack_.emplace_back(path_state::rhs_expression); + state_stack_.emplace_back(path_state::lhs_expression); + ++p_; + ++column_; + break; + case ':': // slice_expression + state_stack_.back() = path_state::rhs_slice_expression_stop ; + state_stack_.emplace_back(path_state::number); + ++p_; + ++column_; + break; + // number + 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_expression; + state_stack_.emplace_back(path_state::number); + break; + default: + ec = jmespath_errc::expected_index_expression; + return jmespath_expression(); + } + break; + case path_state::bracket_specifier_or_multi_select_list: + switch(*p_) + { + case '*': + if (p_+1 >= end_input_) + { + ec = jmespath_errc::unexpected_end_of_input; + return jmespath_expression(); + } + if (*(p_+1) == ']') + { + state_stack_.back() = path_state::bracket_specifier; + } + else + { + push_token(token(begin_multi_select_list_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::multi_select_list; + state_stack_.emplace_back(path_state::lhs_expression); + } + break; + case ']': // [] + case '?': + case ':': // slice_expression + 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::bracket_specifier; + break; + default: + push_token(token(begin_multi_select_list_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::multi_select_list; + state_stack_.emplace_back(path_state::lhs_expression); + break; + } + break; + + case path_state::expect_multi_select_list: + switch(*p_) + { + case ']': + case '?': + case ':': + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + ec = jmespath_errc::expected_multi_select_list; + return jmespath_expression(); + case '*': + push_token(token(jsoncons::make_unique<list_projection>()), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::expect_rbracket; + ++p_; + ++column_; + break; + default: + push_token(token(begin_multi_select_list_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::multi_select_list; + state_stack_.emplace_back(path_state::lhs_expression); + break; + } + break; + + case path_state::multi_select_hash: + switch(*p_) + { + case '*': + case ']': + case '?': + case ':': + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + break; + default: + state_stack_.back() = path_state::key_val_expr; + break; + } + break; + + case path_state::index_or_slice_expression: + switch(*p_) + { + case ']': + { + if (buffer.empty()) + { + push_token(token(jsoncons::make_unique<flatten_projection>()), ec); + if (ec) {return jmespath_expression();} + } + else + { + int64_t val{ 0 }; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); + if (!r) + { + ec = jmespath_errc::invalid_number; + return jmespath_expression(); + } + push_token(token(jsoncons::make_unique<index_selector>(val)), ec); + if (ec) {return jmespath_expression();} + + buffer.clear(); + } + state_stack_.pop_back(); // bracket_specifier + ++p_; + ++column_; + break; + } + case ':': + { + if (!buffer.empty()) + { + int64_t val; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); + if (!r) + { + ec = jmespath_errc::invalid_number; + return jmespath_expression(); + } + slic.start_ = val; + buffer.clear(); + } + state_stack_.back() = path_state::rhs_slice_expression_stop; + state_stack_.emplace_back(path_state::number); + ++p_; + ++column_; + break; + } + default: + ec = jmespath_errc::expected_rbracket; + return jmespath_expression(); + } + break; + case path_state::rhs_slice_expression_stop : + { + if (!buffer.empty()) + { + int64_t val{ 0 }; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); + if (!r) + { + ec = jmespath_errc::invalid_number; + return jmespath_expression(); + } + slic.stop_ = jsoncons::optional<int64_t>(val); + buffer.clear(); + } + switch(*p_) + { + case ']': + push_token(token(jsoncons::make_unique<slice_projection>(slic)), ec); + if (ec) {return jmespath_expression();} + slic = slice{}; + state_stack_.pop_back(); // bracket_specifier2 + ++p_; + ++column_; + break; + case ':': + state_stack_.back() = path_state::rhs_slice_expression_step; + state_stack_.emplace_back(path_state::number); + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_rbracket; + return jmespath_expression(); + } + break; + } + case path_state::rhs_slice_expression_step: + { + if (!buffer.empty()) + { + int64_t val{ 0 }; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); + if (!r) + { + ec = jmespath_errc::invalid_number; + return jmespath_expression(); + } + if (val == 0) + { + ec = jmespath_errc::step_cannot_be_zero; + return jmespath_expression(); + } + slic.step_ = val; + buffer.clear(); + } + switch(*p_) + { + case ']': + push_token(token(jsoncons::make_unique<slice_projection>(slic)), ec); + if (ec) {return jmespath_expression();} + buffer.clear(); + slic = slice{}; + state_stack_.pop_back(); // rhs_slice_expression_step + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_rbracket; + return jmespath_expression(); + } + break; + } + case path_state::expect_rbracket: + { + switch(*p_) + { + case ']': + state_stack_.pop_back(); // expect_rbracket + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_rbracket; + return jmespath_expression(); + } + break; + } + case path_state::expect_rparen: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case ')': + ++p_; + ++column_; + push_token(rparen_arg, ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::rhs_expression; + break; + default: + ec = jmespath_errc::expected_rparen; + return jmespath_expression(); + } + break; + case path_state::key_val_expr: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '\"': + state_stack_.back() = path_state::expect_colon; + state_stack_.emplace_back(path_state::key_expr); + state_stack_.emplace_back(path_state::quoted_string); + ++p_; + ++column_; + break; + case '\'': + state_stack_.back() = path_state::expect_colon; + state_stack_.emplace_back(path_state::raw_string); + ++p_; + ++column_; + break; + default: + if ((*p_ >= 'A' && *p_ <= 'Z') || (*p_ >= 'a' && *p_ <= 'z') || (*p_ == '_')) + { + state_stack_.back() = path_state::expect_colon; + state_stack_.emplace_back(path_state::key_expr); + state_stack_.emplace_back(path_state::unquoted_string); + buffer.push_back(*p_); + ++p_; + ++column_; + } + else + { + ec = jmespath_errc::expected_key; + return jmespath_expression(); + } + break; + }; + break; + } + case path_state::cmp_lt_or_lte: + { + switch(*p_) + { + case '=': + push_token(token(resources_.get_lte_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + push_token(token(resources_.get_lt_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + } + break; + } + case path_state::cmp_gt_or_gte: + { + switch(*p_) + { + case '=': + push_token(token(resources_.get_gte_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + push_token(token(resources_.get_gt_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + } + break; + } + case path_state::cmp_eq: + { + switch(*p_) + { + case '=': + push_token(token(resources_.get_eq_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_comparator; + return jmespath_expression(); + } + break; + } + case path_state::cmp_ne: + { + switch(*p_) + { + case '=': + push_token(token(resources_.get_ne_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_comparator; + return jmespath_expression(); + } + break; + } + case path_state::expect_dot: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case '.': + state_stack_.pop_back(); // expect_dot + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_dot; + return jmespath_expression(); + } + break; + } + case path_state::expect_pipe_or_or: + { + switch(*p_) + { + case '|': + push_token(token(resources_.get_or_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + push_token(token(pipe_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + } + break; + } + case path_state::expect_and: + { + switch(*p_) + { + case '&': + push_token(token(resources_.get_and_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); // expect_and + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_and; + return jmespath_expression(); + } + break; + } + case path_state::multi_select_list: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case ',': + push_token(token(separator_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.emplace_back(path_state::lhs_expression); + ++p_; + ++column_; + break; + case '[': + state_stack_.emplace_back(path_state::lhs_expression); + break; + case '.': + state_stack_.emplace_back(path_state::sub_expression); + ++p_; + ++column_; + break; + case '|': + { + ++p_; + ++column_; + state_stack_.emplace_back(path_state::lhs_expression); + state_stack_.emplace_back(path_state::expect_pipe_or_or); + break; + } + case ']': + { + push_token(token(end_multi_select_list_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + + ++p_; + ++column_; + break; + } + default: + ec = jmespath_errc::expected_rbracket; + return jmespath_expression(); + } + break; + } + case path_state::filter: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case ']': + { + push_token(token(end_filter_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + } + default: + ec = jmespath_errc::expected_rbracket; + return jmespath_expression(); + } + break; + } + case path_state::expect_rbrace: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case ',': + push_token(token(separator_arg), ec); + if (ec) {return jmespath_expression();} + state_stack_.back() = path_state::key_val_expr; + ++p_; + ++column_; + break; + case '[': + case '{': + state_stack_.emplace_back(path_state::lhs_expression); + break; + case '.': + state_stack_.emplace_back(path_state::sub_expression); + ++p_; + ++column_; + break; + case '}': + { + state_stack_.pop_back(); + push_token(end_multi_select_hash_arg, ec); + if (ec) {return jmespath_expression();} + ++p_; + ++column_; + break; + } + default: + ec = jmespath_errc::expected_rbrace; + return jmespath_expression(); + } + break; + } + case path_state::expect_colon: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec); + break; + case ':': + state_stack_.back() = path_state::expect_rbrace; + state_stack_.emplace_back(path_state::lhs_expression); + ++p_; + ++column_; + break; + default: + ec = jmespath_errc::expected_colon; + return jmespath_expression(); + } + break; + } + } + + } + + if (state_stack_.empty()) + { + ec = jmespath_errc::syntax_error; + return jmespath_expression(); + } + while (state_stack_.size() > 1) + { + switch (state_stack_.back()) + { + case path_state::rhs_expression: + if (state_stack_.size() > 1) + { + state_stack_.pop_back(); + } + else + { + ec = jmespath_errc::syntax_error; + return jmespath_expression(); + } + break; + case path_state::val_expr: + push_token(token(jsoncons::make_unique<identifier_selector>(buffer)), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + case path_state::identifier_or_function_expr: + push_token(token(jsoncons::make_unique<identifier_selector>(buffer)), ec); + if (ec) {return jmespath_expression();} + state_stack_.pop_back(); + break; + case path_state::unquoted_string: + state_stack_.pop_back(); + break; + default: + ec = jmespath_errc::syntax_error; + return jmespath_expression(); + break; + } + } + + if (!(state_stack_.size() == 1 && state_stack_.back() == path_state::rhs_expression)) + { + ec = jmespath_errc::unexpected_end_of_input; + return jmespath_expression(); + } + + state_stack_.pop_back(); + + push_token(end_of_expression_arg, ec); + if (ec) {return jmespath_expression();} + + //for (auto& t : output_stack_) + //{ + // std::cout << t.to_string() << std::endl; + //} + + return jmespath_expression(std::move(resources_), std::move(output_stack_)); + } + + void advance_past_space_character(std::error_code& ec) + { + switch (*p_) + { + case ' ':case '\t': + ++p_; + ++column_; + break; + case '\r': + if (p_+1 >= end_input_) + { + ec = jmespath_errc::unexpected_end_of_input; + return; + } + if (*(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 = jmespath_errc::unbalanced_parentheses; + return; + } + ++it; + operator_stack_.erase(it.base(),operator_stack_.end()); + } + + void push_token(token&& tok, std::error_code& ec) + { + switch (tok.type()) + { + case token_kind::end_filter: + { + unwind_rparen(ec); + std::vector<token> toks; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->type() != token_kind::begin_filter) + { + toks.emplace_back(std::move(*it)); + ++it; + } + if (it == output_stack_.rend()) + { + ec = jmespath_errc::unbalanced_braces; + return; + } + if (toks.back().type() != token_kind::literal) + { + toks.emplace_back(current_node_arg); + } + std::reverse(toks.begin(), toks.end()); + ++it; + output_stack_.erase(it.base(),output_stack_.end()); + + if (!output_stack_.empty() && output_stack_.back().is_projection() && + (tok.precedence_level() < output_stack_.back().precedence_level() || + (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative()))) + { + output_stack_.back().expression_->add_expression(jsoncons::make_unique<filter_expression>(std::move(toks))); + } + else + { + output_stack_.emplace_back(token(jsoncons::make_unique<filter_expression>(std::move(toks)))); + } + break; + } + case token_kind::end_multi_select_list: + { + unwind_rparen(ec); + std::vector<std::vector<token>> vals; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->type() != token_kind::begin_multi_select_list) + { + std::vector<token> toks; + do + { + toks.emplace_back(std::move(*it)); + ++it; + } while (it != output_stack_.rend() && it->type() != token_kind::begin_multi_select_list && it->type() != token_kind::separator); + if (it->type() == token_kind::separator) + { + ++it; + } + if (toks.back().type() != token_kind::literal) + { + toks.emplace_back(current_node_arg); + } + std::reverse(toks.begin(), toks.end()); + vals.emplace_back(std::move(toks)); + } + if (it == output_stack_.rend()) + { + ec = jmespath_errc::unbalanced_braces; + return; + } + ++it; + output_stack_.erase(it.base(),output_stack_.end()); + std::reverse(vals.begin(), vals.end()); + if (!output_stack_.empty() && output_stack_.back().is_projection() && + (tok.precedence_level() < output_stack_.back().precedence_level() || + (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative()))) + { + output_stack_.back().expression_->add_expression(jsoncons::make_unique<multi_select_list>(std::move(vals))); + } + else + { + output_stack_.emplace_back(token(jsoncons::make_unique<multi_select_list>(std::move(vals)))); + } + break; + } + case token_kind::end_multi_select_hash: + { + unwind_rparen(ec); + std::vector<key_tokens> key_toks; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->type() != token_kind::begin_multi_select_hash) + { + std::vector<token> toks; + do + { + toks.emplace_back(std::move(*it)); + ++it; + } while (it != output_stack_.rend() && it->type() != token_kind::key); + JSONCONS_ASSERT(it->is_key()); + auto key = std::move(it->key_); + ++it; + if (it->type() == token_kind::separator) + { + ++it; + } + if (toks.back().type() != token_kind::literal) + { + toks.emplace_back(current_node_arg); + } + std::reverse(toks.begin(), toks.end()); + key_toks.emplace_back(std::move(key), std::move(toks)); + } + if (it == output_stack_.rend()) + { + ec = jmespath_errc::unbalanced_braces; + return; + } + std::reverse(key_toks.begin(), key_toks.end()); + ++it; + output_stack_.erase(it.base(),output_stack_.end()); + + if (!output_stack_.empty() && output_stack_.back().is_projection() && + (tok.precedence_level() < output_stack_.back().precedence_level() || + (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative()))) + { + output_stack_.back().expression_->add_expression(jsoncons::make_unique<multi_select_hash>(std::move(key_toks))); + } + else + { + output_stack_.emplace_back(token(jsoncons::make_unique<multi_select_hash>(std::move(key_toks)))); + } + break; + } + case token_kind::end_expression_type: + { + std::vector<token> toks; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->type() != token_kind::begin_expression_type) + { + toks.emplace_back(std::move(*it)); + ++it; + } + if (it == output_stack_.rend()) + { + JSONCONS_THROW(json_runtime_error<std::runtime_error>("Unbalanced braces")); + } + if (toks.back().type() != token_kind::literal) + { + toks.emplace_back(current_node_arg); + } + std::reverse(toks.begin(), toks.end()); + output_stack_.erase(it.base(),output_stack_.end()); + output_stack_.emplace_back(token(jsoncons::make_unique<function_expression>(std::move(toks)))); + break; + } + case token_kind::literal: + if (!output_stack_.empty() && output_stack_.back().type() == token_kind::current_node) + { + output_stack_.back() = std::move(tok); + } + else + { + output_stack_.emplace_back(std::move(tok)); + } + break; + case token_kind::expression: + if (!output_stack_.empty() && output_stack_.back().is_projection() && + (tok.precedence_level() < output_stack_.back().precedence_level() || + (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative()))) + { + output_stack_.back().expression_->add_expression(std::move(tok.expression_)); + } + else + { + output_stack_.emplace_back(std::move(tok)); + } + break; + case token_kind::rparen: + { + unwind_rparen(ec); + break; + } + case token_kind::end_function: + { + unwind_rparen(ec); + std::vector<token> toks; + auto it = output_stack_.rbegin(); + std::size_t arg_count = 0; + while (it != output_stack_.rend() && it->type() != token_kind::function) + { + if (it->type() == token_kind::argument) + { + ++arg_count; + } + toks.emplace_back(std::move(*it)); + ++it; + } + if (it == output_stack_.rend()) + { + ec = jmespath_errc::unbalanced_parentheses; + return; + } + if (it->arity() && arg_count != *(it->arity())) + { + ec = jmespath_errc::invalid_arity; + return; + } + if (toks.back().type() != token_kind::literal) + { + toks.emplace_back(current_node_arg); + } + std::reverse(toks.begin(), toks.end()); + toks.push_back(std::move(*it)); + ++it; + output_stack_.erase(it.base(),output_stack_.end()); + + if (!output_stack_.empty() && output_stack_.back().is_projection() && + (tok.precedence_level() < output_stack_.back().precedence_level() || + (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative()))) + { + output_stack_.back().expression_->add_expression(jsoncons::make_unique<function_expression>(std::move(toks))); + } + else + { + output_stack_.emplace_back(token(jsoncons::make_unique<function_expression>(std::move(toks)))); + } + break; + } + case token_kind::end_of_expression: + { + auto it = operator_stack_.rbegin(); + while (it != operator_stack_.rend()) + { + output_stack_.emplace_back(std::move(*it)); + ++it; + } + operator_stack_.clear(); + break; + } + case token_kind::unary_operator: + case 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; + } + case token_kind::separator: + { + unwind_rparen(ec); + output_stack_.emplace_back(std::move(tok)); + operator_stack_.emplace_back(token(lparen_arg)); + break; + } + case token_kind::begin_filter: + output_stack_.emplace_back(std::move(tok)); + operator_stack_.emplace_back(token(lparen_arg)); + break; + case token_kind::begin_multi_select_list: + output_stack_.emplace_back(std::move(tok)); + operator_stack_.emplace_back(token(lparen_arg)); + break; + case token_kind::begin_multi_select_hash: + output_stack_.emplace_back(std::move(tok)); + operator_stack_.emplace_back(token(lparen_arg)); + break; + case token_kind::function: + output_stack_.emplace_back(std::move(tok)); + operator_stack_.emplace_back(token(lparen_arg)); + break; + case token_kind::current_node: + output_stack_.emplace_back(std::move(tok)); + break; + case token_kind::key: + case token_kind::pipe: + case token_kind::argument: + case token_kind::begin_expression_type: + output_stack_.emplace_back(std::move(tok)); + break; + case token_kind::lparen: + operator_stack_.emplace_back(std::move(tok)); + break; + default: + break; + } + } + + 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 = jmespath_errc::invalid_codepoint; + } + return cp; + } + }; + + } // detail + + template <class Json> + using jmespath_expression = typename jsoncons::jmespath::detail::jmespath_evaluator<Json,const Json&>::jmespath_expression; + + template<class Json> + Json search(const Json& doc, const typename Json::string_view_type& path) + { + jsoncons::jmespath::detail::jmespath_evaluator<Json,const Json&> evaluator; + std::error_code ec; + auto expr = evaluator.compile(path.data(), path.size(), ec); + if (ec) + { + JSONCONS_THROW(jmespath_error(ec, evaluator.line(), evaluator.column())); + } + auto result = expr.evaluate(doc, ec); + if (ec) + { + JSONCONS_THROW(jmespath_error(ec)); + } + return result; + } + + template<class Json> + Json search(const Json& doc, const typename Json::string_view_type& path, std::error_code& ec) + { + jsoncons::jmespath::detail::jmespath_evaluator<Json,const Json&> evaluator; + auto expr = evaluator.compile(path.data(), path.size(), ec); + if (ec) + { + return Json::null(); + } + auto result = expr.evaluate(doc, ec); + if (ec) + { + return Json::null(); + } + return result; + } + + template <class Json> + jmespath_expression<Json> make_expression(const typename json::string_view_type& expr) + { + return jmespath_expression<Json>::compile(expr); + } + + template <class Json> + jmespath_expression<Json> make_expression(const typename json::string_view_type& expr, + std::error_code& ec) + { + return jmespath_expression<Json>::compile(expr, ec); + } + + +} // namespace jmespath +} // namespace jsoncons + +#endif diff --git a/include/jsoncons_ext/jmespath/jmespath_error.hpp b/include/jsoncons_ext/jmespath/jmespath_error.hpp new file mode 100644 index 0000000..6422c65 --- /dev/null +++ b/include/jsoncons_ext/jmespath/jmespath_error.hpp @@ -0,0 +1,215 @@ +/// Copyright 2020 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_JMESPATH_JMESPATH_ERROR_HPP +#define JSONCONS_JMESPATH_JMESPATH_ERROR_HPP + +#include <jsoncons/json_exception.hpp> +#include <system_error> + +namespace jsoncons { namespace jmespath { + + class jmespath_error : public std::system_error, public virtual json_exception + { + std::size_t line_number_; + std::size_t column_number_; + mutable std::string what_; + public: + jmespath_error(std::error_code ec) + : std::system_error(ec), line_number_(0), column_number_(0) + { + } + jmespath_error(std::error_code ec, const std::string& what_arg) + : std::system_error(ec, what_arg), line_number_(0), column_number_(0) + { + } + jmespath_error(std::error_code ec, std::size_t position) + : std::system_error(ec), line_number_(0), column_number_(position) + { + } + jmespath_error(std::error_code ec, std::size_t line, std::size_t column) + : std::system_error(ec), line_number_(line), column_number_(column) + { + } + jmespath_error(const jmespath_error& other) = default; + + jmespath_error(jmespath_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_; + } + }; + +enum class jmespath_errc +{ + success = 0, + expected_identifier, + expected_index, + expected_A_Za_Z_, + expected_rbracket, + expected_rparen, + expected_rbrace, + expected_colon, + expected_dot, + expected_or, + expected_and, + expected_multi_select_list, + invalid_number, + invalid_literal, + expected_comparator, + expected_key, + invalid_argument, + unknown_function, + invalid_type, + unexpected_end_of_input, + step_cannot_be_zero, + syntax_error, + invalid_codepoint, + illegal_escaped_character, + unbalanced_parentheses, + unbalanced_braces, + invalid_arity, + identifier_not_found, + expected_index_expression, + unknown_error +}; + +class jmespath_error_category_impl + : public std::error_category +{ +public: + const char* name() const noexcept override + { + return "jsoncons/jmespath"; + } + std::string message(int ev) const override + { + switch (static_cast<jmespath_errc>(ev)) + { + case jmespath_errc::expected_identifier: + return "Expected identifier"; + case jmespath_errc::expected_index: + return "Expected index"; + case jmespath_errc::expected_A_Za_Z_: + return "Expected A-Z, a-z, or _"; + case jmespath_errc::expected_rbracket: + return "Expected ]"; + case jmespath_errc::expected_rparen: + return "Expected )"; + case jmespath_errc::expected_rbrace: + return "Expected }"; + case jmespath_errc::expected_colon: + return "Expected :"; + case jmespath_errc::expected_dot: + return "Expected \".\""; + case jmespath_errc::expected_or: + return "Expected \"||\""; + case jmespath_errc::expected_and: + return "Expected \"&&\""; + case jmespath_errc::expected_multi_select_list: + return "Expected multi-select-list"; + case jmespath_errc::invalid_number: + return "Invalid number"; + case jmespath_errc::invalid_literal: + return "Invalid literal"; + case jmespath_errc::expected_comparator: + return "Expected <, <=, ==, >=, > or !="; + case jmespath_errc::expected_key: + return "Expected key"; + case jmespath_errc::invalid_argument: + return "Invalid argument type"; + case jmespath_errc::unknown_function: + return "Unknown function"; + case jmespath_errc::invalid_type: + return "Invalid type"; + case jmespath_errc::unexpected_end_of_input: + return "Unexpected end of jmespath input"; + case jmespath_errc::step_cannot_be_zero: + return "Slice step cannot be zero"; + case jmespath_errc::syntax_error: + return "Syntax error"; + case jmespath_errc::invalid_codepoint: + return "Invalid codepoint"; + case jmespath_errc::illegal_escaped_character: + return "Illegal escaped character"; + case jmespath_errc::unbalanced_parentheses: + return "Unbalanced parentheses"; + case jmespath_errc::unbalanced_braces: + return "Unbalanced braces"; + case jmespath_errc::invalid_arity: + return "Function called with wrong number of arguments"; + case jmespath_errc::identifier_not_found: + return "Identifier not found"; + case jmespath_errc::expected_index_expression: + return "Expected index expression"; + case jmespath_errc::unknown_error: + default: + return "Unknown jmespath parser error"; + } + } +}; + +inline +const std::error_category& jmespath_error_category() +{ + static jmespath_error_category_impl instance; + return instance; +} + +inline +std::error_code make_error_code(jmespath_errc result) +{ + return std::error_code(static_cast<int>(result),jmespath_error_category()); +} + +}} + +namespace std { + template<> + struct is_error_code_enum<jsoncons::jmespath::jmespath_errc> : public true_type + { + }; +} + +#endif |