rodecomp/Recovered/jsoncons/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp
ragnarok-clanker 9b9c369f0c Move exe-only code to Recovered/ and rebuild virtualised PakAuxiliary::Extract
Recovered/ holds the code linked only into the standalone exe (scripts/exe/build_exe.py), outside the Clank source dirs:
- jsoncons/ and nProtect/ moved from scripts/exe/; GameGuard stand-in follows main's renamed entry points.
- GPak/PakAuxiliary.cpp: Extract (virtualised in every build; 02-19 jumps out at 0x0065861E) rebuilt from the open GRF Editor decoder, after the visible 02-19 prologue; inline key and kRO keys 1-4. Apply traps (unreachable in 02-19).
- GGHash/GGHash.cpp: CGGHash::Load/Check/VerifyHash trap (RagHash.dat check unreachable in 02-19).
The whole client links: 984 units, 0 unresolved.
2026-11-06 19:33:10 +01:00

2640 lines
125 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_JSONPATH_JSONPATH_EXPRESSION_HPP
#define JSONCONS_JSONPATH_JSONPATH_EXPRESSION_HPP
#include <string>
#include <vector>
#include <memory>
#include <type_traits> // std::is_const
#include <limits> // std::numeric_limits
#include <utility> // std::move
#include <regex>
#include <algorithm> // std::reverse
#include <jsoncons/json.hpp>
#include <jsoncons_ext/jsonpath/jsonpath_error.hpp>
#include <jsoncons_ext/jsonpath/expression.hpp>
#include <jsoncons_ext/jsonpath/jsonpath_selector.hpp>
namespace jsoncons {
namespace jsonpath {
namespace detail {
enum class path_state
{
start,
root_or_current_node,
expect_function_expr,
relative_path,
relative_location,
parent_operator,
ancestor_depth,
filter_expression,
expression_rhs,
recursive_descent_or_expression_lhs,
path_or_literal_or_function,
json_text_or_function,
json_text_or_function_name,
json_text_string,
json_value,
json_string,
identifier_or_function_expr,
name_or_lbracket,
unquoted_string,
anything,
number,
function_expression,
argument,
zero_or_one_arguments,
one_or_more_arguments,
identifier,
single_quoted_string,
double_quoted_string,
bracketed_unquoted_name_or_union,
union_expression,
identifier_or_union,
bracket_specifier_or_union,
bracketed_wildcard,
index_or_slice,
wildcard_or_union,
union_element,
index_or_slice_or_union,
integer,
digit,
slice_expression_stop,
slice_expression_step,
comma_or_rbracket,
expect_rparen,
expect_rbracket,
quoted_string_escape_char,
escape_u1,
escape_u2,
escape_u3,
escape_u4,
escape_expect_surrogate_pair1,
escape_expect_surrogate_pair2,
escape_u5,
escape_u6,
escape_u7,
escape_u8,
expression,
comparator_expression,
eq_or_regex,
expect_regex,
regex,
regex_options,
regex_pattern,
cmp_lt_or_lte,
cmp_gt_or_gte,
cmp_ne,
expect_or,
expect_and
};
template<class Json,
class JsonReference>
class jsonpath_evaluator : public ser_context
{
public:
using allocator_type = typename Json::allocator_type;
using char_type = typename Json::char_type;
using string_type = typename Json::string_type;
using string_view_type = typename Json::string_view_type;
using path_value_pair_type = path_value_pair<Json,JsonReference>;
using value_type = Json;
using reference = JsonReference;
using pointer = typename path_value_pair_type::value_pointer;
using token_type = token<Json,JsonReference>;
using path_expression_type = path_expression<Json,JsonReference>;
using expression_type = expression<Json,JsonReference>;
using json_location_type = json_location<string_type>;
using json_location_node_type = json_location_node<string_type>;
using selector_type = jsonpath_selector<Json,JsonReference>;
private:
allocator_type alloc_;
std::size_t line_;
std::size_t column_;
const char_type* begin_input_;
const char_type* end_input_;
const char_type* p_;
using argument_type = std::vector<pointer>;
std::vector<argument_type> function_stack_;
std::vector<path_state> state_stack_;
std::vector<token_type> output_stack_;
std::vector<token_type> operator_stack_;
public:
jsonpath_evaluator(const allocator_type& alloc = allocator_type())
: alloc_(alloc), line_(1), column_(1),
begin_input_(nullptr), end_input_(nullptr),
p_(nullptr)
{
}
jsonpath_evaluator(std::size_t line, std::size_t column,
const allocator_type& alloc = allocator_type())
: alloc_(alloc), line_(line), column_(column),
begin_input_(nullptr), end_input_(nullptr),
p_(nullptr)
{
}
std::size_t line() const
{
return line_;
}
std::size_t column() const
{
return column_;
}
path_expression_type compile(static_resources<value_type,reference>& resources, const string_view_type& path)
{
std::error_code ec;
auto result = compile(resources, path, ec);
if (ec)
{
JSONCONS_THROW(jsonpath_error(ec, line_, column_));
}
return result;
}
path_expression_type compile(static_resources<value_type,reference>& resources,
const string_view_type& path,
std::error_code& ec)
{
std::size_t selector_id = 0;
string_type buffer(alloc_);
string_type buffer2(alloc_);
uint32_t cp = 0;
uint32_t cp2 = 0;
begin_input_ = path.data();
end_input_ = path.data() + path.length();
p_ = begin_input_;
slice slic;
bool paths_required = false;
int ancestor_depth = 0;
state_stack_.emplace_back(path_state::start);
while (p_ < end_input_ && !state_stack_.empty())
{
switch (state_stack_.back())
{
case path_state::start:
{
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '$':
case '@':
{
push_token(resources, token_type(resources.new_selector(current_node_selector<Json,JsonReference>())), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.emplace_back(path_state::relative_location);
++p_;
++column_;
break;
}
default:
{
state_stack_.emplace_back(path_state::relative_location);
state_stack_.emplace_back(path_state::expect_function_expr);
state_stack_.emplace_back(path_state::unquoted_string);
break;
}
}
break;
}
case path_state::root_or_current_node:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '$':
push_token(resources, token_type(root_node_arg), ec);
push_token(resources, token_type(resources.new_selector(root_selector<Json,JsonReference>(selector_id++))), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
++p_;
++column_;
break;
case '@':
push_token(resources, token_type(current_node_arg), ec); // ISSUE
push_token(resources, token_type(resources.new_selector(current_node_selector<Json,JsonReference>())), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
++p_;
++column_;
break;
default:
ec = jsonpath_errc::syntax_error;
return path_expression_type(alloc_);
}
break;
case path_state::recursive_descent_or_expression_lhs:
switch (*p_)
{
case '.':
push_token(resources, token_type(resources.new_selector(recursive_selector<Json,JsonReference>())), ec);
if (ec) {return path_expression_type(alloc_);}
++p_;
++column_;
state_stack_.back() = path_state::name_or_lbracket;
break;
default:
state_stack_.back() = path_state::relative_path;
break;
}
break;
case path_state::name_or_lbracket:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '[': // [ can follow ..
state_stack_.back() = path_state::bracket_specifier_or_union;
++p_;
++column_;
break;
default:
buffer.clear();
state_stack_.back() = path_state::relative_path;
break;
}
break;
case path_state::json_string:
{
//std::cout << "literal: " << buffer << "\n";
push_token(resources, token_type(literal_arg, Json(buffer,semantic_tag::none,alloc_)), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.pop_back(); // json_value
break;
}
case path_state::path_or_literal_or_function:
{
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '$':
case '@':
state_stack_.back() = path_state::relative_location;
state_stack_.push_back(path_state::root_or_current_node);
break;
case '(':
{
++p_;
++column_;
push_token(resources, lparen_arg, ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::expect_rparen;
state_stack_.emplace_back(path_state::expression_rhs);
state_stack_.emplace_back(path_state::path_or_literal_or_function);
break;
}
case '\'':
state_stack_.back() = path_state::json_string;
state_stack_.emplace_back(path_state::single_quoted_string);
++p_;
++column_;
break;
case '\"':
state_stack_.back() = path_state::json_string;
state_stack_.emplace_back(path_state::double_quoted_string);
++p_;
++column_;
break;
case '!':
{
++p_;
++column_;
push_token(resources, token_type(resources.get_unary_not()), ec);
if (ec) {return path_expression_type(alloc_);}
break;
}
case '-':
{
++p_;
++column_;
push_token(resources, token_type(resources.get_unary_minus()), ec);
if (ec) {return path_expression_type(alloc_);}
break;
}
case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
{
state_stack_.back() = path_state::json_value;
state_stack_.emplace_back(path_state::number);
break;
}
default:
{
state_stack_.back() = path_state::json_text_or_function_name;
break;
}
}
break;
}
case path_state::json_text_or_function:
{
switch(*p_)
{
case '(':
{
auto f = resources.get_function(buffer, ec);
if (ec)
{
return path_expression_type(alloc_);
}
buffer.clear();
push_token(resources, current_node_arg, ec);
if (ec) {return path_expression_type(alloc_);}
push_token(resources, token_type(f), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::function_expression;
state_stack_.emplace_back(path_state::zero_or_one_arguments);
++p_;
++column_;
break;
}
default:
{
json_decoder<Json> decoder(result_allocator_arg, alloc_);
basic_json_parser<char_type> parser;
parser.update(buffer.data(),buffer.size());
parser.parse_some(decoder, ec);
if (ec)
{
return path_expression_type(alloc_);
}
parser.finish_parse(decoder, ec);
if (ec)
{
return path_expression_type(alloc_);
}
push_token(resources, token_type(literal_arg, decoder.get_result()), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.pop_back();
break;
}
}
break;
}
case path_state::json_value:
{
json_decoder<Json> decoder(result_allocator_arg, alloc_);
basic_json_parser<char_type> parser;
parser.update(buffer.data(),buffer.size());
parser.parse_some(decoder, ec);
if (ec)
{
return path_expression_type(alloc_);
}
parser.finish_parse(decoder, ec);
if (ec)
{
return path_expression_type(alloc_);
}
push_token(resources, token_type(literal_arg, decoder.get_result()), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.pop_back();
break;
}
case path_state::json_text_or_function_name:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '{':
case '[':
{
json_decoder<Json> decoder(result_allocator_arg, alloc_);
basic_json_parser<char_type> parser;
parser.update(p_,end_input_ - p_);
parser.parse_some(decoder, ec);
if (ec)
{
return path_expression_type(alloc_);
}
parser.finish_parse(decoder, ec);
if (ec)
{
return path_expression_type(alloc_);
}
push_token(resources, token_type(literal_arg, decoder.get_result()), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.pop_back();
p_ = parser.current();
column_ = column_ + parser.column() - 1;
break;
}
case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
state_stack_.back() = path_state::json_text_or_function;
state_stack_.emplace_back(path_state::number);
buffer.push_back(*p_);
++p_;
++column_;
break;
case '\"':
state_stack_.back() = path_state::json_text_or_function;
state_stack_.emplace_back(path_state::json_text_string);
buffer.push_back(*p_);
++p_;
++column_;
break;
default:
state_stack_.back() = path_state::json_text_or_function;
state_stack_.emplace_back(path_state::unquoted_string);
buffer.push_back(*p_);
++p_;
++column_;
break;
};
break;
case path_state::number:
switch (*p_)
{
case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
case 'e':case 'E':case '.':
buffer.push_back(*p_);
++p_;
++column_;
break;
default:
state_stack_.pop_back(); // number
break;
};
break;
case path_state::json_text_string:
switch (*p_)
{
case '\\':
buffer.push_back(*p_);
++p_;
++column_;
if (p_ == end_input_)
{
ec = jsonpath_errc::unexpected_eof;
return path_expression_type(alloc_);
}
buffer.push_back(*p_);
++p_;
++column_;
break;
case '\"':
buffer.push_back(*p_);
state_stack_.pop_back();
++p_;
++column_;
break;
default:
buffer.push_back(*p_);
++p_;
++column_;
break;
};
break;
case path_state::relative_path:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '*':
push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
++p_;
++column_;
break;
case '\'':
state_stack_.back() = path_state::identifier;
state_stack_.emplace_back(path_state::single_quoted_string);
++p_;
++column_;
break;
case '\"':
state_stack_.back() = path_state::identifier;
state_stack_.emplace_back(path_state::double_quoted_string);
++p_;
++column_;
break;
case '[':
case '.':
ec = jsonpath_errc::expected_relative_path;
return path_expression_type(alloc_);
default:
buffer.clear();
state_stack_.back() = path_state::identifier_or_function_expr;
state_stack_.emplace_back(path_state::unquoted_string);
break;
}
break;
case path_state::identifier_or_function_expr:
{
switch(*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '(':
{
auto f = resources.get_function(buffer, ec);
if (ec)
{
return path_expression_type(alloc_);
}
buffer.clear();
push_token(resources, current_node_arg, ec);
push_token(resources, token_type(f), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::function_expression;
state_stack_.emplace_back(path_state::zero_or_one_arguments);
++p_;
++column_;
break;
}
default:
{
push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.pop_back();
break;
}
}
break;
}
case path_state::expect_function_expr:
{
switch(*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '(':
{
auto f = resources.get_function(buffer, ec);
if (ec)
{
return path_expression_type(alloc_);
}
buffer.clear();
push_token(resources, current_node_arg, ec);
push_token(resources, token_type(f), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::function_expression;
state_stack_.emplace_back(path_state::zero_or_one_arguments);
++p_;
++column_;
break;
}
default:
{
ec = jsonpath_errc::expected_root_or_function;
return path_expression_type(alloc_);
}
}
break;
}
case path_state::function_expression:
{
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ',':
push_token(resources, token_type(current_node_arg), ec);
if (ec) {return path_expression_type(alloc_);}
push_token(resources, token_type(begin_expression_arg), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.emplace_back(path_state::argument);
state_stack_.emplace_back(path_state::expression_rhs);
state_stack_.emplace_back(path_state::path_or_literal_or_function);
++p_;
++column_;
break;
case ')':
{
push_token(resources, token_type(end_function_arg), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
++p_;
++column_;
break;
}
default:
ec = jsonpath_errc::syntax_error;
return path_expression_type(alloc_);
}
break;
}
case path_state::zero_or_one_arguments:
{
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ')':
state_stack_.pop_back();
break;
default:
push_token(resources, token_type(begin_expression_arg), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::one_or_more_arguments;
state_stack_.emplace_back(path_state::argument);
state_stack_.emplace_back(path_state::expression_rhs);
state_stack_.emplace_back(path_state::path_or_literal_or_function);
break;
}
break;
}
case path_state::one_or_more_arguments:
{
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ')':
state_stack_.pop_back();
break;
case ',':
push_token(resources, token_type(begin_expression_arg), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.emplace_back(path_state::argument);
state_stack_.emplace_back(path_state::expression_rhs);
state_stack_.emplace_back(path_state::path_or_literal_or_function);
++p_;
++column_;
break;
}
break;
}
case path_state::argument:
{
switch(*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ',':
case ')':
{
push_token(resources, token_type(end_argument_expression_arg), ec);
push_token(resources, argument_arg, ec);
//push_token(resources, argument_arg, ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
break;
}
default:
ec = jsonpath_errc::expected_comma_or_rparen;
return path_expression_type(alloc_);
}
break;
}
case path_state::unquoted_string:
switch (*p_)
{
case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'g':case 'h':case 'i':case 'j':case 'k':case 'l':case 'm':case 'n':case 'o':case 'p':case 'q':case 'r':case 's':case 't':case 'u':case 'v':case 'w':case 'x':case 'y':case 'z':
case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':case 'G':case 'H':case 'I':case 'J':case 'K':case 'L':case 'M':case 'N':case 'O':case 'P':case 'Q':case 'R':case 'S':case 'T':case 'U':case 'V':case 'W':case 'X':case 'Y':case 'Z':
case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
case '_':
buffer.push_back(*p_);
++p_;
++column_;
break;
default:
if (typename std::make_unsigned<char_type>::type(*p_) > 127)
{
buffer.push_back(*p_);
++p_;
++column_;
}
else
{
state_stack_.pop_back(); // unquoted_string
}
break;
};
break;
case path_state::relative_location:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '.':
state_stack_.emplace_back(path_state::recursive_descent_or_expression_lhs);
++p_;
++column_;
break;
case '[':
state_stack_.emplace_back(path_state::bracket_specifier_or_union);
++p_;
++column_;
break;
case '^':
ancestor_depth = 0;
state_stack_.emplace_back(path_state::parent_operator);
state_stack_.emplace_back(path_state::ancestor_depth);
break;
default:
state_stack_.pop_back();
break;
};
break;
case path_state::parent_operator:
{
push_token(resources, token_type(resources.new_selector(parent_node_selector<Json,JsonReference>(ancestor_depth))), ec);
paths_required = true;
ancestor_depth = 0;
++p_;
++column_;
state_stack_.pop_back();
break;
}
case path_state::ancestor_depth:
{
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '^':
{
++ancestor_depth;
++p_;
++column_;
break;
}
default:
{
state_stack_.pop_back();
break;
}
}
break;
}
case path_state::expression_rhs:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '.':
state_stack_.emplace_back(path_state::recursive_descent_or_expression_lhs);
++p_;
++column_;
break;
case '[':
state_stack_.emplace_back(path_state::bracket_specifier_or_union);
++p_;
++column_;
break;
case ')':
{
state_stack_.pop_back();
break;
}
case '|':
++p_;
++column_;
state_stack_.emplace_back(path_state::path_or_literal_or_function);
state_stack_.emplace_back(path_state::expect_or);
break;
case '&':
++p_;
++column_;
state_stack_.emplace_back(path_state::path_or_literal_or_function);
state_stack_.emplace_back(path_state::expect_and);
break;
case '<':
case '>':
{
state_stack_.emplace_back(path_state::comparator_expression);
break;
}
case '=':
{
state_stack_.emplace_back(path_state::eq_or_regex);
++p_;
++column_;
break;
}
case '!':
{
++p_;
++column_;
state_stack_.emplace_back(path_state::path_or_literal_or_function);
state_stack_.emplace_back(path_state::cmp_ne);
break;
}
case '+':
state_stack_.emplace_back(path_state::path_or_literal_or_function);
push_token(resources, token_type(resources.get_plus_operator()), ec);
if (ec) {return path_expression_type(alloc_);}
++p_;
++column_;
break;
case '-':
state_stack_.emplace_back(path_state::path_or_literal_or_function);
push_token(resources, token_type(resources.get_minus_operator()), ec);
if (ec) {return path_expression_type(alloc_);}
++p_;
++column_;
break;
case '*':
state_stack_.emplace_back(path_state::path_or_literal_or_function);
push_token(resources, token_type(resources.get_mult_operator()), ec);
if (ec) {return path_expression_type(alloc_);}
++p_;
++column_;
break;
case '/':
state_stack_.emplace_back(path_state::path_or_literal_or_function);
push_token(resources, token_type(resources.get_div_operator()), ec);
if (ec) {return path_expression_type(alloc_);}
++p_;
++column_;
break;
case '%':
state_stack_.emplace_back(path_state::path_or_literal_or_function);
push_token(resources, token_type(resources.get_modulus_operator()), ec);
if (ec) {return path_expression_type(alloc_);}
++p_;
++column_;
break;
case ']':
case ',':
state_stack_.pop_back();
break;
default:
ec = jsonpath_errc::expected_separator;
return path_expression_type(alloc_);
};
break;
case path_state::expect_or:
{
switch (*p_)
{
case '|':
push_token(resources, token_type(resources.get_or_operator()), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
++p_;
++column_;
break;
default:
ec = jsonpath_errc::expected_or;
return path_expression_type(alloc_);
}
break;
}
case path_state::expect_and:
{
switch(*p_)
{
case '&':
push_token(resources, token_type(resources.get_and_operator()), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back(); // expect_and
++p_;
++column_;
break;
default:
ec = jsonpath_errc::expected_and;
return path_expression_type(alloc_);
}
break;
}
case path_state::comparator_expression:
switch(*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '<':
++p_;
++column_;
state_stack_.back() = path_state::path_or_literal_or_function;
state_stack_.emplace_back(path_state::cmp_lt_or_lte);
break;
case '>':
++p_;
++column_;
state_stack_.back() = path_state::path_or_literal_or_function;
state_stack_.emplace_back(path_state::cmp_gt_or_gte);
break;
default:
if (state_stack_.size() > 1)
{
state_stack_.pop_back();
}
else
{
ec = jsonpath_errc::syntax_error;
return path_expression_type(alloc_);
}
break;
}
break;
case path_state::eq_or_regex:
switch(*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '=':
{
push_token(resources, token_type(resources.get_eq_operator()), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::path_or_literal_or_function;
++p_;
++column_;
break;
}
case '~':
{
++p_;
++column_;
state_stack_.emplace_back(path_state::expect_regex);
break;
}
default:
if (state_stack_.size() > 1)
{
state_stack_.pop_back();
}
else
{
ec = jsonpath_errc::syntax_error;
return path_expression_type(alloc_);
}
break;
}
break;
case path_state::expect_regex:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '/':
state_stack_.back() = path_state::regex;
state_stack_.emplace_back(path_state::regex_options);
state_stack_.emplace_back(path_state::regex_pattern);
++p_;
++column_;
break;
default:
ec = jsonpath_errc::expected_forward_slash;
return path_expression_type(alloc_);
};
break;
case path_state::regex:
{
std::regex::flag_type options = std::regex_constants::ECMAScript;
if (buffer2.find('i') != string_type::npos)
{
options |= std::regex_constants::icase;
}
std::basic_regex<char_type> pattern(buffer, options);
push_token(resources, resources.get_regex_operator(std::move(pattern)), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
buffer2.clear();
state_stack_.pop_back();
break;
}
case path_state::regex_pattern:
{
switch (*p_)
{
case '/':
{
state_stack_.pop_back();
++p_;
++column_;
}
break;
default:
buffer.push_back(*p_);
++p_;
++column_;
break;
}
break;
}
case path_state::regex_options:
{
if (*p_ == 'i')
{
buffer2.push_back(*p_);
++p_;
++column_;
}
else
{
state_stack_.pop_back();
}
break;
}
case path_state::cmp_lt_or_lte:
{
switch(*p_)
{
case '=':
push_token(resources, token_type(resources.get_lte_operator()), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
++p_;
++column_;
break;
default:
push_token(resources, token_type(resources.get_lt_operator()), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
break;
}
break;
}
case path_state::cmp_gt_or_gte:
{
switch(*p_)
{
case '=':
push_token(resources, token_type(resources.get_gte_operator()), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
++p_;
++column_;
break;
default:
//std::cout << "Parse: gt_operator\n";
push_token(resources, token_type(resources.get_gt_operator()), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
break;
}
break;
}
case path_state::cmp_ne:
{
switch(*p_)
{
case '=':
push_token(resources, token_type(resources.get_ne_operator()), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
++p_;
++column_;
break;
default:
ec = jsonpath_errc::expected_comparator;
return path_expression_type(alloc_);
}
break;
}
case path_state::identifier:
push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.pop_back();
break;
case path_state::single_quoted_string:
switch (*p_)
{
case '\'':
state_stack_.pop_back();
++p_;
++column_;
break;
case '\\':
state_stack_.emplace_back(path_state::quoted_string_escape_char);
++p_;
++column_;
break;
default:
buffer.push_back(*p_);
++p_;
++column_;
break;
};
break;
case path_state::double_quoted_string:
switch (*p_)
{
case '\"':
state_stack_.pop_back();
++p_;
++column_;
break;
case '\\':
state_stack_.emplace_back(path_state::quoted_string_escape_char);
++p_;
++column_;
break;
default:
buffer.push_back(*p_);
++p_;
++column_;
break;
};
break;
case path_state::comma_or_rbracket:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ',':
state_stack_.back() = path_state::bracket_specifier_or_union;
++p_;
++column_;
break;
case ']':
state_stack_.pop_back();
++p_;
++column_;
break;
default:
ec = jsonpath_errc::expected_comma_or_rbracket;
return path_expression_type(alloc_);
}
break;
case path_state::expect_rbracket:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ']':
state_stack_.pop_back();
++p_;
++column_;
break;
default:
ec = jsonpath_errc::expected_rbracket;
return path_expression_type(alloc_);
}
break;
case path_state::expect_rparen:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ')':
++p_;
++column_;
push_token(resources, rparen_arg, ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::expression_rhs;
break;
default:
ec = jsonpath_errc::expected_rparen;
return path_expression_type(alloc_);
}
break;
case path_state::bracket_specifier_or_union:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '(':
{
push_token(resources, token_type(begin_union_arg), ec);
push_token(resources, token_type(begin_expression_arg), ec);
push_token(resources, lparen_arg, ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::union_expression; // union
state_stack_.emplace_back(path_state::expression);
state_stack_.emplace_back(path_state::expect_rparen);
state_stack_.emplace_back(path_state::expression_rhs);
state_stack_.emplace_back(path_state::path_or_literal_or_function);
++p_;
++column_;
break;
}
case '?':
{
push_token(resources, token_type(begin_union_arg), ec);
push_token(resources, token_type(begin_filter_arg), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::union_expression; // union
state_stack_.emplace_back(path_state::filter_expression);
state_stack_.emplace_back(path_state::expression_rhs);
state_stack_.emplace_back(path_state::path_or_literal_or_function);
++p_;
++column_;
break;
}
case '*':
state_stack_.back() = path_state::wildcard_or_union;
++p_;
++column_;
break;
case '\'':
state_stack_.back() = path_state::identifier_or_union;
state_stack_.push_back(path_state::single_quoted_string);
++p_;
++column_;
break;
case '\"':
state_stack_.back() = path_state::identifier_or_union;
state_stack_.push_back(path_state::double_quoted_string);
++p_;
++column_;
break;
case ':': // slice_expression
state_stack_.back() = path_state::index_or_slice_or_union;
break;
case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
state_stack_.back() = path_state::index_or_slice_or_union;
state_stack_.emplace_back(path_state::integer);
break;
case '$':
push_token(resources, token_type(begin_union_arg), ec);
push_token(resources, root_node_arg, ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::union_expression; // union
state_stack_.emplace_back(path_state::relative_location);
++p_;
++column_;
break;
case '@':
push_token(resources, token_type(begin_union_arg), ec);
push_token(resources, token_type(current_node_arg), ec); // ISSUE
push_token(resources, token_type(resources.new_selector(current_node_selector<Json,JsonReference>())), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::union_expression; // union
state_stack_.emplace_back(path_state::relative_location);
++p_;
++column_;
break;
default:
ec = jsonpath_errc::expected_bracket_specifier_or_union;
return path_expression_type(alloc_);
}
break;
case path_state::union_element:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ':': // slice_expression
state_stack_.back() = path_state::index_or_slice;
break;
case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
state_stack_.back() = path_state::index_or_slice;
state_stack_.emplace_back(path_state::integer);
break;
case '(':
{
push_token(resources, token_type(begin_expression_arg), ec);
push_token(resources, lparen_arg, ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::expression;
state_stack_.emplace_back(path_state::expect_rparen);
state_stack_.emplace_back(path_state::expression_rhs);
state_stack_.emplace_back(path_state::path_or_literal_or_function);
++p_;
++column_;
break;
}
case '?':
{
push_token(resources, token_type(begin_filter_arg), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::filter_expression;
state_stack_.emplace_back(path_state::expression_rhs);
state_stack_.emplace_back(path_state::path_or_literal_or_function);
++p_;
++column_;
break;
}
case '*':
push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::relative_location;
++p_;
++column_;
break;
case '$':
push_token(resources, token_type(root_node_arg), ec);
push_token(resources, token_type(resources.new_selector(root_selector<Json,JsonReference>(selector_id++))), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::relative_location;
++p_;
++column_;
break;
case '@':
push_token(resources, token_type(current_node_arg), ec); // ISSUE
push_token(resources, token_type(resources.new_selector(current_node_selector<Json,JsonReference>())), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::relative_location;
++p_;
++column_;
break;
case '\'':
state_stack_.back() = path_state::identifier;
state_stack_.push_back(path_state::single_quoted_string);
++p_;
++column_;
break;
case '\"':
state_stack_.back() = path_state::identifier;
state_stack_.push_back(path_state::double_quoted_string);
++p_;
++column_;
break;
default:
ec = jsonpath_errc::expected_bracket_specifier_or_union;
return path_expression_type(alloc_);
}
break;
case path_state::integer:
switch(*p_)
{
case '-':
buffer.push_back(*p_);
state_stack_.back() = path_state::digit;
++p_;
++column_;
break;
default:
state_stack_.back() = path_state::digit;
break;
}
break;
case path_state::digit:
switch(*p_)
{
case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
buffer.push_back(*p_);
++p_;
++column_;
break;
default:
state_stack_.pop_back(); // digit
break;
}
break;
case path_state::index_or_slice_or_union:
switch(*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ']':
{
if (buffer.empty())
{
ec = jsonpath_errc::invalid_number;
return path_expression_type(alloc_);
}
int64_t n{0};
auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
if (!r)
{
ec = jsonpath_errc::invalid_number;
return path_expression_type(alloc_);
}
push_token(resources, token_type(resources.new_selector(index_selector<Json,JsonReference>(n))), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.pop_back(); // index_or_slice_or_union
++p_;
++column_;
break;
}
case ',':
{
push_token(resources, token_type(begin_union_arg), ec);
if (ec) {return path_expression_type(alloc_);}
if (buffer.empty())
{
ec = jsonpath_errc::invalid_number;
return path_expression_type(alloc_);
}
else
{
int64_t n{0};
auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
if (!r)
{
ec = jsonpath_errc::invalid_number;
return path_expression_type(alloc_);
}
push_token(resources, token_type(resources.new_selector(index_selector<Json,JsonReference>(n))), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
}
push_token(resources, token_type(separator_arg), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.back() = path_state::union_expression; // union
state_stack_.emplace_back(path_state::union_element);
++p_;
++column_;
break;
}
case ':':
{
if (!buffer.empty())
{
int64_t n{0};
auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
if (!r)
{
ec = jsonpath_errc::invalid_number;
return path_expression_type(alloc_);
}
slic.start_ = n;
buffer.clear();
}
push_token(resources, token_type(begin_union_arg), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::union_expression; // union
state_stack_.emplace_back(path_state::slice_expression_stop);
state_stack_.emplace_back(path_state::integer);
++p_;
++column_;
break;
}
default:
ec = jsonpath_errc::expected_rbracket;
return path_expression_type(alloc_);
}
break;
case path_state::slice_expression_stop:
{
if (!buffer.empty())
{
int64_t n{0};
auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
if (!r)
{
ec = jsonpath_errc::invalid_number;
return path_expression_type(alloc_);
}
slic.stop_ = jsoncons::optional<int64_t>(n);
buffer.clear();
}
switch(*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ']':
case ',':
push_token(resources, token_type(resources.new_selector(slice_selector<Json,JsonReference>(slic))), ec);
if (ec) {return path_expression_type(alloc_);}
slic = slice{};
state_stack_.pop_back(); // bracket_specifier2
break;
case ':':
state_stack_.back() = path_state::slice_expression_step;
state_stack_.emplace_back(path_state::integer);
++p_;
++column_;
break;
default:
ec = jsonpath_errc::expected_rbracket;
return path_expression_type(alloc_);
}
break;
}
case path_state::slice_expression_step:
{
if (!buffer.empty())
{
int64_t n{0};
auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
if (!r)
{
ec = jsonpath_errc::invalid_number;
return path_expression_type(alloc_);
}
if (n == 0)
{
ec = jsonpath_errc::step_cannot_be_zero;
return path_expression_type(alloc_);
}
slic.step_ = n;
buffer.clear();
}
switch(*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ']':
case ',':
push_token(resources, token_type(resources.new_selector(slice_selector<Json,JsonReference>(slic))), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
slic = slice{};
state_stack_.pop_back(); // slice_expression_step
break;
default:
ec = jsonpath_errc::expected_rbracket;
return path_expression_type(alloc_);
}
break;
}
case path_state::bracketed_unquoted_name_or_union:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ']':
push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.pop_back();
++p_;
++column_;
break;
case '.':
push_token(resources, token_type(begin_union_arg), ec);
push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.back() = path_state::union_expression; // union
state_stack_.emplace_back(path_state::relative_path);
++p_;
++column_;
break;
case '[':
push_token(resources, token_type(begin_union_arg), ec);
push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.back() = path_state::union_expression; // union
state_stack_.emplace_back(path_state::relative_path);
++p_;
++column_;
break;
case ',':
push_token(resources, token_type(begin_union_arg), ec);
push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
push_token(resources, token_type(separator_arg), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.back() = path_state::union_expression; // union
state_stack_.emplace_back(path_state::relative_path);
++p_;
++column_;
break;
default:
buffer.push_back(*p_);
++p_;
++column_;
break;
}
break;
case path_state::union_expression:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '.':
state_stack_.emplace_back(path_state::relative_path);
++p_;
++column_;
break;
case '[':
state_stack_.emplace_back(path_state::bracket_specifier_or_union);
++p_;
++column_;
break;
case ',':
push_token(resources, token_type(separator_arg), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.emplace_back(path_state::union_element);
++p_;
++column_;
break;
case ']':
push_token(resources, token_type(end_union_arg), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
++p_;
++column_;
break;
default:
ec = jsonpath_errc::expected_rbracket;
return path_expression_type(alloc_);
}
break;
case path_state::identifier_or_union:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ']':
push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.pop_back();
++p_;
++column_;
break;
case ',':
push_token(resources, token_type(begin_union_arg), ec);
push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
push_token(resources, token_type(separator_arg), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.back() = path_state::union_expression; // union
state_stack_.emplace_back(path_state::union_element);
++p_;
++column_;
break;
default:
ec = jsonpath_errc::expected_rbracket;
return path_expression_type(alloc_);
}
break;
case path_state::bracketed_wildcard:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case '[':
case ']':
case ',':
case '.':
push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.pop_back();
break;
default:
ec = jsonpath_errc::expected_rbracket;
return path_expression_type(alloc_);
}
break;
case path_state::index_or_slice:
switch(*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ',':
case ']':
{
if (buffer.empty())
{
ec = jsonpath_errc::invalid_number;
return path_expression_type(alloc_);
}
else
{
int64_t n{0};
auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
if (!r)
{
ec = jsonpath_errc::invalid_number;
return path_expression_type(alloc_);
}
push_token(resources, token_type(resources.new_selector(index_selector<Json,JsonReference>(n))), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
}
state_stack_.pop_back(); // bracket_specifier
break;
}
case ':':
{
if (!buffer.empty())
{
int64_t n{0};
auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
if (!r)
{
ec = jsonpath_errc::invalid_number;
return path_expression_type(alloc_);
}
slic.start_ = n;
buffer.clear();
}
state_stack_.back() = path_state::slice_expression_stop;
state_stack_.emplace_back(path_state::integer);
++p_;
++column_;
break;
}
default:
ec = jsonpath_errc::expected_rbracket;
return path_expression_type(alloc_);
}
break;
case path_state::wildcard_or_union:
switch (*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ']':
push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.pop_back();
++p_;
++column_;
break;
case ',':
push_token(resources, token_type(begin_union_arg), ec);
push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec);
push_token(resources, token_type(separator_arg), ec);
if (ec) {return path_expression_type(alloc_);}
buffer.clear();
state_stack_.back() = path_state::union_expression; // union
state_stack_.emplace_back(path_state::union_element);
++p_;
++column_;
break;
default:
ec = jsonpath_errc::expected_rbracket;
return path_expression_type(alloc_);
}
break;
case path_state::quoted_string_escape_char:
switch (*p_)
{
case '\"':
buffer.push_back('\"');
++p_;
++column_;
state_stack_.pop_back();
break;
case '\'':
buffer.push_back('\'');
++p_;
++column_;
state_stack_.pop_back();
break;
case '\\':
buffer.push_back('\\');
++p_;
++column_;
state_stack_.pop_back();
break;
case '/':
buffer.push_back('/');
++p_;
++column_;
state_stack_.pop_back();
break;
case 'b':
buffer.push_back('\b');
++p_;
++column_;
state_stack_.pop_back();
break;
case 'f':
buffer.push_back('\f');
++p_;
++column_;
state_stack_.pop_back();
break;
case 'n':
buffer.push_back('\n');
++p_;
++column_;
state_stack_.pop_back();
break;
case 'r':
buffer.push_back('\r');
++p_;
++column_;
state_stack_.pop_back();
break;
case 't':
buffer.push_back('\t');
++p_;
++column_;
state_stack_.pop_back();
break;
case 'u':
++p_;
++column_;
state_stack_.back() = path_state::escape_u1;
break;
default:
ec = jsonpath_errc::illegal_escaped_character;
return path_expression_type(alloc_);
}
break;
case path_state::escape_u1:
cp = append_to_codepoint(0, *p_, ec);
if (ec)
{
return path_expression_type(alloc_);
}
++p_;
++column_;
state_stack_.back() = path_state::escape_u2;
break;
case path_state::escape_u2:
cp = append_to_codepoint(cp, *p_, ec);
if (ec)
{
return path_expression_type(alloc_);
}
++p_;
++column_;
state_stack_.back() = path_state::escape_u3;
break;
case path_state::escape_u3:
cp = append_to_codepoint(cp, *p_, ec);
if (ec)
{
return path_expression_type(alloc_);
}
++p_;
++column_;
state_stack_.back() = path_state::escape_u4;
break;
case path_state::escape_u4:
cp = append_to_codepoint(cp, *p_, ec);
if (ec)
{
return path_expression_type(alloc_);
}
if (unicode_traits::is_high_surrogate(cp))
{
++p_;
++column_;
state_stack_.back() = path_state::escape_expect_surrogate_pair1;
}
else
{
unicode_traits::convert(&cp, 1, buffer);
++p_;
++column_;
state_stack_.pop_back();
}
break;
case path_state::escape_expect_surrogate_pair1:
switch (*p_)
{
case '\\':
++p_;
++column_;
state_stack_.back() = path_state::escape_expect_surrogate_pair2;
break;
default:
ec = jsonpath_errc::invalid_codepoint;
return path_expression_type(alloc_);
}
break;
case path_state::escape_expect_surrogate_pair2:
switch (*p_)
{
case 'u':
++p_;
++column_;
state_stack_.back() = path_state::escape_u5;
break;
default:
ec = jsonpath_errc::invalid_codepoint;
return path_expression_type(alloc_);
}
break;
case path_state::escape_u5:
cp2 = append_to_codepoint(0, *p_, ec);
if (ec)
{
return path_expression_type(alloc_);
}
++p_;
++column_;
state_stack_.back() = path_state::escape_u6;
break;
case path_state::escape_u6:
cp2 = append_to_codepoint(cp2, *p_, ec);
if (ec)
{
return path_expression_type(alloc_);
}
++p_;
++column_;
state_stack_.back() = path_state::escape_u7;
break;
case path_state::escape_u7:
cp2 = append_to_codepoint(cp2, *p_, ec);
if (ec)
{
return path_expression_type(alloc_);
}
++p_;
++column_;
state_stack_.back() = path_state::escape_u8;
break;
case path_state::escape_u8:
{
cp2 = append_to_codepoint(cp2, *p_, ec);
if (ec)
{
return path_expression_type(alloc_);
}
uint32_t codepoint = 0x10000 + ((cp & 0x3FF) << 10) + (cp2 & 0x3FF);
unicode_traits::convert(&codepoint, 1, buffer);
state_stack_.pop_back();
++p_;
++column_;
break;
}
case path_state::filter_expression:
{
switch(*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ',':
case ']':
{
push_token(resources, token_type(end_filter_arg), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
break;
}
default:
ec = jsonpath_errc::expected_comma_or_rbracket;
return path_expression_type(alloc_);
}
break;
}
case path_state::expression:
{
switch(*p_)
{
case ' ':case '\t':case '\r':case '\n':
advance_past_space_character();
break;
case ',':
case ']':
{
push_token(resources, token_type(end_index_expression_arg), ec);
if (ec) {return path_expression_type(alloc_);}
state_stack_.pop_back();
break;
}
default:
ec = jsonpath_errc::expected_comma_or_rbracket;
return path_expression_type(alloc_);
}
break;
}
default:
++p_;
++column_;
break;
}
}
if (state_stack_.empty())
{
ec = jsonpath_errc::syntax_error;
return path_expression_type(alloc_);
}
while (state_stack_.size() > 1)
{
switch (state_stack_.back())
{
case path_state::name_or_lbracket:
state_stack_.back() = path_state::relative_path;
break;
case path_state::relative_path:
state_stack_.back() = path_state::identifier_or_function_expr;
state_stack_.emplace_back(path_state::unquoted_string);
break;
case path_state::identifier_or_function_expr:
if (!buffer.empty()) // Can't be quoted string
{
push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
if (ec) {return path_expression_type(alloc_);}
}
state_stack_.pop_back();
break;
case path_state::unquoted_string:
state_stack_.pop_back(); // unquoted_string
break;
case path_state::relative_location:
state_stack_.pop_back();
break;
case path_state::identifier:
if (!buffer.empty()) // Can't be quoted string
{
push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
if (ec) {return path_expression_type(alloc_);}
}
state_stack_.pop_back();
break;
case path_state::parent_operator:
{
push_token(resources, token_type(resources.new_selector(parent_node_selector<Json,JsonReference>(ancestor_depth))), ec);
if (ec) { return path_expression_type(alloc_); }
paths_required = true;
state_stack_.pop_back();
break;
}
case path_state::ancestor_depth:
state_stack_.pop_back();
break;
default:
ec = jsonpath_errc::syntax_error;
return path_expression_type(alloc_);
}
}
if (state_stack_.size() > 2)
{
ec = jsonpath_errc::unexpected_eof;
return path_expression_type(alloc_);
}
//std::cout << "\nTokens\n\n";
//for (const auto& tok : output_stack_)
//{
// std::cout << tok.to_string() << "\n";
//}
//std::cout << "\n";
if (output_stack_.empty() || !operator_stack_.empty())
{
ec = jsonpath_errc::unexpected_eof;
return path_expression_type(alloc_);
}
return path_expression_type(output_stack_.back().selector_, paths_required, alloc_);
}
void advance_past_space_character()
{
switch (*p_)
{
case ' ':case '\t':
++p_;
++column_;
break;
case '\r':
if (p_+1 < end_input_ && *(p_+1) == '\n')
++p_;
++line_;
column_ = 1;
++p_;
break;
case '\n':
++line_;
column_ = 1;
++p_;
break;
default:
break;
}
}
void unwind_rparen(std::error_code& ec)
{
auto it = operator_stack_.rbegin();
while (it != operator_stack_.rend() && !it->is_lparen())
{
output_stack_.emplace_back(std::move(*it));
++it;
}
if (it == operator_stack_.rend())
{
ec = jsonpath_errc::unbalanced_parentheses;
return;
}
++it;
operator_stack_.erase(it.base(),operator_stack_.end());
}
void push_token(jsoncons::jsonpath::detail::static_resources<value_type,reference>& resources, token_type&& tok, std::error_code& ec)
{
//std::cout << tok.to_string() << "\n";
switch (tok.token_kind())
{
case jsonpath_token_kind::begin_filter:
output_stack_.emplace_back(std::move(tok));
operator_stack_.emplace_back(token_type(lparen_arg));
break;
case jsonpath_token_kind::end_filter:
{
//std::cout << "push_token end_filter 1\n";
//for (const auto& tok2 : output_stack_)
//{
// std::cout << tok2.to_string() << "\n";
//}
//std::cout << "\n\n";
unwind_rparen(ec);
if (ec)
{
return;
}
std::vector<token_type> toks;
auto it = output_stack_.rbegin();
while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_filter)
{
toks.emplace_back(std::move(*it));
++it;
}
if (it == output_stack_.rend())
{
ec = jsonpath_errc::unbalanced_parentheses;
return;
}
std::reverse(toks.begin(), toks.end());
++it;
output_stack_.erase(it.base(),output_stack_.end());
if (!output_stack_.empty() && output_stack_.back().is_path())
{
output_stack_.back().selector_->append_selector(resources.new_selector(filter_selector<Json,JsonReference>(expression_type(std::move(toks)))));
}
else
{
output_stack_.emplace_back(token_type(resources.new_selector(filter_selector<Json,JsonReference>(expression_type(std::move(toks))))));
}
//std::cout << "push_token end_filter 2\n";
//for (const auto& tok2 : output_stack_)
//{
// std::cout << tok2.to_string() << "\n";
//}
//std::cout << "\n\n";
break;
}
case jsonpath_token_kind::begin_expression:
//std::cout << "begin_expression\n";
output_stack_.emplace_back(std::move(tok));
operator_stack_.emplace_back(token_type(lparen_arg));
break;
case jsonpath_token_kind::end_index_expression:
{
//std::cout << "jsonpath_token_kind::end_index_expression\n";
//for (const auto& t : output_stack_)
//{
// std::cout << t.to_string() << "\n";
//}
//std::cout << "/jsonpath_token_kind::end_index_expression\n";
unwind_rparen(ec);
if (ec)
{
return;
}
std::vector<token_type> toks;
auto it = output_stack_.rbegin();
while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_expression)
{
toks.emplace_back(std::move(*it));
++it;
}
if (it == output_stack_.rend())
{
ec = jsonpath_errc::unbalanced_parentheses;
return;
}
std::reverse(toks.begin(), toks.end());
++it;
output_stack_.erase(it.base(),output_stack_.end());
if (!output_stack_.empty() && output_stack_.back().is_path())
{
output_stack_.back().selector_->append_selector(resources.new_selector(index_expression_selector<Json,JsonReference>(expression_type(std::move(toks)))));
}
else
{
output_stack_.emplace_back(token_type(resources.new_selector(index_expression_selector<Json,JsonReference>(expression_type(std::move(toks))))));
}
break;
}
case jsonpath_token_kind::end_argument_expression:
{
//std::cout << "jsonpath_token_kind::end_index_expression\n";
//for (const auto& t : output_stack_)
//{
// std::cout << t.to_string() << "\n";
//}
//std::cout << "/jsonpath_token_kind::end_index_expression\n";
unwind_rparen(ec);
if (ec)
{
return;
}
std::vector<token_type> toks;
auto it = output_stack_.rbegin();
while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_expression)
{
toks.emplace_back(std::move(*it));
++it;
}
if (it == output_stack_.rend())
{
ec = jsonpath_errc::unbalanced_parentheses;
return;
}
std::reverse(toks.begin(), toks.end());
++it;
output_stack_.erase(it.base(),output_stack_.end());
output_stack_.emplace_back(token_type(jsoncons::make_unique<expression_type>(std::move(toks))));
break;
}
case jsonpath_token_kind::selector:
{
if (!output_stack_.empty() && output_stack_.back().is_path())
{
output_stack_.back().selector_->append_selector(std::move(tok.selector_));
}
else
{
output_stack_.emplace_back(std::move(tok));
}
break;
}
case jsonpath_token_kind::separator:
output_stack_.emplace_back(std::move(tok));
break;
case jsonpath_token_kind::begin_union:
output_stack_.emplace_back(std::move(tok));
break;
case jsonpath_token_kind::end_union:
{
std::vector<selector_type*> expressions;
auto it = output_stack_.rbegin();
while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_union)
{
if (it->token_kind() == jsonpath_token_kind::selector)
{
expressions.emplace_back(std::move(it->selector_));
}
do
{
++it;
}
while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_union && it->token_kind() != jsonpath_token_kind::separator);
if (it->token_kind() == jsonpath_token_kind::separator)
{
++it;
}
}
if (it == output_stack_.rend())
{
ec = jsonpath_errc::unbalanced_parentheses;
return;
}
std::reverse(expressions.begin(), expressions.end());
++it;
output_stack_.erase(it.base(),output_stack_.end());
if (!output_stack_.empty() && output_stack_.back().is_path())
{
output_stack_.back().selector_->append_selector(resources.new_selector(union_selector<Json,JsonReference>(std::move(expressions))));
}
else
{
output_stack_.emplace_back(token_type(resources.new_selector(union_selector<Json,JsonReference>(std::move(expressions)))));
}
break;
}
case jsonpath_token_kind::lparen:
operator_stack_.emplace_back(std::move(tok));
break;
case jsonpath_token_kind::rparen:
{
unwind_rparen(ec);
break;
}
case jsonpath_token_kind::end_function:
{
//std::cout << "jsonpath_token_kind::end_function\n";
unwind_rparen(ec);
if (ec)
{
return;
}
std::vector<token_type> toks;
auto it = output_stack_.rbegin();
std::size_t arg_count = 0;
while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::function)
{
if (it->token_kind() == jsonpath_token_kind::argument)
{
++arg_count;
}
toks.emplace_back(std::move(*it));
++it;
}
if (it == output_stack_.rend())
{
ec = jsonpath_errc::unbalanced_parentheses;
return;
}
std::reverse(toks.begin(), toks.end());
if (it->arity() && arg_count != *(it->arity()))
{
ec = jsonpath_errc::invalid_arity;
return;
}
toks.push_back(std::move(*it));
++it;
output_stack_.erase(it.base(),output_stack_.end());
if (!output_stack_.empty() && output_stack_.back().is_path())
{
output_stack_.back().selector_->append_selector(resources.new_selector(function_selector<Json,JsonReference>(expression_type(std::move(toks)))));
}
else
{
output_stack_.emplace_back(token_type(resources.new_selector(function_selector<Json,JsonReference>(std::move(toks)))));
}
break;
}
case jsonpath_token_kind::literal:
if (!output_stack_.empty() && (output_stack_.back().token_kind() == jsonpath_token_kind::current_node || output_stack_.back().token_kind() == jsonpath_token_kind::root_node))
{
output_stack_.back() = std::move(tok);
}
else
{
output_stack_.emplace_back(std::move(tok));
}
break;
case jsonpath_token_kind::function:
output_stack_.emplace_back(std::move(tok));
operator_stack_.emplace_back(token_type(lparen_arg));
break;
case jsonpath_token_kind::argument:
output_stack_.emplace_back(std::move(tok));
break;
case jsonpath_token_kind::root_node:
case jsonpath_token_kind::current_node:
output_stack_.emplace_back(std::move(tok));
break;
case jsonpath_token_kind::unary_operator:
case jsonpath_token_kind::binary_operator:
{
if (operator_stack_.empty() || operator_stack_.back().is_lparen())
{
operator_stack_.emplace_back(std::move(tok));
}
else if (tok.precedence_level() < operator_stack_.back().precedence_level()
|| (tok.precedence_level() == operator_stack_.back().precedence_level() && tok.is_right_associative()))
{
operator_stack_.emplace_back(std::move(tok));
}
else
{
auto it = operator_stack_.rbegin();
while (it != operator_stack_.rend() && it->is_operator()
&& (tok.precedence_level() > it->precedence_level()
|| (tok.precedence_level() == it->precedence_level() && tok.is_right_associative())))
{
output_stack_.emplace_back(std::move(*it));
++it;
}
operator_stack_.erase(it.base(),operator_stack_.end());
operator_stack_.emplace_back(std::move(tok));
}
break;
}
default:
break;
}
//std::cout << " " << "Output Stack\n";
//for (auto&& t : output_stack_)
//{
// std::cout << t.to_string(2) << "\n";
//}
//if (!operator_stack_.empty())
//{
// std::cout << " " << "Operator Stack\n";
// for (auto&& t : operator_stack_)
// {
// std::cout << t.to_string(2) << "\n";
// }
//}
}
uint32_t append_to_codepoint(uint32_t cp, int c, std::error_code& ec)
{
cp *= 16;
if (c >= '0' && c <= '9')
{
cp += c - '0';
}
else if (c >= 'a' && c <= 'f')
{
cp += c - 'a' + 10;
}
else if (c >= 'A' && c <= 'F')
{
cp += c - 'A' + 10;
}
else
{
ec = jsonpath_errc::invalid_codepoint;
}
return cp;
}
};
template <class Json, class JsonReference = const Json&>
struct jsonpath_traits
{
using char_type = typename Json::char_type;
using string_type = typename Json::string_type;
using string_view_type = typename Json::string_view_type;
using value_type = Json;
using reference = JsonReference;
using pointer = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value, typename Json::const_pointer, typename Json::pointer>::type;
using allocator_type = typename Json::allocator_type;
using evaluator_type = typename jsoncons::jsonpath::detail::jsonpath_evaluator<Json, JsonReference>;
using json_location_node_type = json_location_node<string_type>;
using json_location_type = json_location<string_type>;
using path_expression_type = path_expression<Json,JsonReference>;
using path_pointer = const json_location_node_type*;
};
} // namespace detail
template <class Json,class JsonReference = const Json&>
class jsonpath_expression
{
public:
using jsonpath_traits_type = jsoncons::jsonpath::detail::jsonpath_traits<Json, JsonReference>;
using allocator_type = typename jsonpath_traits_type::allocator_type;
using evaluator_type = typename jsonpath_traits_type::evaluator_type;
using char_type = typename jsonpath_traits_type::char_type;
using string_type = typename jsonpath_traits_type::string_type;
using string_view_type = typename jsonpath_traits_type::string_view_type;
using value_type = typename jsonpath_traits_type::value_type;
using reference = typename jsonpath_traits_type::reference;
using path_expression_type = typename jsonpath_traits_type::path_expression_type;
using json_location_type = typename jsonpath_traits_type::json_location_type;
private:
allocator_type alloc_;
std::unique_ptr<jsoncons::jsonpath::detail::static_resources<value_type,reference>> static_resources_;
path_expression_type expr_;
public:
jsonpath_expression(std::unique_ptr<jsoncons::jsonpath::detail::static_resources<value_type,reference>>&& resources,
path_expression_type&& expr, const allocator_type& alloc = allocator_type())
: alloc_(alloc),
static_resources_(std::move(resources)),
expr_(std::move(expr))
{
}
jsonpath_expression(const jsonpath_expression&) = delete;
jsonpath_expression(jsonpath_expression&&) = default;
jsonpath_expression& operator=(const jsonpath_expression&) = delete;
jsonpath_expression& operator=(jsonpath_expression&&) = default;
template <class BinaryCallback>
typename std::enable_if<extension_traits::is_binary_function_object<BinaryCallback,const string_type&,reference>::value,void>::type
evaluate(reference instance, BinaryCallback callback, result_options options = result_options()) const
{
jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources{alloc_};
auto f = [&callback](const json_location_type& path, reference val)
{
callback(path.to_string(), val);
};
expr_.evaluate(resources, instance, resources.root_path_node(), instance, f, options);
}
Json evaluate(reference instance, result_options options = result_options()) const
{
if ((options & result_options::path) == result_options::path)
{
jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources{alloc_};
Json result(json_array_arg, semantic_tag::none, alloc_);
auto callback = [&result](const json_location_type& p, reference)
{
result.emplace_back(p.to_string());
};
expr_.evaluate(resources, instance, resources.root_path_node(), instance, callback, options);
return result;
}
else
{
jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources{alloc_};
return expr_.evaluate(resources, instance, resources.current_path_node(), instance, options);
}
}
};
template <class Json>
jsonpath_expression<Json> make_expression(const typename Json::string_view_type& expr,
const custom_functions<Json>& functions = custom_functions<Json>())
{
return make_expression(std::allocator_arg, std::allocator<char>(), expr, functions);
}
template <class Json>
jsonpath_expression<Json> make_expression(const typename Json::string_view_type& expr, std::error_code& ec)
{
return make_expression(std::allocator_arg, std::allocator<char>(), expr, custom_functions<Json>(), ec);
}
template <class Json>
jsonpath_expression<Json> make_expression(const typename Json::string_view_type& expr,
const custom_functions<Json>& functions,
std::error_code& ec)
{
return make_expression(std::allocator_arg, std::allocator<char>(), expr, functions, ec);
}
template <class Json, class Alloc>
jsonpath_expression<Json> make_expression(std::allocator_arg_t, Alloc alloc,
const typename Json::string_view_type& expr, std::error_code& ec)
{
return make_expression(std::allocator_arg, alloc, expr, custom_functions<Json>(), ec);
}
template <class Json, class Alloc>
jsonpath_expression<Json> make_expression(std::allocator_arg_t, Alloc alloc, const typename Json::string_view_type& path,
const custom_functions<Json>& functions = custom_functions<Json>())
{
using jsonpath_traits_type = jsoncons::jsonpath::detail::jsonpath_traits<Json>;
using value_type = typename jsonpath_traits_type::value_type;
using reference = typename jsonpath_traits_type::reference;
using evaluator_type = typename jsonpath_traits_type::evaluator_type;
using path_expression_type = typename jsonpath_traits_type::path_expression_type;
auto resources = jsoncons::make_unique<jsoncons::jsonpath::detail::static_resources<value_type,reference>>(functions, alloc);
evaluator_type evaluator{alloc};
path_expression_type expr = evaluator.compile(*resources, path);
return jsonpath_expression<Json>(std::move(resources), std::move(expr), alloc);
}
template <class Json, class Alloc>
jsonpath_expression<Json> make_expression(std::allocator_arg_t, Alloc alloc,
const typename Json::string_view_type& path,
const custom_functions<Json>& functions, std::error_code& ec)
{
using jsonpath_traits_type = jsoncons::jsonpath::detail::jsonpath_traits<Json>;
using value_type = typename jsonpath_traits_type::value_type;
using reference = typename jsonpath_traits_type::reference;
using evaluator_type = typename jsonpath_traits_type::evaluator_type;
using path_expression_type = typename jsonpath_traits_type::path_expression_type;
auto resources = jsoncons::make_unique<jsoncons::jsonpath::detail::static_resources<value_type,reference>>(functions, alloc);
evaluator_type evaluator{alloc};
path_expression_type expr = evaluator.compile(*resources, path, ec);
return jsonpath_expression<Json>(std::move(resources), std::move(expr), alloc);
}
} // namespace jsonpath
} // namespace jsoncons
#endif