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 {
ScalarDocNode__anon0b9199350111::ScalarDocNode25 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.
toString() const36 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.
fromString(StringRef S,StringRef 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.
getYAMLTag() const111 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
getKindllvm::yaml::PolymorphicTraits149 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
getAsMapllvm::yaml::PolymorphicTraits160 static MapDocNode &getAsMap(DocNode &N) { return N.getMap(/*Convert=*/true); }
161
getAsSequencellvm::yaml::PolymorphicTraits162 static ArrayDocNode &getAsSequence(DocNode &N) {
163 N.getArray(/*Convert=*/true);
164 return *static_cast<ArrayDocNode *>(&N);
165 }
166
getAsScalarllvm::yaml::PolymorphicTraits167 static ScalarDocNode &getAsScalar(DocNode &N) {
168 return *static_cast<ScalarDocNode *>(&N);
169 }
170 };
171
172 /// YAMLIO for ScalarDocNode
173 template <> struct TaggedScalarTraits<ScalarDocNode> {
174
outputllvm::yaml::TaggedScalarTraits175 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
inputllvm::yaml::TaggedScalarTraits181 static StringRef input(StringRef Str, StringRef Tag, void *Ctxt,
182 ScalarDocNode &S) {
183 return S.fromString(Str, Tag);
184 }
185
mustQuotellvm::yaml::TaggedScalarTraits186 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
inputOnellvm::yaml::CustomMappingTraits210 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
outputllvm::yaml::CustomMappingTraits216 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
sizellvm::yaml::SequenceTraits226 static size_t size(IO &IO, ArrayDocNode &A) { return A.size(); }
227
elementllvm::yaml::SequenceTraits228 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.
toYAML(raw_ostream & OS)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.
fromYAML(StringRef S)243 bool msgpack::Document::fromYAML(StringRef S) {
244 clear();
245 yaml::Input Yin(S);
246 Yin >> getRoot();
247 return !Yin.error();
248 }
249
250