From cd45ccb723fef4413c33f3fb018f04dc2a553f09 Mon Sep 17 00:00:00 2001 From: Leonard Rosenthol Date: Mon, 12 Feb 2024 11:19:31 -0500 Subject: [PATCH] work in progress to reorganize the code into multiple files --- .DS_Store | Bin 10244 -> 10244 bytes .vscode/settings.json | 29 +- json-formula.xcodeproj/project.pbxproj | 6 +- .../UserInterfaceState.xcuserstate | Bin 58665 -> 56839 bytes .../xcschemes/json-formula.xcscheme | 4 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 128 +- json-formula/json-formula-declarations.hpp | 306 + ...rmula_error.hpp => json-formula-error.hpp} | 0 json-formula/json-formula-evaluator.hpp | 4713 ++++++++++++ json-formula/json-formula-functions.hpp | 1325 ++++ json-formula/json-formula-resources.hpp | 92 + json-formula/json-formula.hpp | 6437 +---------------- 12 files changed, 6492 insertions(+), 6548 deletions(-) create mode 100644 json-formula/json-formula-declarations.hpp rename json-formula/{json-formula_error.hpp => json-formula-error.hpp} (100%) create mode 100644 json-formula/json-formula-evaluator.hpp create mode 100644 json-formula/json-formula-functions.hpp create mode 100644 json-formula/json-formula-resources.hpp diff --git a/.DS_Store b/.DS_Store index 0bde2b21ab872f986c60e4866af9fda3710abdbb..5cf97e3de8fe0f01da16ff89093859741cfa80cf 100644 GIT binary patch delta 112 zcmZn(XbIS$B+kslCNp`hxPee^zKcszPJR*t0|Q6h-QZ@1wUbwi8%>@m&cg=hIxq%o Pej{$iPpYQLZ^R`4-}fZV delta 112 zcmZn(XbIS$B+ktEcFp9q;s!#w`7SO=Ir&Kp3=A9>9}E6YTr_#LxY6X9;yi3{t^=dN Q<~QPI{G@7{{6<^?0AQsn4FCWD diff --git a/.vscode/settings.json b/.vscode/settings.json index 45c78bf..14b23ae 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,6 +9,33 @@ "*.mp": "python", "*.joboptions": "postscript", "*.icml": "xml", - "numbers": "cpp" + "numbers": "cpp", + "__bit_reference": "cpp", + "__hash_table": "cpp", + "__split_buffer": "cpp", + "__tree": "cpp", + "array": "cpp", + "bitset": "cpp", + "deque": "cpp", + "iterator": "cpp", + "hash_map": "cpp", + "forward_list": "cpp", + "initializer_list": "cpp", + "list": "cpp", + "map": "cpp", + "queue": "cpp", + "regex": "cpp", + "set": "cpp", + "span": "cpp", + "stack": "cpp", + "string": "cpp", + "string_view": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "filesystem": "cpp", + "chrono": "cpp", + "format": "cpp", + "type_traits": "cpp" } } \ No newline at end of file diff --git a/json-formula.xcodeproj/project.pbxproj b/json-formula.xcodeproj/project.pbxproj index c6a1acf..e078d75 100644 --- a/json-formula.xcodeproj/project.pbxproj +++ b/json-formula.xcodeproj/project.pbxproj @@ -28,8 +28,7 @@ BE2D2C6F2AE2FB3300D04840 /* utest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = utest.h; path = utest.h/utest.h; sourceTree = ""; }; BE653A1C2AE16D9C00EDDA36 /* json-formula */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "json-formula"; sourceTree = BUILT_PRODUCTS_DIR; }; BE7B01812B47563C0070674C /* jfHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jfHelpers.h; sourceTree = ""; }; - BE9C1D632B0C3A0A00B6AD6A /* json-formula.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = "json-formula.hpp"; path = "json-formula/json-formula.hpp"; sourceTree = ""; }; - BE9C1D642B0C3A0A00B6AD6A /* json-formula_error.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = "json-formula_error.hpp"; path = "json-formula/json-formula_error.hpp"; sourceTree = ""; }; + BE97DD2A2B5A0103001D25AE /* json-formula */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "json-formula"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -48,10 +47,9 @@ children = ( BE2D2C512AE17A9900D04840 /* main.cpp */, BE7B01812B47563C0070674C /* jfHelpers.h */, - BE9C1D642B0C3A0A00B6AD6A /* json-formula_error.hpp */, - BE9C1D632B0C3A0A00B6AD6A /* json-formula.hpp */, BE2D2C552AE17AC700D04840 /* json.hpp */, BE2D2C6F2AE2FB3300D04840 /* utest.h */, + BE97DD2A2B5A0103001D25AE /* json-formula */, BE653A1D2AE16D9C00EDDA36 /* Products */, ); sourceTree = ""; diff --git a/json-formula.xcodeproj/project.xcworkspace/xcuserdata/lrosenth.xcuserdatad/UserInterfaceState.xcuserstate b/json-formula.xcodeproj/project.xcworkspace/xcuserdata/lrosenth.xcuserdatad/UserInterfaceState.xcuserstate index 2be4c7ddc1e7201938abcd9fbaec4eb01f3466d3..6914f6609731ef4436f5afb297b7a53439f14be3 100644 GIT binary patch delta 28182 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%0DUa=ST z%DoNVgty@R%KNqVJMT&FY44BTYu-EF2j0itr{3qjiN5K+4Zf{D#E1D*KDDpWx6jw& zYx6mLE}z$z^mX{Mn6Kcw>bvQC;2-NR^RM?a{D>d(tNdzzqrcg|$G^{iz;E+A{T{#H z-|mn2WB#A~cLMza(*i33tN;?=2ZRAd01GGsmViAF4#Wb`u`0I76nHJ#{}OB&JJ!1(u4IuSr7|is-Qa97;Fyi3GNH(gQlQ07!4+ZsbD6U z4}K9m96T009{e`=eej3inc&U#k?oV(XSZ)}=eFzHz3u7t^X-3y-UtYg2SRNjd#E#XDclPSj|oo< zPYzdvr-x^S=Y&^>H;1=}31L#066S=FurMqMOT((LI;;sdg?EQr!`^T%d^CI_d_H_F zd_DYo_*VFS_)+*t_*u9o(l;_VQW7bRlts!T??fg?7DiS_)<$+jK8X+_#7K3dCc=)? zM(QJYL>tjX_D1$cFhj%~aYek5KqM52MiP;9BojFr>56nmdZPWJlcH0i^P-EQOQS2I ztD+mCo1V^3nwVm-0HvG{=ap!kq@QM@ERIbIQ;7M~ek6#sX8 zLws|5Yn&8E;^O%3xG`>vJL8`C-T0G4@5JkgqQvw>RRT<4iCu}-L_YCF;&9?<;y;OR z65l1hPyCR$mAIR@mw1?Xl6aPQk$f#VAUP;GG&wvuGC3hRF*!L|kzAA9o+KnmNvtYa zo2*aDl8U4O@E$lOFPo;v@ab zJe^FRPT%hs(J`-s*rDujbsX*ZHPb88Kl5g0aAs(xI5QzLF*7++k(r*EnOT%snpu%q zm06ovpJ8Xj8CeF)s4|*NQ)YMO)6Bt4FcZxrGU*JK&wP^HG4m+%S9V0UI6Eu5 zB1_Ivvp|-aWo2u#ysRXteD-4YO7>3nS+*zp zGS@p-nybi7&&|rcms^(mD7PxNHn$_!kTd0ixz5~~+>P9e{NVi1d{KU6essP#KLO2G z}DKb?@H)>mf7#*Z-dd&Hn*h C5WAuP literal 58665 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 diff --git a/json-formula.xcodeproj/xcshareddata/xcschemes/json-formula.xcscheme b/json-formula.xcodeproj/xcshareddata/xcschemes/json-formula.xcscheme index a699ded..74800e7 100644 --- a/json-formula.xcodeproj/xcshareddata/xcschemes/json-formula.xcscheme +++ b/json-formula.xcodeproj/xcshareddata/xcschemes/json-formula.xcscheme @@ -53,7 +53,7 @@ + isEnabled = "YES"> + isEnabled = "NO"> + endingLineNumber = "6385"> + endingLineNumber = "1987"> + endingLineNumber = "1992"> + endingLineNumber = "2002"> + endingLineNumber = "4556"> - - - - + endingLineNumber = "4608"> + endingLineNumber = "6147"> + endingLineNumber = "6185"> + endingLineNumber = "5772"> + endingLineNumber = "4306"> + endingLineNumber = "4924"> + endingLineNumber = "5729"> + endingLineNumber = "3157"> + endingLineNumber = "3809"> + endingLineNumber = "3538"> - - - - - - - - - - + endingLineNumber = "6503"> start_; + jsoncons::optional stop_; + int64_t step_; + + slice() + : start_(), stop_(), step_(1) + { + } + + slice(const jsoncons::optional& start, const jsoncons::optional& end, int64_t step) + : start_(start), stop_(end), step_(step) + { + } + + slice(const slice& other) + : start_(other.start_), stop_(other.stop_), step_(other.step_) + { + } + + slice& operator=(const slice& rhs) + { + if (this != &rhs) + { + if (rhs.start_) + { + start_ = rhs.start_; + } + else + { + start_.reset(); + } + if (rhs.stop_) + { + stop_ = rhs.stop_; + } + else + { + stop_.reset(); + } + step_ = rhs.step_; + } + return *this; + } + + int64_t get_start(std::size_t size) const + { + if (start_) + { + auto len = *start_ >= 0 ? *start_ : (static_cast(size) + *start_); + return len <= static_cast(size) ? len : static_cast(size); + } + else + { + if (step_ >= 0) + { + return 0; + } + else + { + return static_cast(size); + } + } + } + + int64_t get_stop(std::size_t size) const + { + if (stop_) + { + auto len = *stop_ >= 0 ? *stop_ : (static_cast(size) + *stop_); + return len <= static_cast(size) ? len : static_cast(size); + } + else + { + return step_ >= 0 ? static_cast(size) : -1; + } + } + + int64_t step() const + { + return step_; // Allow negative + } + }; + + +} diff --git a/json-formula/json-formula_error.hpp b/json-formula/json-formula-error.hpp similarity index 100% rename from json-formula/json-formula_error.hpp rename to json-formula/json-formula-error.hpp diff --git a/json-formula/json-formula-evaluator.hpp b/json-formula/json-formula-evaluator.hpp new file mode 100644 index 0000000..3406a50 --- /dev/null +++ b/json-formula/json-formula-evaluator.hpp @@ -0,0 +1,4713 @@ +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, + // json-formula + jf_expression, + plus_operator, + minus_operator, + mult_operator, + div_operator, + concat_operator, + union_operator +}; + + +template +class jsonformula_evaluator +{ +public: + typedef typename Json::char_type char_type; + typedef typename Json::char_traits_type char_traits_type; + typedef std::basic_string string_type; + typedef typename Json::string_view_type string_view_type; + typedef JsonReference reference; + using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + typedef typename Json::const_pointer const_pointer; + + // 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& resources, std::error_code& ec) const = 0; + + virtual void add_expression(std::unique_ptr&& 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_; + } + }; + + #include "json-formula-functions.hpp" + + // operators + 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_number() && ref.template as()==0) || // json-formula also treats 0 as false + 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&, 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& 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&, 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; + } + }; + + + // token + + class token + { + public: + token_kind type_; + + union + { + std::unique_ptr 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) + : type_(token_kind::expression) + { + new (&expression_) std::unique_ptr(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(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(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 arity() const + { + return type_ == token_kind::function ? function_->arity() : jsoncons::optional(); + } + + 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(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& output_stack, dynamic_resources& resources, std::error_code& ec) + { + pointer root_ptr = std::addressof(doc); + std::vector stack; + std::vector 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 = jsonformula_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& 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"); + 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&, 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"); + 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& 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"); + 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& 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"); + 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& resources, std::error_code&) const override + { + if ( lhs.is_number() && rhs.is_number() ) { + return lhs < rhs ? resources.true_value() : resources.false_value(); + } else if ( lhs.is_string() && rhs.is_string() ) { + return lhs < rhs ? resources.true_value() : resources.false_value(); + } else if ( lhs.is_number() && rhs.is_string() ) { + return lhs < std::stod(rhs.template as()) ? resources.true_value() : resources.false_value(); + } else if ( lhs.is_string() && rhs.is_number() ) { + return lhs < std::to_string(rhs.template as()) ? resources.true_value() : resources.false_value(); + } else { + // for any other type comparisons, return false + return 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"); + 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& resources, std::error_code&) const override + { + if ( lhs.is_number() && rhs.is_number() ) { + return lhs <= rhs ? resources.true_value() : resources.false_value(); + } else if ( lhs.is_string() && rhs.is_string() ) { + return lhs <= rhs ? resources.true_value() : resources.false_value(); + } else if ( lhs.is_number() && rhs.is_string() ) { + return lhs <= std::stod(rhs.template as()) ? resources.true_value() : resources.false_value(); + } else if ( lhs.is_string() && rhs.is_number() ) { + return lhs <= std::to_string(rhs.template as()) ? resources.true_value() : resources.false_value(); + } else { + // for any other type comparisons, return false + return 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"); + 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& resources, std::error_code&) const override + { + if ( lhs.is_number() && rhs.is_number() ) { + return lhs > rhs ? resources.true_value() : resources.false_value(); + } else if ( lhs.is_string() && rhs.is_string() ) { + return lhs > rhs ? resources.true_value() : resources.false_value(); + } else if ( lhs.is_number() && rhs.is_string() ) { + return lhs > std::stod(rhs.template as()) ? resources.true_value() : resources.false_value(); + } else if ( lhs.is_string() && rhs.is_number() ) { + return lhs > std::to_string(rhs.template as()) ? resources.true_value() : resources.false_value(); + } else { + // for any other type comparisons, return false + return 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"); + 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& resources, std::error_code&) const override + { + if ( lhs.is_number() && rhs.is_number() ) { + return lhs >= rhs ? resources.true_value() : resources.false_value(); + } else if ( lhs.is_string() && rhs.is_string() ) { + return lhs >= rhs ? resources.true_value() : resources.false_value(); + } else if ( lhs.is_number() && rhs.is_string() ) { + return lhs >= std::stod(rhs.template as()) ? resources.true_value() : resources.false_value(); + } else if ( lhs.is_string() && rhs.is_number() ) { + return lhs >= std::to_string(rhs.template as()) ? resources.true_value() : resources.false_value(); + } else { + // for any other type comparisons, return false + return 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"); + return s; + } + }; + + // for json-fornula + class plus_operator final : public binary_operator + { + public: + plus_operator() + : binary_operator(operator_kind::plus_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override + { + if ( (lhs.is_number() && rhs.is_number()) || + (lhs.is_null() || rhs.is_null()) || + (lhs.is_bool() || rhs.is_bool()) ) { + double l = 0, r = 0; + + if ( lhs.type() == jsoncons::json_type::double_value ) { + l = lhs.template as(); + } else if ( lhs.type() == jsoncons::json_type::string_value ) { + l = std::stod(lhs.template as()); + } else if ( lhs.type() == jsoncons::json_type::bool_value ) { + l = lhs.template as() ? 1 : 0; + } else if ( !lhs.is_null() ) { + l = lhs.template as(); + } else { +// std::cout << pretty_print(lhs) << std::endl; + } + + + if ( rhs.type() == jsoncons::json_type::double_value ) { + r = rhs.template as(); + } else if ( rhs.type() == jsoncons::json_type::string_value ) { + r = std::stod(rhs.template as()); + } else if ( rhs.type() == jsoncons::json_type::bool_value ) { + r = rhs.template as() ? 1 : 0; + } else if ( !rhs.is_null() ){ + r = rhs.template as(); + } else { +// std::cout << pretty_print(rhs) << std::endl; + } + + return *resources.create_json(l + r); + } else if ( lhs.type() == jsoncons::json_type::string_value || rhs.type() == jsoncons::json_type::string_value ) { + double l = 0, r = 0; + + if ( lhs.type() == jsoncons::json_type::string_value ) + l = std::stod(lhs.template as()); + else if ( lhs.type() != jsoncons::json_type::null_value ) + l = lhs.template as(); + + if ( rhs.type() == jsoncons::json_type::string_value ) + r = std::stod(rhs.template as()); + else if ( rhs.type() != jsoncons::json_type::null_value ) + r = rhs.template as(); + + return *resources.create_json(l + r); + } else if ( lhs.type() == jsoncons::json_type::array_value && rhs.type() == jsoncons::json_type::array_value ) { + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + auto numEnts = std::max( lhs.size(), rhs.size() ); + for (int64_t i=0; i(); + if ( i < rhs.size() ) r = rhs[i].template as(); + result->push_back( l + r); + } + return *result; + } else if ( lhs.type() == jsoncons::json_type::array_value ) { + double d = rhs.template as(); + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + for (auto& v : lhs.array_range()) { + result->push_back( v.template as() + d ); + } + return *result; + } else if ( rhs.type() == jsoncons::json_type::array_value ) { + double d = lhs.template as(); + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + for (auto& v : rhs.array_range()) { + result->push_back( v.template as() + d ); + } + return *result; + } else { + std::cout << pretty_print(lhs) << " " << pretty_print(rhs) << std::endl; + } + + 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("plus_operator"); + return s; + } + }; + + class minus_operator final : public binary_operator + { + public: + minus_operator() + : binary_operator(operator_kind::minus_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override + { + if ( (lhs.is_number() && rhs.is_number()) || + (lhs.is_null() || rhs.is_null()) || + (lhs.is_bool() || rhs.is_bool()) ) { + double l = 0, r = 0; + + if ( lhs.type() == jsoncons::json_type::double_value ) { + l = lhs.template as(); + } else if ( lhs.type() == jsoncons::json_type::string_value ) { + l = std::stod(lhs.template as()); + } else if ( lhs.type() == jsoncons::json_type::bool_value ) { + l = lhs.template as() ? 1 : 0; + } else if ( !lhs.is_null() ) { + l = lhs.template as(); + } else { +// std::cout << pretty_print(lhs) << std::endl; + } + + + if ( rhs.type() == jsoncons::json_type::double_value ) { + r = rhs.template as(); + } else if ( rhs.type() == jsoncons::json_type::string_value ) { + r = std::stod(rhs.template as()); + } else if ( rhs.type() == jsoncons::json_type::bool_value ) { + r = rhs.template as() ? 1 : 0; + } else if ( !rhs.is_null() ){ + r = rhs.template as(); + } else { +// std::cout << pretty_print(rhs) << std::endl; + } + + return *resources.create_json(l - r); + } else if ( lhs.type() == jsoncons::json_type::string_value || rhs.type() == jsoncons::json_type::string_value ) { + double l = 0, r = 0; + + if ( lhs.type() == jsoncons::json_type::string_value ) + l = std::stod(lhs.template as()); + else if ( lhs.type() != jsoncons::json_type::null_value ) + l = lhs.template as(); + + if ( rhs.type() == jsoncons::json_type::string_value ) + r = std::stod(rhs.template as()); + else if ( rhs.type() != jsoncons::json_type::null_value ) + r = rhs.template as(); + + return *resources.create_json(l - r); + } else if ( lhs.type() == jsoncons::json_type::array_value && rhs.type() == jsoncons::json_type::array_value ) { + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + auto numEnts = std::max( lhs.size(), rhs.size() ); + for (int64_t i=0; i(); + if ( i < rhs.size() ) r = rhs[i].template as(); + result->push_back( l - r); + } + return *result; + } else if ( lhs.type() == jsoncons::json_type::array_value ) { + double d = rhs.template as(); + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + for (auto& v : lhs.array_range()) { + result->push_back( v.template as() - d ); + } + return *result; + } else if ( rhs.type() == jsoncons::json_type::array_value ) { + double d = lhs.template as(); + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + for (auto& v : rhs.array_range()) { + result->push_back( d - v.template as() ); + } + return *result; + } + + 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("minus_operator"); + return s; + } + }; + + class mult_operator final : public binary_operator + { + public: + mult_operator() + : binary_operator(operator_kind::mult_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override + { + if ( (lhs.is_number() && rhs.is_number()) || + (lhs.is_null() || rhs.is_null()) || + (lhs.is_bool() || rhs.is_bool()) ) { + double l = 0, r = 0; + + if ( lhs.type() == jsoncons::json_type::double_value ) { + l = lhs.template as(); + } else if ( lhs.type() == jsoncons::json_type::string_value ) { + l = std::stod(lhs.template as()); + } else if ( lhs.type() == jsoncons::json_type::bool_value ) { + l = lhs.template as() ? 1 : 0; + } else if ( !lhs.is_null() ) { + l = lhs.template as(); + } else { +// std::cout << pretty_print(lhs) << std::endl; + } + + + if ( rhs.type() == jsoncons::json_type::double_value ) { + r = rhs.template as(); + } else if ( rhs.type() == jsoncons::json_type::string_value ) { + r = std::stod(rhs.template as()); + } else if ( rhs.type() == jsoncons::json_type::bool_value ) { + r = rhs.template as() ? 1 : 0; + } else if ( !rhs.is_null() ){ + r = rhs.template as(); + } else { +// std::cout << pretty_print(rhs) << std::endl; + } + + return *resources.create_json(l * r); + } else if ( lhs.type() == jsoncons::json_type::string_value || rhs.type() == jsoncons::json_type::string_value ) { + double l = 0, r = 0; + + if ( lhs.type() == jsoncons::json_type::string_value ) + l = std::stod(lhs.template as()); + else if ( lhs.type() != jsoncons::json_type::null_value ) + l = lhs.template as(); + + if ( rhs.type() == jsoncons::json_type::string_value ) + r = std::stod(rhs.template as()); + else if ( rhs.type() != jsoncons::json_type::null_value ) + r = rhs.template as(); + + return *resources.create_json(l * r); + } else if ( lhs.type() == jsoncons::json_type::array_value && rhs.type() == jsoncons::json_type::array_value ) { + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + auto numEnts = std::max( lhs.size(), rhs.size() ); + for (int64_t i=0; i(); + if ( i < rhs.size() ) r = rhs[i].template as(); + result->push_back( l * r); + } + return *result; + } else if ( lhs.type() == jsoncons::json_type::array_value ) { + double d = rhs.template as(); + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + for (auto& v : lhs.array_range()) { + result->push_back( v.template as() * d ); + } + return *result; + } else if ( rhs.type() == jsoncons::json_type::array_value ) { + double d = lhs.template as(); + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + for (auto& v : rhs.array_range()) { + result->push_back( v.template as() * d ); + } + return *result; + } + + 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("multiply_operator"); + return s; + } + }; + + class div_operator final : public binary_operator + { + public: + div_operator() + : binary_operator(operator_kind::div_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override + { + if ( (lhs.is_number() && rhs.is_number()) || + (lhs.is_null() || rhs.is_null()) || + (lhs.is_bool() || rhs.is_bool()) ) { + double l = 0, r = 0; + + if ( lhs.type() == jsoncons::json_type::double_value ) { + l = lhs.template as(); + } else if ( lhs.type() == jsoncons::json_type::string_value ) { + l = std::stod(lhs.template as()); + } else if ( lhs.type() == jsoncons::json_type::bool_value ) { + l = lhs.template as() ? 1 : 0; + } else if ( !lhs.is_null() ) { + l = lhs.template as(); + } else { +// std::cout << pretty_print(lhs) << std::endl; + } + + + if ( rhs.type() == jsoncons::json_type::double_value ) { + r = rhs.template as(); + } else if ( rhs.type() == jsoncons::json_type::string_value ) { + r = std::stod(rhs.template as()); + } else if ( rhs.type() == jsoncons::json_type::bool_value ) { + r = rhs.template as() ? 1 : 0; + } else if ( !rhs.is_null() ){ + r = rhs.template as(); + } else { +// std::cout << pretty_print(rhs) << std::endl; + } + + if ( r == 0 ) { + // divide by zero is bad - so return `null` + return resources.null_value(); + } else + return *resources.create_json(l / r); + } else if ( lhs.type() == jsoncons::json_type::string_value || rhs.type() == jsoncons::json_type::string_value ) { + double l = 0, r = 0; + + if ( lhs.type() == jsoncons::json_type::string_value ) + l = std::stod(lhs.template as()); + else if ( lhs.type() != jsoncons::json_type::null_value ) + l = lhs.template as(); + + if ( rhs.type() == jsoncons::json_type::string_value ) + r = std::stod(rhs.template as()); + else if ( rhs.type() != jsoncons::json_type::null_value ) + r = rhs.template as(); + + return *resources.create_json(l / r); + } else if ( lhs.type() == jsoncons::json_type::array_value && rhs.type() == jsoncons::json_type::array_value ) { + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + auto numEnts = std::max( lhs.size(), rhs.size() ); + for (int64_t i=0; i(); + if ( i < rhs.size() ) r = rhs[i].template as(); + result->push_back( l / r); + } + return *result; + } else if ( lhs.type() == jsoncons::json_type::array_value ) { + double d = rhs.template as(); + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + for (auto& v : lhs.array_range()) { + result->push_back( v.template as() / d ); + } + return *result; + } else if ( rhs.type() == jsoncons::json_type::array_value ) { + double d = lhs.template as(); + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + for (auto& v : rhs.array_range()) { + result->push_back( d / v.template as() ); + } + return *result; + } + + 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("divide_operator"); + return s; + } + }; + + class concat_operator final : public binary_operator + { + public: + concat_operator() + : binary_operator(operator_kind::concat_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override + { + // as per json-formula spec + // Arrays shall be coerced to an array of strings. + if ( lhs.type() == jsoncons::json_type::array_value && rhs.type() == jsoncons::json_type::array_value ) { + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + auto numEnts = std::max( lhs.size(), rhs.size() ); + for (int64_t i=0; ipush_back( lhsStr + rhsStr); + } + return *result; + } else if ( lhs.type() == jsoncons::json_type::array_value ) { + std::string rhsStr{to_string(rhs)}; + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + for (auto& v : lhs.array_range()) { + result->push_back( to_string(v) + rhsStr ); + } + return *result; + } else if ( rhs.type() == jsoncons::json_type::array_value ) { + std::string lhsStr{to_string(lhs)}; + auto result = resources.create_json(jsoncons::json_array_arg); // empty array + for (auto& v : rhs.array_range()) { + result->push_back( lhsStr + to_string(v) ); + } + return *result; + } else { + std::string lhsStr{to_string(lhs)}, rhsStr{to_string(rhs)}; + + if ( !lhsStr.empty() || !rhsStr.empty() ) + return *resources.create_json(lhsStr + rhsStr); + 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("concat_operator"); + return s; + } + + std::string to_string(reference& r) const + { + switch ( r.type() ) { + case jsoncons::json_type::array_value: + // should not get here for this scenario... + break; + case jsoncons::json_type::uint64_value: + case jsoncons::json_type::int64_value: + return std::to_string(r.template as()); + break; + case jsoncons::json_type::double_value: + return std::to_string(r.template as()); + break; + case jsoncons::json_type::string_value: + return r.template as(); + break; + case jsoncons::json_type::bool_value: + return r.template as() ? "true" : "false"; + break; + case jsoncons::json_type::null_value: + case jsoncons::json_type::object_value: // technically an error, but we'll treat an NOP + case jsoncons::json_type::half_value: // technically an error, but we'll treat an NOP + case jsoncons::json_type::byte_string_value: // technically an error, but we'll treat an NOP + { + // do nothing! + } + break; + } + + return ""; + } + + }; + + class union_operator final : public binary_operator + { + public: + union_operator() + : binary_operator(operator_kind::union_op) + { + } + + reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override + { + auto addToUnion = [&resources](Json* result, reference r) { + switch ( r.type() ) { + case jsoncons::json_type::array_value: + { + for (auto& v : r.array_range()) { + result->push_back(v); + } + } + break; + case jsoncons::json_type::uint64_value: + case jsoncons::json_type::int64_value: + { + result->push_back(*resources.create_json(r.template as())); + } + break; + case jsoncons::json_type::double_value: + { + result->push_back(*resources.create_json(r.template as())); + } + break; + case jsoncons::json_type::string_value: + { + result->push_back(*resources.create_json(r.template as())); + } + break; + case jsoncons::json_type::bool_value: + { + result->push_back(*resources.create_json(r.template as())); + } + break; + case jsoncons::json_type::null_value: + case jsoncons::json_type::object_value: // technically an error, but we'll treat an NOP + case jsoncons::json_type::half_value: // technically an error, but we'll treat an NOP + case jsoncons::json_type::byte_string_value: // technically an error, but we'll treat an NOP + { + // do nothing! + } + break; + } + }; + + // lhs must be an array... + // and then we determine what to do based on the type of rhs + if ( lhs.type() == jsoncons::json_type::array_value ) { + auto result = resources.create_json(lhs); // copy it since we'll be modifying it + addToUnion(result, rhs); + return *result; + } else if ( rhs.type() == jsoncons::json_type::array_value ) { + auto result = resources.create_json(rhs); // copy it since we'll be modifying it + addToUnion(result, lhs); + return *result; + } + + 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("union_operator"); + 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&&) 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& resources, std::error_code&) const override + { + if ( false /*debug_*/ ) { + std::cout << "(" << to_string() << "): " << 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&, 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& resources, std::error_code&) const override + { + if (!val.is_array()) + { + return resources.null_value(); + } + int64_t slen = static_cast(val.size()); + if (index_ >= 0 && index_ < slen) + { + std::size_t index = static_cast(index_); + return val.at(index); + } + else if ((slen + index_) >= 0 && (slen+index_) < slen) + { + std::size_t index = static_cast(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> expressions_; + public: + projection_base(operator_kind oper) + : expression_base(oper, true) + { + } + + void add_expression(std::unique_ptr&& 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& 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& resources, std::error_code& ec) const override + { + if (!val.is_object()) + { + return resources.null_value(); + } + + auto result = resources.create_json(jsoncons::json_array_arg); + for (auto& item : val.object_range()) + { + // json-formula supports nulls in projections + if (true /*!item.value().is_null()*/) + { + reference j = this->apply_expressions(item.value(), resources, ec); + + // json-formula supports nulls in projections + if (true /*!j.is_null()*/) + { + result->emplace_back(jsoncons::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& resources, std::error_code& ec) const override + { + if (!val.is_array()) + { + // json-formula allows nulls in list projections + // but we need to special case them to return an array of null (since it's a projection!) + if (val.is_null()) { + auto result = resources.create_json(jsoncons::json_array_arg); + result->emplace_back(resources.null_value()); + return *result; + } else + return resources.null_value(); + } + + auto result = resources.create_json(jsoncons::json_array_arg); + for (reference item : val.array_range()) + { + // json-formula allows nulls in list projections + if (true /*!item.is_null()*/) + { + reference j = this->apply_expressions(item, resources, ec); + + // json-formula allows nulls in list projections + if (true /*!j.is_null()*/) + { + result->emplace_back(jsoncons::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& 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 = jsonformula_errc::step_cannot_be_zero; + return resources.null_value(); + } + + auto result = resources.create_json(jsoncons::json_array_arg); + if (step > 0) + { + if (start < 0) + { + start = 0; + } + if (end > static_cast(val.size())) + { + end = val.size(); + } + for (int64_t i = start; i < end; i += step) + { + reference j = this->apply_expressions(val.at(static_cast(i)), resources, ec); + + // json-formula supports nulls in projections + if (true /*!j.is_null()*/) + { + result->emplace_back(jsoncons::json_const_pointer_arg, std::addressof(j)); + } + } + } + else + { + if (start >= static_cast(val.size())) + { + start = static_cast(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(i)), resources, ec); + + // json-formula supports nulls in projections + if (true /*!j.is_null()*/) + { + result->emplace_back(jsoncons::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_list_; + public: + filter_expression(std::vector&& token_list) + : projection_base(operator_kind::projection_op), token_list_(std::move(token_list)) + { + } + + reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override + { + if (!val.is_array()) + { + return resources.null_value(); + } + auto result = resources.create_json(jsoncons::json_array_arg); + + for (auto& item : val.array_range()) + { + Json j(jsoncons::json_const_pointer_arg, evaluate_tokens(item, token_list_, resources, ec)); + if (is_true(j)) + { + reference jj = this->apply_expressions(item, resources, ec); + + // json-formula supports nulls in filter expressions + if (true /*!jj.is_null()*/) + { + result->emplace_back(jsoncons::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& resources, std::error_code& ec) const override + { + if (!val.is_array()) + { + return resources.null_value(); + } + + auto result = resources.create_json(jsoncons::json_array_arg); + for (reference current_elem : val.array_range()) + { + if (current_elem.is_array()) + { + for (reference elem : current_elem.array_range()) + { + // json-formula allows nulls in projections + if (true /*!elem.is_null()*/) + { + reference j = this->apply_expressions(elem, resources, ec); + + // json-formula allows nulls in projections + if (true /*!j.is_null()*/) + { + result->emplace_back(jsoncons::json_const_pointer_arg, std::addressof(j)); + } + } + } + } + else + { + // json-formula allows nulls in projections + if (true /*!current_elem.is_null()*/) + { + reference j = this->apply_expressions(current_elem, resources, ec); + + // json-formula allows nulls in projections + if (true /*!j.is_null()*/) + { + result->emplace_back(jsoncons::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> token_lists_; + public: + multi_select_list(std::vector>&& token_lists) + : token_lists_(std::move(token_lists)) + { + } + + reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override + { +// json-formula allows nulls in projections & lists +// if (val.is_null()) +// { +// return val; +// } + + // in json-formula, empty objects aren't allowed + if ( token_lists_.size()==0 ) { + ec = jsonformula_errc::invalid_object; + return resources.null_value(); + } + + auto result = resources.create_json(jsoncons::json_array_arg); + result->reserve(token_lists_.size()); + + for (auto& list : token_lists_) + { + result->emplace_back(jsoncons::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 tokens; + + key_tokens(string_type&& key, std::vector&& tokens) noexcept + : key(std::move(key)), tokens(std::move(tokens)) + { + } + }; + + class multi_select_hash final : public basic_expression + { + public: + std::vector key_toks_; + + multi_select_hash(std::vector&& key_toks) + : key_toks_(std::move(key_toks)) + { + } + + reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override + { + // in json-formula, null == empty object! +// if (val.is_null()) +// { +// return val; +// } + + // in json-formula, empty objects aren't allowed + if ( key_toks_.size()==0 ) { + ec = jsonformula_errc::invalid_object; + return resources.null_value(); + } + + auto resultp = resources.create_json(jsoncons::json_object_arg); + resultp->reserve(key_toks_.size()); + for (auto& item : key_toks_) + { + resultp->try_emplace(item.key, jsoncons::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"); + return s; + } + }; + + class function_expression final : public basic_expression + { + public: + std::vector toks_; + + function_expression(std::vector&& toks) + : toks_(std::move(toks)) + { + } + + reference evaluate(reference val, dynamic_resources& 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> 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; + static value_function value_func; + + using function_dictionary = std::unordered_map; + 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}, + + // json_formula changes & additions + {string_type{'e','n','d', 's', 'W', 'i', 't', 'h'}, &ends_with_func}, + {string_type{'m','i','n','B','y'}, &min_by_func}, + {string_type{'n','o','t', 'N', 'u','l','l'}, ¬_null_func}, + {string_type{'s','o','r', 't','B','y'}, &sort_by_func}, + {string_type{'s','t','a', 'r','t','s','W','i','t','h'}, &starts_with_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{'v','a','l', 'u','e'}, &value_func}, + }; + auto it = functions_.find(name); + if (it == functions_.end()) + { + ec = jsonformula_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; + } + + const binary_operator* get_plus_operator() const + { + static const plus_operator plus_oper; + return &plus_oper; + } + const binary_operator* get_minus_operator() const + { + static const minus_operator minus_oper; + return &minus_oper; + } + const binary_operator* get_mult_operator() const + { + static const mult_operator mult_oper; + return &mult_oper; + } + const binary_operator* get_div_operator() const + { + static const div_operator div_oper; + return &div_oper; + } + const binary_operator* get_concat_operator() const + { + static const concat_operator concat_oper; + return &concat_oper; + } + const binary_operator* get_union_operator() const + { + static const union_operator union_oper; + return &union_oper; + } + }; + + class jsonformula_expression + { + static_resources resources_; + std::vector output_stack_; + public: + jsonformula_expression() + { + } + + jsonformula_expression(const jsonformula_expression& expr) = delete; + jsonformula_expression& operator=(const jsonformula_expression& expr) = delete; + + jsonformula_expression(jsonformula_expression&& expr) + : resources_(std::move(expr.resources_)), + output_stack_(std::move(expr.output_stack_)) + { + } + + jsonformula_expression(static_resources&& resources, + std::vector&& 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(jsonformula_error(ec)); + } + return result; + } + + Json evaluate(reference doc, std::error_code& ec) + { + if (output_stack_.empty()) + { + return Json::null(); + } + dynamic_resources dynamic_storage; + return deep_copy(*evaluate_tokens(doc, output_stack_, dynamic_storage, ec)); + } + + static jsonformula_expression compile(const string_view_type& expr) + { + jsonformula::detail::jsonformula_evaluator evaluator; + std::error_code ec; + jsonformula_expression result = evaluator.compile(expr.data(), expr.size(), ec); + if (ec) + { + JSONCONS_THROW(jsonformula_error(ec, evaluator.line(), evaluator.column())); + } + return result; + } + + static jsonformula_expression compile(const string_view_type& expr, + std::error_code& ec) + { + jsonformula::detail::jsonformula_evaluator 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 state_stack_; + + std::vector output_stack_; + std::vector operator_stack_; + + + bool debug_; + +public: + jsonformula_evaluator() + : line_(1), column_(1), + begin_input_(nullptr), end_input_(nullptr), + p_(nullptr), debug_(false) + { + } + + std::size_t line() const + { + return line_; + } + + std::size_t column() const + { + return column_; + } + + void debug(bool d=true) { debug_ = d; } + + jsonformula_expression compile(const char_type* path, + std::size_t length, + std::error_code& ec) + { + auto _checkBuffer = [this, &ec](string_type& buffer) { + if (!buffer.empty()) { + jsoncons::json_decoder decoder; + jsoncons::basic_json_reader> reader(buffer, decoder); + std::error_code parse_ec; + reader.read(parse_ec); + if (parse_ec) { + ec = jsonformula_errc::invalid_literal; + return; + } + auto j = decoder.get_result(); + push_token(token(literal_arg, std::move(j)), ec); + buffer.clear(); + } + }; + + push_token(current_node_arg, ec); + if (ec) {return jsonformula_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, buffer); + 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 ')': + { + // json-formula + // check if there is anything in the buffer... + // if so, we need to process it + _checkBuffer(buffer); + + state_stack_.pop_back(); + break; + } + case '[': + ++p_; + ++column_; + state_stack_.emplace_back(path_state::bracket_specifier); + break; + // json-formula + case '+': + case '-': + case '*': + case '/': + case '~': + state_stack_.emplace_back(path_state::jf_expression); + break; + default: + if (state_stack_.size() > 1) + { + state_stack_.pop_back(); + } + else + { + ec = jsonformula_errc::syntax_error; + return jsonformula_expression(); + } + break; + } + break; + case path_state::comparator_expression: + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec, buffer); + 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 = jsonformula_errc::syntax_error; + return jsonformula_expression(); + } + break; + } + break; + case path_state::lhs_expression: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec, buffer); + break; + case '\"': + // in json-formula, quoted strings are strings +// state_stack_.back() = path_state::val_expr; + state_stack_.back() = path_state::quoted_string; + ++p_; + ++column_; + break; + case '\'': + // in json-formula, single quoted/raw strings are value expression + state_stack_.back() = path_state::val_expr; + state_stack_.emplace_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 jsonformula_expression();} + state_stack_.back() = path_state::multi_select_hash; + ++p_; + ++column_; + break; + case '*': // wildcard + push_token(token(jsoncons::make_unique()), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + case '(': + { + ++p_; + ++column_; + push_token(lparen_arg, ec); + if (ec) {return jsonformula_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 jsonformula_expression();} + break; + } + case '@': + ++p_; + ++column_; + push_token(token(jsoncons::make_unique()), ec); + if (ec) {return jsonformula_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 if ((*p_ >= '0' && *p_ <= '9') || (*p_ == '-') || (*p_ == '.')) //json-formula supports #'s + { + state_stack_.back() = path_state::number; + buffer.push_back(*p_); + ++p_; + ++column_; + } + else + { + ec = jsonformula_errc::expected_identifier; + return jsonformula_expression(); + } + break; + }; + break; + } + case path_state::sub_expression: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec, buffer); + break; + case '\"': + // in json-formula, quoted strings are strings +// state_stack_.back() = path_state::val_expr; + state_stack_.emplace_back(path_state::quoted_string); + ++p_; + ++column_; + break; + case '\'': + // in json-formula, single quoted/raw strings are value expression + state_stack_.back() = path_state::val_expr; + state_stack_.emplace_back(path_state::raw_string); + ++p_; + ++column_; + break; + case '{': + push_token(begin_multi_select_hash_arg, ec); + if (ec) {return jsonformula_expression();} + state_stack_.back() = path_state::multi_select_hash; + ++p_; + ++column_; + break; + case '*': + push_token(token(jsoncons::make_unique()), ec); + if (ec) {return jsonformula_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 = jsonformula_errc::expected_identifier; + return jsonformula_expression(); + } + break; + }; + break; + } + case path_state::key_expr: + push_token(token(key_arg, buffer), ec); + if (ec) {return jsonformula_expression();} + buffer.clear(); + state_stack_.pop_back(); + break; + case path_state::val_expr: + push_token(token(jsoncons::make_unique(buffer)), ec); + if (ec) {return jsonformula_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, buffer); + break; + case '&': + push_token(token(begin_expression_type_arg), ec); + if (ec) {return jsonformula_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; + //json-formula supports #'s + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + case '.': // don't forget decimal places! + case '-': // don't forget negative numbers! + // we need to first mark this as an argument + state_stack_.back() = path_state::argument; + state_stack_.emplace_back(path_state::rhs_expression); + state_stack_.emplace_back(path_state::lhs_expression); + + // and now handle the number part... + state_stack_.back() = path_state::number; + buffer.push_back(*p_); + ++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 jsonformula_expression(); + } + buffer.clear(); + push_token(token(f), ec); + if (ec) {return jsonformula_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(buffer)), ec); + if (ec) {return jsonformula_expression();} + buffer.clear(); + state_stack_.pop_back(); + break; + } + } + break; + + case path_state::function_expression: + { + // json-formula + // check if there is anything in the buffer... + // if so, we need to process it + _checkBuffer(buffer); + + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec, buffer); + break; + case ',': + push_token(token(current_node_arg), ec); + if (ec) {return jsonformula_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 jsonformula_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + } + default: + break; + } + } + break; + + case path_state::argument: + push_token(argument_arg, ec); + if (ec) {return jsonformula_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 jsonformula_expression();} + state_stack_.pop_back(); + break; + + case path_state::quoted_string: + switch (*p_) + { + case '\"': + { + // in json-formula, these should be treated as literal strings + // except when they actually mean identifiers + auto stack_grandparent = state_stack_[state_stack_.size()-2]; + + if ( stack_grandparent == path_state::key_expr || + stack_grandparent == path_state::sub_expression ) { + state_stack_.pop_back(); // quoted_string + state_stack_.back() = path_state::val_expr; // reset this to a val expression... + } else { + push_token(token(literal_arg, Json(buffer)), ec); + if (ec) {return jsonformula_expression();} + buffer.clear(); + + 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': + { + string_type dummy; + state_stack_.pop_back(); // unquoted_string + advance_past_space_character(ec, dummy); + } + 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; + // json-formula also supports escaping the escape character! + // it also supports the same set of escapes as double strings. + case '\"': + buffer.push_back('\"'); + ++p_; + ++column_; + state_stack_.pop_back(); + break; + case '\\': + buffer.push_back('\\'); + state_stack_.pop_back(); + ++p_; + ++column_; + 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: + 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 = jsonformula_errc::illegal_escaped_character; + return jsonformula_expression(); + } + break; + case path_state::escape_u1: + cp = append_to_codepoint(0, *p_, ec); + if (ec) + { + return jsonformula_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 jsonformula_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 jsonformula_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 jsonformula_expression(); + } + if (jsoncons::unicode_traits::is_high_surrogate(cp)) + { + ++p_; + ++column_; + state_stack_.back() = path_state::escape_expect_surrogate_pair1; + } + else + { + jsoncons::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 = jsonformula_errc::invalid_codepoint; + return jsonformula_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 = jsonformula_errc::invalid_codepoint; + return jsonformula_expression(); + } + break; + case path_state::escape_u5: + cp2 = append_to_codepoint(0, *p_, ec); + if (ec) + { + return jsonformula_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 jsonformula_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 jsonformula_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 jsonformula_expression(); + } + uint32_t codepoint = 0x10000 + ((cp & 0x3FF) << 10) + (cp2 & 0x3FF); + jsoncons::unicode_traits::convert(&codepoint, 1, buffer); + state_stack_.pop_back(); + ++p_; + ++column_; + break; + } + case path_state::raw_string: + switch (*p_) + { + case '\'': + { + // in json-formula, these should be treated as literal strings + // except when they actually mean identifiers + auto stack_grandparent = state_stack_[state_stack_.size()-2]; + + if ( stack_grandparent == path_state::sub_expression ) { + state_stack_.pop_back(); // raw_string + state_stack_.back() = path_state::val_expr; // reset this to a val expression... + } else { + 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 '`': + { + jsoncons::json_decoder decoder; + jsoncons::basic_json_reader> reader(buffer, decoder); + std::error_code parse_ec; + reader.read(parse_ec); + if (parse_ec) + { + ec = jsonformula_errc::invalid_literal; + return jsonformula_expression(); + } + auto j = decoder.get_result(); + + push_token(token(literal_arg, std::move(j)), ec); + if (ec) {return jsonformula_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 = jsonformula_errc::unexpected_end_of_input; + return jsonformula_expression(); + } + ++p_; + ++column_; + break; + default: + buffer.push_back(*p_); + ++p_; + ++column_; + break; + }; + break; + case path_state::number: + switch(*p_) + { + case '-': + case '+': + { + bool digit_follows = false; // assume the worst + auto stack_grandparent = state_stack_[state_stack_.size()-2]; + + if ( buffer[buffer.length()-1]=='e' || buffer[buffer.length()-1]=='E' ) { + // json-formula also supports exponents, and next is a digit + digit_follows = true; + } else if ( stack_grandparent == path_state::index_or_slice_expression || + stack_grandparent == path_state::rhs_slice_expression_step || + stack_grandparent == path_state::rhs_slice_expression_stop ) { + // we are in an index or slice expression + digit_follows = true; + } + + if ( digit_follows ) { + buffer.push_back(*p_); + state_stack_.back() = path_state::digit; + ++p_; + ++column_; + } else { // it's an equation/formula, so pop out of number! + state_stack_.pop_back(); + } + } + break; + + case '.': // don't forget decimal places! + buffer.push_back(*p_); + state_stack_.back() = path_state::digit; + ++p_; + ++column_; + break; + case 'e': // json-formula also supports exponents + case 'E': + buffer.push_back(*p_); + state_stack_.back() = path_state::number; // keep a number since it might be fllowed by +/- + ++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': + case '.': // don't forget decimal places! + 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()), ec); + if (ec) {return jsonformula_expression();} + state_stack_.back() = path_state::expect_rbracket; + ++p_; + ++column_; + break; + case ']': // [] + push_token(token(jsoncons::make_unique()), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); // bracket_specifier + ++p_; + ++column_; + break; + case '?': + push_token(token(begin_filter_arg), ec); + if (ec) {return jsonformula_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 = jsonformula_errc::expected_index_expression; + return jsonformula_expression(); + } + break; + case path_state::bracket_specifier_or_multi_select_list: + switch(*p_) + { + case '*': + if (p_+1 >= end_input_) + { + ec = jsonformula_errc::unexpected_end_of_input; + return jsonformula_expression(); + } + if (*(p_+1) == ']') + { + state_stack_.back() = path_state::bracket_specifier; + } + else + { + push_token(token(begin_multi_select_list_arg), ec); + if (ec) {return jsonformula_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 jsonformula_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 ':': + ec = jsonformula_errc::expected_multi_select_list; + return jsonformula_expression(); + // json-formula allows indices here... + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + // need to push the multi-select token on first + // then setup the state stack + push_token(token(begin_multi_select_list_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.back() = path_state::multi_select_list; + state_stack_.emplace_back(path_state::number); + break; + case '*': + push_token(token(jsoncons::make_unique()), ec); + if (ec) {return jsonformula_expression();} + state_stack_.back() = path_state::expect_rbracket; + ++p_; + ++column_; + break; + default: + push_token(token(begin_multi_select_list_arg), ec); + if (ec) {return jsonformula_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; + // json-formula supports empty objects + case '}': + { + state_stack_.pop_back(); + push_token(end_multi_select_hash_arg, ec); + if (ec) {return jsonformula_expression();} + ++p_; + ++column_; + 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()), ec); + if (ec) {return jsonformula_expression();} + } + else + { + int64_t val{ 0 }; + auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); + if (!r) + { + ec = jsonformula_errc::invalid_number; + return jsonformula_expression(); + } + push_token(token(jsoncons::make_unique(val)), ec); + if (ec) {return jsonformula_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 = jsonformula_errc::invalid_number; + return jsonformula_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; + } + case ',': + { + // json-formula + // if we landed here, then we MAY have found a raw array of numbers + // it is also possible that it is a bogus array index... + // we check which, based on the output_stack + // if it's a raw array, reset the stack to what it should be... + // Push on the multi-select token + // Replace the current state (index or slice) with multi-select list + // otherwise, leave it alone! + if ( output_stack_[output_stack_.size()-1].type() == token_kind::expression ) { + // nothing to do... + } else { + push_token(token(begin_multi_select_list_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.back() = path_state::multi_select_list; + } + + // check if there is anything in the buffer... (there better be!) + // which will push a literal onto the token stack + _checkBuffer(buffer); + + // now push on the seperator + // and push lhs_expression onto the state stack + push_token(token(separator_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.emplace_back(path_state::lhs_expression); + ++p_; + ++column_; + break; + } + default: + ec = jsonformula_errc::expected_rbracket; + return jsonformula_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 = jsonformula_errc::invalid_number; + return jsonformula_expression(); + } + slic.stop_ = jsoncons::optional(val); + buffer.clear(); + } + switch(*p_) + { + case ']': + push_token(token(jsoncons::make_unique(slic)), ec); + if (ec) {return jsonformula_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 = jsonformula_errc::expected_rbracket; + return jsonformula_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 = jsonformula_errc::invalid_number; + return jsonformula_expression(); + } + if (val == 0) + { + ec = jsonformula_errc::step_cannot_be_zero; + return jsonformula_expression(); + } + slic.step_ = val; + buffer.clear(); + } + switch(*p_) + { + case ']': + push_token(token(jsoncons::make_unique(slic)), ec); + if (ec) {return jsonformula_expression();} + buffer.clear(); + slic = slice{}; + state_stack_.pop_back(); // rhs_slice_expression_step + ++p_; + ++column_; + break; + default: + ec = jsonformula_errc::expected_rbracket; + return jsonformula_expression(); + } + break; + } + case path_state::expect_rbracket: + { + switch(*p_) + { + case ']': + state_stack_.pop_back(); // expect_rbracket + ++p_; + ++column_; + break; + default: + ec = jsonformula_errc::expected_rbracket; + return jsonformula_expression(); + } + break; + } + case path_state::expect_rparen: + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec, buffer); + break; + case ')': + ++p_; + ++column_; + push_token(rparen_arg, ec); + if (ec) {return jsonformula_expression();} + state_stack_.back() = path_state::rhs_expression; + break; + default: + ec = jsonformula_errc::expected_rparen; + return jsonformula_expression(); + } + break; + case path_state::key_val_expr: + { + switch (*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec, buffer); + 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::key_expr); // json-formula support these as keys too + 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 = jsonformula_errc::expected_key; + return jsonformula_expression(); + } + break; + }; + break; + } + case path_state::cmp_lt_or_lte: + { + // json-formula + // check if there is anything in the buffer... + // if so, we need to process it + _checkBuffer(buffer); + + switch(*p_) + { + case '=': + push_token(token(resources_.get_lte_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + // json-formula supports <> for not equal + case '>': + push_token(token(resources_.get_ne_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jsonformula_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 jsonformula_expression();} + state_stack_.pop_back(); + break; + } + break; + } + case path_state::cmp_gt_or_gte: + { + // json-formula + // check if there is anything in the buffer... + // if so, we need to process it + _checkBuffer(buffer); + + switch(*p_) + { + case '=': + push_token(token(resources_.get_gte_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jsonformula_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 jsonformula_expression();} + state_stack_.pop_back(); + break; + } + break; + } + case path_state::cmp_eq: + { + // json-formula + // check if there is anything in the buffer... + // if so, we need to process it + _checkBuffer(buffer); + + // json-formula supports both `=` and `==` + push_token(token(resources_.get_eq_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + if ( *p_ == '=' ) { // be careful not to move past something else! + ++p_; + ++column_; + } + break; + } + case path_state::cmp_ne: + { + // json-formula + // check if there is anything in the buffer... + // if so, we need to process it + _checkBuffer(buffer); + + switch(*p_) + { + case '=': + push_token(token(resources_.get_ne_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + ec = jsonformula_errc::expected_comparator; + return jsonformula_expression(); + } + break; + } + case path_state::jf_expression: + { + // json-formula + // check if there is anything in the buffer... + // if so, we need to process it + _checkBuffer(buffer); + + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec, buffer); + break; + case '+': + ++p_; + ++column_; + state_stack_.back() = path_state::lhs_expression; + state_stack_.emplace_back(path_state::plus_operator); + break; + case '-': + ++p_; + ++column_; + state_stack_.back() = path_state::lhs_expression; + state_stack_.emplace_back(path_state::minus_operator); + break; + case '*': + ++p_; + ++column_; + state_stack_.back() = path_state::lhs_expression; + state_stack_.emplace_back(path_state::mult_operator); + break; + case '/': + ++p_; + ++column_; + state_stack_.back() = path_state::lhs_expression; + state_stack_.emplace_back(path_state::div_operator); + break; + case '~': + ++p_; + ++column_; + state_stack_.back() = path_state::lhs_expression; + state_stack_.emplace_back(path_state::union_operator); + break; + default: + if (state_stack_.size() > 1) + { + state_stack_.pop_back(); + } + else + { + ec = jsonformula_errc::syntax_error; + return jsonformula_expression(); + } + break; + } + break; + } + case path_state::plus_operator: + { + push_token(token(resources_.get_plus_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + break; + } + case path_state::minus_operator: + { + push_token(token(resources_.get_minus_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + break; + } + case path_state::mult_operator: + { + push_token(token(resources_.get_mult_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + break; + } + case path_state::div_operator: + { + push_token(token(resources_.get_div_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + break; + } + case path_state::concat_operator: + { + push_token(token(resources_.get_concat_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + break; + } + case path_state::union_operator: + { + push_token(token(resources_.get_union_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + break; + } + case path_state::expect_dot: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec, buffer); + break; + case '.': + state_stack_.pop_back(); // expect_dot + ++p_; + ++column_; + break; + default: + ec = jsonformula_errc::expected_dot; + return jsonformula_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 jsonformula_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + default: + push_token(token(pipe_arg), ec); + if (ec) {return jsonformula_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 jsonformula_expression();} + state_stack_.pop_back(); // expect_and + ++p_; + ++column_; + break; + default: + // for json-formula, this is the concat operator +// ec = jsonformula_errc::expected_and; +// return jsonformula_expression(); + push_token(token(resources_.get_concat_operator()), ec); + push_token(token(current_node_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); // expect_and + ++p_; + ++column_; + break; + } + break; + } + case path_state::multi_select_list: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec, buffer); + break; + case ',': + // json-formula + // check if there is anything in the buffer...(better be) + _checkBuffer(buffer); + + push_token(token(separator_arg), ec); + if (ec) {return jsonformula_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 ']': + { + // json-formula + // check if there is anything in the buffer... + // if so, we need to process it + _checkBuffer(buffer); + + push_token(token(end_multi_select_list_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + + ++p_; + ++column_; + break; + } + default: + ec = jsonformula_errc::expected_rbracket; + return jsonformula_expression(); + } + break; + } + case path_state::filter: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec, buffer); + break; + case ']': + { + push_token(token(end_filter_arg), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + ++p_; + ++column_; + break; + } + default: + ec = jsonformula_errc::expected_rbracket; + return jsonformula_expression(); + } + break; + } + case path_state::expect_rbrace: + { + // json-formula + // check if there is anything in the buffer... + // if so, we need to process it + _checkBuffer(buffer); + + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec, buffer); + break; + case ',': + push_token(token(separator_arg), ec); + if (ec) {return jsonformula_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 jsonformula_expression();} + ++p_; + ++column_; + break; + } + // json-formula supports functions + case '+': + case '-': + case '*': + case '/': + state_stack_.emplace_back(path_state::jf_expression); + break; + default: + if ( debug_ ) { std::cout << "rbrace: " << *p_ << std::endl; } + + ec = jsonformula_errc::expected_rbrace; + return jsonformula_expression(); + } + break; + } + case path_state::expect_colon: + { + switch(*p_) + { + case ' ':case '\t':case '\r':case '\n': + advance_past_space_character(ec, buffer); + break; + case ':': + state_stack_.back() = path_state::expect_rbrace; + state_stack_.emplace_back(path_state::lhs_expression); + ++p_; + ++column_; + break; + default: + ec = jsonformula_errc::expected_colon; + return jsonformula_expression(); + } + break; + } + } + + } + + if (state_stack_.empty()) + { + ec = jsonformula_errc::syntax_error; + return jsonformula_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 = jsonformula_errc::syntax_error; + return jsonformula_expression(); + } + break; + case path_state::number: // in json-formula, we could end with a number + case path_state::digit: // in json-formula, we could end with a digit + { + jsoncons::json_decoder decoder; + jsoncons::basic_json_reader> reader(buffer, decoder); + std::error_code parse_ec; + reader.read(parse_ec); + if (parse_ec) + { + ec = jsonformula_errc::invalid_literal; + return jsonformula_expression(); + } + auto j = decoder.get_result(); + push_token(token(literal_arg, std::move(j)), ec); + if (ec) {return jsonformula_expression();} + buffer.clear(); + state_stack_.pop_back(); + } + break; + case path_state::val_expr: + push_token(token(jsoncons::make_unique(buffer)), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + break; + case path_state::identifier_or_function_expr: + push_token(token(jsoncons::make_unique(buffer)), ec); + if (ec) {return jsonformula_expression();} + state_stack_.pop_back(); + break; + case path_state::quoted_string: // specific error for unbalanced quotes in json-formula + case path_state::raw_string: + ec = jsonformula_errc::unbalanced_quotes; + return jsonformula_expression(); + break; + case path_state::unquoted_string: + state_stack_.pop_back(); + break; + default: + ec = jsonformula_errc::syntax_error; + return jsonformula_expression(); + break; + } + } + + if (!(state_stack_.size() == 1 && state_stack_.back() == path_state::rhs_expression)) + { + ec = jsonformula_errc::unexpected_end_of_input; + return jsonformula_expression(); + } + + state_stack_.pop_back(); + + push_token(end_of_expression_arg, ec); + if (ec) {return jsonformula_expression();} + + // debug the output stack... + if ( debug_ ) { + for (auto& t : output_stack_) + { + std::cout << t.to_string() << std::endl; + } + } + + return jsonformula_expression(std::move(resources_), std::move(output_stack_)); + } + + void advance_past_space_character(std::error_code& ec, string_type& buffer) + { + // json-formula + // check if there is anything in the buffer... + // if so, we need to process it + if (!buffer.empty()) { + jsoncons::json_decoder decoder; + jsoncons::basic_json_reader> reader(buffer, decoder); + std::error_code parse_ec; + reader.read(parse_ec); + if (parse_ec) { + // we had some problem with the literal, so let's force it to number & back + auto bufD = std::stod(buffer); + buffer = std::to_string(bufD); + return; // will cause it to run again with the new buffer... + } + auto j = decoder.get_result(); + push_token(token(literal_arg, std::move(j)), ec); + buffer.clear(); + } + + switch (*p_) + { + case ' ':case '\t': + ++p_; + ++column_; + break; + case '\r': + if (p_+1 >= end_input_) + { + ec = jsonformula_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 = jsonformula_errc::unbalanced_parentheses; + return; + } + ++it; + operator_stack_.erase(it.base(),operator_stack_.end()); + } + + void push_token(token&& tok, std::error_code& ec) + { + // debug the output stack... + if ( debug_ ) { + std::cout << "pre-push" << std::endl; + for (auto& t : output_stack_) + { + std::cout << t.to_string() << std::endl; + } + } + + switch (tok.type()) + { + case token_kind::end_filter: + { + unwind_rparen(ec); + std::vector 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 = jsonformula_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(std::move(toks))); + } + else + { + output_stack_.emplace_back(token(jsoncons::make_unique(std::move(toks)))); + } + break; + } + case token_kind::end_multi_select_list: + { + unwind_rparen(ec); + std::vector> vals; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->type() != token_kind::begin_multi_select_list) + { + std::vector 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 = jsonformula_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(std::move(vals))); + } + else + { + output_stack_.emplace_back(token(jsoncons::make_unique(std::move(vals)))); + } + break; + } + case token_kind::end_multi_select_hash: + { + unwind_rparen(ec); + std::vector key_toks; + auto it = output_stack_.rbegin(); + while (it != output_stack_.rend() && it->type() != token_kind::begin_multi_select_hash) + { + std::vector 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 = jsonformula_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(std::move(key_toks))); + } + else + { + output_stack_.emplace_back(token(jsoncons::make_unique(std::move(key_toks)))); + } + break; + } + case token_kind::end_expression_type: + { + std::vector 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(jsoncons::json_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(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 toks; + auto it = output_stack_.rbegin(), prev=output_stack_.rbegin(); + std::size_t arg_count = 0, tok_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)); + tok_count++; + + // json formula supports operators as params + // swap it with the argument! + if (it->type() == token_kind::argument && + prev->type() == token_kind::binary_operator) { + std::swap(toks[tok_count-1], toks[tok_count-2]); + } + + prev=it; + ++it; + } + + // debug the toks... + if ( debug_ ) { + std::cout << "toks-1" << std::endl; + for (auto& t : toks) + { + std::cout << t.to_string() << std::endl; + } + } + + if (it == output_stack_.rend()) + { + ec = jsonformula_errc::unbalanced_parentheses; + return; + } + if (it->arity() && arg_count != *(it->arity())) + { + ec = jsonformula_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()); + + // debug the toks... + if ( debug_ ) { + std::cout << "toks-2" << std::endl; + for (auto& t : toks) + { + std::cout << t.to_string() << std::endl; + } + } + + 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(std::move(toks))); + } + else + { + output_stack_.emplace_back(token(jsoncons::make_unique(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; + } + + // debug the output stack... + if ( debug_ ) { + std::cout << "post-push" << std::endl; + for (auto& t : output_stack_) + { + std::cout << t.to_string() << std::endl; + } + } + } + + 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 = jsonformula_errc::invalid_codepoint; + } + return cp; + } +}; + diff --git a/json-formula/json-formula-functions.hpp b/json-formula/json-formula-functions.hpp new file mode 100644 index 0000000..c42255b --- /dev/null +++ b/json-formula/json-formula-functions.hpp @@ -0,0 +1,1325 @@ +// function_base +class function_base +{ + jsoncons::optional arg_count_; +public: + function_base(jsoncons::optional arg_count) + : arg_count_(arg_count) + { + } + + jsoncons::optional arity() const + { + return arg_count_; + } + + virtual ~function_base() = default; + + virtual reference evaluate(std::vector& args, dynamic_resources&, 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) + { + } + + virtual std::string to_string(std::size_t = 0) const override + { + return std::string("abs() function"); + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + switch (arg0.type()) + { + case jsoncons::json_type::uint64_value: + return arg0; + case jsoncons::json_type::int64_value: + { + return arg0.template as() >= 0 ? arg0 : *resources.create_json(std::abs(arg0.template as())); + } + case jsoncons::json_type::double_value: + { + return arg0.template as() >= 0 ? arg0 : *resources.create_json(std::abs(arg0.template as())); + } + default: + { +// auto at = arg0.type(); + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + } + } +}; + +class avg_function : public function_base +{ +public: + avg_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jsonformula_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 = jsonformula_errc::invalid_type; + return resources.null_value(); + } + sum += j.template as(); + } + + 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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + switch (arg0.type()) + { + case jsoncons::json_type::uint64_value: + case jsoncons::json_type::int64_value: + { + return *resources.create_json(arg0.template as()); + } + case jsoncons::json_type::double_value: + { + return *resources.create_json(std::ceil(arg0.template as())); + } + default: + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + } +}; + +class contains_function : public function_base +{ +public: + contains_function() + : function_base(2) + { + } + + virtual std::string to_string(std::size_t = 0) const override + { + return std::string("contains() function"); + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_value())) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + + reference arg0 = args[0].value(); + reference arg1 = args[1].value(); + + switch (arg0.type()) + { + case jsoncons::json_type::array_value: + for (auto& j : arg0.array_range()) + { + if (j == arg1) + { + return resources.true_value(); + } + } + return resources.false_value(); + case jsoncons::json_type::string_value: + { + if (arg1.is_number()) { + auto sv0 = arg0.template as(); + auto sv1 = std::to_string(arg1.template as()); + return sv0.find(sv1) != string_view_type::npos ? resources.true_value() : resources.false_value(); + } else if (!arg1.is_string()) { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } else { + auto sv0 = arg0.template as(); + auto sv1 = arg1.template as(); + return sv0.find(sv1) != string_view_type::npos ? resources.true_value() : resources.false_value(); + } + } + default: + { + ec = jsonformula_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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_value())) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_string()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg1 = args[1].value(); + if (!arg1.is_string()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + auto sv0 = arg0.template as(); + auto sv1 = arg1.template as(); + + 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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + switch (arg0.type()) + { + case jsoncons::json_type::uint64_value: + case jsoncons::json_type::int64_value: + { + return *resources.create_json(arg0.template as()); + } + case jsoncons::json_type::double_value: + { + return *resources.create_json(std::floor(arg0.template as())); + } + default: + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + } +}; + +class join_function : public function_base +{ +public: + join_function() + : function_base(2) + { + } + + reference evaluate(std::vector& args, dynamic_resources& 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 = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + if (!arg0.is_string()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + if (!arg1.is_array()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + string_type sep = arg0.template as(); + string_type buf; + for (auto& j : arg1.array_range()) + { + if (!j.is_string()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + if (!buf.empty()) + { + buf.append(sep); + } + auto sv = j.template as(); + 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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + + switch (arg0.type()) + { + case jsoncons::json_type::object_value: + case jsoncons::json_type::array_value: + return *resources.create_json(arg0.size()); + case jsoncons::json_type::string_value: + { + auto sv0 = arg0.template as(); + auto length = jsoncons::unicode_traits::count_codepoints(sv0.data(), sv0.size()); + return *resources.create_json(length); + } + default: + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + } + } +}; + +class max_function : public function_base +{ +public: + max_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jsonformula_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 = jsonformula_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 = jsonformula_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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_expression())) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jsonformula_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 = jsonformula_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 = jsonformula_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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_expression() && args[1].is_value())) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + const auto& expr = args[0].expression(); + + reference arg0 = args[1].value(); + if (!arg0.is_array()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + auto result = resources.create_json(jsoncons::json_array_arg); + + for (auto& item : arg0.array_range()) + { + auto& j = expr.evaluate(item, resources, ec); + if (ec) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + result->emplace_back(jsoncons::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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jsonformula_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 = jsonformula_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 = jsonformula_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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_expression())) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jsonformula_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 = jsonformula_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 = jsonformula_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()) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + if (args.empty()) + { + ec = jsonformula_errc::invalid_arity; + return resources.null_value(); + } + + for (auto& param : args) + { + if (!param.is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + } + + reference arg0 = args[0].value(); + if (!arg0.is_object()) + { + ec = jsonformula_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 = jsonformula_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) + { + } + + virtual std::string to_string(std::size_t = 0) const override + { + return std::string("type() function"); + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + + switch (arg0.type()) + { + case jsoncons::json_type::int64_value: + case jsoncons::json_type::uint64_value: + case jsoncons::json_type::double_value: + return resources.number_type_name(); + case jsoncons::json_type::bool_value: + return resources.boolean_type_name(); + case jsoncons::json_type::string_value: + return resources.string_type_name(); + case jsoncons::json_type::object_value: + return resources.object_type_name(); + case jsoncons::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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jsonformula_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 = jsonformula_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 = jsonformula_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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_expression())) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jsonformula_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 = jsonformula_errc::invalid_type; + } + + reference key2 = expr.evaluate(rhs, resources, ec2); + if (!(key2.is_number() == is_number && key2.is_string() == is_string)) + { + ec = jsonformula_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) + { + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("keys_function\n"); + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_object()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + auto result = resources.create_json(jsoncons::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) + { + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("values_function\n"); + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_object()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + auto result = resources.create_json(jsoncons::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) + { + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("reverse_function\n"); + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + switch (arg0.type()) + { + case jsoncons::json_type::string_value: + { + string_view_type sv = arg0.as_string_view(); + std::basic_string buf; + jsoncons::unicode_traits::convert(sv.data(), sv.size(), buf); + std::reverse(buf.begin(), buf.end()); + string_type s; + jsoncons::unicode_traits::convert(buf.data(), buf.size(), s); + return *resources.create_json(s); + } + case jsoncons::json_type::array_value: + { + auto result = resources.create_json(arg0); + std::reverse(result->array_range().begin(),result->array_range().end()); + return *result; + } + default: + ec = jsonformula_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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!(args[0].is_value() && args[1].is_value())) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_string()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg1 = args[1].value(); + if (!arg1.is_string()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + auto sv0 = arg0.template as(); + auto sv1 = arg1.template as(); + + 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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (!arg0.is_array()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + double sum = 0; + for (auto& j : arg0.array_range()) + { + if (!j.is_number()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + sum += j.template as(); + } + + return *resources.create_json(sum); + } +}; + +class to_array_function final : public function_base +{ +public: + to_array_function() + : function_base(1) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + if (arg0.is_array()) + { + return arg0; + } + else + { + auto result = resources.create_json(jsoncons::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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + switch (arg0.type()) + { + case jsoncons::json_type::int64_value: + case jsoncons::json_type::uint64_value: + case jsoncons::json_type::double_value: + return arg0; + case jsoncons::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(); + } + } + case jsoncons::json_type::null_value: // json-formula addition + return *resources.create_json(0); + + 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& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + if (!args[0].is_value()) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + return *resources.create_json(arg0.template as()); + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("to_string() function"); + } +}; + +class not_null_function final : public function_base +{ +public: + not_null_function() + : function_base(jsoncons::optional()) + { + } + + reference evaluate(std::vector& args, dynamic_resources& 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("not_null_function"); + } +}; + +class value_function final : public function_base +{ +public: + value_function() + : function_base(2) + { + } + + reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override + { + JSONCONS_ASSERT(args.size() == *this->arity()); + + // we need two params + if (!(args[0].is_value() && args[1].is_value())) + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + + reference arg0 = args[0].value(); + reference arg1 = args[1].value(); + + switch (arg0.type()) + { + case jsoncons::json_type::array_value: + { + int64_t idx = -1; // start with a bogus value + auto a1t = arg1.type(); + if ( a1t == jsoncons::json_type::int64_value || + a1t == jsoncons::json_type::uint64_value || + a1t == jsoncons::json_type::double_value ) { + idx = arg1.template as(); + } else if ( a1t == jsoncons::json_type::string_value ) { + auto sv = arg1.as_string_view(); + jsoncons::detail::to_integer(sv.data(), sv.length(), idx); + } + if ( idx >=0 ) + return arg0[idx]; + else + return resources.null_value(); + } + case jsoncons::json_type::object_value: + { +// auto a1t = arg1.type(); + if ( arg1.is_string() ) { + return arg0[arg1.as_string_view()]; + } else + return resources.null_value(); + } + default: + { + ec = jsonformula_errc::invalid_type; + return resources.null_value(); + } + } + + // return empty/null + return resources.null_value(); + } + + std::string to_string(std::size_t = 0) const override + { + return std::string("value_function"); + } +}; + diff --git a/json-formula/json-formula-resources.hpp b/json-formula/json-formula-resources.hpp new file mode 100644 index 0000000..bce85c5 --- /dev/null +++ b/json-formula/json-formula-resources.hpp @@ -0,0 +1,92 @@ +// dynamic_resources + +#define JSONFORMULA_STRING_CONSTANT(CharT, Str) jsoncons::string_constant_of_type(Str, JSONCONS_WIDEN(Str)) + +template +class dynamic_resources +{ + typedef typename Json::char_type char_type; + typedef typename Json::char_traits_type char_traits_type; + typedef std::basic_string string_type; + typedef typename Json::string_view_type string_view_type; + typedef JsonReference reference; + using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + typedef typename Json::const_pointer const_pointer; + + std::vector> temp_storage_; + +public: + ~dynamic_resources() + { + } + + reference number_type_name() + { + static Json number_type_name(JSONFORMULA_STRING_CONSTANT(char_type, "number")); + + return number_type_name; + } + + reference boolean_type_name() + { + static Json boolean_type_name(JSONFORMULA_STRING_CONSTANT(char_type, "boolean")); + + return boolean_type_name; + } + + reference string_type_name() + { + static Json string_type_name(JSONFORMULA_STRING_CONSTANT(char_type, "string")); + + return string_type_name; + } + + reference object_type_name() + { + static Json object_type_name(JSONFORMULA_STRING_CONSTANT(char_type, "object")); + + return object_type_name; + } + + reference array_type_name() + { + static Json array_type_name(JSONFORMULA_STRING_CONSTANT(char_type, "array")); + + return array_type_name; + } + + reference null_type_name() + { + static Json null_type_name(JSONFORMULA_STRING_CONSTANT(char_type, "null")); + + return null_type_name; + } + + reference true_value() const + { + static const Json true_value(true, jsoncons::semantic_tag::none); + return true_value; + } + + reference false_value() const + { + static const Json false_value(false, jsoncons::semantic_tag::none); + return false_value; + } + + reference null_value() const + { + static const Json null_value(jsoncons::null_type(), jsoncons::semantic_tag::none); + return null_value; + } + + template + Json* create_json(Args&& ... args) + { + auto temp = jsoncons::make_unique(std::forward(args)...); + Json* ptr = temp.get(); + temp_storage_.emplace_back(std::move(temp)); + return ptr; + } +}; + diff --git a/json-formula/json-formula.hpp b/json-formula/json-formula.hpp index 6049a80..1725c7f 100644 --- a/json-formula/json-formula.hpp +++ b/json-formula/json-formula.hpp @@ -5,8 +5,10 @@ // This started as the JMESPath parser for JsonCons // and was extended by Leonard Rosenthol (lrosenth@adobe.com) -// to support JsonFormula - +// to support json-formula +// +// Broke the single file into multiple files (that are all included from here) +// in order to make it easier to add more functions to the parser. #ifndef JSONFORMULA_HPP #define JSONFORMULA_HPP @@ -22,6438 +24,15 @@ #include // std::stable_sort, std::reverse #include // std::abs #include -#include "json-formula_error.hpp" +#include "json-formula-error.hpp" +#include "json-formula-declarations.hpp" namespace jsonformula { - 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, - // json-formula - plus_op, - minus_op, - mult_op, - div_op, - concat_op, - union_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: - case operator_kind::plus_op: // json-formula. right place? - case operator_kind::minus_op: - case operator_kind::mult_op: - case operator_kind::div_op: - case operator_kind::concat_op: - case operator_kind::union_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 start_; - jsoncons::optional stop_; - int64_t step_; - - slice() - : start_(), stop_(), step_(1) - { - } - - slice(const jsoncons::optional& start, const jsoncons::optional& end, int64_t step) - : start_(start), stop_(end), step_(step) - { - } - - slice(const slice& other) - : start_(other.start_), stop_(other.stop_), step_(other.step_) - { - } - - slice& operator=(const slice& rhs) - { - if (this != &rhs) - { - if (rhs.start_) - { - start_ = rhs.start_; - } - else - { - start_.reset(); - } - if (rhs.stop_) - { - stop_ = rhs.stop_; - } - else - { - stop_.reset(); - } - step_ = rhs.step_; - } - return *this; - } - - int64_t get_start(std::size_t size) const - { - if (start_) - { - auto len = *start_ >= 0 ? *start_ : (static_cast(size) + *start_); - return len <= static_cast(size) ? len : static_cast(size); - } - else - { - if (step_ >= 0) - { - return 0; - } - else - { - return static_cast(size); - } - } - } - - int64_t get_stop(std::size_t size) const - { - if (stop_) - { - auto len = *stop_ >= 0 ? *stop_ : (static_cast(size) + *stop_); - return len <= static_cast(size) ? len : static_cast(size); - } - else - { - return step_ >= 0 ? static_cast(size) : -1; - } - } - - int64_t step() const - { - return step_; // Allow negative - } - }; - 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, - // json-formula - jf_expression, - plus_operator, - minus_operator, - mult_operator, - div_operator, - concat_operator, - union_operator - }; - - // dynamic_resources - -#define JSONFORMULA_STRING_CONSTANT(CharT, Str) jsoncons::string_constant_of_type(Str, JSONCONS_WIDEN(Str)) - - template - class dynamic_resources - { - typedef typename Json::char_type char_type; - typedef typename Json::char_traits_type char_traits_type; - typedef std::basic_string string_type; - typedef typename Json::string_view_type string_view_type; - typedef JsonReference reference; - using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; - typedef typename Json::const_pointer const_pointer; - - std::vector> temp_storage_; - - public: - ~dynamic_resources() - { - } - - reference number_type_name() - { - static Json number_type_name(JSONFORMULA_STRING_CONSTANT(char_type, "number")); - - return number_type_name; - } - - reference boolean_type_name() - { - static Json boolean_type_name(JSONFORMULA_STRING_CONSTANT(char_type, "boolean")); - - return boolean_type_name; - } - - reference string_type_name() - { - static Json string_type_name(JSONFORMULA_STRING_CONSTANT(char_type, "string")); - - return string_type_name; - } - - reference object_type_name() - { - static Json object_type_name(JSONFORMULA_STRING_CONSTANT(char_type, "object")); - - return object_type_name; - } - - reference array_type_name() - { - static Json array_type_name(JSONFORMULA_STRING_CONSTANT(char_type, "array")); - - return array_type_name; - } - - reference null_type_name() - { - static Json null_type_name(JSONFORMULA_STRING_CONSTANT(char_type, "null")); - - return null_type_name; - } - - reference true_value() const - { - static const Json true_value(true, jsoncons::semantic_tag::none); - return true_value; - } - - reference false_value() const - { - static const Json false_value(false, jsoncons::semantic_tag::none); - return false_value; - } - - reference null_value() const - { - static const Json null_value(jsoncons::null_type(), jsoncons::semantic_tag::none); - return null_value; - } - - template - Json* create_json(Args&& ... args) - { - auto temp = jsoncons::make_unique(std::forward(args)...); - Json* ptr = temp.get(); - temp_storage_.emplace_back(std::move(temp)); - return ptr; - } - }; - - template - class jsonformula_evaluator - { - public: - typedef typename Json::char_type char_type; - typedef typename Json::char_traits_type char_traits_type; - typedef std::basic_string string_type; - typedef typename Json::string_view_type string_view_type; - typedef JsonReference reference; - using pointer = typename std::conditional::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_number() && ref.template as()==0) || // json-formula also treats 0 as false - 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&, 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& 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&, 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& resources, std::error_code& ec) const = 0; - - virtual void add_expression(std::unique_ptr&& 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 arg_count_; - public: - function_base(jsoncons::optional arg_count) - : arg_count_(arg_count) - { - } - - jsoncons::optional arity() const - { - return arg_count_; - } - - virtual ~function_base() = default; - - virtual reference evaluate(std::vector& args, dynamic_resources&, 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) - { - } - - virtual std::string to_string(std::size_t = 0) const override - { - return std::string("abs() function"); - } - - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - switch (arg0.type()) - { - case jsoncons::json_type::uint64_value: - return arg0; - case jsoncons::json_type::int64_value: - { - return arg0.template as() >= 0 ? arg0 : *resources.create_json(std::abs(arg0.template as())); - } - case jsoncons::json_type::double_value: - { - return arg0.template as() >= 0 ? arg0 : *resources.create_json(std::abs(arg0.template as())); - } - default: - { -// auto at = arg0.type(); - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - } - } - }; - - class avg_function : public function_base - { - public: - avg_function() - : function_base(1) - { - } - - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - if (!arg0.is_array()) - { - ec = jsonformula_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 = jsonformula_errc::invalid_type; - return resources.null_value(); - } - sum += j.template as(); - } - - 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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - switch (arg0.type()) - { - case jsoncons::json_type::uint64_value: - case jsoncons::json_type::int64_value: - { - return *resources.create_json(arg0.template as()); - } - case jsoncons::json_type::double_value: - { - return *resources.create_json(std::ceil(arg0.template as())); - } - default: - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - } - }; - - class contains_function : public function_base - { - public: - contains_function() - : function_base(2) - { - } - - virtual std::string to_string(std::size_t = 0) const override - { - return std::string("contains() function"); - } - - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!(args[0].is_value() && args[1].is_value())) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - - reference arg0 = args[0].value(); - reference arg1 = args[1].value(); - - switch (arg0.type()) - { - case jsoncons::json_type::array_value: - for (auto& j : arg0.array_range()) - { - if (j == arg1) - { - return resources.true_value(); - } - } - return resources.false_value(); - case jsoncons::json_type::string_value: - { - if (arg1.is_number()) { - auto sv0 = arg0.template as(); - auto sv1 = std::to_string(arg1.template as()); - return sv0.find(sv1) != string_view_type::npos ? resources.true_value() : resources.false_value(); - } else if (!arg1.is_string()) { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } else { - auto sv0 = arg0.template as(); - auto sv1 = arg1.template as(); - return sv0.find(sv1) != string_view_type::npos ? resources.true_value() : resources.false_value(); - } - } - default: - { - ec = jsonformula_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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!(args[0].is_value() && args[1].is_value())) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - if (!arg0.is_string()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg1 = args[1].value(); - if (!arg1.is_string()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - auto sv0 = arg0.template as(); - auto sv1 = arg1.template as(); - - 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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - switch (arg0.type()) - { - case jsoncons::json_type::uint64_value: - case jsoncons::json_type::int64_value: - { - return *resources.create_json(arg0.template as()); - } - case jsoncons::json_type::double_value: - { - return *resources.create_json(std::floor(arg0.template as())); - } - default: - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - } - }; - - class join_function : public function_base - { - public: - join_function() - : function_base(2) - { - } - - reference evaluate(std::vector& args, dynamic_resources& 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 = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - if (!arg0.is_string()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - if (!arg1.is_array()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - string_type sep = arg0.template as(); - string_type buf; - for (auto& j : arg1.array_range()) - { - if (!j.is_string()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - if (!buf.empty()) - { - buf.append(sep); - } - auto sv = j.template as(); - 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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - - switch (arg0.type()) - { - case jsoncons::json_type::object_value: - case jsoncons::json_type::array_value: - return *resources.create_json(arg0.size()); - case jsoncons::json_type::string_value: - { - auto sv0 = arg0.template as(); - auto length = jsoncons::unicode_traits::count_codepoints(sv0.data(), sv0.size()); - return *resources.create_json(length); - } - default: - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - } - } - }; - - class max_function : public function_base - { - public: - max_function() - : function_base(1) - { - } - - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - if (!arg0.is_array()) - { - ec = jsonformula_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 = jsonformula_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 = jsonformula_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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!(args[0].is_value() && args[1].is_expression())) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - if (!arg0.is_array()) - { - ec = jsonformula_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 = jsonformula_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 = jsonformula_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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!(args[0].is_expression() && args[1].is_value())) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - const auto& expr = args[0].expression(); - - reference arg0 = args[1].value(); - if (!arg0.is_array()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - auto result = resources.create_json(jsoncons::json_array_arg); - - for (auto& item : arg0.array_range()) - { - auto& j = expr.evaluate(item, resources, ec); - if (ec) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - result->emplace_back(jsoncons::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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - if (!arg0.is_array()) - { - ec = jsonformula_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 = jsonformula_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 = jsonformula_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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!(args[0].is_value() && args[1].is_expression())) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - if (!arg0.is_array()) - { - ec = jsonformula_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 = jsonformula_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 = jsonformula_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()) - { - } - - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override - { - if (args.empty()) - { - ec = jsonformula_errc::invalid_arity; - return resources.null_value(); - } - - for (auto& param : args) - { - if (!param.is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - } - - reference arg0 = args[0].value(); - if (!arg0.is_object()) - { - ec = jsonformula_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 = jsonformula_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) - { - } - - virtual std::string to_string(std::size_t = 0) const override - { - return std::string("type() function"); - } - - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - - switch (arg0.type()) - { - case jsoncons::json_type::int64_value: - case jsoncons::json_type::uint64_value: - case jsoncons::json_type::double_value: - return resources.number_type_name(); - case jsoncons::json_type::bool_value: - return resources.boolean_type_name(); - case jsoncons::json_type::string_value: - return resources.string_type_name(); - case jsoncons::json_type::object_value: - return resources.object_type_name(); - case jsoncons::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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - if (!arg0.is_array()) - { - ec = jsonformula_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 = jsonformula_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 = jsonformula_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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!(args[0].is_value() && args[1].is_expression())) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - if (!arg0.is_array()) - { - ec = jsonformula_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 = jsonformula_errc::invalid_type; - } - - reference key2 = expr.evaluate(rhs, resources, ec2); - if (!(key2.is_number() == is_number && key2.is_string() == is_string)) - { - ec = jsonformula_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) - { - } - - std::string to_string(std::size_t = 0) const override - { - return std::string("keys_function\n"); - } - - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - if (!arg0.is_object()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - auto result = resources.create_json(jsoncons::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) - { - } - - std::string to_string(std::size_t = 0) const override - { - return std::string("values_function\n"); - } - - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - if (!arg0.is_object()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - auto result = resources.create_json(jsoncons::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) - { - } - - std::string to_string(std::size_t = 0) const override - { - return std::string("reverse_function\n"); - } - - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - switch (arg0.type()) - { - case jsoncons::json_type::string_value: - { - string_view_type sv = arg0.as_string_view(); - std::basic_string buf; - jsoncons::unicode_traits::convert(sv.data(), sv.size(), buf); - std::reverse(buf.begin(), buf.end()); - string_type s; - jsoncons::unicode_traits::convert(buf.data(), buf.size(), s); - return *resources.create_json(s); - } - case jsoncons::json_type::array_value: - { - auto result = resources.create_json(arg0); - std::reverse(result->array_range().begin(),result->array_range().end()); - return *result; - } - default: - ec = jsonformula_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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!(args[0].is_value() && args[1].is_value())) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - if (!arg0.is_string()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg1 = args[1].value(); - if (!arg1.is_string()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - auto sv0 = arg0.template as(); - auto sv1 = arg1.template as(); - - 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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - if (!arg0.is_array()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - double sum = 0; - for (auto& j : arg0.array_range()) - { - if (!j.is_number()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - sum += j.template as(); - } - - return *resources.create_json(sum); - } - }; - - class to_array_function final : public function_base - { - public: - to_array_function() - : function_base(1) - { - } - - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - if (arg0.is_array()) - { - return arg0; - } - else - { - auto result = resources.create_json(jsoncons::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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - switch (arg0.type()) - { - case jsoncons::json_type::int64_value: - case jsoncons::json_type::uint64_value: - case jsoncons::json_type::double_value: - return arg0; - case jsoncons::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(); - } - } - case jsoncons::json_type::null_value: // json-formula addition - return *resources.create_json(0); - - 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& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - if (!args[0].is_value()) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - return *resources.create_json(arg0.template as()); - } - - std::string to_string(std::size_t = 0) const override - { - return std::string("to_string() function"); - } - }; - - class not_null_function final : public function_base - { - public: - not_null_function() - : function_base(jsoncons::optional()) - { - } - - reference evaluate(std::vector& args, dynamic_resources& 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("not_null_function"); - } - }; - - class value_function final : public function_base - { - public: - value_function() - : function_base(2) - { - } - - reference evaluate(std::vector& args, dynamic_resources& resources, std::error_code& ec) const override - { - JSONCONS_ASSERT(args.size() == *this->arity()); - - // we need two params - if (!(args[0].is_value() && args[1].is_value())) - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - - reference arg0 = args[0].value(); - reference arg1 = args[1].value(); - - switch (arg0.type()) - { - case jsoncons::json_type::array_value: - { - int64_t idx = -1; // start with a bogus value - auto a1t = arg1.type(); - if ( a1t == jsoncons::json_type::int64_value || - a1t == jsoncons::json_type::uint64_value || - a1t == jsoncons::json_type::double_value ) { - idx = arg1.template as(); - } else if ( a1t == jsoncons::json_type::string_value ) { - auto sv = arg1.as_string_view(); - jsoncons::detail::to_integer(sv.data(), sv.length(), idx); - } - if ( idx >=0 ) - return arg0[idx]; - else - return resources.null_value(); - } - case jsoncons::json_type::object_value: - { -// auto a1t = arg1.type(); - if ( arg1.is_string() ) { - return arg0[arg1.as_string_view()]; - } else - return resources.null_value(); - } - default: - { - ec = jsonformula_errc::invalid_type; - return resources.null_value(); - } - } - - // return empty/null - return resources.null_value(); - } - - std::string to_string(std::size_t = 0) const override - { - return std::string("value_function"); - } - }; - - // token - - class token - { - public: - token_kind type_; - - union - { - std::unique_ptr 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) - : type_(token_kind::expression) - { - new (&expression_) std::unique_ptr(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(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(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 arity() const - { - return type_ == token_kind::function ? function_->arity() : jsoncons::optional(); - } - - 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(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& output_stack, dynamic_resources& resources, std::error_code& ec) - { - pointer root_ptr = std::addressof(doc); - std::vector stack; - std::vector 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 = jsonformula_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& 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"); - 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&, 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"); - 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& 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"); - 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& 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"); - 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& resources, std::error_code&) const override - { - if ( lhs.is_number() && rhs.is_number() ) { - return lhs < rhs ? resources.true_value() : resources.false_value(); - } else if ( lhs.is_string() && rhs.is_string() ) { - return lhs < rhs ? resources.true_value() : resources.false_value(); - } else if ( lhs.is_number() && rhs.is_string() ) { - return lhs < std::stod(rhs.template as()) ? resources.true_value() : resources.false_value(); - } else if ( lhs.is_string() && rhs.is_number() ) { - return lhs < std::to_string(rhs.template as()) ? resources.true_value() : resources.false_value(); - } else { - // for any other type comparisons, return false - return 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"); - 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& resources, std::error_code&) const override - { - if ( lhs.is_number() && rhs.is_number() ) { - return lhs <= rhs ? resources.true_value() : resources.false_value(); - } else if ( lhs.is_string() && rhs.is_string() ) { - return lhs <= rhs ? resources.true_value() : resources.false_value(); - } else if ( lhs.is_number() && rhs.is_string() ) { - return lhs <= std::stod(rhs.template as()) ? resources.true_value() : resources.false_value(); - } else if ( lhs.is_string() && rhs.is_number() ) { - return lhs <= std::to_string(rhs.template as()) ? resources.true_value() : resources.false_value(); - } else { - // for any other type comparisons, return false - return 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"); - 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& resources, std::error_code&) const override - { - if ( lhs.is_number() && rhs.is_number() ) { - return lhs > rhs ? resources.true_value() : resources.false_value(); - } else if ( lhs.is_string() && rhs.is_string() ) { - return lhs > rhs ? resources.true_value() : resources.false_value(); - } else if ( lhs.is_number() && rhs.is_string() ) { - return lhs > std::stod(rhs.template as()) ? resources.true_value() : resources.false_value(); - } else if ( lhs.is_string() && rhs.is_number() ) { - return lhs > std::to_string(rhs.template as()) ? resources.true_value() : resources.false_value(); - } else { - // for any other type comparisons, return false - return 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"); - 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& resources, std::error_code&) const override - { - if ( lhs.is_number() && rhs.is_number() ) { - return lhs >= rhs ? resources.true_value() : resources.false_value(); - } else if ( lhs.is_string() && rhs.is_string() ) { - return lhs >= rhs ? resources.true_value() : resources.false_value(); - } else if ( lhs.is_number() && rhs.is_string() ) { - return lhs >= std::stod(rhs.template as()) ? resources.true_value() : resources.false_value(); - } else if ( lhs.is_string() && rhs.is_number() ) { - return lhs >= std::to_string(rhs.template as()) ? resources.true_value() : resources.false_value(); - } else { - // for any other type comparisons, return false - return 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"); - return s; - } - }; - - // for json-fornula - class plus_operator final : public binary_operator - { - public: - plus_operator() - : binary_operator(operator_kind::plus_op) - { - } - - reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override - { - if ( (lhs.is_number() && rhs.is_number()) || - (lhs.is_null() || rhs.is_null()) || - (lhs.is_bool() || rhs.is_bool()) ) { - double l = 0, r = 0; - - if ( lhs.type() == jsoncons::json_type::double_value ) { - l = lhs.template as(); - } else if ( lhs.type() == jsoncons::json_type::string_value ) { - l = std::stod(lhs.template as()); - } else if ( lhs.type() == jsoncons::json_type::bool_value ) { - l = lhs.template as() ? 1 : 0; - } else if ( !lhs.is_null() ) { - l = lhs.template as(); - } else { -// std::cout << pretty_print(lhs) << std::endl; - } - - - if ( rhs.type() == jsoncons::json_type::double_value ) { - r = rhs.template as(); - } else if ( rhs.type() == jsoncons::json_type::string_value ) { - r = std::stod(rhs.template as()); - } else if ( rhs.type() == jsoncons::json_type::bool_value ) { - r = rhs.template as() ? 1 : 0; - } else if ( !rhs.is_null() ){ - r = rhs.template as(); - } else { -// std::cout << pretty_print(rhs) << std::endl; - } - - return *resources.create_json(l + r); - } else if ( lhs.type() == jsoncons::json_type::string_value || rhs.type() == jsoncons::json_type::string_value ) { - double l = 0, r = 0; - - if ( lhs.type() == jsoncons::json_type::string_value ) - l = std::stod(lhs.template as()); - else if ( lhs.type() != jsoncons::json_type::null_value ) - l = lhs.template as(); - - if ( rhs.type() == jsoncons::json_type::string_value ) - r = std::stod(rhs.template as()); - else if ( rhs.type() != jsoncons::json_type::null_value ) - r = rhs.template as(); - - return *resources.create_json(l + r); - } else if ( lhs.type() == jsoncons::json_type::array_value && rhs.type() == jsoncons::json_type::array_value ) { - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - auto numEnts = std::max( lhs.size(), rhs.size() ); - for (int64_t i=0; i(); - if ( i < rhs.size() ) r = rhs[i].template as(); - result->push_back( l + r); - } - return *result; - } else if ( lhs.type() == jsoncons::json_type::array_value ) { - double d = rhs.template as(); - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - for (auto& v : lhs.array_range()) { - result->push_back( v.template as() + d ); - } - return *result; - } else if ( rhs.type() == jsoncons::json_type::array_value ) { - double d = lhs.template as(); - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - for (auto& v : rhs.array_range()) { - result->push_back( v.template as() + d ); - } - return *result; - } else { - std::cout << pretty_print(lhs) << " " << pretty_print(rhs) << std::endl; - } - - 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("plus_operator"); - return s; - } - }; - - class minus_operator final : public binary_operator - { - public: - minus_operator() - : binary_operator(operator_kind::minus_op) - { - } - - reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override - { - if ( (lhs.is_number() && rhs.is_number()) || - (lhs.is_null() || rhs.is_null()) || - (lhs.is_bool() || rhs.is_bool()) ) { - double l = 0, r = 0; - - if ( lhs.type() == jsoncons::json_type::double_value ) { - l = lhs.template as(); - } else if ( lhs.type() == jsoncons::json_type::string_value ) { - l = std::stod(lhs.template as()); - } else if ( lhs.type() == jsoncons::json_type::bool_value ) { - l = lhs.template as() ? 1 : 0; - } else if ( !lhs.is_null() ) { - l = lhs.template as(); - } else { -// std::cout << pretty_print(lhs) << std::endl; - } - - - if ( rhs.type() == jsoncons::json_type::double_value ) { - r = rhs.template as(); - } else if ( rhs.type() == jsoncons::json_type::string_value ) { - r = std::stod(rhs.template as()); - } else if ( rhs.type() == jsoncons::json_type::bool_value ) { - r = rhs.template as() ? 1 : 0; - } else if ( !rhs.is_null() ){ - r = rhs.template as(); - } else { -// std::cout << pretty_print(rhs) << std::endl; - } - - return *resources.create_json(l - r); - } else if ( lhs.type() == jsoncons::json_type::string_value || rhs.type() == jsoncons::json_type::string_value ) { - double l = 0, r = 0; - - if ( lhs.type() == jsoncons::json_type::string_value ) - l = std::stod(lhs.template as()); - else if ( lhs.type() != jsoncons::json_type::null_value ) - l = lhs.template as(); - - if ( rhs.type() == jsoncons::json_type::string_value ) - r = std::stod(rhs.template as()); - else if ( rhs.type() != jsoncons::json_type::null_value ) - r = rhs.template as(); - - return *resources.create_json(l - r); - } else if ( lhs.type() == jsoncons::json_type::array_value && rhs.type() == jsoncons::json_type::array_value ) { - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - auto numEnts = std::max( lhs.size(), rhs.size() ); - for (int64_t i=0; i(); - if ( i < rhs.size() ) r = rhs[i].template as(); - result->push_back( l - r); - } - return *result; - } else if ( lhs.type() == jsoncons::json_type::array_value ) { - double d = rhs.template as(); - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - for (auto& v : lhs.array_range()) { - result->push_back( v.template as() - d ); - } - return *result; - } else if ( rhs.type() == jsoncons::json_type::array_value ) { - double d = lhs.template as(); - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - for (auto& v : rhs.array_range()) { - result->push_back( d - v.template as() ); - } - return *result; - } - - 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("minus_operator"); - return s; - } - }; - - class mult_operator final : public binary_operator - { - public: - mult_operator() - : binary_operator(operator_kind::mult_op) - { - } - - reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override - { - if ( (lhs.is_number() && rhs.is_number()) || - (lhs.is_null() || rhs.is_null()) || - (lhs.is_bool() || rhs.is_bool()) ) { - double l = 0, r = 0; - - if ( lhs.type() == jsoncons::json_type::double_value ) { - l = lhs.template as(); - } else if ( lhs.type() == jsoncons::json_type::string_value ) { - l = std::stod(lhs.template as()); - } else if ( lhs.type() == jsoncons::json_type::bool_value ) { - l = lhs.template as() ? 1 : 0; - } else if ( !lhs.is_null() ) { - l = lhs.template as(); - } else { -// std::cout << pretty_print(lhs) << std::endl; - } - - - if ( rhs.type() == jsoncons::json_type::double_value ) { - r = rhs.template as(); - } else if ( rhs.type() == jsoncons::json_type::string_value ) { - r = std::stod(rhs.template as()); - } else if ( rhs.type() == jsoncons::json_type::bool_value ) { - r = rhs.template as() ? 1 : 0; - } else if ( !rhs.is_null() ){ - r = rhs.template as(); - } else { -// std::cout << pretty_print(rhs) << std::endl; - } - - return *resources.create_json(l * r); - } else if ( lhs.type() == jsoncons::json_type::string_value || rhs.type() == jsoncons::json_type::string_value ) { - double l = 0, r = 0; - - if ( lhs.type() == jsoncons::json_type::string_value ) - l = std::stod(lhs.template as()); - else if ( lhs.type() != jsoncons::json_type::null_value ) - l = lhs.template as(); - - if ( rhs.type() == jsoncons::json_type::string_value ) - r = std::stod(rhs.template as()); - else if ( rhs.type() != jsoncons::json_type::null_value ) - r = rhs.template as(); - - return *resources.create_json(l * r); - } else if ( lhs.type() == jsoncons::json_type::array_value && rhs.type() == jsoncons::json_type::array_value ) { - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - auto numEnts = std::max( lhs.size(), rhs.size() ); - for (int64_t i=0; i(); - if ( i < rhs.size() ) r = rhs[i].template as(); - result->push_back( l * r); - } - return *result; - } else if ( lhs.type() == jsoncons::json_type::array_value ) { - double d = rhs.template as(); - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - for (auto& v : lhs.array_range()) { - result->push_back( v.template as() * d ); - } - return *result; - } else if ( rhs.type() == jsoncons::json_type::array_value ) { - double d = lhs.template as(); - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - for (auto& v : rhs.array_range()) { - result->push_back( v.template as() * d ); - } - return *result; - } - - 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("multiply_operator"); - return s; - } - }; - - class div_operator final : public binary_operator - { - public: - div_operator() - : binary_operator(operator_kind::div_op) - { - } - - reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override - { - if ( (lhs.is_number() && rhs.is_number()) || - (lhs.is_null() || rhs.is_null()) || - (lhs.is_bool() || rhs.is_bool()) ) { - double l = 0, r = 0; - - if ( lhs.type() == jsoncons::json_type::double_value ) { - l = lhs.template as(); - } else if ( lhs.type() == jsoncons::json_type::string_value ) { - l = std::stod(lhs.template as()); - } else if ( lhs.type() == jsoncons::json_type::bool_value ) { - l = lhs.template as() ? 1 : 0; - } else if ( !lhs.is_null() ) { - l = lhs.template as(); - } else { -// std::cout << pretty_print(lhs) << std::endl; - } - - - if ( rhs.type() == jsoncons::json_type::double_value ) { - r = rhs.template as(); - } else if ( rhs.type() == jsoncons::json_type::string_value ) { - r = std::stod(rhs.template as()); - } else if ( rhs.type() == jsoncons::json_type::bool_value ) { - r = rhs.template as() ? 1 : 0; - } else if ( !rhs.is_null() ){ - r = rhs.template as(); - } else { -// std::cout << pretty_print(rhs) << std::endl; - } - - if ( r == 0 ) { - // divide by zero is bad - so return `null` - return resources.null_value(); - } else - return *resources.create_json(l / r); - } else if ( lhs.type() == jsoncons::json_type::string_value || rhs.type() == jsoncons::json_type::string_value ) { - double l = 0, r = 0; - - if ( lhs.type() == jsoncons::json_type::string_value ) - l = std::stod(lhs.template as()); - else if ( lhs.type() != jsoncons::json_type::null_value ) - l = lhs.template as(); - - if ( rhs.type() == jsoncons::json_type::string_value ) - r = std::stod(rhs.template as()); - else if ( rhs.type() != jsoncons::json_type::null_value ) - r = rhs.template as(); - - return *resources.create_json(l / r); - } else if ( lhs.type() == jsoncons::json_type::array_value && rhs.type() == jsoncons::json_type::array_value ) { - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - auto numEnts = std::max( lhs.size(), rhs.size() ); - for (int64_t i=0; i(); - if ( i < rhs.size() ) r = rhs[i].template as(); - result->push_back( l / r); - } - return *result; - } else if ( lhs.type() == jsoncons::json_type::array_value ) { - double d = rhs.template as(); - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - for (auto& v : lhs.array_range()) { - result->push_back( v.template as() / d ); - } - return *result; - } else if ( rhs.type() == jsoncons::json_type::array_value ) { - double d = lhs.template as(); - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - for (auto& v : rhs.array_range()) { - result->push_back( d / v.template as() ); - } - return *result; - } - - 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("divide_operator"); - return s; - } - }; - - class concat_operator final : public binary_operator - { - public: - concat_operator() - : binary_operator(operator_kind::concat_op) - { - } - - reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override - { - // as per json-formula spec - // Arrays shall be coerced to an array of strings. - if ( lhs.type() == jsoncons::json_type::array_value && rhs.type() == jsoncons::json_type::array_value ) { - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - auto numEnts = std::max( lhs.size(), rhs.size() ); - for (int64_t i=0; ipush_back( lhsStr + rhsStr); - } - return *result; - } else if ( lhs.type() == jsoncons::json_type::array_value ) { - std::string rhsStr{to_string(rhs)}; - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - for (auto& v : lhs.array_range()) { - result->push_back( to_string(v) + rhsStr ); - } - return *result; - } else if ( rhs.type() == jsoncons::json_type::array_value ) { - std::string lhsStr{to_string(lhs)}; - auto result = resources.create_json(jsoncons::json_array_arg); // empty array - for (auto& v : rhs.array_range()) { - result->push_back( lhsStr + to_string(v) ); - } - return *result; - } else { - std::string lhsStr{to_string(lhs)}, rhsStr{to_string(rhs)}; - - if ( !lhsStr.empty() || !rhsStr.empty() ) - return *resources.create_json(lhsStr + rhsStr); - 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("concat_operator"); - return s; - } - - std::string to_string(reference& r) const - { - switch ( r.type() ) { - case jsoncons::json_type::array_value: - // should not get here for this scenario... - break; - case jsoncons::json_type::uint64_value: - case jsoncons::json_type::int64_value: - return std::to_string(r.template as()); - break; - case jsoncons::json_type::double_value: - return std::to_string(r.template as()); - break; - case jsoncons::json_type::string_value: - return r.template as(); - break; - case jsoncons::json_type::bool_value: - return r.template as() ? "true" : "false"; - break; - case jsoncons::json_type::null_value: - case jsoncons::json_type::object_value: // technically an error, but we'll treat an NOP - case jsoncons::json_type::half_value: // technically an error, but we'll treat an NOP - case jsoncons::json_type::byte_string_value: // technically an error, but we'll treat an NOP - { - // do nothing! - } - break; - } - - return ""; - } - - }; - - class union_operator final : public binary_operator - { - public: - union_operator() - : binary_operator(operator_kind::union_op) - { - } - - reference evaluate(reference lhs, reference rhs, dynamic_resources& resources, std::error_code&) const override - { - auto addToUnion = [&resources](Json* result, reference r) { - switch ( r.type() ) { - case jsoncons::json_type::array_value: - { - for (auto& v : r.array_range()) { - result->push_back(v); - } - } - break; - case jsoncons::json_type::uint64_value: - case jsoncons::json_type::int64_value: - { - result->push_back(*resources.create_json(r.template as())); - } - break; - case jsoncons::json_type::double_value: - { - result->push_back(*resources.create_json(r.template as())); - } - break; - case jsoncons::json_type::string_value: - { - result->push_back(*resources.create_json(r.template as())); - } - break; - case jsoncons::json_type::bool_value: - { - result->push_back(*resources.create_json(r.template as())); - } - break; - case jsoncons::json_type::null_value: - case jsoncons::json_type::object_value: // technically an error, but we'll treat an NOP - case jsoncons::json_type::half_value: // technically an error, but we'll treat an NOP - case jsoncons::json_type::byte_string_value: // technically an error, but we'll treat an NOP - { - // do nothing! - } - break; - } - }; - - // lhs must be an array... - // and then we determine what to do based on the type of rhs - if ( lhs.type() == jsoncons::json_type::array_value ) { - auto result = resources.create_json(lhs); // copy it since we'll be modifying it - addToUnion(result, rhs); - return *result; - } else if ( rhs.type() == jsoncons::json_type::array_value ) { - auto result = resources.create_json(rhs); // copy it since we'll be modifying it - addToUnion(result, lhs); - return *result; - } - - 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("union_operator"); - 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&&) 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& resources, std::error_code&) const override - { - if ( false /*debug_*/ ) { - std::cout << "(" << to_string() << "): " << 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&, 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& resources, std::error_code&) const override - { - if (!val.is_array()) - { - return resources.null_value(); - } - int64_t slen = static_cast(val.size()); - if (index_ >= 0 && index_ < slen) - { - std::size_t index = static_cast(index_); - return val.at(index); - } - else if ((slen + index_) >= 0 && (slen+index_) < slen) - { - std::size_t index = static_cast(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> expressions_; - public: - projection_base(operator_kind oper) - : expression_base(oper, true) - { - } - - void add_expression(std::unique_ptr&& 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& 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& resources, std::error_code& ec) const override - { - if (!val.is_object()) - { - return resources.null_value(); - } - - auto result = resources.create_json(jsoncons::json_array_arg); - for (auto& item : val.object_range()) - { - // json-formula supports nulls in projections - if (true /*!item.value().is_null()*/) - { - reference j = this->apply_expressions(item.value(), resources, ec); - - // json-formula supports nulls in projections - if (true /*!j.is_null()*/) - { - result->emplace_back(jsoncons::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& resources, std::error_code& ec) const override - { - if (!val.is_array()) - { - // json-formula allows nulls in list projections - // but we need to special case them to return an array of null (since it's a projection!) - if (val.is_null()) { - auto result = resources.create_json(jsoncons::json_array_arg); - result->emplace_back(resources.null_value()); - return *result; - } else - return resources.null_value(); - } - - auto result = resources.create_json(jsoncons::json_array_arg); - for (reference item : val.array_range()) - { - // json-formula allows nulls in list projections - if (true /*!item.is_null()*/) - { - reference j = this->apply_expressions(item, resources, ec); - - // json-formula allows nulls in list projections - if (true /*!j.is_null()*/) - { - result->emplace_back(jsoncons::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& 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 = jsonformula_errc::step_cannot_be_zero; - return resources.null_value(); - } - - auto result = resources.create_json(jsoncons::json_array_arg); - if (step > 0) - { - if (start < 0) - { - start = 0; - } - if (end > static_cast(val.size())) - { - end = val.size(); - } - for (int64_t i = start; i < end; i += step) - { - reference j = this->apply_expressions(val.at(static_cast(i)), resources, ec); - - // json-formula supports nulls in projections - if (true /*!j.is_null()*/) - { - result->emplace_back(jsoncons::json_const_pointer_arg, std::addressof(j)); - } - } - } - else - { - if (start >= static_cast(val.size())) - { - start = static_cast(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(i)), resources, ec); - - // json-formula supports nulls in projections - if (true /*!j.is_null()*/) - { - result->emplace_back(jsoncons::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_list_; - public: - filter_expression(std::vector&& token_list) - : projection_base(operator_kind::projection_op), token_list_(std::move(token_list)) - { - } - - reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override - { - if (!val.is_array()) - { - return resources.null_value(); - } - auto result = resources.create_json(jsoncons::json_array_arg); - - for (auto& item : val.array_range()) - { - Json j(jsoncons::json_const_pointer_arg, evaluate_tokens(item, token_list_, resources, ec)); - if (is_true(j)) - { - reference jj = this->apply_expressions(item, resources, ec); - - // json-formula supports nulls in filter expressions - if (true /*!jj.is_null()*/) - { - result->emplace_back(jsoncons::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& resources, std::error_code& ec) const override - { - if (!val.is_array()) - { - return resources.null_value(); - } - - auto result = resources.create_json(jsoncons::json_array_arg); - for (reference current_elem : val.array_range()) - { - if (current_elem.is_array()) - { - for (reference elem : current_elem.array_range()) - { - // json-formula allows nulls in projections - if (true /*!elem.is_null()*/) - { - reference j = this->apply_expressions(elem, resources, ec); - - // json-formula allows nulls in projections - if (true /*!j.is_null()*/) - { - result->emplace_back(jsoncons::json_const_pointer_arg, std::addressof(j)); - } - } - } - } - else - { - // json-formula allows nulls in projections - if (true /*!current_elem.is_null()*/) - { - reference j = this->apply_expressions(current_elem, resources, ec); - - // json-formula allows nulls in projections - if (true /*!j.is_null()*/) - { - result->emplace_back(jsoncons::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> token_lists_; - public: - multi_select_list(std::vector>&& token_lists) - : token_lists_(std::move(token_lists)) - { - } - - reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override - { -// json-formula allows nulls in projections & lists -// if (val.is_null()) -// { -// return val; -// } - - // in json-formula, empty objects aren't allowed - if ( token_lists_.size()==0 ) { - ec = jsonformula_errc::invalid_object; - return resources.null_value(); - } - - auto result = resources.create_json(jsoncons::json_array_arg); - result->reserve(token_lists_.size()); - - for (auto& list : token_lists_) - { - result->emplace_back(jsoncons::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 tokens; - - key_tokens(string_type&& key, std::vector&& tokens) noexcept - : key(std::move(key)), tokens(std::move(tokens)) - { - } - }; - - class multi_select_hash final : public basic_expression - { - public: - std::vector key_toks_; - - multi_select_hash(std::vector&& key_toks) - : key_toks_(std::move(key_toks)) - { - } - - reference evaluate(reference val, dynamic_resources& resources, std::error_code& ec) const override - { - // in json-formula, null == empty object! -// if (val.is_null()) -// { -// return val; -// } - - // in json-formula, empty objects aren't allowed - if ( key_toks_.size()==0 ) { - ec = jsonformula_errc::invalid_object; - return resources.null_value(); - } - - auto resultp = resources.create_json(jsoncons::json_object_arg); - resultp->reserve(key_toks_.size()); - for (auto& item : key_toks_) - { - resultp->try_emplace(item.key, jsoncons::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"); - return s; - } - }; - - class function_expression final : public basic_expression - { - public: - std::vector toks_; - - function_expression(std::vector&& toks) - : toks_(std::move(toks)) - { - } - - reference evaluate(reference val, dynamic_resources& 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> 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; - static value_function value_func; - - using function_dictionary = std::unordered_map; - 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}, - - // json_formula changes & additions - {string_type{'e','n','d', 's', 'W', 'i', 't', 'h'}, &ends_with_func}, - {string_type{'m','i','n','B','y'}, &min_by_func}, - {string_type{'n','o','t', 'N', 'u','l','l'}, ¬_null_func}, - {string_type{'s','o','r', 't','B','y'}, &sort_by_func}, - {string_type{'s','t','a', 'r','t','s','W','i','t','h'}, &starts_with_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{'v','a','l', 'u','e'}, &value_func}, - }; - auto it = functions_.find(name); - if (it == functions_.end()) - { - ec = jsonformula_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; - } - - const binary_operator* get_plus_operator() const - { - static const plus_operator plus_oper; - return &plus_oper; - } - const binary_operator* get_minus_operator() const - { - static const minus_operator minus_oper; - return &minus_oper; - } - const binary_operator* get_mult_operator() const - { - static const mult_operator mult_oper; - return &mult_oper; - } - const binary_operator* get_div_operator() const - { - static const div_operator div_oper; - return &div_oper; - } - const binary_operator* get_concat_operator() const - { - static const concat_operator concat_oper; - return &concat_oper; - } - const binary_operator* get_union_operator() const - { - static const union_operator union_oper; - return &union_oper; - } - }; - - class jsonformula_expression - { - static_resources resources_; - std::vector output_stack_; - public: - jsonformula_expression() - { - } - - jsonformula_expression(const jsonformula_expression& expr) = delete; - jsonformula_expression& operator=(const jsonformula_expression& expr) = delete; - - jsonformula_expression(jsonformula_expression&& expr) - : resources_(std::move(expr.resources_)), - output_stack_(std::move(expr.output_stack_)) - { - } - - jsonformula_expression(static_resources&& resources, - std::vector&& 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(jsonformula_error(ec)); - } - return result; - } - - Json evaluate(reference doc, std::error_code& ec) - { - if (output_stack_.empty()) - { - return Json::null(); - } - dynamic_resources dynamic_storage; - return deep_copy(*evaluate_tokens(doc, output_stack_, dynamic_storage, ec)); - } - - static jsonformula_expression compile(const string_view_type& expr) - { - jsonformula::detail::jsonformula_evaluator evaluator; - std::error_code ec; - jsonformula_expression result = evaluator.compile(expr.data(), expr.size(), ec); - if (ec) - { - JSONCONS_THROW(jsonformula_error(ec, evaluator.line(), evaluator.column())); - } - return result; - } - - static jsonformula_expression compile(const string_view_type& expr, - std::error_code& ec) - { - jsonformula::detail::jsonformula_evaluator 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 state_stack_; - - std::vector output_stack_; - std::vector operator_stack_; - - - bool debug_; - - public: - jsonformula_evaluator() - : line_(1), column_(1), - begin_input_(nullptr), end_input_(nullptr), - p_(nullptr), debug_(false) - { - } - - std::size_t line() const - { - return line_; - } - - std::size_t column() const - { - return column_; - } - - void debug(bool d=true) { debug_ = d; } - - jsonformula_expression compile(const char_type* path, - std::size_t length, - std::error_code& ec) - { - auto _checkBuffer = [this, &ec](string_type& buffer) { - if (!buffer.empty()) { - jsoncons::json_decoder decoder; - jsoncons::basic_json_reader> reader(buffer, decoder); - std::error_code parse_ec; - reader.read(parse_ec); - if (parse_ec) { - ec = jsonformula_errc::invalid_literal; - return; - } - auto j = decoder.get_result(); - push_token(token(literal_arg, std::move(j)), ec); - buffer.clear(); - } - }; - - push_token(current_node_arg, ec); - if (ec) {return jsonformula_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, buffer); - 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 ')': - { - // json-formula - // check if there is anything in the buffer... - // if so, we need to process it - _checkBuffer(buffer); - - state_stack_.pop_back(); - break; - } - case '[': - ++p_; - ++column_; - state_stack_.emplace_back(path_state::bracket_specifier); - break; - // json-formula - case '+': - case '-': - case '*': - case '/': - case '~': - state_stack_.emplace_back(path_state::jf_expression); - break; - default: - if (state_stack_.size() > 1) - { - state_stack_.pop_back(); - } - else - { - ec = jsonformula_errc::syntax_error; - return jsonformula_expression(); - } - break; - } - break; - case path_state::comparator_expression: - switch(*p_) - { - case ' ':case '\t':case '\r':case '\n': - advance_past_space_character(ec, buffer); - 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 = jsonformula_errc::syntax_error; - return jsonformula_expression(); - } - break; - } - break; - case path_state::lhs_expression: - { - switch (*p_) - { - case ' ':case '\t':case '\r':case '\n': - advance_past_space_character(ec, buffer); - break; - case '\"': - // in json-formula, quoted strings are strings -// state_stack_.back() = path_state::val_expr; - state_stack_.back() = path_state::quoted_string; - ++p_; - ++column_; - break; - case '\'': - // in json-formula, single quoted/raw strings are value expression - state_stack_.back() = path_state::val_expr; - state_stack_.emplace_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 jsonformula_expression();} - state_stack_.back() = path_state::multi_select_hash; - ++p_; - ++column_; - break; - case '*': // wildcard - push_token(token(jsoncons::make_unique()), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - ++p_; - ++column_; - break; - case '(': - { - ++p_; - ++column_; - push_token(lparen_arg, ec); - if (ec) {return jsonformula_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 jsonformula_expression();} - break; - } - case '@': - ++p_; - ++column_; - push_token(token(jsoncons::make_unique()), ec); - if (ec) {return jsonformula_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 if ((*p_ >= '0' && *p_ <= '9') || (*p_ == '-') || (*p_ == '.')) //json-formula supports #'s - { - state_stack_.back() = path_state::number; - buffer.push_back(*p_); - ++p_; - ++column_; - } - else - { - ec = jsonformula_errc::expected_identifier; - return jsonformula_expression(); - } - break; - }; - break; - } - case path_state::sub_expression: - { - switch (*p_) - { - case ' ':case '\t':case '\r':case '\n': - advance_past_space_character(ec, buffer); - break; - case '\"': - // in json-formula, quoted strings are strings -// state_stack_.back() = path_state::val_expr; - state_stack_.emplace_back(path_state::quoted_string); - ++p_; - ++column_; - break; - case '\'': - // in json-formula, single quoted/raw strings are value expression - state_stack_.back() = path_state::val_expr; - state_stack_.emplace_back(path_state::raw_string); - ++p_; - ++column_; - break; - case '{': - push_token(begin_multi_select_hash_arg, ec); - if (ec) {return jsonformula_expression();} - state_stack_.back() = path_state::multi_select_hash; - ++p_; - ++column_; - break; - case '*': - push_token(token(jsoncons::make_unique()), ec); - if (ec) {return jsonformula_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 = jsonformula_errc::expected_identifier; - return jsonformula_expression(); - } - break; - }; - break; - } - case path_state::key_expr: - push_token(token(key_arg, buffer), ec); - if (ec) {return jsonformula_expression();} - buffer.clear(); - state_stack_.pop_back(); - break; - case path_state::val_expr: - push_token(token(jsoncons::make_unique(buffer)), ec); - if (ec) {return jsonformula_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, buffer); - break; - case '&': - push_token(token(begin_expression_type_arg), ec); - if (ec) {return jsonformula_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; - //json-formula supports #'s - case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': - case '.': // don't forget decimal places! - case '-': // don't forget negative numbers! - // we need to first mark this as an argument - state_stack_.back() = path_state::argument; - state_stack_.emplace_back(path_state::rhs_expression); - state_stack_.emplace_back(path_state::lhs_expression); - - // and now handle the number part... - state_stack_.back() = path_state::number; - buffer.push_back(*p_); - ++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 jsonformula_expression(); - } - buffer.clear(); - push_token(token(f), ec); - if (ec) {return jsonformula_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(buffer)), ec); - if (ec) {return jsonformula_expression();} - buffer.clear(); - state_stack_.pop_back(); - break; - } - } - break; - - case path_state::function_expression: - { - // json-formula - // check if there is anything in the buffer... - // if so, we need to process it - _checkBuffer(buffer); - - switch (*p_) - { - case ' ':case '\t':case '\r':case '\n': - advance_past_space_character(ec, buffer); - break; - case ',': - push_token(token(current_node_arg), ec); - if (ec) {return jsonformula_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 jsonformula_expression();} - state_stack_.pop_back(); - ++p_; - ++column_; - break; - } - default: - break; - } - } - break; - - case path_state::argument: - push_token(argument_arg, ec); - if (ec) {return jsonformula_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 jsonformula_expression();} - state_stack_.pop_back(); - break; - - case path_state::quoted_string: - switch (*p_) - { - case '\"': - { - // in json-formula, these should be treated as literal strings - // except when they actually mean identifiers - auto stack_grandparent = state_stack_[state_stack_.size()-2]; - - if ( stack_grandparent == path_state::key_expr || - stack_grandparent == path_state::sub_expression ) { - state_stack_.pop_back(); // quoted_string - state_stack_.back() = path_state::val_expr; // reset this to a val expression... - } else { - push_token(token(literal_arg, Json(buffer)), ec); - if (ec) {return jsonformula_expression();} - buffer.clear(); - - 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': - { - string_type dummy; - state_stack_.pop_back(); // unquoted_string - advance_past_space_character(ec, dummy); - } - 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; - // json-formula also supports escaping the escape character! - // it also supports the same set of escapes as double strings. - case '\"': - buffer.push_back('\"'); - ++p_; - ++column_; - state_stack_.pop_back(); - break; - case '\\': - buffer.push_back('\\'); - state_stack_.pop_back(); - ++p_; - ++column_; - 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: - 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 = jsonformula_errc::illegal_escaped_character; - return jsonformula_expression(); - } - break; - case path_state::escape_u1: - cp = append_to_codepoint(0, *p_, ec); - if (ec) - { - return jsonformula_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 jsonformula_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 jsonformula_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 jsonformula_expression(); - } - if (jsoncons::unicode_traits::is_high_surrogate(cp)) - { - ++p_; - ++column_; - state_stack_.back() = path_state::escape_expect_surrogate_pair1; - } - else - { - jsoncons::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 = jsonformula_errc::invalid_codepoint; - return jsonformula_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 = jsonformula_errc::invalid_codepoint; - return jsonformula_expression(); - } - break; - case path_state::escape_u5: - cp2 = append_to_codepoint(0, *p_, ec); - if (ec) - { - return jsonformula_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 jsonformula_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 jsonformula_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 jsonformula_expression(); - } - uint32_t codepoint = 0x10000 + ((cp & 0x3FF) << 10) + (cp2 & 0x3FF); - jsoncons::unicode_traits::convert(&codepoint, 1, buffer); - state_stack_.pop_back(); - ++p_; - ++column_; - break; - } - case path_state::raw_string: - switch (*p_) - { - case '\'': - { - // in json-formula, these should be treated as literal strings - // except when they actually mean identifiers - auto stack_grandparent = state_stack_[state_stack_.size()-2]; - - if ( stack_grandparent == path_state::sub_expression ) { - state_stack_.pop_back(); // raw_string - state_stack_.back() = path_state::val_expr; // reset this to a val expression... - } else { - 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 '`': - { - jsoncons::json_decoder decoder; - jsoncons::basic_json_reader> reader(buffer, decoder); - std::error_code parse_ec; - reader.read(parse_ec); - if (parse_ec) - { - ec = jsonformula_errc::invalid_literal; - return jsonformula_expression(); - } - auto j = decoder.get_result(); - - push_token(token(literal_arg, std::move(j)), ec); - if (ec) {return jsonformula_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 = jsonformula_errc::unexpected_end_of_input; - return jsonformula_expression(); - } - ++p_; - ++column_; - break; - default: - buffer.push_back(*p_); - ++p_; - ++column_; - break; - }; - break; - case path_state::number: - switch(*p_) - { - case '-': - case '+': - { - bool digit_follows = false; // assume the worst - auto stack_grandparent = state_stack_[state_stack_.size()-2]; - - if ( buffer[buffer.length()-1]=='e' || buffer[buffer.length()-1]=='E' ) { - // json-formula also supports exponents, and next is a digit - digit_follows = true; - } else if ( stack_grandparent == path_state::index_or_slice_expression || - stack_grandparent == path_state::rhs_slice_expression_step || - stack_grandparent == path_state::rhs_slice_expression_stop ) { - // we are in an index or slice expression - digit_follows = true; - } - - if ( digit_follows ) { - buffer.push_back(*p_); - state_stack_.back() = path_state::digit; - ++p_; - ++column_; - } else { // it's an equation/formula, so pop out of number! - state_stack_.pop_back(); - } - } - break; - - case '.': // don't forget decimal places! - buffer.push_back(*p_); - state_stack_.back() = path_state::digit; - ++p_; - ++column_; - break; - case 'e': // json-formula also supports exponents - case 'E': - buffer.push_back(*p_); - state_stack_.back() = path_state::number; // keep a number since it might be fllowed by +/- - ++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': - case '.': // don't forget decimal places! - 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()), ec); - if (ec) {return jsonformula_expression();} - state_stack_.back() = path_state::expect_rbracket; - ++p_; - ++column_; - break; - case ']': // [] - push_token(token(jsoncons::make_unique()), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); // bracket_specifier - ++p_; - ++column_; - break; - case '?': - push_token(token(begin_filter_arg), ec); - if (ec) {return jsonformula_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 = jsonformula_errc::expected_index_expression; - return jsonformula_expression(); - } - break; - case path_state::bracket_specifier_or_multi_select_list: - switch(*p_) - { - case '*': - if (p_+1 >= end_input_) - { - ec = jsonformula_errc::unexpected_end_of_input; - return jsonformula_expression(); - } - if (*(p_+1) == ']') - { - state_stack_.back() = path_state::bracket_specifier; - } - else - { - push_token(token(begin_multi_select_list_arg), ec); - if (ec) {return jsonformula_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 jsonformula_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 ':': - ec = jsonformula_errc::expected_multi_select_list; - return jsonformula_expression(); - // json-formula allows indices here... - case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': - // need to push the multi-select token on first - // then setup the state stack - push_token(token(begin_multi_select_list_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.back() = path_state::multi_select_list; - state_stack_.emplace_back(path_state::number); - break; - case '*': - push_token(token(jsoncons::make_unique()), ec); - if (ec) {return jsonformula_expression();} - state_stack_.back() = path_state::expect_rbracket; - ++p_; - ++column_; - break; - default: - push_token(token(begin_multi_select_list_arg), ec); - if (ec) {return jsonformula_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; - // json-formula supports empty objects - case '}': - { - state_stack_.pop_back(); - push_token(end_multi_select_hash_arg, ec); - if (ec) {return jsonformula_expression();} - ++p_; - ++column_; - 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()), ec); - if (ec) {return jsonformula_expression();} - } - else - { - int64_t val{ 0 }; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); - if (!r) - { - ec = jsonformula_errc::invalid_number; - return jsonformula_expression(); - } - push_token(token(jsoncons::make_unique(val)), ec); - if (ec) {return jsonformula_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 = jsonformula_errc::invalid_number; - return jsonformula_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; - } - case ',': - { - // json-formula - // if we landed here, then we MAY have found a raw array of numbers - // it is also possible that it is a bogus array index... - // we check which, based on the output_stack - // if it's a raw array, reset the stack to what it should be... - // Push on the multi-select token - // Replace the current state (index or slice) with multi-select list - // otherwise, leave it alone! - if ( output_stack_[output_stack_.size()-1].type() == token_kind::expression ) { - // nothing to do... - } else { - push_token(token(begin_multi_select_list_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.back() = path_state::multi_select_list; - } - - // check if there is anything in the buffer... (there better be!) - // which will push a literal onto the token stack - _checkBuffer(buffer); - - // now push on the seperator - // and push lhs_expression onto the state stack - push_token(token(separator_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.emplace_back(path_state::lhs_expression); - ++p_; - ++column_; - break; - } - default: - ec = jsonformula_errc::expected_rbracket; - return jsonformula_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 = jsonformula_errc::invalid_number; - return jsonformula_expression(); - } - slic.stop_ = jsoncons::optional(val); - buffer.clear(); - } - switch(*p_) - { - case ']': - push_token(token(jsoncons::make_unique(slic)), ec); - if (ec) {return jsonformula_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 = jsonformula_errc::expected_rbracket; - return jsonformula_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 = jsonformula_errc::invalid_number; - return jsonformula_expression(); - } - if (val == 0) - { - ec = jsonformula_errc::step_cannot_be_zero; - return jsonformula_expression(); - } - slic.step_ = val; - buffer.clear(); - } - switch(*p_) - { - case ']': - push_token(token(jsoncons::make_unique(slic)), ec); - if (ec) {return jsonformula_expression();} - buffer.clear(); - slic = slice{}; - state_stack_.pop_back(); // rhs_slice_expression_step - ++p_; - ++column_; - break; - default: - ec = jsonformula_errc::expected_rbracket; - return jsonformula_expression(); - } - break; - } - case path_state::expect_rbracket: - { - switch(*p_) - { - case ']': - state_stack_.pop_back(); // expect_rbracket - ++p_; - ++column_; - break; - default: - ec = jsonformula_errc::expected_rbracket; - return jsonformula_expression(); - } - break; - } - case path_state::expect_rparen: - switch (*p_) - { - case ' ':case '\t':case '\r':case '\n': - advance_past_space_character(ec, buffer); - break; - case ')': - ++p_; - ++column_; - push_token(rparen_arg, ec); - if (ec) {return jsonformula_expression();} - state_stack_.back() = path_state::rhs_expression; - break; - default: - ec = jsonformula_errc::expected_rparen; - return jsonformula_expression(); - } - break; - case path_state::key_val_expr: - { - switch (*p_) - { - case ' ':case '\t':case '\r':case '\n': - advance_past_space_character(ec, buffer); - 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::key_expr); // json-formula support these as keys too - 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 = jsonformula_errc::expected_key; - return jsonformula_expression(); - } - break; - }; - break; - } - case path_state::cmp_lt_or_lte: - { - // json-formula - // check if there is anything in the buffer... - // if so, we need to process it - _checkBuffer(buffer); - - switch(*p_) - { - case '=': - push_token(token(resources_.get_lte_operator()), ec); - push_token(token(current_node_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - ++p_; - ++column_; - break; - // json-formula supports <> for not equal - case '>': - push_token(token(resources_.get_ne_operator()), ec); - push_token(token(current_node_arg), ec); - if (ec) {return jsonformula_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 jsonformula_expression();} - state_stack_.pop_back(); - break; - } - break; - } - case path_state::cmp_gt_or_gte: - { - // json-formula - // check if there is anything in the buffer... - // if so, we need to process it - _checkBuffer(buffer); - - switch(*p_) - { - case '=': - push_token(token(resources_.get_gte_operator()), ec); - push_token(token(current_node_arg), ec); - if (ec) {return jsonformula_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 jsonformula_expression();} - state_stack_.pop_back(); - break; - } - break; - } - case path_state::cmp_eq: - { - // json-formula - // check if there is anything in the buffer... - // if so, we need to process it - _checkBuffer(buffer); - - // json-formula supports both `=` and `==` - push_token(token(resources_.get_eq_operator()), ec); - push_token(token(current_node_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - if ( *p_ == '=' ) { // be careful not to move past something else! - ++p_; - ++column_; - } - break; - } - case path_state::cmp_ne: - { - // json-formula - // check if there is anything in the buffer... - // if so, we need to process it - _checkBuffer(buffer); - - switch(*p_) - { - case '=': - push_token(token(resources_.get_ne_operator()), ec); - push_token(token(current_node_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - ++p_; - ++column_; - break; - default: - ec = jsonformula_errc::expected_comparator; - return jsonformula_expression(); - } - break; - } - case path_state::jf_expression: - { - // json-formula - // check if there is anything in the buffer... - // if so, we need to process it - _checkBuffer(buffer); - - switch(*p_) - { - case ' ':case '\t':case '\r':case '\n': - advance_past_space_character(ec, buffer); - break; - case '+': - ++p_; - ++column_; - state_stack_.back() = path_state::lhs_expression; - state_stack_.emplace_back(path_state::plus_operator); - break; - case '-': - ++p_; - ++column_; - state_stack_.back() = path_state::lhs_expression; - state_stack_.emplace_back(path_state::minus_operator); - break; - case '*': - ++p_; - ++column_; - state_stack_.back() = path_state::lhs_expression; - state_stack_.emplace_back(path_state::mult_operator); - break; - case '/': - ++p_; - ++column_; - state_stack_.back() = path_state::lhs_expression; - state_stack_.emplace_back(path_state::div_operator); - break; - case '~': - ++p_; - ++column_; - state_stack_.back() = path_state::lhs_expression; - state_stack_.emplace_back(path_state::union_operator); - break; - default: - if (state_stack_.size() > 1) - { - state_stack_.pop_back(); - } - else - { - ec = jsonformula_errc::syntax_error; - return jsonformula_expression(); - } - break; - } - break; - } - case path_state::plus_operator: - { - push_token(token(resources_.get_plus_operator()), ec); - push_token(token(current_node_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - break; - } - case path_state::minus_operator: - { - push_token(token(resources_.get_minus_operator()), ec); - push_token(token(current_node_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - break; - } - case path_state::mult_operator: - { - push_token(token(resources_.get_mult_operator()), ec); - push_token(token(current_node_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - break; - } - case path_state::div_operator: - { - push_token(token(resources_.get_div_operator()), ec); - push_token(token(current_node_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - break; - } - case path_state::concat_operator: - { - push_token(token(resources_.get_concat_operator()), ec); - push_token(token(current_node_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - break; - } - case path_state::union_operator: - { - push_token(token(resources_.get_union_operator()), ec); - push_token(token(current_node_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - break; - } - case path_state::expect_dot: - { - switch(*p_) - { - case ' ':case '\t':case '\r':case '\n': - advance_past_space_character(ec, buffer); - break; - case '.': - state_stack_.pop_back(); // expect_dot - ++p_; - ++column_; - break; - default: - ec = jsonformula_errc::expected_dot; - return jsonformula_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 jsonformula_expression();} - state_stack_.pop_back(); - ++p_; - ++column_; - break; - default: - push_token(token(pipe_arg), ec); - if (ec) {return jsonformula_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 jsonformula_expression();} - state_stack_.pop_back(); // expect_and - ++p_; - ++column_; - break; - default: - // for json-formula, this is the concat operator -// ec = jsonformula_errc::expected_and; -// return jsonformula_expression(); - push_token(token(resources_.get_concat_operator()), ec); - push_token(token(current_node_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); // expect_and - ++p_; - ++column_; - break; - } - break; - } - case path_state::multi_select_list: - { - switch(*p_) - { - case ' ':case '\t':case '\r':case '\n': - advance_past_space_character(ec, buffer); - break; - case ',': - // json-formula - // check if there is anything in the buffer...(better be) - _checkBuffer(buffer); - - push_token(token(separator_arg), ec); - if (ec) {return jsonformula_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 ']': - { - // json-formula - // check if there is anything in the buffer... - // if so, we need to process it - _checkBuffer(buffer); - - push_token(token(end_multi_select_list_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - - ++p_; - ++column_; - break; - } - default: - ec = jsonformula_errc::expected_rbracket; - return jsonformula_expression(); - } - break; - } - case path_state::filter: - { - switch(*p_) - { - case ' ':case '\t':case '\r':case '\n': - advance_past_space_character(ec, buffer); - break; - case ']': - { - push_token(token(end_filter_arg), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - ++p_; - ++column_; - break; - } - default: - ec = jsonformula_errc::expected_rbracket; - return jsonformula_expression(); - } - break; - } - case path_state::expect_rbrace: - { - // json-formula - // check if there is anything in the buffer... - // if so, we need to process it - _checkBuffer(buffer); - - switch(*p_) - { - case ' ':case '\t':case '\r':case '\n': - advance_past_space_character(ec, buffer); - break; - case ',': - push_token(token(separator_arg), ec); - if (ec) {return jsonformula_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 jsonformula_expression();} - ++p_; - ++column_; - break; - } - // json-formula supports functions - case '+': - case '-': - case '*': - case '/': - state_stack_.emplace_back(path_state::jf_expression); - break; - default: - if ( debug_ ) { std::cout << "rbrace: " << *p_ << std::endl; } - - ec = jsonformula_errc::expected_rbrace; - return jsonformula_expression(); - } - break; - } - case path_state::expect_colon: - { - switch(*p_) - { - case ' ':case '\t':case '\r':case '\n': - advance_past_space_character(ec, buffer); - break; - case ':': - state_stack_.back() = path_state::expect_rbrace; - state_stack_.emplace_back(path_state::lhs_expression); - ++p_; - ++column_; - break; - default: - ec = jsonformula_errc::expected_colon; - return jsonformula_expression(); - } - break; - } - } - - } - - if (state_stack_.empty()) - { - ec = jsonformula_errc::syntax_error; - return jsonformula_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 = jsonformula_errc::syntax_error; - return jsonformula_expression(); - } - break; - case path_state::number: // in json-formula, we could end with a number - case path_state::digit: // in json-formula, we could end with a digit - { - jsoncons::json_decoder decoder; - jsoncons::basic_json_reader> reader(buffer, decoder); - std::error_code parse_ec; - reader.read(parse_ec); - if (parse_ec) - { - ec = jsonformula_errc::invalid_literal; - return jsonformula_expression(); - } - auto j = decoder.get_result(); - push_token(token(literal_arg, std::move(j)), ec); - if (ec) {return jsonformula_expression();} - buffer.clear(); - state_stack_.pop_back(); - } - break; - case path_state::val_expr: - push_token(token(jsoncons::make_unique(buffer)), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - break; - case path_state::identifier_or_function_expr: - push_token(token(jsoncons::make_unique(buffer)), ec); - if (ec) {return jsonformula_expression();} - state_stack_.pop_back(); - break; - case path_state::quoted_string: // specific error for unbalanced quotes in json-formula - case path_state::raw_string: - ec = jsonformula_errc::unbalanced_quotes; - return jsonformula_expression(); - break; - case path_state::unquoted_string: - state_stack_.pop_back(); - break; - default: - ec = jsonformula_errc::syntax_error; - return jsonformula_expression(); - break; - } - } - - if (!(state_stack_.size() == 1 && state_stack_.back() == path_state::rhs_expression)) - { - ec = jsonformula_errc::unexpected_end_of_input; - return jsonformula_expression(); - } - - state_stack_.pop_back(); - - push_token(end_of_expression_arg, ec); - if (ec) {return jsonformula_expression();} - - // debug the output stack... - if ( debug_ ) { - for (auto& t : output_stack_) - { - std::cout << t.to_string() << std::endl; - } - } - - return jsonformula_expression(std::move(resources_), std::move(output_stack_)); - } - - void advance_past_space_character(std::error_code& ec, string_type& buffer) - { - // json-formula - // check if there is anything in the buffer... - // if so, we need to process it - if (!buffer.empty()) { - jsoncons::json_decoder decoder; - jsoncons::basic_json_reader> reader(buffer, decoder); - std::error_code parse_ec; - reader.read(parse_ec); - if (parse_ec) { - // we had some problem with the literal, so let's force it to number & back - auto bufD = std::stod(buffer); - buffer = std::to_string(bufD); - return; // will cause it to run again with the new buffer... - } - auto j = decoder.get_result(); - push_token(token(literal_arg, std::move(j)), ec); - buffer.clear(); - } - - switch (*p_) - { - case ' ':case '\t': - ++p_; - ++column_; - break; - case '\r': - if (p_+1 >= end_input_) - { - ec = jsonformula_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 = jsonformula_errc::unbalanced_parentheses; - return; - } - ++it; - operator_stack_.erase(it.base(),operator_stack_.end()); - } - - void push_token(token&& tok, std::error_code& ec) - { - // debug the output stack... - if ( debug_ ) { - std::cout << "pre-push" << std::endl; - for (auto& t : output_stack_) - { - std::cout << t.to_string() << std::endl; - } - } - - switch (tok.type()) - { - case token_kind::end_filter: - { - unwind_rparen(ec); - std::vector 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 = jsonformula_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(std::move(toks))); - } - else - { - output_stack_.emplace_back(token(jsoncons::make_unique(std::move(toks)))); - } - break; - } - case token_kind::end_multi_select_list: - { - unwind_rparen(ec); - std::vector> vals; - auto it = output_stack_.rbegin(); - while (it != output_stack_.rend() && it->type() != token_kind::begin_multi_select_list) - { - std::vector 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 = jsonformula_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(std::move(vals))); - } - else - { - output_stack_.emplace_back(token(jsoncons::make_unique(std::move(vals)))); - } - break; - } - case token_kind::end_multi_select_hash: - { - unwind_rparen(ec); - std::vector key_toks; - auto it = output_stack_.rbegin(); - while (it != output_stack_.rend() && it->type() != token_kind::begin_multi_select_hash) - { - std::vector 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 = jsonformula_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(std::move(key_toks))); - } - else - { - output_stack_.emplace_back(token(jsoncons::make_unique(std::move(key_toks)))); - } - break; - } - case token_kind::end_expression_type: - { - std::vector 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(jsoncons::json_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(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 toks; - auto it = output_stack_.rbegin(), prev=output_stack_.rbegin(); - std::size_t arg_count = 0, tok_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)); - tok_count++; - - // json formula supports operators as params - // swap it with the argument! - if (it->type() == token_kind::argument && - prev->type() == token_kind::binary_operator) { - std::swap(toks[tok_count-1], toks[tok_count-2]); - } - - prev=it; - ++it; - } - - // debug the toks... - if ( debug_ ) { - std::cout << "toks-1" << std::endl; - for (auto& t : toks) - { - std::cout << t.to_string() << std::endl; - } - } - - if (it == output_stack_.rend()) - { - ec = jsonformula_errc::unbalanced_parentheses; - return; - } - if (it->arity() && arg_count != *(it->arity())) - { - ec = jsonformula_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()); - - // debug the toks... - if ( debug_ ) { - std::cout << "toks-2" << std::endl; - for (auto& t : toks) - { - std::cout << t.to_string() << std::endl; - } - } - - 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(std::move(toks))); - } - else - { - output_stack_.emplace_back(token(jsoncons::make_unique(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; - } - - // debug the output stack... - if ( debug_ ) { - std::cout << "post-push" << std::endl; - for (auto& t : output_stack_) - { - std::cout << t.to_string() << std::endl; - } - } - } - - 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 = jsonformula_errc::invalid_codepoint; - } - return cp; - } - }; + #include "json-formula-resources.hpp" + #include "json-formula-evaluator.hpp" } // detail