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