From 190ab8d0f2dd7098b1be47bf8cbd86ba274c7901 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Wed, 16 Apr 2025 15:42:11 -0700 Subject: [PATCH 01/11] first part of extracting jsonlogic --- .DS_Store | Bin 0 -> 6148 bytes CMakeLists.txt | 29 +- examples/logic/testeval.cpp | 133 +- examples/oo-howdy/howdy-eval.cpp | 82 +- include/clippy/attic/clippy-ast.hpp | 1843 ++++++++++++++++ include/clippy/{ => attic}/clippy-eval.hpp | 350 +-- include/clippy/clippy-ast.hpp | 2300 -------------------- include/experimental/json-io.hpp | 215 +- test/CMakeLists.txt | 9 +- test/TestBag/remove_if.cpp | 7 +- test/TestGraph/dump.cpp | 6 +- test/TestGraph/for_all_edges.cpp | 9 +- test/TestGraph/where.cpp | 16 +- test/TestSet/remove_if.cpp | 4 +- test/include/wheredev.cpp | 2 +- 15 files changed, 2252 insertions(+), 2753 deletions(-) create mode 100644 .DS_Store create mode 100644 include/clippy/attic/clippy-ast.hpp rename include/clippy/{ => attic}/clippy-eval.hpp (90%) delete mode 100644 include/clippy/clippy-ast.hpp diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a2eb533abf551d042629249e6fe5fb378b3b5ee0 GIT binary patch literal 6148 zcmeH~u?oUK42Bc!Ah>jNyu}Cb4Gz&K=nFU~E>c0O^F6wMazU^xC+1b{XuyJ79K1T}(S!u1*@b}wNMJ-@TJzTK|1JE}{6A`8N&+PC zX9Tp_belC^D(=>|*R%RAs -#include - +#include #include +#include +#include #include "clippy/clippy-eval.hpp" -#include - namespace bjsn = boost::json; -bjsn::value -parseStream(std::istream& inps) -{ +bjsn::value parseStream(std::istream& inps) { bjsn::stream_parser p; - std::string line; + std::string line; // \todo skips ws in strings - while (inps >> line) - { + while (inps >> line) { bjsn::error_code ec; p.write(line.c_str(), line.size(), ec); @@ -34,18 +29,15 @@ parseStream(std::istream& inps) return p.release(); } -bjsn::value -parseFile(const std::string& filename) -{ +bjsn::value parseFile(const std::string& filename) { std::ifstream is{filename}; return parseStream(is); } - template -bool matchOpt1(const std::vector& args, N& pos, std::string opt, T& fld) -{ +bool matchOpt1(const std::vector& args, N& pos, std::string opt, + T& fld) { std::string arg(args.at(pos)); if (arg.find(opt) != 0) return false; @@ -57,8 +49,8 @@ bool matchOpt1(const std::vector& args, N& pos, std::string opt, T& } template -bool matchOpt1(const std::vector& args, N& pos, std::string opt, Fn fn) -{ +bool matchOpt1(const std::vector& args, N& pos, std::string opt, + Fn fn) { std::string arg(args.at(pos)); if (arg.find(opt) != 0) return false; @@ -70,8 +62,8 @@ bool matchOpt1(const std::vector& args, N& pos, std::string opt, Fn } template -bool matchOpt0(const std::vector& args, N& pos, std::string opt, Fn fn) -{ +bool matchOpt0(const std::vector& args, N& pos, std::string opt, + Fn fn) { std::string arg(args.at(pos)); if (arg.find(opt) != 0) return false; @@ -82,40 +74,34 @@ bool matchOpt0(const std::vector& args, N& pos, std::string opt, Fn } template -bool noSwitch0(const std::vector& args, N& pos, Fn /*fn*/) -{ +bool noSwitch0(const std::vector& args, N& pos, Fn /*fn*/) { //~ if (fn(args[pos])) - //~ return true; + //~ return true; std::cerr << "unrecognized argument: " << args[pos] << std::endl; ++pos; return false; } -bool endsWith(const std::string& str, const std::string& suffix) -{ - return ( str.size() >= suffix.size() - && std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()) - ); +bool endsWith(const std::string& str, const std::string& suffix) { + return (str.size() >= suffix.size() && + std::equal(suffix.rbegin(), suffix.rend(), str.rbegin())); } - -int main(int argc, const char** argv) -{ +int main(int argc, const char** argv) { constexpr bool MATCH = false; - bool verbose = false; + bool verbose = false; bool genExpected = false; - int errorCode = 0; - std::vector arguments(argv, argv+argc); - std::string filename; - size_t argn = 1; + int errorCode = 0; + std::vector arguments(argv, argv + argc); + std::string filename; + size_t argn = 1; - auto setVerbose = [&verbose]() -> void { verbose = true; }; - auto setResult = [&genExpected]() -> void { genExpected = true; }; - auto setFile = [&filename](const std::string& name) -> bool - { + auto setVerbose = [&verbose]() -> void { verbose = true; }; + auto setResult = [&genExpected]() -> void { genExpected = true; }; + auto setFile = [&filename](const std::string& name) -> bool { const bool jsonFile = endsWith(name, ".json"); if (jsonFile) filename = name; @@ -123,39 +109,34 @@ int main(int argc, const char** argv) return jsonFile; }; - while (argn < arguments.size()) - { + while (argn < arguments.size()) { MATCH - || matchOpt0(arguments, argn, "-v", setVerbose) - || matchOpt0(arguments, argn, "--verbose", setVerbose) - || matchOpt0(arguments, argn, "-r", setResult) - || matchOpt0(arguments, argn, "--result", setResult) - || noSwitch0(arguments, argn, setFile) - ; + || matchOpt0(arguments, argn, "-v", setVerbose) || + matchOpt0(arguments, argn, "--verbose", setVerbose) || + matchOpt0(arguments, argn, "-r", setResult) || + matchOpt0(arguments, argn, "--result", setResult) || + noSwitch0(arguments, argn, setFile); } - bjsn::value all = parseStream(std::cin); - bjsn::object& allobj = all.as_object(); + bjsn::value all = parseStream(std::cin); + bjsn::object& allobj = all.as_object(); - bjsn::value rule = allobj["rule"]; - const bool hasData = allobj.contains("data"); - bjsn::value dat; - const bool hasExpected = allobj.contains("expected"); + bjsn::value rule = allobj["rule"]; + const bool hasData = allobj.contains("data"); + bjsn::value dat; + const bool hasExpected = allobj.contains("expected"); if (hasData) dat = allobj["data"]; else dat.emplace_object(); - try - { - json_logic::ValueExpr res = json_logic::apply(rule, dat); + try { + jsonlogic::any_expr res = jsonlogic::apply(rule, dat); - if (verbose) - std::cerr << res << std::endl; + if (verbose) std::cerr << res << std::endl; - if (genExpected) - { + if (genExpected) { std::stringstream resStream; resStream << res; @@ -163,9 +144,7 @@ int main(int argc, const char** argv) allobj["expected"] = parseStream(resStream); if (verbose) std::cerr << allobj["expected"] << std::endl; - } - else if (hasExpected) - { + } else if (hasExpected) { std::stringstream expStream; std::stringstream resStream; @@ -176,37 +155,27 @@ int main(int argc, const char** argv) if (verbose && errorCode) std::cerr << "test failed: " << "\n exp: " << expStream.str() - << "\n got: " << resStream.str() - << std::endl; - } - else - { + << "\n got: " << resStream.str() << std::endl; + } else { errorCode = 1; if (verbose) std::cerr << "unexpected completion, result: " << res << std::endl; } - } - catch (const std::exception& ex) - { - if (verbose) - std::cerr << "caught error: " << ex.what() << std::endl; + } catch (const std::exception& ex) { + if (verbose) std::cerr << "caught error: " << ex.what() << std::endl; if (genExpected) allobj.erase("expected"); else if (hasExpected) errorCode = 1; - } - catch (...) - { - if (verbose) - std::cerr << "caught unknown error" << std::endl; + } catch (...) { + if (verbose) std::cerr << "caught unknown error" << std::endl; errorCode = 1; } - if (genExpected && (errorCode == 0)) - std::cout << allobj << std::endl; + if (genExpected && (errorCode == 0)) std::cout << allobj << std::endl; if (verbose && errorCode) std::cerr << "errorCode: " << errorCode << std::endl; diff --git a/examples/oo-howdy/howdy-eval.cpp b/examples/oo-howdy/howdy-eval.cpp index b4f9906..bcef944 100644 --- a/examples/oo-howdy/howdy-eval.cpp +++ b/examples/oo-howdy/howdy-eval.cpp @@ -1,92 +1,80 @@ -#include +#include #include +#include -#include -#include "clippy/clippy.hpp" #include "clippy/clippy-eval.hpp" +#include "clippy/clippy.hpp" namespace boostjsn = boost::json; -namespace jl = json_logic; +namespace jl = json_logic; static const std::string className = "Greeter"; static const std::string methodName = "eval"; static const std::string expr = "expressions"; -static const std::string selLetters = "letters"; -static const std::string selFieldAll = "all"; -static const std::string selFieldVowels = "vowels"; -static const std::string selFieldConsonants = "consonants"; +static const std::string selLetters = "letters"; +static const std::string selFieldAll = "all"; +static const std::string selFieldVowels = "vowels"; +static const std::string selFieldConsonants = "consonants"; static const std::string stGreeting = "greeting"; -static const std::string stGreeted = "greeted"; +static const std::string stGreeted = "greeted"; static const std::string stSelected = "selected"; - -int main(int argc, char** argv) -{ - int error_code = 0; +int main(int argc, char** argv) { + int error_code = 0; clippy::clippy clip{methodName, "Eval example"}; clip.member_of(className, "Customizable Greeting Generator"); - if (clip.parse(argc, argv)) { return 0; } + if (clip.parse(argc, argv)) { + return 0; + } - try - { + try { using JsonExpression = std::vector; - std::string greeting = clip.get_state(stGreeting); - std::string greeted = clip.get_state(stGreeted); + std::string greeting = clip.get_state(stGreeting); + std::string greeted = clip.get_state(stGreeted); JsonExpression jsonExpression = clip.get_state(stSelected); - std::string chars = greeting + " " + greeted; + std::string chars = greeting + " " + greeted; - for (boost::json::object& jexp : jsonExpression) - { - auto [ast, vars, hasComputed] = json_logic::translateNode(jexp["rule"]); + for (boost::json::object& jexp : jsonExpression) { + auto [ast, vars, hasComputed] = jsonlogic::translateNode(jexp["rule"]); std::string tmp; tmp.swap(chars); - for (char ch : tmp) - { + for (char ch : tmp) { boostjsn::string elem(1, ch); - jl::ValueExpr val = jl::calculate( ast, - [&elem](const boostjsn::value&, int) -> jl::ValueExpr - { - return jl::toValueExpr(elem); - } - ); - if (jl::unpackValue(val)) - chars += ch; + jl::any_expr val = jl::calculate( + ast, [&elem](const boostjsn::value&, int) -> jl::any_expr { + return jl::toany_expr(elem); + }); + if (jl::unpack_value(val)) chars += ch; } } - clippy::object res; res.set_val("result", chars); -/* - auto [ast, vars, hasComputed] = json_logic::translateNode(val); + /* + auto [ast, vars, hasComputed] = jsonlogic::translateNode(val); - std::cerr << "Ast has " << vars.size() << " free variables" - << (hasComputed ? ", and variables with computed names" : "") - << "." - << std::endl; + std::cerr << "Ast has " << vars.size() << " free variables" + << (hasComputed ? ", and variables with computed names" : "") + << "." + << std::endl; - json_logic::ValueExpr res = json_logic::calculate(*ast.get()); -*/ + jsonlogic::any_expr res = jsonlogic::calculate(*ast.get()); + */ clip.to_return(chars); - } - catch (const std::exception& err) - { + } catch (const std::exception& err) { clip.to_return(err.what()); error_code = 1; } return error_code; } - - - diff --git a/include/clippy/attic/clippy-ast.hpp b/include/clippy/attic/clippy-ast.hpp new file mode 100644 index 0000000..c4af6b8 --- /dev/null +++ b/include/clippy/attic/clippy-ast.hpp @@ -0,0 +1,1843 @@ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace json_logic { +constexpr bool DEBUG_OUTPUT = true; + +namespace json = boost::json; + +using JsonExpr = json::value; + +template +T& as(T& n) { + return n; +} + +template +T& deref(T* p) { + assert(p); + return *p; +} + +struct Visitor; + +// the root class +struct Expr { + Expr() = default; + virtual ~Expr() = default; + + virtual void accept(Visitor&) = 0; + + private: + Expr(Expr&&) = delete; + Expr(const Expr&) = delete; + Expr& operator=(Expr&&) = delete; + Expr& operator=(const Expr&) = delete; +}; + +//~ Expr::~Expr() {} + +// +// foundation classes +// \{ + +using any_expr = std::unique_ptr; + +struct Operator : Expr, private std::vector { + using container_type = std::vector; + + using container_type::back; + using container_type::begin; + using container_type::const_iterator; + using container_type::const_reverse_iterator; + using container_type::crbegin; + using container_type::crend; + using container_type::end; + using container_type::iterator; + using container_type::push_back; + using container_type::rbegin; + using container_type::rend; + using container_type::reverse_iterator; + + void set_operands(container_type&& opers) { this->swap(opers); } + + Expr& operand(int n) const { return deref(this->at(n).get()); } + + virtual int num_evaluated_operands() const; +}; + +// defines operators that have an upper bound on how many +// arguments are evaluated. +template +struct OperatorN : Operator { + enum { MAX_OPERANDS = MaxArity }; + + int num_evaluated_operands() const final; +}; + +template +struct Value : Expr { + using value_type = T; + + explicit Value(T t) : val(std::move(t)) {} + + T& value() { return val; } + const T& value() const { return val; } + + private: + T val; +}; + +// \} + +// +// expression hierarchy +// comparison + +// binary +struct Eq : OperatorN<2> { + void accept(Visitor&) final; +}; + +struct StrictEq : OperatorN<2> { + void accept(Visitor&) final; +}; + +struct Neq : OperatorN<2> { + void accept(Visitor&) final; +}; + +struct StrictNeq : OperatorN<2> { + void accept(Visitor&) final; +}; + +// binary or ternary +struct Less : OperatorN<3> { + void accept(Visitor&) final; +}; + +struct Greater : OperatorN<3> { + void accept(Visitor&) final; +}; + +struct Leq : OperatorN<3> { + void accept(Visitor&) final; +}; + +struct Geq : OperatorN<3> { + void accept(Visitor&) final; +}; + +// logical operators + +// unary +struct Not : OperatorN<1> { + void accept(Visitor&) final; +}; + +struct NotNot : OperatorN<1> { + void accept(Visitor&) final; +}; + +// n-ary +struct And : Operator { + void accept(Visitor&) final; +}; + +struct Or : Operator { + void accept(Visitor&) final; +}; + +// control structure +struct If : Operator { + void accept(Visitor&) final; +}; + +// arithmetics +// n-ary +struct Add : Operator { + void accept(Visitor&) final; +}; + +struct Mul : Operator { + void accept(Visitor&) final; +}; + +struct Min : Operator { + void accept(Visitor&) final; +}; + +struct Max : Operator { + void accept(Visitor&) final; +}; + +// binary +struct Sub : OperatorN<2> { + void accept(Visitor&) final; +}; + +struct Div : OperatorN<2> { + void accept(Visitor&) final; +}; + +struct Mod : OperatorN<2> { + void accept(Visitor&) final; +}; + +// array +struct Array : Operator // array is modeled as operator +{ + void accept(Visitor&) final; +}; + +struct Map : OperatorN<2> { + void accept(Visitor&) final; +}; + +struct Reduce : OperatorN<3> { + void accept(Visitor&) final; +}; + +struct Filter : OperatorN<2> { + void accept(Visitor&) final; +}; + +struct All : OperatorN<2> { + void accept(Visitor&) final; +}; + +struct None : OperatorN<2> { + void accept(Visitor&) final; +}; + +struct Some : OperatorN<2> { + void accept(Visitor&) final; +}; + +struct Merge : Operator { + void accept(Visitor&) final; +}; + +// string operations +struct Var : OperatorN<1> { + enum { computed = -1 }; + + void accept(Visitor&) final; + + void num(int val) { idx = val; } + int num() const { return idx; } + + private: + int idx = computed; +}; + +struct Cat : Operator { + void accept(Visitor&) final; +}; + +struct Substr : OperatorN<3> { + void accept(Visitor&) final; +}; + +// string and array operation +struct In : Operator { + void accept(Visitor&) final; +}; + +// values +struct NullVal : Expr { + void accept(Visitor&) final; + + std::nullptr_t value() const { return nullptr; } +}; + +struct BoolVal : Value { + using base = Value; + using base::base; + + void accept(Visitor&) final; +}; + +struct IntVal : Value { + using base = Value; + using base::base; + + void accept(Visitor&) final; +}; + +struct UintVal : Value { + using base = Value; + using base::base; + + void accept(Visitor&) final; +}; + +struct DoubleVal : Value { + using base = Value; + using base::base; + + void accept(Visitor&) final; +}; + +struct StringVal : Value { + using base = Value; + using base::base; + + void accept(Visitor&) final; +}; + +// logger +struct Log : OperatorN<1> { + void accept(Visitor&) final; +}; + +// error node +struct Error : Expr { + void accept(Visitor&) final; +}; + +// Visitor +struct Visitor { + virtual void visit(Expr&) = 0; // error + virtual void visit(Operator& n) = 0; + virtual void visit(Eq&) = 0; + virtual void visit(StrictEq&) = 0; + virtual void visit(Neq&) = 0; + virtual void visit(StrictNeq&) = 0; + virtual void visit(Less&) = 0; + virtual void visit(Greater&) = 0; + virtual void visit(Leq&) = 0; + virtual void visit(Geq&) = 0; + virtual void visit(And&) = 0; + virtual void visit(Or&) = 0; + virtual void visit(Not&) = 0; + virtual void visit(NotNot&) = 0; + virtual void visit(Add&) = 0; + virtual void visit(Sub&) = 0; + virtual void visit(Mul&) = 0; + virtual void visit(Div&) = 0; + virtual void visit(Mod&) = 0; + virtual void visit(Min&) = 0; + virtual void visit(Max&) = 0; + virtual void visit(Map&) = 0; + virtual void visit(Reduce&) = 0; + virtual void visit(Filter&) = 0; + virtual void visit(All&) = 0; + virtual void visit(None&) = 0; + virtual void visit(Some&) = 0; + virtual void visit(Array&) = 0; + virtual void visit(Merge&) = 0; + virtual void visit(Cat&) = 0; + virtual void visit(Substr&) = 0; + virtual void visit(In&) = 0; + virtual void visit(Var&) = 0; + virtual void visit(Log&) = 0; + + // control structure + virtual void visit(If&) = 0; + + // values + virtual void visit(NullVal&) = 0; + virtual void visit(BoolVal&) = 0; + virtual void visit(IntVal&) = 0; + virtual void visit(UintVal&) = 0; + virtual void visit(DoubleVal&) = 0; + virtual void visit(StringVal&) = 0; + //~ virtual void visit(ObjectVal&) = 0; + + virtual void visit(Error&) = 0; +}; + +struct FwdVisitor : Visitor { + void visit(Expr&) override; + void visit(Operator& n) override { visit(as(n)); } + void visit(Eq& n) override { visit(as(n)); } + void visit(StrictEq& n) override { visit(as(n)); } + void visit(Neq& n) override { visit(as(n)); } + void visit(StrictNeq& n) override { visit(as(n)); } + void visit(Less& n) override { visit(as(n)); } + void visit(Greater& n) override { visit(as(n)); } + void visit(Leq& n) override { visit(as(n)); } + void visit(Geq& n) override { visit(as(n)); } + void visit(And& n) override { visit(as(n)); } + void visit(Or& n) override { visit(as(n)); } + void visit(Not& n) override { visit(as(n)); } + void visit(NotNot& n) override { visit(as(n)); } + void visit(Add& n) override { visit(as(n)); } + void visit(Sub& n) override { visit(as(n)); } + void visit(Mul& n) override { visit(as(n)); } + void visit(Div& n) override { visit(as(n)); } + void visit(Mod& n) override { visit(as(n)); } + void visit(Min& n) override { visit(as(n)); } + void visit(Max& n) override { visit(as(n)); } + void visit(Map& n) override { visit(as(n)); } + void visit(Reduce& n) override { visit(as(n)); } + void visit(Filter& n) override { visit(as(n)); } + void visit(All& n) override { visit(as(n)); } + void visit(None& n) override { visit(as(n)); } + void visit(Some& n) override { visit(as(n)); } + void visit(Array& n) override { visit(as(n)); } + void visit(Merge& n) override { visit(as(n)); } + void visit(Cat& n) override { visit(as(n)); } + void visit(Substr& n) override { visit(as(n)); } + void visit(In& n) override { visit(as(n)); } + void visit(Var& n) override { visit(as(n)); } + void visit(Log& n) override { visit(as(n)); } + + void visit(If& n) override { visit(as(n)); } + + void visit(NullVal& n) override { visit(as(n)); } + void visit(BoolVal& n) override { visit(as(n)); } + void visit(IntVal& n) override { visit(as(n)); } + void visit(UintVal& n) override { visit(as(n)); } + void visit(DoubleVal& n) override { visit(as(n)); } + void visit(StringVal& n) override { visit(as(n)); } + + void visit(Error& n) override { visit(as(n)); } +}; + +/// AST traversal function that calls v's visit methods in post-fix order +void traverseInSAttributeOrder(Expr& e, Visitor& vis); + +/// traverses the children of a node; does not traverse grandchildren +void traverseChildren(Visitor& v, const Operator& node); +void traverseAllChildren(Visitor& v, const Operator& node); +void traverseChildrenReverse(Visitor& v, const Operator& node); + +namespace { +CXX_NORETURN +void unsupported() { throw std::logic_error("not yet implemented"); } + +CXX_NORETURN +void typeError() { throw std::runtime_error("typing error"); } + +struct VarMap { + void insert(Var& el); + std::vector toVector() const; + bool hasComputedVariables() const { return hasComputed; } + + private: + using container_type = std::map; + + container_type mapping = {}; + bool hasComputed = false; +}; + +void VarMap::insert(Var& var) { + any_expr& arg = var.back(); + StringVal* str = dynamic_cast(arg.get()); + + if (str == nullptr) { + CXX_UNLIKELY; + hasComputed = true; + return; + } + + // do nothing for free variables in "lambdas" + if (str->value() == "") return; + + auto [pos, success] = mapping.emplace(str->value(), mapping.size()); + + var.num(pos->second); +} + +std::vector VarMap::toVector() const { + std::vector res; + + res.resize(mapping.size()); + + for (const container_type::value_type& el : mapping) + res.at(el.second) = el.first; + + return res; +} + +/// translates all children +/// \{ +Operator::container_type translateChildren(json::array& children, VarMap&); + +Operator::container_type translateChildren(JsonExpr& n, VarMap&); +/// \} + +template +Expr& mkOperator(json::object& n, VarMap& m) { + assert(n.size() == 1); + + ExprT& res = deref(new ExprT); + + res.set_operands(translateChildren(n.begin()->value(), m)); + return res; +} + +Array& mkArrayOperator(json::array& children, VarMap& m) { + Array& res = deref(new Array); + + res.set_operands(translateChildren(children, m)); + return res; +} + +template +ValueT& mkValue(typename ValueT::value_type n) { + return deref(new ValueT(std::move(n))); +} + +NullVal& mkNullValue() { return deref(new NullVal); } + +using DispatchTable = std::map; + +DispatchTable::const_iterator lookup(const DispatchTable& m, + const json::object& op) { + if (op.size() != 1) return m.end(); + + return m.find(op.begin()->key()); +} + +any_expr translateNode_internal(JsonExpr& n, VarMap& varmap) { + static const DispatchTable dt = { + {"==", &mkOperator}, {"===", &mkOperator}, + {"!=", &mkOperator}, {"!==", &mkOperator}, + {"if", &mkOperator}, {"!", &mkOperator}, + {"!!", &mkOperator}, {"or", &mkOperator}, + {"and", &mkOperator}, {">", &mkOperator}, + {">=", &mkOperator}, {"<", &mkOperator}, + {"<=", &mkOperator}, {"max", &mkOperator}, + {"min", &mkOperator}, {"+", &mkOperator}, + {"-", &mkOperator}, {"*", &mkOperator}, + {"/", &mkOperator
}, {"%", &mkOperator}, + {"map", &mkOperator}, {"reduce", &mkOperator}, + {"filter", &mkOperator}, {"all", &mkOperator}, + {"none", &mkOperator}, {"some", &mkOperator}, + {"merge", &mkOperator}, {"in", &mkOperator}, + {"cat", &mkOperator}, {"log", &mkOperator}, + {"var", &mkOperator}}; + + Expr* res = nullptr; + + switch (n.kind()) { + case json::kind::object: { + json::object& obj = n.get_object(); + DispatchTable::const_iterator pos = lookup(dt, obj); + + if (pos != dt.end()) { + CXX_LIKELY; + res = &pos->second(obj, varmap); + + if (pos->second == mkOperator) + varmap.insert(dynamic_cast(*res)); + } else { + // does json_logic support value objects? + unsupported(); + } + + break; + } + + case json::kind::array: { + // array is an operator that combines its subexpressions into an array + res = &mkArrayOperator(n.get_array(), varmap); + break; + } + + case json::kind::string: { + res = &mkValue(std::move(n.get_string())); + break; + } + + case json::kind::int64: { + res = &mkValue(n.get_int64()); + break; + } + + case json::kind::uint64: { + res = &mkValue(n.get_uint64()); + break; + } + + case json::kind::double_: { + res = &mkValue(n.get_double()); + break; + } + + case json::kind::bool_: { + res = &mkValue(n.get_bool()); + break; + } + + case json::kind::null: { + res = &mkNullValue(); + break; + } + + default: + unsupported(); + } + + return std::unique_ptr(res); +} + +std::tuple, bool> translateNode( + JsonExpr& n) { + VarMap varmap; + any_expr node = translateNode_internal(n, varmap); + bool hasComputedVariables = varmap.hasComputedVariables(); + + return std::make_tuple(std::move(node), varmap.toVector(), + hasComputedVariables); +} + +Operator::container_type translateChildren(json::array& children, + VarMap& varmap) { + Operator::container_type res; + + res.reserve(children.size()); + + for (JsonExpr& elem : children) + res.emplace_back(translateNode_internal(elem, varmap)); + + return res; +} + +Operator::container_type translateChildren(JsonExpr& n, VarMap& varmap) { + if (json::array* arr = n.if_array()) { + CXX_LIKELY; + return translateChildren(*arr, varmap); + } + + Operator::container_type res; + + res.emplace_back(translateNode_internal(n, varmap)); + return res; +} +} // namespace + +// only operators have children +void _traverseChildren(Visitor& v, Operator::const_iterator aa, + Operator::const_iterator zz) { + std::for_each(aa, zz, [&v](const any_expr& e) -> void { e->accept(v); }); +} + +void traverseChildren(Visitor& v, const Operator& node) { + Operator::const_iterator aa = node.begin(); + + _traverseChildren(v, aa, aa + node.num_evaluated_operands()); +} + +void traverseAllChildren(Visitor& v, const Operator& node) { + _traverseChildren(v, node.begin(), node.end()); +} + +void traverseChildrenReverse(Visitor& v, const Operator& node) { + Operator::const_reverse_iterator zz = node.crend(); + Operator::const_reverse_iterator aa = zz - node.num_evaluated_operands(); + + std::for_each(aa, zz, [&v](const any_expr& e) -> void { e->accept(v); }); +} + +namespace { +struct SAttributeTraversal : Visitor { + explicit SAttributeTraversal(Visitor& client) : sub(client) {} + + void visit(Expr&) final; + void visit(Operator&) final; + void visit(Eq&) final; + void visit(StrictEq&) final; + void visit(Neq&) final; + void visit(StrictNeq&) final; + void visit(Less&) final; + void visit(Greater&) final; + void visit(Leq&) final; + void visit(Geq&) final; + void visit(And&) final; + void visit(Or&) final; + void visit(Not&) final; + void visit(NotNot&) final; + void visit(Add&) final; + void visit(Sub&) final; + void visit(Mul&) final; + void visit(Div&) final; + void visit(Mod&) final; + void visit(Min&) final; + void visit(Max&) final; + void visit(Map&) final; + void visit(Reduce&) final; + void visit(Filter&) final; + void visit(All&) final; + void visit(None&) final; + void visit(Some&) final; + void visit(Merge&) final; + void visit(Cat&) final; + void visit(Substr&) final; + void visit(In&) final; + void visit(Array& n) final; + void visit(Var&) final; + void visit(Log&) final; + + void visit(If&) final; + + void visit(NullVal& n) final; + void visit(BoolVal& n) final; + void visit(IntVal& n) final; + void visit(UintVal& n) final; + void visit(DoubleVal& n) final; + void visit(StringVal& n) final; + + void visit(Error& n) final; + + private: + Visitor& sub; + + template + inline void _visit(OperatorNode& n) { + traverseChildren(*this, n); + sub.visit(n); + } + + template + inline void _value(ValueNode& n) { + sub.visit(n); + } +}; + +void SAttributeTraversal::visit(Expr&) { typeError(); } +void SAttributeTraversal::visit(Operator&) { typeError(); } +void SAttributeTraversal::visit(Eq& n) { _visit(n); } +void SAttributeTraversal::visit(StrictEq& n) { _visit(n); } +void SAttributeTraversal::visit(Neq& n) { _visit(n); } +void SAttributeTraversal::visit(StrictNeq& n) { _visit(n); } +void SAttributeTraversal::visit(Less& n) { _visit(n); } +void SAttributeTraversal::visit(Greater& n) { _visit(n); } +void SAttributeTraversal::visit(Leq& n) { _visit(n); } +void SAttributeTraversal::visit(Geq& n) { _visit(n); } +void SAttributeTraversal::visit(And& n) { _visit(n); } +void SAttributeTraversal::visit(Or& n) { _visit(n); } +void SAttributeTraversal::visit(Not& n) { _visit(n); } +void SAttributeTraversal::visit(NotNot& n) { _visit(n); } +void SAttributeTraversal::visit(Add& n) { _visit(n); } +void SAttributeTraversal::visit(Sub& n) { _visit(n); } +void SAttributeTraversal::visit(Mul& n) { _visit(n); } +void SAttributeTraversal::visit(Div& n) { _visit(n); } +void SAttributeTraversal::visit(Mod& n) { _visit(n); } +void SAttributeTraversal::visit(Min& n) { _visit(n); } +void SAttributeTraversal::visit(Max& n) { _visit(n); } +void SAttributeTraversal::visit(Array& n) { _visit(n); } +void SAttributeTraversal::visit(Map& n) { _visit(n); } +void SAttributeTraversal::visit(Reduce& n) { _visit(n); } +void SAttributeTraversal::visit(Filter& n) { _visit(n); } +void SAttributeTraversal::visit(All& n) { _visit(n); } +void SAttributeTraversal::visit(None& n) { _visit(n); } +void SAttributeTraversal::visit(Some& n) { _visit(n); } +void SAttributeTraversal::visit(Merge& n) { _visit(n); } +void SAttributeTraversal::visit(Cat& n) { _visit(n); } +void SAttributeTraversal::visit(Substr& n) { _visit(n); } +void SAttributeTraversal::visit(In& n) { _visit(n); } +void SAttributeTraversal::visit(Var& n) { _visit(n); } +void SAttributeTraversal::visit(Log& n) { _visit(n); } + +void SAttributeTraversal::visit(If& n) { _visit(n); } + +void SAttributeTraversal::visit(NullVal& n) { _value(n); } +void SAttributeTraversal::visit(BoolVal& n) { _value(n); } +void SAttributeTraversal::visit(IntVal& n) { _value(n); } +void SAttributeTraversal::visit(UintVal& n) { _value(n); } +void SAttributeTraversal::visit(DoubleVal& n) { _value(n); } +void SAttributeTraversal::visit(StringVal& n) { _value(n); } + +void SAttributeTraversal::visit(Error& n) { sub.visit(n); } +} // namespace + +using any_expr = + std::unique_ptr; // could be value if we have a type for that + +std::ostream& operator<<(std::ostream& os, any_expr& n); + +any_expr toany_expr(std::nullptr_t) { return any_expr(new NullVal); } +any_expr toany_expr(bool val) { return any_expr(new BoolVal(val)); } +any_expr toany_expr(std::int64_t val) { return any_expr(new IntVal(val)); } +any_expr toany_expr(std::uint64_t val) { return any_expr(new UintVal(val)); } +any_expr toany_expr(double val) { return any_expr(new DoubleVal(val)); } +any_expr toany_expr(json::string val) { + return any_expr(new StringVal(std::move(val))); +} + +// +// coercion functions + +std::int64_t toInt(const json::string& str) { + return std::stoi(std::string{str.c_str()}); +} + +std::int64_t toInt(double d) { return d; } + +std::int64_t toInt(bool b) { return b; } + +std::int64_t toInt(std::uint64_t v) { return v; } + +std::uint64_t toUint(const json::string& str) { + return std::stoull(std::string{str.c_str()}); +} + +std::uint64_t toUint(double d) { return d; } + +std::uint64_t toUint(std::int64_t v) { return v; } + +std::uint64_t toUint(bool b) { return b; } + +double toDouble(const json::string& str) { + return std::stod(std::string{str.c_str()}); +} + +double toDouble(std::int64_t val) { return val; } + +double toDouble(std::uint64_t val) { return val; } + +template +json::string toString(Val v) { + return json::string{std::to_string(v)}; +} + +json::string toString(bool b) { return json::string{b ? "true" : "false"}; } + +json::string toString(std::nullptr_t) { return json::string{"null"}; } + +/// conversion to boolean +/// \{ + +bool toBool(std::int64_t v) { return v; } +bool toBool(std::uint64_t v) { return v; } +bool toBool(double v) { return v; } +bool toBool(const json::string& v) { return v.size() != 0; } +bool toBool(Array& v) { return v.num_evaluated_operands(); } + +bool toBool(Expr& e) { + struct BoolConverter : FwdVisitor { + void visit(Expr&) final { typeError(); } + + void visit(NullVal&) final { res = false; } + void visit(BoolVal& n) final { res = n.value(); } + void visit(IntVal& n) final { res = toBool(n.value()); } + void visit(UintVal& n) final { res = toBool(n.value()); } + void visit(DoubleVal& n) final { res = toBool(n.value()); } + void visit(StringVal& n) final { res = toBool(n.value()); } + void visit(Array& n) final { res = toBool(n); } + + bool res; + }; + + BoolConverter conv; + + e.accept(conv); + return conv.res; +} + +bool toBool(any_expr& el) { return toBool(*el); } + +/// \} + +struct LogicalOperatorBase { + enum { + definedForString = true, + definedForDouble = true, + definedForInteger = true, + definedForBool = false, + definedForNull = false + }; + + using result_type = bool; +}; + +struct NoCoercion {}; + +/// \brief a strict binary operator operates on operands of the same +/// type. The operation on two different types returns false. +/// NO type coercion is performed. +struct StrictLogicalBinaryOperator : LogicalOperatorBase { + template + std::tuple coerce(LhsT* lv, RhsT* rv) { + return std::make_tuple(std::move(*lv), std::move(*rv)); + } +}; + +struct NumericBinaryOperatorBase { + std::tuple coerce(double* lv, double* rv) { + return std::make_tuple(*lv, *rv); + } + + std::tuple coerce(double* lv, std::int64_t* rv) { + return std::make_tuple(*lv, toDouble(*rv)); + } + + std::tuple coerce(double* lv, std::uint64_t* rv) { + return std::make_tuple(*lv, toDouble(*rv)); + } + + std::tuple coerce(std::int64_t* lv, double* rv) { + return std::make_tuple(toDouble(*lv), *rv); + } + + std::tuple coerce(std::int64_t* lv, + std::int64_t* rv) { + return std::make_tuple(*lv, *rv); + } + + std::tuple coerce(std::int64_t* lv, + std::uint64_t* rv) { + return std::make_tuple(*lv, toInt(*rv)); + } + + std::tuple coerce(std::uint64_t* lv, double* rv) { + return std::make_tuple(toDouble(*lv), *rv); + } + + std::tuple coerce(std::uint64_t* lv, + std::int64_t* rv) { + return std::make_tuple(toInt(*lv), *rv); + } + + std::tuple coerce(std::uint64_t* lv, + std::uint64_t* rv) { + return std::make_tuple(*lv, *rv); + } +}; + +/// \brief a logical binary operator compares two values. If the +/// values have a different type, type coercion is performed +/// on one of the operands. +struct LogicalBinaryOperator : NumericBinaryOperatorBase, LogicalOperatorBase { + using NumericBinaryOperatorBase::coerce; + + std::tuple coerce(double* lv, json::string* rv) { + return std::make_tuple(*lv, toDouble(*rv)); + } + + std::tuple coerce(std::int64_t* lv, + json::string* rv) { + return std::make_tuple(*lv, toInt(*rv)); + } + + std::tuple coerce(std::uint64_t* lv, + json::string* rv) { + return std::make_tuple(*lv, toUint(*rv)); + } + + std::tuple coerce(json::string* lv, double* rv) { + return std::make_tuple(toDouble(*lv), *rv); + } + + std::tuple coerce(json::string* lv, + std::int64_t* rv) { + return std::make_tuple(toInt(*lv), *rv); + } + + std::tuple coerce(json::string* lv, + std::uint64_t* rv) { + return std::make_tuple(toInt(*lv), *rv); + } + + std::tuple coerce(json::string* lv, + json::string* rv) { + return std::make_tuple(std::move(*lv), std::move(*rv)); + } +}; +// @} + +// Arith +struct ArithmeticOperator : NumericBinaryOperatorBase { + enum { + definedForString = false, + definedForDouble = true, + definedForInteger = true, + definedForBool = false, + definedForNull = true + }; + + using result_type = any_expr; + + using NumericBinaryOperatorBase::coerce; + + std::tuple coerce(double*, std::nullptr_t) { + return std::make_tuple(nullptr, nullptr); + } + + std::tuple coerce(std::int64_t*, + std::nullptr_t) { + return std::make_tuple(nullptr, nullptr); + } + + std::tuple coerce(std::uint64_t*, + std::nullptr_t) { + return std::make_tuple(nullptr, nullptr); + } + + std::tuple coerce(std::nullptr_t, double*) { + return std::make_tuple(nullptr, nullptr); + } + + std::tuple coerce(std::nullptr_t, + std::int64_t*) { + return std::make_tuple(nullptr, nullptr); + } + + std::tuple coerce(std::nullptr_t, + std::uint64_t*) { + return std::make_tuple(nullptr, nullptr); + } + + std::tuple coerce(std::nullptr_t, + std::nullptr_t) { + return std::make_tuple(nullptr, nullptr); + } +}; + +struct IntegerArithmeticOperator : ArithmeticOperator { + enum { + definedForString = false, + definedForDouble = false, + definedForInteger = true, + definedForBool = false, + definedForNull = false + }; + + using ArithmeticOperator::coerce; +}; + +struct StringOperator { + enum { + definedForString = true, + definedForDouble = false, + definedForInteger = false, + definedForBool = false, + definedForNull = false + }; + + using result_type = any_expr; + + std::tuple coerce(json::string* lv, + json::string* rv) { + return std::make_tuple(std::move(*lv), std::move(*rv)); + } +}; + +any_expr convert(any_expr val, ...) { return val; } + +any_expr convert(any_expr val, const ArithmeticOperator&) { + struct ArithmeticConverter : FwdVisitor { + explicit ArithmeticConverter(any_expr val) : res(std::move(val)) {} + + void visit(Expr&) final { typeError(); } + + // defined for the following types + void visit(IntVal&) final {} + void visit(UintVal&) final {} + void visit(DoubleVal&) final {} + void visit(NullVal&) final {} + + // need to convert values + void visit(StringVal& el) final { + double dd = toDouble(el.value()); + int64_t ii = toInt(el.value()); + // uint? + + res = (dd != ii) ? toany_expr(dd) : toany_expr(ii); + } + + void visit(BoolVal&) final { + // \todo correct? + res = toany_expr(nullptr); + } + + any_expr result() && { return std::move(res); } + + private: + any_expr res; + }; + + Expr* node = val.get(); + ArithmeticConverter conv{std::move(val)}; + + node->accept(conv); + return std::move(conv).result(); +} + +any_expr convert(any_expr val, const IntegerArithmeticOperator&) { + struct IntegerArithmeticConverter : FwdVisitor { + explicit IntegerArithmeticConverter(any_expr val) : res(std::move(val)) {} + + void visit(Expr&) final { typeError(); } + + // defined for the following types + void visit(IntVal&) final {} + void visit(UintVal&) final {} + + // need to convert values + void visit(StringVal& el) final { res = toany_expr(toInt(el.value())); } + + void visit(BoolVal& el) final { res = toany_expr(toInt(el.value())); } + + void visit(DoubleVal& el) final { res = toany_expr(toInt(el.value())); } + + void visit(NullVal&) final { res = toany_expr(std::int64_t(0)); } + + any_expr result() && { return std::move(res); } + + private: + any_expr res; + }; + + Expr* node = val.get(); + IntegerArithmeticConverter conv{std::move(val)}; + + node->accept(conv); + return std::move(conv).result(); +} + +any_expr convert(any_expr val, const StringOperator&) { + struct StringConverter : FwdVisitor { + explicit StringConverter(any_expr val) : res(std::move(val)) {} + + void visit(Expr&) final { typeError(); } + + // defined for the following types + void visit(StringVal&) final {} + + // need to convert values + void visit(BoolVal& el) final { res = toany_expr(toString(el.value())); } + + void visit(IntVal& el) final { res = toany_expr(toString(el.value())); } + + void visit(UintVal& el) final { res = toany_expr(toString(el.value())); } + + void visit(DoubleVal& el) final { res = toany_expr(toString(el.value())); } + + void visit(NullVal& el) final { res = toany_expr(toString(el.value())); } + + any_expr result() && { return std::move(res); } + + private: + any_expr res; + }; + + Expr* node = val.get(); + StringConverter conv{std::move(val)}; + + node->accept(conv); + return std::move(conv).result(); +} + +template +struct BinaryOperatorVisitor_ : FwdVisitor { + using result_type = typename BinaryOperator::result_type; + + BinaryOperatorVisitor_(LhsValue lval, BinaryOperator oper) + : lv(lval), op(oper), res() {} + + template + void calc(RhsValue rv) { + auto [ll, rr] = op.coerce(lv, rv); + + res = op(ll, rr); + } + + void visit(Expr&) final { typeError(); } + + void visit(StringVal& n) final { + if constexpr (BinaryOperator::definedForString) return calc(&n.value()); + + typeError(); + } + + void visit(NullVal&) final { + if constexpr (BinaryOperator::definedForNull) return calc(nullptr); + + typeError(); + } + + void visit(BoolVal& n) final { + if constexpr (BinaryOperator::definedForBool) return calc(&n.value()); + + typeError(); + } + + void visit(IntVal& n) final { + if constexpr (BinaryOperator::definedForInteger) return calc(&n.value()); + + typeError(); + } + + void visit(UintVal& n) final { + if constexpr (BinaryOperator::definedForInteger) return calc(&n.value()); + + typeError(); + } + + void visit(DoubleVal& n) final { + if constexpr (BinaryOperator::definedForDouble) return calc(&n.value()); + + typeError(); + } + + void visit(Array&) final { + unsupported(); // for now + } + + result_type result() && { return std::move(res); } + + private: + LhsValue lv; + BinaryOperator op; + result_type res; +}; + +template +struct BinaryOperatorVisitor : FwdVisitor { + using result_type = typename BinaryOperator::result_type; + + BinaryOperatorVisitor(BinaryOperator oper, any_expr& rhsarg) + : op(oper), rhs(rhsarg), res() {} + + template + void calc(LhsValue lv) { + using RhsVisitor = BinaryOperatorVisitor_; + + RhsVisitor vis{lv, op}; + + rhs->accept(vis); + res = std::move(vis).result(); + } + + void visit(StringVal& n) final { + if constexpr (BinaryOperator::definedForString) return calc(&n.value()); + + typeError(); + } + + void visit(NullVal&) final { + if constexpr (BinaryOperator::definedForNull) return calc(nullptr); + + typeError(); + } + + void visit(BoolVal& n) final { + if constexpr (BinaryOperator::definedForBool) return calc(&n.value()); + + typeError(); + } + + void visit(IntVal& n) final { + if constexpr (BinaryOperator::definedForInteger) return calc(&n.value()); + + typeError(); + } + + void visit(UintVal& n) final { + if constexpr (BinaryOperator::definedForInteger) return calc(&n.value()); + + typeError(); + } + + void visit(DoubleVal& n) final { + if constexpr (BinaryOperator::definedForDouble) return calc(&n.value()); + + typeError(); + } + + void visit(Array&) final { + typeError(); // for now + } + + result_type result() && { return std::move(res); } + + private: + BinaryOperator op; + any_expr& rhs; + result_type res; +}; + +template +typename BinaryOperator::result_type compute(any_expr& lhs, any_expr& rhs, + BinaryOperator op) { + using LhsVisitor = BinaryOperatorVisitor; + + LhsVisitor vis{op, rhs}; + + assert(lhs.get()); + lhs->accept(vis); + return std::move(vis).result(); +} + +template +struct Calc {}; + +template <> +struct Calc : LogicalBinaryOperator { + using LogicalBinaryOperator::result_type; + + template + result_type operator()(const T& lhs, const T& rhs) const { + return lhs == rhs; + } +}; + +template <> +struct Calc : LogicalBinaryOperator { + using LogicalBinaryOperator::result_type; + + template + result_type operator()(const T& lhs, const T& rhs) const { + return lhs != rhs; + } +}; + +template <> +struct Calc : StrictLogicalBinaryOperator { + using StrictLogicalBinaryOperator::result_type; + + result_type operator()(...) const { return false; } // type mismatch + + template + result_type operator()(const T& lhs, const T& rhs) const { + return lhs == rhs; + } +}; + +template <> +struct Calc : StrictLogicalBinaryOperator { + using StrictLogicalBinaryOperator::result_type; + + result_type operator()(...) const { return false; } // type mismatch + + template + result_type operator()(const T& lhs, const T& rhs) const { + return lhs == rhs; + } +}; + +template <> +struct Calc : LogicalBinaryOperator { + using LogicalBinaryOperator::result_type; + + template + result_type operator()(const T& lhs, const T& rhs) const { + return lhs < rhs; + } +}; + +template <> +struct Calc : LogicalBinaryOperator { + using LogicalBinaryOperator::result_type; + + template + result_type operator()(const T& lhs, const T& rhs) const { + return rhs < lhs; + } +}; + +template <> +struct Calc : LogicalBinaryOperator { + using LogicalBinaryOperator::result_type; + + template + result_type operator()(const T& lhs, const T& rhs) const { + return lhs <= rhs; + } +}; + +template <> +struct Calc : LogicalBinaryOperator { + using LogicalBinaryOperator::result_type; + + template + result_type operator()(const T& lhs, const T& rhs) const { + return rhs <= lhs; + } +}; + +template <> +struct Calc : ArithmeticOperator { + using ArithmeticOperator::result_type; + + result_type operator()(std::nullptr_t, std::nullptr_t) const { + return toany_expr(nullptr); + } + + template + result_type operator()(const T& lhs, const T& rhs) const { + return toany_expr(lhs + rhs); + } +}; + +template <> +struct Calc : ArithmeticOperator { + using ArithmeticOperator::result_type; + + result_type operator()(std::nullptr_t, std::nullptr_t) const { + return toany_expr(nullptr); + } + + template + result_type operator()(const T& lhs, const T& rhs) const { + return toany_expr(lhs - rhs); + } +}; + +template <> +struct Calc : ArithmeticOperator { + using ArithmeticOperator::result_type; + + result_type operator()(std::nullptr_t, std::nullptr_t) const { + return toany_expr(nullptr); + } + + template + result_type operator()(const T& lhs, const T& rhs) const { + return toany_expr(lhs * rhs); + } +}; + +template <> +struct Calc
: ArithmeticOperator { + using ArithmeticOperator::result_type; + + result_type operator()(std::nullptr_t, std::nullptr_t) const { + return toany_expr(nullptr); + } + + result_type operator()(double lhs, double rhs) const { + double res = lhs / rhs; + + // if (isInteger(res)) return toInt(res); + return toany_expr(res); + } + + template + result_type operator()(Int_t lhs, Int_t rhs) const { + if (lhs % rhs) return (*this)(double(lhs), double(rhs)); + + return toany_expr(lhs / rhs); + } +}; + +template <> +struct Calc : IntegerArithmeticOperator { + using IntegerArithmeticOperator::result_type; + + std::nullptr_t operator()(std::nullptr_t, std::nullptr_t) const { + return nullptr; + } + + template + result_type operator()(const T& lhs, const T& rhs) const { + if (rhs == 0) return toany_expr(nullptr); + + return toany_expr(lhs % rhs); + } +}; + +template <> +struct Calc : ArithmeticOperator { + using ArithmeticOperator::result_type; + + template + result_type operator()(const T& lhs, const T& rhs) const { + return toany_expr(std::min(lhs, rhs)); + } +}; + +template <> +struct Calc : ArithmeticOperator { + using ArithmeticOperator::result_type; + + template + result_type operator()(const T& lhs, const T& rhs) const { + return toany_expr(std::max(lhs, rhs)); + } +}; + +template <> +struct Calc { + using result_type = bool; + + result_type operator()(Expr& val) const { return !toBool(val); } +}; + +template <> +struct Calc { + using result_type = bool; + + result_type operator()(Expr& val) const { return toBool(val); } +}; + +template <> +struct Calc : StringOperator { + using StringOperator::result_type; + + result_type operator()(const json::string& lhs, + const json::string& rhs) const { + json::string tmp; + + tmp.reserve(lhs.size() + rhs.size()); + + tmp.append(lhs.begin(), lhs.end()); + tmp.append(rhs.begin(), rhs.end()); + + return toany_expr(std::move(tmp)); + } +}; + +struct Calculator : FwdVisitor { + using VarAccess = std::function; + + Calculator(const VarAccess& varAccess, std::ostream& out) + : vars(varAccess), logger(out), calcres(nullptr) {} + + void visit(Eq&) final; + void visit(StrictEq&) final; + void visit(Neq&) final; + void visit(StrictNeq&) final; + void visit(Less&) final; + void visit(Greater&) final; + void visit(Leq&) final; + void visit(Geq&) final; + void visit(And&) final; + void visit(Or&) final; + void visit(Not&) final; + void visit(NotNot&) final; + void visit(Add&) final; + void visit(Sub&) final; + void visit(Mul&) final; + void visit(Div&) final; + void visit(Mod&) final; + void visit(Min&) final; + void visit(Max&) final; + void visit(Array&) final; + void visit(Map&) final; + void visit(Reduce&) final; + void visit(Filter&) final; + void visit(All&) final; + void visit(None&) final; + void visit(Some&) final; + void visit(Merge&) final; + void visit(Cat&) final; + void visit(Substr&) final; + void visit(In&) final; + void visit(Var&) final; + void visit(Log&) final; + + void visit(NullVal& n) final; + void visit(BoolVal& n) final; + void visit(IntVal& n) final; + void visit(UintVal& n) final; + void visit(DoubleVal& n) final; + void visit(StringVal& n) final; + + void visit(Error& n) final; + + any_expr eval(Expr& n); + + private: + const VarAccess& vars; + std::ostream& logger; + any_expr calcres; + + Calculator(const Calculator&) = delete; + Calculator(Calculator&&) = delete; + Calculator& operator=(const Calculator&) = delete; + Calculator& operator=(Calculator&&) = delete; + + // + // opers + + /// implements relop : [1, 2, 3, whatever] + template + void evalPairShortCircuit(Operator& n, BinaryPredicate calc) { + const int num = n.num_evaluated_operands(); + assert(num >= 2); + + bool res = true; + int idx = -1; + any_expr rhs = eval(n.operand(++idx)); + assert(rhs.get()); + + while (res && (idx != (num - 1))) { + any_expr lhs = std::move(rhs); + assert(lhs.get()); + + rhs = eval(n.operand(++idx)); + assert(rhs.get()); + + res = compute(lhs, rhs, calc); + } + + calcres = toany_expr(res); + } + + template + void reduce(Operator& n, BinaryOperator op) { + const int num = n.num_evaluated_operands(); + assert(num >= 1); + + int idx = -1; + any_expr res = eval(n.operand(++idx)); + + res = convert(std::move(res), op); + + while (idx != (num - 1)) { + any_expr rhs = eval(n.operand(++idx)); + + rhs = convert(std::move(rhs), op); + res = compute(res, rhs, op); + } + + calcres = std::move(res); + } + + template + void binary(Operator& n, BinaryOperator calc) { + const int num = n.num_evaluated_operands(); + assert(num == 1 || num == 2); + + int idx = -1; + any_expr lhs; + + if (num == 2) { + CXX_LIKELY; + lhs = eval(n.operand(++idx)); + } else { + lhs = toany_expr(std::int64_t(0)); + } + + any_expr rhs = eval(n.operand(++idx)); + + calcres = compute(lhs, rhs, calc); + } + + template + void unary(Operator& n, UnaryOperator calc) { + const int num = n.num_evaluated_operands(); + assert(num == 1); + + const bool res = calc(*eval(n.operand(0))); + + calcres = toany_expr(res); + } + + void evalShortCircuit(Operator& n, bool val) { + const int num = n.num_evaluated_operands(); + assert(num >= 1); + + int idx = -1; + any_expr oper = eval(n.operand(++idx)); + bool found = (idx == num - 1) || (toBool(*oper) == val); + + // loop until *aa == val or when *aa is the last valid element + while (!found) { + oper = eval(n.operand(++idx)); + + found = (idx == (num - 1)) || (toBool(*oper) == val); + } + + calcres = std::move(oper); + } + + template + void _value(const ValueNode& val) { + calcres = toany_expr(val.value()); + } +}; + +any_expr Calculator::eval(Expr& n) { + any_expr res; + + n.accept(*this); + res.swap(calcres); + + return res; +} + +void Calculator::visit(Eq& n) { evalPairShortCircuit(n, Calc{}); } + +void Calculator::visit(StrictEq& n) { + evalPairShortCircuit(n, Calc{}); +} + +void Calculator::visit(Neq& n) { evalPairShortCircuit(n, Calc{}); } + +void Calculator::visit(StrictNeq& n) { + evalPairShortCircuit(n, Calc{}); +} + +void Calculator::visit(Less& n) { evalPairShortCircuit(n, Calc{}); } + +void Calculator::visit(Greater& n) { evalPairShortCircuit(n, Calc{}); } + +void Calculator::visit(Leq& n) { evalPairShortCircuit(n, Calc{}); } + +void Calculator::visit(Geq& n) { evalPairShortCircuit(n, Calc{}); } + +void Calculator::visit(And& n) { evalShortCircuit(n, false); } + +void Calculator::visit(Or& n) { evalShortCircuit(n, true); } + +void Calculator::visit(Not& n) { unary(n, Calc{}); } + +void Calculator::visit(NotNot& n) { unary(n, Calc{}); } + +void Calculator::visit(Add& n) { reduce(n, Calc{}); } + +void Calculator::visit(Sub& n) { binary(n, Calc{}); } + +void Calculator::visit(Mul& n) { reduce(n, Calc{}); } + +void Calculator::visit(Div& n) { binary(n, Calc
{}); } + +void Calculator::visit(Mod& n) { binary(n, Calc{}); } + +void Calculator::visit(Min& n) { reduce(n, Calc{}); } + +void Calculator::visit(Max& n) { reduce(n, Calc{}); } + +void Calculator::visit(Cat& n) { reduce(n, Calc{}); } + +void Calculator::visit(Substr&) { unsupported(); } +void Calculator::visit(Array&) { unsupported(); } +void Calculator::visit(Map&) { unsupported(); } +void Calculator::visit(Reduce&) { unsupported(); } +void Calculator::visit(Filter&) { unsupported(); } +void Calculator::visit(All&) { unsupported(); } +void Calculator::visit(None&) { unsupported(); } +void Calculator::visit(Some&) { unsupported(); } +void Calculator::visit(Merge&) { unsupported(); } +void Calculator::visit(In&) { unsupported(); } + +void Calculator::visit(Error&) { unsupported(); } + +void Calculator::visit(Var& n) { + assert(n.num_evaluated_operands() == 1); + + any_expr elm = convert(eval(n.operand(0)), StringOperator{}); + + if (StringVal* str = dynamic_cast(elm.get())) { + CXX_LIKELY; + calcres = vars(str->value(), n.num()); + return; + } + + typeError(); +} + +void Calculator::visit(Log&) { + const int num = n.num_evaluated_operands(); + assert(num == 1); + + calcres = calc(*eval(n.operand(0))); + + std::cerr << calcres << std::endl; +} + +void Calculator::visit(NullVal& n) { _value(n); } +void Calculator::visit(BoolVal& n) { _value(n); } +void Calculator::visit(IntVal& n) { _value(n); } +void Calculator::visit(UintVal& n) { _value(n); } +void Calculator::visit(DoubleVal& n) { _value(n); } +void Calculator::visit(StringVal& n) { _value(n); } + +void traverseInSAttributeOrder(Expr& e, Visitor& vis) { + SAttributeTraversal trav{vis}; + + e.accept(trav); +} + +any_expr calculate(any_expr& exp, const Calculator::VarAccess& vars) { + Calculator calc{vars, std::cerr}; + + assert(exp.get()); + return calc.eval(*exp); +} + +any_expr calculate(any_expr& exp) { + return calculate(exp, + [](const json::string&, int) -> any_expr { unsupported(); }); +} + +std::ostream& operator<<(std::ostream& os, any_expr& n) { + struct ValuePrinter : FwdVisitor { + explicit ValuePrinter(std::ostream& stream) : os(stream) {} + + void visit(NullVal&) final { os << toString(nullptr); } + void visit(BoolVal& n) final { os << toString(n.value()); } + void visit(IntVal& n) final { os << n.value(); } + void visit(UintVal& n) final { os << n.value(); } + void visit(DoubleVal& n) final { os << n.value(); } + void visit(StringVal& n) final { os << n.value(); } + void visit(Array&) final { unsupported(); } + + std::ostream& os; + }; + + ValuePrinter prn{os}; + + n->accept(prn); + return os; +} + +// accept implementations +void Eq::accept(Visitor& v) { v.visit(*this); } +void StrictEq::accept(Visitor& v) { v.visit(*this); } +void Neq::accept(Visitor& v) { v.visit(*this); } +void StrictNeq::accept(Visitor& v) { v.visit(*this); } +void Less::accept(Visitor& v) { v.visit(*this); } +void Greater::accept(Visitor& v) { v.visit(*this); } +void Leq::accept(Visitor& v) { v.visit(*this); } +void Geq::accept(Visitor& v) { v.visit(*this); } +void And::accept(Visitor& v) { v.visit(*this); } +void Or::accept(Visitor& v) { v.visit(*this); } +void Not::accept(Visitor& v) { v.visit(*this); } +void NotNot::accept(Visitor& v) { v.visit(*this); } +void Add::accept(Visitor& v) { v.visit(*this); } +void Sub::accept(Visitor& v) { v.visit(*this); } +void Mul::accept(Visitor& v) { v.visit(*this); } +void Div::accept(Visitor& v) { v.visit(*this); } +void Mod::accept(Visitor& v) { v.visit(*this); } +void Min::accept(Visitor& v) { v.visit(*this); } +void Max::accept(Visitor& v) { v.visit(*this); } +void Map::accept(Visitor& v) { v.visit(*this); } +void Reduce::accept(Visitor& v) { v.visit(*this); } +void Filter::accept(Visitor& v) { v.visit(*this); } +void All::accept(Visitor& v) { v.visit(*this); } +void None::accept(Visitor& v) { v.visit(*this); } +void Some::accept(Visitor& v) { v.visit(*this); } +void Array::accept(Visitor& v) { v.visit(*this); } +void Merge::accept(Visitor& v) { v.visit(*this); } +void Cat::accept(Visitor& v) { v.visit(*this); } +void Substr::accept(Visitor& v) { v.visit(*this); } +void In::accept(Visitor& v) { v.visit(*this); } +void Var::accept(Visitor& v) { v.visit(*this); } +void Log::accept(Visitor& v) { v.visit(*this); } +void If::accept(Visitor& v) { v.visit(*this); } + +void NullVal::accept(Visitor& v) { v.visit(*this); } +void BoolVal::accept(Visitor& v) { v.visit(*this); } +void IntVal::accept(Visitor& v) { v.visit(*this); } +void UintVal::accept(Visitor& v) { v.visit(*this); } +void DoubleVal::accept(Visitor& v) { v.visit(*this); } +void StringVal::accept(Visitor& v) { v.visit(*this); } + +void Error::accept(Visitor& v) { v.visit(*this); } + +void FwdVisitor::visit(Expr&) { /* error ? */ } + +// num_evaluated_operands implementations +int Operator::num_evaluated_operands() const { return size(); } + +template +int OperatorN::num_evaluated_operands() const { + return std::min(MaxArity, Operator::num_evaluated_operands()); +} +} // namespace json_logic diff --git a/include/clippy/clippy-eval.hpp b/include/clippy/attic/clippy-eval.hpp similarity index 90% rename from include/clippy/clippy-eval.hpp rename to include/clippy/attic/clippy-eval.hpp index ad29610..06a1320 100644 --- a/include/clippy/clippy-eval.hpp +++ b/include/clippy/attic/clippy-eval.hpp @@ -82,12 +82,12 @@ struct Expr { // foundation classes // \{ -using AnyExpr = std::unique_ptr; -using ValueExpr = +using any_expr = std::unique_ptr; +using any_expr = std::unique_ptr; // \todo consider std::unique_ptr -struct Operator : Expr, private std::vector { - using container_type = std::vector; +struct Operator : Expr, private std::vector { + using container_type = std::vector; using container_type::at; using container_type::back; @@ -380,8 +380,8 @@ struct StringVal : ValueT { void accept(Visitor&) final; }; -struct ObjectVal : Expr, private std::map { - using base = std::map; +struct ObjectVal : Expr, private std::map { + using base = std::map; using base::base; using base::begin; @@ -673,7 +673,7 @@ struct VarMap { void VarMap::insert(Var& var) { try { - AnyExpr& arg = var.back(); + any_expr& arg = var.back(); StringVal& str = down_cast(*arg); const bool comp = (str.value().find('.') != json::string::npos && str.value().find('[') != json::string::npos); @@ -754,7 +754,7 @@ DispatchTable::const_iterator lookup(const DispatchTable& m, return m.find(op.begin()->key()); } -AnyExpr translateNode_internal(JsonExpr& n, VarMap& varmap) { +any_expr translateNode_internal(JsonExpr& n, VarMap& varmap) { static const DispatchTable dt = { {"==", &mkOperator}, {"===", &mkOperator}, @@ -852,13 +852,13 @@ AnyExpr translateNode_internal(JsonExpr& n, VarMap& varmap) { unsupported(); } - return AnyExpr(res); + return any_expr(res); } -inline std::tuple, bool> translateNode( +inline std::tuple, bool> translateNode( JsonExpr& n) { VarMap varmap; - AnyExpr node = translateNode_internal(n, varmap); + any_expr node = translateNode_internal(n, varmap); bool hasComputedVariables = varmap.hasComputedVariables(); return {std::move(node), varmap.toVector(), hasComputedVariables}; @@ -889,72 +889,72 @@ Operator::container_type translateChildren(JsonExpr& n, VarMap& varmap) { } } // namespace -std::ostream& operator<<(std::ostream& os, ValueExpr& n); +std::ostream& operator<<(std::ostream& os, any_expr& n); // // to value conversion -ValueExpr toValueExpr(const json::value& n); +any_expr toany_expr(const json::value& n); -ValueExpr toValueExpr(std::nullptr_t) { return ValueExpr(new NullVal); } -ValueExpr toValueExpr(bool val) { return ValueExpr(new BoolVal(val)); } -ValueExpr toValueExpr(std::int64_t val) { return ValueExpr(new IntVal(val)); } -ValueExpr toValueExpr(std::uint64_t val) { return ValueExpr(new UintVal(val)); } -ValueExpr toValueExpr(double val) { return ValueExpr(new DoubleVal(val)); } -ValueExpr toValueExpr(json::string val) { - return ValueExpr(new StringVal(std::move(val))); +any_expr toany_expr(std::nullptr_t) { return any_expr(new NullVal); } +any_expr toany_expr(bool val) { return any_expr(new BoolVal(val)); } +any_expr toany_expr(std::int64_t val) { return any_expr(new IntVal(val)); } +any_expr toany_expr(std::uint64_t val) { return any_expr(new UintVal(val)); } +any_expr toany_expr(double val) { return any_expr(new DoubleVal(val)); } +any_expr toany_expr(json::string val) { + return any_expr(new StringVal(std::move(val))); } -ValueExpr toValueExpr(const json::array& val) { +any_expr toany_expr(const json::array& val) { Operator::container_type elems; std::transform( val.begin(), val.end(), std::back_inserter(elems), - [](const json::value& el) -> ValueExpr { return toValueExpr(el); }); + [](const json::value& el) -> any_expr { return toany_expr(el); }); Array& arr = deref(new Array); arr.set_operands(std::move(elems)); - return ValueExpr(&arr); + return any_expr(&arr); } CXX_MAYBE_UNUSED -ValueExpr toValueExpr(const json::value& n) { - ValueExpr res; +any_expr toany_expr(const json::value& n) { + any_expr res; switch (n.kind()) { case json::kind::string: { - res = toValueExpr(n.get_string()); + res = toany_expr(n.get_string()); break; } case json::kind::int64: { - res = toValueExpr(n.get_int64()); + res = toany_expr(n.get_int64()); break; } case json::kind::uint64: { - res = toValueExpr(n.get_uint64()); + res = toany_expr(n.get_uint64()); break; } case json::kind::double_: { - res = toValueExpr(n.get_double()); + res = toany_expr(n.get_double()); break; } case json::kind::bool_: { - res = toValueExpr(n.get_bool()); + res = toany_expr(n.get_bool()); break; } case json::kind::null: { - res = toValueExpr(nullptr); + res = toany_expr(nullptr); break; } case json::kind::array: { - res = toValueExpr(n.get_array()); + res = toany_expr(n.get_array()); break; } @@ -1391,7 +1391,7 @@ struct ArithmeticOperator : NumericBinaryOperatorBase { definedForArray = false }; - using result_type = ValueExpr; + using result_type = any_expr; using NumericBinaryOperatorBase::coerce; @@ -1452,7 +1452,7 @@ struct StringOperator { definedForArray = false }; - using result_type = ValueExpr; + using result_type = any_expr; std::tuple coerce(json::string* lv, json::string* rv) { @@ -1470,16 +1470,16 @@ struct ArrayOperator { definedForArray = true }; - using result_type = ValueExpr; + using result_type = any_expr; std::tuple coerce(Array* lv, Array* rv) { return {lv, rv}; } }; -AnyExpr convert(AnyExpr val, ...) { return val; } +any_expr convert(any_expr val, ...) { return val; } -AnyExpr convert(AnyExpr val, const ArithmeticOperator&) { +any_expr convert(any_expr val, const ArithmeticOperator&) { struct ArithmeticConverter : FwdVisitor { - explicit ArithmeticConverter(AnyExpr val) : res(std::move(val)) {} + explicit ArithmeticConverter(any_expr val) : res(std::move(val)) {} void visit(Expr&) final { typeError(); } @@ -1495,18 +1495,18 @@ AnyExpr convert(AnyExpr val, const ArithmeticOperator&) { std::int64_t ii = toConcreteValue(el.value(), std::int64_t{}); // uint? - res = (dd != ii) ? toValueExpr(dd) : toValueExpr(ii); + res = (dd != ii) ? toany_expr(dd) : toany_expr(ii); } void visit(BoolVal&) final { // \todo correct? - res = toValueExpr(nullptr); + res = toany_expr(nullptr); } - AnyExpr result() && { return std::move(res); } + any_expr result() && { return std::move(res); } private: - AnyExpr res; + any_expr res; }; Expr* node = val.get(); @@ -1516,9 +1516,9 @@ AnyExpr convert(AnyExpr val, const ArithmeticOperator&) { return std::move(conv).result(); } -AnyExpr convert(AnyExpr val, const IntegerArithmeticOperator&) { +any_expr convert(any_expr val, const IntegerArithmeticOperator&) { struct IntegerArithmeticConverter : FwdVisitor { - explicit IntegerArithmeticConverter(AnyExpr val) : res(std::move(val)) {} + explicit IntegerArithmeticConverter(any_expr val) : res(std::move(val)) {} void visit(Expr&) final { typeError(); } @@ -1528,23 +1528,23 @@ AnyExpr convert(AnyExpr val, const IntegerArithmeticOperator&) { // need to convert values void visit(StringVal& el) final { - res = toValueExpr(toConcreteValue(el.value(), std::int64_t{})); + res = toany_expr(toConcreteValue(el.value(), std::int64_t{})); } void visit(BoolVal& el) final { - res = toValueExpr(toConcreteValue(el.value(), std::int64_t{})); + res = toany_expr(toConcreteValue(el.value(), std::int64_t{})); } void visit(DoubleVal& el) final { - res = toValueExpr(toConcreteValue(el.value(), std::int64_t{})); + res = toany_expr(toConcreteValue(el.value(), std::int64_t{})); } - void visit(NullVal&) final { res = toValueExpr(std::int64_t{0}); } + void visit(NullVal&) final { res = toany_expr(std::int64_t{0}); } - AnyExpr result() && { return std::move(res); } + any_expr result() && { return std::move(res); } private: - AnyExpr res; + any_expr res; }; Expr* node = val.get(); @@ -1554,9 +1554,9 @@ AnyExpr convert(AnyExpr val, const IntegerArithmeticOperator&) { return std::move(conv).result(); } -AnyExpr convert(AnyExpr val, const StringOperator&) { +any_expr convert(any_expr val, const StringOperator&) { struct StringConverter : FwdVisitor { - explicit StringConverter(AnyExpr val) : res(std::move(val)) {} + explicit StringConverter(any_expr val) : res(std::move(val)) {} void visit(Expr&) final { typeError(); } @@ -1565,29 +1565,29 @@ AnyExpr convert(AnyExpr val, const StringOperator&) { // need to convert values void visit(BoolVal& el) final { - res = toValueExpr(toConcreteValue(el.value(), json::string{})); + res = toany_expr(toConcreteValue(el.value(), json::string{})); } void visit(IntVal& el) final { - res = toValueExpr(toConcreteValue(el.value(), json::string{})); + res = toany_expr(toConcreteValue(el.value(), json::string{})); } void visit(UintVal& el) final { - res = toValueExpr(toConcreteValue(el.value(), json::string{})); + res = toany_expr(toConcreteValue(el.value(), json::string{})); } void visit(DoubleVal& el) final { - res = toValueExpr(toConcreteValue(el.value(), json::string{})); + res = toany_expr(toConcreteValue(el.value(), json::string{})); } void visit(NullVal& el) final { - res = toValueExpr(toConcreteValue(el.value(), json::string{})); + res = toany_expr(toConcreteValue(el.value(), json::string{})); } - AnyExpr result() && { return std::move(res); } + any_expr result() && { return std::move(res); } private: - AnyExpr res; + any_expr res; }; Expr* node = val.get(); @@ -1597,9 +1597,9 @@ AnyExpr convert(AnyExpr val, const StringOperator&) { return std::move(conv).result(); } -AnyExpr convert(AnyExpr val, const ArrayOperator&) { +any_expr convert(any_expr val, const ArrayOperator&) { struct ArrayConverter : FwdVisitor { - explicit ArrayConverter(AnyExpr val) : val(std::move(val)) {} + explicit ArrayConverter(any_expr val) : val(std::move(val)) {} void visit(Expr&) final { typeError(); } @@ -1628,10 +1628,10 @@ AnyExpr convert(AnyExpr val, const ArrayOperator&) { void visit(DoubleVal&) final { toArray(); } void visit(NullVal&) final { toArray(); } - AnyExpr result() && { return std::move(val); } + any_expr result() && { return std::move(val); } private: - AnyExpr val; + any_expr val; }; Expr* node = val.get(); @@ -1681,7 +1681,7 @@ struct UnpackVisitor : FwdVisitor { }; template -T unpackValue(Expr& expr) { +T unpack_value(Expr& expr) { UnpackVisitor unpack; expr.accept(unpack); @@ -1689,28 +1689,28 @@ T unpackValue(Expr& expr) { } template -T unpackValue(ValueExpr& el) { - return unpackValue(*el); +T unpack_value(any_expr& el) { + return unpack_value(*el); } template -T unpackValue(ValueExpr&& el) { - return unpackValue(*el); +T unpack_value(any_expr&& el) { + return unpack_value(*el); } // // Json Logic - truthy/falsy -bool truthy(Expr& el) { return unpackValue(el); } -bool truthy(ValueExpr& el) { return unpackValue(el); } -bool truthy(ValueExpr&& el) { return unpackValue(std::move(el)); } +bool truthy(Expr& el) { return unpack_value(el); } +bool truthy(any_expr& el) { return unpack_value(el); } +bool truthy(any_expr&& el) { return unpack_value(std::move(el)); } bool falsy(Expr& el) { return !truthy(el); } -bool falsy(ValueExpr& el) { return !truthy(el); } -bool falsy(ValueExpr&& el) { return !truthy(std::move(el)); } +bool falsy(any_expr& el) { return !truthy(el); } +bool falsy(any_expr&& el) { return !truthy(std::move(el)); } // // cloning -AnyExpr cloneExpr(AnyExpr& expr); +any_expr cloneExpr(any_expr& expr); struct ExprCloner : GVisitor { using base = GVisitor; @@ -1764,7 +1764,7 @@ Expr& ExprCloner::init(Operator& src, Operator& tgt) { std::transform(src.operands().begin(), src.operands().end(), std::back_inserter(children), - [](AnyExpr& e) -> AnyExpr { return cloneExpr(e); }); + [](any_expr& e) -> any_expr { return cloneExpr(e); }); tgt.set_operands(std::move(children)); return tgt; @@ -1780,11 +1780,11 @@ Expr& ExprCloner::init(ObjectVal& src, ObjectVal& tgt) { return tgt; } -AnyExpr cloneExpr(AnyExpr& expr) { +any_expr cloneExpr(any_expr& expr) { ExprCloner cloner; expr->accept(cloner); - return ValueExpr{cloner.result()}; + return any_expr{cloner.result()}; } // @@ -1935,7 +1935,7 @@ template struct BinaryOperatorVisitor : FwdVisitor { using result_type = typename BinaryOperator::result_type; - BinaryOperatorVisitor(BinaryOperator oper, ValueExpr& rhsarg) + BinaryOperatorVisitor(BinaryOperator oper, any_expr& rhsarg) : op(oper), rhs(rhsarg), res() {} template @@ -2029,7 +2029,7 @@ struct BinaryOperatorVisitor : FwdVisitor { private: BinaryOperator op; - ValueExpr& rhs; + any_expr& rhs; result_type res; }; @@ -2037,7 +2037,7 @@ struct BinaryOperatorVisitor : FwdVisitor { // compute and sequence functions template -typename BinaryOperator::result_type compute(ValueExpr& lhs, ValueExpr& rhs, +typename BinaryOperator::result_type compute(any_expr& lhs, any_expr& rhs, BinaryOperator op) { using LhsVisitor = BinaryOperatorVisitor; @@ -2274,12 +2274,12 @@ struct Calc : ArithmeticOperator { using ArithmeticOperator::result_type; result_type operator()(std::nullptr_t, std::nullptr_t) const { - return toValueExpr(nullptr); + return toany_expr(nullptr); } template result_type operator()(const T& lhs, const T& rhs) const { - return toValueExpr(lhs + rhs); + return toany_expr(lhs + rhs); } }; @@ -2288,12 +2288,12 @@ struct Calc : ArithmeticOperator { using ArithmeticOperator::result_type; result_type operator()(std::nullptr_t, std::nullptr_t) const { - return toValueExpr(nullptr); + return toany_expr(nullptr); } template result_type operator()(const T& lhs, const T& rhs) const { - return toValueExpr(lhs - rhs); + return toany_expr(lhs - rhs); } }; @@ -2302,12 +2302,12 @@ struct Calc : ArithmeticOperator { using ArithmeticOperator::result_type; result_type operator()(std::nullptr_t, std::nullptr_t) const { - return toValueExpr(nullptr); + return toany_expr(nullptr); } template result_type operator()(const T& lhs, const T& rhs) const { - return toValueExpr(lhs * rhs); + return toany_expr(lhs * rhs); } }; @@ -2316,21 +2316,21 @@ struct Calc
: ArithmeticOperator { using ArithmeticOperator::result_type; result_type operator()(std::nullptr_t, std::nullptr_t) const { - return toValueExpr(nullptr); + return toany_expr(nullptr); } result_type operator()(double lhs, double rhs) const { double res = lhs / rhs; // if (isInteger(res)) return toInt(res); - return toValueExpr(res); + return toany_expr(res); } template result_type operator()(Int_t lhs, Int_t rhs) const { if (lhs % rhs) return (*this)(double(lhs), double(rhs)); - return toValueExpr(lhs / rhs); + return toany_expr(lhs / rhs); } }; @@ -2344,9 +2344,9 @@ struct Calc : IntegerArithmeticOperator { template result_type operator()(const T& lhs, const T& rhs) const { - if (rhs == 0) return toValueExpr(nullptr); + if (rhs == 0) return toany_expr(nullptr); - return toValueExpr(lhs % rhs); + return toany_expr(lhs % rhs); } }; @@ -2360,7 +2360,7 @@ struct Calc : ArithmeticOperator { template result_type operator()(const T& lhs, const T& rhs) const { - return toValueExpr(std::min(lhs, rhs)); + return toany_expr(std::min(lhs, rhs)); } }; @@ -2374,7 +2374,7 @@ struct Calc : ArithmeticOperator { template result_type operator()(const T& lhs, const T& rhs) const { - return toValueExpr(std::max(lhs, rhs)); + return toany_expr(std::max(lhs, rhs)); } }; @@ -2405,7 +2405,7 @@ struct Calc : StringOperator { tmp.append(lhs.begin(), lhs.end()); tmp.append(rhs.begin(), rhs.end()); - return toValueExpr(std::move(tmp)); + return toany_expr(std::move(tmp)); } }; @@ -2418,7 +2418,7 @@ struct Calc : StringOperator // \todo the conversion rules differ const json::string& rhs) const { const bool res = (rhs.find(lhs) != json::string::npos); - return toValueExpr(res); + return toany_expr(res); } }; @@ -2431,7 +2431,7 @@ struct Calc : StringOperator // \todo the conversion rules differ const json::string& rhs) const { std::regex rgx(lhs.c_str(), lhs.size()); - return toValueExpr(std::regex_search(rhs.begin(), rhs.end(), rgx)); + return toany_expr(std::regex_search(rhs.begin(), rhs.end(), rgx)); } }; @@ -2441,7 +2441,7 @@ struct Calc : ArrayOperator { result_type operator()(Array* lhs, Array* rhs) const { // note, to use the lhs entirely, it would need to be released - // from its ValueExpr + // from its any_expr Array& res = deref(new Array); { @@ -2455,12 +2455,12 @@ struct Calc : ArrayOperator { std::make_move_iterator(ropers.end())); } - return ValueExpr(&res); + return any_expr(&res); } }; struct Calculator : FwdVisitor { - using VarAccess = std::function; + using VarAccess = std::function; Calculator(VarAccess varAccess, std::ostream& out) : vars(std::move(varAccess)), logger(out), calcres(nullptr) {} @@ -2512,12 +2512,12 @@ struct Calculator : FwdVisitor { void visit(Error& n) final; void visit(RegexMatch& n) final; - ValueExpr eval(Expr& n); + any_expr eval(Expr& n); private: VarAccess vars; std::ostream& logger; - ValueExpr calcres; + any_expr calcres; Calculator(const Calculator&) = delete; Calculator(Calculator&&) = delete; @@ -2553,7 +2553,7 @@ struct Calculator : FwdVisitor { template void _value(const ValueNode& val) { - calcres = toValueExpr(val.value()); + calcres = toany_expr(val.value()); } /// auxiliary missing method @@ -2564,10 +2564,10 @@ struct SequenceFunction { SequenceFunction(Expr& e, std::ostream& logstream) : expr(e), logger(logstream) {} - ValueExpr operator()(ValueExpr&& elem) const { - ValueExpr* elptr = &elem; // workaround, b/c unique_ptr cannot be captured + any_expr operator()(any_expr&& elem) const { + any_expr* elptr = &elem; // workaround, b/c unique_ptr cannot be captured - Calculator sub{[elptr](const json::value& keyval, int) -> ValueExpr { + Calculator sub{[elptr](const json::value& keyval, int) -> any_expr { if (const json::string* pkey = keyval.if_string()) { const json::string& key = *pkey; @@ -2582,7 +2582,7 @@ struct SequenceFunction { } } - return toValueExpr(nullptr); + return toany_expr(nullptr); }, logger}; @@ -2597,7 +2597,7 @@ struct SequenceFunction { struct SequencePredicate : SequenceFunction { using SequenceFunction::SequenceFunction; - bool operator()(ValueExpr&& elem) const { + bool operator()(any_expr&& elem) const { return truthy(SequenceFunction::operator()(std::move(elem))); } }; @@ -2605,15 +2605,15 @@ struct SequencePredicate : SequenceFunction { struct SequencePredicateNondestructive : SequenceFunction { using SequenceFunction::SequenceFunction; - bool operator()(ValueExpr&& elem) const { + bool operator()(any_expr&& elem) const { return truthy(SequenceFunction::operator()(cloneExpr(elem))); } }; /* template - ValueExpr - accumulate_move(InputIterator pos, InputIterator lim, ValueExpr accu, + any_expr + accumulate_move(InputIterator pos, InputIterator lim, any_expr accu, BinaryOperation binop) { for ( ; pos != lim; ++pos) @@ -2632,19 +2632,19 @@ struct SequenceReduction { // g++ -std=c++17 passes in a & ref, while g++ -std=c++20 passes in a &&. // the templated && together with reference collapsing make the code portable // across standard versions. - template - ValueExpr operator()(ValueExprT&& accu, ValueExpr elem) const { - ValueExpr* acptr = &accu; // workaround, b/c unique_ptr cannot be captured - ValueExpr* elptr = &elem; // workaround, b/c unique_ptr cannot be captured + template + any_expr operator()(any_exprT&& accu, any_expr elem) const { + any_expr* acptr = &accu; // workaround, b/c unique_ptr cannot be captured + any_expr* elptr = &elem; // workaround, b/c unique_ptr cannot be captured - Calculator sub{[acptr, elptr](const json::value& keyval, int) -> ValueExpr { + Calculator sub{[acptr, elptr](const json::value& keyval, int) -> any_expr { if (const json::string* pkey = keyval.if_string()) { if (*pkey == "current") return cloneExpr(*elptr); if (*pkey == "accumulator") return cloneExpr(*acptr); } - return toValueExpr(nullptr); + return toany_expr(nullptr); }, logger}; @@ -2664,7 +2664,7 @@ ValueT Calculator::unpackOptionalArg(Operator& n, int argpos, return defaultVal; } - return unpackValue(*eval(n.operand(argpos))); + return unpack_value(*eval(n.operand(argpos))); } template @@ -2674,7 +2674,7 @@ void Calculator::unary(Operator& n, UnaryPredicate pred) { const bool res = pred(*eval(n.operand(0))); - calcres = toValueExpr(res); + calcres = toany_expr(res); } template @@ -2683,16 +2683,16 @@ void Calculator::binary(Operator& n, BinaryOperator binop) { assert(num == 1 || num == 2); int idx = -1; - ValueExpr lhs; + any_expr lhs; if (num == 2) { CXX_LIKELY; lhs = eval(n.operand(++idx)); } else { - lhs = toValueExpr(std::int64_t(0)); + lhs = toany_expr(std::int64_t(0)); } - ValueExpr rhs = eval(n.operand(++idx)); + any_expr rhs = eval(n.operand(++idx)); calcres = compute(lhs, rhs, binop); } @@ -2703,12 +2703,12 @@ void Calculator::reduce(Operator& n, BinaryOperator op) { assert(num >= 1); int idx = -1; - ValueExpr res = eval(n.operand(++idx)); + any_expr res = eval(n.operand(++idx)); res = convert(std::move(res), op); while (idx != (num - 1)) { - ValueExpr rhs = eval(n.operand(++idx)); + any_expr rhs = eval(n.operand(++idx)); rhs = convert(std::move(rhs), op); res = compute(res, rhs, op); @@ -2724,11 +2724,11 @@ void Calculator::evalPairShortCircuit(Operator& n, BinaryPredicate pred) { bool res = true; int idx = -1; - ValueExpr rhs = eval(n.operand(++idx)); + any_expr rhs = eval(n.operand(++idx)); assert(rhs.get()); while (res && (idx != (num - 1))) { - ValueExpr lhs = std::move(rhs); + any_expr lhs = std::move(rhs); assert(lhs.get()); rhs = eval(n.operand(++idx)); @@ -2737,7 +2737,7 @@ void Calculator::evalPairShortCircuit(Operator& n, BinaryPredicate pred) { res = compute(lhs, rhs, pred); } - calcres = toValueExpr(res); + calcres = toany_expr(res); } void Calculator::evalShortCircuit(Operator& n, bool val) { @@ -2749,7 +2749,7 @@ void Calculator::evalShortCircuit(Operator& n, bool val) { } int idx = -1; - ValueExpr oper = eval(n.operand(++idx)); + any_expr oper = eval(n.operand(++idx)); //~ std::cerr << idx << ") " << oper << std::endl; bool found = (idx == num - 1) || (truthy(*oper) == val); @@ -2765,8 +2765,8 @@ void Calculator::evalShortCircuit(Operator& n, bool val) { calcres = std::move(oper); } -ValueExpr Calculator::eval(Expr& n) { - ValueExpr res; +any_expr Calculator::eval(Expr& n) { + any_expr res; n.accept(*this); res.swap(calcres); @@ -2825,7 +2825,7 @@ void Calculator::visit(RegexMatch& n) { binary(n, Calc{}); } void Calculator::visit(Substr& n) { assert(n.num_evaluated_operands() >= 1); - json::string str = unpackValue(*eval(n.operand(0))); + json::string str = unpack_value(*eval(n.operand(0))); std::int64_t ofs = unpackOptionalArg(n, 1, 0); std::int64_t cnt = unpackOptionalArg(n, 2, 0); @@ -2839,7 +2839,7 @@ void Calculator::visit(Substr& n) { cnt = std::max(std::int64_t(str.size()) - ofs + cnt, std::int64_t(0)); } - calcres = toValueExpr(json::string{str.subview(ofs, cnt)}); + calcres = toany_expr(json::string{str.subview(ofs, cnt)}); } void Calculator::visit(Array& n) { @@ -2850,25 +2850,25 @@ void Calculator::visit(Array& n) { std::transform( std::make_move_iterator(n.begin()), std::make_move_iterator(n.end()), std::back_inserter(elems), - [self](AnyExpr&& exp) -> ValueExpr { return self->eval(*exp); }); + [self](any_expr&& exp) -> any_expr { return self->eval(*exp); }); Array& res = deref(new Array); res.set_operands(std::move(elems)); - calcres = ValueExpr(&res); + calcres = any_expr(&res); } void Calculator::visit(Merge& n) { reduce(n, Calc{}); } void Calculator::visit(Reduce& n) { - ValueExpr arr = eval(n.operand(0)); + any_expr arr = eval(n.operand(0)); Expr& expr = n.operand(1); - ValueExpr accu = eval(n.operand(2)); - ValueExpr* acptr = &accu; + any_expr accu = eval(n.operand(2)); + any_expr* acptr = &accu; auto op = [&expr, acptr, - calclogger = &this->logger](Array& arrop) -> ValueExpr { + calclogger = &this->logger](Array& arrop) -> any_expr { // non destructive predicate is required for evaluating and copying return std::accumulate(std::make_move_iterator(arrop.begin()), std::make_move_iterator(arrop.end()), @@ -2877,13 +2877,13 @@ void Calculator::visit(Reduce& n) { }; calcres = - with_type(arr.get(), op, []() -> ValueExpr { return nullptr; }); + with_type(arr.get(), op, []() -> any_expr { return nullptr; }); } void Calculator::visit(Map& n) { - ValueExpr arr = eval(n.operand(0)); + any_expr arr = eval(n.operand(0)); auto mapper = [&n, &arr, - calclogger = &this->logger](Array& arrop) -> ValueExpr { + calclogger = &this->logger](Array& arrop) -> any_expr { Expr& expr = n.operand(1); Operator::container_type mapped_elements; @@ -2896,14 +2896,14 @@ void Calculator::visit(Map& n) { return std::move(arr); }; - calcres = with_type( - arr.get(), mapper, []() -> ValueExpr { return ValueExpr{new Array}; }); + calcres = with_type(arr.get(), mapper, + []() -> any_expr { return any_expr{new Array}; }); } void Calculator::visit(Filter& n) { - ValueExpr arr = eval(n.operand(0)); + any_expr arr = eval(n.operand(0)); auto filter = [&n, &arr, - calclogger = &this->logger](Array& arrop) -> ValueExpr { + calclogger = &this->logger](Array& arrop) -> any_expr { Expr& expr = n.operand(1); Operator::container_type filtered_elements; @@ -2917,41 +2917,41 @@ void Calculator::visit(Filter& n) { return std::move(arr); }; - calcres = with_type( - arr.get(), filter, []() -> ValueExpr { return ValueExpr{new Array}; }); + calcres = with_type(arr.get(), filter, + []() -> any_expr { return any_expr{new Array}; }); } void Calculator::visit(All& n) { - ValueExpr arr = eval(n.operand(0)); + any_expr arr = eval(n.operand(0)); Array& elems = down_cast(*arr); // evaluated elements Expr& expr = n.operand(1); const bool res = std::all_of(std::make_move_iterator(elems.begin()), std::make_move_iterator(elems.end()), SequencePredicate{expr, logger}); - calcres = toValueExpr(res); + calcres = toany_expr(res); } void Calculator::visit(None& n) { - ValueExpr arr = eval(n.operand(0)); + any_expr arr = eval(n.operand(0)); Array& elems = down_cast(*arr); // evaluated elements Expr& expr = n.operand(1); const bool res = std::none_of(std::make_move_iterator(elems.begin()), std::make_move_iterator(elems.end()), SequencePredicate{expr, logger}); - calcres = toValueExpr(res); + calcres = toany_expr(res); } void Calculator::visit(Some& n) { - ValueExpr arr = eval(n.operand(0)); + any_expr arr = eval(n.operand(0)); Array& elems = down_cast(*arr); // evaluated elements Expr& expr = n.operand(1); const bool res = std::any_of(std::make_move_iterator(elems.begin()), std::make_move_iterator(elems.end()), SequencePredicate{expr, logger}); - calcres = toValueExpr(res); + calcres = toany_expr(res); } void Calculator::visit(Error&) { unsupported(); } @@ -2959,19 +2959,19 @@ void Calculator::visit(Error&) { unsupported(); } void Calculator::visit(Var& n) { assert(n.num_evaluated_operands() >= 1); - AnyExpr elm = eval(n.operand(0)); + any_expr elm = eval(n.operand(0)); Value& val = down_cast(*elm); try { calcres = vars(val.toJson(), n.num()); } catch (...) { calcres = (n.num_evaluated_operands() > 1) ? eval(n.operand(1)) - : toValueExpr(nullptr); + : toany_expr(nullptr); } } std::size_t Calculator::missing_aux(Array& elems) { - auto avail = [calc = this](ValueExpr& v) -> bool { + auto avail = [calc = this](any_expr& v) -> bool { try { Value& val = down_cast(*v); @@ -2993,7 +2993,7 @@ std::size_t Calculator::missing_aux(Array& elems) { } void Calculator::visit(Missing& n) { - ValueExpr arg = eval(n.operand(0)); + any_expr arg = eval(n.operand(0)); auto nonArrayHandler = [&arg, &n, calc = this]() -> Array& { Array& res = deref(new Array); @@ -3015,8 +3015,8 @@ void Calculator::visit(Missing& n) { } void Calculator::visit(MissingSome& n) { - const std::uint64_t minreq = unpackValue(eval(n.operand(0))); - ValueExpr arr = eval(n.operand(1)); + const std::uint64_t minreq = unpack_value(eval(n.operand(0))); + any_expr arr = eval(n.operand(1)); Array& elems = down_cast(*arr); // evaluated elements std::size_t avail = missing_aux(elems); @@ -3029,7 +3029,7 @@ void Calculator::visit(If& n) { const int num = n.num_evaluated_operands(); if (num == 0) { - calcres = toValueExpr(nullptr); + calcres = toany_expr(nullptr); return; } @@ -3045,7 +3045,7 @@ void Calculator::visit(If& n) { pos += 2; } - calcres = (pos < num) ? eval(n.operand(pos)) : toValueExpr(nullptr); + calcres = (pos < num) ? eval(n.operand(pos)) : toany_expr(nullptr); } void Calculator::visit(Log& n) { @@ -3064,27 +3064,27 @@ void Calculator::visit(DoubleVal& n) { _value(n); } void Calculator::visit(StringVal& n) { _value(n); } CXX_MAYBE_UNUSED -ValueExpr calculate(Expr& exp, const Calculator::VarAccess& vars) { +any_expr calculate(Expr& exp, const Calculator::VarAccess& vars) { Calculator calc{vars, std::cerr}; return calc.eval(exp); } CXX_MAYBE_UNUSED -ValueExpr calculate(AnyExpr& exp, const Calculator::VarAccess& vars) { +any_expr calculate(any_expr& exp, const Calculator::VarAccess& vars) { assert(exp.get()); return calculate(*exp, vars); } CXX_MAYBE_UNUSED -ValueExpr calculate(AnyExpr& exp) { +any_expr calculate(any_expr& exp) { return calculate(exp, - [](const json::value&, int) -> ValueExpr { unsupported(); }); + [](const json::value&, int) -> any_expr { unsupported(); }); } -ValueExpr evalPath(const json::string& path, const json::object& obj) { +any_expr evalPath(const json::string& path, const json::object& obj) { if (auto pos = obj.find(path); pos != obj.end()) - return json_logic::toValueExpr(pos->value()); + return jsonlogic::toany_expr(pos->value()); if (std::size_t pos = path.find('.'); pos != json::string::npos) { json::string selector = path.subview(0, pos); @@ -3097,19 +3097,19 @@ ValueExpr evalPath(const json::string& path, const json::object& obj) { } template -ValueExpr evalIdx(IntT idx, const json::array& arr) { - return json_logic::toValueExpr(arr[idx]); +any_expr evalIdx(IntT idx, const json::array& arr) { + return jsonlogic::toany_expr(arr[idx]); } CXX_MAYBE_UNUSED -ValueExpr apply(json::value rule, json::value data) { +any_expr apply(json::value rule, json::value data) { auto [ast, vars, hasComputed] = translateNode(rule); Calculator::VarAccess varlookup = [data](const json::value& keyval, - int) -> ValueExpr { + int) -> any_expr { if (const json::string* ppath = keyval.if_string()) { //~ std::cerr << *ppath << std::endl; return ppath->size() ? evalPath(*ppath, data.as_object()) - : json_logic::toValueExpr(data); + : jsonlogic::toany_expr(data); } if (const std::int64_t* pidx = keyval.if_int64()) @@ -3135,7 +3135,7 @@ struct ValuePrinter : FwdVisitor { bool first = true; os << "["; - for (AnyExpr& el : n) { + for (any_expr& el : n) { if (first) first = false; else @@ -3151,7 +3151,7 @@ struct ValuePrinter : FwdVisitor { std::ostream& os; }; -std::ostream& operator<<(std::ostream& os, ValueExpr& n) { +std::ostream& operator<<(std::ostream& os, any_expr& n) { ValuePrinter prn{os}; deref(n).accept(prn); @@ -3169,7 +3169,7 @@ void traverseChildrenReverse(Visitor& v, const Operator& node); // only operators have children void _traverseChildren(Visitor& v, Operator::const_iterator aa, Operator::const_iterator zz) { - std::for_each(aa, zz, [&v](const AnyExpr& e) -> void { e->accept(v); }); + std::for_each(aa, zz, [&v](const any_expr& e) -> void { e->accept(v); }); } void traverseChildren(Visitor& v, const Operator& node) { @@ -3186,7 +3186,7 @@ void traverseChildrenReverse(Visitor& v, const Operator& node) { Operator::const_reverse_iterator zz = node.crend(); Operator::const_reverse_iterator aa = zz - node.num_evaluated_operands(); - std::for_each(aa, zz, [&v](const AnyExpr& e) -> void { e->accept(v); }); + std::for_each(aa, zz, [&v](const any_expr& e) -> void { e->accept(v); }); } namespace { diff --git a/include/clippy/clippy-ast.hpp b/include/clippy/clippy-ast.hpp deleted file mode 100644 index cdd2ee4..0000000 --- a/include/clippy/clippy-ast.hpp +++ /dev/null @@ -1,2300 +0,0 @@ - -#pragma once - -#include -#include -#include -#include -#include - -#include - -#include - -namespace json_logic -{ - constexpr bool DEBUG_OUTPUT = true; - - namespace json = boost::json; - - using JsonExpr = json::value; - - template - T& as(T& n) { return n; } - - template - T& deref(T* p) - { - assert(p); - return *p; - } - - struct Visitor; - - // the root class - struct Expr - { - Expr() = default; - virtual ~Expr() = default; - - virtual void accept(Visitor&) = 0; - - private: - Expr(Expr&&) = delete; - Expr(const Expr&) = delete; - Expr& operator=(Expr&&) = delete; - Expr& operator=(const Expr&) = delete; - }; - - //~ Expr::~Expr() {} - - // - // foundation classes - // \{ - - using AnyExpr = std::unique_ptr; - - struct Operator : Expr, private std::vector - { - using container_type = std::vector; - - using container_type::iterator; - using container_type::const_iterator; - using container_type::const_reverse_iterator; - using container_type::reverse_iterator; - using container_type::begin; - using container_type::end; - using container_type::rbegin; - using container_type::rend; - using container_type::crbegin; - using container_type::crend; - using container_type::back; - using container_type::push_back; - - void set_operands(container_type&& opers) - { - this->swap(opers); - } - - Expr& operand(int n) const - { - return deref(this->at(n).get()); - } - - virtual int num_evaluated_operands() const; - }; - - // defines operators that have an upper bound on how many - // arguments are evaluated. - template - struct OperatorN : Operator - { - enum { MAX_OPERANDS = MaxArity }; - - int num_evaluated_operands() const final; - }; - - template - struct Value : Expr - { - using value_type = T; - - explicit - Value(T t) - : val(std::move(t)) - {} - - T& value() { return val; } - const T& value() const { return val; } - - private: - T val; - }; - - // \} - - // - // expression hierarchy - // comparison - - // binary - struct Eq : OperatorN<2> - { - void accept(Visitor&) final; - }; - - struct StrictEq : OperatorN<2> - { - void accept(Visitor&) final; - }; - - struct Neq : OperatorN<2> - { - void accept(Visitor&) final; - }; - - struct StrictNeq : OperatorN<2> - { - void accept(Visitor&) final; - }; - - // binary or ternary - struct Less : OperatorN<3> - { - void accept(Visitor&) final; - }; - - struct Greater : OperatorN<3> - { - void accept(Visitor&) final; - }; - - struct Leq : OperatorN<3> - { - void accept(Visitor&) final; - }; - - struct Geq : OperatorN<3> - { - void accept(Visitor&) final; - }; - - // logical operators - - // unary - struct Not : OperatorN<1> - { - void accept(Visitor&) final; - }; - - struct NotNot : OperatorN<1> - { - void accept(Visitor&) final; - }; - - // n-ary - struct And : Operator - { - void accept(Visitor&) final; - }; - - struct Or : Operator - { - void accept(Visitor&) final; - }; - - - // control structure - struct If : Operator - { - void accept(Visitor&) final; - }; - - // arithmetics - // n-ary - struct Add : Operator - { - void accept(Visitor&) final; - }; - - struct Mul : Operator - { - void accept(Visitor&) final; - }; - - struct Min : Operator - { - void accept(Visitor&) final; - }; - - struct Max : Operator - { - void accept(Visitor&) final; - }; - - - // binary - struct Sub : OperatorN<2> - { - void accept(Visitor&) final; - }; - - struct Div : OperatorN<2> - { - void accept(Visitor&) final; - }; - - struct Mod : OperatorN<2> - { - void accept(Visitor&) final; - }; - - - // array - struct Array : Operator // array is modeled as operator - { - void accept(Visitor&) final; - }; - - struct Map : OperatorN<2> - { - void accept(Visitor&) final; - }; - - struct Reduce : OperatorN<3> - { - void accept(Visitor&) final; - }; - - struct Filter : OperatorN<2> - { - void accept(Visitor&) final; - }; - - struct All : OperatorN<2> - { - void accept(Visitor&) final; - }; - - struct None : OperatorN<2> - { - void accept(Visitor&) final; - }; - - struct Some : OperatorN<2> - { - void accept(Visitor&) final; - }; - - struct Merge : Operator - { - void accept(Visitor&) final; - }; - - - // string operations - struct Var : OperatorN<1> - { - enum { computed = -1 }; - - void accept(Visitor&) final; - - void num(int val) { idx = val; } - int num() const { return idx; } - - private: - int idx = computed; - }; - - struct Cat : Operator - { - void accept(Visitor&) final; - }; - - struct Substr : OperatorN<3> - { - void accept(Visitor&) final; - }; - - - // string and array operation - struct In : Operator - { - void accept(Visitor&) final; - }; - - - // values - struct NullVal : Expr - { - void accept(Visitor&) final; - - std::nullptr_t value() const { return nullptr; } - }; - - struct BoolVal : Value - { - using base = Value; - using base::base; - - void accept(Visitor&) final; - }; - - struct IntVal : Value - { - using base = Value; - using base::base; - - void accept(Visitor&) final; - }; - - struct UintVal : Value - { - using base = Value; - using base::base; - - void accept(Visitor&) final; - }; - - struct DoubleVal : Value - { - using base = Value; - using base::base; - - void accept(Visitor&) final; - }; - - struct StringVal : Value - { - using base = Value; - using base::base; - - void accept(Visitor&) final; - }; - - // logger - struct Log : OperatorN<1> - { - void accept(Visitor&) final; - }; - - // error node - struct Error : Expr - { - void accept(Visitor&) final; - }; - - - // Visitor - struct Visitor - { - virtual void visit(Expr&) = 0; // error - virtual void visit(Operator& n) = 0; - virtual void visit(Eq&) = 0; - virtual void visit(StrictEq&) = 0; - virtual void visit(Neq&) = 0; - virtual void visit(StrictNeq&) = 0; - virtual void visit(Less&) = 0; - virtual void visit(Greater&) = 0; - virtual void visit(Leq&) = 0; - virtual void visit(Geq&) = 0; - virtual void visit(And&) = 0; - virtual void visit(Or&) = 0; - virtual void visit(Not&) = 0; - virtual void visit(NotNot&) = 0; - virtual void visit(Add&) = 0; - virtual void visit(Sub&) = 0; - virtual void visit(Mul&) = 0; - virtual void visit(Div&) = 0; - virtual void visit(Mod&) = 0; - virtual void visit(Min&) = 0; - virtual void visit(Max&) = 0; - virtual void visit(Map&) = 0; - virtual void visit(Reduce&) = 0; - virtual void visit(Filter&) = 0; - virtual void visit(All&) = 0; - virtual void visit(None&) = 0; - virtual void visit(Some&) = 0; - virtual void visit(Array&) = 0; - virtual void visit(Merge&) = 0; - virtual void visit(Cat&) = 0; - virtual void visit(Substr&) = 0; - virtual void visit(In&) = 0; - virtual void visit(Var&) = 0; - virtual void visit(Log&) = 0; - - // control structure - virtual void visit(If&) = 0; - - // values - virtual void visit(NullVal&) = 0; - virtual void visit(BoolVal&) = 0; - virtual void visit(IntVal&) = 0; - virtual void visit(UintVal&) = 0; - virtual void visit(DoubleVal&) = 0; - virtual void visit(StringVal&) = 0; - //~ virtual void visit(ObjectVal&) = 0; - - virtual void visit(Error&) = 0; - }; - - - struct FwdVisitor : Visitor - { - void visit(Expr&) override; - void visit(Operator& n) override { visit(as(n)); } - void visit(Eq& n) override { visit(as(n)); } - void visit(StrictEq& n) override { visit(as(n)); } - void visit(Neq& n) override { visit(as(n)); } - void visit(StrictNeq& n) override { visit(as(n)); } - void visit(Less& n) override { visit(as(n)); } - void visit(Greater& n) override { visit(as(n)); } - void visit(Leq& n) override { visit(as(n)); } - void visit(Geq& n) override { visit(as(n)); } - void visit(And& n) override { visit(as(n)); } - void visit(Or& n) override { visit(as(n)); } - void visit(Not& n) override { visit(as(n)); } - void visit(NotNot& n) override { visit(as(n)); } - void visit(Add& n) override { visit(as(n)); } - void visit(Sub& n) override { visit(as(n)); } - void visit(Mul& n) override { visit(as(n)); } - void visit(Div& n) override { visit(as(n)); } - void visit(Mod& n) override { visit(as(n)); } - void visit(Min& n) override { visit(as(n)); } - void visit(Max& n) override { visit(as(n)); } - void visit(Map& n) override { visit(as(n)); } - void visit(Reduce& n) override { visit(as(n)); } - void visit(Filter& n) override { visit(as(n)); } - void visit(All& n) override { visit(as(n)); } - void visit(None& n) override { visit(as(n)); } - void visit(Some& n) override { visit(as(n)); } - void visit(Array& n) override { visit(as(n)); } - void visit(Merge& n) override { visit(as(n)); } - void visit(Cat& n) override { visit(as(n)); } - void visit(Substr& n) override { visit(as(n)); } - void visit(In& n) override { visit(as(n)); } - void visit(Var& n) override { visit(as(n)); } - void visit(Log& n) override { visit(as(n)); } - - void visit(If& n) override { visit(as(n)); } - - void visit(NullVal& n) override { visit(as(n)); } - void visit(BoolVal& n) override { visit(as(n)); } - void visit(IntVal& n) override { visit(as(n)); } - void visit(UintVal& n) override { visit(as(n)); } - void visit(DoubleVal& n) override { visit(as(n)); } - void visit(StringVal& n) override { visit(as(n)); } - - void visit(Error& n) override { visit(as(n)); } - }; - - /// AST traversal function that calls v's visit methods in post-fix order - void traverseInSAttributeOrder(Expr& e, Visitor& vis); - - /// traverses the children of a node; does not traverse grandchildren - void traverseChildren(Visitor& v, const Operator& node); - void traverseAllChildren(Visitor& v, const Operator& node); - void traverseChildrenReverse(Visitor& v, const Operator& node); - - namespace - { - CXX_NORETURN - void unsupported() - { - throw std::logic_error("not yet implemented"); - } - - CXX_NORETURN - void typeError() - { - throw std::runtime_error("typing error"); - } - - struct VarMap - { - void insert(Var& el); - std::vector toVector() const; - bool hasComputedVariables() const { return hasComputed; } - - private: - using container_type = std::map; - - container_type mapping = {}; - bool hasComputed = false; - }; - - void VarMap::insert(Var& var) - { - AnyExpr& arg = var.back(); - StringVal* str = dynamic_cast(arg.get()); - - if (str == nullptr) - { - CXX_UNLIKELY; - hasComputed = true; - return; - } - - // do nothing for free variables in "lambdas" - if (str->value() == "") - return; - - auto [pos, success] = mapping.emplace(str->value(), mapping.size()); - - var.num(pos->second); - } - - std::vector - VarMap::toVector() const - { - std::vector res; - - res.resize(mapping.size()); - - for (const container_type::value_type& el : mapping) - res.at(el.second) = el.first; - - return res; - } - - - /// translates all children - /// \{ - Operator::container_type - translateChildren(json::array& children, VarMap&); - - Operator::container_type - translateChildren(JsonExpr& n, VarMap&); - /// \} - - - template - Expr& mkOperator(json::object& n, VarMap& m) - { - assert(n.size() == 1); - - ExprT& res = deref(new ExprT); - - res.set_operands(translateChildren(n.begin()->value(), m)); - return res; - } - - Array& mkArrayOperator(json::array& children, VarMap& m) - { - Array& res = deref(new Array); - - res.set_operands(translateChildren(children, m)); - return res; - } - - template - ValueT& mkValue(typename ValueT::value_type n) - { - return deref(new ValueT(std::move(n))); - } - - NullVal& mkNullValue() - { - return deref(new NullVal); - } - - using DispatchTable = std::map; - - DispatchTable::const_iterator - lookup(const DispatchTable& m, const json::object& op) - { - if (op.size() != 1) return m.end(); - - return m.find(op.begin()->key()); - } - - - AnyExpr - translateNode_internal(JsonExpr& n, VarMap& varmap) - { - static const DispatchTable dt = { { "==", &mkOperator }, - { "===", &mkOperator }, - { "!=", &mkOperator }, - { "!==", &mkOperator }, - { "if", &mkOperator }, - { "!", &mkOperator }, - { "!!", &mkOperator }, - { "or", &mkOperator }, - { "and", &mkOperator }, - { ">", &mkOperator }, - { ">=", &mkOperator }, - { "<", &mkOperator }, - { "<=", &mkOperator }, - { "max", &mkOperator }, - { "min", &mkOperator }, - { "+", &mkOperator }, - { "-", &mkOperator }, - { "*", &mkOperator }, - { "/", &mkOperator
}, - { "%", &mkOperator }, - { "map", &mkOperator }, - { "reduce", &mkOperator }, - { "filter", &mkOperator }, - { "all", &mkOperator }, - { "none", &mkOperator }, - { "some", &mkOperator }, - { "merge", &mkOperator }, - { "in", &mkOperator }, - { "cat", &mkOperator }, - { "log", &mkOperator }, - { "var", &mkOperator } - }; - - Expr* res = nullptr; - - switch (n.kind()) - { - case json::kind::object: - { - json::object& obj = n.get_object(); - DispatchTable::const_iterator pos = lookup(dt, obj); - - if (pos != dt.end()) - { - CXX_LIKELY; - res = &pos->second(obj, varmap); - - if (pos->second == mkOperator) - varmap.insert(dynamic_cast(*res)); - } - else - { - // does json_logic support value objects? - unsupported(); - } - - break; - } - - case json::kind::array: - { - // array is an operator that combines its subexpressions into an array - res = &mkArrayOperator(n.get_array(), varmap); - break; - } - - case json::kind::string: - { - res = &mkValue(std::move(n.get_string())); - break; - } - - case json::kind::int64: - { - res = &mkValue(n.get_int64()); - break; - } - - case json::kind::uint64: - { - res = &mkValue(n.get_uint64()); - break; - } - - case json::kind::double_: - { - res = &mkValue(n.get_double()); - break; - } - - case json::kind::bool_: - { - res = &mkValue(n.get_bool()); - break; - } - - case json::kind::null: - { - res = &mkNullValue(); - break; - } - - default: - unsupported(); - } - - return std::unique_ptr(res); - } - - - std::tuple, bool> - translateNode(JsonExpr& n) - { - VarMap varmap; - AnyExpr node = translateNode_internal(n, varmap); - bool hasComputedVariables = varmap.hasComputedVariables(); - - return std::make_tuple(std::move(node), varmap.toVector(), hasComputedVariables); - } - - Operator::container_type - translateChildren(json::array& children, VarMap& varmap) - { - Operator::container_type res; - - res.reserve(children.size()); - - for (JsonExpr& elem : children) - res.emplace_back(translateNode_internal(elem, varmap)); - - return res; - } - - Operator::container_type - translateChildren(JsonExpr& n, VarMap& varmap) - { - if (json::array* arr = n.if_array()) - { - CXX_LIKELY; - return translateChildren(*arr, varmap); - } - - Operator::container_type res; - - res.emplace_back(translateNode_internal(n, varmap)); - return res; - } - } - - // only operators have children - void _traverseChildren(Visitor& v, Operator::const_iterator aa, Operator::const_iterator zz) - { - std::for_each( aa, zz, - [&v](const AnyExpr& e) -> void - { - e->accept(v); - } - ); - } - - void traverseChildren(Visitor& v, const Operator& node) - { - Operator::const_iterator aa = node.begin(); - - _traverseChildren(v, aa, aa + node.num_evaluated_operands()); - } - - void traverseAllChildren(Visitor& v, const Operator& node) - { - _traverseChildren(v, node.begin(), node.end()); - } - - void traverseChildrenReverse(Visitor& v, const Operator& node) - { - Operator::const_reverse_iterator zz = node.crend(); - Operator::const_reverse_iterator aa = zz - node.num_evaluated_operands(); - - std::for_each( aa, zz, - [&v](const AnyExpr& e) -> void - { - e->accept(v); - } - ); - } - - - namespace - { - struct SAttributeTraversal : Visitor - { - explicit - SAttributeTraversal(Visitor& client) - : sub(client) - {} - - void visit(Expr&) final; - void visit(Operator&) final; - void visit(Eq&) final; - void visit(StrictEq&) final; - void visit(Neq&) final; - void visit(StrictNeq&) final; - void visit(Less&) final; - void visit(Greater&) final; - void visit(Leq&) final; - void visit(Geq&) final; - void visit(And&) final; - void visit(Or&) final; - void visit(Not&) final; - void visit(NotNot&) final; - void visit(Add&) final; - void visit(Sub&) final; - void visit(Mul&) final; - void visit(Div&) final; - void visit(Mod&) final; - void visit(Min&) final; - void visit(Max&) final; - void visit(Map&) final; - void visit(Reduce&) final; - void visit(Filter&) final; - void visit(All&) final; - void visit(None&) final; - void visit(Some&) final; - void visit(Merge&) final; - void visit(Cat&) final; - void visit(Substr&) final; - void visit(In&) final; - void visit(Array& n) final; - void visit(Var&) final; - void visit(Log&) final; - - void visit(If&) final; - - void visit(NullVal& n) final; - void visit(BoolVal& n) final; - void visit(IntVal& n) final; - void visit(UintVal& n) final; - void visit(DoubleVal& n) final; - void visit(StringVal& n) final; - - void visit(Error& n) final; - - private: - Visitor& sub; - - template - inline - void _visit(OperatorNode& n) - { - traverseChildren(*this, n); - sub.visit(n); - } - - template - inline - void _value(ValueNode& n) - { - sub.visit(n); - } - }; - - void SAttributeTraversal::visit(Expr&) { typeError(); } - void SAttributeTraversal::visit(Operator&) { typeError(); } - void SAttributeTraversal::visit(Eq& n) { _visit(n); } - void SAttributeTraversal::visit(StrictEq& n) { _visit(n); } - void SAttributeTraversal::visit(Neq& n) { _visit(n); } - void SAttributeTraversal::visit(StrictNeq& n) { _visit(n); } - void SAttributeTraversal::visit(Less& n) { _visit(n); } - void SAttributeTraversal::visit(Greater& n) { _visit(n); } - void SAttributeTraversal::visit(Leq& n) { _visit(n); } - void SAttributeTraversal::visit(Geq& n) { _visit(n); } - void SAttributeTraversal::visit(And& n) { _visit(n); } - void SAttributeTraversal::visit(Or& n) { _visit(n); } - void SAttributeTraversal::visit(Not& n) { _visit(n); } - void SAttributeTraversal::visit(NotNot& n) { _visit(n); } - void SAttributeTraversal::visit(Add& n) { _visit(n); } - void SAttributeTraversal::visit(Sub& n) { _visit(n); } - void SAttributeTraversal::visit(Mul& n) { _visit(n); } - void SAttributeTraversal::visit(Div& n) { _visit(n); } - void SAttributeTraversal::visit(Mod& n) { _visit(n); } - void SAttributeTraversal::visit(Min& n) { _visit(n); } - void SAttributeTraversal::visit(Max& n) { _visit(n); } - void SAttributeTraversal::visit(Array& n) { _visit(n); } - void SAttributeTraversal::visit(Map& n) { _visit(n); } - void SAttributeTraversal::visit(Reduce& n) { _visit(n); } - void SAttributeTraversal::visit(Filter& n) { _visit(n); } - void SAttributeTraversal::visit(All& n) { _visit(n); } - void SAttributeTraversal::visit(None& n) { _visit(n); } - void SAttributeTraversal::visit(Some& n) { _visit(n); } - void SAttributeTraversal::visit(Merge& n) { _visit(n); } - void SAttributeTraversal::visit(Cat& n) { _visit(n); } - void SAttributeTraversal::visit(Substr& n) { _visit(n); } - void SAttributeTraversal::visit(In& n) { _visit(n); } - void SAttributeTraversal::visit(Var& n) { _visit(n); } - void SAttributeTraversal::visit(Log& n) { _visit(n); } - - void SAttributeTraversal::visit(If& n) { _visit(n); } - - void SAttributeTraversal::visit(NullVal& n) { _value(n); } - void SAttributeTraversal::visit(BoolVal& n) { _value(n); } - void SAttributeTraversal::visit(IntVal& n) { _value(n); } - void SAttributeTraversal::visit(UintVal& n) { _value(n); } - void SAttributeTraversal::visit(DoubleVal& n) { _value(n); } - void SAttributeTraversal::visit(StringVal& n) { _value(n); } - - void SAttributeTraversal::visit(Error& n) { sub.visit(n); } - } - - using ValueExpr = std::unique_ptr; // could be value if we have a type for that - - std::ostream& operator<<(std::ostream& os, ValueExpr& n); - - ValueExpr toValueExpr(std::nullptr_t) { return ValueExpr(new NullVal); } - ValueExpr toValueExpr(bool val) { return ValueExpr(new BoolVal(val)); } - ValueExpr toValueExpr(std::int64_t val) { return ValueExpr(new IntVal(val)); } - ValueExpr toValueExpr(std::uint64_t val) { return ValueExpr(new UintVal(val)); } - ValueExpr toValueExpr(double val) { return ValueExpr(new DoubleVal(val)); } - ValueExpr toValueExpr(json::string val) { return ValueExpr(new StringVal(std::move(val))); } - - // - // coercion functions - - std::int64_t toInt(const json::string& str) - { - return std::stoi(std::string{str.c_str()}); - } - - std::int64_t toInt(double d) - { - return d; - } - - std::int64_t toInt(bool b) - { - return b; - } - - std::int64_t toInt(std::uint64_t v) - { - return v; - } - - std::uint64_t toUint(const json::string& str) - { - return std::stoull(std::string{str.c_str()}); - } - - std::uint64_t toUint(double d) - { - return d; - } - - std::uint64_t toUint(std::int64_t v) - { - return v; - } - - std::uint64_t toUint(bool b) - { - return b; - } - - double toDouble(const json::string& str) - { - return std::stod(std::string{str.c_str()}); - } - - double toDouble(std::int64_t val) - { - return val; - } - - double toDouble(std::uint64_t val) - { - return val; - } - - - template - json::string toString(Val v) - { - return json::string{std::to_string(v)}; - } - - json::string toString(bool b) - { - return json::string{b ? "true" : "false"}; - } - - json::string toString(std::nullptr_t) - { - return json::string{"null"}; - } - - /// conversion to boolean - /// \{ - - bool toBool(std::int64_t v) { return v; } - bool toBool(std::uint64_t v) { return v; } - bool toBool(double v) { return v; } - bool toBool(const json::string& v) { return v.size() != 0; } - bool toBool(Array& v) { return v.num_evaluated_operands(); } - - bool toBool(Expr& e) - { - struct BoolConverter : FwdVisitor - { - void visit(Expr&) final { typeError(); } - - void visit(NullVal&) final { res = false; } - void visit(BoolVal& n) final { res = n.value(); } - void visit(IntVal& n) final { res = toBool(n.value()); } - void visit(UintVal& n) final { res = toBool(n.value()); } - void visit(DoubleVal& n) final { res = toBool(n.value()); } - void visit(StringVal& n) final { res = toBool(n.value()); } - void visit(Array& n) final { res = toBool(n); } - - bool res; - }; - - BoolConverter conv; - - e.accept(conv); - return conv.res; - } - - bool toBool(ValueExpr& el) - { - return toBool(*el); - } - - /// \} - - - struct LogicalOperatorBase - { - enum - { - definedForString = true, - definedForDouble = true, - definedForInteger = true, - definedForBool = false, - definedForNull = false - }; - - using result_type = bool; - }; - - struct NoCoercion {}; - - /// \brief a strict binary operator operates on operands of the same - /// type. The operation on two different types returns false. - /// NO type coercion is performed. - struct StrictLogicalBinaryOperator : LogicalOperatorBase - { - template - std::tuple - coerce(LhsT* lv, RhsT* rv) - { - return std::make_tuple(std::move(*lv), std::move(*rv)); - } - }; - - struct NumericBinaryOperatorBase - { - std::tuple - coerce(double* lv, double* rv) - { - return std::make_tuple(*lv, *rv); - } - - std::tuple - coerce(double* lv, std::int64_t* rv) - { - return std::make_tuple(*lv, toDouble(*rv)); - } - - std::tuple - coerce(double* lv, std::uint64_t* rv) - { - return std::make_tuple(*lv, toDouble(*rv)); - } - - std::tuple - coerce(std::int64_t* lv, double* rv) - { - return std::make_tuple(toDouble(*lv), *rv); - } - - std::tuple - coerce(std::int64_t* lv, std::int64_t* rv) - { - return std::make_tuple(*lv, *rv); - } - - std::tuple - coerce(std::int64_t* lv, std::uint64_t* rv) - { - return std::make_tuple(*lv, toInt(*rv)); - } - - std::tuple - coerce(std::uint64_t* lv, double* rv) - { - return std::make_tuple(toDouble(*lv), *rv); - } - - std::tuple - coerce(std::uint64_t* lv, std::int64_t* rv) - { - return std::make_tuple(toInt(*lv), *rv); - } - - std::tuple - coerce(std::uint64_t* lv, std::uint64_t* rv) - { - return std::make_tuple(*lv, *rv); - } - }; - - - /// \brief a logical binary operator compares two values. If the - /// values have a different type, type coercion is performed - /// on one of the operands. - struct LogicalBinaryOperator : NumericBinaryOperatorBase, LogicalOperatorBase - { - using NumericBinaryOperatorBase::coerce; - - std::tuple - coerce(double* lv, json::string* rv) - { - return std::make_tuple(*lv, toDouble(*rv)); - } - - std::tuple - coerce(std::int64_t* lv, json::string* rv) - { - return std::make_tuple(*lv, toInt(*rv)); - } - - std::tuple - coerce(std::uint64_t* lv, json::string* rv) - { - return std::make_tuple(*lv, toUint(*rv)); - } - - std::tuple - coerce(json::string* lv, double* rv) - { - return std::make_tuple(toDouble(*lv), *rv); - } - - std::tuple - coerce(json::string* lv, std::int64_t* rv) - { - return std::make_tuple(toInt(*lv), *rv); - } - - std::tuple - coerce(json::string* lv, std::uint64_t* rv) - { - return std::make_tuple(toInt(*lv), *rv); - } - - std::tuple - coerce(json::string* lv, json::string* rv) - { - return std::make_tuple(std::move(*lv), std::move(*rv)); - } - }; - // @} - - // Arith - struct ArithmeticOperator : NumericBinaryOperatorBase - { - enum - { - definedForString = false, - definedForDouble = true, - definedForInteger = true, - definedForBool = false, - definedForNull = true - }; - - using result_type = ValueExpr; - - using NumericBinaryOperatorBase::coerce; - - std::tuple - coerce(double*, std::nullptr_t) - { - return std::make_tuple(nullptr, nullptr); - } - - std::tuple - coerce(std::int64_t*, std::nullptr_t) - { - return std::make_tuple(nullptr, nullptr); - } - - std::tuple - coerce(std::uint64_t*, std::nullptr_t) - { - return std::make_tuple(nullptr, nullptr); - } - - std::tuple - coerce(std::nullptr_t, double*) - { - return std::make_tuple(nullptr, nullptr); - } - - std::tuple - coerce(std::nullptr_t, std::int64_t*) - { - return std::make_tuple(nullptr, nullptr); - } - - std::tuple - coerce(std::nullptr_t, std::uint64_t*) - { - return std::make_tuple(nullptr, nullptr); - } - - std::tuple - coerce(std::nullptr_t, std::nullptr_t) - { - return std::make_tuple(nullptr, nullptr); - } - }; - - struct IntegerArithmeticOperator : ArithmeticOperator - { - enum - { - definedForString = false, - definedForDouble = false, - definedForInteger = true, - definedForBool = false, - definedForNull = false - }; - - using ArithmeticOperator::coerce; - }; - - struct StringOperator - { - enum - { - definedForString = true, - definedForDouble = false, - definedForInteger = false, - definedForBool = false, - definedForNull = false - }; - - using result_type = ValueExpr; - - std::tuple - coerce(json::string* lv, json::string* rv) - { - return std::make_tuple(std::move(*lv), std::move(*rv)); - } - }; - - - AnyExpr convert(AnyExpr val, ...) - { - return val; - } - - - AnyExpr convert(AnyExpr val, const ArithmeticOperator&) - { - struct ArithmeticConverter : FwdVisitor - { - explicit - ArithmeticConverter(AnyExpr val) - : res(std::move(val)) - {} - - void visit(Expr&) final { typeError(); } - - // defined for the following types - void visit(IntVal&) final {} - void visit(UintVal&) final {} - void visit(DoubleVal&) final {} - void visit(NullVal&) final {} - - // need to convert values - void visit(StringVal& el) final - { - double dd = toDouble(el.value()); - int64_t ii = toInt(el.value()); - // uint? - - res = (dd != ii) ? toValueExpr(dd) : toValueExpr(ii); - } - - void visit(BoolVal&) final - { - // \todo correct? - res = toValueExpr(nullptr); - } - - AnyExpr result() && { return std::move(res); } - private: - AnyExpr res; - }; - - Expr* node = val.get(); - ArithmeticConverter conv{std::move(val)}; - - node->accept(conv); - return std::move(conv).result(); - } - - AnyExpr convert(AnyExpr val, const IntegerArithmeticOperator&) - { - struct IntegerArithmeticConverter : FwdVisitor - { - explicit - IntegerArithmeticConverter(AnyExpr val) - : res(std::move(val)) - {} - - void visit(Expr&) final { typeError(); } - - // defined for the following types - void visit(IntVal&) final {} - void visit(UintVal&) final {} - - // need to convert values - void visit(StringVal& el) final - { - res = toValueExpr(toInt(el.value())); - } - - void visit(BoolVal& el) final - { - res = toValueExpr(toInt(el.value())); - } - - void visit(DoubleVal& el) final - { - res = toValueExpr(toInt(el.value())); - } - - void visit(NullVal&) final - { - res = toValueExpr(std::int64_t(0)); - } - - AnyExpr result() && { return std::move(res); } - - private: - AnyExpr res; - }; - - Expr* node = val.get(); - IntegerArithmeticConverter conv{std::move(val)}; - - node->accept(conv); - return std::move(conv).result(); - } - - AnyExpr convert(AnyExpr val, const StringOperator&) - { - struct StringConverter : FwdVisitor - { - explicit - StringConverter(AnyExpr val) - : res(std::move(val)) - {} - - void visit(Expr&) final { typeError(); } - - // defined for the following types - void visit(StringVal&) final {} - - - // need to convert values - void visit(BoolVal& el) final - { - res = toValueExpr(toString(el.value())); - } - - void visit(IntVal& el) final - { - res = toValueExpr(toString(el.value())); - } - - void visit(UintVal& el) final - { - res = toValueExpr(toString(el.value())); - } - - void visit(DoubleVal& el) final - { - res = toValueExpr(toString(el.value())); - } - - void visit(NullVal& el) final - { - res = toValueExpr(toString(el.value())); - } - - AnyExpr result() && { return std::move(res); } - - private: - AnyExpr res; - }; - - Expr* node = val.get(); - StringConverter conv{std::move(val)}; - - node->accept(conv); - return std::move(conv).result(); - } - - template - struct BinaryOperatorVisitor_ : FwdVisitor - { - using result_type = typename BinaryOperator::result_type; - - BinaryOperatorVisitor_(LhsValue lval, BinaryOperator oper) - : lv(lval), op(oper), res() - {} - - template - void calc(RhsValue rv) - { - auto [ll, rr] = op.coerce(lv, rv); - - res = op(ll, rr); - } - - void visit(Expr&) final { typeError(); } - - void visit(StringVal& n) final - { - if constexpr (BinaryOperator::definedForString) - return calc(&n.value()); - - typeError(); - } - - void visit(NullVal&) final - { - if constexpr (BinaryOperator::definedForNull) - return calc(nullptr); - - typeError(); - } - - void visit(BoolVal& n) final - { - if constexpr (BinaryOperator::definedForBool) - return calc(&n.value()); - - typeError(); - } - - void visit(IntVal& n) final - { - if constexpr (BinaryOperator::definedForInteger) - return calc(&n.value()); - - typeError(); - } - - void visit(UintVal& n) final - { - if constexpr (BinaryOperator::definedForInteger) - return calc(&n.value()); - - typeError(); - } - - void visit(DoubleVal& n) final - { - if constexpr (BinaryOperator::definedForDouble) - return calc(&n.value()); - - typeError(); - } - - void visit(Array&) final - { - unsupported(); // for now - } - - result_type result() && { return std::move(res); } - - private: - LhsValue lv; - BinaryOperator op; - result_type res; - }; - - template - struct BinaryOperatorVisitor : FwdVisitor - { - using result_type = typename BinaryOperator::result_type; - - BinaryOperatorVisitor(BinaryOperator oper, ValueExpr& rhsarg) - : op(oper), rhs(rhsarg), res() - {} - - template - void calc(LhsValue lv) - { - using RhsVisitor = BinaryOperatorVisitor_; - - RhsVisitor vis{lv, op}; - - rhs->accept(vis); - res = std::move(vis).result(); - } - - void visit(StringVal& n) final - { - if constexpr (BinaryOperator::definedForString) - return calc(&n.value()); - - typeError(); - } - - void visit(NullVal&) final - { - if constexpr (BinaryOperator::definedForNull) - return calc(nullptr); - - typeError(); - } - - void visit(BoolVal& n) final - { - if constexpr (BinaryOperator::definedForBool) - return calc(&n.value()); - - typeError(); - } - - void visit(IntVal& n) final - { - if constexpr (BinaryOperator::definedForInteger) - return calc(&n.value()); - - typeError(); - } - - void visit(UintVal& n) final - { - if constexpr (BinaryOperator::definedForInteger) - return calc(&n.value()); - - typeError(); - } - - void visit(DoubleVal& n) final - { - if constexpr (BinaryOperator::definedForDouble) - return calc(&n.value()); - - typeError(); - } - - void visit(Array&) final - { - typeError(); // for now - } - - result_type result() && { return std::move(res); } - - private: - BinaryOperator op; - ValueExpr& rhs; - result_type res; - }; - - template - typename BinaryOperator::result_type - compute(ValueExpr& lhs, ValueExpr& rhs, BinaryOperator op) - { - using LhsVisitor = BinaryOperatorVisitor; - - LhsVisitor vis{op, rhs}; - - assert(lhs.get()); - lhs->accept(vis); - return std::move(vis).result(); - } - - - - template - struct Calc {}; - - - template <> - struct Calc : LogicalBinaryOperator - { - using LogicalBinaryOperator::result_type; - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - return lhs == rhs; - } - }; - - template <> - struct Calc : LogicalBinaryOperator - { - using LogicalBinaryOperator::result_type; - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - return lhs != rhs; - } - }; - - template <> - struct Calc : StrictLogicalBinaryOperator - { - using StrictLogicalBinaryOperator::result_type; - - result_type operator()(...) const { return false; } // type mismatch - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - return lhs == rhs; - } - }; - - template <> - struct Calc : StrictLogicalBinaryOperator - { - using StrictLogicalBinaryOperator::result_type; - - result_type operator()(...) const { return false; } // type mismatch - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - return lhs == rhs; - } - }; - - template <> - struct Calc : LogicalBinaryOperator - { - using LogicalBinaryOperator::result_type; - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - return lhs < rhs; - } - }; - - template <> - struct Calc : LogicalBinaryOperator - { - using LogicalBinaryOperator::result_type; - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - return rhs < lhs; - } - }; - - template <> - struct Calc : LogicalBinaryOperator - { - using LogicalBinaryOperator::result_type; - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - return lhs <= rhs; - } - }; - - template <> - struct Calc : LogicalBinaryOperator - { - using LogicalBinaryOperator::result_type; - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - return rhs <= lhs; - } - }; - - template <> - struct Calc : ArithmeticOperator - { - using ArithmeticOperator::result_type; - - result_type - operator()(std::nullptr_t, std::nullptr_t) const { return toValueExpr(nullptr); } - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - return toValueExpr(lhs + rhs); - } - }; - - template <> - struct Calc : ArithmeticOperator - { - using ArithmeticOperator::result_type; - - result_type - operator()(std::nullptr_t, std::nullptr_t) const { return toValueExpr(nullptr); } - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - return toValueExpr(lhs - rhs); - } - }; - - template <> - struct Calc : ArithmeticOperator - { - using ArithmeticOperator::result_type; - - result_type - operator()(std::nullptr_t, std::nullptr_t) const { return toValueExpr(nullptr); } - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - return toValueExpr(lhs * rhs); - } - }; - - template <> - struct Calc
: ArithmeticOperator - { - using ArithmeticOperator::result_type; - - result_type - operator()(std::nullptr_t, std::nullptr_t) const { return toValueExpr(nullptr); } - - result_type - operator()(double lhs, double rhs) const - { - double res = lhs / rhs; - - // if (isInteger(res)) return toInt(res); - return toValueExpr(res); - } - - template - result_type - operator()(Int_t lhs, Int_t rhs) const - { - if (lhs % rhs) return (*this)(double(lhs), double(rhs)); - - return toValueExpr(lhs / rhs); - } - }; - - template <> - struct Calc : IntegerArithmeticOperator - { - using IntegerArithmeticOperator::result_type; - - std::nullptr_t - operator()(std::nullptr_t, std::nullptr_t) const { return nullptr; } - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - if (rhs == 0) return toValueExpr(nullptr); - - return toValueExpr(lhs % rhs); - } - }; - - template <> - struct Calc : ArithmeticOperator - { - using ArithmeticOperator::result_type; - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - return toValueExpr(std::min(lhs, rhs)); - } - }; - - template <> - struct Calc : ArithmeticOperator - { - using ArithmeticOperator::result_type; - - template - result_type - operator()(const T& lhs, const T& rhs) const - { - return toValueExpr(std::max(lhs, rhs)); - } - }; - - template <> - struct Calc - { - using result_type = bool; - - result_type - operator()(Expr& val) const - { - return !toBool(val); - } - }; - - template <> - struct Calc - { - using result_type = bool; - - result_type - operator()(Expr& val) const - { - return toBool(val); - } - }; - - template <> - struct Calc : StringOperator - { - using StringOperator::result_type; - - result_type - operator()(const json::string& lhs, const json::string& rhs) const - { - json::string tmp; - - tmp.reserve(lhs.size()+rhs.size()); - - tmp.append(lhs.begin(), lhs.end()); - tmp.append(rhs.begin(), rhs.end()); - - return toValueExpr(std::move(tmp)); - } - }; - - struct Calculator : FwdVisitor - { - using VarAccess = std::function; - - Calculator(const VarAccess& varAccess, std::ostream& out) - : vars(varAccess), logger(out), calcres(nullptr) - {} - - void visit(Eq&) final; - void visit(StrictEq&) final; - void visit(Neq&) final; - void visit(StrictNeq&) final; - void visit(Less&) final; - void visit(Greater&) final; - void visit(Leq&) final; - void visit(Geq&) final; - void visit(And&) final; - void visit(Or&) final; - void visit(Not&) final; - void visit(NotNot&) final; - void visit(Add&) final; - void visit(Sub&) final; - void visit(Mul&) final; - void visit(Div&) final; - void visit(Mod&) final; - void visit(Min&) final; - void visit(Max&) final; - void visit(Array&) final; - void visit(Map&) final; - void visit(Reduce&) final; - void visit(Filter&) final; - void visit(All&) final; - void visit(None&) final; - void visit(Some&) final; - void visit(Merge&) final; - void visit(Cat&) final; - void visit(Substr&) final; - void visit(In&) final; - void visit(Var&) final; - void visit(Log&) final; - - void visit(NullVal& n) final; - void visit(BoolVal& n) final; - void visit(IntVal& n) final; - void visit(UintVal& n) final; - void visit(DoubleVal& n) final; - void visit(StringVal& n) final; - - void visit(Error& n) final; - - ValueExpr eval(Expr& n); - - private: - const VarAccess& vars; - std::ostream& logger; - ValueExpr calcres; - - Calculator(const Calculator&) = delete; - Calculator(Calculator&&) = delete; - Calculator& operator=(const Calculator&) = delete; - Calculator& operator=(Calculator&&) = delete; - - // - // opers - - /// implements relop : [1, 2, 3, whatever] - template - void evalPairShortCircuit(Operator& n, BinaryPredicate calc) - { - const int num = n.num_evaluated_operands(); - assert(num >= 2); - - bool res = true; - int idx = -1; - ValueExpr rhs = eval(n.operand(++idx)); - assert(rhs.get()); - - while (res && (idx != (num-1))) - { - ValueExpr lhs = std::move(rhs); - assert(lhs.get()); - - rhs = eval(n.operand(++idx)); - assert(rhs.get()); - - res = compute(lhs, rhs, calc); - } - - calcres = toValueExpr(res); - } - - template - void reduce(Operator& n, BinaryOperator op) - { - const int num = n.num_evaluated_operands(); - assert(num >= 1); - - int idx = -1; - ValueExpr res = eval(n.operand(++idx)); - - res = convert(std::move(res), op); - - while (idx != (num-1)) - { - ValueExpr rhs = eval(n.operand(++idx)); - - rhs = convert(std::move(rhs), op); - res = compute(res, rhs, op); - } - - calcres = std::move(res); - } - - template - void binary(Operator& n, BinaryOperator calc) - { - const int num = n.num_evaluated_operands(); - assert(num == 1 || num == 2); - - int idx = -1; - ValueExpr lhs; - - if (num == 2) - { - CXX_LIKELY; - lhs = eval(n.operand(++idx)); - } - else - { - lhs = toValueExpr(std::int64_t(0)); - } - - ValueExpr rhs = eval(n.operand(++idx)); - - calcres = compute(lhs, rhs, calc); - } - - template - void unary(Operator& n, UnaryOperator calc) - { - const int num = n.num_evaluated_operands(); - assert(num == 1); - - const bool res = calc(*eval(n.operand(0))); - - calcres = toValueExpr(res); - } - - void evalShortCircuit(Operator& n, bool val) - { - const int num = n.num_evaluated_operands(); - assert(num >= 1); - - int idx = -1; - ValueExpr oper = eval(n.operand(++idx)); - bool found = (idx == num-1) || (toBool(*oper) == val); - - // loop until *aa == val or when *aa is the last valid element - while (!found) - { - oper = eval(n.operand(++idx)); - - found = (idx == (num-1)) || (toBool(*oper) == val); - } - - calcres = std::move(oper); - } - - template - void _value(const ValueNode& val) - { - calcres = toValueExpr(val.value()); - } - }; - - ValueExpr Calculator::eval(Expr& n) - { - ValueExpr res; - - n.accept(*this); - res.swap(calcres); - - return res; - } - - void Calculator::visit(Eq& n) - { - evalPairShortCircuit(n, Calc{}); - } - - void Calculator::visit(StrictEq& n) - { - evalPairShortCircuit(n, Calc{}); - } - - void Calculator::visit(Neq& n) - { - evalPairShortCircuit(n, Calc{}); - } - - void Calculator::visit(StrictNeq& n) - { - evalPairShortCircuit(n, Calc{}); - } - - void Calculator::visit(Less& n) - { - evalPairShortCircuit(n, Calc{}); - } - - void Calculator::visit(Greater& n) - { - evalPairShortCircuit(n, Calc{}); - } - - void Calculator::visit(Leq& n) - { - evalPairShortCircuit(n, Calc{}); - } - - void Calculator::visit(Geq& n) - { - evalPairShortCircuit(n, Calc{}); - } - - void Calculator::visit(And& n) - { - evalShortCircuit(n, false); - } - - void Calculator::visit(Or& n) - { - evalShortCircuit(n, true); - } - - void Calculator::visit(Not& n) - { - unary(n, Calc{}); - } - - void Calculator::visit(NotNot& n) - { - unary(n, Calc{}); - } - - void Calculator::visit(Add& n) - { - reduce(n, Calc{}); - } - - void Calculator::visit(Sub& n) - { - binary(n, Calc{}); - } - - void Calculator::visit(Mul& n) - { - reduce(n, Calc{}); - } - - void Calculator::visit(Div& n) - { - binary(n, Calc
{}); - } - - void Calculator::visit(Mod& n) - { - binary(n, Calc{}); - } - - void Calculator::visit(Min& n) - { - reduce(n, Calc{}); - } - - void Calculator::visit(Max& n) - { - reduce(n, Calc{}); - } - - void Calculator::visit(Cat& n) - { - reduce(n, Calc{}); - } - - void Calculator::visit(Substr&) { unsupported(); } - void Calculator::visit(Array&) { unsupported(); } - void Calculator::visit(Map&) { unsupported(); } - void Calculator::visit(Reduce&) { unsupported(); } - void Calculator::visit(Filter&) { unsupported(); } - void Calculator::visit(All&) { unsupported(); } - void Calculator::visit(None&) { unsupported(); } - void Calculator::visit(Some&) { unsupported(); } - void Calculator::visit(Merge&) { unsupported(); } - void Calculator::visit(In&) { unsupported(); } - - void Calculator::visit(Error&) { unsupported(); } - - void Calculator::visit(Var& n) - { - assert(n.num_evaluated_operands() == 1); - - AnyExpr elm = convert(eval(n.operand(0)), StringOperator{}); - - if (StringVal* str = dynamic_cast(elm.get())) - { - CXX_LIKELY; - calcres = vars(str->value(), n.num()); - return; - } - - typeError(); - } - - - void Calculator::visit(Log&) - { - const int num = n.num_evaluated_operands(); - assert(num == 1); - - calcres = calc(*eval(n.operand(0))); - - std::cerr << calcres << std::endl; - } - - void Calculator::visit(NullVal& n) { _value(n); } - void Calculator::visit(BoolVal& n) { _value(n); } - void Calculator::visit(IntVal& n) { _value(n); } - void Calculator::visit(UintVal& n) { _value(n); } - void Calculator::visit(DoubleVal& n) { _value(n); } - void Calculator::visit(StringVal& n) { _value(n); } - - void traverseInSAttributeOrder(Expr& e, Visitor& vis) - { - SAttributeTraversal trav{vis}; - - e.accept(trav); - } - - ValueExpr calculate(ValueExpr& exp, const Calculator::VarAccess& vars) - { - Calculator calc{vars, std::cerr}; - - assert(exp.get()); - return calc.eval(*exp); - } - - ValueExpr calculate(ValueExpr& exp) - { - return calculate(exp, [](const json::string&, int) -> ValueExpr { unsupported(); }); - } - - std::ostream& operator<<(std::ostream& os, ValueExpr& n) - { - struct ValuePrinter : FwdVisitor - { - explicit - ValuePrinter(std::ostream& stream) - : os(stream) - {} - - void visit(NullVal&) final { os << toString(nullptr); } - void visit(BoolVal& n) final { os << toString(n.value()); } - void visit(IntVal& n) final { os << n.value(); } - void visit(UintVal& n) final { os << n.value(); } - void visit(DoubleVal& n) final { os << n.value(); } - void visit(StringVal& n) final { os << n.value(); } - void visit(Array&) final { unsupported(); } - - std::ostream& os; - }; - - ValuePrinter prn{os}; - - n->accept(prn); - return os; - } - - // accept implementations - void Eq::accept(Visitor& v) { v.visit(*this); } - void StrictEq::accept(Visitor& v) { v.visit(*this); } - void Neq::accept(Visitor& v) { v.visit(*this); } - void StrictNeq::accept(Visitor& v) { v.visit(*this); } - void Less::accept(Visitor& v) { v.visit(*this); } - void Greater::accept(Visitor& v) { v.visit(*this); } - void Leq::accept(Visitor& v) { v.visit(*this); } - void Geq::accept(Visitor& v) { v.visit(*this); } - void And::accept(Visitor& v) { v.visit(*this); } - void Or::accept(Visitor& v) { v.visit(*this); } - void Not::accept(Visitor& v) { v.visit(*this); } - void NotNot::accept(Visitor& v) { v.visit(*this); } - void Add::accept(Visitor& v) { v.visit(*this); } - void Sub::accept(Visitor& v) { v.visit(*this); } - void Mul::accept(Visitor& v) { v.visit(*this); } - void Div::accept(Visitor& v) { v.visit(*this); } - void Mod::accept(Visitor& v) { v.visit(*this); } - void Min::accept(Visitor& v) { v.visit(*this); } - void Max::accept(Visitor& v) { v.visit(*this); } - void Map::accept(Visitor& v) { v.visit(*this); } - void Reduce::accept(Visitor& v) { v.visit(*this); } - void Filter::accept(Visitor& v) { v.visit(*this); } - void All::accept(Visitor& v) { v.visit(*this); } - void None::accept(Visitor& v) { v.visit(*this); } - void Some::accept(Visitor& v) { v.visit(*this); } - void Array::accept(Visitor& v) { v.visit(*this); } - void Merge::accept(Visitor& v) { v.visit(*this); } - void Cat::accept(Visitor& v) { v.visit(*this); } - void Substr::accept(Visitor& v) { v.visit(*this); } - void In::accept(Visitor& v) { v.visit(*this); } - void Var::accept(Visitor& v) { v.visit(*this); } - void Log::accept(Visitor& v) { v.visit(*this); } - void If::accept(Visitor& v) { v.visit(*this); } - - void NullVal::accept(Visitor& v) { v.visit(*this); } - void BoolVal::accept(Visitor& v) { v.visit(*this); } - void IntVal::accept(Visitor& v) { v.visit(*this); } - void UintVal::accept(Visitor& v) { v.visit(*this); } - void DoubleVal::accept(Visitor& v) { v.visit(*this); } - void StringVal::accept(Visitor& v) { v.visit(*this); } - - void Error::accept(Visitor& v) { v.visit(*this); } - - void FwdVisitor::visit(Expr&) { /* error ? */ } - - // num_evaluated_operands implementations - int Operator::num_evaluated_operands() const - { - return size(); - } - - template - int OperatorN::num_evaluated_operands() const - { - return std::min(MaxArity, Operator::num_evaluated_operands()); - } -} - diff --git a/include/experimental/json-io.hpp b/include/experimental/json-io.hpp index d93bace..18d9ceb 100644 --- a/include/experimental/json-io.hpp +++ b/include/experimental/json-io.hpp @@ -2,130 +2,113 @@ #pragma once +#include #include #include -#include - #include "cxx-compat.hpp" #include "dataframe.hpp" // use clippy-eval's conversion functions #include +namespace experimental { +std::string OTHER_COLUMN = "__other_column%%"; -namespace experimental -{ - std::string OTHER_COLUMN = "__other_column%%"; - -namespace -{ - using ColumnID = std::optional; +namespace { +using ColumnID = std::optional; - ColumnID - findColumn(const std::vector& colnames, boost::string_view colname) - { - using iterator = std::vector::const_iterator; +ColumnID findColumn(const std::vector& colnames, + boost::string_view colname) { + using iterator = std::vector::const_iterator; - iterator beg = colnames.begin(); - iterator lim = colnames.end(); - iterator pos = std::find(beg, lim, colname); + iterator beg = colnames.begin(); + iterator lim = colnames.end(); + iterator pos = std::find(beg, lim, colname); - return pos != lim ? ColumnID{std::distance(beg, pos)} : ColumnID{}; - } + return pos != lim ? ColumnID{std::distance(beg, pos)} : ColumnID{}; +} - template - T extractValue(const boost::json::value& val, T&& tag) - { - if (const boost::json::string* s = val.if_string()) - return json_logic::toConcreteValue(*s, tag); +template +T extractValue(const boost::json::value& val, T&& tag) { + if (const boost::json::string* s = val.if_string()) + return jsonlogic::toConcreteValue(*s, tag); - if (const std::int64_t* i = val.if_int64()) - return json_logic::toConcreteValue(*i, tag); + if (const std::int64_t* i = val.if_int64()) + return jsonlogic::toConcreteValue(*i, tag); - if (const std::uint64_t* u = val.if_uint64()) - return json_logic::toConcreteValue(*u, tag); + if (const std::uint64_t* u = val.if_uint64()) + return jsonlogic::toConcreteValue(*u, tag); - if (const double* d = val.if_double()) - return json_logic::toConcreteValue(*d, tag); + if (const double* d = val.if_double()) + return jsonlogic::toConcreteValue(*d, tag); - if (val.is_null()) - return json_logic::toConcreteValue(std::nullptr_t{}, tag); + if (val.is_null()) return jsonlogic::toConcreteValue(std::nullptr_t{}, tag); - throw std::logic_error("unsupported json value conversion."); - } + throw std::logic_error("unsupported json value conversion."); +} - template - T extractValue(json_logic::ValueExpr&& val, T&& tag) - { - return json_logic::unpackValue(std::move(val)); - } +template +T extractValue(jsonlogic::any_expr&& val, T&& tag) { + return jsonlogic::unpack_value(std::move(val)); +} +void storeAtColumn(DataFrame& frame, const ColumnVariant& coldesc, + dataframe_variant_t& cell, const boost::json::value& val) { + const std::string& celltype = coldesc.type_name(); + + if (celltype == string_type_str) + cell = + frame.persistent_string_std(extractValue(val, boost::json::string{})); + else if (celltype == int_type_str) + cell = extractValue(val, std::int64_t{}); + else if (celltype == uint_type_str) + cell = extractValue(val, std::uint64_t{}); + else if (celltype == real_type_str) + cell = extractValue(val, double{}); + else + throw std::logic_error("unknown column type"); +} - void storeAtColumn( DataFrame& frame, - const ColumnVariant& coldesc, - dataframe_variant_t& cell, - const boost::json::value& val - ) - { - const std::string& celltype = coldesc.type_name(); - - if (celltype == string_type_str) - cell = frame.persistent_string_std(extractValue(val, boost::json::string{})); - else if (celltype == int_type_str) - cell = extractValue(val, std::int64_t{}); - else if (celltype == uint_type_str) - cell = extractValue(val, std::uint64_t{}); - else if (celltype == real_type_str) - cell = extractValue(val, double{}); - else throw std::logic_error("unknown column type"); - } +std::vector allColumns(const DataFrame& frame) { + std::vector res(frame.columns()); - std::vector allColumns(const DataFrame& frame) - { - std::vector res(frame.columns()); + std::iota(res.begin(), res.end(), 0); + return res; +} - std::iota(res.begin(), res.end(), 0); - return res; - } +auto toJson(const dataframe_variant_t& el) -> boost::json::value { + if (const string_t* s = std::get_if(&el)) + return boost::json::value{boost::string_view(&*s->begin(), s->size())}; - auto toJson(const dataframe_variant_t& el) -> boost::json::value - { - if (const string_t* s = std::get_if(&el)) - return boost::json::value{ boost::string_view(&*s->begin(), s->size()) }; + if (const int_t* i = std::get_if(&el)) return boost::json::value{*i}; - if (const int_t* i = std::get_if(&el)) - return boost::json::value{*i}; + if (const real_t* r = std::get_if(&el)) return boost::json::value{*r}; - if (const real_t* r = std::get_if(&el)) - return boost::json::value{*r}; + if (const uint_t* u = std::get_if(&el)) return boost::json::value{*u}; - if (const uint_t* u = std::get_if(&el)) - return boost::json::value{*u}; + CXX_UNLIKELY; + return boost::json::value{}; +} +void injectValue(boost::json::object& o, const std::string& colname, + const dataframe_variant_t& el) { + if (std::get_if(&el)) { CXX_UNLIKELY; - return boost::json::value{}; + return; } - void injectValue(boost::json::object& o, const std::string& colname, const dataframe_variant_t& el) - { - if (std::get_if(&el)) - { - CXX_UNLIKELY; - return; - } - - o[colname] = toJson(el); - } + o[colname] = toJson(el); } +} // namespace -inline -void setCellValue(DataFrame& frame, const ColumnVariant& coldesc, std::int64_t row, json_logic::ValueExpr&& val) -{ +inline void setCellValue(DataFrame& frame, const ColumnVariant& coldesc, + std::int64_t row, jsonlogic::any_expr&& val) { ColumnVariant::pointer_variant_t ptr = coldesc.at_variant(row); if (string_t** s = std::get_if(&ptr)) - **s = frame.persistent_string_std(extractValue(std::move(val), boost::json::string{})); + **s = frame.persistent_string_std( + extractValue(std::move(val), boost::json::string{})); else if (int_t** i = std::get_if(&ptr)) **i = extractValue(std::move(val), std::int64_t{}); else if (uint_t** u = std::get_if(&ptr)) @@ -136,30 +119,26 @@ void setCellValue(DataFrame& frame, const ColumnVariant& coldesc, std::int64_t r throw std::runtime_error{"unknown column type"}; } - -void importJson(DataFrame& frame, const boost::json::value& val, bool useOtherColumn = true) -{ - const boost::json::object& obj = val.as_object(); +void importJson(DataFrame& frame, const boost::json::value& val, + bool useOtherColumn = true) { + const boost::json::object& obj = val.as_object(); std::vector row(frame.columns(), notavail_t{}); - std::vector allColumns = frame.get_column_variants(); - std::vector allColumnNames = frame.get_column_names(); - boost::json::object other; - - for (auto& el : obj) - { - if (ColumnID colid = findColumn(allColumnNames, el.key())) - { + std::vector allColumns = frame.get_column_variants(); + std::vector allColumnNames = frame.get_column_names(); + boost::json::object other; + + for (auto& el : obj) { + if (ColumnID colid = findColumn(allColumnNames, el.key())) { CXX_LIKELY; storeAtColumn(frame, allColumns.at(*colid), row.at(*colid), el.value()); - } - else if (useOtherColumn) + } else if (useOtherColumn) other.insert_or_assign(el.key(), el.value()); else - throw std::logic_error("automatic sparse column creation not yet supported"); + throw std::logic_error( + "automatic sparse column creation not yet supported"); } - if (!other.empty()) - { + if (!other.empty()) { ColumnID colid = findColumn(allColumnNames, OTHER_COLUMN); assert(colid); @@ -176,28 +155,24 @@ void importJson(DataFrame& frame, const boost::json::value& val, bool useOtherCo //~ auto cellToJson(const dataframe_variant_t& el) -> boost::json::value //~ { - //~ return toJson(el); +//~ return toJson(el); //~ } -auto exportJson(const DataFrame& frame, const std::vector& sel, std::size_t rownum) -> boost::json::object -{ - boost::json::object res; - std::vector colsel = sel.size() ? frame.get_index_list_std(sel) - : allColumns(frame); +auto exportJson(const DataFrame& frame, const std::vector& sel, + std::size_t rownum) -> boost::json::object { + boost::json::object res; + std::vector colsel = + sel.size() ? frame.get_index_list_std(sel) : allColumns(frame); std::vector row = frame.get_row_variant(rownum, colsel); - std::vector allColumnNames = frame.get_column_names(); + std::vector allColumnNames = frame.get_column_names(); - for (int idx : colsel) - { + for (int idx : colsel) { const std::string& colname = allColumnNames.at(idx); - if (idx >= 0) - { + if (idx >= 0) { CXX_LIKELY; injectValue(res, colname, row.at(idx)); - } - else - { + } else { throw std::logic_error("other column conversion not yet supported"); // res[colname] = toJsonObject(std::get(row.at(idx))); } @@ -206,6 +181,4 @@ auto exportJson(const DataFrame& frame, const std::vector& sel, std return res; } -} // namespace experimental - - +} // namespace experimental diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0327a01..5d559ee 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,12 +13,17 @@ function ( add_test class_name method_name ) # target_include_directories(${target} PRIVATE ${Boost_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_SOURCE_DIR} include/) set_target_properties(${target} PROPERTIES OUTPUT_NAME "${method_name}" ) - target_include_directories(${target} PRIVATE ${PROJECT_SOURCE_DIR}/include ${BOOST_INCLUDE_DIRS} include/) + target_include_directories(${target} PRIVATE + ${PROJECT_SOURCE_DIR}/include + ${BOOST_INCLUDE_DIRS} + include/ + ${jsonlogic_SOURCE_DIR}/cpp/include + ${jsonlogic_SOURCE_DIR}/cpp/src + ) target_link_libraries(${target} PRIVATE Boost::json) endfunction() - add_subdirectory(TestBag) add_subdirectory(TestSet) add_subdirectory(TestFunctions) diff --git a/test/TestBag/remove_if.cpp b/test/TestBag/remove_if.cpp index 2078546..6fc793b 100644 --- a/test/TestBag/remove_if.cpp +++ b/test/TestBag/remove_if.cpp @@ -3,12 +3,13 @@ // // SPDX-License-Identifier: MIT -#include "clippy/clippy-eval.hpp" #include #include #include #include #include +#include +// #include namespace boostjsn = boost::json; @@ -34,8 +35,8 @@ int main(int argc, char **argv) { auto apply_jl = [&expression](int value) { boostjsn::object data; data["value"] = value; - json_logic::ValueExpr res = json_logic::apply(expression["rule"], data); - return json_logic::unpackValue(res); + jsonlogic::any_expr res = jsonlogic::apply(expression["rule"], data); + return jsonlogic::unpack_value(res); }; the_bag.remove_if(apply_jl); diff --git a/test/TestGraph/dump.cpp b/test/TestGraph/dump.cpp index 5c93fbb..b6ded1a 100644 --- a/test/TestGraph/dump.cpp +++ b/test/TestGraph/dump.cpp @@ -69,7 +69,7 @@ int main(int argc, char **argv) { // we need to make a copy here because translateNode modifies the object boost::json::object exp2(expression); auto [_a /*unused*/, vars, _b /*unused*/] = - json_logic::translateNode(exp2["rule"]); + jsonlogic::translateNode(exp2["rule"]); std::cerr << "post-translate expression: " << expression << std::endl; std::cerr << "post-translate expression['rule']: " << expression["rule"] << std::endl; @@ -82,9 +82,9 @@ int main(int argc, char **argv) { std::cerr << " apply_jl: var: " << var << " val: " << val << std::endl; } - json_logic::ValueExpr res = json_logic::apply(expression["rule"], data); + jsonlogic::any_expr res = jsonlogic::apply(expression["rule"], data); std::cerr << " apply_jl: res: " << res << std::endl; - return json_logic::unpackValue(res); + return jsonlogic::unpack_value(res); }; std::string tail_sel = tailsel_opt.value(); diff --git a/test/TestGraph/for_all_edges.cpp b/test/TestGraph/for_all_edges.cpp index 80ff892..974b64e 100644 --- a/test/TestGraph/for_all_edges.cpp +++ b/test/TestGraph/for_all_edges.cpp @@ -3,14 +3,15 @@ // // SPDX-License-Identifier: MIT -#include "clippy/clippy-eval.hpp" -#include "testgraph.hpp" #include #include #include #include #include +#include "clippy/clippy-eval.hpp" +#include "testgraph.hpp" + static const std::string method_name = "add_node"; static const std::string state_name = "INTERNAL"; @@ -39,8 +40,8 @@ int main(int argc, char **argv) { data["src"] = value.first; data["dst"] = value.second; data["loc"] = boost::json::value_from(loc); - json_logic::ValueExpr res = json_logic::apply(expression["rule"], data); - return json_logic::unpackValue(res); + jsonlogic::any_expr res = jsonlogic::apply(expression["rule"], data); + return jsonlogic::unpack_value(res); }; the_graph.for_all_edges(apply_jl); diff --git a/test/TestGraph/where.cpp b/test/TestGraph/where.cpp index be25551..39b62b0 100644 --- a/test/TestGraph/where.cpp +++ b/test/TestGraph/where.cpp @@ -18,7 +18,7 @@ auto parse_where_expression(M& mvmap_, boost::json::object& expression, // boost::json::object submission_data{}; auto [_a /*unused*/, vars, _b /*unused*/] = - json_logic::translateNode(exp2["rule"]); + jsonlogic::translateNode(exp2["rule"]); std::cerr << "parse_where: # of vars: " << vars.size() << std::endl; for (const auto& var : vars) { @@ -62,10 +62,10 @@ auto parse_where_expression(M& mvmap_, boost::json::object& expression, } std::cerr << " apply_jl: submission_data: " << submission_data << std::endl; - json_logic::ValueExpr res = - json_logic::apply(expression["rule"], submission_data); + jsonlogic::any_expr res = + jsonlogic::apply(expression["rule"], submission_data); std::cerr << " apply_jl: res: " << res << std::endl; - return json_logic::unpackValue(res); + return jsonlogic::unpack_value(res); }; return apply_jl; @@ -78,7 +78,7 @@ std::vector where_nodes(const testgraph::testgraph& g, std::cerr << " where: expression: " << expression << std::endl; // auto [_a /*unused*/, vars, _b /*unused*/] = - // json_logic::translateNode(exp2["rule"]); + // jsonlogic::translateNode(exp2["rule"]); // auto nodemap = g.nodemap(); // boost::json::object submission_data{}; @@ -115,10 +115,10 @@ std::vector where_nodes(const testgraph::testgraph& g, // } // } - // json_logic::ValueExpr res = - // json_logic::apply(expression["rule"], submission_data); + // jsonlogic::any_expr res = + // jsonlogic::apply(expression["rule"], submission_data); // std::cerr << " apply_jl: res: " << res << std::endl; - // return json_logic::unpackValue(res); + // return jsonlogic::unpack_value(res); // }; auto nodemap = g.nodemap(); diff --git a/test/TestSet/remove_if.cpp b/test/TestSet/remove_if.cpp index 6e322e7..828d1a0 100644 --- a/test/TestSet/remove_if.cpp +++ b/test/TestSet/remove_if.cpp @@ -34,8 +34,8 @@ int main(int argc, char **argv) { auto apply_jl = [&expression](int value) { boostjsn::object data; data["value"] = value; - json_logic::ValueExpr res = json_logic::apply(expression["rule"], data); - return json_logic::unpackValue(res); + jsonlogic::any_expr res = jsonlogic::apply(expression["rule"], data); + return jsonlogic::unpack_value(res); }; for (auto first = the_set.begin(), last = the_set.end(); first != last;) { diff --git a/test/include/wheredev.cpp b/test/include/wheredev.cpp index aa2fabb..e0e4d33 100644 --- a/test/include/wheredev.cpp +++ b/test/include/wheredev.cpp @@ -12,7 +12,7 @@ int main() { std::cout << json_blob << std::endl; auto data = boost::json::parse(json_blob).as_object(); auto [_a /*unused*/, vars, _b /*unused*/] = - json_logic::translateNode(data["rule"]); + jsonlogic::translateNode(data["rule"]); for (auto &v : vars) { std::cout << v << std::endl; From 823ba8174f7cc26f69f949e650b857d978b5d110 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Thu, 17 Apr 2025 15:22:47 -0700 Subject: [PATCH 02/11] extraced all jsonlogic --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 1 + examples/logic/testeval.cpp | 3 +- examples/oo-howdy/howdy-eval.cpp | 2 +- include/clippy/attic/clippy-ast.hpp | 1843 ------------- include/clippy/attic/clippy-eval.hpp | 3312 ----------------------- include/experimental/json-io.hpp | 2 +- test/CMakeLists.txt | 1 - test/TestBag/remove_if.cpp | 2 +- test/TestGraph/assign.cpp | 2 +- test/TestGraph/connected_components.cpp | 2 +- test/TestGraph/count.cpp | 2 +- test/TestGraph/degree.cpp | 2 +- test/TestGraph/dump.cpp | 2 +- test/TestGraph/dump2.cpp | 2 +- test/TestGraph/extrema.cpp | 2 +- test/TestGraph/for_all_edges.cpp | 2 +- test/TestGraph/series_str.cpp | 2 +- test/TestGraph/where.cpp | 26 +- test/TestSet/remove_if.cpp | 3 +- test/include/wheredev.cpp | 3 +- 21 files changed, 35 insertions(+), 5181 deletions(-) delete mode 100644 .DS_Store delete mode 100644 include/clippy/attic/clippy-ast.hpp delete mode 100644 include/clippy/attic/clippy-eval.hpp diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index a2eb533abf551d042629249e6fe5fb378b3b5ee0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~u?oUK42Bc!Ah>jNyu}Cb4Gz&K=nFU~E>c0O^F6wMazU^xC+1b{XuyJ79K1T}(S!u1*@b}wNMJ-@TJzTK|1JE}{6A`8N&+PC zX9Tp_belC^D(=>|*R%RAs #include #include - -#include "clippy/clippy-eval.hpp" +#include namespace bjsn = boost::json; diff --git a/examples/oo-howdy/howdy-eval.cpp b/examples/oo-howdy/howdy-eval.cpp index bcef944..10179a5 100644 --- a/examples/oo-howdy/howdy-eval.cpp +++ b/examples/oo-howdy/howdy-eval.cpp @@ -3,8 +3,8 @@ #include #include #include +#include -#include "clippy/clippy-eval.hpp" #include "clippy/clippy.hpp" namespace boostjsn = boost::json; diff --git a/include/clippy/attic/clippy-ast.hpp b/include/clippy/attic/clippy-ast.hpp deleted file mode 100644 index c4af6b8..0000000 --- a/include/clippy/attic/clippy-ast.hpp +++ /dev/null @@ -1,1843 +0,0 @@ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace json_logic { -constexpr bool DEBUG_OUTPUT = true; - -namespace json = boost::json; - -using JsonExpr = json::value; - -template -T& as(T& n) { - return n; -} - -template -T& deref(T* p) { - assert(p); - return *p; -} - -struct Visitor; - -// the root class -struct Expr { - Expr() = default; - virtual ~Expr() = default; - - virtual void accept(Visitor&) = 0; - - private: - Expr(Expr&&) = delete; - Expr(const Expr&) = delete; - Expr& operator=(Expr&&) = delete; - Expr& operator=(const Expr&) = delete; -}; - -//~ Expr::~Expr() {} - -// -// foundation classes -// \{ - -using any_expr = std::unique_ptr; - -struct Operator : Expr, private std::vector { - using container_type = std::vector; - - using container_type::back; - using container_type::begin; - using container_type::const_iterator; - using container_type::const_reverse_iterator; - using container_type::crbegin; - using container_type::crend; - using container_type::end; - using container_type::iterator; - using container_type::push_back; - using container_type::rbegin; - using container_type::rend; - using container_type::reverse_iterator; - - void set_operands(container_type&& opers) { this->swap(opers); } - - Expr& operand(int n) const { return deref(this->at(n).get()); } - - virtual int num_evaluated_operands() const; -}; - -// defines operators that have an upper bound on how many -// arguments are evaluated. -template -struct OperatorN : Operator { - enum { MAX_OPERANDS = MaxArity }; - - int num_evaluated_operands() const final; -}; - -template -struct Value : Expr { - using value_type = T; - - explicit Value(T t) : val(std::move(t)) {} - - T& value() { return val; } - const T& value() const { return val; } - - private: - T val; -}; - -// \} - -// -// expression hierarchy -// comparison - -// binary -struct Eq : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct StrictEq : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct Neq : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct StrictNeq : OperatorN<2> { - void accept(Visitor&) final; -}; - -// binary or ternary -struct Less : OperatorN<3> { - void accept(Visitor&) final; -}; - -struct Greater : OperatorN<3> { - void accept(Visitor&) final; -}; - -struct Leq : OperatorN<3> { - void accept(Visitor&) final; -}; - -struct Geq : OperatorN<3> { - void accept(Visitor&) final; -}; - -// logical operators - -// unary -struct Not : OperatorN<1> { - void accept(Visitor&) final; -}; - -struct NotNot : OperatorN<1> { - void accept(Visitor&) final; -}; - -// n-ary -struct And : Operator { - void accept(Visitor&) final; -}; - -struct Or : Operator { - void accept(Visitor&) final; -}; - -// control structure -struct If : Operator { - void accept(Visitor&) final; -}; - -// arithmetics -// n-ary -struct Add : Operator { - void accept(Visitor&) final; -}; - -struct Mul : Operator { - void accept(Visitor&) final; -}; - -struct Min : Operator { - void accept(Visitor&) final; -}; - -struct Max : Operator { - void accept(Visitor&) final; -}; - -// binary -struct Sub : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct Div : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct Mod : OperatorN<2> { - void accept(Visitor&) final; -}; - -// array -struct Array : Operator // array is modeled as operator -{ - void accept(Visitor&) final; -}; - -struct Map : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct Reduce : OperatorN<3> { - void accept(Visitor&) final; -}; - -struct Filter : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct All : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct None : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct Some : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct Merge : Operator { - void accept(Visitor&) final; -}; - -// string operations -struct Var : OperatorN<1> { - enum { computed = -1 }; - - void accept(Visitor&) final; - - void num(int val) { idx = val; } - int num() const { return idx; } - - private: - int idx = computed; -}; - -struct Cat : Operator { - void accept(Visitor&) final; -}; - -struct Substr : OperatorN<3> { - void accept(Visitor&) final; -}; - -// string and array operation -struct In : Operator { - void accept(Visitor&) final; -}; - -// values -struct NullVal : Expr { - void accept(Visitor&) final; - - std::nullptr_t value() const { return nullptr; } -}; - -struct BoolVal : Value { - using base = Value; - using base::base; - - void accept(Visitor&) final; -}; - -struct IntVal : Value { - using base = Value; - using base::base; - - void accept(Visitor&) final; -}; - -struct UintVal : Value { - using base = Value; - using base::base; - - void accept(Visitor&) final; -}; - -struct DoubleVal : Value { - using base = Value; - using base::base; - - void accept(Visitor&) final; -}; - -struct StringVal : Value { - using base = Value; - using base::base; - - void accept(Visitor&) final; -}; - -// logger -struct Log : OperatorN<1> { - void accept(Visitor&) final; -}; - -// error node -struct Error : Expr { - void accept(Visitor&) final; -}; - -// Visitor -struct Visitor { - virtual void visit(Expr&) = 0; // error - virtual void visit(Operator& n) = 0; - virtual void visit(Eq&) = 0; - virtual void visit(StrictEq&) = 0; - virtual void visit(Neq&) = 0; - virtual void visit(StrictNeq&) = 0; - virtual void visit(Less&) = 0; - virtual void visit(Greater&) = 0; - virtual void visit(Leq&) = 0; - virtual void visit(Geq&) = 0; - virtual void visit(And&) = 0; - virtual void visit(Or&) = 0; - virtual void visit(Not&) = 0; - virtual void visit(NotNot&) = 0; - virtual void visit(Add&) = 0; - virtual void visit(Sub&) = 0; - virtual void visit(Mul&) = 0; - virtual void visit(Div&) = 0; - virtual void visit(Mod&) = 0; - virtual void visit(Min&) = 0; - virtual void visit(Max&) = 0; - virtual void visit(Map&) = 0; - virtual void visit(Reduce&) = 0; - virtual void visit(Filter&) = 0; - virtual void visit(All&) = 0; - virtual void visit(None&) = 0; - virtual void visit(Some&) = 0; - virtual void visit(Array&) = 0; - virtual void visit(Merge&) = 0; - virtual void visit(Cat&) = 0; - virtual void visit(Substr&) = 0; - virtual void visit(In&) = 0; - virtual void visit(Var&) = 0; - virtual void visit(Log&) = 0; - - // control structure - virtual void visit(If&) = 0; - - // values - virtual void visit(NullVal&) = 0; - virtual void visit(BoolVal&) = 0; - virtual void visit(IntVal&) = 0; - virtual void visit(UintVal&) = 0; - virtual void visit(DoubleVal&) = 0; - virtual void visit(StringVal&) = 0; - //~ virtual void visit(ObjectVal&) = 0; - - virtual void visit(Error&) = 0; -}; - -struct FwdVisitor : Visitor { - void visit(Expr&) override; - void visit(Operator& n) override { visit(as(n)); } - void visit(Eq& n) override { visit(as(n)); } - void visit(StrictEq& n) override { visit(as(n)); } - void visit(Neq& n) override { visit(as(n)); } - void visit(StrictNeq& n) override { visit(as(n)); } - void visit(Less& n) override { visit(as(n)); } - void visit(Greater& n) override { visit(as(n)); } - void visit(Leq& n) override { visit(as(n)); } - void visit(Geq& n) override { visit(as(n)); } - void visit(And& n) override { visit(as(n)); } - void visit(Or& n) override { visit(as(n)); } - void visit(Not& n) override { visit(as(n)); } - void visit(NotNot& n) override { visit(as(n)); } - void visit(Add& n) override { visit(as(n)); } - void visit(Sub& n) override { visit(as(n)); } - void visit(Mul& n) override { visit(as(n)); } - void visit(Div& n) override { visit(as(n)); } - void visit(Mod& n) override { visit(as(n)); } - void visit(Min& n) override { visit(as(n)); } - void visit(Max& n) override { visit(as(n)); } - void visit(Map& n) override { visit(as(n)); } - void visit(Reduce& n) override { visit(as(n)); } - void visit(Filter& n) override { visit(as(n)); } - void visit(All& n) override { visit(as(n)); } - void visit(None& n) override { visit(as(n)); } - void visit(Some& n) override { visit(as(n)); } - void visit(Array& n) override { visit(as(n)); } - void visit(Merge& n) override { visit(as(n)); } - void visit(Cat& n) override { visit(as(n)); } - void visit(Substr& n) override { visit(as(n)); } - void visit(In& n) override { visit(as(n)); } - void visit(Var& n) override { visit(as(n)); } - void visit(Log& n) override { visit(as(n)); } - - void visit(If& n) override { visit(as(n)); } - - void visit(NullVal& n) override { visit(as(n)); } - void visit(BoolVal& n) override { visit(as(n)); } - void visit(IntVal& n) override { visit(as(n)); } - void visit(UintVal& n) override { visit(as(n)); } - void visit(DoubleVal& n) override { visit(as(n)); } - void visit(StringVal& n) override { visit(as(n)); } - - void visit(Error& n) override { visit(as(n)); } -}; - -/// AST traversal function that calls v's visit methods in post-fix order -void traverseInSAttributeOrder(Expr& e, Visitor& vis); - -/// traverses the children of a node; does not traverse grandchildren -void traverseChildren(Visitor& v, const Operator& node); -void traverseAllChildren(Visitor& v, const Operator& node); -void traverseChildrenReverse(Visitor& v, const Operator& node); - -namespace { -CXX_NORETURN -void unsupported() { throw std::logic_error("not yet implemented"); } - -CXX_NORETURN -void typeError() { throw std::runtime_error("typing error"); } - -struct VarMap { - void insert(Var& el); - std::vector toVector() const; - bool hasComputedVariables() const { return hasComputed; } - - private: - using container_type = std::map; - - container_type mapping = {}; - bool hasComputed = false; -}; - -void VarMap::insert(Var& var) { - any_expr& arg = var.back(); - StringVal* str = dynamic_cast(arg.get()); - - if (str == nullptr) { - CXX_UNLIKELY; - hasComputed = true; - return; - } - - // do nothing for free variables in "lambdas" - if (str->value() == "") return; - - auto [pos, success] = mapping.emplace(str->value(), mapping.size()); - - var.num(pos->second); -} - -std::vector VarMap::toVector() const { - std::vector res; - - res.resize(mapping.size()); - - for (const container_type::value_type& el : mapping) - res.at(el.second) = el.first; - - return res; -} - -/// translates all children -/// \{ -Operator::container_type translateChildren(json::array& children, VarMap&); - -Operator::container_type translateChildren(JsonExpr& n, VarMap&); -/// \} - -template -Expr& mkOperator(json::object& n, VarMap& m) { - assert(n.size() == 1); - - ExprT& res = deref(new ExprT); - - res.set_operands(translateChildren(n.begin()->value(), m)); - return res; -} - -Array& mkArrayOperator(json::array& children, VarMap& m) { - Array& res = deref(new Array); - - res.set_operands(translateChildren(children, m)); - return res; -} - -template -ValueT& mkValue(typename ValueT::value_type n) { - return deref(new ValueT(std::move(n))); -} - -NullVal& mkNullValue() { return deref(new NullVal); } - -using DispatchTable = std::map; - -DispatchTable::const_iterator lookup(const DispatchTable& m, - const json::object& op) { - if (op.size() != 1) return m.end(); - - return m.find(op.begin()->key()); -} - -any_expr translateNode_internal(JsonExpr& n, VarMap& varmap) { - static const DispatchTable dt = { - {"==", &mkOperator}, {"===", &mkOperator}, - {"!=", &mkOperator}, {"!==", &mkOperator}, - {"if", &mkOperator}, {"!", &mkOperator}, - {"!!", &mkOperator}, {"or", &mkOperator}, - {"and", &mkOperator}, {">", &mkOperator}, - {">=", &mkOperator}, {"<", &mkOperator}, - {"<=", &mkOperator}, {"max", &mkOperator}, - {"min", &mkOperator}, {"+", &mkOperator}, - {"-", &mkOperator}, {"*", &mkOperator}, - {"/", &mkOperator
}, {"%", &mkOperator}, - {"map", &mkOperator}, {"reduce", &mkOperator}, - {"filter", &mkOperator}, {"all", &mkOperator}, - {"none", &mkOperator}, {"some", &mkOperator}, - {"merge", &mkOperator}, {"in", &mkOperator}, - {"cat", &mkOperator}, {"log", &mkOperator}, - {"var", &mkOperator}}; - - Expr* res = nullptr; - - switch (n.kind()) { - case json::kind::object: { - json::object& obj = n.get_object(); - DispatchTable::const_iterator pos = lookup(dt, obj); - - if (pos != dt.end()) { - CXX_LIKELY; - res = &pos->second(obj, varmap); - - if (pos->second == mkOperator) - varmap.insert(dynamic_cast(*res)); - } else { - // does json_logic support value objects? - unsupported(); - } - - break; - } - - case json::kind::array: { - // array is an operator that combines its subexpressions into an array - res = &mkArrayOperator(n.get_array(), varmap); - break; - } - - case json::kind::string: { - res = &mkValue(std::move(n.get_string())); - break; - } - - case json::kind::int64: { - res = &mkValue(n.get_int64()); - break; - } - - case json::kind::uint64: { - res = &mkValue(n.get_uint64()); - break; - } - - case json::kind::double_: { - res = &mkValue(n.get_double()); - break; - } - - case json::kind::bool_: { - res = &mkValue(n.get_bool()); - break; - } - - case json::kind::null: { - res = &mkNullValue(); - break; - } - - default: - unsupported(); - } - - return std::unique_ptr(res); -} - -std::tuple, bool> translateNode( - JsonExpr& n) { - VarMap varmap; - any_expr node = translateNode_internal(n, varmap); - bool hasComputedVariables = varmap.hasComputedVariables(); - - return std::make_tuple(std::move(node), varmap.toVector(), - hasComputedVariables); -} - -Operator::container_type translateChildren(json::array& children, - VarMap& varmap) { - Operator::container_type res; - - res.reserve(children.size()); - - for (JsonExpr& elem : children) - res.emplace_back(translateNode_internal(elem, varmap)); - - return res; -} - -Operator::container_type translateChildren(JsonExpr& n, VarMap& varmap) { - if (json::array* arr = n.if_array()) { - CXX_LIKELY; - return translateChildren(*arr, varmap); - } - - Operator::container_type res; - - res.emplace_back(translateNode_internal(n, varmap)); - return res; -} -} // namespace - -// only operators have children -void _traverseChildren(Visitor& v, Operator::const_iterator aa, - Operator::const_iterator zz) { - std::for_each(aa, zz, [&v](const any_expr& e) -> void { e->accept(v); }); -} - -void traverseChildren(Visitor& v, const Operator& node) { - Operator::const_iterator aa = node.begin(); - - _traverseChildren(v, aa, aa + node.num_evaluated_operands()); -} - -void traverseAllChildren(Visitor& v, const Operator& node) { - _traverseChildren(v, node.begin(), node.end()); -} - -void traverseChildrenReverse(Visitor& v, const Operator& node) { - Operator::const_reverse_iterator zz = node.crend(); - Operator::const_reverse_iterator aa = zz - node.num_evaluated_operands(); - - std::for_each(aa, zz, [&v](const any_expr& e) -> void { e->accept(v); }); -} - -namespace { -struct SAttributeTraversal : Visitor { - explicit SAttributeTraversal(Visitor& client) : sub(client) {} - - void visit(Expr&) final; - void visit(Operator&) final; - void visit(Eq&) final; - void visit(StrictEq&) final; - void visit(Neq&) final; - void visit(StrictNeq&) final; - void visit(Less&) final; - void visit(Greater&) final; - void visit(Leq&) final; - void visit(Geq&) final; - void visit(And&) final; - void visit(Or&) final; - void visit(Not&) final; - void visit(NotNot&) final; - void visit(Add&) final; - void visit(Sub&) final; - void visit(Mul&) final; - void visit(Div&) final; - void visit(Mod&) final; - void visit(Min&) final; - void visit(Max&) final; - void visit(Map&) final; - void visit(Reduce&) final; - void visit(Filter&) final; - void visit(All&) final; - void visit(None&) final; - void visit(Some&) final; - void visit(Merge&) final; - void visit(Cat&) final; - void visit(Substr&) final; - void visit(In&) final; - void visit(Array& n) final; - void visit(Var&) final; - void visit(Log&) final; - - void visit(If&) final; - - void visit(NullVal& n) final; - void visit(BoolVal& n) final; - void visit(IntVal& n) final; - void visit(UintVal& n) final; - void visit(DoubleVal& n) final; - void visit(StringVal& n) final; - - void visit(Error& n) final; - - private: - Visitor& sub; - - template - inline void _visit(OperatorNode& n) { - traverseChildren(*this, n); - sub.visit(n); - } - - template - inline void _value(ValueNode& n) { - sub.visit(n); - } -}; - -void SAttributeTraversal::visit(Expr&) { typeError(); } -void SAttributeTraversal::visit(Operator&) { typeError(); } -void SAttributeTraversal::visit(Eq& n) { _visit(n); } -void SAttributeTraversal::visit(StrictEq& n) { _visit(n); } -void SAttributeTraversal::visit(Neq& n) { _visit(n); } -void SAttributeTraversal::visit(StrictNeq& n) { _visit(n); } -void SAttributeTraversal::visit(Less& n) { _visit(n); } -void SAttributeTraversal::visit(Greater& n) { _visit(n); } -void SAttributeTraversal::visit(Leq& n) { _visit(n); } -void SAttributeTraversal::visit(Geq& n) { _visit(n); } -void SAttributeTraversal::visit(And& n) { _visit(n); } -void SAttributeTraversal::visit(Or& n) { _visit(n); } -void SAttributeTraversal::visit(Not& n) { _visit(n); } -void SAttributeTraversal::visit(NotNot& n) { _visit(n); } -void SAttributeTraversal::visit(Add& n) { _visit(n); } -void SAttributeTraversal::visit(Sub& n) { _visit(n); } -void SAttributeTraversal::visit(Mul& n) { _visit(n); } -void SAttributeTraversal::visit(Div& n) { _visit(n); } -void SAttributeTraversal::visit(Mod& n) { _visit(n); } -void SAttributeTraversal::visit(Min& n) { _visit(n); } -void SAttributeTraversal::visit(Max& n) { _visit(n); } -void SAttributeTraversal::visit(Array& n) { _visit(n); } -void SAttributeTraversal::visit(Map& n) { _visit(n); } -void SAttributeTraversal::visit(Reduce& n) { _visit(n); } -void SAttributeTraversal::visit(Filter& n) { _visit(n); } -void SAttributeTraversal::visit(All& n) { _visit(n); } -void SAttributeTraversal::visit(None& n) { _visit(n); } -void SAttributeTraversal::visit(Some& n) { _visit(n); } -void SAttributeTraversal::visit(Merge& n) { _visit(n); } -void SAttributeTraversal::visit(Cat& n) { _visit(n); } -void SAttributeTraversal::visit(Substr& n) { _visit(n); } -void SAttributeTraversal::visit(In& n) { _visit(n); } -void SAttributeTraversal::visit(Var& n) { _visit(n); } -void SAttributeTraversal::visit(Log& n) { _visit(n); } - -void SAttributeTraversal::visit(If& n) { _visit(n); } - -void SAttributeTraversal::visit(NullVal& n) { _value(n); } -void SAttributeTraversal::visit(BoolVal& n) { _value(n); } -void SAttributeTraversal::visit(IntVal& n) { _value(n); } -void SAttributeTraversal::visit(UintVal& n) { _value(n); } -void SAttributeTraversal::visit(DoubleVal& n) { _value(n); } -void SAttributeTraversal::visit(StringVal& n) { _value(n); } - -void SAttributeTraversal::visit(Error& n) { sub.visit(n); } -} // namespace - -using any_expr = - std::unique_ptr; // could be value if we have a type for that - -std::ostream& operator<<(std::ostream& os, any_expr& n); - -any_expr toany_expr(std::nullptr_t) { return any_expr(new NullVal); } -any_expr toany_expr(bool val) { return any_expr(new BoolVal(val)); } -any_expr toany_expr(std::int64_t val) { return any_expr(new IntVal(val)); } -any_expr toany_expr(std::uint64_t val) { return any_expr(new UintVal(val)); } -any_expr toany_expr(double val) { return any_expr(new DoubleVal(val)); } -any_expr toany_expr(json::string val) { - return any_expr(new StringVal(std::move(val))); -} - -// -// coercion functions - -std::int64_t toInt(const json::string& str) { - return std::stoi(std::string{str.c_str()}); -} - -std::int64_t toInt(double d) { return d; } - -std::int64_t toInt(bool b) { return b; } - -std::int64_t toInt(std::uint64_t v) { return v; } - -std::uint64_t toUint(const json::string& str) { - return std::stoull(std::string{str.c_str()}); -} - -std::uint64_t toUint(double d) { return d; } - -std::uint64_t toUint(std::int64_t v) { return v; } - -std::uint64_t toUint(bool b) { return b; } - -double toDouble(const json::string& str) { - return std::stod(std::string{str.c_str()}); -} - -double toDouble(std::int64_t val) { return val; } - -double toDouble(std::uint64_t val) { return val; } - -template -json::string toString(Val v) { - return json::string{std::to_string(v)}; -} - -json::string toString(bool b) { return json::string{b ? "true" : "false"}; } - -json::string toString(std::nullptr_t) { return json::string{"null"}; } - -/// conversion to boolean -/// \{ - -bool toBool(std::int64_t v) { return v; } -bool toBool(std::uint64_t v) { return v; } -bool toBool(double v) { return v; } -bool toBool(const json::string& v) { return v.size() != 0; } -bool toBool(Array& v) { return v.num_evaluated_operands(); } - -bool toBool(Expr& e) { - struct BoolConverter : FwdVisitor { - void visit(Expr&) final { typeError(); } - - void visit(NullVal&) final { res = false; } - void visit(BoolVal& n) final { res = n.value(); } - void visit(IntVal& n) final { res = toBool(n.value()); } - void visit(UintVal& n) final { res = toBool(n.value()); } - void visit(DoubleVal& n) final { res = toBool(n.value()); } - void visit(StringVal& n) final { res = toBool(n.value()); } - void visit(Array& n) final { res = toBool(n); } - - bool res; - }; - - BoolConverter conv; - - e.accept(conv); - return conv.res; -} - -bool toBool(any_expr& el) { return toBool(*el); } - -/// \} - -struct LogicalOperatorBase { - enum { - definedForString = true, - definedForDouble = true, - definedForInteger = true, - definedForBool = false, - definedForNull = false - }; - - using result_type = bool; -}; - -struct NoCoercion {}; - -/// \brief a strict binary operator operates on operands of the same -/// type. The operation on two different types returns false. -/// NO type coercion is performed. -struct StrictLogicalBinaryOperator : LogicalOperatorBase { - template - std::tuple coerce(LhsT* lv, RhsT* rv) { - return std::make_tuple(std::move(*lv), std::move(*rv)); - } -}; - -struct NumericBinaryOperatorBase { - std::tuple coerce(double* lv, double* rv) { - return std::make_tuple(*lv, *rv); - } - - std::tuple coerce(double* lv, std::int64_t* rv) { - return std::make_tuple(*lv, toDouble(*rv)); - } - - std::tuple coerce(double* lv, std::uint64_t* rv) { - return std::make_tuple(*lv, toDouble(*rv)); - } - - std::tuple coerce(std::int64_t* lv, double* rv) { - return std::make_tuple(toDouble(*lv), *rv); - } - - std::tuple coerce(std::int64_t* lv, - std::int64_t* rv) { - return std::make_tuple(*lv, *rv); - } - - std::tuple coerce(std::int64_t* lv, - std::uint64_t* rv) { - return std::make_tuple(*lv, toInt(*rv)); - } - - std::tuple coerce(std::uint64_t* lv, double* rv) { - return std::make_tuple(toDouble(*lv), *rv); - } - - std::tuple coerce(std::uint64_t* lv, - std::int64_t* rv) { - return std::make_tuple(toInt(*lv), *rv); - } - - std::tuple coerce(std::uint64_t* lv, - std::uint64_t* rv) { - return std::make_tuple(*lv, *rv); - } -}; - -/// \brief a logical binary operator compares two values. If the -/// values have a different type, type coercion is performed -/// on one of the operands. -struct LogicalBinaryOperator : NumericBinaryOperatorBase, LogicalOperatorBase { - using NumericBinaryOperatorBase::coerce; - - std::tuple coerce(double* lv, json::string* rv) { - return std::make_tuple(*lv, toDouble(*rv)); - } - - std::tuple coerce(std::int64_t* lv, - json::string* rv) { - return std::make_tuple(*lv, toInt(*rv)); - } - - std::tuple coerce(std::uint64_t* lv, - json::string* rv) { - return std::make_tuple(*lv, toUint(*rv)); - } - - std::tuple coerce(json::string* lv, double* rv) { - return std::make_tuple(toDouble(*lv), *rv); - } - - std::tuple coerce(json::string* lv, - std::int64_t* rv) { - return std::make_tuple(toInt(*lv), *rv); - } - - std::tuple coerce(json::string* lv, - std::uint64_t* rv) { - return std::make_tuple(toInt(*lv), *rv); - } - - std::tuple coerce(json::string* lv, - json::string* rv) { - return std::make_tuple(std::move(*lv), std::move(*rv)); - } -}; -// @} - -// Arith -struct ArithmeticOperator : NumericBinaryOperatorBase { - enum { - definedForString = false, - definedForDouble = true, - definedForInteger = true, - definedForBool = false, - definedForNull = true - }; - - using result_type = any_expr; - - using NumericBinaryOperatorBase::coerce; - - std::tuple coerce(double*, std::nullptr_t) { - return std::make_tuple(nullptr, nullptr); - } - - std::tuple coerce(std::int64_t*, - std::nullptr_t) { - return std::make_tuple(nullptr, nullptr); - } - - std::tuple coerce(std::uint64_t*, - std::nullptr_t) { - return std::make_tuple(nullptr, nullptr); - } - - std::tuple coerce(std::nullptr_t, double*) { - return std::make_tuple(nullptr, nullptr); - } - - std::tuple coerce(std::nullptr_t, - std::int64_t*) { - return std::make_tuple(nullptr, nullptr); - } - - std::tuple coerce(std::nullptr_t, - std::uint64_t*) { - return std::make_tuple(nullptr, nullptr); - } - - std::tuple coerce(std::nullptr_t, - std::nullptr_t) { - return std::make_tuple(nullptr, nullptr); - } -}; - -struct IntegerArithmeticOperator : ArithmeticOperator { - enum { - definedForString = false, - definedForDouble = false, - definedForInteger = true, - definedForBool = false, - definedForNull = false - }; - - using ArithmeticOperator::coerce; -}; - -struct StringOperator { - enum { - definedForString = true, - definedForDouble = false, - definedForInteger = false, - definedForBool = false, - definedForNull = false - }; - - using result_type = any_expr; - - std::tuple coerce(json::string* lv, - json::string* rv) { - return std::make_tuple(std::move(*lv), std::move(*rv)); - } -}; - -any_expr convert(any_expr val, ...) { return val; } - -any_expr convert(any_expr val, const ArithmeticOperator&) { - struct ArithmeticConverter : FwdVisitor { - explicit ArithmeticConverter(any_expr val) : res(std::move(val)) {} - - void visit(Expr&) final { typeError(); } - - // defined for the following types - void visit(IntVal&) final {} - void visit(UintVal&) final {} - void visit(DoubleVal&) final {} - void visit(NullVal&) final {} - - // need to convert values - void visit(StringVal& el) final { - double dd = toDouble(el.value()); - int64_t ii = toInt(el.value()); - // uint? - - res = (dd != ii) ? toany_expr(dd) : toany_expr(ii); - } - - void visit(BoolVal&) final { - // \todo correct? - res = toany_expr(nullptr); - } - - any_expr result() && { return std::move(res); } - - private: - any_expr res; - }; - - Expr* node = val.get(); - ArithmeticConverter conv{std::move(val)}; - - node->accept(conv); - return std::move(conv).result(); -} - -any_expr convert(any_expr val, const IntegerArithmeticOperator&) { - struct IntegerArithmeticConverter : FwdVisitor { - explicit IntegerArithmeticConverter(any_expr val) : res(std::move(val)) {} - - void visit(Expr&) final { typeError(); } - - // defined for the following types - void visit(IntVal&) final {} - void visit(UintVal&) final {} - - // need to convert values - void visit(StringVal& el) final { res = toany_expr(toInt(el.value())); } - - void visit(BoolVal& el) final { res = toany_expr(toInt(el.value())); } - - void visit(DoubleVal& el) final { res = toany_expr(toInt(el.value())); } - - void visit(NullVal&) final { res = toany_expr(std::int64_t(0)); } - - any_expr result() && { return std::move(res); } - - private: - any_expr res; - }; - - Expr* node = val.get(); - IntegerArithmeticConverter conv{std::move(val)}; - - node->accept(conv); - return std::move(conv).result(); -} - -any_expr convert(any_expr val, const StringOperator&) { - struct StringConverter : FwdVisitor { - explicit StringConverter(any_expr val) : res(std::move(val)) {} - - void visit(Expr&) final { typeError(); } - - // defined for the following types - void visit(StringVal&) final {} - - // need to convert values - void visit(BoolVal& el) final { res = toany_expr(toString(el.value())); } - - void visit(IntVal& el) final { res = toany_expr(toString(el.value())); } - - void visit(UintVal& el) final { res = toany_expr(toString(el.value())); } - - void visit(DoubleVal& el) final { res = toany_expr(toString(el.value())); } - - void visit(NullVal& el) final { res = toany_expr(toString(el.value())); } - - any_expr result() && { return std::move(res); } - - private: - any_expr res; - }; - - Expr* node = val.get(); - StringConverter conv{std::move(val)}; - - node->accept(conv); - return std::move(conv).result(); -} - -template -struct BinaryOperatorVisitor_ : FwdVisitor { - using result_type = typename BinaryOperator::result_type; - - BinaryOperatorVisitor_(LhsValue lval, BinaryOperator oper) - : lv(lval), op(oper), res() {} - - template - void calc(RhsValue rv) { - auto [ll, rr] = op.coerce(lv, rv); - - res = op(ll, rr); - } - - void visit(Expr&) final { typeError(); } - - void visit(StringVal& n) final { - if constexpr (BinaryOperator::definedForString) return calc(&n.value()); - - typeError(); - } - - void visit(NullVal&) final { - if constexpr (BinaryOperator::definedForNull) return calc(nullptr); - - typeError(); - } - - void visit(BoolVal& n) final { - if constexpr (BinaryOperator::definedForBool) return calc(&n.value()); - - typeError(); - } - - void visit(IntVal& n) final { - if constexpr (BinaryOperator::definedForInteger) return calc(&n.value()); - - typeError(); - } - - void visit(UintVal& n) final { - if constexpr (BinaryOperator::definedForInteger) return calc(&n.value()); - - typeError(); - } - - void visit(DoubleVal& n) final { - if constexpr (BinaryOperator::definedForDouble) return calc(&n.value()); - - typeError(); - } - - void visit(Array&) final { - unsupported(); // for now - } - - result_type result() && { return std::move(res); } - - private: - LhsValue lv; - BinaryOperator op; - result_type res; -}; - -template -struct BinaryOperatorVisitor : FwdVisitor { - using result_type = typename BinaryOperator::result_type; - - BinaryOperatorVisitor(BinaryOperator oper, any_expr& rhsarg) - : op(oper), rhs(rhsarg), res() {} - - template - void calc(LhsValue lv) { - using RhsVisitor = BinaryOperatorVisitor_; - - RhsVisitor vis{lv, op}; - - rhs->accept(vis); - res = std::move(vis).result(); - } - - void visit(StringVal& n) final { - if constexpr (BinaryOperator::definedForString) return calc(&n.value()); - - typeError(); - } - - void visit(NullVal&) final { - if constexpr (BinaryOperator::definedForNull) return calc(nullptr); - - typeError(); - } - - void visit(BoolVal& n) final { - if constexpr (BinaryOperator::definedForBool) return calc(&n.value()); - - typeError(); - } - - void visit(IntVal& n) final { - if constexpr (BinaryOperator::definedForInteger) return calc(&n.value()); - - typeError(); - } - - void visit(UintVal& n) final { - if constexpr (BinaryOperator::definedForInteger) return calc(&n.value()); - - typeError(); - } - - void visit(DoubleVal& n) final { - if constexpr (BinaryOperator::definedForDouble) return calc(&n.value()); - - typeError(); - } - - void visit(Array&) final { - typeError(); // for now - } - - result_type result() && { return std::move(res); } - - private: - BinaryOperator op; - any_expr& rhs; - result_type res; -}; - -template -typename BinaryOperator::result_type compute(any_expr& lhs, any_expr& rhs, - BinaryOperator op) { - using LhsVisitor = BinaryOperatorVisitor; - - LhsVisitor vis{op, rhs}; - - assert(lhs.get()); - lhs->accept(vis); - return std::move(vis).result(); -} - -template -struct Calc {}; - -template <> -struct Calc : LogicalBinaryOperator { - using LogicalBinaryOperator::result_type; - - template - result_type operator()(const T& lhs, const T& rhs) const { - return lhs == rhs; - } -}; - -template <> -struct Calc : LogicalBinaryOperator { - using LogicalBinaryOperator::result_type; - - template - result_type operator()(const T& lhs, const T& rhs) const { - return lhs != rhs; - } -}; - -template <> -struct Calc : StrictLogicalBinaryOperator { - using StrictLogicalBinaryOperator::result_type; - - result_type operator()(...) const { return false; } // type mismatch - - template - result_type operator()(const T& lhs, const T& rhs) const { - return lhs == rhs; - } -}; - -template <> -struct Calc : StrictLogicalBinaryOperator { - using StrictLogicalBinaryOperator::result_type; - - result_type operator()(...) const { return false; } // type mismatch - - template - result_type operator()(const T& lhs, const T& rhs) const { - return lhs == rhs; - } -}; - -template <> -struct Calc : LogicalBinaryOperator { - using LogicalBinaryOperator::result_type; - - template - result_type operator()(const T& lhs, const T& rhs) const { - return lhs < rhs; - } -}; - -template <> -struct Calc : LogicalBinaryOperator { - using LogicalBinaryOperator::result_type; - - template - result_type operator()(const T& lhs, const T& rhs) const { - return rhs < lhs; - } -}; - -template <> -struct Calc : LogicalBinaryOperator { - using LogicalBinaryOperator::result_type; - - template - result_type operator()(const T& lhs, const T& rhs) const { - return lhs <= rhs; - } -}; - -template <> -struct Calc : LogicalBinaryOperator { - using LogicalBinaryOperator::result_type; - - template - result_type operator()(const T& lhs, const T& rhs) const { - return rhs <= lhs; - } -}; - -template <> -struct Calc : ArithmeticOperator { - using ArithmeticOperator::result_type; - - result_type operator()(std::nullptr_t, std::nullptr_t) const { - return toany_expr(nullptr); - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return toany_expr(lhs + rhs); - } -}; - -template <> -struct Calc : ArithmeticOperator { - using ArithmeticOperator::result_type; - - result_type operator()(std::nullptr_t, std::nullptr_t) const { - return toany_expr(nullptr); - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return toany_expr(lhs - rhs); - } -}; - -template <> -struct Calc : ArithmeticOperator { - using ArithmeticOperator::result_type; - - result_type operator()(std::nullptr_t, std::nullptr_t) const { - return toany_expr(nullptr); - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return toany_expr(lhs * rhs); - } -}; - -template <> -struct Calc
: ArithmeticOperator { - using ArithmeticOperator::result_type; - - result_type operator()(std::nullptr_t, std::nullptr_t) const { - return toany_expr(nullptr); - } - - result_type operator()(double lhs, double rhs) const { - double res = lhs / rhs; - - // if (isInteger(res)) return toInt(res); - return toany_expr(res); - } - - template - result_type operator()(Int_t lhs, Int_t rhs) const { - if (lhs % rhs) return (*this)(double(lhs), double(rhs)); - - return toany_expr(lhs / rhs); - } -}; - -template <> -struct Calc : IntegerArithmeticOperator { - using IntegerArithmeticOperator::result_type; - - std::nullptr_t operator()(std::nullptr_t, std::nullptr_t) const { - return nullptr; - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - if (rhs == 0) return toany_expr(nullptr); - - return toany_expr(lhs % rhs); - } -}; - -template <> -struct Calc : ArithmeticOperator { - using ArithmeticOperator::result_type; - - template - result_type operator()(const T& lhs, const T& rhs) const { - return toany_expr(std::min(lhs, rhs)); - } -}; - -template <> -struct Calc : ArithmeticOperator { - using ArithmeticOperator::result_type; - - template - result_type operator()(const T& lhs, const T& rhs) const { - return toany_expr(std::max(lhs, rhs)); - } -}; - -template <> -struct Calc { - using result_type = bool; - - result_type operator()(Expr& val) const { return !toBool(val); } -}; - -template <> -struct Calc { - using result_type = bool; - - result_type operator()(Expr& val) const { return toBool(val); } -}; - -template <> -struct Calc : StringOperator { - using StringOperator::result_type; - - result_type operator()(const json::string& lhs, - const json::string& rhs) const { - json::string tmp; - - tmp.reserve(lhs.size() + rhs.size()); - - tmp.append(lhs.begin(), lhs.end()); - tmp.append(rhs.begin(), rhs.end()); - - return toany_expr(std::move(tmp)); - } -}; - -struct Calculator : FwdVisitor { - using VarAccess = std::function; - - Calculator(const VarAccess& varAccess, std::ostream& out) - : vars(varAccess), logger(out), calcres(nullptr) {} - - void visit(Eq&) final; - void visit(StrictEq&) final; - void visit(Neq&) final; - void visit(StrictNeq&) final; - void visit(Less&) final; - void visit(Greater&) final; - void visit(Leq&) final; - void visit(Geq&) final; - void visit(And&) final; - void visit(Or&) final; - void visit(Not&) final; - void visit(NotNot&) final; - void visit(Add&) final; - void visit(Sub&) final; - void visit(Mul&) final; - void visit(Div&) final; - void visit(Mod&) final; - void visit(Min&) final; - void visit(Max&) final; - void visit(Array&) final; - void visit(Map&) final; - void visit(Reduce&) final; - void visit(Filter&) final; - void visit(All&) final; - void visit(None&) final; - void visit(Some&) final; - void visit(Merge&) final; - void visit(Cat&) final; - void visit(Substr&) final; - void visit(In&) final; - void visit(Var&) final; - void visit(Log&) final; - - void visit(NullVal& n) final; - void visit(BoolVal& n) final; - void visit(IntVal& n) final; - void visit(UintVal& n) final; - void visit(DoubleVal& n) final; - void visit(StringVal& n) final; - - void visit(Error& n) final; - - any_expr eval(Expr& n); - - private: - const VarAccess& vars; - std::ostream& logger; - any_expr calcres; - - Calculator(const Calculator&) = delete; - Calculator(Calculator&&) = delete; - Calculator& operator=(const Calculator&) = delete; - Calculator& operator=(Calculator&&) = delete; - - // - // opers - - /// implements relop : [1, 2, 3, whatever] - template - void evalPairShortCircuit(Operator& n, BinaryPredicate calc) { - const int num = n.num_evaluated_operands(); - assert(num >= 2); - - bool res = true; - int idx = -1; - any_expr rhs = eval(n.operand(++idx)); - assert(rhs.get()); - - while (res && (idx != (num - 1))) { - any_expr lhs = std::move(rhs); - assert(lhs.get()); - - rhs = eval(n.operand(++idx)); - assert(rhs.get()); - - res = compute(lhs, rhs, calc); - } - - calcres = toany_expr(res); - } - - template - void reduce(Operator& n, BinaryOperator op) { - const int num = n.num_evaluated_operands(); - assert(num >= 1); - - int idx = -1; - any_expr res = eval(n.operand(++idx)); - - res = convert(std::move(res), op); - - while (idx != (num - 1)) { - any_expr rhs = eval(n.operand(++idx)); - - rhs = convert(std::move(rhs), op); - res = compute(res, rhs, op); - } - - calcres = std::move(res); - } - - template - void binary(Operator& n, BinaryOperator calc) { - const int num = n.num_evaluated_operands(); - assert(num == 1 || num == 2); - - int idx = -1; - any_expr lhs; - - if (num == 2) { - CXX_LIKELY; - lhs = eval(n.operand(++idx)); - } else { - lhs = toany_expr(std::int64_t(0)); - } - - any_expr rhs = eval(n.operand(++idx)); - - calcres = compute(lhs, rhs, calc); - } - - template - void unary(Operator& n, UnaryOperator calc) { - const int num = n.num_evaluated_operands(); - assert(num == 1); - - const bool res = calc(*eval(n.operand(0))); - - calcres = toany_expr(res); - } - - void evalShortCircuit(Operator& n, bool val) { - const int num = n.num_evaluated_operands(); - assert(num >= 1); - - int idx = -1; - any_expr oper = eval(n.operand(++idx)); - bool found = (idx == num - 1) || (toBool(*oper) == val); - - // loop until *aa == val or when *aa is the last valid element - while (!found) { - oper = eval(n.operand(++idx)); - - found = (idx == (num - 1)) || (toBool(*oper) == val); - } - - calcres = std::move(oper); - } - - template - void _value(const ValueNode& val) { - calcres = toany_expr(val.value()); - } -}; - -any_expr Calculator::eval(Expr& n) { - any_expr res; - - n.accept(*this); - res.swap(calcres); - - return res; -} - -void Calculator::visit(Eq& n) { evalPairShortCircuit(n, Calc{}); } - -void Calculator::visit(StrictEq& n) { - evalPairShortCircuit(n, Calc{}); -} - -void Calculator::visit(Neq& n) { evalPairShortCircuit(n, Calc{}); } - -void Calculator::visit(StrictNeq& n) { - evalPairShortCircuit(n, Calc{}); -} - -void Calculator::visit(Less& n) { evalPairShortCircuit(n, Calc{}); } - -void Calculator::visit(Greater& n) { evalPairShortCircuit(n, Calc{}); } - -void Calculator::visit(Leq& n) { evalPairShortCircuit(n, Calc{}); } - -void Calculator::visit(Geq& n) { evalPairShortCircuit(n, Calc{}); } - -void Calculator::visit(And& n) { evalShortCircuit(n, false); } - -void Calculator::visit(Or& n) { evalShortCircuit(n, true); } - -void Calculator::visit(Not& n) { unary(n, Calc{}); } - -void Calculator::visit(NotNot& n) { unary(n, Calc{}); } - -void Calculator::visit(Add& n) { reduce(n, Calc{}); } - -void Calculator::visit(Sub& n) { binary(n, Calc{}); } - -void Calculator::visit(Mul& n) { reduce(n, Calc{}); } - -void Calculator::visit(Div& n) { binary(n, Calc
{}); } - -void Calculator::visit(Mod& n) { binary(n, Calc{}); } - -void Calculator::visit(Min& n) { reduce(n, Calc{}); } - -void Calculator::visit(Max& n) { reduce(n, Calc{}); } - -void Calculator::visit(Cat& n) { reduce(n, Calc{}); } - -void Calculator::visit(Substr&) { unsupported(); } -void Calculator::visit(Array&) { unsupported(); } -void Calculator::visit(Map&) { unsupported(); } -void Calculator::visit(Reduce&) { unsupported(); } -void Calculator::visit(Filter&) { unsupported(); } -void Calculator::visit(All&) { unsupported(); } -void Calculator::visit(None&) { unsupported(); } -void Calculator::visit(Some&) { unsupported(); } -void Calculator::visit(Merge&) { unsupported(); } -void Calculator::visit(In&) { unsupported(); } - -void Calculator::visit(Error&) { unsupported(); } - -void Calculator::visit(Var& n) { - assert(n.num_evaluated_operands() == 1); - - any_expr elm = convert(eval(n.operand(0)), StringOperator{}); - - if (StringVal* str = dynamic_cast(elm.get())) { - CXX_LIKELY; - calcres = vars(str->value(), n.num()); - return; - } - - typeError(); -} - -void Calculator::visit(Log&) { - const int num = n.num_evaluated_operands(); - assert(num == 1); - - calcres = calc(*eval(n.operand(0))); - - std::cerr << calcres << std::endl; -} - -void Calculator::visit(NullVal& n) { _value(n); } -void Calculator::visit(BoolVal& n) { _value(n); } -void Calculator::visit(IntVal& n) { _value(n); } -void Calculator::visit(UintVal& n) { _value(n); } -void Calculator::visit(DoubleVal& n) { _value(n); } -void Calculator::visit(StringVal& n) { _value(n); } - -void traverseInSAttributeOrder(Expr& e, Visitor& vis) { - SAttributeTraversal trav{vis}; - - e.accept(trav); -} - -any_expr calculate(any_expr& exp, const Calculator::VarAccess& vars) { - Calculator calc{vars, std::cerr}; - - assert(exp.get()); - return calc.eval(*exp); -} - -any_expr calculate(any_expr& exp) { - return calculate(exp, - [](const json::string&, int) -> any_expr { unsupported(); }); -} - -std::ostream& operator<<(std::ostream& os, any_expr& n) { - struct ValuePrinter : FwdVisitor { - explicit ValuePrinter(std::ostream& stream) : os(stream) {} - - void visit(NullVal&) final { os << toString(nullptr); } - void visit(BoolVal& n) final { os << toString(n.value()); } - void visit(IntVal& n) final { os << n.value(); } - void visit(UintVal& n) final { os << n.value(); } - void visit(DoubleVal& n) final { os << n.value(); } - void visit(StringVal& n) final { os << n.value(); } - void visit(Array&) final { unsupported(); } - - std::ostream& os; - }; - - ValuePrinter prn{os}; - - n->accept(prn); - return os; -} - -// accept implementations -void Eq::accept(Visitor& v) { v.visit(*this); } -void StrictEq::accept(Visitor& v) { v.visit(*this); } -void Neq::accept(Visitor& v) { v.visit(*this); } -void StrictNeq::accept(Visitor& v) { v.visit(*this); } -void Less::accept(Visitor& v) { v.visit(*this); } -void Greater::accept(Visitor& v) { v.visit(*this); } -void Leq::accept(Visitor& v) { v.visit(*this); } -void Geq::accept(Visitor& v) { v.visit(*this); } -void And::accept(Visitor& v) { v.visit(*this); } -void Or::accept(Visitor& v) { v.visit(*this); } -void Not::accept(Visitor& v) { v.visit(*this); } -void NotNot::accept(Visitor& v) { v.visit(*this); } -void Add::accept(Visitor& v) { v.visit(*this); } -void Sub::accept(Visitor& v) { v.visit(*this); } -void Mul::accept(Visitor& v) { v.visit(*this); } -void Div::accept(Visitor& v) { v.visit(*this); } -void Mod::accept(Visitor& v) { v.visit(*this); } -void Min::accept(Visitor& v) { v.visit(*this); } -void Max::accept(Visitor& v) { v.visit(*this); } -void Map::accept(Visitor& v) { v.visit(*this); } -void Reduce::accept(Visitor& v) { v.visit(*this); } -void Filter::accept(Visitor& v) { v.visit(*this); } -void All::accept(Visitor& v) { v.visit(*this); } -void None::accept(Visitor& v) { v.visit(*this); } -void Some::accept(Visitor& v) { v.visit(*this); } -void Array::accept(Visitor& v) { v.visit(*this); } -void Merge::accept(Visitor& v) { v.visit(*this); } -void Cat::accept(Visitor& v) { v.visit(*this); } -void Substr::accept(Visitor& v) { v.visit(*this); } -void In::accept(Visitor& v) { v.visit(*this); } -void Var::accept(Visitor& v) { v.visit(*this); } -void Log::accept(Visitor& v) { v.visit(*this); } -void If::accept(Visitor& v) { v.visit(*this); } - -void NullVal::accept(Visitor& v) { v.visit(*this); } -void BoolVal::accept(Visitor& v) { v.visit(*this); } -void IntVal::accept(Visitor& v) { v.visit(*this); } -void UintVal::accept(Visitor& v) { v.visit(*this); } -void DoubleVal::accept(Visitor& v) { v.visit(*this); } -void StringVal::accept(Visitor& v) { v.visit(*this); } - -void Error::accept(Visitor& v) { v.visit(*this); } - -void FwdVisitor::visit(Expr&) { /* error ? */ } - -// num_evaluated_operands implementations -int Operator::num_evaluated_operands() const { return size(); } - -template -int OperatorN::num_evaluated_operands() const { - return std::min(MaxArity, Operator::num_evaluated_operands()); -} -} // namespace json_logic diff --git a/include/clippy/attic/clippy-eval.hpp b/include/clippy/attic/clippy-eval.hpp deleted file mode 100644 index 06a1320..0000000 --- a/include/clippy/attic/clippy-eval.hpp +++ /dev/null @@ -1,3312 +0,0 @@ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace json_logic { -constexpr bool DEBUG_OUTPUT = false; - -namespace json = boost::json; - -using JsonExpr = json::value; - -struct Expr; - -template -T& up_cast(T& n) { - return n; -} - -template -T& down_cast(Expr& e); - -struct cast_error : std::runtime_error { - using base = std::runtime_error; - using base::base; -}; - -struct type_error : std::runtime_error { - using base = std::runtime_error; - using base::base; -}; - -template -T& deref(T* p, const char* msg = "assertion failed") { - if (p == nullptr) { - CXX_UNLIKELY; - throw Error{msg}; - } - - return *p; -} - -template -T& deref(std::unique_ptr& p, const char* msg = "assertion failed") { - if (p.get() == nullptr) { - CXX_UNLIKELY; - throw Error{msg}; - } - - return *p; -} - -struct Visitor; - -// the root class -struct Expr { - Expr() = default; - virtual ~Expr() = default; - - virtual void accept(Visitor&) = 0; - - private: - Expr(Expr&&) = delete; - Expr(const Expr&) = delete; - Expr& operator=(Expr&&) = delete; - Expr& operator=(const Expr&) = delete; -}; - -//~ Expr::~Expr() {} - -// -// foundation classes -// \{ - -using any_expr = std::unique_ptr; -using any_expr = - std::unique_ptr; // \todo consider std::unique_ptr - -struct Operator : Expr, private std::vector { - using container_type = std::vector; - - using container_type::at; - using container_type::back; - using container_type::begin; - using container_type::const_iterator; - using container_type::const_reverse_iterator; - using container_type::crbegin; - using container_type::crend; - using container_type::end; - using container_type::iterator; - using container_type::push_back; - using container_type::rbegin; - using container_type::rend; - using container_type::reverse_iterator; - using container_type::size; - - // convenience function so that the constructor does not need to be - // implemented in every derived class. - void set_operands(container_type&& opers) { this->swap(opers); } - - container_type& operands() { return *this; } - container_type&& move_operands() && { return std::move(*this); } - - Expr& operand(int n) const { return deref(this->at(n).get()); } - - virtual int num_evaluated_operands() const; -}; - -// defines operators that have an upper bound on how many -// arguments are evaluated. -template -struct OperatorN : Operator { - enum { MAX_OPERANDS = MaxArity }; - - int num_evaluated_operands() const final; -}; - -struct Value : Expr { - virtual JsonExpr toJson() const = 0; -}; - -template -struct ValueT : Value { - using value_type = T; - - explicit ValueT(T t) : val(std::move(t)) {} - - T& value() { return val; } - const T& value() const { return val; } - - JsonExpr toJson() const final; - - private: - T val; -}; - -// \} - -// -// expression hierarchy -// comparison - -// binary -struct Eq : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct StrictEq : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct Neq : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct StrictNeq : OperatorN<2> { - void accept(Visitor&) final; -}; - -// binary or ternary -struct Less : OperatorN<3> { - void accept(Visitor&) final; -}; - -struct Greater : OperatorN<3> { - void accept(Visitor&) final; -}; - -struct Leq : OperatorN<3> { - void accept(Visitor&) final; -}; - -struct Geq : OperatorN<3> { - void accept(Visitor&) final; -}; - -// logical operators - -// unary -struct Not : OperatorN<1> { - void accept(Visitor&) final; -}; - -struct NotNot : OperatorN<1> { - void accept(Visitor&) final; -}; - -// n-ary -struct And : Operator { - void accept(Visitor&) final; -}; - -struct Or : Operator { - void accept(Visitor&) final; -}; - -// control structure -struct If : Operator { - void accept(Visitor&) final; -}; - -// arithmetics -// n-ary -struct Add : Operator { - void accept(Visitor&) final; -}; - -struct Mul : Operator { - void accept(Visitor&) final; -}; - -struct Min : Operator { - void accept(Visitor&) final; -}; - -struct Max : Operator { - void accept(Visitor&) final; -}; - -// binary -struct Sub : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct Div : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct Mod : OperatorN<2> { - void accept(Visitor&) final; -}; - -// array - -// arrays serve a dual purpose -// they can be considered collections, but also an aggregate value -// The class is final and it supports move ctor/assignment, so the data -// can move efficiently. -struct Array final : Operator // array is modeled as operator -{ - void accept(Visitor&) final; - - // define - Array() = default; - Array(Array&&); - Array& operator=(Array&&); -}; - -Array::Array(Array&& other) : Operator() { - set_operands(std::move(other).move_operands()); -} - -Array& Array::operator=(Array&& other) { - set_operands(std::move(other).move_operands()); - return *this; -} - -struct Map : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct Reduce : OperatorN<3> { - void accept(Visitor&) final; -}; - -struct Filter : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct All : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct None : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct Some : OperatorN<2> { - void accept(Visitor&) final; -}; - -struct Merge : Operator { - void accept(Visitor&) final; -}; - -// data access -struct Var : Operator { - enum { computed = -1 }; - - void accept(Visitor&) final; - - void num(int val) { idx = val; } - int num() const { return idx; } - - private: - int idx = computed; -}; - -/// Missing is modeled as operator with arbitrary number of arguments -/// \details -/// In Calculator::visit(Missing&): -/// If the first argument is an array, only the array will be considered -/// otherwise all operands are treated as array. -struct Missing : Operator { - void accept(Visitor&) final; -}; - -struct MissingSome : OperatorN<2> { - void accept(Visitor&) final; -}; - -// string operations -struct Cat : Operator { - void accept(Visitor&) final; -}; - -struct Substr : OperatorN<3> { - void accept(Visitor&) final; -}; - -// string and array operation -struct In : Operator { - void accept(Visitor&) final; -}; - -// values -struct NullVal : Value { - NullVal() = default; - NullVal(std::nullptr_t) {} - - void accept(Visitor&) final; - - std::nullptr_t value() const { return nullptr; } - - JsonExpr toJson() const final; -}; - -struct BoolVal : ValueT { - using base = ValueT; - using base::base; - - void accept(Visitor&) final; -}; - -struct IntVal : ValueT { - using base = ValueT; - using base::base; - - void accept(Visitor&) final; -}; - -struct UintVal : ValueT { - using base = ValueT; - using base::base; - - void accept(Visitor&) final; -}; - -struct DoubleVal : ValueT { - using base = ValueT; - using base::base; - - void accept(Visitor&) final; -}; - -struct StringVal : ValueT { - using base = ValueT; - using base::base; - - void accept(Visitor&) final; -}; - -struct ObjectVal : Expr, private std::map { - using base = std::map; - using base::base; - - using base::begin; - using base::const_iterator; - using base::end; - using base::find; - using base::insert; - using base::iterator; - using base::value_type; - - base& elements() { return *this; } - - void accept(Visitor&) final; -}; - -// logger -struct Log : OperatorN<1> { - void accept(Visitor&) final; -}; - -// error node -struct Error : Expr { - void accept(Visitor&) final; -}; - -// -// jsonlogic extensions -// - -struct RegexMatch : OperatorN<2> { - void accept(Visitor&) final; -}; - -// Visitor -struct Visitor { - virtual void visit(Expr&) = 0; // error - virtual void visit(Operator& n) = 0; - virtual void visit(Eq&) = 0; - virtual void visit(StrictEq&) = 0; - virtual void visit(Neq&) = 0; - virtual void visit(StrictNeq&) = 0; - virtual void visit(Less&) = 0; - virtual void visit(Greater&) = 0; - virtual void visit(Leq&) = 0; - virtual void visit(Geq&) = 0; - virtual void visit(And&) = 0; - virtual void visit(Or&) = 0; - virtual void visit(Not&) = 0; - virtual void visit(NotNot&) = 0; - virtual void visit(Add&) = 0; - virtual void visit(Sub&) = 0; - virtual void visit(Mul&) = 0; - virtual void visit(Div&) = 0; - virtual void visit(Mod&) = 0; - virtual void visit(Min&) = 0; - virtual void visit(Max&) = 0; - virtual void visit(Map&) = 0; - virtual void visit(Reduce&) = 0; - virtual void visit(Filter&) = 0; - virtual void visit(All&) = 0; - virtual void visit(None&) = 0; - virtual void visit(Some&) = 0; - virtual void visit(Array&) = 0; - virtual void visit(Merge&) = 0; - virtual void visit(Cat&) = 0; - virtual void visit(Substr&) = 0; - virtual void visit(In&) = 0; - virtual void visit(Var&) = 0; - virtual void visit(Missing&) = 0; - virtual void visit(MissingSome&) = 0; - virtual void visit(Log&) = 0; - - // control structure - virtual void visit(If&) = 0; - - // values - virtual void visit(Value&) = 0; - virtual void visit(NullVal&) = 0; - virtual void visit(BoolVal&) = 0; - virtual void visit(IntVal&) = 0; - virtual void visit(UintVal&) = 0; - virtual void visit(DoubleVal&) = 0; - virtual void visit(StringVal&) = 0; - //~ virtual void visit(ObjectVal&) = 0; - - virtual void visit(Error&) = 0; - - // extensions - virtual void visit(RegexMatch&) = 0; -}; - -// accept implementations -void Eq::accept(Visitor& v) { v.visit(*this); } -void StrictEq::accept(Visitor& v) { v.visit(*this); } -void Neq::accept(Visitor& v) { v.visit(*this); } -void StrictNeq::accept(Visitor& v) { v.visit(*this); } -void Less::accept(Visitor& v) { v.visit(*this); } -void Greater::accept(Visitor& v) { v.visit(*this); } -void Leq::accept(Visitor& v) { v.visit(*this); } -void Geq::accept(Visitor& v) { v.visit(*this); } -void And::accept(Visitor& v) { v.visit(*this); } -void Or::accept(Visitor& v) { v.visit(*this); } -void Not::accept(Visitor& v) { v.visit(*this); } -void NotNot::accept(Visitor& v) { v.visit(*this); } -void Add::accept(Visitor& v) { v.visit(*this); } -void Sub::accept(Visitor& v) { v.visit(*this); } -void Mul::accept(Visitor& v) { v.visit(*this); } -void Div::accept(Visitor& v) { v.visit(*this); } -void Mod::accept(Visitor& v) { v.visit(*this); } -void Min::accept(Visitor& v) { v.visit(*this); } -void Max::accept(Visitor& v) { v.visit(*this); } -void Map::accept(Visitor& v) { v.visit(*this); } -void Reduce::accept(Visitor& v) { v.visit(*this); } -void Filter::accept(Visitor& v) { v.visit(*this); } -void All::accept(Visitor& v) { v.visit(*this); } -void None::accept(Visitor& v) { v.visit(*this); } -void Some::accept(Visitor& v) { v.visit(*this); } -void Array::accept(Visitor& v) { v.visit(*this); } -void Merge::accept(Visitor& v) { v.visit(*this); } -void Cat::accept(Visitor& v) { v.visit(*this); } -void Substr::accept(Visitor& v) { v.visit(*this); } -void In::accept(Visitor& v) { v.visit(*this); } -void Var::accept(Visitor& v) { v.visit(*this); } -void Missing::accept(Visitor& v) { v.visit(*this); } -void MissingSome::accept(Visitor& v) { v.visit(*this); } -void Log::accept(Visitor& v) { v.visit(*this); } -void If::accept(Visitor& v) { v.visit(*this); } - -void NullVal::accept(Visitor& v) { v.visit(*this); } -void BoolVal::accept(Visitor& v) { v.visit(*this); } -void IntVal::accept(Visitor& v) { v.visit(*this); } -void UintVal::accept(Visitor& v) { v.visit(*this); } -void DoubleVal::accept(Visitor& v) { v.visit(*this); } -void StringVal::accept(Visitor& v) { v.visit(*this); } - -void Error::accept(Visitor& v) { v.visit(*this); } -void RegexMatch::accept(Visitor& v) { v.visit(*this); } - -// toJson implementations -template -JsonExpr ValueT::toJson() const { - return value(); -} - -JsonExpr NullVal::toJson() const { return value(); } - -// num_evaluated_operands implementations -int Operator::num_evaluated_operands() const { return size(); } - -template -int OperatorN::num_evaluated_operands() const { - return std::min(MaxArity, Operator::num_evaluated_operands()); -} - -struct FwdVisitor : Visitor { - void visit(Expr&) override {} // error - void visit(Operator& n) override { visit(up_cast(n)); } - void visit(Eq& n) override { visit(up_cast(n)); } - void visit(StrictEq& n) override { visit(up_cast(n)); } - void visit(Neq& n) override { visit(up_cast(n)); } - void visit(StrictNeq& n) override { visit(up_cast(n)); } - void visit(Less& n) override { visit(up_cast(n)); } - void visit(Greater& n) override { visit(up_cast(n)); } - void visit(Leq& n) override { visit(up_cast(n)); } - void visit(Geq& n) override { visit(up_cast(n)); } - void visit(And& n) override { visit(up_cast(n)); } - void visit(Or& n) override { visit(up_cast(n)); } - void visit(Not& n) override { visit(up_cast(n)); } - void visit(NotNot& n) override { visit(up_cast(n)); } - void visit(Add& n) override { visit(up_cast(n)); } - void visit(Sub& n) override { visit(up_cast(n)); } - void visit(Mul& n) override { visit(up_cast(n)); } - void visit(Div& n) override { visit(up_cast(n)); } - void visit(Mod& n) override { visit(up_cast(n)); } - void visit(Min& n) override { visit(up_cast(n)); } - void visit(Max& n) override { visit(up_cast(n)); } - void visit(Map& n) override { visit(up_cast(n)); } - void visit(Reduce& n) override { visit(up_cast(n)); } - void visit(Filter& n) override { visit(up_cast(n)); } - void visit(All& n) override { visit(up_cast(n)); } - void visit(None& n) override { visit(up_cast(n)); } - void visit(Some& n) override { visit(up_cast(n)); } - void visit(Array& n) override { visit(up_cast(n)); } - void visit(Merge& n) override { visit(up_cast(n)); } - void visit(Cat& n) override { visit(up_cast(n)); } - void visit(Substr& n) override { visit(up_cast(n)); } - void visit(In& n) override { visit(up_cast(n)); } - void visit(Var& n) override { visit(up_cast(n)); } - void visit(Missing& n) override { visit(up_cast(n)); } - void visit(MissingSome& n) override { visit(up_cast(n)); } - void visit(Log& n) override { visit(up_cast(n)); } - - void visit(If& n) override { visit(up_cast(n)); } - - void visit(Value& n) override { visit(up_cast(n)); } - void visit(NullVal& n) override { visit(up_cast(n)); } - void visit(BoolVal& n) override { visit(up_cast(n)); } - void visit(IntVal& n) override { visit(up_cast(n)); } - void visit(UintVal& n) override { visit(up_cast(n)); } - void visit(DoubleVal& n) override { visit(up_cast(n)); } - void visit(StringVal& n) override { visit(up_cast(n)); } - - void visit(Error& n) override { visit(up_cast(n)); } - - // extensions - void visit(RegexMatch& n) override { visit(up_cast(n)); } -}; - -template -struct GVisitor : FwdVisitor { - explicit GVisitor(UnderVisitorT& selfref) : self(selfref) {} - - // list of all concrete types - void visit(Eq& n) final { self.gvisit(n); } - void visit(StrictEq& n) final { self.gvisit(n); } - void visit(Neq& n) final { self.gvisit(n); } - void visit(StrictNeq& n) final { self.gvisit(n); } - void visit(Less& n) final { self.gvisit(n); } - void visit(Greater& n) final { self.gvisit(n); } - void visit(Leq& n) final { self.gvisit(n); } - void visit(Geq& n) final { self.gvisit(n); } - void visit(And& n) final { self.gvisit(n); } - void visit(Or& n) final { self.gvisit(n); } - void visit(Not& n) final { self.gvisit(n); } - void visit(NotNot& n) final { self.gvisit(n); } - void visit(Add& n) final { self.gvisit(n); } - void visit(Sub& n) final { self.gvisit(n); } - void visit(Mul& n) final { self.gvisit(n); } - void visit(Div& n) final { self.gvisit(n); } - void visit(Mod& n) final { self.gvisit(n); } - void visit(Min& n) final { self.gvisit(n); } - void visit(Max& n) final { self.gvisit(n); } - void visit(Map& n) final { self.gvisit(n); } - void visit(Reduce& n) final { self.gvisit(n); } - void visit(Filter& n) final { self.gvisit(n); } - void visit(All& n) final { self.gvisit(n); } - void visit(None& n) final { self.gvisit(n); } - void visit(Some& n) final { self.gvisit(n); } - void visit(Array& n) final { self.gvisit(n); } - void visit(Merge& n) final { self.gvisit(n); } - void visit(Cat& n) final { self.gvisit(n); } - void visit(Substr& n) final { self.gvisit(n); } - void visit(In& n) final { self.gvisit(n); } - void visit(Var& n) final { self.gvisit(n); } - void visit(Missing& n) final { self.gvisit(n); } - void visit(MissingSome& n) final { self.gvisit(n); } - void visit(Log& n) final { self.gvisit(n); } - - void visit(If& n) final { self.gvisit(n); } - - // void visit(Value& n) final { self.gvisit(n); } - void visit(NullVal& n) final { self.gvisit(n); } - void visit(BoolVal& n) final { self.gvisit(n); } - void visit(IntVal& n) final { self.gvisit(n); } - void visit(UintVal& n) final { self.gvisit(n); } - void visit(DoubleVal& n) final { self.gvisit(n); } - void visit(StringVal& n) final { self.gvisit(n); } - - void visit(Error& n) final { self.gvisit(n); } - void visit(RegexMatch& n) final { self.gvisit(n); } - - private: - UnderVisitorT& self; -}; - -namespace { -CXX_NORETURN -void unsupported() { throw std::logic_error("not yet implemented"); } - -CXX_NORETURN -void typeError() { throw type_error("typing error"); } - -CXX_NORETURN -void requiresArgumentError() { - throw std::runtime_error("insufficient arguments"); -} - -struct VarMap { - void insert(Var& el); - std::vector toVector() const; - bool hasComputedVariables() const { return hasComputed; } - - private: - using container_type = std::map; - - container_type mapping = {}; - bool hasComputed = false; -}; - -void VarMap::insert(Var& var) { - try { - any_expr& arg = var.back(); - StringVal& str = down_cast(*arg); - const bool comp = (str.value().find('.') != json::string::npos && - str.value().find('[') != json::string::npos); - - if (comp) { - hasComputed = true; - } else if (str.value() != "") // do nothing for free variables in "lambdas" - { - auto [pos, success] = mapping.emplace(str.value(), mapping.size()); - - var.num(pos->second); - } - } catch (const cast_error&) { - hasComputed = true; - } -} - -std::vector VarMap::toVector() const { - std::vector res; - - res.resize(mapping.size()); - - for (const container_type::value_type& el : mapping) - res.at(el.second) = el.first; - - return res; -} - -/// translates all children -/// \{ -Operator::container_type translateChildren(json::array& children, VarMap&); - -Operator::container_type translateChildren(JsonExpr& n, VarMap&); -/// \} - -template -ExprT& mkOperator_(json::object& n, VarMap& m) { - assert(n.size() == 1); - - ExprT& res = deref(new ExprT); - - res.set_operands(translateChildren(n.begin()->value(), m)); - return res; -} - -template -Expr& mkOperator(json::object& n, VarMap& m) { - return mkOperator_(n, m); -} - -Expr& mkVar(json::object& n, VarMap& m) { - Var& v = mkOperator_(n, m); - - m.insert(v); - return v; -} - -Array& mkArrayOperator(json::array& children, VarMap& m) { - Array& res = deref(new Array); - - res.set_operands(translateChildren(children, m)); - return res; -} - -template -ValueT& mkValue(typename ValueT::value_type n) { - return deref(new ValueT(std::move(n))); -} - -NullVal& mkNullValue() { return deref(new NullVal); } - -using DispatchTable = std::map; - -DispatchTable::const_iterator lookup(const DispatchTable& m, - const json::object& op) { - if (op.size() != 1) return m.end(); - - return m.find(op.begin()->key()); -} - -any_expr translateNode_internal(JsonExpr& n, VarMap& varmap) { - static const DispatchTable dt = { - {"==", &mkOperator}, - {"===", &mkOperator}, - {"!=", &mkOperator}, - {"!==", &mkOperator}, - {"if", &mkOperator}, - {"!", &mkOperator}, - {"!!", &mkOperator}, - {"or", &mkOperator}, - {"and", &mkOperator}, - {">", &mkOperator}, - {">=", &mkOperator}, - {"<", &mkOperator}, - {"<=", &mkOperator}, - {"max", &mkOperator}, - {"min", &mkOperator}, - {"+", &mkOperator}, - {"-", &mkOperator}, - {"*", &mkOperator}, - {"/", &mkOperator
}, - {"%", &mkOperator}, - {"map", &mkOperator}, - {"reduce", &mkOperator}, - {"filter", &mkOperator}, - {"all", &mkOperator}, - {"none", &mkOperator}, - {"some", &mkOperator}, - {"merge", &mkOperator}, - {"in", &mkOperator}, - {"cat", &mkOperator}, - {"log", &mkOperator}, - {"var", &mkVar}, - {"missing", &mkOperator}, - {"missing_some", &mkOperator}, - /// extensions - {"regex", &mkOperator}, - }; - - Expr* res = nullptr; - - switch (n.kind()) { - case json::kind::object: { - json::object& obj = n.get_object(); - DispatchTable::const_iterator pos = lookup(dt, obj); - - if (pos != dt.end()) { - CXX_LIKELY; - res = &pos->second(obj, varmap); - } else { - // does json_logic support value objects? - - unsupported(); - } - - break; - } - - case json::kind::array: { - // array is an operator that combines its subexpressions into an array - res = &mkArrayOperator(n.get_array(), varmap); - break; - } - - case json::kind::string: { - res = &mkValue(std::move(n.get_string())); - break; - } - - case json::kind::int64: { - res = &mkValue(n.get_int64()); - break; - } - - case json::kind::uint64: { - res = &mkValue(n.get_uint64()); - break; - } - - case json::kind::double_: { - res = &mkValue(n.get_double()); - break; - } - - case json::kind::bool_: { - res = &mkValue(n.get_bool()); - break; - } - - case json::kind::null: { - res = &mkNullValue(); - break; - } - - default: - unsupported(); - } - - return any_expr(res); -} - -inline std::tuple, bool> translateNode( - JsonExpr& n) { - VarMap varmap; - any_expr node = translateNode_internal(n, varmap); - bool hasComputedVariables = varmap.hasComputedVariables(); - - return {std::move(node), varmap.toVector(), hasComputedVariables}; -} - -Operator::container_type translateChildren(json::array& children, - VarMap& varmap) { - Operator::container_type res; - - res.reserve(children.size()); - - for (JsonExpr& elem : children) - res.emplace_back(translateNode_internal(elem, varmap)); - - return res; -} - -Operator::container_type translateChildren(JsonExpr& n, VarMap& varmap) { - if (json::array* arr = n.if_array()) { - CXX_LIKELY; - return translateChildren(*arr, varmap); - } - - Operator::container_type res; - - res.emplace_back(translateNode_internal(n, varmap)); - return res; -} -} // namespace - -std::ostream& operator<<(std::ostream& os, any_expr& n); - -// -// to value conversion - -any_expr toany_expr(const json::value& n); - -any_expr toany_expr(std::nullptr_t) { return any_expr(new NullVal); } -any_expr toany_expr(bool val) { return any_expr(new BoolVal(val)); } -any_expr toany_expr(std::int64_t val) { return any_expr(new IntVal(val)); } -any_expr toany_expr(std::uint64_t val) { return any_expr(new UintVal(val)); } -any_expr toany_expr(double val) { return any_expr(new DoubleVal(val)); } -any_expr toany_expr(json::string val) { - return any_expr(new StringVal(std::move(val))); -} - -any_expr toany_expr(const json::array& val) { - Operator::container_type elems; - - std::transform( - val.begin(), val.end(), std::back_inserter(elems), - [](const json::value& el) -> any_expr { return toany_expr(el); }); - - Array& arr = deref(new Array); - - arr.set_operands(std::move(elems)); - return any_expr(&arr); -} - -CXX_MAYBE_UNUSED -any_expr toany_expr(const json::value& n) { - any_expr res; - - switch (n.kind()) { - case json::kind::string: { - res = toany_expr(n.get_string()); - break; - } - - case json::kind::int64: { - res = toany_expr(n.get_int64()); - break; - } - - case json::kind::uint64: { - res = toany_expr(n.get_uint64()); - break; - } - - case json::kind::double_: { - res = toany_expr(n.get_double()); - break; - } - - case json::kind::bool_: { - res = toany_expr(n.get_bool()); - break; - } - - case json::kind::null: { - res = toany_expr(nullptr); - break; - } - - case json::kind::array: { - res = toany_expr(n.get_array()); - break; - } - - default: - unsupported(); - } - - assert(res.get()); - return res; -} - -// -// coercion functions - -struct CoercionError {}; -struct OutsideOfInt64Range : CoercionError {}; -struct OutsideOfUint64Range : CoercionError {}; -struct UnpackedArrayRequired : CoercionError {}; - -/// conversion to int64 -/// \{ -inline std::int64_t toConcreteValue(std::int64_t v, const std::int64_t&) { - return v; -} -inline std::int64_t toConcreteValue(const json::string& str, - const std::int64_t&) { - return std::stoll(std::string{str.c_str()}); -} -inline std::int64_t toConcreteValue(double v, const std::int64_t&) { return v; } -inline std::int64_t toConcreteValue(bool v, const std::int64_t&) { return v; } -inline std::int64_t toConcreteValue(std::nullptr_t, const std::int64_t&) { - return 0; -} - -inline std::int64_t toConcreteValue(std::uint64_t v, const std::int64_t&) { - if (v > std::uint64_t(std::numeric_limits::max())) { - CXX_UNLIKELY; - throw OutsideOfInt64Range{}; - } - - return v; -} -/// \} - -/// conversion to uint64 -/// \{ -inline std::uint64_t toConcreteValue(std::uint64_t v, const std::uint64_t&) { - return v; -} -inline std::uint64_t toConcreteValue(const json::string& str, - const std::uint64_t&) { - return std::stoull(std::string{str.c_str()}); -} -inline std::uint64_t toConcreteValue(double v, const std::uint64_t&) { - return v; -} -inline std::uint64_t toConcreteValue(bool v, const std::uint64_t&) { return v; } -inline std::uint64_t toConcreteValue(std::nullptr_t, const std::uint64_t&) { - return 0; -} - -inline std::uint64_t toConcreteValue(std::int64_t v, const std::uint64_t&) { - if (v < 0) { - CXX_UNLIKELY; - throw OutsideOfUint64Range{}; - } - - return v; -} -/// \} - -/// conversion to double -/// \{ -inline double toConcreteValue(const json::string& str, const double&) { - return std::stod(std::string{str.c_str()}); -} -inline double toConcreteValue(std::int64_t v, const double&) { return v; } -inline double toConcreteValue(std::uint64_t v, const double&) { return v; } -inline double toConcreteValue(double v, const double&) { return v; } -inline double toConcreteValue(bool v, const double&) { return v; } -inline double toConcreteValue(std::nullptr_t, const double&) { return 0; } -/// \} - -/// conversion to string -/// \{ -template -inline json::string toConcreteValue(Val v, const json::string&) { - return json::string{std::to_string(v)}; -} - -inline json::string toConcreteValue(bool v, const json::string&) { - return json::string{v ? "true" : "false"}; -} -inline json::string toConcreteValue(const json::string& s, - const json::string&) { - return s; -} -inline json::string toConcreteValue(std::nullptr_t, const json::string&) { - return json::string{"null"}; -} -/// \} - -/// conversion to boolean -/// implements truthy, falsy as described by https://jsonlogic.com/truthy.html -/// \{ -inline bool toConcreteValue(bool v, const bool&) { return v; } -inline bool toConcreteValue(std::int64_t v, const bool&) { return v; } -inline bool toConcreteValue(std::uint64_t v, const bool&) { return v; } -inline bool toConcreteValue(double v, const bool&) { return v; } -inline bool toConcreteValue(const json::string& v, const bool&) { - return v.size() != 0; -} -inline bool toConcreteValue(std::nullptr_t, const bool&) { return false; } - -// \todo not sure if conversions from arrays to values should be supported like -// this -inline bool toConcreteValue(const Array& v, const bool&) { - return v.num_evaluated_operands(); -} -/// \} - -struct ComparisonOperatorBase { - enum { - definedForString = true, - definedForDouble = true, - definedForInteger = true, - definedForBool = true, - definedForNull = true, - definedForArray = true - }; - - using result_type = bool; -}; - -/// \brief a strict equality operator operates on operands of the same -/// type. The operation on two different types returns false. -/// NO type coercion is performed. -struct StrictEqualityOperator : ComparisonOperatorBase { - std::tuple coerce(Array*, Array*) { - return {true, false}; // arrays are never equal - } - - template - std::tuple coerce(LhsT* lv, RhsT* rv) { - return {std::move(*lv), std::move(*rv)}; - } - - std::tuple coerce(std::nullptr_t, - std::nullptr_t) { - return {nullptr, nullptr}; // two null pointers are equal - } - - template - std::tuple coerce(LhsT* lv, std::nullptr_t) { - return {std::move(*lv), nullptr}; - } - - template - std::tuple coerce(std::nullptr_t, RhsT* rv) { - return {nullptr, std::move(*rv)}; - } -}; - -struct NumericBinaryOperatorBase { - std::tuple coerce(double* lv, double* rv) { - return {*lv, *rv}; - } - - std::tuple coerce(double* lv, std::int64_t* rv) { - return {*lv, toConcreteValue(*rv, *lv)}; - } - - std::tuple coerce(double* lv, std::uint64_t* rv) { - return {*lv, toConcreteValue(*rv, *lv)}; - } - - std::tuple coerce(std::int64_t* lv, double* rv) { - return {toConcreteValue(*lv, *rv), *rv}; - } - - std::tuple coerce(std::int64_t* lv, - std::int64_t* rv) { - return {*lv, *rv}; - } - - std::tuple coerce(std::int64_t* lv, - std::uint64_t* rv) { - return {*lv, toConcreteValue(*rv, *lv)}; - } - - std::tuple coerce(std::uint64_t* lv, double* rv) { - return {toConcreteValue(*lv, *rv), *rv}; - } - - std::tuple coerce(std::uint64_t* lv, - std::int64_t* rv) { - return {toConcreteValue(*lv, *rv), *rv}; - } - - std::tuple coerce(std::uint64_t* lv, - std::uint64_t* rv) { - return {*lv, *rv}; - } -}; - -/// \brief an equality operator compares two values. If the -/// values have a different type, type coercion is performed -/// on one of the operands. -struct RelationalOperatorBase : NumericBinaryOperatorBase { - using NumericBinaryOperatorBase::coerce; - - std::tuple coerce(double* lv, json::string* rv) { - return {*lv, toConcreteValue(*rv, *lv)}; - } - - std::tuple coerce(double* lv, bool* rv) { - return {*lv, toConcreteValue(*rv, *lv)}; - } - - std::tuple coerce(std::int64_t* lv, - json::string* rv) { - return {*lv, toConcreteValue(*rv, *lv)}; - } - - std::tuple coerce(std::int64_t* lv, bool* rv) { - return {*lv, toConcreteValue(*rv, *lv)}; - } - - std::tuple coerce(std::uint64_t* lv, - json::string* rv) { - return {*lv, toConcreteValue(*rv, *lv)}; - } - - std::tuple coerce(std::uint64_t* lv, bool* rv) { - return {*lv, toConcreteValue(*rv, *lv)}; - } - - std::tuple coerce(json::string* lv, double* rv) { - return {toConcreteValue(*lv, *rv), *rv}; - } - - std::tuple coerce(bool* lv, double* rv) { - return {toConcreteValue(*lv, *rv), *rv}; - } - - std::tuple coerce(json::string* lv, - std::int64_t* rv) { - return {toConcreteValue(*lv, *rv), *rv}; - } - - std::tuple coerce(bool* lv, std::int64_t* rv) { - return {toConcreteValue(*lv, *rv), *rv}; - } - - std::tuple coerce(json::string* lv, - std::uint64_t* rv) { - return {toConcreteValue(*lv, *rv), *rv}; - } - - std::tuple coerce(bool* lv, std::uint64_t* rv) { - return {toConcreteValue(*lv, *rv), *rv}; - } - - std::tuple coerce(json::string*, bool* rv) { - // strings and boolean are never equal - return {!*rv, *rv}; - } - - std::tuple coerce(bool* lv, json::string*) { - // strings and boolean are never equal - return {*lv, !*lv}; - } - - std::tuple coerce(json::string* lv, - json::string* rv) { - return {std::move(*lv), std::move(*rv)}; - } - - std::tuple coerce(bool* lv, bool* rv) { return {*lv, *rv}; } -}; - -struct EqualityOperator : RelationalOperatorBase, ComparisonOperatorBase { - using RelationalOperatorBase::coerce; - - // due to special conversion rules, the coercion function may just produce - // the result instead of just unpacking and coercing values. - - std::tuple coerce(Array*, Array*) { - return {true, false}; // arrays are never equal - } - - template - std::tuple coerce(T* lv, Array* rv) { - // an array may be compared to a value - // (1) *lv == arr[0], iff the array has exactly one element - if (rv->num_evaluated_operands() == 1) throw UnpackedArrayRequired{}; - - // (2) or if [] and *lv converts to false - if (rv->num_evaluated_operands() > 1) return {false, true}; - - const bool convToFalse = toConcreteValue(*lv, false) == false; - - return {convToFalse, true /* zero elements */}; - } - - template - std::tuple coerce(Array* lv, T* rv) { - // see comments in coerce(T*,Array*) - if (lv->num_evaluated_operands() == 1) throw UnpackedArrayRequired{}; - - if (lv->num_evaluated_operands() > 1) return {false, true}; - - const bool convToFalse = toConcreteValue(*rv, false) == false; - - return {true /* zero elements */, convToFalse}; - } - - std::tuple coerce(std::nullptr_t, - std::nullptr_t) { - return {nullptr, nullptr}; // two null pointers are equal - } - - template - std::tuple coerce(T*, std::nullptr_t) { - return {false, true}; // null pointer is only equal to itself - } - - template - std::tuple coerce(std::nullptr_t, T*) { - return {true, false}; // null pointer is only equal to itself - } - - std::tuple coerce(Array*, std::nullptr_t) { - return {true, false}; // null pointer is only equal to itself - } - - std::tuple coerce(std::nullptr_t, Array*) { - return {true, false}; // null pointer is only equal to itself - } -}; - -struct RelationalOperator : RelationalOperatorBase, ComparisonOperatorBase { - using RelationalOperatorBase::coerce; - - std::tuple coerce(Array* lv, Array* rv) { return {lv, rv}; } - - template - std::tuple coerce(T* lv, Array* rv) { - // an array may be equal to another value if - // (1) *lv == arr[0], iff the array has exactly one element - if (rv->num_evaluated_operands() == 1) throw UnpackedArrayRequired{}; - - // (2) or if [] and *lv converts to false - if (rv->num_evaluated_operands() > 1) return {false, true}; - - const bool convToTrue = toConcreteValue(*lv, true) == true; - - return {convToTrue, false /* zero elements */}; - } - - template - std::tuple coerce(Array* lv, T* rv) { - // see comments in coerce(T*,Array*) - if (lv->num_evaluated_operands() == 1) throw UnpackedArrayRequired{}; - - if (lv->num_evaluated_operands() > 1) return {false, true}; - - const bool convToTrue = toConcreteValue(*rv, true) == true; - - return {false /* zero elements */, convToTrue}; - } - - std::tuple coerce(std::nullptr_t, - std::nullptr_t) { - return {nullptr, nullptr}; // two null pointers are equal - } - - std::tuple coerce(bool* lv, std::nullptr_t) { - return {*lv, false}; // null pointer -> false - } - - std::tuple coerce(std::int64_t* lv, - std::nullptr_t) { - return {*lv, 0}; // null pointer -> 0 - } - - std::tuple coerce(std::uint64_t* lv, - std::nullptr_t) { - return {*lv, 0}; // null pointer -> 0 - } - - std::tuple coerce(double* lv, std::nullptr_t) { - return {*lv, 0}; // null pointer -> 0.0 - } - - std::tuple coerce(json::string* lv, - std::nullptr_t) { - return {std::move(*lv), nullptr}; // requires special handling - } - - std::tuple coerce(std::nullptr_t, bool* rv) { - return {false, *rv}; // null pointer -> false - } - - std::tuple coerce(std::nullptr_t, - std::int64_t* rv) { - return {0, *rv}; // null pointer -> 0 - } - - std::tuple coerce(std::nullptr_t, - std::uint64_t* rv) { - return {0, *rv}; // null pointer -> 0 - } - - std::tuple coerce(std::nullptr_t, double* rv) { - return {0, *rv}; // null pointer -> 0 - } - - std::tuple coerce(std::nullptr_t, - json::string* rv) { - return {nullptr, std::move(*rv)}; // requires special handling - } -}; -// @} - -// Arith -struct ArithmeticOperator : NumericBinaryOperatorBase { - enum { - definedForString = false, - definedForDouble = true, - definedForInteger = true, - definedForBool = false, - definedForNull = true, - definedForArray = false - }; - - using result_type = any_expr; - - using NumericBinaryOperatorBase::coerce; - - std::tuple coerce(double*, std::nullptr_t) { - return {nullptr, nullptr}; - } - - std::tuple coerce(std::int64_t*, - std::nullptr_t) { - return {nullptr, nullptr}; - } - - std::tuple coerce(std::uint64_t*, - std::nullptr_t) { - return {nullptr, nullptr}; - } - - std::tuple coerce(std::nullptr_t, double*) { - return {nullptr, nullptr}; - } - - std::tuple coerce(std::nullptr_t, - std::int64_t*) { - return {nullptr, nullptr}; - } - - std::tuple coerce(std::nullptr_t, - std::uint64_t*) { - return {nullptr, nullptr}; - } - - std::tuple coerce(std::nullptr_t, - std::nullptr_t) { - return {nullptr, nullptr}; - } -}; - -struct IntegerArithmeticOperator : ArithmeticOperator { - enum { - definedForString = false, - definedForDouble = false, - definedForInteger = true, - definedForBool = false, - definedForNull = false, - definedForArray = false - }; - - using ArithmeticOperator::coerce; -}; - -struct StringOperator { - enum { - definedForString = true, - definedForDouble = false, - definedForInteger = false, - definedForBool = false, - definedForNull = false, - definedForArray = false - }; - - using result_type = any_expr; - - std::tuple coerce(json::string* lv, - json::string* rv) { - return {std::move(*lv), std::move(*rv)}; - } -}; - -struct ArrayOperator { - enum { - definedForString = false, - definedForDouble = false, - definedForInteger = false, - definedForBool = false, - definedForNull = false, - definedForArray = true - }; - - using result_type = any_expr; - - std::tuple coerce(Array* lv, Array* rv) { return {lv, rv}; } -}; - -any_expr convert(any_expr val, ...) { return val; } - -any_expr convert(any_expr val, const ArithmeticOperator&) { - struct ArithmeticConverter : FwdVisitor { - explicit ArithmeticConverter(any_expr val) : res(std::move(val)) {} - - void visit(Expr&) final { typeError(); } - - // defined for the following types - void visit(IntVal&) final {} - void visit(UintVal&) final {} - void visit(DoubleVal&) final {} - void visit(NullVal&) final {} - - // need to convert values - void visit(StringVal& el) final { - double dd = toConcreteValue(el.value(), double{}); - std::int64_t ii = toConcreteValue(el.value(), std::int64_t{}); - // uint? - - res = (dd != ii) ? toany_expr(dd) : toany_expr(ii); - } - - void visit(BoolVal&) final { - // \todo correct? - res = toany_expr(nullptr); - } - - any_expr result() && { return std::move(res); } - - private: - any_expr res; - }; - - Expr* node = val.get(); - ArithmeticConverter conv{std::move(val)}; - - node->accept(conv); - return std::move(conv).result(); -} - -any_expr convert(any_expr val, const IntegerArithmeticOperator&) { - struct IntegerArithmeticConverter : FwdVisitor { - explicit IntegerArithmeticConverter(any_expr val) : res(std::move(val)) {} - - void visit(Expr&) final { typeError(); } - - // defined for the following types - void visit(IntVal&) final {} - void visit(UintVal&) final {} - - // need to convert values - void visit(StringVal& el) final { - res = toany_expr(toConcreteValue(el.value(), std::int64_t{})); - } - - void visit(BoolVal& el) final { - res = toany_expr(toConcreteValue(el.value(), std::int64_t{})); - } - - void visit(DoubleVal& el) final { - res = toany_expr(toConcreteValue(el.value(), std::int64_t{})); - } - - void visit(NullVal&) final { res = toany_expr(std::int64_t{0}); } - - any_expr result() && { return std::move(res); } - - private: - any_expr res; - }; - - Expr* node = val.get(); - IntegerArithmeticConverter conv{std::move(val)}; - - node->accept(conv); - return std::move(conv).result(); -} - -any_expr convert(any_expr val, const StringOperator&) { - struct StringConverter : FwdVisitor { - explicit StringConverter(any_expr val) : res(std::move(val)) {} - - void visit(Expr&) final { typeError(); } - - // defined for the following types - void visit(StringVal&) final {} - - // need to convert values - void visit(BoolVal& el) final { - res = toany_expr(toConcreteValue(el.value(), json::string{})); - } - - void visit(IntVal& el) final { - res = toany_expr(toConcreteValue(el.value(), json::string{})); - } - - void visit(UintVal& el) final { - res = toany_expr(toConcreteValue(el.value(), json::string{})); - } - - void visit(DoubleVal& el) final { - res = toany_expr(toConcreteValue(el.value(), json::string{})); - } - - void visit(NullVal& el) final { - res = toany_expr(toConcreteValue(el.value(), json::string{})); - } - - any_expr result() && { return std::move(res); } - - private: - any_expr res; - }; - - Expr* node = val.get(); - StringConverter conv{std::move(val)}; - - node->accept(conv); - return std::move(conv).result(); -} - -any_expr convert(any_expr val, const ArrayOperator&) { - struct ArrayConverter : FwdVisitor { - explicit ArrayConverter(any_expr val) : val(std::move(val)) {} - - void visit(Expr&) final { typeError(); } - - // moves res into - void toArray() { - Array& arr = deref(new Array); - Operator::container_type operands; - - operands.emplace_back(&arr); - - // swap the operand and result - std::swap(operands.back(), val); - - // then set the operands - arr.set_operands(std::move(operands)); - } - - // defined for the following types - void visit(Array&) final {} - - // need to move value to new array - void visit(StringVal&) final { toArray(); } - void visit(BoolVal&) final { toArray(); } - void visit(IntVal&) final { toArray(); } - void visit(UintVal&) final { toArray(); } - void visit(DoubleVal&) final { toArray(); } - void visit(NullVal&) final { toArray(); } - - any_expr result() && { return std::move(val); } - - private: - any_expr val; - }; - - Expr* node = val.get(); - ArrayConverter conv{std::move(val)}; - - node->accept(conv); - return std::move(conv).result(); -} - -template -struct UnpackVisitor : FwdVisitor { - UnpackVisitor() : res() {} - - void assign(ValueT& lhs, const ValueT& val) { lhs = val; } - - template - void assign(ValueT& lhs, const U& val) { - lhs = toConcreteValue(val, lhs); - } - - void visit(Expr&) final { typeError(); } - - // defined for the following types - void visit(StringVal& el) final { assign(res, el.value()); } - - // need to convert values - void visit(BoolVal& el) final { assign(res, el.value()); } - - void visit(IntVal& el) final { assign(res, el.value()); } - - void visit(UintVal& el) final { assign(res, el.value()); } - - void visit(DoubleVal& el) final { assign(res, el.value()); } - - void visit(NullVal& el) final { assign(res, el.value()); } - - void visit(Array& el) final { - if constexpr (std::is_same::value) return assign(res, el); - - typeError(); - } - - ValueT result() && { return std::move(res); } - - private: - ValueT res; -}; - -template -T unpack_value(Expr& expr) { - UnpackVisitor unpack; - - expr.accept(unpack); - return std::move(unpack).result(); -} - -template -T unpack_value(any_expr& el) { - return unpack_value(*el); -} - -template -T unpack_value(any_expr&& el) { - return unpack_value(*el); -} - -// -// Json Logic - truthy/falsy -bool truthy(Expr& el) { return unpack_value(el); } -bool truthy(any_expr& el) { return unpack_value(el); } -bool truthy(any_expr&& el) { return unpack_value(std::move(el)); } -bool falsy(Expr& el) { return !truthy(el); } -bool falsy(any_expr& el) { return !truthy(el); } -bool falsy(any_expr&& el) { return !truthy(std::move(el)); } - -// -// cloning - -any_expr cloneExpr(any_expr& expr); - -struct ExprCloner : GVisitor { - using base = GVisitor; - - ExprCloner() : base(*this), res(nullptr) {} - - /// init functions that set up children - /// \{ - Expr& init(Operator&, Operator&); - Expr& init(ObjectVal&, ObjectVal&); - /// \} - - /// function family for type specific cloning - /// \param n the original node - /// \param unnamed a tag parameter to summarily handle groups of types - /// \return the cloned node - /// \{ - CXX_NORETURN - Expr& clone(Expr&, const Expr&) { unsupported(); } - - Expr& clone(Error&, const Error&) { return deref(new Error); } - - template - Expr& clone(TValue& n, const Value&) { - return deref(new TValue(n.value())); - } - - template - Expr& clone(TOperator& n, const Operator&) { - return init(n, deref(new TOperator)); - } - - Expr& clone(ObjectVal& n, const ObjectVal&) { - return init(n, deref(new ObjectVal)); - } - /// \} - - template - void gvisit(TExpr& n) { - res = &clone(n, n); - } - - Expr* result() { return res; } - - private: - Expr* res; -}; - -Expr& ExprCloner::init(Operator& src, Operator& tgt) { - Operator::container_type children; - - std::transform(src.operands().begin(), src.operands().end(), - std::back_inserter(children), - [](any_expr& e) -> any_expr { return cloneExpr(e); }); - - tgt.set_operands(std::move(children)); - return tgt; -} - -Expr& ExprCloner::init(ObjectVal& src, ObjectVal& tgt) { - std::transform(src.begin(), src.end(), - std::inserter(tgt.elements(), tgt.end()), - [](ObjectVal::value_type& entry) -> ObjectVal::value_type { - return {entry.first, cloneExpr(entry.second)}; - }); - - return tgt; -} - -any_expr cloneExpr(any_expr& expr) { - ExprCloner cloner; - - expr->accept(cloner); - return any_expr{cloner.result()}; -} - -// -// internal down cast - -template -struct DownCastVisitor : GVisitor > { - using base = GVisitor >; - - DownCastVisitor() : base(*this), res(nullptr) {} - - void gvisit(Expr&) { /* not found */ } - - void gvisit(ExprT& n) { res = &n; } - - ExprT* result() { return res; } - - private: - ExprT* res; -}; - -template -ExprT& down_cast(Expr& expr) { - DownCastVisitor vis; - - expr.accept(vis); - return deref(vis.result()); -} - -template -auto with_type(Expr* e, Fn fn, AltFn altfn) -> decltype(altfn()) { - if (e == nullptr) { - CXX_UNLIKELY; - return altfn(); - } - - DownCastVisitor vis; - e->accept(vis); - - return vis.result() ? fn(*vis.result()) : altfn(); -} - -// -// binary operator - double dispatch pattern - -template -struct BinaryOperatorVisitor_ : FwdVisitor { - using result_type = typename BinaryOperator::result_type; - - BinaryOperatorVisitor_(LhsValue lval, BinaryOperator oper) - : lv(lval), op(oper), res() {} - - template - void calc(RhsValue rv) { - auto [ll, rr] = op.coerce(lv, rv); - - res = op(std::move(ll), std::move(rr)); - } - - void visit(Expr&) final { typeError(); } - - void visit(StringVal& n) final { - if constexpr (BinaryOperator::definedForString) return calc(&n.value()); - - typeError(); - } - - void visit(NullVal&) final { - if constexpr (BinaryOperator::definedForNull) return calc(nullptr); - - typeError(); - } - - void visit(BoolVal& n) final { - if constexpr (BinaryOperator::definedForBool) return calc(&n.value()); - - typeError(); - } - - void visit(IntVal& n) final { - if constexpr (BinaryOperator::definedForInteger) { - try { - return calc(&n.value()); - } catch (const OutsideOfInt64Range& ex) { - if (n.value() < 0) { - CXX_UNLIKELY; - throw std::range_error{ - "unable to consolidate uint>max(int) with int<0"}; - } - } - - std::uint64_t alt = n.value(); - return calc(&alt); - } - - typeError(); - } - - void visit(UintVal& n) final { - if constexpr (BinaryOperator::definedForInteger) { - try { - return calc(&n.value()); - } catch (const OutsideOfUint64Range& ex) { - if (n.value() > std::uint64_t(std::numeric_limits::max)) { - CXX_UNLIKELY; - throw std::range_error{ - "unable to consolidate int<0 with uint>max(int)"}; - } - } - - std::int64_t alt = n.value(); - return calc(&alt); - } - - typeError(); - } - - void visit(DoubleVal& n) final { - if constexpr (BinaryOperator::definedForDouble) return calc(&n.value()); - - typeError(); - } - - void visit(Array& n) final { - if constexpr (BinaryOperator::definedForArray) { - try { - calc(&n); - } catch (const UnpackedArrayRequired&) { - assert(n.num_evaluated_operands() == 1); - n.operand(0).accept(*this); - } - - return; - } - - typeError(); - } - - result_type result() && { return std::move(res); } - - private: - LhsValue lv; - BinaryOperator op; - result_type res; -}; - -template -struct BinaryOperatorVisitor : FwdVisitor { - using result_type = typename BinaryOperator::result_type; - - BinaryOperatorVisitor(BinaryOperator oper, any_expr& rhsarg) - : op(oper), rhs(rhsarg), res() {} - - template - void calc(LhsValue lv) { - using RhsVisitor = BinaryOperatorVisitor_; - - RhsVisitor vis{lv, op}; - - rhs->accept(vis); - res = std::move(vis).result(); - } - - void visit(StringVal& n) final { - if constexpr (BinaryOperator::definedForString) return calc(&n.value()); - - typeError(); - } - - void visit(NullVal&) final { - if constexpr (BinaryOperator::definedForNull) return calc(nullptr); - - typeError(); - } - - void visit(BoolVal& n) final { - if constexpr (BinaryOperator::definedForBool) return calc(&n.value()); - - typeError(); - } - - void visit(IntVal& n) final { - if constexpr (BinaryOperator::definedForInteger) { - try { - return calc(&n.value()); - } catch (const OutsideOfInt64Range& ex) { - if (n.value() < 0) { - CXX_UNLIKELY; - throw std::range_error{ - "unable to consolidate int<0 with uint>max(int)"}; - } - } - - std::uint64_t alt = n.value(); - return calc(&alt); - } - - typeError(); - } - - void visit(UintVal& n) final { - if constexpr (BinaryOperator::definedForInteger) { - try { - return calc(&n.value()); - } catch (const OutsideOfUint64Range& ex) { - if (n.value() > std::uint64_t(std::numeric_limits::max)) { - CXX_UNLIKELY; - throw std::range_error{ - "unable to consolidate uint>max(int) with int<0"}; - } - } - - std::int64_t alt = n.value(); - return calc(&alt); - } - - typeError(); - } - - void visit(DoubleVal& n) final { - if constexpr (BinaryOperator::definedForDouble) return calc(&n.value()); - - typeError(); - } - - void visit(Array& n) final { - if constexpr (BinaryOperator::definedForArray) { - try { - calc(&n); - } catch (const UnpackedArrayRequired&) { - assert(n.num_evaluated_operands() == 1); - n.operand(0).accept(*this); - } - - return; - } - - typeError(); - } - - result_type result() && { return std::move(res); } - - private: - BinaryOperator op; - any_expr& rhs; - result_type res; -}; - -// -// compute and sequence functions - -template -typename BinaryOperator::result_type compute(any_expr& lhs, any_expr& rhs, - BinaryOperator op) { - using LhsVisitor = BinaryOperatorVisitor; - - assert(lhs.get() && rhs.get()); - - LhsVisitor vis{op, rhs}; - - lhs->accept(vis); - return std::move(vis).result(); -} - -template -bool compareSeq(Array& lv, Array& rv, BinaryPredicate pred) { - const std::size_t lsz = lv.num_evaluated_operands(); - const std::size_t rsz = rv.num_evaluated_operands(); - - if (lsz == 0) return pred(false, rsz != 0); - - if (rsz == 0) return pred(true, false); - - std::size_t const len = std::min(lsz, rsz); - std::size_t i = 0; - bool res = false; - bool found = false; - - while ((i < len) && !found) { - res = compute(lv.at(i), rv.at(i), pred); - - // res is conclusive if the reverse test yields a different result - found = res != compute(rv.at(i), lv.at(i), pred); - - ++i; - } - - return found ? res : pred(lsz, rsz); -} - -template -bool compareSeq(Array* lv, Array* rv, BinaryPredicate pred) { - return compareSeq(deref(lv), deref(rv), std::move(pred)); -} - -// convenience template that only returns a type when the types mismatch -template -struct MismatchedTypes { - using type = ResultType; -}; - -template -struct MismatchedTypes { - // using type // triggers SFINAE exclusion -}; - -// -// the calc operator implementations - -template -struct Calc {}; - -template <> -struct Calc : EqualityOperator { - using EqualityOperator::result_type; - - template - auto operator()(const U&, const V&) const -> - typename MismatchedTypes::type // type mismatch - { - return false; - } - - template - auto operator()(const T& lhs, const T& rhs) const -> result_type { - return lhs == rhs; - } -}; - -template <> -struct Calc : EqualityOperator { - using EqualityOperator::result_type; - - template - auto operator()(const U&, const V&) const -> - typename MismatchedTypes::type // type mismatch - { - return true; - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return lhs != rhs; - } -}; - -template <> -struct Calc : StrictEqualityOperator { - using StrictEqualityOperator::result_type; - - template - auto operator()(const U&, const V&) const -> - typename MismatchedTypes::type // type mismatch - { - return false; - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return lhs == rhs; - } -}; - -template <> -struct Calc : StrictEqualityOperator { - using StrictEqualityOperator::result_type; - - template - auto operator()(const U&, const V&) const -> - typename MismatchedTypes::type // type mismatch - { - return true; - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return lhs != rhs; - } -}; - -template <> -struct Calc : RelationalOperator { - using RelationalOperator::result_type; - - result_type operator()(const std::nullptr_t, std::nullptr_t) const { - return false; - } - - result_type operator()(Array* lv, Array* rv) const { - return compareSeq(lv, rv, *this); - } - - result_type operator()(const json::string&, std::nullptr_t) const { - return false; - } - - result_type operator()(std::nullptr_t, const json::string&) const { - return false; - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return lhs < rhs; - } -}; - -template <> -struct Calc : RelationalOperator { - using RelationalOperator::result_type; - - result_type operator()(const std::nullptr_t, std::nullptr_t) const { - return false; - } - - result_type operator()(Array* lv, Array* rv) const { - return compareSeq(lv, rv, *this); - } - - result_type operator()(const json::string&, std::nullptr_t) const { - return false; - } - - result_type operator()(std::nullptr_t, const json::string&) const { - return false; - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return rhs < lhs; - } -}; - -template <> -struct Calc : RelationalOperator { - using RelationalOperator::result_type; - - result_type operator()(const std::nullptr_t, std::nullptr_t) const { - return true; - } - - result_type operator()(Array* lv, Array* rv) const { - return compareSeq(lv, rv, *this); - } - - result_type operator()(const json::string& lhs, std::nullptr_t) const { - return lhs.empty(); - } - - result_type operator()(std::nullptr_t, const json::string& rhs) const { - return rhs.empty(); - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return lhs <= rhs; - } -}; - -template <> -struct Calc : RelationalOperator { - using RelationalOperator::result_type; - - result_type operator()(const std::nullptr_t, std::nullptr_t) const { - return true; - } - - result_type operator()(Array* lv, Array* rv) const { - return compareSeq(lv, rv, *this); - } - - result_type operator()(const json::string& lhs, std::nullptr_t) const { - return lhs.empty(); - } - - result_type operator()(std::nullptr_t, const json::string& rhs) const { - return rhs.empty(); - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return rhs <= lhs; - } -}; - -template <> -struct Calc : ArithmeticOperator { - using ArithmeticOperator::result_type; - - result_type operator()(std::nullptr_t, std::nullptr_t) const { - return toany_expr(nullptr); - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return toany_expr(lhs + rhs); - } -}; - -template <> -struct Calc : ArithmeticOperator { - using ArithmeticOperator::result_type; - - result_type operator()(std::nullptr_t, std::nullptr_t) const { - return toany_expr(nullptr); - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return toany_expr(lhs - rhs); - } -}; - -template <> -struct Calc : ArithmeticOperator { - using ArithmeticOperator::result_type; - - result_type operator()(std::nullptr_t, std::nullptr_t) const { - return toany_expr(nullptr); - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return toany_expr(lhs * rhs); - } -}; - -template <> -struct Calc
: ArithmeticOperator { - using ArithmeticOperator::result_type; - - result_type operator()(std::nullptr_t, std::nullptr_t) const { - return toany_expr(nullptr); - } - - result_type operator()(double lhs, double rhs) const { - double res = lhs / rhs; - - // if (isInteger(res)) return toInt(res); - return toany_expr(res); - } - - template - result_type operator()(Int_t lhs, Int_t rhs) const { - if (lhs % rhs) return (*this)(double(lhs), double(rhs)); - - return toany_expr(lhs / rhs); - } -}; - -template <> -struct Calc : IntegerArithmeticOperator { - using IntegerArithmeticOperator::result_type; - - std::nullptr_t operator()(std::nullptr_t, std::nullptr_t) const { - return nullptr; - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - if (rhs == 0) return toany_expr(nullptr); - - return toany_expr(lhs % rhs); - } -}; - -template <> -struct Calc : ArithmeticOperator { - using ArithmeticOperator::result_type; - - result_type operator()(const std::nullptr_t, std::nullptr_t) const { - return nullptr; - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return toany_expr(std::min(lhs, rhs)); - } -}; - -template <> -struct Calc : ArithmeticOperator { - using ArithmeticOperator::result_type; - - result_type operator()(const std::nullptr_t, std::nullptr_t) const { - return nullptr; - } - - template - result_type operator()(const T& lhs, const T& rhs) const { - return toany_expr(std::max(lhs, rhs)); - } -}; - -template <> -struct Calc { - using result_type = bool; - - result_type operator()(Expr& val) const { return falsy(val); } -}; - -template <> -struct Calc { - using result_type = bool; - - result_type operator()(Expr& val) const { return truthy(val); } -}; - -template <> -struct Calc : StringOperator { - using StringOperator::result_type; - - result_type operator()(const json::string& lhs, - const json::string& rhs) const { - json::string tmp; - - tmp.reserve(lhs.size() + rhs.size()); - - tmp.append(lhs.begin(), lhs.end()); - tmp.append(rhs.begin(), rhs.end()); - - return toany_expr(std::move(tmp)); - } -}; - -template <> -struct Calc : StringOperator // \todo the conversion rules differ -{ - using StringOperator::result_type; - - result_type operator()(const json::string& lhs, - const json::string& rhs) const { - const bool res = (rhs.find(lhs) != json::string::npos); - - return toany_expr(res); - } -}; - -template <> -struct Calc : StringOperator // \todo the conversion rules differ -{ - using StringOperator::result_type; - - result_type operator()(const json::string& lhs, - const json::string& rhs) const { - std::regex rgx(lhs.c_str(), lhs.size()); - - return toany_expr(std::regex_search(rhs.begin(), rhs.end(), rgx)); - } -}; - -template <> -struct Calc : ArrayOperator { - using ArrayOperator::result_type; - - result_type operator()(Array* lhs, Array* rhs) const { - // note, to use the lhs entirely, it would need to be released - // from its any_expr - Array& res = deref(new Array); - - { - Operator::container_type& opers = res.operands(); - - opers.swap(lhs->operands()); - - Operator::container_type& ropers = rhs->operands(); - - opers.insert(opers.end(), std::make_move_iterator(ropers.begin()), - std::make_move_iterator(ropers.end())); - } - - return any_expr(&res); - } -}; - -struct Calculator : FwdVisitor { - using VarAccess = std::function; - - Calculator(VarAccess varAccess, std::ostream& out) - : vars(std::move(varAccess)), logger(out), calcres(nullptr) {} - - void visit(Eq&) final; - void visit(StrictEq&) final; - void visit(Neq&) final; - void visit(StrictNeq&) final; - void visit(Less&) final; - void visit(Greater&) final; - void visit(Leq&) final; - void visit(Geq&) final; - void visit(And&) final; - void visit(Or&) final; - void visit(Not&) final; - void visit(NotNot&) final; - void visit(Add&) final; - void visit(Sub&) final; - void visit(Mul&) final; - void visit(Div&) final; - void visit(Mod&) final; - void visit(Min&) final; - void visit(Max&) final; - void visit(Array&) final; - void visit(Map&) final; - void visit(Reduce&) final; - void visit(Filter&) final; - void visit(All&) final; - void visit(None&) final; - void visit(Some&) final; - void visit(Merge&) final; - void visit(Cat&) final; - void visit(Substr&) final; - void visit(In&) final; - void visit(Var&) final; - void visit(Missing&) final; - void visit(MissingSome&) final; - void visit(Log&) final; - - void visit(If&) final; - - void visit(NullVal& n) final; - void visit(BoolVal& n) final; - void visit(IntVal& n) final; - void visit(UintVal& n) final; - void visit(DoubleVal& n) final; - void visit(StringVal& n) final; - - void visit(Error& n) final; - void visit(RegexMatch& n) final; - - any_expr eval(Expr& n); - - private: - VarAccess vars; - std::ostream& logger; - any_expr calcres; - - Calculator(const Calculator&) = delete; - Calculator(Calculator&&) = delete; - Calculator& operator=(const Calculator&) = delete; - Calculator& operator=(Calculator&&) = delete; - - // - // opers - - /// implements relop : [1, 2, 3, whatever] as 1 relop 2 relop 3 - template - void evalPairShortCircuit(Operator& n, BinaryPredicate pred); - - /// returns the first expression in [ e1, e2, e3 ] that evaluates to val, or - /// the last expression otherwise - void evalShortCircuit(Operator& n, bool val); - - /// reduction operation on all elements - template - void reduce(Operator& n, BinaryOperator op); - - /// computes unary operation on n[0] - template - void unary(Operator& n, UnaryOperator calc); - - /// binary operation on all elements (invents an element if none is present) - template - void binary(Operator& n, BinaryOperator binop); - - /// evaluates and unpacks n[argpos] to a fundamental value - template - ValueT unpackOptionalArg(Operator& n, int argpos, const ValueT& defaultVal); - - template - void _value(const ValueNode& val) { - calcres = toany_expr(val.value()); - } - - /// auxiliary missing method - std::size_t missing_aux(Array& elems); -}; - -struct SequenceFunction { - SequenceFunction(Expr& e, std::ostream& logstream) - : expr(e), logger(logstream) {} - - any_expr operator()(any_expr&& elem) const { - any_expr* elptr = &elem; // workaround, b/c unique_ptr cannot be captured - - Calculator sub{[elptr](const json::value& keyval, int) -> any_expr { - if (const json::string* pkey = keyval.if_string()) { - const json::string& key = *pkey; - - if (key.size() == 0) return cloneExpr(*elptr); - - try { - ObjectVal& o = down_cast(**elptr); - - if (auto pos = o.find(key); pos != o.end()) - return cloneExpr(pos->second); - } catch (const cast_error&) { - } - } - - return toany_expr(nullptr); - }, - logger}; - - return sub.eval(expr); - } - - private: - Expr& expr; - std::ostream& logger; -}; - -struct SequencePredicate : SequenceFunction { - using SequenceFunction::SequenceFunction; - - bool operator()(any_expr&& elem) const { - return truthy(SequenceFunction::operator()(std::move(elem))); - } -}; - -struct SequencePredicateNondestructive : SequenceFunction { - using SequenceFunction::SequenceFunction; - - bool operator()(any_expr&& elem) const { - return truthy(SequenceFunction::operator()(cloneExpr(elem))); - } -}; - -/* - template - any_expr - accumulate_move(InputIterator pos, InputIterator lim, any_expr accu, - BinaryOperation binop) - { - for ( ; pos != lim; ++pos) - accu = binop(std::move(accu), std::move(*first)); - - return accu; - } -*/ - -struct SequenceReduction { - SequenceReduction(Expr& e, std::ostream& logstream) - : expr(e), logger(logstream) {} - - // for compatibility reasons, the first argument is passed in as templated && - // ref. - // g++ -std=c++17 passes in a & ref, while g++ -std=c++20 passes in a &&. - // the templated && together with reference collapsing make the code portable - // across standard versions. - template - any_expr operator()(any_exprT&& accu, any_expr elem) const { - any_expr* acptr = &accu; // workaround, b/c unique_ptr cannot be captured - any_expr* elptr = &elem; // workaround, b/c unique_ptr cannot be captured - - Calculator sub{[acptr, elptr](const json::value& keyval, int) -> any_expr { - if (const json::string* pkey = keyval.if_string()) { - if (*pkey == "current") return cloneExpr(*elptr); - - if (*pkey == "accumulator") return cloneExpr(*acptr); - } - - return toany_expr(nullptr); - }, - logger}; - - return sub.eval(expr); - } - - private: - Expr& expr; - std::ostream& logger; -}; - -template -ValueT Calculator::unpackOptionalArg(Operator& n, int argpos, - const ValueT& defaultVal) { - if (std::size_t(argpos) >= n.size()) { - CXX_UNLIKELY; - return defaultVal; - } - - return unpack_value(*eval(n.operand(argpos))); -} - -template -void Calculator::unary(Operator& n, UnaryPredicate pred) { - const int num = n.num_evaluated_operands(); - assert(num == 1); - - const bool res = pred(*eval(n.operand(0))); - - calcres = toany_expr(res); -} - -template -void Calculator::binary(Operator& n, BinaryOperator binop) { - const int num = n.num_evaluated_operands(); - assert(num == 1 || num == 2); - - int idx = -1; - any_expr lhs; - - if (num == 2) { - CXX_LIKELY; - lhs = eval(n.operand(++idx)); - } else { - lhs = toany_expr(std::int64_t(0)); - } - - any_expr rhs = eval(n.operand(++idx)); - - calcres = compute(lhs, rhs, binop); -} - -template -void Calculator::reduce(Operator& n, BinaryOperator op) { - const int num = n.num_evaluated_operands(); - assert(num >= 1); - - int idx = -1; - any_expr res = eval(n.operand(++idx)); - - res = convert(std::move(res), op); - - while (idx != (num - 1)) { - any_expr rhs = eval(n.operand(++idx)); - - rhs = convert(std::move(rhs), op); - res = compute(res, rhs, op); - } - - calcres = std::move(res); -} - -template -void Calculator::evalPairShortCircuit(Operator& n, BinaryPredicate pred) { - const int num = n.num_evaluated_operands(); - assert(num >= 2); - - bool res = true; - int idx = -1; - any_expr rhs = eval(n.operand(++idx)); - assert(rhs.get()); - - while (res && (idx != (num - 1))) { - any_expr lhs = std::move(rhs); - assert(lhs.get()); - - rhs = eval(n.operand(++idx)); - assert(rhs.get()); - - res = compute(lhs, rhs, pred); - } - - calcres = toany_expr(res); -} - -void Calculator::evalShortCircuit(Operator& n, bool val) { - const int num = n.num_evaluated_operands(); - - if (num == 0) { - CXX_UNLIKELY; - requiresArgumentError(); - } - - int idx = -1; - any_expr oper = eval(n.operand(++idx)); - //~ std::cerr << idx << ") " << oper << std::endl; - - bool found = (idx == num - 1) || (truthy(*oper) == val); - - // loop until *aa == val or when *aa is the last valid element - while (!found) { - oper = eval(n.operand(++idx)); - //~ std::cerr << idx << ") " << oper << std::endl; - - found = (idx == (num - 1)) || (truthy(*oper) == val); - } - - calcres = std::move(oper); -} - -any_expr Calculator::eval(Expr& n) { - any_expr res; - - n.accept(*this); - res.swap(calcres); - - return res; -} - -void Calculator::visit(Eq& n) { evalPairShortCircuit(n, Calc{}); } - -void Calculator::visit(StrictEq& n) { - evalPairShortCircuit(n, Calc{}); -} - -void Calculator::visit(Neq& n) { evalPairShortCircuit(n, Calc{}); } - -void Calculator::visit(StrictNeq& n) { - evalPairShortCircuit(n, Calc{}); -} - -void Calculator::visit(Less& n) { evalPairShortCircuit(n, Calc{}); } - -void Calculator::visit(Greater& n) { evalPairShortCircuit(n, Calc{}); } - -void Calculator::visit(Leq& n) { evalPairShortCircuit(n, Calc{}); } - -void Calculator::visit(Geq& n) { evalPairShortCircuit(n, Calc{}); } - -void Calculator::visit(And& n) { evalShortCircuit(n, false); } - -void Calculator::visit(Or& n) { evalShortCircuit(n, true); } - -void Calculator::visit(Not& n) { unary(n, Calc{}); } - -void Calculator::visit(NotNot& n) { unary(n, Calc{}); } - -void Calculator::visit(Add& n) { reduce(n, Calc{}); } - -void Calculator::visit(Sub& n) { binary(n, Calc{}); } - -void Calculator::visit(Mul& n) { reduce(n, Calc{}); } - -void Calculator::visit(Div& n) { binary(n, Calc
{}); } - -void Calculator::visit(Mod& n) { binary(n, Calc{}); } - -void Calculator::visit(Min& n) { reduce(n, Calc{}); } - -void Calculator::visit(Max& n) { reduce(n, Calc{}); } - -void Calculator::visit(Cat& n) { reduce(n, Calc{}); } - -void Calculator::visit(In& n) { binary(n, Calc{}); } - -void Calculator::visit(RegexMatch& n) { binary(n, Calc{}); } - -void Calculator::visit(Substr& n) { - assert(n.num_evaluated_operands() >= 1); - - json::string str = unpack_value(*eval(n.operand(0))); - std::int64_t ofs = unpackOptionalArg(n, 1, 0); - std::int64_t cnt = unpackOptionalArg(n, 2, 0); - - if (ofs < 0) { - CXX_UNLIKELY; - ofs = std::max(std::int64_t(str.size()) + ofs, std::int64_t(0)); - } - - if (cnt < 0) { - CXX_UNLIKELY; - cnt = std::max(std::int64_t(str.size()) - ofs + cnt, std::int64_t(0)); - } - - calcres = toany_expr(json::string{str.subview(ofs, cnt)}); -} - -void Calculator::visit(Array& n) { - Operator::container_type elems; - Calculator* self = this; - - // \todo consider making arrays lazy - std::transform( - std::make_move_iterator(n.begin()), std::make_move_iterator(n.end()), - std::back_inserter(elems), - [self](any_expr&& exp) -> any_expr { return self->eval(*exp); }); - - Array& res = deref(new Array); - - res.set_operands(std::move(elems)); - - calcres = any_expr(&res); -} - -void Calculator::visit(Merge& n) { reduce(n, Calc{}); } - -void Calculator::visit(Reduce& n) { - any_expr arr = eval(n.operand(0)); - Expr& expr = n.operand(1); - any_expr accu = eval(n.operand(2)); - any_expr* acptr = &accu; - - auto op = [&expr, acptr, - calclogger = &this->logger](Array& arrop) -> any_expr { - // non destructive predicate is required for evaluating and copying - return std::accumulate(std::make_move_iterator(arrop.begin()), - std::make_move_iterator(arrop.end()), - std::move(*acptr), - SequenceReduction{expr, *calclogger}); - }; - - calcres = - with_type(arr.get(), op, []() -> any_expr { return nullptr; }); -} - -void Calculator::visit(Map& n) { - any_expr arr = eval(n.operand(0)); - auto mapper = [&n, &arr, - calclogger = &this->logger](Array& arrop) -> any_expr { - Expr& expr = n.operand(1); - Operator::container_type mapped_elements; - - std::transform(std::make_move_iterator(arrop.begin()), - std::make_move_iterator(arrop.end()), - std::back_inserter(mapped_elements), - SequenceFunction{expr, *calclogger}); - - arrop.set_operands(std::move(mapped_elements)); - return std::move(arr); - }; - - calcres = with_type(arr.get(), mapper, - []() -> any_expr { return any_expr{new Array}; }); -} - -void Calculator::visit(Filter& n) { - any_expr arr = eval(n.operand(0)); - auto filter = [&n, &arr, - calclogger = &this->logger](Array& arrop) -> any_expr { - Expr& expr = n.operand(1); - Operator::container_type filtered_elements; - - // non destructive predicate is required for evaluating and copying - std::copy_if(std::make_move_iterator(arrop.begin()), - std::make_move_iterator(arrop.end()), - std::back_inserter(filtered_elements), - SequencePredicateNondestructive{expr, *calclogger}); - - arrop.set_operands(std::move(filtered_elements)); - return std::move(arr); - }; - - calcres = with_type(arr.get(), filter, - []() -> any_expr { return any_expr{new Array}; }); -} - -void Calculator::visit(All& n) { - any_expr arr = eval(n.operand(0)); - Array& elems = down_cast(*arr); // evaluated elements - Expr& expr = n.operand(1); - const bool res = std::all_of(std::make_move_iterator(elems.begin()), - std::make_move_iterator(elems.end()), - SequencePredicate{expr, logger}); - - calcres = toany_expr(res); -} - -void Calculator::visit(None& n) { - any_expr arr = eval(n.operand(0)); - Array& elems = down_cast(*arr); // evaluated elements - Expr& expr = n.operand(1); - const bool res = std::none_of(std::make_move_iterator(elems.begin()), - std::make_move_iterator(elems.end()), - SequencePredicate{expr, logger}); - - calcres = toany_expr(res); -} - -void Calculator::visit(Some& n) { - any_expr arr = eval(n.operand(0)); - Array& elems = down_cast(*arr); // evaluated elements - Expr& expr = n.operand(1); - const bool res = std::any_of(std::make_move_iterator(elems.begin()), - std::make_move_iterator(elems.end()), - SequencePredicate{expr, logger}); - - calcres = toany_expr(res); -} - -void Calculator::visit(Error&) { unsupported(); } - -void Calculator::visit(Var& n) { - assert(n.num_evaluated_operands() >= 1); - - any_expr elm = eval(n.operand(0)); - Value& val = down_cast(*elm); - - try { - calcres = vars(val.toJson(), n.num()); - } catch (...) { - calcres = (n.num_evaluated_operands() > 1) ? eval(n.operand(1)) - : toany_expr(nullptr); - } -} - -std::size_t Calculator::missing_aux(Array& elems) { - auto avail = [calc = this](any_expr& v) -> bool { - try { - Value& val = down_cast(*v); - - calc->vars(val.toJson(), -1 /* not in varmap */); - } catch (...) { - return false; - } - - return true; - }; - - Array::iterator beg = elems.begin(); - Array::iterator lim = elems.end(); - Array::iterator pos = std::remove_if(beg, lim, avail); - std::size_t res = std::distance(pos, lim); - - elems.operands().erase(pos, lim); - return res; -} - -void Calculator::visit(Missing& n) { - any_expr arg = eval(n.operand(0)); - auto nonArrayHandler = [&arg, &n, calc = this]() -> Array& { - Array& res = deref(new Array); - - res.set_operands(std::move(n).move_operands()); - res.operands().front().swap(arg); - arg.reset(&res); - calc->visit(res); - - return res; - }; - - Array& elems = with_type( - arg.get(), - [](Array& array) -> Array& { return array; }, // ignore other args - nonArrayHandler); - - missing_aux(elems); - calcres = std::move(arg); -} - -void Calculator::visit(MissingSome& n) { - const std::uint64_t minreq = unpack_value(eval(n.operand(0))); - any_expr arr = eval(n.operand(1)); - Array& elems = down_cast(*arr); // evaluated elements - std::size_t avail = missing_aux(elems); - - if (avail >= minreq) elems.operands().clear(); - - calcres = std::move(arr); -} - -void Calculator::visit(If& n) { - const int num = n.num_evaluated_operands(); - - if (num == 0) { - calcres = toany_expr(nullptr); - return; - } - - const int lim = num - 1; - int pos = 0; - - while (pos < lim) { - if (truthy(eval(n.operand(pos)))) { - calcres = eval(n.operand(pos + 1)); - return; - } - - pos += 2; - } - - calcres = (pos < num) ? eval(n.operand(pos)) : toany_expr(nullptr); -} - -void Calculator::visit(Log& n) { - assert(n.num_evaluated_operands() == 1); - - calcres = eval(n.operand(0)); - - logger << calcres << std::endl; -} - -void Calculator::visit(NullVal& n) { _value(n); } -void Calculator::visit(BoolVal& n) { _value(n); } -void Calculator::visit(IntVal& n) { _value(n); } -void Calculator::visit(UintVal& n) { _value(n); } -void Calculator::visit(DoubleVal& n) { _value(n); } -void Calculator::visit(StringVal& n) { _value(n); } - -CXX_MAYBE_UNUSED -any_expr calculate(Expr& exp, const Calculator::VarAccess& vars) { - Calculator calc{vars, std::cerr}; - - return calc.eval(exp); -} - -CXX_MAYBE_UNUSED -any_expr calculate(any_expr& exp, const Calculator::VarAccess& vars) { - assert(exp.get()); - return calculate(*exp, vars); -} - -CXX_MAYBE_UNUSED -any_expr calculate(any_expr& exp) { - return calculate(exp, - [](const json::value&, int) -> any_expr { unsupported(); }); -} - -any_expr evalPath(const json::string& path, const json::object& obj) { - if (auto pos = obj.find(path); pos != obj.end()) - return jsonlogic::toany_expr(pos->value()); - - if (std::size_t pos = path.find('.'); pos != json::string::npos) { - json::string selector = path.subview(0, pos); - json::string suffix = path.subview(pos + 1); - - return evalPath(suffix, obj.at(selector).as_object()); - } - - throw std::out_of_range("json_logic - unable to locate path"); -} - -template -any_expr evalIdx(IntT idx, const json::array& arr) { - return jsonlogic::toany_expr(arr[idx]); -} - -CXX_MAYBE_UNUSED -any_expr apply(json::value rule, json::value data) { - auto [ast, vars, hasComputed] = translateNode(rule); - Calculator::VarAccess varlookup = [data](const json::value& keyval, - int) -> any_expr { - if (const json::string* ppath = keyval.if_string()) { - //~ std::cerr << *ppath << std::endl; - return ppath->size() ? evalPath(*ppath, data.as_object()) - : jsonlogic::toany_expr(data); - } - - if (const std::int64_t* pidx = keyval.if_int64()) - return evalIdx(*pidx, data.as_array()); - - if (const std::uint64_t* pidx = keyval.if_uint64()) - return evalIdx(*pidx, data.as_array()); - - throw std::logic_error{"json_logic - unsupported var access"}; - }; - - return calculate(ast, varlookup); -} - -struct ValuePrinter : FwdVisitor { - explicit ValuePrinter(std::ostream& stream) : os(stream) {} - - void prn(json::value val) { os << val; } - - void visit(Value& n) final { prn(n.toJson()); } - - void visit(Array& n) final { - bool first = true; - - os << "["; - for (any_expr& el : n) { - if (first) - first = false; - else - os << ","; - - deref(el).accept(*this); - } - - os << "]"; - } - - private: - std::ostream& os; -}; - -std::ostream& operator<<(std::ostream& os, any_expr& n) { - ValuePrinter prn{os}; - - deref(n).accept(prn); - return os; -} -} // namespace json_logic - -#if SUPPLEMENTAL - -/// traverses the children of a node; does not traverse grandchildren -void traverseChildren(Visitor& v, const Operator& node); -void traverseAllChildren(Visitor& v, const Operator& node); -void traverseChildrenReverse(Visitor& v, const Operator& node); - -// only operators have children -void _traverseChildren(Visitor& v, Operator::const_iterator aa, - Operator::const_iterator zz) { - std::for_each(aa, zz, [&v](const any_expr& e) -> void { e->accept(v); }); -} - -void traverseChildren(Visitor& v, const Operator& node) { - Operator::const_iterator aa = node.begin(); - - _traverseChildren(v, aa, aa + node.num_evaluated_operands()); -} - -void traverseAllChildren(Visitor& v, const Operator& node) { - _traverseChildren(v, node.begin(), node.end()); -} - -void traverseChildrenReverse(Visitor& v, const Operator& node) { - Operator::const_reverse_iterator zz = node.crend(); - Operator::const_reverse_iterator aa = zz - node.num_evaluated_operands(); - - std::for_each(aa, zz, [&v](const any_expr& e) -> void { e->accept(v); }); -} - -namespace { -struct SAttributeTraversal : Visitor { - explicit SAttributeTraversal(Visitor& client) : sub(client) {} - - void visit(Expr&) final; - void visit(Operator&) final; - void visit(Eq&) final; - void visit(StrictEq&) final; - void visit(Neq&) final; - void visit(StrictNeq&) final; - void visit(Less&) final; - void visit(Greater&) final; - void visit(Leq&) final; - void visit(Geq&) final; - void visit(And&) final; - void visit(Or&) final; - void visit(Not&) final; - void visit(NotNot&) final; - void visit(Add&) final; - void visit(Sub&) final; - void visit(Mul&) final; - void visit(Div&) final; - void visit(Mod&) final; - void visit(Min&) final; - void visit(Max&) final; - void visit(Map&) final; - void visit(Reduce&) final; - void visit(Filter&) final; - void visit(All&) final; - void visit(None&) final; - void visit(Some&) final; - void visit(Merge&) final; - void visit(Cat&) final; - void visit(Substr&) final; - void visit(In&) final; - void visit(Array& n) final; - void visit(Var&) final; - void visit(Log&) final; - - void visit(If&) final; - - void visit(NullVal& n) final; - void visit(BoolVal& n) final; - void visit(IntVal& n) final; - void visit(UintVal& n) final; - void visit(DoubleVal& n) final; - void visit(StringVal& n) final; - - void visit(Error& n) final; - - private: - Visitor& sub; - - template - inline void _visit(OperatorNode& n) { - traverseChildren(*this, n); - sub.visit(n); - } - - template - inline void _value(ValueNode& n) { - sub.visit(n); - } -}; - -void SAttributeTraversal::visit(Expr&) { typeError(); } -void SAttributeTraversal::visit(Operator&) { typeError(); } -void SAttributeTraversal::visit(Eq& n) { _visit(n); } -void SAttributeTraversal::visit(StrictEq& n) { _visit(n); } -void SAttributeTraversal::visit(Neq& n) { _visit(n); } -void SAttributeTraversal::visit(StrictNeq& n) { _visit(n); } -void SAttributeTraversal::visit(Less& n) { _visit(n); } -void SAttributeTraversal::visit(Greater& n) { _visit(n); } -void SAttributeTraversal::visit(Leq& n) { _visit(n); } -void SAttributeTraversal::visit(Geq& n) { _visit(n); } -void SAttributeTraversal::visit(And& n) { _visit(n); } -void SAttributeTraversal::visit(Or& n) { _visit(n); } -void SAttributeTraversal::visit(Not& n) { _visit(n); } -void SAttributeTraversal::visit(NotNot& n) { _visit(n); } -void SAttributeTraversal::visit(Add& n) { _visit(n); } -void SAttributeTraversal::visit(Sub& n) { _visit(n); } -void SAttributeTraversal::visit(Mul& n) { _visit(n); } -void SAttributeTraversal::visit(Div& n) { _visit(n); } -void SAttributeTraversal::visit(Mod& n) { _visit(n); } -void SAttributeTraversal::visit(Min& n) { _visit(n); } -void SAttributeTraversal::visit(Max& n) { _visit(n); } -void SAttributeTraversal::visit(Array& n) { _visit(n); } -void SAttributeTraversal::visit(Map& n) { _visit(n); } -void SAttributeTraversal::visit(Reduce& n) { _visit(n); } -void SAttributeTraversal::visit(Filter& n) { _visit(n); } -void SAttributeTraversal::visit(All& n) { _visit(n); } -void SAttributeTraversal::visit(None& n) { _visit(n); } -void SAttributeTraversal::visit(Some& n) { _visit(n); } -void SAttributeTraversal::visit(Merge& n) { _visit(n); } -void SAttributeTraversal::visit(Cat& n) { _visit(n); } -void SAttributeTraversal::visit(Substr& n) { _visit(n); } -void SAttributeTraversal::visit(In& n) { _visit(n); } -void SAttributeTraversal::visit(Var& n) { _visit(n); } -void SAttributeTraversal::visit(Log& n) { _visit(n); } - -void SAttributeTraversal::visit(If& n) { _visit(n); } - -void SAttributeTraversal::visit(NullVal& n) { _value(n); } -void SAttributeTraversal::visit(BoolVal& n) { _value(n); } -void SAttributeTraversal::visit(IntVal& n) { _value(n); } -void SAttributeTraversal::visit(UintVal& n) { _value(n); } -void SAttributeTraversal::visit(DoubleVal& n) { _value(n); } -void SAttributeTraversal::visit(StringVal& n) { _value(n); } - -void SAttributeTraversal::visit(Error& n) { sub.visit(n); } -} // namespace - -/// AST traversal function that calls v's visit methods in post-fix order -void traverseInSAttributeOrder(Expr& e, Visitor& vis); - -void traverseInSAttributeOrder(Expr& e, Visitor& vis) { - SAttributeTraversal trav{vis}; - - e.accept(trav); -} -#endif /* SUPPLEMENTAL */ diff --git a/include/experimental/json-io.hpp b/include/experimental/json-io.hpp index 18d9ceb..4a4321c 100644 --- a/include/experimental/json-io.hpp +++ b/include/experimental/json-io.hpp @@ -10,7 +10,7 @@ #include "dataframe.hpp" // use clippy-eval's conversion functions -#include +#include namespace experimental { std::string OTHER_COLUMN = "__other_column%%"; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5d559ee..ff11887 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,7 +18,6 @@ function ( add_test class_name method_name ) ${BOOST_INCLUDE_DIRS} include/ ${jsonlogic_SOURCE_DIR}/cpp/include - ${jsonlogic_SOURCE_DIR}/cpp/src ) target_link_libraries(${target} PRIVATE Boost::json) endfunction() diff --git a/test/TestBag/remove_if.cpp b/test/TestBag/remove_if.cpp index 6fc793b..62b172c 100644 --- a/test/TestBag/remove_if.cpp +++ b/test/TestBag/remove_if.cpp @@ -7,8 +7,8 @@ #include #include #include +#include #include -#include // #include namespace boostjsn = boost::json; diff --git a/test/TestGraph/assign.cpp b/test/TestGraph/assign.cpp index fd96529..ffeb548 100644 --- a/test/TestGraph/assign.cpp +++ b/test/TestGraph/assign.cpp @@ -8,9 +8,9 @@ #include #include #include +#include #include -#include "clippy/clippy-eval.hpp" #include "clippy/selector.hpp" #include "testgraph.hpp" #include "where.cpp" diff --git a/test/TestGraph/connected_components.cpp b/test/TestGraph/connected_components.cpp index 22421cc..3316f0a 100644 --- a/test/TestGraph/connected_components.cpp +++ b/test/TestGraph/connected_components.cpp @@ -8,9 +8,9 @@ #include #include #include +#include #include -#include "clippy/clippy-eval.hpp" #include "testgraph.hpp" static const std::string method_name = "connected_components"; diff --git a/test/TestGraph/count.cpp b/test/TestGraph/count.cpp index 2391e4c..e7407fc 100644 --- a/test/TestGraph/count.cpp +++ b/test/TestGraph/count.cpp @@ -7,9 +7,9 @@ #include #include #include +#include #include -#include "clippy/clippy-eval.hpp" #include "clippy/selector.hpp" #include "testgraph.hpp" diff --git a/test/TestGraph/degree.cpp b/test/TestGraph/degree.cpp index 657556d..ec761bd 100644 --- a/test/TestGraph/degree.cpp +++ b/test/TestGraph/degree.cpp @@ -7,9 +7,9 @@ #include #include #include +#include #include -#include "clippy/clippy-eval.hpp" #include "clippy/selector.hpp" #include "testgraph.hpp" diff --git a/test/TestGraph/dump.cpp b/test/TestGraph/dump.cpp index b6ded1a..2b4b46d 100644 --- a/test/TestGraph/dump.cpp +++ b/test/TestGraph/dump.cpp @@ -8,9 +8,9 @@ #include #include #include +#include #include -#include "clippy/clippy-eval.hpp" #include "clippy/selector.hpp" #include "testgraph.hpp" diff --git a/test/TestGraph/dump2.cpp b/test/TestGraph/dump2.cpp index 666f4d9..6bde2b1 100644 --- a/test/TestGraph/dump2.cpp +++ b/test/TestGraph/dump2.cpp @@ -8,9 +8,9 @@ #include #include #include +// #include #include -#include "clippy/clippy-eval.hpp" #include "clippy/selector.hpp" #include "testgraph.hpp" #include "where.cpp" diff --git a/test/TestGraph/extrema.cpp b/test/TestGraph/extrema.cpp index cc13f1e..fd537b2 100644 --- a/test/TestGraph/extrema.cpp +++ b/test/TestGraph/extrema.cpp @@ -7,8 +7,8 @@ #include #include #include +#include -#include "clippy/clippy-eval.hpp" #include "clippy/selector.hpp" #include "testgraph.hpp" diff --git a/test/TestGraph/for_all_edges.cpp b/test/TestGraph/for_all_edges.cpp index 974b64e..bd4e90d 100644 --- a/test/TestGraph/for_all_edges.cpp +++ b/test/TestGraph/for_all_edges.cpp @@ -7,9 +7,9 @@ #include #include #include +#include #include -#include "clippy/clippy-eval.hpp" #include "testgraph.hpp" static const std::string method_name = "add_node"; diff --git a/test/TestGraph/series_str.cpp b/test/TestGraph/series_str.cpp index f4ea6e7..c7120f7 100644 --- a/test/TestGraph/series_str.cpp +++ b/test/TestGraph/series_str.cpp @@ -7,8 +7,8 @@ #include #include #include +#include -#include "clippy/clippy-eval.hpp" #include "clippy/selector.hpp" #include "testgraph.hpp" diff --git a/test/TestGraph/where.cpp b/test/TestGraph/where.cpp index 39b62b0..d42a13e 100644 --- a/test/TestGraph/where.cpp +++ b/test/TestGraph/where.cpp @@ -1,11 +1,12 @@ #include #include #include +#include #include +#include #include #include -#include "clippy/clippy-eval.hpp" #include "clippy/selector.hpp" #include "testgraph.hpp" @@ -17,16 +18,27 @@ auto parse_where_expression(M& mvmap_, boost::json::object& expression, boost::json::object exp2(expression); // boost::json::object submission_data{}; - auto [_a /*unused*/, vars, _b /*unused*/] = - jsonlogic::translateNode(exp2["rule"]); + std::vector vars; + jsonlogic::any_expr expression_rule_; + + // we use expression_rule_ (and expression_rule; see below) in order to avoid + // having to recompute this every time we call the lambda. + std::tie(expression_rule_, vars, std::ignore) = + jsonlogic::create_logic(exp2["rule"]); + + // this works around a deficiency in C++ compilers where + // unique pointers moved into a lambda cannot be moved into + // an std::function. + jsonlogic::expr* rawexpr = expression_rule_.release(); + std::shared_ptr expression_rule{rawexpr}; std::cerr << "parse_where: # of vars: " << vars.size() << std::endl; for (const auto& var : vars) { std::cerr << " apply_jl var dump: var: " << var << std::endl; } - auto apply_jl = [&expression, vars, &mvmap_, - &submission_data](mvmap::locator loc) { + auto apply_jl = [&expression, vars, expression_rule, &mvmap_, + &submission_data](mvmap::locator loc) mutable { std::cerr << " apply_jl: # of vars: " << vars.size() << std::endl; for (const auto& var : vars) { std::cerr << " apply_jl: var: " << var << std::endl; @@ -62,8 +74,8 @@ auto parse_where_expression(M& mvmap_, boost::json::object& expression, } std::cerr << " apply_jl: submission_data: " << submission_data << std::endl; - jsonlogic::any_expr res = - jsonlogic::apply(expression["rule"], submission_data); + jsonlogic::any_expr res = jsonlogic::apply( + *expression_rule, jsonlogic::data_accessor(submission_data)); std::cerr << " apply_jl: res: " << res << std::endl; return jsonlogic::unpack_value(res); }; diff --git a/test/TestSet/remove_if.cpp b/test/TestSet/remove_if.cpp index 828d1a0..28b69ea 100644 --- a/test/TestSet/remove_if.cpp +++ b/test/TestSet/remove_if.cpp @@ -7,10 +7,9 @@ #include #include #include +#include #include -#include "clippy/clippy-eval.hpp" - namespace boostjsn = boost::json; static const std::string method_name = "remove_if"; diff --git a/test/include/wheredev.cpp b/test/include/wheredev.cpp index e0e4d33..b822259 100644 --- a/test/include/wheredev.cpp +++ b/test/include/wheredev.cpp @@ -1,10 +1,9 @@ #include #include #include +#include #include -#include "clippy/clippy-eval.hpp" - static const std::string json_blob = R"({"rule":{"and":[{"<":[{"var":"temp"},110]},{"==":[{"var":"pie.filling"},"apple"]}]},"data":{"temp":100,"pie":{"filling":"apple"}},"expected":true})"; From 70402fe1f800451e256a32312010054f9a824a6e Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Thu, 17 Apr 2025 16:02:55 -0700 Subject: [PATCH 03/11] changed workflow --- .github/workflows/pr.yml | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 7abeace..9142040 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -19,35 +19,14 @@ jobs: python -m pip install pytest git+https://github.com/llnl/clippy@master - - name: Install Boost - uses: MarkusJx/install-boost@v2.4.5 - id: install-boost - with: - # REQUIRED: Specify the required boost version - # A list of supported versions can be found here: - # https://github.com/MarkusJx/prebuilt-boost/blob/main/versions-manifest.json - boost_version: 1.83.0 - # OPTIONAL: Specify a platform version - # platform_version: 18.04 - # OPTIONAL: Specify a custom install location - boost_install_dir: /home/runner/work/boost - # OPTIONAL: Specify a toolset - toolset: gcc - # OPTIONAL: Specify an architecture - # arch: x86 - - name: Build backend id: build-backend env: BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} run: | - echo BOOST_ROOT is $BOOST_ROOT /end/ - sudo apt install doxygen - # TMPDIR=$(mktemp -d) - # git clone https://github.com/LLNL/clippy-cpp --branch $GITHUB_HEAD_REF $TMPDIR - + sudo apt install doxygen mkdir -p build - cd build && cmake -DBOOST_ROOT=$BOOST_ROOT .. && make && cd .. + cd build && cmake .. && make && cd .. ls -l build/test BACKEND=$PWD/build/test echo "BACKEND=$BACKEND" >> $GITHUB_ENV From 6265fb9ce465b354ef0fd126574a8881df737fa0 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Thu, 17 Apr 2025 16:49:20 -0700 Subject: [PATCH 04/11] revert runner update --- .github/workflows/pr.yml | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 9142040..7abeace 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -19,14 +19,35 @@ jobs: python -m pip install pytest git+https://github.com/llnl/clippy@master + - name: Install Boost + uses: MarkusJx/install-boost@v2.4.5 + id: install-boost + with: + # REQUIRED: Specify the required boost version + # A list of supported versions can be found here: + # https://github.com/MarkusJx/prebuilt-boost/blob/main/versions-manifest.json + boost_version: 1.83.0 + # OPTIONAL: Specify a platform version + # platform_version: 18.04 + # OPTIONAL: Specify a custom install location + boost_install_dir: /home/runner/work/boost + # OPTIONAL: Specify a toolset + toolset: gcc + # OPTIONAL: Specify an architecture + # arch: x86 + - name: Build backend id: build-backend env: BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} run: | - sudo apt install doxygen + echo BOOST_ROOT is $BOOST_ROOT /end/ + sudo apt install doxygen + # TMPDIR=$(mktemp -d) + # git clone https://github.com/LLNL/clippy-cpp --branch $GITHUB_HEAD_REF $TMPDIR + mkdir -p build - cd build && cmake .. && make && cd .. + cd build && cmake -DBOOST_ROOT=$BOOST_ROOT .. && make && cd .. ls -l build/test BACKEND=$PWD/build/test echo "BACKEND=$BACKEND" >> $GITHUB_ENV From 8c0012e6771183d1b7fa45c5bfa49b10c7eeee0c Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Thu, 17 Apr 2025 17:03:43 -0700 Subject: [PATCH 05/11] bump install boost action to see if it fixes the cache change issue --- .github/workflows/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 7abeace..248f7f0 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -20,7 +20,7 @@ jobs: - name: Install Boost - uses: MarkusJx/install-boost@v2.4.5 + uses: MarkusJx/install-boost@v2.5 id: install-boost with: # REQUIRED: Specify the required boost version From ea57f045d9e2b782e1f64c75201aa3e9e848dca1 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Thu, 17 Apr 2025 17:05:43 -0700 Subject: [PATCH 06/11] oops --- .github/workflows/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 248f7f0..deb86f8 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -20,7 +20,7 @@ jobs: - name: Install Boost - uses: MarkusJx/install-boost@v2.5 + uses: MarkusJx/install-boost@v2 id: install-boost with: # REQUIRED: Specify the required boost version From 4b5101ed0c8e10a9498da7ebf9d54736f7c97ca4 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Thu, 17 Apr 2025 17:14:07 -0700 Subject: [PATCH 07/11] bump boost version --- .github/workflows/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index deb86f8..0873d13 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -26,7 +26,7 @@ jobs: # REQUIRED: Specify the required boost version # A list of supported versions can be found here: # https://github.com/MarkusJx/prebuilt-boost/blob/main/versions-manifest.json - boost_version: 1.83.0 + boost_version: 1.87.0 # OPTIONAL: Specify a platform version # platform_version: 18.04 # OPTIONAL: Specify a custom install location From b2dc663d0ce5def9d4c82ad5aab3d6ee906d99a3 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Fri, 11 Jul 2025 09:50:40 -0700 Subject: [PATCH 08/11] dev env setup --- CMakeLists.txt | 26 +++++++++++++++----------- CMakePresets.json | 14 ++++++++++++++ test/requirements-dev.txt | 6 ++++++ test/requirements.txt | 1 + 4 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 CMakePresets.json create mode 100644 test/requirements-dev.txt create mode 100644 test/requirements.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index d6ac50c..c7121fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,8 @@ # # SPDX-License-Identifier: MIT -# Works with 3.11 and tested through 3.15 (not tested yet) -cmake_minimum_required(VERSION 3.14) + +cmake_minimum_required(VERSION 3.26) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set(ALLOW_DUPLICATE_CUSTOM_TARGETS TRUE) @@ -37,7 +37,7 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) # Note this needs to be done in the main CMakeLists # since it calls enable_testing, which must be in the # main CMakeLists. - include(CTest) + # include(CTest) # Docs only available if this is the main app find_package(Doxygen) @@ -77,19 +77,20 @@ endif() # # Boost -set(BOOST_URL - "https://github.com/boostorg/boost/releases/download/boost-1.87.0/boost-1.87.0-cmake.tar.gz" +# Download and build Boost::json +set(BOOST_URL + "https://github.com/boostorg/boost/releases/download/boost-1.87.0/boost-1.87.0-cmake.tar.gz" CACHE STRING "URL to fetch Boost tarball") -# -# Boost + +set(BOOST_INCLUDE_LIBRARIES json lexical_cast range) +set(BUILD_SHARED_LIBS ON) FetchContent_Declare( - Boost - URL ${BOOST_URL} -) -set(BOOST_INCLUDE_LIBRARIES json) + Boost + URL ${BOOST_URL}) FetchContent_MakeAvailable(Boost) + # # JSONLogic # find_package(jsonlogic QUIET) @@ -125,9 +126,12 @@ option(TEST_WITH_SLURM "Run tests with Slurm" OFF) # Header-only library, so likely not have src dir # add_subdirectory(src) +message(STATUS "CMAKE_PROJECT_NAME: ${CMAKE_PROJECT_NAME}") +message(STATUS "PROJECT_NAME: ${PROJECT_NAME}") # Testing & examples are only available if this is the main app # Emergency override MODERN_CMAKE_BUILD_TESTING provided as well if((CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME OR MODERN_CMAKE_BUILD_TESTING) AND BUILD_TESTING) + message(STATUS "adding test subdir") add_subdirectory(test) # Example codes are here. #add_subdirectory(examples) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..2cf1031 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,14 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "default", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "BUILD_TESTING": "ON" + }, + "generator": "Unix Makefiles", + "binaryDir": "${sourceDir}/build" + } + ] +} \ No newline at end of file diff --git a/test/requirements-dev.txt b/test/requirements-dev.txt new file mode 100644 index 0000000..1254e18 --- /dev/null +++ b/test/requirements-dev.txt @@ -0,0 +1,6 @@ +coverage>=5.5 +flake8 +mypy +pylint>=2.15,<3 +pyproj>=3.6,<4 +pytest>=7,<8 diff --git a/test/requirements.txt b/test/requirements.txt new file mode 100644 index 0000000..eca0173 --- /dev/null +++ b/test/requirements.txt @@ -0,0 +1 @@ +semver >= 3.0 From c2a5c55ccfca0d0775e9b83ea30f61d1d0ddd2f6 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Fri, 11 Jul 2025 23:53:59 +0000 Subject: [PATCH 09/11] modified tests, added devcontainer --- .devcontainer/Dockerfile | 22 ++++++++++++ .devcontainer/devcontainer.json | 21 ++++++++++++ .devcontainer/reinstall-cmake.sh | 59 ++++++++++++++++++++++++++++++++ .gitignore | 3 +- test/requirements-dev.txt | 6 ---- test/requirements.txt | 3 +- test/test_clippy.py | 2 +- 7 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/reinstall-cmake.sh delete mode 100644 test/requirements-dev.txt diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..5df108f --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,22 @@ +# FROM mcr.microsoft.com/devcontainers/cpp:1-ubuntu-24.04 +FROM mcr.microsoft.com/devcontainers/cpp:dev-ubuntu-24.04 + +ARG REINSTALL_CMAKE_VERSION_FROM_SOURCE="none" + +# Optionally install the cmake for vcpkg +COPY ./reinstall-cmake.sh /tmp/ + +RUN if [ "${REINSTALL_CMAKE_VERSION_FROM_SOURCE}" != "none" ]; then \ + chmod +x /tmp/reinstall-cmake.sh && /tmp/reinstall-cmake.sh ${REINSTALL_CMAKE_VERSION_FROM_SOURCE}; \ + fi \ + && rm -f /tmp/reinstall-cmake.sh + +# [Optional] Uncomment this section to install additional vcpkg ports. +# RUN su vscode -c "${VCPKG_ROOT}/vcpkg install clangd" + +# [Optional] Uncomment this section to install additional packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends +RUN apt update && export DEBIAN_FRONTEND=noninteractive && apt -y install clangd-19 clang-tidy-19 python3-pip +RUN update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-19 100 +RUN update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-19 100 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..68e3eee --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,21 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/cpp +{ + "name": "C++", + "build": { + "dockerfile": "Dockerfile" + }, + "containerEnv": { + "CLIPPY_BACKEND_PATH": "${containerWorkspaceFolder}/build/test" + }, + "customizations": { + "vscode": { + "extensions": [ + "llvm-vs-code-extensions.vscode-clangd", + "ms-python.python" + // add other extensions as needed + ] + } + }, + "postCreateCommand": "pip install --break-system-packages -r ${containerWorkspaceFolder}/test/requirements.txt" +} diff --git a/.devcontainer/reinstall-cmake.sh b/.devcontainer/reinstall-cmake.sh new file mode 100644 index 0000000..408b81d --- /dev/null +++ b/.devcontainer/reinstall-cmake.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +set -e + +CMAKE_VERSION=${1:-"none"} + +if [ "${CMAKE_VERSION}" = "none" ]; then + echo "No CMake version specified, skipping CMake reinstallation" + exit 0 +fi + +# Cleanup temporary directory and associated files when exiting the script. +cleanup() { + EXIT_CODE=$? + set +e + if [[ -n "${TMP_DIR}" ]]; then + echo "Executing cleanup of tmp files" + rm -Rf "${TMP_DIR}" + fi + exit $EXIT_CODE +} +trap cleanup EXIT + + +echo "Installing CMake..." +apt-get -y purge --auto-remove cmake +mkdir -p /opt/cmake + +architecture=$(dpkg --print-architecture) +case "${architecture}" in + arm64) + ARCH=aarch64 ;; + amd64) + ARCH=x86_64 ;; + *) + echo "Unsupported architecture ${architecture}." + exit 1 + ;; +esac + +CMAKE_BINARY_NAME="cmake-${CMAKE_VERSION}-linux-${ARCH}.sh" +CMAKE_CHECKSUM_NAME="cmake-${CMAKE_VERSION}-SHA-256.txt" +TMP_DIR=$(mktemp -d -t cmake-XXXXXXXXXX) + +echo "${TMP_DIR}" +cd "${TMP_DIR}" + +curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_BINARY_NAME}" -O +curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_CHECKSUM_NAME}" -O + +sha256sum -c --ignore-missing "${CMAKE_CHECKSUM_NAME}" +sh "${TMP_DIR}/${CMAKE_BINARY_NAME}" --prefix=/opt/cmake --skip-license + +ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake +ln -s /opt/cmake/bin/ctest /usr/local/bin/ctest diff --git a/.gitignore b/.gitignore index 968addd..a886cab 100644 --- a/.gitignore +++ b/.gitignore @@ -7,11 +7,10 @@ CMakeDoxygenDefaults.cmake DartConfiguration.tcl __pycache__ /.vscode -/.devcontainer *.dSYM testbracket testmvmap testdf test/TestGraph/testgraph test/include/wheredev -a.out \ No newline at end of file +a.out diff --git a/test/requirements-dev.txt b/test/requirements-dev.txt deleted file mode 100644 index 1254e18..0000000 --- a/test/requirements-dev.txt +++ /dev/null @@ -1,6 +0,0 @@ -coverage>=5.5 -flake8 -mypy -pylint>=2.15,<3 -pyproj>=3.6,<4 -pytest>=7,<8 diff --git a/test/requirements.txt b/test/requirements.txt index eca0173..bcb9aac 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -1 +1,2 @@ -semver >= 3.0 +llnl-clippy >= 0.4 +pytest>=7,<8 \ No newline at end of file diff --git a/test/test_clippy.py b/test/test_clippy.py index 6cd1d2c..ca4d57c 100644 --- a/test/test_clippy.py +++ b/test/test_clippy.py @@ -3,7 +3,7 @@ import clippy from clippy.error import ClippyValidationError, ClippyInvalidSelectorError -from clippy.expressions import Selector +from clippy.selectors import Selector import logging From 6c1684cadddb866a21f45088ccac34e75c57061a Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sat, 12 Jul 2025 00:43:53 +0000 Subject: [PATCH 10/11] tests now include TestGraph. --- test/test_clippy.py | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/test/test_clippy.py b/test/test_clippy.py index ca4d57c..585c114 100644 --- a/test/test_clippy.py +++ b/test/test_clippy.py @@ -1,9 +1,12 @@ +# This should mirror test_clippy.py from the llnl-clippy repo. import pytest import sys +sys.path.append("src") + +import jsonlogic as jl import clippy from clippy.error import ClippyValidationError, ClippyInvalidSelectorError -from clippy.selectors import Selector import logging @@ -31,6 +34,11 @@ def testsel(): return clippy.TestSelector() +@pytest.fixture() +def testgraph(): + return clippy.TestGraph() + + def test_imports(): assert "TestBag" in clippy.__dict__ @@ -49,6 +57,12 @@ def test_bag(testbag): assert testbag.size() == 2 +def test_clippy_call_with_string(testfun): + assert testfun.call_with_string("Seth") == "Howdy, Seth" + with pytest.raises(ClippyValidationError): + testfun.call_with_string() + + def test_expression_gt_gte(testbag): testbag.insert(10).insert(41).insert(42).insert(50).insert(51).insert(52) assert testbag.size() == 6 @@ -127,12 +141,6 @@ def test_expression_mod(testbag): # assert testbag.size() == 2 -def test_clippy_call_with_string(testfun): - assert testfun.call_with_string("Seth") == "Howdy, Seth" - with pytest.raises(ClippyValidationError): - testfun.call_with_string() - - def test_clippy_returns_int(testfun): assert testfun.returns_int() == 42 @@ -173,11 +181,25 @@ def test_selectors(testsel): assert testsel.nodes.b.__doc__ == "docstring for nodes.b" assert testsel.nodes.b.c.__doc__ == "docstring for nodes.b.c" - assert isinstance(testsel.nodes.b, Selector) - assert isinstance(testsel.nodes.b.c, Selector) + assert isinstance(testsel.nodes.b, jl.Variable) + assert isinstance(testsel.nodes.b.c, jl.Variable) with pytest.raises(ClippyInvalidSelectorError): testsel.add(testsel.nodes, "_bad", desc="this is a bad selector name") # with pytest.raises(ClippyInvalidSelectorError): # testsel.add(testsel, 'bad', desc="this is a top-level selector") + + +def test_graph(testgraph): + testgraph.add_edge("a", "b").add_edge("b", "c").add_edge("a", "c").add_edge( + "c", "d" + ).add_edge("d", "e").add_edge("e", "f").add_edge("f", "g").add_edge("e", "g") + + assert testgraph.nv() == 7 + assert testgraph.ne() == 8 + + testgraph.add_series(testgraph.node, "degree", desc="node degrees") + testgraph.degree(testgraph.node.degree) + c_e_only = testgraph.dump2(testgraph.node.degree, where=testgraph.node.degree > 2) + assert "c" in c_e_only and "e" in c_e_only and len(c_e_only) == 2 From a8dbbc0c411d76520ce89e5326558a0b64d56da3 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Sat, 12 Jul 2025 00:45:53 +0000 Subject: [PATCH 11/11] bumped python version --- .github/workflows/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 0873d13..1564894 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.10' - name: Install dependencies run: |