//===-- MsgPackDocumentYAML.cpp - MsgPack Document YAML interface -------*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // /// This file implements YAMLIO on a msgpack::Document. // //===----------------------------------------------------------------------===// #include "llvm/BinaryFormat/MsgPackDocument.h" #include "llvm/Support/YAMLTraits.h" using namespace llvm; using namespace msgpack; namespace { // Struct used to represent scalar node. (MapDocNode and ArrayDocNode already // exist in MsgPackDocument.h.) struct ScalarDocNode : DocNode { ScalarDocNode(DocNode N) : DocNode(N) {} /// Get the YAML tag for this ScalarDocNode. This normally returns ""; it only /// returns something else if the result of toString would be ambiguous, e.g. /// a string that parses as a number or boolean. StringRef getYAMLTag() const; }; } // namespace /// Convert this DocNode to a string, assuming it is scalar. std::string DocNode::toString() const { std::string S; raw_string_ostream OS(S); switch (getKind()) { case msgpack::Type::String: OS << Raw; break; case msgpack::Type::Nil: break; case msgpack::Type::Boolean: OS << (Bool ? "true" : "false"); break; case msgpack::Type::Int: OS << Int; break; case msgpack::Type::UInt: if (getDocument()->getHexMode()) OS << format("%#llx", (unsigned long long)UInt); else OS << UInt; break; case msgpack::Type::Float: OS << Float; break; default: llvm_unreachable("not scalar"); break; } return OS.str(); } /// Convert the StringRef and use it to set this DocNode (assuming scalar). If /// it is a string, copy the string into the Document's strings list so we do /// not rely on S having a lifetime beyond this call. Tag is "" or a YAML tag. StringRef DocNode::fromString(StringRef S, StringRef Tag) { if (Tag == "tag:yaml.org,2002:str") Tag = ""; if (Tag == "!int" || Tag == "") { // Try unsigned int then signed int. *this = getDocument()->getNode(uint64_t(0)); StringRef Err = yaml::ScalarTraits::input(S, nullptr, getUInt()); if (Err != "") { *this = getDocument()->getNode(int64_t(0)); Err = yaml::ScalarTraits::input(S, nullptr, getInt()); } if (Err == "" || Tag != "") return Err; } if (Tag == "!nil") { *this = getDocument()->getNode(); return ""; } if (Tag == "!bool" || Tag == "") { *this = getDocument()->getNode(false); StringRef Err = yaml::ScalarTraits::input(S, nullptr, getBool()); if (Err == "" || Tag != "") return Err; } if (Tag == "!float" || Tag == "") { *this = getDocument()->getNode(0.0); StringRef Err = yaml::ScalarTraits::input(S, nullptr, getFloat()); if (Err == "" || Tag != "") return Err; } assert((Tag == "!str" || Tag == "") && "unsupported tag"); std::string V; StringRef Err = yaml::ScalarTraits::input(S, nullptr, V); if (Err == "") *this = getDocument()->getNode(V, /*Copy=*/true); return Err; } /// Get the YAML tag for this ScalarDocNode. This normally returns ""; it only /// returns something else if the result of toString would be ambiguous, e.g. /// a string that parses as a number or boolean. StringRef ScalarDocNode::getYAMLTag() const { if (getKind() == msgpack::Type::Nil) return "!nil"; // Try converting both ways and see if we get the same kind. If not, we need // a tag. ScalarDocNode N = getDocument()->getNode(); N.fromString(toString(), ""); if (N.getKind() == getKind()) return ""; // Tolerate signedness of int changing, as tags do not differentiate between // them anyway. if (N.getKind() == msgpack::Type::UInt && getKind() == msgpack::Type::Int) return ""; if (N.getKind() == msgpack::Type::Int && getKind() == msgpack::Type::UInt) return ""; // We do need a tag. switch (getKind()) { case msgpack::Type::String: return "!str"; case msgpack::Type::Int: return "!int"; case msgpack::Type::UInt: return "!int"; case msgpack::Type::Boolean: return "!bool"; case msgpack::Type::Float: return "!float"; default: llvm_unreachable("unrecognized kind"); } } namespace llvm { namespace yaml { /// YAMLIO for DocNode template <> struct PolymorphicTraits { static NodeKind getKind(const DocNode &N) { switch (N.getKind()) { case msgpack::Type::Map: return NodeKind::Map; case msgpack::Type::Array: return NodeKind::Sequence; default: return NodeKind::Scalar; } } static MapDocNode &getAsMap(DocNode &N) { return N.getMap(/*Convert=*/true); } static ArrayDocNode &getAsSequence(DocNode &N) { N.getArray(/*Convert=*/true); return *static_cast(&N); } static ScalarDocNode &getAsScalar(DocNode &N) { return *static_cast(&N); } }; /// YAMLIO for ScalarDocNode template <> struct TaggedScalarTraits { static void output(const ScalarDocNode &S, void *Ctxt, raw_ostream &OS, raw_ostream &TagOS) { TagOS << S.getYAMLTag(); OS << S.toString(); } static StringRef input(StringRef Str, StringRef Tag, void *Ctxt, ScalarDocNode &S) { return S.fromString(Str, Tag); } static QuotingType mustQuote(const ScalarDocNode &S, StringRef ScalarStr) { switch (S.getKind()) { case Type::Int: return ScalarTraits::mustQuote(ScalarStr); case Type::UInt: return ScalarTraits::mustQuote(ScalarStr); case Type::Nil: return ScalarTraits::mustQuote(ScalarStr); case Type::Boolean: return ScalarTraits::mustQuote(ScalarStr); case Type::Float: return ScalarTraits::mustQuote(ScalarStr); case Type::Binary: case Type::String: return ScalarTraits::mustQuote(ScalarStr); default: llvm_unreachable("unrecognized ScalarKind"); } } }; /// YAMLIO for MapDocNode template <> struct CustomMappingTraits { static void inputOne(IO &IO, StringRef Key, MapDocNode &M) { ScalarDocNode KeyObj = M.getDocument()->getNode(); KeyObj.fromString(Key, ""); IO.mapRequired(Key.str().c_str(), M.getMap()[KeyObj]); } static void output(IO &IO, MapDocNode &M) { for (auto I : M.getMap()) { IO.mapRequired(I.first.toString().c_str(), I.second); } } }; /// YAMLIO for ArrayNode template <> struct SequenceTraits { static size_t size(IO &IO, ArrayDocNode &A) { return A.size(); } static DocNode &element(IO &IO, ArrayDocNode &A, size_t Index) { return A[Index]; } }; } // namespace yaml } // namespace llvm /// Convert MsgPack Document to YAML text. void msgpack::Document::toYAML(raw_ostream &OS) { yaml::Output Yout(OS); Yout << getRoot(); } /// Read YAML text into the MsgPack document. Returns false on failure. bool msgpack::Document::fromYAML(StringRef S) { clear(); yaml::Input Yin(S); Yin >> getRoot(); return !Yin.error(); }