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