#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include "revng/ADT/KeyedObjectContainer.h" #include "revng/ADT/UpcastablePointer.h" #include "revng/TupleTree/TupleLikeTraits.h" #include "revng/TupleTree/TupleTreeCompatible.h" #include "revng/TupleTree/TupleTreePath.h" // // visitTupleTree implementation // namespace tupletree::detail { template void visitTuple(Visitor &V, T &Obj) { if constexpr (I < std::tuple_size_v) { // Visit the field visitTupleTree(V, get(Obj)); // Visit next element in tuple visitTuple(V, Obj); } } } // namespace tupletree::detail // UpcastablePointerLike-like template void visitTupleTree(Visitor &V, T &Obj) { upcast(Obj, [&V](auto &Upcasted) { visitTupleTree(V, Upcasted); }); } // Tuple-like template void visitTupleTree(Visitor &V, T &Obj) { V.PreVisit(Obj); tupletree::detail::visitTuple(V, Obj); V.PostVisit(Obj); } // Container-like template void visitTupleTree(Visitor &V, T &Obj) { V.PreVisit(Obj); using value_type = typename T::value_type; for (value_type &Element : Obj) { visitTupleTree(V, Element); } V.PostVisit(Obj); } // All the others template void visitTupleTree(Visitor &V, T &Element) { V.PreVisit(Element); V.PostVisit(Element); } template void visitTupleTree(T &Element, const Pre &PreVisitor, const Post &PostVisitor) { struct { const Pre &PreVisit; const Post &PostVisit; } Visitor{ PreVisitor, PostVisitor }; visitTupleTree(Visitor, Element); } // // tupleIndexByName // template size_t tupleIndexByName(llvm::StringRef Name) { if constexpr (I < std::tuple_size_v) { llvm::StringRef ThisName = TupleLikeTraits::FieldsName[I]; if (Name == ThisName) return I; else return tupleIndexByName(Name); } else { return -1; } } // // getByKey // namespace tupletree::detail { template ResultT *getByKeyTuple(RootT &M, KeyT Key) { if constexpr (I < std::tuple_size_v) { 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); } } else { return nullptr; } } } // namespace tupletree::detail template ResultT getByKey(RootT &M, KeyT Key) { auto Dispatcher = [&](auto &Upcasted) { return getByKey(Upcasted, Key); }; return upcast(M, Dispatcher, ResultT{}); } template ResultT getByKey(RootT &M, KeyT Key) { return tupletree::detail::getByKeyTuple(M, Key); } template ResultT *getByKey(RootT &M, KeyT Key) { for (auto &Element : M) { using KOT = KeyedObjectTraits>; if (KOT::key(Element) == Key) return ∈ } return nullptr; } // // callOnPathSteps (no instance) // template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path); template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path) { return false; } template bool callOnPathStepsImpl(Visitor &V, llvm::ArrayRef Path) { return callOnPathSteps(V, Path.slice(1)); } template bool callOnPathStepsImpl(Visitor &V, llvm::ArrayRef Path) { auto Dispatcher = [&](auto &Upcasted) { return callOnPathStepsImpl>(V, Path); }; using KOT = KeyedObjectTraits; using key_type = decltype(KOT::key(std::declval())); auto TargetKey = Path[0].get(); // TODO: in case of nullptr we should abort auto Temporary = KeyedObjectTraits::fromKey(TargetKey); return upcast(Temporary, Dispatcher, false); } template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path) { using value_type = typename RootT::value_type; using KOT = KeyedObjectTraits; using key_type = decltype(KOT::key(std::declval())); auto TargetKey = Path[0].get(); V.template visitContainerElement(TargetKey); if (Path.size() > 1) { return callOnPathStepsImpl(V, Path); } return true; } namespace tupletree::detail { template bool callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path) { if constexpr (I < std::tuple_size_v) { if (Path.size() == 0) return true; if (Path[0].get() == I) { using next_type = typename std::tuple_element::type; V.template visitTupleElement(); if (Path.size() > 1) { return callOnPathStepsImpl(V, Path); } } else { return callOnPathStepsTuple(V, Path); } } return true; } } // namespace tupletree::detail template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path) { return tupletree::detail::callOnPathStepsTuple(V, Path); } // // callOnPathSteps (with instance) // namespace tupletree::detail { template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path, RootT &M) { return false; } template bool callOnPathStepsTuple(Visitor &V, llvm::ArrayRef Path, RootT &M) { if constexpr (I < std::tuple_size_v) { if (Path[0].get() == 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 bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path, RootT &M) { auto Dispatcher = [&](auto &Upcasted) { return callOnPathStepsTuple(V, Path, Upcasted); }; // TODO: in case of nullptr we should abort return upcast(M, Dispatcher, false); } template bool callOnPathSteps(Visitor &V, llvm::ArrayRef Path, RootT &M) { return tupletree::detail::callOnPathStepsTuple(V, Path, M); } template bool 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())); auto TargetKey = Path[0].get(); auto It = M.find(TargetKey); if (It == M.end()) return false; auto *Matching = &*It; V.template visitContainerElement(TargetKey, *Matching); if (Path.size() > 1) { return callOnPathSteps(V, Path.slice(1), *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) { PathSize -= 1; if (PathSize == 0) V.template visitContainerElement(Key); } }; } // namespace tupletree::detail template bool callByPath(Visitor &V, const TupleTreePath &Path) { using namespace tupletree::detail; CallByPathVisitor CBPV{ Path.size(), V }; return callOnPathSteps(CBPV, Path.toArrayRef()); } // // 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) { PathSize -= 1; if (PathSize == 0) V.template visitContainerElement(Key, *Element.get()); } template void visitContainerElement(KeyT Key, K &Element) { PathSize -= 1; if (PathSize == 0) V.template visitContainerElement(Key, Element); } }; } // namespace tupletree::detail template bool callByPath(Visitor &V, const TupleTreePath &Path, RootT &M) { using namespace tupletree::detail; CallByPathVisitorWithInstance CBPV{ Path.size(), V }; return callOnPathSteps(CBPV, Path.toArrayRef(), 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 TupleTreePath &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::FieldsName[I]; } template void visitContainerElement(KeyT Key) { Stream << "/" << getNameFromYAMLScalar(Key); } }; } // namespace tupletree::detail template std::optional pathAsString(const TupleTreePath &Path) { std::string Result; { tupletree::detail::DumpPathVisitor PV(Result); if (not callOnPathSteps(PV, Path.toArrayRef())) return {}; } return Result; } class PathMatcher { private: TupleTreePath 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 TupleTreePath &path() const { return Path; } public: template TupleTreePath apply(Ts... Args) const { revng_assert(sizeof...(Args) == Free.size()); TupleTreePath Result = Path; applyImpl<0, Ts...>(Result, Args...); return Result; } template std::optional> match(const TupleTreePath &Search) { revng_assert(sizeof...(Args) == Free.size()); if (Path.size() != Search.size()) return {}; // // Check non-variable parts match // std::vector Terminator{ Path.size() }; size_t LastEnd = 0; for (auto Index : llvm::concat(Free, Terminator)) { for (size_t I = LastEnd; I < Index; ++I) { if (Search[I] != Path[I]) return {}; } LastEnd = Index + 1; } // // Compute result // std::tuple Result; extractKeys(Search, Result); return Result; } private: template void depositKey(TupleTreePath &Result, T Arg) const { auto Index = Free.at(I); Result[Index] = ConcreteTupleTreeKeyWrapper(Arg); } template void applyImpl(TupleTreePath &Result, T Arg) const { depositKey(Result, Arg); } template void applyImpl(TupleTreePath &Result, T Arg, Ts... Args) const { depositKey(Result, Arg); applyImpl(Result, Args...); } template void extractKeys(const TupleTreePath &Search, T &Tuple) const { if constexpr (I < std::tuple_size_v) { using element = std::tuple_element_t; std::get(Tuple) = Search[Free[I]].get(); extractKeys(Search, Tuple); } } private: template static bool visitTuple(llvm::StringRef Current, llvm::StringRef Rest, PathMatcher &Result); template static bool visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result); template static bool visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result); template static bool visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result); template static bool visitTupleTreeNode(llvm::StringRef Path, PathMatcher &Result); }; template bool PathMatcher::visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result) { using element_type = std::remove_reference_t())>; return PathMatcher::visitTupleTreeNode(String, Result); } template bool PathMatcher::visitTupleTreeNode(llvm::StringRef String, PathMatcher &Result) { if (String.size() == 0) return true; auto [Before, After] = String.split('/'); return visitTuple(Before, After, Result); } template bool 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 == "*") { Result.Free.push_back(Result.Path.size()); Result.Path.emplace_back(); } else { Result.Path.push_back(getValueFromYAMLScalar(Before)); } return visitTupleTreeNode(After, Result); } template bool 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::FieldsName[I] == Current) { Result.Path.push_back(size_t(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) { if (Path.empty()) return std::nullopt; auto Result = PathMatcher::create(Path); if (Result) return Result->path(); else return std::nullopt; } template ResultT *getByPath(llvm::StringRef Path, RootT &M) { auto MaybeKeyVector = stringAsPath(Path); if (not MaybeKeyVector) return {}; else return getByPath(*MaybeKeyVector, M); } // // validateTupleTree // template constexpr bool validateTupleTree(L); template constexpr bool validateTupleTree(L); template constexpr bool validateTupleTree(L); template constexpr bool validateTupleTree(L); template constexpr bool validateTupleTree(L Check) { return Check((T *) nullptr) and validateTupleTree(Check); } template constexpr bool validateTupleTree(L Check) { return Check((T *) nullptr) and validateTupleTree(Check); } template constexpr bool validateTupleTree(L Check) { return Check((std::remove_const_t *) nullptr); } template constexpr bool validateTupleTree(L Check) { if constexpr (I == 0 and not Check((T *) nullptr)) return false; if constexpr (I < std::tuple_size_v) { if constexpr (not validateTupleTree>(Check)) return false; return validateTupleTree(Check); } return true; }