#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include "llvm/ADT/StringRef.h" #include "revng/ADT/Concepts.h" #include "revng/Support/Assert.h" #include "revng/Support/YAMLTraits.h" #include "revng/TupleTree/TupleTreePath.h" #include "revng/TupleTree/Visits.h" template class TupleTreeReference { public: using RootT = RootType; using RootVariant = std::variant; public: RootVariant Root = static_cast(nullptr); TupleTreePath Path; public: static TupleTreeReference fromPath(ConstOrNot auto *Root, const TupleTreePath &Path) { return TupleTreeReference{ .Root = RootVariant{ Root }, .Path = Path }; } static TupleTreeReference fromString(ConstOrNot auto *Root, llvm::StringRef Path) { std::optional OptionalPath = stringAsPath(Path); if (not OptionalPath.has_value()) return TupleTreeReference{}; return fromPath(Root, *OptionalPath); } bool operator==(const TupleTreeReference &Other) const { // The paths are the same even if they are referred to different roots return Path == Other.Path; } bool operator<(const TupleTreeReference &Other) const { // The paths are the same even if they are referred to different roots return Path < Other.Path; } public: std::string toString() const { return *pathAsString(Path); } const TupleTreePath &path() const { return Path; } private: bool hasNullRoot() const { const auto IsNullVisitor = [](const auto &Pointer) { return Pointer == nullptr; }; return std::visit(IsNullVisitor, Root); } bool canGet() const { return Root.index() != std::variant_npos and not hasNullRoot(); } public: const T *get() const { static_assert(TupleTreeCompatible); revng_assert(canGet()); if (Path.size() == 0) return nullptr; const auto GetByPathVisitor = [&Path = Path](const auto &RootPointer) { return getByPath(Path, *RootPointer); }; return std::visit(GetByPathVisitor, Root); } T *get() { static_assert(TupleTreeCompatible); revng_assert(canGet()); if (Path.size() == 0) return nullptr; if (std::holds_alternative(Root)) { return getByPath(Path, *std::get(Root)); } else if (std::holds_alternative(Root)) { revng_abort("Called get() with const root, use getConst!"); } else { revng_abort("Invalid root variant!"); } } const T *getConst() const { static_assert(TupleTreeCompatible); revng_assert(canGet()); if (Path.size() == 0) return nullptr; if (std::holds_alternative(Root)) { return getByPath(Path, *std::get(Root)); } else if (std::holds_alternative(Root)) { return getByPath(Path, *std::get(Root)); } else { revng_abort("Invalid root variant!"); } } bool isValid() const debug_function { return canGet() and not Path.empty() and get() != nullptr; } }; template concept IsTupleTreeReference = is_specialization_v; template struct llvm::yaml::ScalarTraits { static void output(const T &Obj, void *, llvm::raw_ostream &Out) { Out << Obj.toString(); } static llvm::StringRef input(llvm::StringRef Path, void *, T &Obj) { // We temporarily initialize Root to nullptr, a post-processing phase will // take care of fixup these Obj = T::fromString(static_cast(nullptr), Path); return {}; } static auto mustQuote(llvm::StringRef) { return llvm::yaml::QuotingType::Double; } }; /// \brief Specialization for the std::variant we have in TupleTreeReference template inline void writeToLog(Logger &This, const std::variant &Var, int Ignored) { if (Var.index() == std::variant_npos) writeToLog(This, llvm::StringRef("std::variant_npos"), Ignored); else if (std::holds_alternative(Var)) writeToLog(This, std::get(Var), Ignored); else if (std::holds_alternative(Var)) writeToLog(This, std::get(Var), Ignored); }