build_exe.py compiles every unit in data/compile.json with the pinned toolchain and links Ragexe.exe. Not part of the decompilation and not in data/compile.json: imports/*.def (original DLL import tables), jsoncons/ (v0.170.2 headers + JsonCppLoader glue), nProtect/GameGuard.cpp (no-op GameGuard stand-in).
5215 lines
192 KiB
C++
5215 lines
192 KiB
C++
// Copyright 2013-2023 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
|