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-exception |
6 | // |
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 | |
16 | using namespace llvm; |
17 | using namespace msgpack; |
18 | |
19 | namespace { |
20 | |
21 | // Struct used to represent scalar node. (MapDocNode and ArrayDocNode already |
22 | // exist in MsgPackDocument.h.) |
23 | struct ScalarDocNode : DocNode { |
24 | ScalarDocNode(DocNode N) : DocNode(N) {} |
25 | |
26 | /// Get the YAML tag for this ScalarDocNode. This normally returns ""; it only |
27 | /// 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 | } // namespace |
33 | |
34 | /// Convert this DocNode to a string, assuming it is scalar. |
35 | std::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(Fmt: "%#llx" , Vals: (unsigned long long)UInt); |
53 | else |
54 | 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). If |
67 | /// it is a string, copy the string into the Document's strings list so we do |
68 | /// not rely on S having a lifetime beyond this call. Tag is "" or a YAML tag. |
69 | StringRef 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(V: uint64_t(0)); |
75 | StringRef Err = yaml::ScalarTraits<uint64_t>::input(S, nullptr, getUInt()); |
76 | if (Err != "" ) { |
77 | *this = getDocument()->getNode(V: 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(V: 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(V: 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 only |
108 | /// returns something else if the result of toString would be ambiguous, e.g. |
109 | /// a string that parses as a number or boolean. |
110 | StringRef 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 need |
114 | // a tag. |
115 | ScalarDocNode N = getDocument()->getNode(); |
116 | N.fromString(S: toString(), Tag: "" ); |
117 | if (N.getKind() == getKind()) |
118 | return "" ; |
119 | // Tolerate signedness of int changing, as tags do not differentiate between |
120 | // 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 | |
142 | namespace llvm { |
143 | namespace yaml { |
144 | |
145 | /// YAMLIO for DocNode |
146 | template <> 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 ScalarDocNode |
172 | template <> 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(S: 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(S: 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(S: ScalarStr); |
200 | default: |
201 | llvm_unreachable("unrecognized ScalarKind" ); |
202 | } |
203 | } |
204 | }; |
205 | |
206 | /// YAMLIO for MapDocNode |
207 | template <> struct CustomMappingTraits<MapDocNode> { |
208 | |
209 | static void inputOne(IO &IO, StringRef Key, MapDocNode &M) { |
210 | ScalarDocNode KeyObj = M.getDocument()->getNode(); |
211 | KeyObj.fromString(S: Key, Tag: "" ); |
212 | IO.mapRequired(Key: Key.str().c_str(), Val&: M.getMap()[KeyObj]); |
213 | } |
214 | |
215 | static void output(IO &IO, MapDocNode &M) { |
216 | for (auto I : M.getMap()) { |
217 | IO.mapRequired(Key: I.first.toString().c_str(), Val&: I.second); |
218 | } |
219 | } |
220 | }; |
221 | |
222 | /// YAMLIO for ArrayNode |
223 | template <> 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 yaml |
233 | } // namespace llvm |
234 | |
235 | /// Convert MsgPack Document to YAML text. |
236 | void 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. |
242 | bool msgpack::Document::fromYAML(StringRef S) { |
243 | clear(); |
244 | yaml::Input Yin(S); |
245 | Yin >> getRoot(); |
246 | return !Yin.error(); |
247 | } |
248 | |
249 | |