|  | //===-- 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<uint64_t>::input(S, nullptr, getUInt()); | 
|  | if (Err != "") { | 
|  | *this = getDocument()->getNode(int64_t(0)); | 
|  | Err = yaml::ScalarTraits<int64_t>::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<bool>::input(S, nullptr, getBool()); | 
|  | if (Err == "" || Tag != "") | 
|  | return Err; | 
|  | } | 
|  | if (Tag == "!float" || Tag == "") { | 
|  | *this = getDocument()->getNode(0.0); | 
|  | StringRef Err = yaml::ScalarTraits<double>::input(S, nullptr, getFloat()); | 
|  | if (Err == "" || Tag != "") | 
|  | return Err; | 
|  | } | 
|  | assert((Tag == "!str" || Tag == "") && "unsupported tag"); | 
|  | std::string V; | 
|  | StringRef Err = yaml::ScalarTraits<std::string>::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<DocNode> { | 
|  |  | 
|  | 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<ArrayDocNode *>(&N); | 
|  | } | 
|  |  | 
|  | static ScalarDocNode &getAsScalar(DocNode &N) { | 
|  | return *static_cast<ScalarDocNode *>(&N); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// YAMLIO for ScalarDocNode | 
|  | template <> struct TaggedScalarTraits<ScalarDocNode> { | 
|  |  | 
|  | 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<int64_t>::mustQuote(ScalarStr); | 
|  | case Type::UInt: | 
|  | return ScalarTraits<uint64_t>::mustQuote(ScalarStr); | 
|  | case Type::Nil: | 
|  | return ScalarTraits<StringRef>::mustQuote(ScalarStr); | 
|  | case Type::Boolean: | 
|  | return ScalarTraits<bool>::mustQuote(ScalarStr); | 
|  | case Type::Float: | 
|  | return ScalarTraits<double>::mustQuote(ScalarStr); | 
|  | case Type::Binary: | 
|  | case Type::String: | 
|  | return ScalarTraits<std::string>::mustQuote(ScalarStr); | 
|  | default: | 
|  | llvm_unreachable("unrecognized ScalarKind"); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// YAMLIO for MapDocNode | 
|  | template <> struct CustomMappingTraits<MapDocNode> { | 
|  |  | 
|  | 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<ArrayDocNode> { | 
|  |  | 
|  | 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(); | 
|  | } | 
|  |  |