1 | //===- llvm/Support/YAMLTraits.h --------------------------------*- C++ -*-===// |
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 | #ifndef LLVM_SUPPORT_YAMLTRAITS_H |
10 | #define LLVM_SUPPORT_YAMLTRAITS_H |
11 | |
12 | #include "llvm/ADT/ArrayRef.h" |
13 | #include "llvm/ADT/BitVector.h" |
14 | #include "llvm/ADT/SmallVector.h" |
15 | #include "llvm/ADT/StringExtras.h" |
16 | #include "llvm/ADT/StringMap.h" |
17 | #include "llvm/ADT/StringRef.h" |
18 | #include "llvm/ADT/Twine.h" |
19 | #include "llvm/Support/AlignOf.h" |
20 | #include "llvm/Support/Allocator.h" |
21 | #include "llvm/Support/Endian.h" |
22 | #include "llvm/Support/SMLoc.h" |
23 | #include "llvm/Support/SourceMgr.h" |
24 | #include "llvm/Support/YAMLParser.h" |
25 | #include "llvm/Support/raw_ostream.h" |
26 | #include <cassert> |
27 | #include <map> |
28 | #include <memory> |
29 | #include <new> |
30 | #include <optional> |
31 | #include <string> |
32 | #include <system_error> |
33 | #include <type_traits> |
34 | #include <vector> |
35 | |
36 | namespace llvm { |
37 | |
38 | class VersionTuple; |
39 | |
40 | namespace yaml { |
41 | |
42 | enum class NodeKind : uint8_t { |
43 | Scalar, |
44 | Map, |
45 | Sequence, |
46 | }; |
47 | |
48 | struct EmptyContext {}; |
49 | |
50 | /// This class should be specialized by any type that needs to be converted |
51 | /// to/from a YAML mapping. For example: |
52 | /// |
53 | /// struct MappingTraits<MyStruct> { |
54 | /// static void mapping(IO &io, MyStruct &s) { |
55 | /// io.mapRequired("name", s.name); |
56 | /// io.mapRequired("size", s.size); |
57 | /// io.mapOptional("age", s.age); |
58 | /// } |
59 | /// }; |
60 | template<class T> |
61 | struct MappingTraits { |
62 | // Must provide: |
63 | // static void mapping(IO &io, T &fields); |
64 | // Optionally may provide: |
65 | // static std::string validate(IO &io, T &fields); |
66 | // static void enumInput(IO &io, T &value); |
67 | // |
68 | // The optional flow flag will cause generated YAML to use a flow mapping |
69 | // (e.g. { a: 0, b: 1 }): |
70 | // static const bool flow = true; |
71 | }; |
72 | |
73 | /// This class is similar to MappingTraits<T> but allows you to pass in |
74 | /// additional context for each map operation. For example: |
75 | /// |
76 | /// struct MappingContextTraits<MyStruct, MyContext> { |
77 | /// static void mapping(IO &io, MyStruct &s, MyContext &c) { |
78 | /// io.mapRequired("name", s.name); |
79 | /// io.mapRequired("size", s.size); |
80 | /// io.mapOptional("age", s.age); |
81 | /// ++c.TimesMapped; |
82 | /// } |
83 | /// }; |
84 | template <class T, class Context> struct MappingContextTraits { |
85 | // Must provide: |
86 | // static void mapping(IO &io, T &fields, Context &Ctx); |
87 | // Optionally may provide: |
88 | // static std::string validate(IO &io, T &fields, Context &Ctx); |
89 | // |
90 | // The optional flow flag will cause generated YAML to use a flow mapping |
91 | // (e.g. { a: 0, b: 1 }): |
92 | // static const bool flow = true; |
93 | }; |
94 | |
95 | /// This class should be specialized by any integral type that converts |
96 | /// to/from a YAML scalar where there is a one-to-one mapping between |
97 | /// in-memory values and a string in YAML. For example: |
98 | /// |
99 | /// struct ScalarEnumerationTraits<Colors> { |
100 | /// static void enumeration(IO &io, Colors &value) { |
101 | /// io.enumCase(value, "red", cRed); |
102 | /// io.enumCase(value, "blue", cBlue); |
103 | /// io.enumCase(value, "green", cGreen); |
104 | /// } |
105 | /// }; |
106 | template <typename T, typename Enable = void> struct ScalarEnumerationTraits { |
107 | // Must provide: |
108 | // static void enumeration(IO &io, T &value); |
109 | }; |
110 | |
111 | /// This class should be specialized by any integer type that is a union |
112 | /// of bit values and the YAML representation is a flow sequence of |
113 | /// strings. For example: |
114 | /// |
115 | /// struct ScalarBitSetTraits<MyFlags> { |
116 | /// static void bitset(IO &io, MyFlags &value) { |
117 | /// io.bitSetCase(value, "big", flagBig); |
118 | /// io.bitSetCase(value, "flat", flagFlat); |
119 | /// io.bitSetCase(value, "round", flagRound); |
120 | /// } |
121 | /// }; |
122 | template <typename T, typename Enable = void> struct ScalarBitSetTraits { |
123 | // Must provide: |
124 | // static void bitset(IO &io, T &value); |
125 | }; |
126 | |
127 | /// Describe which type of quotes should be used when quoting is necessary. |
128 | /// Some non-printable characters need to be double-quoted, while some others |
129 | /// are fine with simple-quoting, and some don't need any quoting. |
130 | enum class QuotingType { None, Single, Double }; |
131 | |
132 | /// This class should be specialized by type that requires custom conversion |
133 | /// to/from a yaml scalar. For example: |
134 | /// |
135 | /// template<> |
136 | /// struct ScalarTraits<MyType> { |
137 | /// static void output(const MyType &val, void*, llvm::raw_ostream &out) { |
138 | /// // stream out custom formatting |
139 | /// out << llvm::format("%x", val); |
140 | /// } |
141 | /// static StringRef input(StringRef scalar, void*, MyType &value) { |
142 | /// // parse scalar and set `value` |
143 | /// // return empty string on success, or error string |
144 | /// return StringRef(); |
145 | /// } |
146 | /// static QuotingType mustQuote(StringRef) { return QuotingType::Single; } |
147 | /// }; |
148 | template <typename T, typename Enable = void> struct ScalarTraits { |
149 | // Must provide: |
150 | // |
151 | // Function to write the value as a string: |
152 | // static void output(const T &value, void *ctxt, llvm::raw_ostream &out); |
153 | // |
154 | // Function to convert a string to a value. Returns the empty |
155 | // StringRef on success or an error string if string is malformed: |
156 | // static StringRef input(StringRef scalar, void *ctxt, T &value); |
157 | // |
158 | // Function to determine if the value should be quoted. |
159 | // static QuotingType mustQuote(StringRef); |
160 | }; |
161 | |
162 | /// This class should be specialized by type that requires custom conversion |
163 | /// to/from a YAML literal block scalar. For example: |
164 | /// |
165 | /// template <> |
166 | /// struct BlockScalarTraits<MyType> { |
167 | /// static void output(const MyType &Value, void*, llvm::raw_ostream &Out) |
168 | /// { |
169 | /// // stream out custom formatting |
170 | /// Out << Value; |
171 | /// } |
172 | /// static StringRef input(StringRef Scalar, void*, MyType &Value) { |
173 | /// // parse scalar and set `value` |
174 | /// // return empty string on success, or error string |
175 | /// return StringRef(); |
176 | /// } |
177 | /// }; |
178 | template <typename T> |
179 | struct BlockScalarTraits { |
180 | // Must provide: |
181 | // |
182 | // Function to write the value as a string: |
183 | // static void output(const T &Value, void *ctx, llvm::raw_ostream &Out); |
184 | // |
185 | // Function to convert a string to a value. Returns the empty |
186 | // StringRef on success or an error string if string is malformed: |
187 | // static StringRef input(StringRef Scalar, void *ctxt, T &Value); |
188 | // |
189 | // Optional: |
190 | // static StringRef inputTag(T &Val, std::string Tag) |
191 | // static void outputTag(const T &Val, raw_ostream &Out) |
192 | }; |
193 | |
194 | /// This class should be specialized by type that requires custom conversion |
195 | /// to/from a YAML scalar with optional tags. For example: |
196 | /// |
197 | /// template <> |
198 | /// struct TaggedScalarTraits<MyType> { |
199 | /// static void output(const MyType &Value, void*, llvm::raw_ostream |
200 | /// &ScalarOut, llvm::raw_ostream &TagOut) |
201 | /// { |
202 | /// // stream out custom formatting including optional Tag |
203 | /// Out << Value; |
204 | /// } |
205 | /// static StringRef input(StringRef Scalar, StringRef Tag, void*, MyType |
206 | /// &Value) { |
207 | /// // parse scalar and set `value` |
208 | /// // return empty string on success, or error string |
209 | /// return StringRef(); |
210 | /// } |
211 | /// static QuotingType mustQuote(const MyType &Value, StringRef) { |
212 | /// return QuotingType::Single; |
213 | /// } |
214 | /// }; |
215 | template <typename T> struct TaggedScalarTraits { |
216 | // Must provide: |
217 | // |
218 | // Function to write the value and tag as strings: |
219 | // static void output(const T &Value, void *ctx, llvm::raw_ostream &ScalarOut, |
220 | // llvm::raw_ostream &TagOut); |
221 | // |
222 | // Function to convert a string to a value. Returns the empty |
223 | // StringRef on success or an error string if string is malformed: |
224 | // static StringRef input(StringRef Scalar, StringRef Tag, void *ctxt, T |
225 | // &Value); |
226 | // |
227 | // Function to determine if the value should be quoted. |
228 | // static QuotingType mustQuote(const T &Value, StringRef Scalar); |
229 | }; |
230 | |
231 | /// This class should be specialized by any type that needs to be converted |
232 | /// to/from a YAML sequence. For example: |
233 | /// |
234 | /// template<> |
235 | /// struct SequenceTraits<MyContainer> { |
236 | /// static size_t size(IO &io, MyContainer &seq) { |
237 | /// return seq.size(); |
238 | /// } |
239 | /// static MyType& element(IO &, MyContainer &seq, size_t index) { |
240 | /// if ( index >= seq.size() ) |
241 | /// seq.resize(index+1); |
242 | /// return seq[index]; |
243 | /// } |
244 | /// }; |
245 | template<typename T, typename EnableIf = void> |
246 | struct SequenceTraits { |
247 | // Must provide: |
248 | // static size_t size(IO &io, T &seq); |
249 | // static T::value_type& element(IO &io, T &seq, size_t index); |
250 | // |
251 | // The following is option and will cause generated YAML to use |
252 | // a flow sequence (e.g. [a,b,c]). |
253 | // static const bool flow = true; |
254 | }; |
255 | |
256 | /// This class should be specialized by any type for which vectors of that |
257 | /// type need to be converted to/from a YAML sequence. |
258 | template<typename T, typename EnableIf = void> |
259 | struct SequenceElementTraits { |
260 | // Must provide: |
261 | // static const bool flow; |
262 | }; |
263 | |
264 | /// This class should be specialized by any type that needs to be converted |
265 | /// to/from a list of YAML documents. |
266 | template<typename T> |
267 | struct DocumentListTraits { |
268 | // Must provide: |
269 | // static size_t size(IO &io, T &seq); |
270 | // static T::value_type& element(IO &io, T &seq, size_t index); |
271 | }; |
272 | |
273 | /// This class should be specialized by any type that needs to be converted |
274 | /// to/from a YAML mapping in the case where the names of the keys are not known |
275 | /// in advance, e.g. a string map. |
276 | template <typename T> |
277 | struct CustomMappingTraits { |
278 | // static void inputOne(IO &io, StringRef key, T &elem); |
279 | // static void output(IO &io, T &elem); |
280 | }; |
281 | |
282 | /// This class should be specialized by any type that can be represented as |
283 | /// a scalar, map, or sequence, decided dynamically. For example: |
284 | /// |
285 | /// typedef std::unique_ptr<MyBase> MyPoly; |
286 | /// |
287 | /// template<> |
288 | /// struct PolymorphicTraits<MyPoly> { |
289 | /// static NodeKind getKind(const MyPoly &poly) { |
290 | /// return poly->getKind(); |
291 | /// } |
292 | /// static MyScalar& getAsScalar(MyPoly &poly) { |
293 | /// if (!poly || !isa<MyScalar>(poly)) |
294 | /// poly.reset(new MyScalar()); |
295 | /// return *cast<MyScalar>(poly.get()); |
296 | /// } |
297 | /// // ... |
298 | /// }; |
299 | template <typename T> struct PolymorphicTraits { |
300 | // Must provide: |
301 | // static NodeKind getKind(const T &poly); |
302 | // static scalar_type &getAsScalar(T &poly); |
303 | // static map_type &getAsMap(T &poly); |
304 | // static sequence_type &getAsSequence(T &poly); |
305 | }; |
306 | |
307 | // Only used for better diagnostics of missing traits |
308 | template <typename T> |
309 | struct MissingTrait; |
310 | |
311 | // Test if ScalarEnumerationTraits<T> is defined on type T. |
312 | template <class T> |
313 | struct has_ScalarEnumerationTraits |
314 | { |
315 | using Signature_enumeration = void (*)(class IO&, T&); |
316 | |
317 | template <typename U> |
318 | static char test(SameType<Signature_enumeration, &U::enumeration>*); |
319 | |
320 | template <typename U> |
321 | static double test(...); |
322 | |
323 | static bool const value = |
324 | (sizeof(test<ScalarEnumerationTraits<T>>(nullptr)) == 1); |
325 | }; |
326 | |
327 | // Test if ScalarBitSetTraits<T> is defined on type T. |
328 | template <class T> |
329 | struct has_ScalarBitSetTraits |
330 | { |
331 | using Signature_bitset = void (*)(class IO&, T&); |
332 | |
333 | template <typename U> |
334 | static char test(SameType<Signature_bitset, &U::bitset>*); |
335 | |
336 | template <typename U> |
337 | static double test(...); |
338 | |
339 | static bool const value = (sizeof(test<ScalarBitSetTraits<T>>(nullptr)) == 1); |
340 | }; |
341 | |
342 | // Test if ScalarTraits<T> is defined on type T. |
343 | template <class T> |
344 | struct has_ScalarTraits |
345 | { |
346 | using Signature_input = StringRef (*)(StringRef, void*, T&); |
347 | using Signature_output = void (*)(const T&, void*, raw_ostream&); |
348 | using Signature_mustQuote = QuotingType (*)(StringRef); |
349 | |
350 | template <typename U> |
351 | static char test(SameType<Signature_input, &U::input> *, |
352 | SameType<Signature_output, &U::output> *, |
353 | SameType<Signature_mustQuote, &U::mustQuote> *); |
354 | |
355 | template <typename U> |
356 | static double test(...); |
357 | |
358 | static bool const value = |
359 | (sizeof(test<ScalarTraits<T>>(nullptr, nullptr, nullptr)) == 1); |
360 | }; |
361 | |
362 | // Test if BlockScalarTraits<T> is defined on type T. |
363 | template <class T> |
364 | struct has_BlockScalarTraits |
365 | { |
366 | using Signature_input = StringRef (*)(StringRef, void *, T &); |
367 | using Signature_output = void (*)(const T &, void *, raw_ostream &); |
368 | |
369 | template <typename U> |
370 | static char test(SameType<Signature_input, &U::input> *, |
371 | SameType<Signature_output, &U::output> *); |
372 | |
373 | template <typename U> |
374 | static double test(...); |
375 | |
376 | static bool const value = |
377 | (sizeof(test<BlockScalarTraits<T>>(nullptr, nullptr)) == 1); |
378 | }; |
379 | |
380 | // Test if TaggedScalarTraits<T> is defined on type T. |
381 | template <class T> struct has_TaggedScalarTraits { |
382 | using Signature_input = StringRef (*)(StringRef, StringRef, void *, T &); |
383 | using Signature_output = void (*)(const T &, void *, raw_ostream &, |
384 | raw_ostream &); |
385 | using Signature_mustQuote = QuotingType (*)(const T &, StringRef); |
386 | |
387 | template <typename U> |
388 | static char test(SameType<Signature_input, &U::input> *, |
389 | SameType<Signature_output, &U::output> *, |
390 | SameType<Signature_mustQuote, &U::mustQuote> *); |
391 | |
392 | template <typename U> static double test(...); |
393 | |
394 | static bool const value = |
395 | (sizeof(test<TaggedScalarTraits<T>>(nullptr, nullptr, nullptr)) == 1); |
396 | }; |
397 | |
398 | // Test if MappingContextTraits<T> is defined on type T. |
399 | template <class T, class Context> struct has_MappingTraits { |
400 | using Signature_mapping = void (*)(class IO &, T &, Context &); |
401 | |
402 | template <typename U> |
403 | static char test(SameType<Signature_mapping, &U::mapping>*); |
404 | |
405 | template <typename U> |
406 | static double test(...); |
407 | |
408 | static bool const value = |
409 | (sizeof(test<MappingContextTraits<T, Context>>(nullptr)) == 1); |
410 | }; |
411 | |
412 | // Test if MappingTraits<T> is defined on type T. |
413 | template <class T> struct has_MappingTraits<T, EmptyContext> { |
414 | using Signature_mapping = void (*)(class IO &, T &); |
415 | |
416 | template <typename U> |
417 | static char test(SameType<Signature_mapping, &U::mapping> *); |
418 | |
419 | template <typename U> static double test(...); |
420 | |
421 | static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1); |
422 | }; |
423 | |
424 | // Test if MappingContextTraits<T>::validate() is defined on type T. |
425 | template <class T, class Context> struct has_MappingValidateTraits { |
426 | using Signature_validate = std::string (*)(class IO &, T &, Context &); |
427 | |
428 | template <typename U> |
429 | static char test(SameType<Signature_validate, &U::validate>*); |
430 | |
431 | template <typename U> |
432 | static double test(...); |
433 | |
434 | static bool const value = |
435 | (sizeof(test<MappingContextTraits<T, Context>>(nullptr)) == 1); |
436 | }; |
437 | |
438 | // Test if MappingTraits<T>::validate() is defined on type T. |
439 | template <class T> struct has_MappingValidateTraits<T, EmptyContext> { |
440 | using Signature_validate = std::string (*)(class IO &, T &); |
441 | |
442 | template <typename U> |
443 | static char test(SameType<Signature_validate, &U::validate> *); |
444 | |
445 | template <typename U> static double test(...); |
446 | |
447 | static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1); |
448 | }; |
449 | |
450 | // Test if MappingContextTraits<T>::enumInput() is defined on type T. |
451 | template <class T, class Context> struct has_MappingEnumInputTraits { |
452 | using Signature_validate = void (*)(class IO &, T &); |
453 | |
454 | template <typename U> |
455 | static char test(SameType<Signature_validate, &U::enumInput> *); |
456 | |
457 | template <typename U> static double test(...); |
458 | |
459 | static bool const value = |
460 | (sizeof(test<MappingContextTraits<T, Context>>(nullptr)) == 1); |
461 | }; |
462 | |
463 | // Test if MappingTraits<T>::enumInput() is defined on type T. |
464 | template <class T> struct has_MappingEnumInputTraits<T, EmptyContext> { |
465 | using Signature_validate = void (*)(class IO &, T &); |
466 | |
467 | template <typename U> |
468 | static char test(SameType<Signature_validate, &U::enumInput> *); |
469 | |
470 | template <typename U> static double test(...); |
471 | |
472 | static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1); |
473 | }; |
474 | |
475 | // Test if SequenceTraits<T> is defined on type T. |
476 | template <class T> |
477 | struct has_SequenceMethodTraits |
478 | { |
479 | using Signature_size = size_t (*)(class IO&, T&); |
480 | |
481 | template <typename U> |
482 | static char test(SameType<Signature_size, &U::size>*); |
483 | |
484 | template <typename U> |
485 | static double test(...); |
486 | |
487 | static bool const value = (sizeof(test<SequenceTraits<T>>(nullptr)) == 1); |
488 | }; |
489 | |
490 | // Test if CustomMappingTraits<T> is defined on type T. |
491 | template <class T> |
492 | struct has_CustomMappingTraits |
493 | { |
494 | using Signature_input = void (*)(IO &io, StringRef key, T &v); |
495 | |
496 | template <typename U> |
497 | static char test(SameType<Signature_input, &U::inputOne>*); |
498 | |
499 | template <typename U> |
500 | static double test(...); |
501 | |
502 | static bool const value = |
503 | (sizeof(test<CustomMappingTraits<T>>(nullptr)) == 1); |
504 | }; |
505 | |
506 | // has_FlowTraits<int> will cause an error with some compilers because |
507 | // it subclasses int. Using this wrapper only instantiates the |
508 | // real has_FlowTraits only if the template type is a class. |
509 | template <typename T, bool Enabled = std::is_class_v<T>> class has_FlowTraits { |
510 | public: |
511 | static const bool value = false; |
512 | }; |
513 | |
514 | // Some older gcc compilers don't support straight forward tests |
515 | // for members, so test for ambiguity cause by the base and derived |
516 | // classes both defining the member. |
517 | template <class T> |
518 | struct has_FlowTraits<T, true> |
519 | { |
520 | struct Fallback { bool flow; }; |
521 | struct Derived : T, Fallback { }; |
522 | |
523 | template<typename C> |
524 | static char (&f(SameType<bool Fallback::*, &C::flow>*))[1]; |
525 | |
526 | template<typename C> |
527 | static char (&f(...))[2]; |
528 | |
529 | static bool const value = sizeof(f<Derived>(nullptr)) == 2; |
530 | }; |
531 | |
532 | // Test if SequenceTraits<T> is defined on type T |
533 | template<typename T> |
534 | struct has_SequenceTraits : public std::integral_constant<bool, |
535 | has_SequenceMethodTraits<T>::value > { }; |
536 | |
537 | // Test if DocumentListTraits<T> is defined on type T |
538 | template <class T> |
539 | struct has_DocumentListTraits |
540 | { |
541 | using Signature_size = size_t (*)(class IO &, T &); |
542 | |
543 | template <typename U> |
544 | static char test(SameType<Signature_size, &U::size>*); |
545 | |
546 | template <typename U> |
547 | static double test(...); |
548 | |
549 | static bool const value = (sizeof(test<DocumentListTraits<T>>(nullptr))==1); |
550 | }; |
551 | |
552 | template <class T> struct has_PolymorphicTraits { |
553 | using Signature_getKind = NodeKind (*)(const T &); |
554 | |
555 | template <typename U> |
556 | static char test(SameType<Signature_getKind, &U::getKind> *); |
557 | |
558 | template <typename U> static double test(...); |
559 | |
560 | static bool const value = (sizeof(test<PolymorphicTraits<T>>(nullptr)) == 1); |
561 | }; |
562 | |
563 | inline bool isNumeric(StringRef S) { |
564 | const auto skipDigits = [](StringRef Input) { |
565 | return Input.ltrim(Chars: "0123456789" ); |
566 | }; |
567 | |
568 | // Make S.front() and S.drop_front().front() (if S.front() is [+-]) calls |
569 | // safe. |
570 | if (S.empty() || S.equals(RHS: "+" ) || S.equals(RHS: "-" )) |
571 | return false; |
572 | |
573 | if (S.equals(RHS: ".nan" ) || S.equals(RHS: ".NaN" ) || S.equals(RHS: ".NAN" )) |
574 | return true; |
575 | |
576 | // Infinity and decimal numbers can be prefixed with sign. |
577 | StringRef Tail = (S.front() == '-' || S.front() == '+') ? S.drop_front() : S; |
578 | |
579 | // Check for infinity first, because checking for hex and oct numbers is more |
580 | // expensive. |
581 | if (Tail.equals(RHS: ".inf" ) || Tail.equals(RHS: ".Inf" ) || Tail.equals(RHS: ".INF" )) |
582 | return true; |
583 | |
584 | // Section 10.3.2 Tag Resolution |
585 | // YAML 1.2 Specification prohibits Base 8 and Base 16 numbers prefixed with |
586 | // [-+], so S should be used instead of Tail. |
587 | if (S.starts_with(Prefix: "0o" )) |
588 | return S.size() > 2 && |
589 | S.drop_front(N: 2).find_first_not_of(Chars: "01234567" ) == StringRef::npos; |
590 | |
591 | if (S.starts_with(Prefix: "0x" )) |
592 | return S.size() > 2 && S.drop_front(N: 2).find_first_not_of( |
593 | Chars: "0123456789abcdefABCDEF" ) == StringRef::npos; |
594 | |
595 | // Parse float: [-+]? (\. [0-9]+ | [0-9]+ (\. [0-9]* )?) ([eE] [-+]? [0-9]+)? |
596 | S = Tail; |
597 | |
598 | // Handle cases when the number starts with '.' and hence needs at least one |
599 | // digit after dot (as opposed by number which has digits before the dot), but |
600 | // doesn't have one. |
601 | if (S.starts_with(Prefix: "." ) && |
602 | (S.equals(RHS: "." ) || |
603 | (S.size() > 1 && std::strchr(s: "0123456789" , c: S[1]) == nullptr))) |
604 | return false; |
605 | |
606 | if (S.starts_with(Prefix: "E" ) || S.starts_with(Prefix: "e" )) |
607 | return false; |
608 | |
609 | enum ParseState { |
610 | Default, |
611 | FoundDot, |
612 | FoundExponent, |
613 | }; |
614 | ParseState State = Default; |
615 | |
616 | S = skipDigits(S); |
617 | |
618 | // Accept decimal integer. |
619 | if (S.empty()) |
620 | return true; |
621 | |
622 | if (S.front() == '.') { |
623 | State = FoundDot; |
624 | S = S.drop_front(); |
625 | } else if (S.front() == 'e' || S.front() == 'E') { |
626 | State = FoundExponent; |
627 | S = S.drop_front(); |
628 | } else { |
629 | return false; |
630 | } |
631 | |
632 | if (State == FoundDot) { |
633 | S = skipDigits(S); |
634 | if (S.empty()) |
635 | return true; |
636 | |
637 | if (S.front() == 'e' || S.front() == 'E') { |
638 | State = FoundExponent; |
639 | S = S.drop_front(); |
640 | } else { |
641 | return false; |
642 | } |
643 | } |
644 | |
645 | assert(State == FoundExponent && "Should have found exponent at this point." ); |
646 | if (S.empty()) |
647 | return false; |
648 | |
649 | if (S.front() == '+' || S.front() == '-') { |
650 | S = S.drop_front(); |
651 | if (S.empty()) |
652 | return false; |
653 | } |
654 | |
655 | return skipDigits(S).empty(); |
656 | } |
657 | |
658 | inline bool isNull(StringRef S) { |
659 | return S.equals(RHS: "null" ) || S.equals(RHS: "Null" ) || S.equals(RHS: "NULL" ) || |
660 | S.equals(RHS: "~" ); |
661 | } |
662 | |
663 | inline bool isBool(StringRef S) { |
664 | // FIXME: using parseBool is causing multiple tests to fail. |
665 | return S.equals(RHS: "true" ) || S.equals(RHS: "True" ) || S.equals(RHS: "TRUE" ) || |
666 | S.equals(RHS: "false" ) || S.equals(RHS: "False" ) || S.equals(RHS: "FALSE" ); |
667 | } |
668 | |
669 | // 5.1. Character Set |
670 | // The allowed character range explicitly excludes the C0 control block #x0-#x1F |
671 | // (except for TAB #x9, LF #xA, and CR #xD which are allowed), DEL #x7F, the C1 |
672 | // control block #x80-#x9F (except for NEL #x85 which is allowed), the surrogate |
673 | // block #xD800-#xDFFF, #xFFFE, and #xFFFF. |
674 | inline QuotingType needsQuotes(StringRef S) { |
675 | if (S.empty()) |
676 | return QuotingType::Single; |
677 | |
678 | QuotingType MaxQuotingNeeded = QuotingType::None; |
679 | if (isSpace(C: static_cast<unsigned char>(S.front())) || |
680 | isSpace(C: static_cast<unsigned char>(S.back()))) |
681 | MaxQuotingNeeded = QuotingType::Single; |
682 | if (isNull(S)) |
683 | MaxQuotingNeeded = QuotingType::Single; |
684 | if (isBool(S)) |
685 | MaxQuotingNeeded = QuotingType::Single; |
686 | if (isNumeric(S)) |
687 | MaxQuotingNeeded = QuotingType::Single; |
688 | |
689 | // 7.3.3 Plain Style |
690 | // Plain scalars must not begin with most indicators, as this would cause |
691 | // ambiguity with other YAML constructs. |
692 | if (std::strchr(s: R"(-?:\,[]{}#&*!|>'"%@`)" , c: S[0]) != nullptr) |
693 | MaxQuotingNeeded = QuotingType::Single; |
694 | |
695 | for (unsigned char C : S) { |
696 | // Alphanum is safe. |
697 | if (isAlnum(C)) |
698 | continue; |
699 | |
700 | switch (C) { |
701 | // Safe scalar characters. |
702 | case '_': |
703 | case '-': |
704 | case '^': |
705 | case '.': |
706 | case ',': |
707 | case ' ': |
708 | // TAB (0x9) is allowed in unquoted strings. |
709 | case 0x9: |
710 | continue; |
711 | // LF(0xA) and CR(0xD) may delimit values and so require at least single |
712 | // quotes. LLVM YAML parser cannot handle single quoted multiline so use |
713 | // double quoting to produce valid YAML. |
714 | case 0xA: |
715 | case 0xD: |
716 | return QuotingType::Double; |
717 | // DEL (0x7F) are excluded from the allowed character range. |
718 | case 0x7F: |
719 | return QuotingType::Double; |
720 | // Forward slash is allowed to be unquoted, but we quote it anyway. We have |
721 | // many tests that use FileCheck against YAML output, and this output often |
722 | // contains paths. If we quote backslashes but not forward slashes then |
723 | // paths will come out either quoted or unquoted depending on which platform |
724 | // the test is run on, making FileCheck comparisons difficult. |
725 | case '/': |
726 | default: { |
727 | // C0 control block (0x0 - 0x1F) is excluded from the allowed character |
728 | // range. |
729 | if (C <= 0x1F) |
730 | return QuotingType::Double; |
731 | |
732 | // Always double quote UTF-8. |
733 | if ((C & 0x80) != 0) |
734 | return QuotingType::Double; |
735 | |
736 | // The character is not safe, at least simple quoting needed. |
737 | MaxQuotingNeeded = QuotingType::Single; |
738 | } |
739 | } |
740 | } |
741 | |
742 | return MaxQuotingNeeded; |
743 | } |
744 | |
745 | template <typename T, typename Context> |
746 | struct missingTraits |
747 | : public std::integral_constant<bool, |
748 | !has_ScalarEnumerationTraits<T>::value && |
749 | !has_ScalarBitSetTraits<T>::value && |
750 | !has_ScalarTraits<T>::value && |
751 | !has_BlockScalarTraits<T>::value && |
752 | !has_TaggedScalarTraits<T>::value && |
753 | !has_MappingTraits<T, Context>::value && |
754 | !has_SequenceTraits<T>::value && |
755 | !has_CustomMappingTraits<T>::value && |
756 | !has_DocumentListTraits<T>::value && |
757 | !has_PolymorphicTraits<T>::value> {}; |
758 | |
759 | template <typename T, typename Context> |
760 | struct validatedMappingTraits |
761 | : public std::integral_constant< |
762 | bool, has_MappingTraits<T, Context>::value && |
763 | has_MappingValidateTraits<T, Context>::value> {}; |
764 | |
765 | template <typename T, typename Context> |
766 | struct unvalidatedMappingTraits |
767 | : public std::integral_constant< |
768 | bool, has_MappingTraits<T, Context>::value && |
769 | !has_MappingValidateTraits<T, Context>::value> {}; |
770 | |
771 | // Base class for Input and Output. |
772 | class IO { |
773 | public: |
774 | IO(void *Ctxt = nullptr); |
775 | virtual ~IO(); |
776 | |
777 | virtual bool outputting() const = 0; |
778 | |
779 | virtual unsigned beginSequence() = 0; |
780 | virtual bool preflightElement(unsigned, void *&) = 0; |
781 | virtual void postflightElement(void*) = 0; |
782 | virtual void endSequence() = 0; |
783 | virtual bool canElideEmptySequence() = 0; |
784 | |
785 | virtual unsigned beginFlowSequence() = 0; |
786 | virtual bool preflightFlowElement(unsigned, void *&) = 0; |
787 | virtual void postflightFlowElement(void*) = 0; |
788 | virtual void endFlowSequence() = 0; |
789 | |
790 | virtual bool mapTag(StringRef Tag, bool Default=false) = 0; |
791 | virtual void beginMapping() = 0; |
792 | virtual void endMapping() = 0; |
793 | virtual bool preflightKey(const char*, bool, bool, bool &, void *&) = 0; |
794 | virtual void postflightKey(void*) = 0; |
795 | virtual std::vector<StringRef> keys() = 0; |
796 | |
797 | virtual void beginFlowMapping() = 0; |
798 | virtual void endFlowMapping() = 0; |
799 | |
800 | virtual void beginEnumScalar() = 0; |
801 | virtual bool matchEnumScalar(const char*, bool) = 0; |
802 | virtual bool matchEnumFallback() = 0; |
803 | virtual void endEnumScalar() = 0; |
804 | |
805 | virtual bool beginBitSetScalar(bool &) = 0; |
806 | virtual bool bitSetMatch(const char*, bool) = 0; |
807 | virtual void endBitSetScalar() = 0; |
808 | |
809 | virtual void scalarString(StringRef &, QuotingType) = 0; |
810 | virtual void blockScalarString(StringRef &) = 0; |
811 | virtual void scalarTag(std::string &) = 0; |
812 | |
813 | virtual NodeKind getNodeKind() = 0; |
814 | |
815 | virtual void setError(const Twine &) = 0; |
816 | virtual void setAllowUnknownKeys(bool Allow); |
817 | |
818 | template <typename T> |
819 | void enumCase(T &Val, const char* Str, const T ConstVal) { |
820 | if ( matchEnumScalar(Str, outputting() && Val == ConstVal) ) { |
821 | Val = ConstVal; |
822 | } |
823 | } |
824 | |
825 | // allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF |
826 | template <typename T> |
827 | void enumCase(T &Val, const char* Str, const uint32_t ConstVal) { |
828 | if ( matchEnumScalar(Str, outputting() && Val == static_cast<T>(ConstVal)) ) { |
829 | Val = ConstVal; |
830 | } |
831 | } |
832 | |
833 | template <typename FBT, typename T> |
834 | void enumFallback(T &Val) { |
835 | if (matchEnumFallback()) { |
836 | EmptyContext Context; |
837 | // FIXME: Force integral conversion to allow strong typedefs to convert. |
838 | FBT Res = static_cast<typename FBT::BaseType>(Val); |
839 | yamlize(*this, Res, true, Context); |
840 | Val = static_cast<T>(static_cast<typename FBT::BaseType>(Res)); |
841 | } |
842 | } |
843 | |
844 | template <typename T> |
845 | void bitSetCase(T &Val, const char* Str, const T ConstVal) { |
846 | if ( bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal) ) { |
847 | Val = static_cast<T>(Val | ConstVal); |
848 | } |
849 | } |
850 | |
851 | // allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF |
852 | template <typename T> |
853 | void bitSetCase(T &Val, const char* Str, const uint32_t ConstVal) { |
854 | if ( bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal) ) { |
855 | Val = static_cast<T>(Val | ConstVal); |
856 | } |
857 | } |
858 | |
859 | template <typename T> |
860 | void maskedBitSetCase(T &Val, const char *Str, T ConstVal, T Mask) { |
861 | if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal)) |
862 | Val = Val | ConstVal; |
863 | } |
864 | |
865 | template <typename T> |
866 | void maskedBitSetCase(T &Val, const char *Str, uint32_t ConstVal, |
867 | uint32_t Mask) { |
868 | if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal)) |
869 | Val = Val | ConstVal; |
870 | } |
871 | |
872 | void *getContext() const; |
873 | void setContext(void *); |
874 | |
875 | template <typename T> void mapRequired(const char *Key, T &Val) { |
876 | EmptyContext Ctx; |
877 | this->processKey(Key, Val, true, Ctx); |
878 | } |
879 | |
880 | template <typename T, typename Context> |
881 | void mapRequired(const char *Key, T &Val, Context &Ctx) { |
882 | this->processKey(Key, Val, true, Ctx); |
883 | } |
884 | |
885 | template <typename T> void mapOptional(const char *Key, T &Val) { |
886 | EmptyContext Ctx; |
887 | mapOptionalWithContext(Key, Val, Ctx); |
888 | } |
889 | |
890 | template <typename T, typename DefaultT> |
891 | void mapOptional(const char *Key, T &Val, const DefaultT &Default) { |
892 | EmptyContext Ctx; |
893 | mapOptionalWithContext(Key, Val, Default, Ctx); |
894 | } |
895 | |
896 | template <typename T, typename Context> |
897 | std::enable_if_t<has_SequenceTraits<T>::value, void> |
898 | mapOptionalWithContext(const char *Key, T &Val, Context &Ctx) { |
899 | // omit key/value instead of outputting empty sequence |
900 | if (this->canElideEmptySequence() && !(Val.begin() != Val.end())) |
901 | return; |
902 | this->processKey(Key, Val, false, Ctx); |
903 | } |
904 | |
905 | template <typename T, typename Context> |
906 | void mapOptionalWithContext(const char *Key, std::optional<T> &Val, |
907 | Context &Ctx) { |
908 | this->processKeyWithDefault(Key, Val, std::optional<T>(), |
909 | /*Required=*/false, Ctx); |
910 | } |
911 | |
912 | template <typename T, typename Context> |
913 | std::enable_if_t<!has_SequenceTraits<T>::value, void> |
914 | mapOptionalWithContext(const char *Key, T &Val, Context &Ctx) { |
915 | this->processKey(Key, Val, false, Ctx); |
916 | } |
917 | |
918 | template <typename T, typename Context, typename DefaultT> |
919 | void mapOptionalWithContext(const char *Key, T &Val, const DefaultT &Default, |
920 | Context &Ctx) { |
921 | static_assert(std::is_convertible<DefaultT, T>::value, |
922 | "Default type must be implicitly convertible to value type!" ); |
923 | this->processKeyWithDefault(Key, Val, static_cast<const T &>(Default), |
924 | false, Ctx); |
925 | } |
926 | |
927 | private: |
928 | template <typename T, typename Context> |
929 | void processKeyWithDefault(const char *Key, std::optional<T> &Val, |
930 | const std::optional<T> &DefaultValue, |
931 | bool Required, Context &Ctx); |
932 | |
933 | template <typename T, typename Context> |
934 | void processKeyWithDefault(const char *Key, T &Val, const T &DefaultValue, |
935 | bool Required, Context &Ctx) { |
936 | void *SaveInfo; |
937 | bool UseDefault; |
938 | const bool sameAsDefault = outputting() && Val == DefaultValue; |
939 | if ( this->preflightKey(Key, Required, sameAsDefault, UseDefault, |
940 | SaveInfo) ) { |
941 | yamlize(*this, Val, Required, Ctx); |
942 | this->postflightKey(SaveInfo); |
943 | } |
944 | else { |
945 | if ( UseDefault ) |
946 | Val = DefaultValue; |
947 | } |
948 | } |
949 | |
950 | template <typename T, typename Context> |
951 | void processKey(const char *Key, T &Val, bool Required, Context &Ctx) { |
952 | void *SaveInfo; |
953 | bool UseDefault; |
954 | if ( this->preflightKey(Key, Required, false, UseDefault, SaveInfo) ) { |
955 | yamlize(*this, Val, Required, Ctx); |
956 | this->postflightKey(SaveInfo); |
957 | } |
958 | } |
959 | |
960 | private: |
961 | void *Ctxt; |
962 | }; |
963 | |
964 | namespace detail { |
965 | |
966 | template <typename T, typename Context> |
967 | void doMapping(IO &io, T &Val, Context &Ctx) { |
968 | MappingContextTraits<T, Context>::mapping(io, Val, Ctx); |
969 | } |
970 | |
971 | template <typename T> void doMapping(IO &io, T &Val, EmptyContext &Ctx) { |
972 | MappingTraits<T>::mapping(io, Val); |
973 | } |
974 | |
975 | } // end namespace detail |
976 | |
977 | template <typename T> |
978 | std::enable_if_t<has_ScalarEnumerationTraits<T>::value, void> |
979 | yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { |
980 | io.beginEnumScalar(); |
981 | ScalarEnumerationTraits<T>::enumeration(io, Val); |
982 | io.endEnumScalar(); |
983 | } |
984 | |
985 | template <typename T> |
986 | std::enable_if_t<has_ScalarBitSetTraits<T>::value, void> |
987 | yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { |
988 | bool DoClear; |
989 | if ( io.beginBitSetScalar(DoClear) ) { |
990 | if ( DoClear ) |
991 | Val = T(); |
992 | ScalarBitSetTraits<T>::bitset(io, Val); |
993 | io.endBitSetScalar(); |
994 | } |
995 | } |
996 | |
997 | template <typename T> |
998 | std::enable_if_t<has_ScalarTraits<T>::value, void> yamlize(IO &io, T &Val, bool, |
999 | EmptyContext &Ctx) { |
1000 | if ( io.outputting() ) { |
1001 | SmallString<128> Storage; |
1002 | raw_svector_ostream Buffer(Storage); |
1003 | ScalarTraits<T>::output(Val, io.getContext(), Buffer); |
1004 | StringRef Str = Buffer.str(); |
1005 | io.scalarString(Str, ScalarTraits<T>::mustQuote(Str)); |
1006 | } |
1007 | else { |
1008 | StringRef Str; |
1009 | io.scalarString(Str, ScalarTraits<T>::mustQuote(Str)); |
1010 | StringRef Result = ScalarTraits<T>::input(Str, io.getContext(), Val); |
1011 | if ( !Result.empty() ) { |
1012 | io.setError(Twine(Result)); |
1013 | } |
1014 | } |
1015 | } |
1016 | |
1017 | template <typename T> |
1018 | std::enable_if_t<has_BlockScalarTraits<T>::value, void> |
1019 | yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) { |
1020 | if (YamlIO.outputting()) { |
1021 | std::string Storage; |
1022 | raw_string_ostream Buffer(Storage); |
1023 | BlockScalarTraits<T>::output(Val, YamlIO.getContext(), Buffer); |
1024 | StringRef Str = Buffer.str(); |
1025 | YamlIO.blockScalarString(Str); |
1026 | } else { |
1027 | StringRef Str; |
1028 | YamlIO.blockScalarString(Str); |
1029 | StringRef Result = |
1030 | BlockScalarTraits<T>::input(Str, YamlIO.getContext(), Val); |
1031 | if (!Result.empty()) |
1032 | YamlIO.setError(Twine(Result)); |
1033 | } |
1034 | } |
1035 | |
1036 | template <typename T> |
1037 | std::enable_if_t<has_TaggedScalarTraits<T>::value, void> |
1038 | yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { |
1039 | if (io.outputting()) { |
1040 | std::string ScalarStorage, TagStorage; |
1041 | raw_string_ostream ScalarBuffer(ScalarStorage), TagBuffer(TagStorage); |
1042 | TaggedScalarTraits<T>::output(Val, io.getContext(), ScalarBuffer, |
1043 | TagBuffer); |
1044 | io.scalarTag(TagBuffer.str()); |
1045 | StringRef ScalarStr = ScalarBuffer.str(); |
1046 | io.scalarString(ScalarStr, |
1047 | TaggedScalarTraits<T>::mustQuote(Val, ScalarStr)); |
1048 | } else { |
1049 | std::string Tag; |
1050 | io.scalarTag(Tag); |
1051 | StringRef Str; |
1052 | io.scalarString(Str, QuotingType::None); |
1053 | StringRef Result = |
1054 | TaggedScalarTraits<T>::input(Str, Tag, io.getContext(), Val); |
1055 | if (!Result.empty()) { |
1056 | io.setError(Twine(Result)); |
1057 | } |
1058 | } |
1059 | } |
1060 | |
1061 | namespace detail { |
1062 | |
1063 | template <typename T, typename Context> |
1064 | std::string doValidate(IO &io, T &Val, Context &Ctx) { |
1065 | return MappingContextTraits<T, Context>::validate(io, Val, Ctx); |
1066 | } |
1067 | |
1068 | template <typename T> std::string doValidate(IO &io, T &Val, EmptyContext &) { |
1069 | return MappingTraits<T>::validate(io, Val); |
1070 | } |
1071 | |
1072 | } // namespace detail |
1073 | |
1074 | template <typename T, typename Context> |
1075 | std::enable_if_t<validatedMappingTraits<T, Context>::value, void> |
1076 | yamlize(IO &io, T &Val, bool, Context &Ctx) { |
1077 | if (has_FlowTraits<MappingTraits<T>>::value) |
1078 | io.beginFlowMapping(); |
1079 | else |
1080 | io.beginMapping(); |
1081 | if (io.outputting()) { |
1082 | std::string Err = detail::doValidate(io, Val, Ctx); |
1083 | if (!Err.empty()) { |
1084 | errs() << Err << "\n" ; |
1085 | assert(Err.empty() && "invalid struct trying to be written as yaml" ); |
1086 | } |
1087 | } |
1088 | detail::doMapping(io, Val, Ctx); |
1089 | if (!io.outputting()) { |
1090 | std::string Err = detail::doValidate(io, Val, Ctx); |
1091 | if (!Err.empty()) |
1092 | io.setError(Err); |
1093 | } |
1094 | if (has_FlowTraits<MappingTraits<T>>::value) |
1095 | io.endFlowMapping(); |
1096 | else |
1097 | io.endMapping(); |
1098 | } |
1099 | |
1100 | template <typename T, typename Context> |
1101 | std::enable_if_t<!has_MappingEnumInputTraits<T, Context>::value, bool> |
1102 | yamlizeMappingEnumInput(IO &io, T &Val) { |
1103 | return false; |
1104 | } |
1105 | |
1106 | template <typename T, typename Context> |
1107 | std::enable_if_t<has_MappingEnumInputTraits<T, Context>::value, bool> |
1108 | yamlizeMappingEnumInput(IO &io, T &Val) { |
1109 | if (io.outputting()) |
1110 | return false; |
1111 | |
1112 | io.beginEnumScalar(); |
1113 | MappingTraits<T>::enumInput(io, Val); |
1114 | bool Matched = !io.matchEnumFallback(); |
1115 | io.endEnumScalar(); |
1116 | return Matched; |
1117 | } |
1118 | |
1119 | template <typename T, typename Context> |
1120 | std::enable_if_t<unvalidatedMappingTraits<T, Context>::value, void> |
1121 | yamlize(IO &io, T &Val, bool, Context &Ctx) { |
1122 | if (yamlizeMappingEnumInput<T, Context>(io, Val)) |
1123 | return; |
1124 | if (has_FlowTraits<MappingTraits<T>>::value) { |
1125 | io.beginFlowMapping(); |
1126 | detail::doMapping(io, Val, Ctx); |
1127 | io.endFlowMapping(); |
1128 | } else { |
1129 | io.beginMapping(); |
1130 | detail::doMapping(io, Val, Ctx); |
1131 | io.endMapping(); |
1132 | } |
1133 | } |
1134 | |
1135 | template <typename T> |
1136 | std::enable_if_t<has_CustomMappingTraits<T>::value, void> |
1137 | yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { |
1138 | if ( io.outputting() ) { |
1139 | io.beginMapping(); |
1140 | CustomMappingTraits<T>::output(io, Val); |
1141 | io.endMapping(); |
1142 | } else { |
1143 | io.beginMapping(); |
1144 | for (StringRef key : io.keys()) |
1145 | CustomMappingTraits<T>::inputOne(io, key, Val); |
1146 | io.endMapping(); |
1147 | } |
1148 | } |
1149 | |
1150 | template <typename T> |
1151 | std::enable_if_t<has_PolymorphicTraits<T>::value, void> |
1152 | yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { |
1153 | switch (io.outputting() ? PolymorphicTraits<T>::getKind(Val) |
1154 | : io.getNodeKind()) { |
1155 | case NodeKind::Scalar: |
1156 | return yamlize(io, PolymorphicTraits<T>::getAsScalar(Val), true, Ctx); |
1157 | case NodeKind::Map: |
1158 | return yamlize(io, PolymorphicTraits<T>::getAsMap(Val), true, Ctx); |
1159 | case NodeKind::Sequence: |
1160 | return yamlize(io, PolymorphicTraits<T>::getAsSequence(Val), true, Ctx); |
1161 | } |
1162 | } |
1163 | |
1164 | template <typename T> |
1165 | std::enable_if_t<missingTraits<T, EmptyContext>::value, void> |
1166 | yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { |
1167 | char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)]; |
1168 | } |
1169 | |
1170 | template <typename T, typename Context> |
1171 | std::enable_if_t<has_SequenceTraits<T>::value, void> |
1172 | yamlize(IO &io, T &Seq, bool, Context &Ctx) { |
1173 | if ( has_FlowTraits< SequenceTraits<T>>::value ) { |
1174 | unsigned incnt = io.beginFlowSequence(); |
1175 | unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt; |
1176 | for(unsigned i=0; i < count; ++i) { |
1177 | void *SaveInfo; |
1178 | if ( io.preflightFlowElement(i, SaveInfo) ) { |
1179 | yamlize(io, SequenceTraits<T>::element(io, Seq, i), true, Ctx); |
1180 | io.postflightFlowElement(SaveInfo); |
1181 | } |
1182 | } |
1183 | io.endFlowSequence(); |
1184 | } |
1185 | else { |
1186 | unsigned incnt = io.beginSequence(); |
1187 | unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt; |
1188 | for(unsigned i=0; i < count; ++i) { |
1189 | void *SaveInfo; |
1190 | if ( io.preflightElement(i, SaveInfo) ) { |
1191 | yamlize(io, SequenceTraits<T>::element(io, Seq, i), true, Ctx); |
1192 | io.postflightElement(SaveInfo); |
1193 | } |
1194 | } |
1195 | io.endSequence(); |
1196 | } |
1197 | } |
1198 | |
1199 | template<> |
1200 | struct ScalarTraits<bool> { |
1201 | static void output(const bool &, void* , raw_ostream &); |
1202 | static StringRef input(StringRef, void *, bool &); |
1203 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1204 | }; |
1205 | |
1206 | template<> |
1207 | struct ScalarTraits<StringRef> { |
1208 | static void output(const StringRef &, void *, raw_ostream &); |
1209 | static StringRef input(StringRef, void *, StringRef &); |
1210 | static QuotingType mustQuote(StringRef S) { return needsQuotes(S); } |
1211 | }; |
1212 | |
1213 | template<> |
1214 | struct ScalarTraits<std::string> { |
1215 | static void output(const std::string &, void *, raw_ostream &); |
1216 | static StringRef input(StringRef, void *, std::string &); |
1217 | static QuotingType mustQuote(StringRef S) { return needsQuotes(S); } |
1218 | }; |
1219 | |
1220 | template<> |
1221 | struct ScalarTraits<uint8_t> { |
1222 | static void output(const uint8_t &, void *, raw_ostream &); |
1223 | static StringRef input(StringRef, void *, uint8_t &); |
1224 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1225 | }; |
1226 | |
1227 | template<> |
1228 | struct ScalarTraits<uint16_t> { |
1229 | static void output(const uint16_t &, void *, raw_ostream &); |
1230 | static StringRef input(StringRef, void *, uint16_t &); |
1231 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1232 | }; |
1233 | |
1234 | template<> |
1235 | struct ScalarTraits<uint32_t> { |
1236 | static void output(const uint32_t &, void *, raw_ostream &); |
1237 | static StringRef input(StringRef, void *, uint32_t &); |
1238 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1239 | }; |
1240 | |
1241 | template<> |
1242 | struct ScalarTraits<uint64_t> { |
1243 | static void output(const uint64_t &, void *, raw_ostream &); |
1244 | static StringRef input(StringRef, void *, uint64_t &); |
1245 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1246 | }; |
1247 | |
1248 | template<> |
1249 | struct ScalarTraits<int8_t> { |
1250 | static void output(const int8_t &, void *, raw_ostream &); |
1251 | static StringRef input(StringRef, void *, int8_t &); |
1252 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1253 | }; |
1254 | |
1255 | template<> |
1256 | struct ScalarTraits<int16_t> { |
1257 | static void output(const int16_t &, void *, raw_ostream &); |
1258 | static StringRef input(StringRef, void *, int16_t &); |
1259 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1260 | }; |
1261 | |
1262 | template<> |
1263 | struct ScalarTraits<int32_t> { |
1264 | static void output(const int32_t &, void *, raw_ostream &); |
1265 | static StringRef input(StringRef, void *, int32_t &); |
1266 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1267 | }; |
1268 | |
1269 | template<> |
1270 | struct ScalarTraits<int64_t> { |
1271 | static void output(const int64_t &, void *, raw_ostream &); |
1272 | static StringRef input(StringRef, void *, int64_t &); |
1273 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1274 | }; |
1275 | |
1276 | template<> |
1277 | struct ScalarTraits<float> { |
1278 | static void output(const float &, void *, raw_ostream &); |
1279 | static StringRef input(StringRef, void *, float &); |
1280 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1281 | }; |
1282 | |
1283 | template<> |
1284 | struct ScalarTraits<double> { |
1285 | static void output(const double &, void *, raw_ostream &); |
1286 | static StringRef input(StringRef, void *, double &); |
1287 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1288 | }; |
1289 | |
1290 | // For endian types, we use existing scalar Traits class for the underlying |
1291 | // type. This way endian aware types are supported whenever the traits are |
1292 | // defined for the underlying type. |
1293 | template <typename value_type, llvm::endianness endian, size_t alignment> |
1294 | struct ScalarTraits<support::detail::packed_endian_specific_integral< |
1295 | value_type, endian, alignment>, |
1296 | std::enable_if_t<has_ScalarTraits<value_type>::value>> { |
1297 | using endian_type = |
1298 | support::detail::packed_endian_specific_integral<value_type, endian, |
1299 | alignment>; |
1300 | |
1301 | static void output(const endian_type &E, void *Ctx, raw_ostream &Stream) { |
1302 | ScalarTraits<value_type>::output(static_cast<value_type>(E), Ctx, Stream); |
1303 | } |
1304 | |
1305 | static StringRef input(StringRef Str, void *Ctx, endian_type &E) { |
1306 | value_type V; |
1307 | auto R = ScalarTraits<value_type>::input(Str, Ctx, V); |
1308 | E = static_cast<endian_type>(V); |
1309 | return R; |
1310 | } |
1311 | |
1312 | static QuotingType mustQuote(StringRef Str) { |
1313 | return ScalarTraits<value_type>::mustQuote(Str); |
1314 | } |
1315 | }; |
1316 | |
1317 | template <typename value_type, llvm::endianness endian, size_t alignment> |
1318 | struct ScalarEnumerationTraits< |
1319 | support::detail::packed_endian_specific_integral<value_type, endian, |
1320 | alignment>, |
1321 | std::enable_if_t<has_ScalarEnumerationTraits<value_type>::value>> { |
1322 | using endian_type = |
1323 | support::detail::packed_endian_specific_integral<value_type, endian, |
1324 | alignment>; |
1325 | |
1326 | static void enumeration(IO &io, endian_type &E) { |
1327 | value_type V = E; |
1328 | ScalarEnumerationTraits<value_type>::enumeration(io, V); |
1329 | E = V; |
1330 | } |
1331 | }; |
1332 | |
1333 | template <typename value_type, llvm::endianness endian, size_t alignment> |
1334 | struct ScalarBitSetTraits< |
1335 | support::detail::packed_endian_specific_integral<value_type, endian, |
1336 | alignment>, |
1337 | std::enable_if_t<has_ScalarBitSetTraits<value_type>::value>> { |
1338 | using endian_type = |
1339 | support::detail::packed_endian_specific_integral<value_type, endian, |
1340 | alignment>; |
1341 | static void bitset(IO &io, endian_type &E) { |
1342 | value_type V = E; |
1343 | ScalarBitSetTraits<value_type>::bitset(io, V); |
1344 | E = V; |
1345 | } |
1346 | }; |
1347 | |
1348 | // Utility for use within MappingTraits<>::mapping() method |
1349 | // to [de]normalize an object for use with YAML conversion. |
1350 | template <typename TNorm, typename TFinal> |
1351 | struct MappingNormalization { |
1352 | MappingNormalization(IO &i_o, TFinal &Obj) |
1353 | : io(i_o), BufPtr(nullptr), Result(Obj) { |
1354 | if ( io.outputting() ) { |
1355 | BufPtr = new (&Buffer) TNorm(io, Obj); |
1356 | } |
1357 | else { |
1358 | BufPtr = new (&Buffer) TNorm(io); |
1359 | } |
1360 | } |
1361 | |
1362 | ~MappingNormalization() { |
1363 | if ( ! io.outputting() ) { |
1364 | Result = BufPtr->denormalize(io); |
1365 | } |
1366 | BufPtr->~TNorm(); |
1367 | } |
1368 | |
1369 | TNorm* operator->() { return BufPtr; } |
1370 | |
1371 | private: |
1372 | using Storage = AlignedCharArrayUnion<TNorm>; |
1373 | |
1374 | Storage Buffer; |
1375 | IO &io; |
1376 | TNorm *BufPtr; |
1377 | TFinal &Result; |
1378 | }; |
1379 | |
1380 | // Utility for use within MappingTraits<>::mapping() method |
1381 | // to [de]normalize an object for use with YAML conversion. |
1382 | template <typename TNorm, typename TFinal> |
1383 | struct MappingNormalizationHeap { |
1384 | MappingNormalizationHeap(IO &i_o, TFinal &Obj, BumpPtrAllocator *allocator) |
1385 | : io(i_o), Result(Obj) { |
1386 | if ( io.outputting() ) { |
1387 | BufPtr = new (&Buffer) TNorm(io, Obj); |
1388 | } |
1389 | else if (allocator) { |
1390 | BufPtr = allocator->Allocate<TNorm>(); |
1391 | new (BufPtr) TNorm(io); |
1392 | } else { |
1393 | BufPtr = new TNorm(io); |
1394 | } |
1395 | } |
1396 | |
1397 | ~MappingNormalizationHeap() { |
1398 | if ( io.outputting() ) { |
1399 | BufPtr->~TNorm(); |
1400 | } |
1401 | else { |
1402 | Result = BufPtr->denormalize(io); |
1403 | } |
1404 | } |
1405 | |
1406 | TNorm* operator->() { return BufPtr; } |
1407 | |
1408 | private: |
1409 | using Storage = AlignedCharArrayUnion<TNorm>; |
1410 | |
1411 | Storage Buffer; |
1412 | IO &io; |
1413 | TNorm *BufPtr = nullptr; |
1414 | TFinal &Result; |
1415 | }; |
1416 | |
1417 | /// |
1418 | /// The Input class is used to parse a yaml document into in-memory structs |
1419 | /// and vectors. |
1420 | /// |
1421 | /// It works by using YAMLParser to do a syntax parse of the entire yaml |
1422 | /// document, then the Input class builds a graph of HNodes which wraps |
1423 | /// each yaml Node. The extra layer is buffering. The low level yaml |
1424 | /// parser only lets you look at each node once. The buffering layer lets |
1425 | /// you search and interate multiple times. This is necessary because |
1426 | /// the mapRequired() method calls may not be in the same order |
1427 | /// as the keys in the document. |
1428 | /// |
1429 | class Input : public IO { |
1430 | public: |
1431 | // Construct a yaml Input object from a StringRef and optional |
1432 | // user-data. The DiagHandler can be specified to provide |
1433 | // alternative error reporting. |
1434 | Input(StringRef InputContent, |
1435 | void *Ctxt = nullptr, |
1436 | SourceMgr::DiagHandlerTy DiagHandler = nullptr, |
1437 | void *DiagHandlerCtxt = nullptr); |
1438 | Input(MemoryBufferRef Input, |
1439 | void *Ctxt = nullptr, |
1440 | SourceMgr::DiagHandlerTy DiagHandler = nullptr, |
1441 | void *DiagHandlerCtxt = nullptr); |
1442 | ~Input() override; |
1443 | |
1444 | // Check if there was an syntax or semantic error during parsing. |
1445 | std::error_code error(); |
1446 | |
1447 | private: |
1448 | bool outputting() const override; |
1449 | bool mapTag(StringRef, bool) override; |
1450 | void beginMapping() override; |
1451 | void endMapping() override; |
1452 | bool preflightKey(const char *, bool, bool, bool &, void *&) override; |
1453 | void postflightKey(void *) override; |
1454 | std::vector<StringRef> keys() override; |
1455 | void beginFlowMapping() override; |
1456 | void endFlowMapping() override; |
1457 | unsigned beginSequence() override; |
1458 | void endSequence() override; |
1459 | bool preflightElement(unsigned index, void *&) override; |
1460 | void postflightElement(void *) override; |
1461 | unsigned beginFlowSequence() override; |
1462 | bool preflightFlowElement(unsigned , void *&) override; |
1463 | void postflightFlowElement(void *) override; |
1464 | void endFlowSequence() override; |
1465 | void beginEnumScalar() override; |
1466 | bool matchEnumScalar(const char*, bool) override; |
1467 | bool matchEnumFallback() override; |
1468 | void endEnumScalar() override; |
1469 | bool beginBitSetScalar(bool &) override; |
1470 | bool bitSetMatch(const char *, bool ) override; |
1471 | void endBitSetScalar() override; |
1472 | void scalarString(StringRef &, QuotingType) override; |
1473 | void blockScalarString(StringRef &) override; |
1474 | void scalarTag(std::string &) override; |
1475 | NodeKind getNodeKind() override; |
1476 | void setError(const Twine &message) override; |
1477 | bool canElideEmptySequence() override; |
1478 | |
1479 | class HNode { |
1480 | public: |
1481 | HNode(Node *n) : _node(n) {} |
1482 | |
1483 | static bool classof(const HNode *) { return true; } |
1484 | |
1485 | Node *_node; |
1486 | }; |
1487 | |
1488 | class EmptyHNode : public HNode { |
1489 | public: |
1490 | EmptyHNode(Node *n) : HNode(n) { } |
1491 | |
1492 | static bool classof(const HNode *n) { return NullNode::classof(N: n->_node); } |
1493 | |
1494 | static bool classof(const EmptyHNode *) { return true; } |
1495 | }; |
1496 | |
1497 | class ScalarHNode : public HNode { |
1498 | public: |
1499 | ScalarHNode(Node *n, StringRef s) : HNode(n), _value(s) { } |
1500 | |
1501 | StringRef value() const { return _value; } |
1502 | |
1503 | static bool classof(const HNode *n) { |
1504 | return ScalarNode::classof(N: n->_node) || |
1505 | BlockScalarNode::classof(N: n->_node); |
1506 | } |
1507 | |
1508 | static bool classof(const ScalarHNode *) { return true; } |
1509 | |
1510 | protected: |
1511 | StringRef _value; |
1512 | }; |
1513 | |
1514 | class MapHNode : public HNode { |
1515 | public: |
1516 | MapHNode(Node *n) : HNode(n) { } |
1517 | |
1518 | static bool classof(const HNode *n) { |
1519 | return MappingNode::classof(N: n->_node); |
1520 | } |
1521 | |
1522 | static bool classof(const MapHNode *) { return true; } |
1523 | |
1524 | using NameToNodeAndLoc = StringMap<std::pair<HNode *, SMRange>>; |
1525 | |
1526 | NameToNodeAndLoc Mapping; |
1527 | SmallVector<std::string, 6> ValidKeys; |
1528 | }; |
1529 | |
1530 | class SequenceHNode : public HNode { |
1531 | public: |
1532 | SequenceHNode(Node *n) : HNode(n) { } |
1533 | |
1534 | static bool classof(const HNode *n) { |
1535 | return SequenceNode::classof(N: n->_node); |
1536 | } |
1537 | |
1538 | static bool classof(const SequenceHNode *) { return true; } |
1539 | |
1540 | std::vector<HNode *> Entries; |
1541 | }; |
1542 | |
1543 | Input::HNode *createHNodes(Node *node); |
1544 | void setError(HNode *hnode, const Twine &message); |
1545 | void setError(Node *node, const Twine &message); |
1546 | void setError(const SMRange &Range, const Twine &message); |
1547 | |
1548 | void reportWarning(HNode *hnode, const Twine &message); |
1549 | void reportWarning(Node *hnode, const Twine &message); |
1550 | void reportWarning(const SMRange &Range, const Twine &message); |
1551 | |
1552 | /// Release memory used by HNodes. |
1553 | void releaseHNodeBuffers(); |
1554 | |
1555 | public: |
1556 | // These are only used by operator>>. They could be private |
1557 | // if those templated things could be made friends. |
1558 | bool setCurrentDocument(); |
1559 | bool nextDocument(); |
1560 | |
1561 | /// Returns the current node that's being parsed by the YAML Parser. |
1562 | const Node *getCurrentNode() const; |
1563 | |
1564 | void setAllowUnknownKeys(bool Allow) override; |
1565 | |
1566 | private: |
1567 | SourceMgr SrcMgr; // must be before Strm |
1568 | std::unique_ptr<llvm::yaml::Stream> Strm; |
1569 | HNode *TopNode = nullptr; |
1570 | std::error_code EC; |
1571 | BumpPtrAllocator StringAllocator; |
1572 | SpecificBumpPtrAllocator<EmptyHNode> EmptyHNodeAllocator; |
1573 | SpecificBumpPtrAllocator<ScalarHNode> ScalarHNodeAllocator; |
1574 | SpecificBumpPtrAllocator<MapHNode> MapHNodeAllocator; |
1575 | SpecificBumpPtrAllocator<SequenceHNode> SequenceHNodeAllocator; |
1576 | document_iterator DocIterator; |
1577 | llvm::BitVector BitValuesUsed; |
1578 | HNode *CurrentNode = nullptr; |
1579 | bool ScalarMatchFound = false; |
1580 | bool AllowUnknownKeys = false; |
1581 | }; |
1582 | |
1583 | /// |
1584 | /// The Output class is used to generate a yaml document from in-memory structs |
1585 | /// and vectors. |
1586 | /// |
1587 | class Output : public IO { |
1588 | public: |
1589 | Output(raw_ostream &, void *Ctxt = nullptr, int WrapColumn = 70); |
1590 | ~Output() override; |
1591 | |
1592 | /// Set whether or not to output optional values which are equal |
1593 | /// to the default value. By default, when outputting if you attempt |
1594 | /// to write a value that is equal to the default, the value gets ignored. |
1595 | /// Sometimes, it is useful to be able to see these in the resulting YAML |
1596 | /// anyway. |
1597 | void setWriteDefaultValues(bool Write) { WriteDefaultValues = Write; } |
1598 | |
1599 | bool outputting() const override; |
1600 | bool mapTag(StringRef, bool) override; |
1601 | void beginMapping() override; |
1602 | void endMapping() override; |
1603 | bool preflightKey(const char *key, bool, bool, bool &, void *&) override; |
1604 | void postflightKey(void *) override; |
1605 | std::vector<StringRef> keys() override; |
1606 | void beginFlowMapping() override; |
1607 | void endFlowMapping() override; |
1608 | unsigned beginSequence() override; |
1609 | void endSequence() override; |
1610 | bool preflightElement(unsigned, void *&) override; |
1611 | void postflightElement(void *) override; |
1612 | unsigned beginFlowSequence() override; |
1613 | bool preflightFlowElement(unsigned, void *&) override; |
1614 | void postflightFlowElement(void *) override; |
1615 | void endFlowSequence() override; |
1616 | void beginEnumScalar() override; |
1617 | bool matchEnumScalar(const char*, bool) override; |
1618 | bool matchEnumFallback() override; |
1619 | void endEnumScalar() override; |
1620 | bool beginBitSetScalar(bool &) override; |
1621 | bool bitSetMatch(const char *, bool ) override; |
1622 | void endBitSetScalar() override; |
1623 | void scalarString(StringRef &, QuotingType) override; |
1624 | void blockScalarString(StringRef &) override; |
1625 | void scalarTag(std::string &) override; |
1626 | NodeKind getNodeKind() override; |
1627 | void setError(const Twine &message) override; |
1628 | bool canElideEmptySequence() override; |
1629 | |
1630 | // These are only used by operator<<. They could be private |
1631 | // if that templated operator could be made a friend. |
1632 | void beginDocuments(); |
1633 | bool preflightDocument(unsigned); |
1634 | void postflightDocument(); |
1635 | void endDocuments(); |
1636 | |
1637 | private: |
1638 | void output(StringRef s); |
1639 | void outputUpToEndOfLine(StringRef s); |
1640 | void newLineCheck(bool EmptySequence = false); |
1641 | void outputNewLine(); |
1642 | void paddedKey(StringRef key); |
1643 | void flowKey(StringRef Key); |
1644 | |
1645 | enum InState { |
1646 | inSeqFirstElement, |
1647 | inSeqOtherElement, |
1648 | inFlowSeqFirstElement, |
1649 | inFlowSeqOtherElement, |
1650 | inMapFirstKey, |
1651 | inMapOtherKey, |
1652 | inFlowMapFirstKey, |
1653 | inFlowMapOtherKey |
1654 | }; |
1655 | |
1656 | static bool inSeqAnyElement(InState State); |
1657 | static bool inFlowSeqAnyElement(InState State); |
1658 | static bool inMapAnyKey(InState State); |
1659 | static bool inFlowMapAnyKey(InState State); |
1660 | |
1661 | raw_ostream &Out; |
1662 | int WrapColumn; |
1663 | SmallVector<InState, 8> StateStack; |
1664 | int Column = 0; |
1665 | int ColumnAtFlowStart = 0; |
1666 | int ColumnAtMapFlowStart = 0; |
1667 | bool NeedBitValueComma = false; |
1668 | bool NeedFlowSequenceComma = false; |
1669 | bool EnumerationMatchFound = false; |
1670 | bool WriteDefaultValues = false; |
1671 | StringRef Padding; |
1672 | StringRef PaddingBeforeContainer; |
1673 | }; |
1674 | |
1675 | template <typename T, typename Context> |
1676 | void IO::processKeyWithDefault(const char *Key, std::optional<T> &Val, |
1677 | const std::optional<T> &DefaultValue, |
1678 | bool Required, Context &Ctx) { |
1679 | assert(!DefaultValue && "std::optional<T> shouldn't have a value!" ); |
1680 | void *SaveInfo; |
1681 | bool UseDefault = true; |
1682 | const bool sameAsDefault = outputting() && !Val; |
1683 | if (!outputting() && !Val) |
1684 | Val = T(); |
1685 | if (Val && |
1686 | this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) { |
1687 | |
1688 | // When reading an std::optional<X> key from a YAML description, we allow |
1689 | // the special "<none>" value, which can be used to specify that no value |
1690 | // was requested, i.e. the DefaultValue will be assigned. The DefaultValue |
1691 | // is usually None. |
1692 | bool IsNone = false; |
1693 | if (!outputting()) |
1694 | if (const auto *Node = |
1695 | dyn_cast<ScalarNode>(Val: ((Input *)this)->getCurrentNode())) |
1696 | // We use rtrim to ignore possible white spaces that might exist when a |
1697 | // comment is present on the same line. |
1698 | IsNone = Node->getRawValue().rtrim(Char: ' ') == "<none>" ; |
1699 | |
1700 | if (IsNone) |
1701 | Val = DefaultValue; |
1702 | else |
1703 | yamlize(*this, *Val, Required, Ctx); |
1704 | this->postflightKey(SaveInfo); |
1705 | } else { |
1706 | if (UseDefault) |
1707 | Val = DefaultValue; |
1708 | } |
1709 | } |
1710 | |
1711 | /// YAML I/O does conversion based on types. But often native data types |
1712 | /// are just a typedef of built in intergral types (e.g. int). But the C++ |
1713 | /// type matching system sees through the typedef and all the typedefed types |
1714 | /// look like a built in type. This will cause the generic YAML I/O conversion |
1715 | /// to be used. To provide better control over the YAML conversion, you can |
1716 | /// use this macro instead of typedef. It will create a class with one field |
1717 | /// and automatic conversion operators to and from the base type. |
1718 | /// Based on BOOST_STRONG_TYPEDEF |
1719 | #define LLVM_YAML_STRONG_TYPEDEF(_base, _type) \ |
1720 | struct _type { \ |
1721 | _type() = default; \ |
1722 | _type(const _base v) : value(v) {} \ |
1723 | _type(const _type &v) = default; \ |
1724 | _type &operator=(const _type &rhs) = default; \ |
1725 | _type &operator=(const _base &rhs) { value = rhs; return *this; } \ |
1726 | operator const _base & () const { return value; } \ |
1727 | bool operator==(const _type &rhs) const { return value == rhs.value; } \ |
1728 | bool operator==(const _base &rhs) const { return value == rhs; } \ |
1729 | bool operator<(const _type &rhs) const { return value < rhs.value; } \ |
1730 | _base value; \ |
1731 | using BaseType = _base; \ |
1732 | }; |
1733 | |
1734 | /// |
1735 | /// Use these types instead of uintXX_t in any mapping to have |
1736 | /// its yaml output formatted as hexadecimal. |
1737 | /// |
1738 | LLVM_YAML_STRONG_TYPEDEF(uint8_t, Hex8) |
1739 | LLVM_YAML_STRONG_TYPEDEF(uint16_t, Hex16) |
1740 | LLVM_YAML_STRONG_TYPEDEF(uint32_t, Hex32) |
1741 | LLVM_YAML_STRONG_TYPEDEF(uint64_t, Hex64) |
1742 | |
1743 | template<> |
1744 | struct ScalarTraits<Hex8> { |
1745 | static void output(const Hex8 &, void *, raw_ostream &); |
1746 | static StringRef input(StringRef, void *, Hex8 &); |
1747 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1748 | }; |
1749 | |
1750 | template<> |
1751 | struct ScalarTraits<Hex16> { |
1752 | static void output(const Hex16 &, void *, raw_ostream &); |
1753 | static StringRef input(StringRef, void *, Hex16 &); |
1754 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1755 | }; |
1756 | |
1757 | template<> |
1758 | struct ScalarTraits<Hex32> { |
1759 | static void output(const Hex32 &, void *, raw_ostream &); |
1760 | static StringRef input(StringRef, void *, Hex32 &); |
1761 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1762 | }; |
1763 | |
1764 | template<> |
1765 | struct ScalarTraits<Hex64> { |
1766 | static void output(const Hex64 &, void *, raw_ostream &); |
1767 | static StringRef input(StringRef, void *, Hex64 &); |
1768 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1769 | }; |
1770 | |
1771 | template <> struct ScalarTraits<VersionTuple> { |
1772 | static void output(const VersionTuple &Value, void *, llvm::raw_ostream &Out); |
1773 | static StringRef input(StringRef, void *, VersionTuple &); |
1774 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1775 | }; |
1776 | |
1777 | // Define non-member operator>> so that Input can stream in a document list. |
1778 | template <typename T> |
1779 | inline std::enable_if_t<has_DocumentListTraits<T>::value, Input &> |
1780 | operator>>(Input &yin, T &docList) { |
1781 | int i = 0; |
1782 | EmptyContext Ctx; |
1783 | while ( yin.setCurrentDocument() ) { |
1784 | yamlize(yin, DocumentListTraits<T>::element(yin, docList, i), true, Ctx); |
1785 | if ( yin.error() ) |
1786 | return yin; |
1787 | yin.nextDocument(); |
1788 | ++i; |
1789 | } |
1790 | return yin; |
1791 | } |
1792 | |
1793 | // Define non-member operator>> so that Input can stream in a map as a document. |
1794 | template <typename T> |
1795 | inline std::enable_if_t<has_MappingTraits<T, EmptyContext>::value, Input &> |
1796 | operator>>(Input &yin, T &docMap) { |
1797 | EmptyContext Ctx; |
1798 | yin.setCurrentDocument(); |
1799 | yamlize(yin, docMap, true, Ctx); |
1800 | return yin; |
1801 | } |
1802 | |
1803 | // Define non-member operator>> so that Input can stream in a sequence as |
1804 | // a document. |
1805 | template <typename T> |
1806 | inline std::enable_if_t<has_SequenceTraits<T>::value, Input &> |
1807 | operator>>(Input &yin, T &docSeq) { |
1808 | EmptyContext Ctx; |
1809 | if (yin.setCurrentDocument()) |
1810 | yamlize(yin, docSeq, true, Ctx); |
1811 | return yin; |
1812 | } |
1813 | |
1814 | // Define non-member operator>> so that Input can stream in a block scalar. |
1815 | template <typename T> |
1816 | inline std::enable_if_t<has_BlockScalarTraits<T>::value, Input &> |
1817 | operator>>(Input &In, T &Val) { |
1818 | EmptyContext Ctx; |
1819 | if (In.setCurrentDocument()) |
1820 | yamlize(In, Val, true, Ctx); |
1821 | return In; |
1822 | } |
1823 | |
1824 | // Define non-member operator>> so that Input can stream in a string map. |
1825 | template <typename T> |
1826 | inline std::enable_if_t<has_CustomMappingTraits<T>::value, Input &> |
1827 | operator>>(Input &In, T &Val) { |
1828 | EmptyContext Ctx; |
1829 | if (In.setCurrentDocument()) |
1830 | yamlize(In, Val, true, Ctx); |
1831 | return In; |
1832 | } |
1833 | |
1834 | // Define non-member operator>> so that Input can stream in a polymorphic type. |
1835 | template <typename T> |
1836 | inline std::enable_if_t<has_PolymorphicTraits<T>::value, Input &> |
1837 | operator>>(Input &In, T &Val) { |
1838 | EmptyContext Ctx; |
1839 | if (In.setCurrentDocument()) |
1840 | yamlize(In, Val, true, Ctx); |
1841 | return In; |
1842 | } |
1843 | |
1844 | // Provide better error message about types missing a trait specialization |
1845 | template <typename T> |
1846 | inline std::enable_if_t<missingTraits<T, EmptyContext>::value, Input &> |
1847 | operator>>(Input &yin, T &docSeq) { |
1848 | char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)]; |
1849 | return yin; |
1850 | } |
1851 | |
1852 | // Define non-member operator<< so that Output can stream out document list. |
1853 | template <typename T> |
1854 | inline std::enable_if_t<has_DocumentListTraits<T>::value, Output &> |
1855 | operator<<(Output &yout, T &docList) { |
1856 | EmptyContext Ctx; |
1857 | yout.beginDocuments(); |
1858 | const size_t count = DocumentListTraits<T>::size(yout, docList); |
1859 | for(size_t i=0; i < count; ++i) { |
1860 | if ( yout.preflightDocument(i) ) { |
1861 | yamlize(yout, DocumentListTraits<T>::element(yout, docList, i), true, |
1862 | Ctx); |
1863 | yout.postflightDocument(); |
1864 | } |
1865 | } |
1866 | yout.endDocuments(); |
1867 | return yout; |
1868 | } |
1869 | |
1870 | // Define non-member operator<< so that Output can stream out a map. |
1871 | template <typename T> |
1872 | inline std::enable_if_t<has_MappingTraits<T, EmptyContext>::value, Output &> |
1873 | operator<<(Output &yout, T &map) { |
1874 | EmptyContext Ctx; |
1875 | yout.beginDocuments(); |
1876 | if ( yout.preflightDocument(0) ) { |
1877 | yamlize(yout, map, true, Ctx); |
1878 | yout.postflightDocument(); |
1879 | } |
1880 | yout.endDocuments(); |
1881 | return yout; |
1882 | } |
1883 | |
1884 | // Define non-member operator<< so that Output can stream out a sequence. |
1885 | template <typename T> |
1886 | inline std::enable_if_t<has_SequenceTraits<T>::value, Output &> |
1887 | operator<<(Output &yout, T &seq) { |
1888 | EmptyContext Ctx; |
1889 | yout.beginDocuments(); |
1890 | if ( yout.preflightDocument(0) ) { |
1891 | yamlize(yout, seq, true, Ctx); |
1892 | yout.postflightDocument(); |
1893 | } |
1894 | yout.endDocuments(); |
1895 | return yout; |
1896 | } |
1897 | |
1898 | // Define non-member operator<< so that Output can stream out a block scalar. |
1899 | template <typename T> |
1900 | inline std::enable_if_t<has_BlockScalarTraits<T>::value, Output &> |
1901 | operator<<(Output &Out, T &Val) { |
1902 | EmptyContext Ctx; |
1903 | Out.beginDocuments(); |
1904 | if (Out.preflightDocument(0)) { |
1905 | yamlize(Out, Val, true, Ctx); |
1906 | Out.postflightDocument(); |
1907 | } |
1908 | Out.endDocuments(); |
1909 | return Out; |
1910 | } |
1911 | |
1912 | // Define non-member operator<< so that Output can stream out a string map. |
1913 | template <typename T> |
1914 | inline std::enable_if_t<has_CustomMappingTraits<T>::value, Output &> |
1915 | operator<<(Output &Out, T &Val) { |
1916 | EmptyContext Ctx; |
1917 | Out.beginDocuments(); |
1918 | if (Out.preflightDocument(0)) { |
1919 | yamlize(Out, Val, true, Ctx); |
1920 | Out.postflightDocument(); |
1921 | } |
1922 | Out.endDocuments(); |
1923 | return Out; |
1924 | } |
1925 | |
1926 | // Define non-member operator<< so that Output can stream out a polymorphic |
1927 | // type. |
1928 | template <typename T> |
1929 | inline std::enable_if_t<has_PolymorphicTraits<T>::value, Output &> |
1930 | operator<<(Output &Out, T &Val) { |
1931 | EmptyContext Ctx; |
1932 | Out.beginDocuments(); |
1933 | if (Out.preflightDocument(0)) { |
1934 | // FIXME: The parser does not support explicit documents terminated with a |
1935 | // plain scalar; the end-marker is included as part of the scalar token. |
1936 | assert(PolymorphicTraits<T>::getKind(Val) != NodeKind::Scalar && "plain scalar documents are not supported" ); |
1937 | yamlize(Out, Val, true, Ctx); |
1938 | Out.postflightDocument(); |
1939 | } |
1940 | Out.endDocuments(); |
1941 | return Out; |
1942 | } |
1943 | |
1944 | // Provide better error message about types missing a trait specialization |
1945 | template <typename T> |
1946 | inline std::enable_if_t<missingTraits<T, EmptyContext>::value, Output &> |
1947 | operator<<(Output &yout, T &seq) { |
1948 | char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)]; |
1949 | return yout; |
1950 | } |
1951 | |
1952 | template <bool B> struct IsFlowSequenceBase {}; |
1953 | template <> struct IsFlowSequenceBase<true> { static const bool flow = true; }; |
1954 | |
1955 | template <typename T, typename U = void> |
1956 | struct IsResizable : std::false_type {}; |
1957 | |
1958 | template <typename T> |
1959 | struct IsResizable<T, std::void_t<decltype(std::declval<T>().resize(0))>> |
1960 | : public std::true_type {}; |
1961 | |
1962 | template <typename T, bool B> struct IsResizableBase { |
1963 | using type = typename T::value_type; |
1964 | |
1965 | static type &element(IO &io, T &seq, size_t index) { |
1966 | if (index >= seq.size()) |
1967 | seq.resize(index + 1); |
1968 | return seq[index]; |
1969 | } |
1970 | }; |
1971 | |
1972 | template <typename T> struct IsResizableBase<T, false> { |
1973 | using type = typename T::value_type; |
1974 | |
1975 | static type &element(IO &io, T &seq, size_t index) { |
1976 | if (index >= seq.size()) { |
1977 | io.setError(Twine("value sequence extends beyond static size (" ) + |
1978 | Twine(seq.size()) + ")" ); |
1979 | return seq[0]; |
1980 | } |
1981 | return seq[index]; |
1982 | } |
1983 | }; |
1984 | |
1985 | template <typename T, bool Flow> |
1986 | struct SequenceTraitsImpl |
1987 | : IsFlowSequenceBase<Flow>, IsResizableBase<T, IsResizable<T>::value> { |
1988 | static size_t size(IO &io, T &seq) { return seq.size(); } |
1989 | }; |
1990 | |
1991 | // Simple helper to check an expression can be used as a bool-valued template |
1992 | // argument. |
1993 | template <bool> struct CheckIsBool { static const bool value = true; }; |
1994 | |
1995 | // If T has SequenceElementTraits, then vector<T> and SmallVector<T, N> have |
1996 | // SequenceTraits that do the obvious thing. |
1997 | template <typename T> |
1998 | struct SequenceTraits< |
1999 | std::vector<T>, |
2000 | std::enable_if_t<CheckIsBool<SequenceElementTraits<T>::flow>::value>> |
2001 | : SequenceTraitsImpl<std::vector<T>, SequenceElementTraits<T>::flow> {}; |
2002 | template <typename T, unsigned N> |
2003 | struct SequenceTraits< |
2004 | SmallVector<T, N>, |
2005 | std::enable_if_t<CheckIsBool<SequenceElementTraits<T>::flow>::value>> |
2006 | : SequenceTraitsImpl<SmallVector<T, N>, SequenceElementTraits<T>::flow> {}; |
2007 | template <typename T> |
2008 | struct SequenceTraits< |
2009 | SmallVectorImpl<T>, |
2010 | std::enable_if_t<CheckIsBool<SequenceElementTraits<T>::flow>::value>> |
2011 | : SequenceTraitsImpl<SmallVectorImpl<T>, SequenceElementTraits<T>::flow> {}; |
2012 | template <typename T> |
2013 | struct SequenceTraits< |
2014 | MutableArrayRef<T>, |
2015 | std::enable_if_t<CheckIsBool<SequenceElementTraits<T>::flow>::value>> |
2016 | : SequenceTraitsImpl<MutableArrayRef<T>, SequenceElementTraits<T>::flow> {}; |
2017 | |
2018 | // Sequences of fundamental types use flow formatting. |
2019 | template <typename T> |
2020 | struct SequenceElementTraits<T, std::enable_if_t<std::is_fundamental_v<T>>> { |
2021 | static const bool flow = true; |
2022 | }; |
2023 | |
2024 | // Sequences of strings use block formatting. |
2025 | template<> struct SequenceElementTraits<std::string> { |
2026 | static const bool flow = false; |
2027 | }; |
2028 | template<> struct SequenceElementTraits<StringRef> { |
2029 | static const bool flow = false; |
2030 | }; |
2031 | template<> struct SequenceElementTraits<std::pair<std::string, std::string>> { |
2032 | static const bool flow = false; |
2033 | }; |
2034 | |
2035 | /// Implementation of CustomMappingTraits for std::map<std::string, T>. |
2036 | template <typename T> struct StdMapStringCustomMappingTraitsImpl { |
2037 | using map_type = std::map<std::string, T>; |
2038 | |
2039 | static void inputOne(IO &io, StringRef key, map_type &v) { |
2040 | io.mapRequired(key.str().c_str(), v[std::string(key)]); |
2041 | } |
2042 | |
2043 | static void output(IO &io, map_type &v) { |
2044 | for (auto &p : v) |
2045 | io.mapRequired(p.first.c_str(), p.second); |
2046 | } |
2047 | }; |
2048 | |
2049 | } // end namespace yaml |
2050 | } // end namespace llvm |
2051 | |
2052 | #define LLVM_YAML_IS_SEQUENCE_VECTOR_IMPL(TYPE, FLOW) \ |
2053 | namespace llvm { \ |
2054 | namespace yaml { \ |
2055 | static_assert( \ |
2056 | !std::is_fundamental_v<TYPE> && !std::is_same_v<TYPE, std::string> && \ |
2057 | !std::is_same_v<TYPE, llvm::StringRef>, \ |
2058 | "only use LLVM_YAML_IS_SEQUENCE_VECTOR for types you control"); \ |
2059 | template <> struct SequenceElementTraits<TYPE> { \ |
2060 | static const bool flow = FLOW; \ |
2061 | }; \ |
2062 | } \ |
2063 | } |
2064 | |
2065 | /// Utility for declaring that a std::vector of a particular type |
2066 | /// should be considered a YAML sequence. |
2067 | #define LLVM_YAML_IS_SEQUENCE_VECTOR(type) \ |
2068 | LLVM_YAML_IS_SEQUENCE_VECTOR_IMPL(type, false) |
2069 | |
2070 | /// Utility for declaring that a std::vector of a particular type |
2071 | /// should be considered a YAML flow sequence. |
2072 | #define LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(type) \ |
2073 | LLVM_YAML_IS_SEQUENCE_VECTOR_IMPL(type, true) |
2074 | |
2075 | #define LLVM_YAML_DECLARE_MAPPING_TRAITS(Type) \ |
2076 | namespace llvm { \ |
2077 | namespace yaml { \ |
2078 | template <> struct MappingTraits<Type> { \ |
2079 | static void mapping(IO &IO, Type &Obj); \ |
2080 | }; \ |
2081 | } \ |
2082 | } |
2083 | |
2084 | #define LLVM_YAML_DECLARE_ENUM_TRAITS(Type) \ |
2085 | namespace llvm { \ |
2086 | namespace yaml { \ |
2087 | template <> struct ScalarEnumerationTraits<Type> { \ |
2088 | static void enumeration(IO &io, Type &Value); \ |
2089 | }; \ |
2090 | } \ |
2091 | } |
2092 | |
2093 | #define LLVM_YAML_DECLARE_BITSET_TRAITS(Type) \ |
2094 | namespace llvm { \ |
2095 | namespace yaml { \ |
2096 | template <> struct ScalarBitSetTraits<Type> { \ |
2097 | static void bitset(IO &IO, Type &Options); \ |
2098 | }; \ |
2099 | } \ |
2100 | } |
2101 | |
2102 | #define LLVM_YAML_DECLARE_SCALAR_TRAITS(Type, MustQuote) \ |
2103 | namespace llvm { \ |
2104 | namespace yaml { \ |
2105 | template <> struct ScalarTraits<Type> { \ |
2106 | static void output(const Type &Value, void *ctx, raw_ostream &Out); \ |
2107 | static StringRef input(StringRef Scalar, void *ctxt, Type &Value); \ |
2108 | static QuotingType mustQuote(StringRef) { return MustQuote; } \ |
2109 | }; \ |
2110 | } \ |
2111 | } |
2112 | |
2113 | /// Utility for declaring that a std::vector of a particular type |
2114 | /// should be considered a YAML document list. |
2115 | #define LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(_type) \ |
2116 | namespace llvm { \ |
2117 | namespace yaml { \ |
2118 | template <unsigned N> \ |
2119 | struct DocumentListTraits<SmallVector<_type, N>> \ |
2120 | : public SequenceTraitsImpl<SmallVector<_type, N>, false> {}; \ |
2121 | template <> \ |
2122 | struct DocumentListTraits<std::vector<_type>> \ |
2123 | : public SequenceTraitsImpl<std::vector<_type>, false> {}; \ |
2124 | } \ |
2125 | } |
2126 | |
2127 | /// Utility for declaring that std::map<std::string, _type> should be considered |
2128 | /// a YAML map. |
2129 | #define LLVM_YAML_IS_STRING_MAP(_type) \ |
2130 | namespace llvm { \ |
2131 | namespace yaml { \ |
2132 | template <> \ |
2133 | struct CustomMappingTraits<std::map<std::string, _type>> \ |
2134 | : public StdMapStringCustomMappingTraitsImpl<_type> {}; \ |
2135 | } \ |
2136 | } |
2137 | |
2138 | LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::Hex64) |
2139 | LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::Hex32) |
2140 | LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::Hex16) |
2141 | LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::Hex8) |
2142 | |
2143 | #endif // LLVM_SUPPORT_YAMLTRAITS_H |
2144 | |