249 lines · cpp
1//===-- MsgPackDocumentYAML.cpp - MsgPack Document YAML interface -------*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9/// This file implements YAMLIO on a msgpack::Document.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/BinaryFormat/MsgPackDocument.h"14#include "llvm/Support/YAMLTraits.h"15 16using namespace llvm;17using namespace msgpack;18 19namespace {20 21// Struct used to represent scalar node. (MapDocNode and ArrayDocNode already22// exist in MsgPackDocument.h.)23struct ScalarDocNode : DocNode {24 ScalarDocNode(DocNode N) : DocNode(N) {}25 26 /// Get the YAML tag for this ScalarDocNode. This normally returns ""; it only27 /// returns something else if the result of toString would be ambiguous, e.g.28 /// a string that parses as a number or boolean.29 StringRef getYAMLTag() const;30};31 32} // namespace33 34/// Convert this DocNode to a string, assuming it is scalar.35std::string DocNode::toString() const {36 std::string S;37 raw_string_ostream OS(S);38 switch (getKind()) {39 case msgpack::Type::String:40 OS << Raw;41 break;42 case msgpack::Type::Nil:43 break;44 case msgpack::Type::Boolean:45 OS << (Bool ? "true" : "false");46 break;47 case msgpack::Type::Int:48 OS << Int;49 break;50 case msgpack::Type::UInt:51 if (getDocument()->getHexMode())52 OS << format("%#llx", (unsigned long long)UInt);53 else54 OS << UInt;55 break;56 case msgpack::Type::Float:57 OS << Float;58 break;59 default:60 llvm_unreachable("not scalar");61 break;62 }63 return OS.str();64}65 66/// Convert the StringRef and use it to set this DocNode (assuming scalar). If67/// it is a string, copy the string into the Document's strings list so we do68/// not rely on S having a lifetime beyond this call. Tag is "" or a YAML tag.69StringRef DocNode::fromString(StringRef S, StringRef Tag) {70 if (Tag == "tag:yaml.org,2002:str")71 Tag = "";72 if (Tag == "!int" || Tag == "") {73 // Try unsigned int then signed int.74 *this = getDocument()->getNode(uint64_t(0));75 StringRef Err = yaml::ScalarTraits<uint64_t>::input(S, nullptr, getUInt());76 if (Err != "") {77 *this = getDocument()->getNode(int64_t(0));78 Err = yaml::ScalarTraits<int64_t>::input(S, nullptr, getInt());79 }80 if (Err == "" || Tag != "")81 return Err;82 }83 if (Tag == "!nil") {84 *this = getDocument()->getNode();85 return "";86 }87 if (Tag == "!bool" || Tag == "") {88 *this = getDocument()->getNode(false);89 StringRef Err = yaml::ScalarTraits<bool>::input(S, nullptr, getBool());90 if (Err == "" || Tag != "")91 return Err;92 }93 if (Tag == "!float" || Tag == "") {94 *this = getDocument()->getNode(0.0);95 StringRef Err = yaml::ScalarTraits<double>::input(S, nullptr, getFloat());96 if (Err == "" || Tag != "")97 return Err;98 }99 assert((Tag == "!str" || Tag == "") && "unsupported tag");100 std::string V;101 StringRef Err = yaml::ScalarTraits<std::string>::input(S, nullptr, V);102 if (Err == "")103 *this = getDocument()->getNode(V, /*Copy=*/true);104 return Err;105}106 107/// Get the YAML tag for this ScalarDocNode. This normally returns ""; it only108/// returns something else if the result of toString would be ambiguous, e.g.109/// a string that parses as a number or boolean.110StringRef ScalarDocNode::getYAMLTag() const {111 if (getKind() == msgpack::Type::Nil)112 return "!nil";113 // Try converting both ways and see if we get the same kind. If not, we need114 // a tag.115 ScalarDocNode N = getDocument()->getNode();116 N.fromString(toString(), "");117 if (N.getKind() == getKind())118 return "";119 // Tolerate signedness of int changing, as tags do not differentiate between120 // them anyway.121 if (N.getKind() == msgpack::Type::UInt && getKind() == msgpack::Type::Int)122 return "";123 if (N.getKind() == msgpack::Type::Int && getKind() == msgpack::Type::UInt)124 return "";125 // We do need a tag.126 switch (getKind()) {127 case msgpack::Type::String:128 return "!str";129 case msgpack::Type::Int:130 return "!int";131 case msgpack::Type::UInt:132 return "!int";133 case msgpack::Type::Boolean:134 return "!bool";135 case msgpack::Type::Float:136 return "!float";137 default:138 llvm_unreachable("unrecognized kind");139 }140}141 142namespace llvm {143namespace yaml {144 145/// YAMLIO for DocNode146template <> struct PolymorphicTraits<DocNode> {147 148 static NodeKind getKind(const DocNode &N) {149 switch (N.getKind()) {150 case msgpack::Type::Map:151 return NodeKind::Map;152 case msgpack::Type::Array:153 return NodeKind::Sequence;154 default:155 return NodeKind::Scalar;156 }157 }158 159 static MapDocNode &getAsMap(DocNode &N) { return N.getMap(/*Convert=*/true); }160 161 static ArrayDocNode &getAsSequence(DocNode &N) {162 N.getArray(/*Convert=*/true);163 return *static_cast<ArrayDocNode *>(&N);164 }165 166 static ScalarDocNode &getAsScalar(DocNode &N) {167 return *static_cast<ScalarDocNode *>(&N);168 }169};170 171/// YAMLIO for ScalarDocNode172template <> struct TaggedScalarTraits<ScalarDocNode> {173 174 static void output(const ScalarDocNode &S, void *Ctxt, raw_ostream &OS,175 raw_ostream &TagOS) {176 TagOS << S.getYAMLTag();177 OS << S.toString();178 }179 180 static StringRef input(StringRef Str, StringRef Tag, void *Ctxt,181 ScalarDocNode &S) {182 return S.fromString(Str, Tag);183 }184 185 static QuotingType mustQuote(const ScalarDocNode &S, StringRef ScalarStr) {186 switch (S.getKind()) {187 case Type::Int:188 return ScalarTraits<int64_t>::mustQuote(ScalarStr);189 case Type::UInt:190 return ScalarTraits<uint64_t>::mustQuote(ScalarStr);191 case Type::Nil:192 return ScalarTraits<StringRef>::mustQuote(ScalarStr);193 case Type::Boolean:194 return ScalarTraits<bool>::mustQuote(ScalarStr);195 case Type::Float:196 return ScalarTraits<double>::mustQuote(ScalarStr);197 case Type::Binary:198 case Type::String:199 return ScalarTraits<std::string>::mustQuote(ScalarStr);200 default:201 llvm_unreachable("unrecognized ScalarKind");202 }203 }204};205 206/// YAMLIO for MapDocNode207template <> struct CustomMappingTraits<MapDocNode> {208 209 static void inputOne(IO &IO, StringRef Key, MapDocNode &M) {210 ScalarDocNode KeyObj = M.getDocument()->getNode();211 KeyObj.fromString(Key, "");212 IO.mapRequired(Key, M.getMap()[KeyObj]);213 }214 215 static void output(IO &IO, MapDocNode &M) {216 for (auto I : M.getMap()) {217 IO.mapRequired(I.first.toString(), I.second);218 }219 }220};221 222/// YAMLIO for ArrayNode223template <> struct SequenceTraits<ArrayDocNode> {224 225 static size_t size(IO &IO, ArrayDocNode &A) { return A.size(); }226 227 static DocNode &element(IO &IO, ArrayDocNode &A, size_t Index) {228 return A[Index];229 }230};231 232} // namespace yaml233} // namespace llvm234 235/// Convert MsgPack Document to YAML text.236void msgpack::Document::toYAML(raw_ostream &OS) {237 yaml::Output Yout(OS);238 Yout << getRoot();239}240 241/// Read YAML text into the MsgPack document. Returns false on failure.242bool msgpack::Document::fromYAML(StringRef S) {243 clear();244 yaml::Input Yin(S);245 Yin >> getRoot();246 return !Yin.error();247}248 249