Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 1 | //===- YAMLParser.h - Simple YAML parser ------------------------*- 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 | // This is a YAML 1.2 parser. |
||
10 | // |
||
11 | // See http://www.yaml.org/spec/1.2/spec.html for the full standard. |
||
12 | // |
||
13 | // This currently does not implement the following: |
||
14 | // * Tag resolution. |
||
15 | // * UTF-16. |
||
16 | // * BOMs anywhere other than the first Unicode scalar value in the file. |
||
17 | // |
||
18 | // The most important class here is Stream. This represents a YAML stream with |
||
19 | // 0, 1, or many documents. |
||
20 | // |
||
21 | // SourceMgr sm; |
||
22 | // StringRef input = getInput(); |
||
23 | // yaml::Stream stream(input, sm); |
||
24 | // |
||
25 | // for (yaml::document_iterator di = stream.begin(), de = stream.end(); |
||
26 | // di != de; ++di) { |
||
27 | // yaml::Node *n = di->getRoot(); |
||
28 | // if (n) { |
||
29 | // // Do something with n... |
||
30 | // } else |
||
31 | // break; |
||
32 | // } |
||
33 | // |
||
34 | //===----------------------------------------------------------------------===// |
||
35 | |||
36 | #ifndef LLVM_SUPPORT_YAMLPARSER_H |
||
37 | #define LLVM_SUPPORT_YAMLPARSER_H |
||
38 | |||
39 | #include "llvm/ADT/StringRef.h" |
||
40 | #include "llvm/Support/Allocator.h" |
||
41 | #include "llvm/Support/SMLoc.h" |
||
42 | #include "llvm/Support/SourceMgr.h" |
||
43 | #include <cassert> |
||
44 | #include <cstddef> |
||
45 | #include <iterator> |
||
46 | #include <map> |
||
47 | #include <memory> |
||
48 | #include <optional> |
||
49 | #include <string> |
||
50 | #include <system_error> |
||
51 | |||
52 | namespace llvm { |
||
53 | |||
54 | class MemoryBufferRef; |
||
55 | class raw_ostream; |
||
56 | class Twine; |
||
57 | |||
58 | namespace yaml { |
||
59 | |||
60 | class Document; |
||
61 | class document_iterator; |
||
62 | class Node; |
||
63 | class Scanner; |
||
64 | struct Token; |
||
65 | |||
66 | /// Dump all the tokens in this stream to OS. |
||
67 | /// \returns true if there was an error, false otherwise. |
||
68 | bool dumpTokens(StringRef Input, raw_ostream &); |
||
69 | |||
70 | /// Scans all tokens in input without outputting anything. This is used |
||
71 | /// for benchmarking the tokenizer. |
||
72 | /// \returns true if there was an error, false otherwise. |
||
73 | bool scanTokens(StringRef Input); |
||
74 | |||
75 | /// Escape \a Input for a double quoted scalar; if \p EscapePrintable |
||
76 | /// is true, all UTF8 sequences will be escaped, if \p EscapePrintable is |
||
77 | /// false, those UTF8 sequences encoding printable unicode scalars will not be |
||
78 | /// escaped, but emitted verbatim. |
||
79 | std::string escape(StringRef Input, bool EscapePrintable = true); |
||
80 | |||
81 | /// Parse \p S as a bool according to https://yaml.org/type/bool.html. |
||
82 | std::optional<bool> parseBool(StringRef S); |
||
83 | |||
84 | /// This class represents a YAML stream potentially containing multiple |
||
85 | /// documents. |
||
86 | class Stream { |
||
87 | public: |
||
88 | /// This keeps a reference to the string referenced by \p Input. |
||
89 | Stream(StringRef Input, SourceMgr &, bool ShowColors = true, |
||
90 | std::error_code *EC = nullptr); |
||
91 | |||
92 | Stream(MemoryBufferRef InputBuffer, SourceMgr &, bool ShowColors = true, |
||
93 | std::error_code *EC = nullptr); |
||
94 | ~Stream(); |
||
95 | |||
96 | document_iterator begin(); |
||
97 | document_iterator end(); |
||
98 | void skip(); |
||
99 | bool failed(); |
||
100 | |||
101 | bool validate() { |
||
102 | skip(); |
||
103 | return !failed(); |
||
104 | } |
||
105 | |||
106 | void printError(Node *N, const Twine &Msg, |
||
107 | SourceMgr::DiagKind Kind = SourceMgr::DK_Error); |
||
108 | void printError(const SMRange &Range, const Twine &Msg, |
||
109 | SourceMgr::DiagKind Kind = SourceMgr::DK_Error); |
||
110 | |||
111 | private: |
||
112 | friend class Document; |
||
113 | |||
114 | std::unique_ptr<Scanner> scanner; |
||
115 | std::unique_ptr<Document> CurrentDoc; |
||
116 | }; |
||
117 | |||
118 | /// Abstract base class for all Nodes. |
||
119 | class Node { |
||
120 | virtual void anchor(); |
||
121 | |||
122 | public: |
||
123 | enum NodeKind { |
||
124 | NK_Null, |
||
125 | NK_Scalar, |
||
126 | NK_BlockScalar, |
||
127 | NK_KeyValue, |
||
128 | NK_Mapping, |
||
129 | NK_Sequence, |
||
130 | NK_Alias |
||
131 | }; |
||
132 | |||
133 | Node(unsigned int Type, std::unique_ptr<Document> &, StringRef Anchor, |
||
134 | StringRef Tag); |
||
135 | |||
136 | // It's not safe to copy YAML nodes; the document is streamed and the position |
||
137 | // is part of the state. |
||
138 | Node(const Node &) = delete; |
||
139 | void operator=(const Node &) = delete; |
||
140 | |||
141 | void *operator new(size_t Size, BumpPtrAllocator &Alloc, |
||
142 | size_t Alignment = 16) noexcept { |
||
143 | return Alloc.Allocate(Size, Alignment); |
||
144 | } |
||
145 | |||
146 | void operator delete(void *Ptr, BumpPtrAllocator &Alloc, |
||
147 | size_t Size) noexcept { |
||
148 | Alloc.Deallocate(Ptr, Size, 0); |
||
149 | } |
||
150 | |||
151 | void operator delete(void *) noexcept = delete; |
||
152 | |||
153 | /// Get the value of the anchor attached to this node. If it does not |
||
154 | /// have one, getAnchor().size() will be 0. |
||
155 | StringRef getAnchor() const { return Anchor; } |
||
156 | |||
157 | /// Get the tag as it was written in the document. This does not |
||
158 | /// perform tag resolution. |
||
159 | StringRef getRawTag() const { return Tag; } |
||
160 | |||
161 | /// Get the verbatium tag for a given Node. This performs tag resoluton |
||
162 | /// and substitution. |
||
163 | std::string getVerbatimTag() const; |
||
164 | |||
165 | SMRange getSourceRange() const { return SourceRange; } |
||
166 | void setSourceRange(SMRange SR) { SourceRange = SR; } |
||
167 | |||
168 | // These functions forward to Document and Scanner. |
||
169 | Token &peekNext(); |
||
170 | Token getNext(); |
||
171 | Node *parseBlockNode(); |
||
172 | BumpPtrAllocator &getAllocator(); |
||
173 | void setError(const Twine &Message, Token &Location) const; |
||
174 | bool failed() const; |
||
175 | |||
176 | virtual void skip() {} |
||
177 | |||
178 | unsigned int getType() const { return TypeID; } |
||
179 | |||
180 | protected: |
||
181 | std::unique_ptr<Document> &Doc; |
||
182 | SMRange SourceRange; |
||
183 | |||
184 | ~Node() = default; |
||
185 | |||
186 | private: |
||
187 | unsigned int TypeID; |
||
188 | StringRef Anchor; |
||
189 | /// The tag as typed in the document. |
||
190 | StringRef Tag; |
||
191 | }; |
||
192 | |||
193 | /// A null value. |
||
194 | /// |
||
195 | /// Example: |
||
196 | /// !!null null |
||
197 | class NullNode final : public Node { |
||
198 | void anchor() override; |
||
199 | |||
200 | public: |
||
201 | NullNode(std::unique_ptr<Document> &D) |
||
202 | : Node(NK_Null, D, StringRef(), StringRef()) {} |
||
203 | |||
204 | static bool classof(const Node *N) { return N->getType() == NK_Null; } |
||
205 | }; |
||
206 | |||
207 | /// A scalar node is an opaque datum that can be presented as a |
||
208 | /// series of zero or more Unicode scalar values. |
||
209 | /// |
||
210 | /// Example: |
||
211 | /// Adena |
||
212 | class ScalarNode final : public Node { |
||
213 | void anchor() override; |
||
214 | |||
215 | public: |
||
216 | ScalarNode(std::unique_ptr<Document> &D, StringRef Anchor, StringRef Tag, |
||
217 | StringRef Val) |
||
218 | : Node(NK_Scalar, D, Anchor, Tag), Value(Val) { |
||
219 | SMLoc Start = SMLoc::getFromPointer(Val.begin()); |
||
220 | SMLoc End = SMLoc::getFromPointer(Val.end()); |
||
221 | SourceRange = SMRange(Start, End); |
||
222 | } |
||
223 | |||
224 | // Return Value without any escaping or folding or other fun YAML stuff. This |
||
225 | // is the exact bytes that are contained in the file (after conversion to |
||
226 | // utf8). |
||
227 | StringRef getRawValue() const { return Value; } |
||
228 | |||
229 | /// Gets the value of this node as a StringRef. |
||
230 | /// |
||
231 | /// \param Storage is used to store the content of the returned StringRef if |
||
232 | /// it requires any modification from how it appeared in the source. |
||
233 | /// This happens with escaped characters and multi-line literals. |
||
234 | StringRef getValue(SmallVectorImpl<char> &Storage) const; |
||
235 | |||
236 | static bool classof(const Node *N) { |
||
237 | return N->getType() == NK_Scalar; |
||
238 | } |
||
239 | |||
240 | private: |
||
241 | StringRef Value; |
||
242 | |||
243 | StringRef unescapeDoubleQuoted(StringRef UnquotedValue, |
||
244 | StringRef::size_type Start, |
||
245 | SmallVectorImpl<char> &Storage) const; |
||
246 | }; |
||
247 | |||
248 | /// A block scalar node is an opaque datum that can be presented as a |
||
249 | /// series of zero or more Unicode scalar values. |
||
250 | /// |
||
251 | /// Example: |
||
252 | /// | |
||
253 | /// Hello |
||
254 | /// World |
||
255 | class BlockScalarNode final : public Node { |
||
256 | void anchor() override; |
||
257 | |||
258 | public: |
||
259 | BlockScalarNode(std::unique_ptr<Document> &D, StringRef Anchor, StringRef Tag, |
||
260 | StringRef Value, StringRef RawVal) |
||
261 | : Node(NK_BlockScalar, D, Anchor, Tag), Value(Value) { |
||
262 | SMLoc Start = SMLoc::getFromPointer(RawVal.begin()); |
||
263 | SMLoc End = SMLoc::getFromPointer(RawVal.end()); |
||
264 | SourceRange = SMRange(Start, End); |
||
265 | } |
||
266 | |||
267 | /// Gets the value of this node as a StringRef. |
||
268 | StringRef getValue() const { return Value; } |
||
269 | |||
270 | static bool classof(const Node *N) { |
||
271 | return N->getType() == NK_BlockScalar; |
||
272 | } |
||
273 | |||
274 | private: |
||
275 | StringRef Value; |
||
276 | }; |
||
277 | |||
278 | /// A key and value pair. While not technically a Node under the YAML |
||
279 | /// representation graph, it is easier to treat them this way. |
||
280 | /// |
||
281 | /// TODO: Consider making this not a child of Node. |
||
282 | /// |
||
283 | /// Example: |
||
284 | /// Section: .text |
||
285 | class KeyValueNode final : public Node { |
||
286 | void anchor() override; |
||
287 | |||
288 | public: |
||
289 | KeyValueNode(std::unique_ptr<Document> &D) |
||
290 | : Node(NK_KeyValue, D, StringRef(), StringRef()) {} |
||
291 | |||
292 | /// Parse and return the key. |
||
293 | /// |
||
294 | /// This may be called multiple times. |
||
295 | /// |
||
296 | /// \returns The key, or nullptr if failed() == true. |
||
297 | Node *getKey(); |
||
298 | |||
299 | /// Parse and return the value. |
||
300 | /// |
||
301 | /// This may be called multiple times. |
||
302 | /// |
||
303 | /// \returns The value, or nullptr if failed() == true. |
||
304 | Node *getValue(); |
||
305 | |||
306 | void skip() override { |
||
307 | if (Node *Key = getKey()) { |
||
308 | Key->skip(); |
||
309 | if (Node *Val = getValue()) |
||
310 | Val->skip(); |
||
311 | } |
||
312 | } |
||
313 | |||
314 | static bool classof(const Node *N) { |
||
315 | return N->getType() == NK_KeyValue; |
||
316 | } |
||
317 | |||
318 | private: |
||
319 | Node *Key = nullptr; |
||
320 | Node *Value = nullptr; |
||
321 | }; |
||
322 | |||
323 | /// This is an iterator abstraction over YAML collections shared by both |
||
324 | /// sequences and maps. |
||
325 | /// |
||
326 | /// BaseT must have a ValueT* member named CurrentEntry and a member function |
||
327 | /// increment() which must set CurrentEntry to 0 to create an end iterator. |
||
328 | template <class BaseT, class ValueT> class basic_collection_iterator { |
||
329 | public: |
||
330 | using iterator_category = std::input_iterator_tag; |
||
331 | using value_type = ValueT; |
||
332 | using difference_type = std::ptrdiff_t; |
||
333 | using pointer = value_type *; |
||
334 | using reference = value_type &; |
||
335 | |||
336 | basic_collection_iterator() = default; |
||
337 | basic_collection_iterator(BaseT *B) : Base(B) {} |
||
338 | |||
339 | ValueT *operator->() const { |
||
340 | assert(Base && Base->CurrentEntry && "Attempted to access end iterator!"); |
||
341 | return Base->CurrentEntry; |
||
342 | } |
||
343 | |||
344 | ValueT &operator*() const { |
||
345 | assert(Base && Base->CurrentEntry && |
||
346 | "Attempted to dereference end iterator!"); |
||
347 | return *Base->CurrentEntry; |
||
348 | } |
||
349 | |||
350 | operator ValueT *() const { |
||
351 | assert(Base && Base->CurrentEntry && "Attempted to access end iterator!"); |
||
352 | return Base->CurrentEntry; |
||
353 | } |
||
354 | |||
355 | /// Note on EqualityComparable: |
||
356 | /// |
||
357 | /// The iterator is not re-entrant, |
||
358 | /// it is meant to be used for parsing YAML on-demand |
||
359 | /// Once iteration started - it can point only to one entry at a time |
||
360 | /// hence Base.CurrentEntry and Other.Base.CurrentEntry are equal |
||
361 | /// iff Base and Other.Base are equal. |
||
362 | bool operator==(const basic_collection_iterator &Other) const { |
||
363 | if (Base && (Base == Other.Base)) { |
||
364 | assert((Base->CurrentEntry == Other.Base->CurrentEntry) |
||
365 | && "Equal Bases expected to point to equal Entries"); |
||
366 | } |
||
367 | |||
368 | return Base == Other.Base; |
||
369 | } |
||
370 | |||
371 | bool operator!=(const basic_collection_iterator &Other) const { |
||
372 | return !(Base == Other.Base); |
||
373 | } |
||
374 | |||
375 | basic_collection_iterator &operator++() { |
||
376 | assert(Base && "Attempted to advance iterator past end!"); |
||
377 | Base->increment(); |
||
378 | // Create an end iterator. |
||
379 | if (!Base->CurrentEntry) |
||
380 | Base = nullptr; |
||
381 | return *this; |
||
382 | } |
||
383 | |||
384 | private: |
||
385 | BaseT *Base = nullptr; |
||
386 | }; |
||
387 | |||
388 | // The following two templates are used for both MappingNode and Sequence Node. |
||
389 | template <class CollectionType> |
||
390 | typename CollectionType::iterator begin(CollectionType &C) { |
||
391 | assert(C.IsAtBeginning && "You may only iterate over a collection once!"); |
||
392 | C.IsAtBeginning = false; |
||
393 | typename CollectionType::iterator ret(&C); |
||
394 | ++ret; |
||
395 | return ret; |
||
396 | } |
||
397 | |||
398 | template <class CollectionType> void skip(CollectionType &C) { |
||
399 | // TODO: support skipping from the middle of a parsed collection ;/ |
||
400 | assert((C.IsAtBeginning || C.IsAtEnd) && "Cannot skip mid parse!"); |
||
401 | if (C.IsAtBeginning) |
||
402 | for (typename CollectionType::iterator i = begin(C), e = C.end(); i != e; |
||
403 | ++i) |
||
404 | i->skip(); |
||
405 | } |
||
406 | |||
407 | /// Represents a YAML map created from either a block map for a flow map. |
||
408 | /// |
||
409 | /// This parses the YAML stream as increment() is called. |
||
410 | /// |
||
411 | /// Example: |
||
412 | /// Name: _main |
||
413 | /// Scope: Global |
||
414 | class MappingNode final : public Node { |
||
415 | void anchor() override; |
||
416 | |||
417 | public: |
||
418 | enum MappingType { |
||
419 | MT_Block, |
||
420 | MT_Flow, |
||
421 | MT_Inline ///< An inline mapping node is used for "[key: value]". |
||
422 | }; |
||
423 | |||
424 | MappingNode(std::unique_ptr<Document> &D, StringRef Anchor, StringRef Tag, |
||
425 | MappingType MT) |
||
426 | : Node(NK_Mapping, D, Anchor, Tag), Type(MT) {} |
||
427 | |||
428 | friend class basic_collection_iterator<MappingNode, KeyValueNode>; |
||
429 | |||
430 | using iterator = basic_collection_iterator<MappingNode, KeyValueNode>; |
||
431 | |||
432 | template <class T> friend typename T::iterator yaml::begin(T &); |
||
433 | template <class T> friend void yaml::skip(T &); |
||
434 | |||
435 | iterator begin() { return yaml::begin(*this); } |
||
436 | |||
437 | iterator end() { return iterator(); } |
||
438 | |||
439 | void skip() override { yaml::skip(*this); } |
||
440 | |||
441 | static bool classof(const Node *N) { |
||
442 | return N->getType() == NK_Mapping; |
||
443 | } |
||
444 | |||
445 | private: |
||
446 | MappingType Type; |
||
447 | bool IsAtBeginning = true; |
||
448 | bool IsAtEnd = false; |
||
449 | KeyValueNode *CurrentEntry = nullptr; |
||
450 | |||
451 | void increment(); |
||
452 | }; |
||
453 | |||
454 | /// Represents a YAML sequence created from either a block sequence for a |
||
455 | /// flow sequence. |
||
456 | /// |
||
457 | /// This parses the YAML stream as increment() is called. |
||
458 | /// |
||
459 | /// Example: |
||
460 | /// - Hello |
||
461 | /// - World |
||
462 | class SequenceNode final : public Node { |
||
463 | void anchor() override; |
||
464 | |||
465 | public: |
||
466 | enum SequenceType { |
||
467 | ST_Block, |
||
468 | ST_Flow, |
||
469 | // Use for: |
||
470 | // |
||
471 | // key: |
||
472 | // - val1 |
||
473 | // - val2 |
||
474 | // |
||
475 | // As a BlockMappingEntry and BlockEnd are not created in this case. |
||
476 | ST_Indentless |
||
477 | }; |
||
478 | |||
479 | SequenceNode(std::unique_ptr<Document> &D, StringRef Anchor, StringRef Tag, |
||
480 | SequenceType ST) |
||
481 | : Node(NK_Sequence, D, Anchor, Tag), SeqType(ST) {} |
||
482 | |||
483 | friend class basic_collection_iterator<SequenceNode, Node>; |
||
484 | |||
485 | using iterator = basic_collection_iterator<SequenceNode, Node>; |
||
486 | |||
487 | template <class T> friend typename T::iterator yaml::begin(T &); |
||
488 | template <class T> friend void yaml::skip(T &); |
||
489 | |||
490 | void increment(); |
||
491 | |||
492 | iterator begin() { return yaml::begin(*this); } |
||
493 | |||
494 | iterator end() { return iterator(); } |
||
495 | |||
496 | void skip() override { yaml::skip(*this); } |
||
497 | |||
498 | static bool classof(const Node *N) { |
||
499 | return N->getType() == NK_Sequence; |
||
500 | } |
||
501 | |||
502 | private: |
||
503 | SequenceType SeqType; |
||
504 | bool IsAtBeginning = true; |
||
505 | bool IsAtEnd = false; |
||
506 | bool WasPreviousTokenFlowEntry = true; // Start with an imaginary ','. |
||
507 | Node *CurrentEntry = nullptr; |
||
508 | }; |
||
509 | |||
510 | /// Represents an alias to a Node with an anchor. |
||
511 | /// |
||
512 | /// Example: |
||
513 | /// *AnchorName |
||
514 | class AliasNode final : public Node { |
||
515 | void anchor() override; |
||
516 | |||
517 | public: |
||
518 | AliasNode(std::unique_ptr<Document> &D, StringRef Val) |
||
519 | : Node(NK_Alias, D, StringRef(), StringRef()), Name(Val) {} |
||
520 | |||
521 | StringRef getName() const { return Name; } |
||
522 | |||
523 | static bool classof(const Node *N) { return N->getType() == NK_Alias; } |
||
524 | |||
525 | private: |
||
526 | StringRef Name; |
||
527 | }; |
||
528 | |||
529 | /// A YAML Stream is a sequence of Documents. A document contains a root |
||
530 | /// node. |
||
531 | class Document { |
||
532 | public: |
||
533 | Document(Stream &ParentStream); |
||
534 | |||
535 | /// Root for parsing a node. Returns a single node. |
||
536 | Node *parseBlockNode(); |
||
537 | |||
538 | /// Finish parsing the current document and return true if there are |
||
539 | /// more. Return false otherwise. |
||
540 | bool skip(); |
||
541 | |||
542 | /// Parse and return the root level node. |
||
543 | Node *getRoot() { |
||
544 | if (Root) |
||
545 | return Root; |
||
546 | return Root = parseBlockNode(); |
||
547 | } |
||
548 | |||
549 | const std::map<StringRef, StringRef> &getTagMap() const { return TagMap; } |
||
550 | |||
551 | private: |
||
552 | friend class Node; |
||
553 | friend class document_iterator; |
||
554 | |||
555 | /// Stream to read tokens from. |
||
556 | Stream &stream; |
||
557 | |||
558 | /// Used to allocate nodes to. All are destroyed without calling their |
||
559 | /// destructor when the document is destroyed. |
||
560 | BumpPtrAllocator NodeAllocator; |
||
561 | |||
562 | /// The root node. Used to support skipping a partially parsed |
||
563 | /// document. |
||
564 | Node *Root; |
||
565 | |||
566 | /// Maps tag prefixes to their expansion. |
||
567 | std::map<StringRef, StringRef> TagMap; |
||
568 | |||
569 | Token &peekNext(); |
||
570 | Token getNext(); |
||
571 | void setError(const Twine &Message, Token &Location) const; |
||
572 | bool failed() const; |
||
573 | |||
574 | /// Parse %BLAH directives and return true if any were encountered. |
||
575 | bool parseDirectives(); |
||
576 | |||
577 | /// Parse %YAML |
||
578 | void parseYAMLDirective(); |
||
579 | |||
580 | /// Parse %TAG |
||
581 | void parseTAGDirective(); |
||
582 | |||
583 | /// Consume the next token and error if it is not \a TK. |
||
584 | bool expectToken(int TK); |
||
585 | }; |
||
586 | |||
587 | /// Iterator abstraction for Documents over a Stream. |
||
588 | class document_iterator { |
||
589 | public: |
||
590 | document_iterator() = default; |
||
591 | document_iterator(std::unique_ptr<Document> &D) : Doc(&D) {} |
||
592 | |||
593 | bool operator==(const document_iterator &Other) const { |
||
594 | if (isAtEnd() || Other.isAtEnd()) |
||
595 | return isAtEnd() && Other.isAtEnd(); |
||
596 | |||
597 | return Doc == Other.Doc; |
||
598 | } |
||
599 | bool operator!=(const document_iterator &Other) const { |
||
600 | return !(*this == Other); |
||
601 | } |
||
602 | |||
603 | document_iterator operator++() { |
||
604 | assert(Doc && "incrementing iterator past the end."); |
||
605 | if (!(*Doc)->skip()) { |
||
606 | Doc->reset(nullptr); |
||
607 | } else { |
||
608 | Stream &S = (*Doc)->stream; |
||
609 | Doc->reset(new Document(S)); |
||
610 | } |
||
611 | return *this; |
||
612 | } |
||
613 | |||
614 | Document &operator*() { return **Doc; } |
||
615 | |||
616 | std::unique_ptr<Document> &operator->() { return *Doc; } |
||
617 | |||
618 | private: |
||
619 | bool isAtEnd() const { return !Doc || !*Doc; } |
||
620 | |||
621 | std::unique_ptr<Document> *Doc = nullptr; |
||
622 | }; |
||
623 | |||
624 | } // end namespace yaml |
||
625 | |||
626 | } // end namespace llvm |
||
627 | |||
628 | #endif // LLVM_SUPPORT_YAMLPARSER_H |