#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/YAMLTraits.h" #include "revng/ADT/KeyTraits.h" #include "revng/ADT/KeyedObjectContainer.h" #include "revng/ADT/KeyedObjectTraits.h" #include "revng/Support/Assert.h" #include "revng/Support/Debug.h" #include "revng/Support/YAMLTraits.h" // // has_yaml // namespace tupletree::detail { using namespace llvm::yaml; template constexpr bool has_yaml_v = has_DocumentListTraits::value or has_MappingTraits::value or has_SequenceTraits::value or has_BlockScalarTraits::value or has_CustomMappingTraits::value or has_PolymorphicTraits::value or has_ScalarTraits::value or has_ScalarEnumerationTraits::value; struct NoYaml {}; } // namespace tupletree::detail template constexpr bool has_yaml_v = tupletree::detail::has_yaml_v; static_assert(!has_yaml_v); static_assert(has_yaml_v); static_assert(has_yaml_v>); template using enable_if_has_yaml_t = std::enable_if_t, K>; template using enable_if_has_not_yaml_t = std::enable_if_t, K>; // // slice // /// Copy into a std::array a slice of an llvm::ArrayRef template std::array slice(llvm::ArrayRef Old) { std::array Result; auto StartIt = Old.begin() + Start; std::copy(StartIt, StartIt + Size, Result.begin()); return Result; } /// Copy into a std::array a slice of a std::array template std::array slice(const std::array &Old) { std::array Result; auto StartIt = Old.begin() + Start; std::copy(StartIt, StartIt + Size, Result.begin()); return Result; } // // TupleLikeTraits // /// Trait to provide name of the tuple-like class and its fields template struct TupleLikeTraits { // static const char *name(); // template // static const char *fieldName(); }; // // Implementation of MappingTraits for TupleLikeTraits implementors // /// Tuple-liek can implement llvm::yaml::MappingTraits inheriting this class template struct TupleLikeMappingTraits { // Recursive step template static void mapping(llvm::yaml::IO &io, T &Obj) { // Define the field using getTupleFieldName and the associated field io.mapRequired(TupleLikeTraits::template fieldName(), get(Obj)); // Recur mapping(io, Obj); } // Base case template<> void mapping>(llvm::yaml::IO &io, T &Obj) {} }; // // visit implementation // namespace tupletree::detail { template enable_if_tuple_end_t visitTuple(Visitor &V, T &Obj) { } template enable_if_not_tuple_end_t visitTuple(Visitor &V, T &Obj) { // Visit the field visit(V, get(Obj)); // Visit next element in tuple visitTuple(V, Obj); } } // namespace tupletree::detail // Tuple-like template enable_if_has_tuple_size_t visit(Visitor &V, T &Obj) { V.preVisit(Obj); tupletree::detail::visitTuple(V, Obj); V.postVisit(Obj); } // Container-like template enable_if_is_container_t visit(Visitor &V, T &Obj) { V.preVisit(Obj); using value_type = typename T::value_type; for (value_type &Element : Obj) { visit(V, Element); } V.postVisit(Obj); } // All the others template std::enable_if_t or has_tuple_size_v)> visit(Visitor &V, T &Element) { V.preVisit(Element); V.postVisit(Element); } /// Default visitor, doing nothing struct DefaultTupleTreeVisitor { template void preVisit(T &) {} template void postVisit(T &) {} }; // // tupleIndexByName // template enable_if_tuple_end_t tupleIndexByName(llvm::StringRef Name) { return -1; } template enable_if_not_tuple_end_t tupleIndexByName(llvm::StringRef Name) { llvm::StringRef ThisName = TupleLikeTraits::template fieldName(); if (Name == ThisName) return I; else return tupleIndexByName(Name); } // // getByKey // namespace tupletree::detail { template enable_if_tuple_end_t getByKeyTuple(RootT &M, KeyT Key) { return nullptr; } template enable_if_not_tuple_end_t getByKeyTuple(RootT &M, KeyT Key) { if (I == Key) { using tuple_element = typename std::tuple_element::type; revng_assert((std::is_same_v) ); return reinterpret_cast(&get(M)); } else { return getByKeyTuple(M, Key); } } } // namespace tupletree::detail template enable_if_has_tuple_size_t getByKey(RootT &M, KeyT Key) { return tupletree::detail::getByKeyTuple(M, Key); } template enable_if_is_container_t getByKey(RootT &M, KeyT Key) { for (auto &Element : M) { using KOT = KeyedObjectTraits>; if (KOT::key(Element) == Key) return ∈ } return nullptr; } // // callOnPathSteps (no instance) // template enable_if_has_tuple_size_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path); template enable_if_is_not_container_or_tuple_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path) { return false; } template enable_if_is_container_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path) { using value_type = typename RootT::value_type; using KOT = KeyedObjectTraits; using key_type = decltype(KOT::key(std::declval())); constexpr size_t IntsCount = KeyTraits::IntsCount; auto PathStep = slice<0, IntsCount>(Path); auto TargetKey = KeyTraits::fromInts(PathStep); V.template visitContainerElement(TargetKey); if (Path.size() > IntsCount) { return callOnPathSteps(V, Path.slice(IntsCount)); } return true; } namespace tupletree::detail { template enable_if_tuple_end_t callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path) { return true; } template enable_if_not_tuple_end_t callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path) { if (Path[0] == I) { using next_type = typename std::tuple_element::type; V.template visitTupleElement(); if (Path.size() > 1) { return callOnPathSteps(V, Path.slice(1)); } } else { return callOnPathStepsTuple(V, Path); } return true; } } // namespace tupletree::detail template enable_if_has_tuple_size_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path) { return tupletree::detail::callOnPathStepsTuple(V, Path); } // // callOnPathSteps (with instance) // namespace tupletree::detail { template enable_if_tuple_end_t callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path, RootT &M) { return true; } template enable_if_is_not_container_or_tuple_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path, RootT &M) { return false; } template enable_if_not_tuple_end_t callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path, RootT &M) { if (Path[0] == I) { using next_type = typename std::tuple_element::type; next_type &Element = get(M); V.template visitTupleElement(Element); if (Path.size() > 1) { return callOnPathSteps(V, Path.slice(1), Element); } } else { return callOnPathStepsTuple(V, Path, M); } return true; } } // namespace tupletree::detail template enable_if_has_tuple_size_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path, RootT &M) { return tupletree::detail::callOnPathStepsTuple(V, Path, M); } template enable_if_is_container_t callOnPathSteps(Visitor &V, llvm::ArrayRef Path, RootT &M) { using value_type = typename RootT::value_type; using KOT = KeyedObjectTraits; using key_type = decltype(KOT::key(std::declval())); constexpr size_t IntsCount = KeyTraits::IntsCount; auto PathStep = slice<0, IntsCount>(Path); auto TargetKey = KeyTraits::fromInts(PathStep); value_type *Matching = nullptr; for (value_type &Element : M) { using KOT = KeyedObjectTraits; if (KOT::key(Element) == TargetKey) { Matching = ∈ break; } } if (Matching == nullptr) return false; V.template visitContainerElement(TargetKey, *Matching); if (Path.size() > IntsCount) { return callOnPathSteps(V, Path.slice(IntsCount), *Matching); } return true; } // // callByPath (no instance) // namespace tupletree::detail { template struct CallByPathVisitor { size_t PathSize; Visitor &V; template void visitTupleElement() { --PathSize; if (PathSize == 0) V.template visitTupleElement(); } template void visitContainerElement(KeyT Key) { constexpr size_t IntsCount = KeyTraits::IntsCount; PathSize -= IntsCount; if (PathSize == 0) V.template visitContainerElement(Key); } }; } // namespace tupletree::detail template bool callByPath(Visitor &V, const KeyIntVector &Path) { using namespace tupletree::detail; CallByPathVisitor CBPV{ Path.size(), V }; return callOnPathSteps(CBPV, Path); } // // callByPath (with instance) // namespace tupletree::detail { template struct CallByPathVisitorWithInstance { size_t PathSize; Visitor &V; template void visitTupleElement(K &Element) { --PathSize; if (PathSize == 0) V.template visitTupleElement(Element); } template void visitContainerElement(KeyT Key, K &Element) { constexpr size_t IntsCount = KeyTraits::IntsCount; PathSize -= IntsCount; if (PathSize == 0) V.template visitContainerElement(Key, Element); } }; } // namespace tupletree::detail template bool callByPath(Visitor &V, const KeyIntVector &Path, RootT &M) { using namespace tupletree::detail; CallByPathVisitorWithInstance CBPV{ Path.size(), V }; return callOnPathSteps(CBPV, Path, M); } // // getByPath // namespace tupletree::detail { template struct GetByPathVisitor { ResultT *Result = nullptr; template void visitContainerElement(KeyT, K &) { Result = nullptr; } template void visitContainerElement(KeyT, ResultT &Element) { Result = ∈ } template void visitTupleElement(K &) { Result = nullptr; } template void visitTupleElement(ResultT &Element) { Result = ∈ } }; } // namespace tupletree::detail template ResultT *getByPath(const KeyIntVector &Path, RootT &M) { using namespace tupletree::detail; GetByPathVisitor GBPV; if (not callByPath(GBPV, Path, M)) return nullptr; else return GBPV.Result; } // // pathAsString // namespace tupletree::detail { class DumpPathVisitor { private: llvm::raw_string_ostream Stream; public: DumpPathVisitor(std::string &Result) : Stream(Result) {} template void visitTupleElement() { Stream << "/" << TupleLikeTraits::template fieldName(); } template void visitContainerElement(KeyT Key) { Stream << "/" << getNameFromYAMLScalar(Key); } }; } // namespace tupletree::detail template std::optional pathAsString(const KeyIntVector &Path) { std::string Result; { tupletree::detail::DumpPathVisitor PV(Result); if (not callOnPathSteps(PV, Path)) return {}; } return Result; } class PathMatcher { private: KeyIntVector Path; std::vector> Free; private: PathMatcher() = default; public: template static std::optional create(llvm::StringRef Path) { revng_assert(Path.startswith("/")); PathMatcher Result; if (visitTupleTreeNode(Path.substr(1), Result)) return Result; else return {}; } public: const KeyIntVector &path() const { return Path; } public: template KeyIntVector apply(Ts... Args) const { revng_assert(sizeof...(Args) == Free.size()); KeyIntVector Result = Path; applyImpl<0, Ts...>(Result, Args...); return Result; } template std::optional> match(const KeyIntVector &Search) { revng_assert(sizeof...(Args) == Free.size()); if (Path.size() != Search.size()) return {}; // // Check non-variable parts match // using Pair = std::pair; std::vector Terminator{ { Path.size(), 0 } }; size_t LastEnd = 0; for (auto [Start, Size] : llvm::concat(Free, Terminator)) { for (size_t I = LastEnd; I < Start; ++I) { if (Search[I] != Path[I]) return {}; } LastEnd = Start + Size; } // // Compute result // std::tuple Result; extractKeys(Search, Result); return Result; } private: template void depositKey(KeyIntVector &Result, T Arg) const { auto [Start, Size] = Free.at(I); revng_assert(Size == KeyTraits::IntsCount); for (auto P : llvm::enumerate(KeyTraits::toInts(Arg))) Result[Start + P.index()] = P.value(); } template void applyImpl(KeyIntVector &Result, T Arg) const { depositKey(Result, Arg); } template void applyImpl(KeyIntVector &Result, T Arg, Ts... Args) const { depositKey(Result, Arg); applyImpl(Result, Args...); } template void extractKeys(const KeyIntVector &Search, T &Tuple) const { if constexpr (I < std::tuple_size_v) { using element = std::tuple_element_t; using IntsArray = typename KeyTraits::IntsArray; IntsArray Ints; auto [Start, Size] = Free[I]; for (auto P : llvm::enumerate(Ints)) P.value() = Search[Start + P.index()]; std::get(Tuple) = KeyTraits::fromInts(Ints); extractKeys(Search, Tuple); } } private: template static bool visitTuple(llvm::StringRef Current, llvm::StringRef Rest, PathMatcher &Result); template static enable_if_has_tuple_size_t visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result); template static enable_if_is_container_t visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result); template static enable_if_is_not_container_or_tuple_t visitTupleTreeNode(llvm::StringRef Path, PathMatcher &Result); }; template enable_if_has_tuple_size_t PathMatcher::visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result) { if (String.size() == 0) return true; auto [Before, After] = String.split('/'); return visitTuple(Before, After, Result); } template enable_if_is_container_t PathMatcher::visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result) { if (String.size() == 0) return true; auto [Before, After] = String.split('/'); using Key = std::remove_cv_t; using Value = typename T::value_type; if (Before == "*") { auto Count = KeyTraits::IntsCount; Result.Free.push_back({ Result.Path.size(), Count }); for (size_t I = 0; I < Count; ++I) Result.Path.push_back(0); } else { for (KeyInt I : KeyTraits::toInts(getValueFromYAMLScalar(Before))) Result.Path.push_back(I); } return visitTupleTreeNode(After, Result); } template enable_if_is_not_container_or_tuple_t PathMatcher::visitTupleTreeNode(llvm::StringRef Path, PathMatcher &Result) { return Path.size() == 0; } template bool PathMatcher::visitTuple(llvm::StringRef Current, llvm::StringRef Rest, PathMatcher &Result) { if constexpr (I < std::tuple_size_v) { if (TupleLikeTraits::template fieldName() == Current) { Result.Path.push_back(I); using element = typename std::tuple_element_t; return PathMatcher::visitTupleTreeNode(Rest, Result); } else { return visitTuple(Current, Rest, Result); } } else { // Not found return false; } } template std::optional stringAsPath(llvm::StringRef Path) { auto Result = PathMatcher::create(Path); if (Result) return Result->path(); else return {}; } template ResultT *getByPath(llvm::StringRef Path, RootT &M) { auto MaybeKeyVector = stringAsPath(Path); if (not MaybeKeyVector) return {}; else return getByPath(*MaybeKeyVector, M); } // // FOR_EACH macro implemenation // #define GET_MACRO(_0, \ _1, \ _2, \ _3, \ _4, \ _5, \ _6, \ _7, \ _8, \ _9, \ _10, \ _11, \ _12, \ _13, \ _14, \ _15, \ _16, \ NAME, \ ...) \ NAME #define NUMARGS(...) \ GET_MACRO(_0, \ __VA_ARGS__, \ 16, \ 15, \ 14, \ 13, \ 12, \ 11, \ 10, \ 9, \ 8, \ 7, \ 6, \ 5, \ 4, \ 3, \ 2, \ 1) #define FE_0(ACTION, TOTAL, ARG) #define FE_1(ACTION, TOTAL, ARG, X) ACTION(ARG, (TOTAL) -0, X) #define FE_2(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -1, X) \ FE_1(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_3(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -2, X) \ FE_2(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_4(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -3, X) \ FE_3(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_5(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -4, X) \ FE_4(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_6(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -5, X) \ FE_5(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_7(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -6, X) \ FE_6(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_8(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -7, X) \ FE_7(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_9(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -8, X) \ FE_8(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_10(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -9, X) \ FE_9(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_11(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -10, X) \ FE_10(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_12(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -11, X) \ FE_11(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_13(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -12, X) \ FE_12(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_14(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -13, X) \ FE_13(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_15(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -14, X) \ FE_14(ACTION, TOTAL, ARG, __VA_ARGS__) #define FE_16(ACTION, TOTAL, ARG, X, ...) \ ACTION(ARG, (TOTAL) -15, X) \ FE_15(ACTION, TOTAL, ARG, __VA_ARGS__) /// Calls ACTION(ARG, INDEX, VA_ARG) for each VA_ARG in ... #define FOR_EACH(ACTION, ARG, ...) \ GET_MACRO(_0, \ __VA_ARGS__, \ FE_16, \ FE_15, \ FE_14, \ FE_13, \ FE_12, \ FE_11, \ FE_10, \ FE_9, \ FE_8, \ FE_7, \ FE_6, \ FE_5, \ FE_4, \ FE_3, \ FE_2, \ FE_1, \ FE_0) \ (ACTION, (NUMARGS(__VA_ARGS__) - 1), ARG, __VA_ARGS__) // // Macros to transform struct in tuple-like // #define TUPLE_ELEMENTS(class, index, field) \ template<> \ struct std::tuple_element { \ using type = decltype(class ::field); \ }; #define GET_IMPLEMENTATIONS(class, index, field) \ else if constexpr (I == index) return x.field; #define GET_TUPLE_FIELD_NAME(class, index, field) \ template<> \ const char *fieldName() { \ return #field; \ } #define INTROSPECTION_1(class, ...) \ template<> \ struct std::tuple_size \ : std::integral_constant {}; \ \ FOR_EACH(TUPLE_ELEMENTS, class, __VA_ARGS__) \ \ template<> \ struct TupleLikeTraits { \ static const char *name() { return #class; } \ \ template \ static const char *fieldName(); \ \ FOR_EACH(GET_TUPLE_FIELD_NAME, class, __VA_ARGS__) \ }; #define INTROSPECTION_2(class, ...) \ template \ auto &get(class &&x) { \ if constexpr (false) \ return NULL; \ FOR_EACH(GET_IMPLEMENTATIONS, class, __VA_ARGS__) \ } \ \ template \ const auto &get(const class &x) { \ if constexpr (false) \ return NULL; \ FOR_EACH(GET_IMPLEMENTATIONS, class, __VA_ARGS__) \ } \ \ template \ auto &get(class &x) { \ if constexpr (false) \ return NULL; \ FOR_EACH(GET_IMPLEMENTATIONS, class, __VA_ARGS__) \ } #define INTROSPECTION(class, ...) \ INTROSPECTION_1(class, __VA_ARGS__) \ INTROSPECTION_2(class, __VA_ARGS__) #define INTROSPECTION_NS(ns, class, ...) \ INTROSPECTION_1(ns::class, __VA_ARGS__) \ namespace ns { \ INTROSPECTION_2(class, __VA_ARGS__) \ }