rapidyaml  0.16.0
parseandemitYAML,anddoitfast
parse_engine.hpp
Go to the documentation of this file.
1 #ifndef C4_YML_PARSE_ENGINE_HPP_
2 #define C4_YML_PARSE_ENGINE_HPP_
3 
4 #ifndef C4_YML_PARSER_STATE_HPP_
6 #endif
7 #ifndef C4_YML_PARSE_OPTIONS_HPP_
9 #endif
10 #ifndef C4_YML_FWD_HPP_
11 #include "c4/yml/fwd.hpp"
12 #endif
13 
14 
15 #if defined(_MSC_VER)
16 # pragma warning(push)
17 # pragma warning(disable: 4251/*needs to have dll-interface to be used by clients of struct*/)
18 #endif
19 
20 // NOLINTBEGIN(hicpp-signed-bitwise)
21 
22 namespace c4 {
23 namespace yml {
24 
25 /** @addtogroup doc_parse
26  * @{ */
27 
28 /** @addtogroup doc_event_handlers Event Handlers
29  *
30  * @brief rapidyaml implements its parsing logic with a two-level
31  * model, where a @ref ParseEngine object reads through the YAML
32  * source, and dispatches events to an EventHandler bound to the @ref
33  * ParseEngine. Because @ref ParseEngine is templated on the event
34  * handler, the binding uses static polymorphism, without any virtual
35  * functions. The actual handler object can be changed at run time,
36  * (but of course needs to be the type of the template parameter).
37  * This is thus a very efficient architecture, and further enables the
38  * user to provide his own custom handler if he wishes to bypass the
39  * rapidyaml @ref Tree.
40  *
41  * The following handlers are implemented in this project:
42  *
43  * - @ref EventHandlerTree is the handler responsible for creating the
44  * ryml @ref Tree . This is part of the library.
45  *
46  * - Extra handlers (not part of the library, but provided as extra classes):
47  *
48  * - @ref extra::EventHandlerInts parses YAML into a contiguous
49  * integer array representing the YAML structure.
50  * - [play.yaml.com](https://play.yaml.com/)
51  * - [matrix.yaml.info/](https://matrix.yaml.info/)
52  * - the CI of this project.
53  *
54  *
55  * ### Event model
56  *
57  * The event model used by the parse engine and event handlers follows
58  * very closely the event model in the [YAML test
59  * suite](https://github.com/yaml/yaml-test-suite).
60  *
61  * Consider for example this YAML,
62  * ```yaml
63  * {foo: bar,foo2: bar2}
64  * ```
65  * which would produce these events in the test-suite parlance:
66  * ```
67  * +STR
68  * +DOC
69  * +MAP {}
70  * =VAL :foo
71  * =VAL :bar
72  * =VAL :foo2
73  * =VAL :bar2
74  * -MAP
75  * -DOC
76  * -STR
77  * ```
78  *
79  * For reference, the @ref ParseEngine object will produce this
80  * sequence of calls to its bound EventHandler:
81  * ```cpp
82  * handler.begin_stream();
83  * handler.begin_doc();
84  * handler.begin_map_val_flow();
85  * handler.set_key_scalar_plain("foo");
86  * handler.set_val_scalar_plain("bar");
87  * handler.add_sibling();
88  * handler.set_key_scalar_plain("foo2");
89  * handler.set_val_scalar_plain("bar2");
90  * handler.end_map();
91  * handler.end_doc();
92  * handler.end_stream();
93  * ```
94  *
95  * For many other examples of all areas of YAML and how ryml's parse
96  * model corresponds to the YAML standard model, refer to the [unit
97  * tests for the parse
98  * engine](https://github.com/biojppm/rapidyaml/tree/master/test/test_parse_engine.cpp).
99  *
100  *
101  * ### Special events
102  *
103  * Most of the parsing events adopted by rapidyaml in its event model
104  * are fairly obvious, but there are two less-obvious events requiring
105  * some explanation.
106  *
107  * These events exist to make it easier to parse some special YAML
108  * cases. They are called by the parser when a just-handled
109  * value/container is actually the first key of a new map:
110  *
111  * - `actually_val_is_first_key_of_new_map_flow()` (@ref EventHandlerTree::actually_val_is_first_key_of_new_map_flow() "see implementation in EventHandlerTree" / @ref extra::EventHandlerInts::actually_val_is_first_key_of_new_map_flow() "see implementation in EventHandlerInts")
112  * - `actually_val_is_first_key_of_new_map_block()` (@ref EventHandlerTree::actually_val_is_first_key_of_new_map_block() "see implementation in EventHandlerTree" / @ref extra::EventHandlerInts::actually_val_is_first_key_of_new_map_block() "see implementation in EventHandlerInts")
113  *
114  * For example, consider an implicit map inside a seq: `[a: b, c:
115  * d]` which is parsed as `[{a: b}, {c: d}]`. The standard event
116  * sequence for this YAML would be the following:
117  * ```cpp
118  * handler.begin_seq_val_flow();
119  * handler.begin_map_val_flow();
120  * handler.set_key_scalar_plain("a");
121  * handler.set_val_scalar_plain("b");
122  * handler.end_map();
123  * handler.add_sibling();
124  * handler.begin_map_val_flow();
125  * handler.set_key_scalar_plain("c");
126  * handler.set_val_scalar_plain("d");
127  * handler.end_map();
128  * handler.end_seq();
129  * ```
130  * The problem with this event sequence is that it forces the
131  * parser to delay setting the val scalar (in this case "a" and
132  * "c") until it knows whether the scalar is a key or a val. This
133  * would require the parser to store the scalar until this
134  * time. For instance, in the example above, the parser should
135  * delay setting "a" and "c", because they are in fact keys and
136  * not vals. Until then, the parser would have to store "a" and
137  * "c" in its internal state. The downside is that this complexity
138  * cost would apply even if there is no implicit map -- every val
139  * in a seq would have to be delayed until one of the
140  * disambiguating subsequent tokens `,-]:` is found.
141  * By calling this function, the parser can avoid this complexity,
142  * by preemptively setting the scalar as a val. Then a call to
143  * this function will create the map and rearrange the scalar as
144  * key. Now the cost applies only once: when a seqimap starts. So
145  * the following (easier and cheaper) event sequence below has the
146  * same effect as the event sequence above:
147  * ```cpp
148  * handler.begin_seq_val_flow();
149  * handler.set_val_scalar_plain("notmap");
150  * handler.set_val_scalar_plain("a"); // preemptively set "a" as val!
151  * handler.actually_as_new_map_key(); // create a map, move the "a" val as the key of the first child of the new map
152  * handler.set_val_scalar_plain("b"); // now "a" is a key and "b" the val
153  * handler.end_map();
154  * handler.set_val_scalar_plain("c"); // "c" also as val!
155  * handler.actually_as_block_flow(); // likewise
156  * handler.set_val_scalar_plain("d"); // now "c" is a key and "b" the val
157  * handler.end_map();
158  * handler.end_seq();
159  * ```
160  * This also applies to container keys (although ryml's tree
161  * cannot accomodate these): the parser can preemptively set a
162  * container as a val, and call this event to turn that container
163  * into a key. For example, consider this yaml:
164  * ```yaml
165  * [aa, bb]: [cc, dd]
166  * # ^ ^ ^
167  * # | | |
168  * # (2) (1) (3) <- event sequence
169  * ```
170  * The standard event sequence for this YAML would be the
171  * following:
172  * ```cpp
173  * handler.begin_map_val_block(); // (1)
174  * handler.begin_seq_key_flow(); // (2)
175  * handler.set_val_scalar_plain("aa");
176  * handler.add_sibling();
177  * handler.set_val_scalar_plain("bb");
178  * handler.end_seq();
179  * handler.begin_seq_val_flow(); // (3)
180  * handler.set_val_scalar_plain("cc");
181  * handler.add_sibling();
182  * handler.set_val_scalar_plain("dd");
183  * handler.end_seq();
184  * handler.end_map();
185  * ```
186  * The problem with the sequence above is that, reading from
187  * left-to-right, the parser can only detect the proper calls at
188  * (1) and (2) once it reaches (1) in the YAML source. So, the
189  * parser would have to buffer the entire event sequence starting
190  * from the beginning until it reaches (1). Using this function,
191  * the parser can do instead:
192  * ```cpp
193  * handler.begin_seq_val_flow(); // (2) -- preemptively as val!
194  * handler.set_val_scalar_plain("aa");
195  * handler.add_sibling();
196  * handler.set_val_scalar_plain("bb");
197  * handler.end_seq();
198  * handler.actually_as_new_map_key(); // (1) -- adjust when finding that the prev val was actually a key.
199  * handler.begin_seq_val_flow(); // (3) -- go on as before
200  * handler.set_val_scalar_plain("cc");
201  * handler.add_sibling();
202  * handler.set_val_scalar_plain("dd");
203  * handler.end_seq();
204  * handler.end_map();
205  * ```
206  * @{
207  * @}
208  */
209 
210 
211 /** Quickly inspect the source to estimate the number of nodes the
212  * resulting tree is likely to have. If a tree is empty before
213  * parsing, considerable time will be spent growing it, so calling
214  * this to reserve the tree size prior to parsing is likely to
215  * result in a time gain. We encourage using this method before
216  * parsing, but as always measure its impact in performance to
217  * obtain a good trade-off.
218  *
219  * @note since this method is meant for optimizing performance, it
220  * is approximate. The result may be actually smaller than the
221  * resulting number of nodes, notably if the YAML uses implicit
222  * maps as flow seq members as in `[these: are, individual:
223  * maps]`.
224  */
225 RYML_EXPORT id_type estimate_tree_capacity(csubstr src); // NOLINT(readability-redundant-declaration)
226 
227 
228 
229 /** @cond dev */
230 struct FilterResult;
231 struct FilterResultExtending;
232 typedef enum BlockChomp_ { // NOLINT
233  CHOMP_CLIP, //!< single newline at end (default)
234  CHOMP_STRIP, //!< no newline at end (-)
235  CHOMP_KEEP //!< all newlines from end (+)
236 } BlockChomp_e;
237 struct ScannedBlock
238 {
239  substr scalar;
240  size_t indentation;
241  BlockChomp_e chomp;
242 };
243 struct ScannedScalar
244 {
245  substr scalar;
246  bool needs_filter;
247 };
248 /** store pending tag or anchor/ref annotations */
249 struct Annotation
250 {
251  struct Entry
252  {
253  csubstr str;
254  size_t indentation;
255  size_t line;
256  csubstr orig;
257  };
258  Entry annotations[2];
259  uint8_t num_entries;
260 };
261 /** @endcond */
262 
263 
264 //-----------------------------------------------------------------------------
265 //-----------------------------------------------------------------------------
266 //-----------------------------------------------------------------------------
267 
268 /** This is the main driver of parsing logic: it scans the YAML or
269  * JSON source for tokens, and emits the appropriate sequence of
270  * parsing events to its event handler. The parse engine itself has no
271  * special limitations, and *can* accomodate containers as keys; it is the
272  * event handler may introduce additional constraints.
273  *
274  * There are two implemented handlers (see @ref doc_event_handlers,
275  * which has important notes about the event model):
276  *
277  * - @ref EventHandlerTree (see @ref doc_event_handlers_tree) is the
278  * handler responsible for creating the ryml @ref Tree
279  *
280  * - @ref extra::EventHandlerInts (see @ref doc_event_handlers_ints)
281  * is the handler responsible for emitting integer-coded events. It
282  * is intended for implementing fully-conformant parsing in other
283  * programming languages (integration is currently under work for
284  * [YamlScript](https://github.com/yaml/yamlscript) and
285  * [go-yaml](https://github.com/yaml/go-yaml/)). It is not part of
286  * the library and is not installed.
287  *
288  */
289 template<class EventHandler>
290 class ParseEngine
291 {
292 public:
293 
294  using handler_type = EventHandler;
295 
296 public:
297 
298  /** @name construction and assignment */
299  /** @{ */
300 
301  ParseEngine(EventHandler *evt_handler, ParserOptions const& opts={});
302  ~ParseEngine() noexcept;
303 
304  ParseEngine(ParseEngine &&) noexcept;
305  ParseEngine(ParseEngine const&);
306  ParseEngine& operator=(ParseEngine &&) noexcept;
307  ParseEngine& operator=(ParseEngine const&);
308 
309  /** @} */
310 
311 public:
312 
313  /** @name modifiers */
314  /** @{ */
315 
316  /** Reserve a certain capacity for the parsing stack.
317  * This should be larger than the expected depth of the parsed
318  * YAML tree.
319  *
320  * The parsing stack is the only (potential) heap memory used
321  * directly by the parser.
322  *
323  * If the requested capacity is below the default
324  * stack size of 16, the memory is used directly in the parser
325  * object; otherwise it will be allocated from the heap.
326  *
327  * @note this reserves memory only for the parser itself; all the
328  * allocations for the parsed tree will go through the tree's
329  * allocator (when different).
330  *
331  * @note for maximum efficiency, the tree and the arena can (and
332  * should) also be reserved. */
333  void reserve_stack(id_type capacity)
334  {
335  RYML_ASSERT_BASIC_(m_evt_handler);
336  m_evt_handler->m_stack.reserve(capacity);
337  }
338 
339  /** Reserve a certain capacity for the array used to track node
340  * locations in the source buffer. */
341  void reserve_locations(size_t num_source_lines)
342  {
343  _resize_locations(num_source_lines);
344  }
345 
346  /** @} */
347 
348 public:
349 
350  /** @name getters */
351  /** @{ */
352 
353  /** Get the options used to build this parser object. */
354  ParserOptions const& options() const { return m_options; }
355 
356  /** Get the current callbacks in the parser. */
357  Callbacks const& callbacks() const { RYML_ASSERT_BASIC_(m_evt_handler); return m_evt_handler->m_stack.m_callbacks; }
358 
359  /** Get the name of the latest file parsed by this object. */
360  csubstr filename() const { return m_evt_handler->m_curr ? m_evt_handler->m_curr->pos.name : csubstr{}; }
361 
362  /** Get the latest YAML buffer parsed by this object. */
363  csubstr source() const { return m_evt_handler ? m_evt_handler->m_src : csubstr{}; }
364 
365  /** Get the encoding of the latest YAML buffer parsed by this object.
366  * If no encoding was specified, UTF8 is assumed as per the YAML standard. */
367  Encoding_e encoding() const { return m_encoding != NOBOM ? m_encoding : UTF8; }
368 
369  id_type stack_capacity() const { RYML_ASSERT_BASIC_(m_evt_handler); return m_evt_handler->m_stack.capacity(); }
370  size_t locations_capacity() const { return m_newline_offsets_capacity; }
371 
372  /** @} */
373 
374 public:
375 
376  /** @name parse methods */
377  /** @{ */
378 
379  /** parse YAML in place, emitting events to the current handler */
381 
382  /** parse JSON in place, emitting events to the current handler */
384 
385  /** @} */
386 
387 public:
388 
389  /** @name locations */
390  /** @{ */
391 
392  /** Get the string starting at a particular location, to the end
393  * of the parsed source buffer. */
394  csubstr location_contents(Location const& loc) const;
395 
396  /** Given a pointer to a buffer position, get the location.
397  * @param[in] val must be pointing to somewhere in the source
398  * buffer that was last parsed by this object. */
399  Location val_location(const char *val) const;
400 
401  /** @} */
402 
403 public:
404 
405  /** @name scalar filtering */
406  /** @{*/
407 
408  /** filter a plain scalar */
409  FilterResult filter_scalar_plain(csubstr scalar, substr dst, size_t indentation);
410  /** filter a plain scalar in place */
411  FilterResult filter_scalar_plain_in_place(substr scalar, size_t cap, size_t indentation);
412 
413  /** filter a single-quoted scalar */
415  /** filter a single-quoted scalar in place */
417 
418  /** filter a double-quoted scalar */
420  /** filter a double-quoted scalar in place */
422 
423  /** filter a block-literal scalar */
424  FilterResult filter_scalar_block_literal(csubstr scalar, substr dst, size_t indentation, BlockChomp_e chomp);
425  /** filter a block-literal scalar in place */
426  FilterResult filter_scalar_block_literal_in_place(substr scalar, size_t cap, size_t indentation, BlockChomp_e chomp);
427 
428  /** filter a block-folded scalar */
429  FilterResult filter_scalar_block_folded(csubstr scalar, substr dst, size_t indentation, BlockChomp_e chomp);
430  /** filter a block-folded scalar in place */
431  FilterResult filter_scalar_block_folded_in_place(substr scalar, size_t cap, size_t indentation, BlockChomp_e chomp);
432 
433  /** @} */
434 
435 private:
436 
437  bool _is_doc_begin(csubstr s);
438  bool _is_doc_end(csubstr s);
439 
440  bool _scan_scalar_plain_blck(ScannedScalar *C4_RESTRICT sc, size_t indentation);
441  bool _scan_scalar_plain_seq_flow(ScannedScalar *C4_RESTRICT sc);
442  bool _scan_scalar_plain_seq_blck(ScannedScalar *C4_RESTRICT sc);
443  bool _scan_scalar_plain_map_flow(ScannedScalar *C4_RESTRICT sc);
444  bool _scan_scalar_plain_map_blck(ScannedScalar *C4_RESTRICT sc);
445  bool _scan_scalar_map_json(ScannedScalar *C4_RESTRICT sc);
446  bool _scan_scalar_seq_json(ScannedScalar *C4_RESTRICT sc);
447  bool _scan_scalar_plain_unk(ScannedScalar *C4_RESTRICT sc);
448  bool _is_valid_start_scalar_plain_flow(csubstr s);
449  bool _is_valid_start_scalar_plain_flow_check_block_token(csubstr s);
450  bool _is_valid_start_scalar_plain_flow_check_qmrk(csubstr s);
451  bool _scan_scalar_plain_handle_newline(csubstr s, size_t offs);
452  void _check_valid_newline_in_quoted_scalar();
453 
454  ScannedScalar _scan_scalar_squot();
455  ScannedScalar _scan_scalar_dquot();
456 
457  void _scan_block(ScannedBlock *C4_RESTRICT sb, size_t indref);
458  csubstr _scan_anchor();
459  csubstr _scan_ref_seq();
460  csubstr _scan_ref_map();
461  csubstr _scan_tag();
462  csubstr _scan_tag(csubstr *orig);
463 
464 public: // exposed for testing
465 
466  /** @cond dev */
467  csubstr _filter_scalar_plain(substr s, size_t indentation);
468  csubstr _filter_scalar_squot(substr s);
469  csubstr _filter_scalar_dquot(substr s);
470  csubstr _filter_scalar_literal(substr s, size_t indentation, BlockChomp_e chomp);
471  csubstr _filter_scalar_folded(substr s, size_t indentation, BlockChomp_e chomp);
472  csubstr _move_scalar_left_and_add_newline(substr s);
473 
474  csubstr _maybe_filter_key_scalar_plain(ScannedScalar const& sc, size_t indendation);
475  csubstr _maybe_filter_val_scalar_plain(ScannedScalar const& sc, size_t indendation);
476  csubstr _maybe_filter_key_scalar_squot(ScannedScalar const& sc);
477  csubstr _maybe_filter_val_scalar_squot(ScannedScalar const& sc);
478  csubstr _maybe_filter_key_scalar_dquot(ScannedScalar const& sc);
479  csubstr _maybe_filter_val_scalar_dquot(ScannedScalar const& sc);
480  csubstr _maybe_filter_key_scalar_literal(ScannedBlock const& sb);
481  csubstr _maybe_filter_val_scalar_literal(ScannedBlock const& sb);
482  csubstr _maybe_filter_key_scalar_folded(ScannedBlock const& sb);
483  csubstr _maybe_filter_val_scalar_folded(ScannedBlock const& sb);
484  /** @endcond */
485 
486 private:
487 
488  void _handle_map_block();
489  bool _handle_map_block_qmrk();
490  bool _handle_map_block_rkcl();
491  void _handle_seq_block();
492  void _handle_map_flow();
493  void _handle_seq_flow();
494  void _handle_seq_imap();
495  void _handle_map_json();
496  void _handle_seq_json();
497 
498  void _handle_unk();
499  void _handle_unk_json();
500 
501  void _handle_usty();
502 
503  void _handle_flow_skip_whitespace();
504  void _handle_flow_line_beginning();
505 
506  size_t _handle_unk_check_left_tokens(size_t realindent, size_t col, bool skip_annotations=true);
507  void _handle_unk_get_first_non_pending_token_pos(csubstr s, size_t *indent, size_t *first_non_token_pos);
508  void _handle_unk_begin_doc();
509 
510  size_t _handle_block_skip_leading_whitespace();
511  C4_ALWAYS_INLINE
512  size_t _handle_block_get_whitespace_mark() const noexcept { return m_evt_handler->m_curr->pos.offset; }
513  void _handle_block_check_leading_tabs(size_t prev_mark) { return _handle_block_check_leading_tabs(prev_mark, m_evt_handler->m_curr->pos.offset); }
514  void _handle_block_check_leading_tabs(size_t start_mark, size_t end_mark);
515 
516  void _end_map_flow();
517  void _end_seq_flow();
518  void _end_map_blck();
519  void _end_seq_blck();
520  void _end2_map();
521  void _end2_seq();
522  void _end_flow_container(size_t orig_indent, bool multiline);
523  void _flow_container_was_a_key(size_t orig_indent);
524 
525  void _begin2_doc();
526  void _begin2_doc_expl();
527  void _end2_doc();
528  void _end2_doc_expl();
529  void _check_doc_end_tokens() const;
530 
531  void _maybe_begin_doc();
532  void _maybe_end_doc();
533 
534  void _start_doc_suddenly();
535  void _end_doc_suddenly();
536  void _end_doc_suddenly__pop();
537  void _check_trailing_doc_token();
538  void _end_stream();
539 
540  void _set_indentation(size_t indentation) noexcept;
541  void _save_indentation();
542  void _mark_seqflow_val_end() noexcept;
543  void _handle_indentation_pop_from_block_seq();
544  void _handle_indentation_pop_from_block_map();
545  void _handle_indentation_pop(ParserState const* dst);
546 
547  void _maybe_skip_comment();
548  void _maybe_skip_comment_strict();
549  void _skip_comment();
550  void _maybe_skip_whitespace_tokens();
551  void _maybe_skipchars(char c);
552  template<size_t N>
553  void _skipchars(const char (&chars)[N]);
554  bool _maybe_scan_following_colon() noexcept;
555 
556 public:
557 
558  /** @cond dev */
559  template<class FilterProcessor> auto _filter_plain(FilterProcessor &C4_RESTRICT proc, size_t indentation) -> decltype(proc.result());
560  template<class FilterProcessor> auto _filter_squoted(FilterProcessor &C4_RESTRICT proc) -> decltype(proc.result());
561  template<class FilterProcessor> auto _filter_dquoted(FilterProcessor &C4_RESTRICT proc) -> decltype(proc.result());
562  template<class FilterProcessor> auto _filter_block_literal(FilterProcessor &C4_RESTRICT proc, size_t indentation, BlockChomp_e chomp) -> decltype(proc.result());
563  template<class FilterProcessor> auto _filter_block_folded(FilterProcessor &C4_RESTRICT proc, size_t indentation, BlockChomp_e chomp) -> decltype(proc.result());
564  /** @endcond */
565 
566 public:
567 
568  /** @cond dev */
569  template<class FilterProcessor> void _filter_nl_plain(FilterProcessor &C4_RESTRICT proc, size_t indentation);
570  template<class FilterProcessor> void _filter_nl_squoted(FilterProcessor &C4_RESTRICT proc);
571  template<class FilterProcessor> void _filter_nl_dquoted(FilterProcessor &C4_RESTRICT proc);
572 
573  template<class FilterProcessor> bool _filter_ws_handle_to_first_non_space(FilterProcessor &C4_RESTRICT proc);
574  template<class FilterProcessor> void _filter_ws_copy_trailing(FilterProcessor &C4_RESTRICT proc);
575  template<class FilterProcessor> void _filter_ws_skip_trailing(FilterProcessor &C4_RESTRICT proc);
576 
577  template<class FilterProcessor> void _filter_dquoted_backslash(FilterProcessor &C4_RESTRICT proc);
578  template<class FilterProcessor> void _filter_dquoted_backslash_decode(FilterProcessor &C4_RESTRICT proc, size_t sz);
579 
580  template<class FilterProcessor> void _filter_chomp(FilterProcessor &C4_RESTRICT proc, BlockChomp_e chomp, size_t indentation);
581  template<class FilterProcessor> size_t _handle_all_whitespace(FilterProcessor &C4_RESTRICT proc, BlockChomp_e chomp);
582  template<class FilterProcessor> size_t _extend_to_chomp(FilterProcessor &C4_RESTRICT proc, size_t contents_len);
583  template<class FilterProcessor> void _filter_block_indentation(FilterProcessor &C4_RESTRICT proc, size_t indentation);
584  template<class FilterProcessor> void _filter_block_folded_newlines(FilterProcessor &C4_RESTRICT proc, size_t indentation, size_t len);
585  template<class FilterProcessor> size_t _filter_block_folded_newlines_compress(FilterProcessor &C4_RESTRICT proc, size_t num_newl, size_t wpos_at_first_newl);
586  template<class FilterProcessor> void _filter_block_folded_newlines_leading(FilterProcessor &C4_RESTRICT proc, size_t indentation, size_t len);
587  template<class FilterProcessor> void _filter_block_folded_indented_block(FilterProcessor &C4_RESTRICT proc, size_t indentation, size_t len, size_t curr_indentation) noexcept;
588 
589  substr _alloc_arena(size_t len, substr *relocated=nullptr);
590  substr _alloc_arena(size_t len, csubstr *relocated) { return _alloc_arena(len, reinterpret_cast<substr*>(relocated)); } // NOLINT
591 
592  /** @endcond */
593 
594 private:
595 
596  void _line_progressed(size_t ahead);
597  void _line_ended();
598  void _line_ended_undo();
599 
600  bool _finished_file() const;
601  bool _finished_line() const;
602 
603  void _scan_line();
604  substr _peek_next_line(size_t pos=npos) const;
605 
606  void _relocate_arena(csubstr prev_arena, substr next_arena, substr *other_string=nullptr);
607 
608 private:
609 
610  C4_ALWAYS_INLINE substr _buf() const noexcept { return m_evt_handler->m_src; }
611 
612  C4_ALWAYS_INLINE bool has_all(ParserFlag_t f) const noexcept { return (m_evt_handler->m_curr->flags & f) == f; }
613  C4_ALWAYS_INLINE bool has_any(ParserFlag_t f) const noexcept { return (m_evt_handler->m_curr->flags & f) != 0; }
614  C4_ALWAYS_INLINE bool has_none(ParserFlag_t f) const noexcept { return (m_evt_handler->m_curr->flags & f) == 0; }
615  static C4_ALWAYS_INLINE bool has_all(ParserFlag_t f, ParserState const* C4_RESTRICT s) noexcept { return (s->flags & f) == f; }
616  static C4_ALWAYS_INLINE bool has_any(ParserFlag_t f, ParserState const* C4_RESTRICT s) noexcept { return (s->flags & f) != 0; }
617  static C4_ALWAYS_INLINE bool has_none(ParserFlag_t f, ParserState const* C4_RESTRICT s) noexcept { return (s->flags & f) == 0; }
618 
619  #ifndef RYML_DBG
620  C4_ALWAYS_INLINE void add_flags(ParserFlag_t on) noexcept { m_evt_handler->m_curr->flags |= on; }
621  C4_ALWAYS_INLINE void addrem_flags(ParserFlag_t on, ParserFlag_t off) noexcept { m_evt_handler->m_curr->flags &= ~off; m_evt_handler->m_curr->flags |= on; }
622  C4_ALWAYS_INLINE void rem_flags(ParserFlag_t off) noexcept { m_evt_handler->m_curr->flags &= ~off; }
623  #else
624  C4_ALWAYS_INLINE void add_flags(ParserFlag_t on);
625  C4_ALWAYS_INLINE void addrem_flags(ParserFlag_t on, ParserFlag_t off);
626  C4_ALWAYS_INLINE void rem_flags(ParserFlag_t off);
627  #endif
628 
629 private:
630 
631  void _prepare_locations();
632  void _resize_locations(size_t sz);
633  bool _locations_dirty() const;
634 
635 private:
636 
637  void _reset();
638  void _free();
639  void _clr();
640 
641  template<class ...Args> C4_NORETURN C4_NO_INLINE void _err(Location const& cpploc, const char *fmt, Args const& ...args) const;
642  template<class ...Args> C4_NORETURN C4_NO_INLINE void _err(Location const& cpploc, Location const& ymlloc, const char *fmt, Args const& ...args) const;
643  #ifdef RYML_DBG
644  template<class ...Args> C4_NO_INLINE void _dbg(csubstr fmt, Args const& ...args) const;
645  template<class DumpFn> C4_NO_INLINE void _fmt_msg(DumpFn &&dumpfn) const;
646  C4_NO_INLINE void _print_state_stack() const;
647  C4_NO_INLINE void _print_state_stack(substr buf) const;
648  #endif
649 
650 private:
651 
652  void _handle_colon();
653  void _add_annotation(Annotation *C4_RESTRICT dst, csubstr str, size_t indentation, size_t line);
654  void _add_annotation(Annotation *C4_RESTRICT dst, csubstr str, size_t indentation, size_t line, csubstr orig);
655  void _add_annotation(Annotation *C4_RESTRICT dst, csubstr str);
656  C4_ALWAYS_INLINE void _clear_annotations(Annotation *C4_RESTRICT dst) noexcept { dst->num_entries = 0; }
657  bool _annotations_require_key_container() const;
658  bool _handle_annotations_before_unexpected_flow_token_rkey();
659  void _handle_annotations_before_blck_key_scalar();
660  void _handle_annotations_before_blck_val_scalar();
661  void _handle_annotations_before_start_mapblck(size_t current_line);
662  void _handle_annotations_before_start_mapblck_as_key();
663  void _handle_annotations_and_indentation_after_start_mapblck(size_t key_indentation, size_t key_line);
664  size_t _select_indentation_from_annotations(size_t val_indentation, size_t val_line);
665  uint32_t _get_annotations_same_line(csubstr token_soup, csubstr * first, csubstr * second) const;
666  void _handle_keyref(csubstr alias);
667  void _handle_valref(csubstr alias);
668  csubstr _resolve_tag(csubstr tag);
669  void _handle_directive(csubstr rem);
670  bool _validate_directive_yaml(csubstr *C4_RESTRICT directive, csubstr *C4_RESTRICT version) const;
671  bool _validate_directive_tag(csubstr *C4_RESTRICT directive, csubstr *C4_RESTRICT handle, csubstr *C4_RESTRICT prefix) const;
672  bool _handle_bom();
673  void _handle_bom(Encoding_e enc);
674 
675 private:
676 
677  ParserOptions m_options;
678 
679 public:
680 
681  /** @cond dev */
682  EventHandler *C4_RESTRICT m_evt_handler; // NOLINT
683  /** @endcond */
684 
685 private:
686 
687  Annotation m_pending_anchors;
688  Annotation m_pending_tags;
689 
690  bool m_has_directives_yaml;
691  bool m_has_directives;
692  bool m_doc_empty;
693  size_t m_prev_colon;
694  size_t m_prev_val_end;
695 
696 private:
697 
698  size_t m_bom_len;
699  size_t m_bom_line;
700  Encoding_e m_encoding;
701 
702 private:
703 
704  size_t *m_newline_offsets;
705  size_t m_newline_offsets_size;
706  size_t m_newline_offsets_capacity;
707 
708 public:
709 
710  // deprecated methods
711 
712  /** @cond dev */
713  RYML_DEPRECATED("filter arena no longer needed") size_t filter_arena_capacity() const { return 0u; } // LCOV_EXCL_LINE
714  RYML_DEPRECATED("filter arena no longer needed") void reserve_filter_arena(size_t) {} // LCOV_EXCL_LINE
715 
716  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_place(csubstr filename, substr yaml, Tree *t, size_t node_id);
717  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_place( substr yaml, Tree *t, size_t node_id);
718  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_place(csubstr filename, substr yaml, Tree *t );
719  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_place( substr yaml, Tree *t );
720  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_place(csubstr filename, substr yaml, NodeRef node );
721  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_place( substr yaml, NodeRef node );
722  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, Tree>::type parse_in_place(csubstr filename, substr yaml );
723  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, Tree>::type parse_in_place( substr yaml );
724  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena(csubstr filename, csubstr yaml, Tree *t, size_t node_id);
725  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena( csubstr yaml, Tree *t, size_t node_id);
726  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena(csubstr filename, csubstr yaml, Tree *t );
727  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena( csubstr yaml, Tree *t );
728  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena(csubstr filename, csubstr yaml, NodeRef node );
729  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena( csubstr yaml, NodeRef node );
730  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, Tree>::type parse_in_arena(csubstr filename, csubstr yaml );
731  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the function in parse.hpp.") typename std::enable_if<U::is_wtree, Tree>::type parse_in_arena( csubstr yaml );
732  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena(csubstr filename, substr yaml, Tree *t, size_t node_id);
733  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena( substr yaml, Tree *t, size_t node_id);
734  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena(csubstr filename, substr yaml, Tree *t );
735  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena( substr yaml, Tree *t );
736  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena(csubstr filename, substr yaml, NodeRef node );
737  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena( substr yaml, NodeRef node );
738  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, Tree>::type parse_in_arena(csubstr filename, substr yaml );
739  template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, Tree>::type parse_in_arena( substr yaml );
740 
741  template<class U>
742  RYML_DEPRECATED("moved to Tree::location(Parser const&). deliberately undefined here.")
743  auto location(Tree const&, id_type node) const -> typename std::enable_if<U::is_wtree, Location>::type;
744 
745  template<class U>
746  RYML_DEPRECATED("moved to ConstNodeRef::location(Parser const&), deliberately undefined here.")
747  auto location(ConstNodeRef const&) const -> typename std::enable_if<U::is_wtree, Location>::type;
748  /** @endcond */
749 
750 };
751 
752 /** @} */
753 
754 /** @cond dev */
755 C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wattributes")
756 C4_NO_INLINE inline size_t _find_last_newline_and_larger_indentation(csubstr s, size_t indentation) noexcept
757 {
758  if(indentation + 1 > s.len)
759  return npos;
760  for(size_t i = s.len-indentation-1; i != size_t(-1); --i) // NOLINT
761  {
762  if(s.str[i] == '\n')
763  {
764  csubstr rem = s.sub(i + 1);
765  size_t first = rem.first_not_of(' ');
766  first = (first != npos) ? first : rem.len;
767  if(first > indentation)
768  return i;
769  }
770  }
771  return npos;
772 }
773 C4_SUPPRESS_WARNING_GCC_POP
774 /** @endcond */
775 
776 } // namespace yml
777 } // namespace c4
778 
779 // NOLINTEND(hicpp-signed-bitwise)
780 
781 #if defined(_MSC_VER)
782 # pragma warning(pop)
783 #endif
784 
785 #endif /* C4_YML_PARSE_ENGINE_HPP_ */
size_t locations_capacity() const
enum c4::yml::Encoding_ Encoding_e
FilterResult filter_scalar_block_literal(csubstr scalar, substr dst, size_t indentation, BlockChomp_e chomp)
filter a block-literal scalar
FilterResult filter_scalar_block_folded(csubstr scalar, substr dst, size_t indentation, BlockChomp_e chomp)
filter a block-folded scalar
Location val_location(const char *val) const
Given a pointer to a buffer position, get the location.
FilterResult filter_scalar_block_folded_in_place(substr scalar, size_t cap, size_t indentation, BlockChomp_e chomp)
filter a block-folded scalar in place
void reserve_stack(id_type capacity)
Reserve a certain capacity for the parsing stack.
id_type stack_capacity() const
A c-style callbacks class to customize behavior on errors or allocation.
Definition: common.hpp:373
A parser event handler that creates a compact representation of the YAML tree in a contiguous buffer ...
forward declarations
FilterResult filter_scalar_plain_in_place(substr scalar, size_t cap, size_t indentation)
filter a plain scalar in place
const char * str
Definition: node_type.cpp:70
FilterResult filter_scalar_squoted_in_place(substr scalar, size_t cap)
filter a single-quoted scalar in place
ParseEngine< EventHandlerTree > Parser
This is the main ryml parser, where the parser events are handled to create a ryml tree (see Event Ha...
Definition: fwd.hpp:19
FilterResultExtending filter_scalar_dquoted_in_place(substr scalar, size_t cap)
filter a double-quoted scalar in place
void parse_in_place_ev(csubstr filename, substr src)
parse YAML in place, emitting events to the current handler
void parse_json_in_place_ev(csubstr filename, substr src)
parse JSON in place, emitting events to the current handler
void parse_in_arena(Parser *parser, csubstr filename, csubstr yaml, Tree *tree, id_type node_id)
(1) parse YAML into an existing tree node. The filename will be used in any error messages arising du...
Definition: parse.cpp:209
csubstr source() const
Get the latest YAML buffer parsed by this object.
ParserOptions const & options() const
Get the options used to build this parser object.
RYML_ID_TYPE id_type
The type of a node id in the YAML tree; to override the default type, define the macro RYML_ID_TYPE t...
Definition: common.hpp:124
Options to give to the ParseEngine to control its behavior.
UTF8.
Definition: common.hpp:144
void reserve_locations(size_t num_source_lines)
Reserve a certain capacity for the array used to track node locations in the source buffer...
basic_substring sub(size_t first) const noexcept
return [first,len[
Definition: substr.hpp:502
FilterResult filter_scalar_block_literal_in_place(substr scalar, size_t cap, size_t indentation, BlockChomp_e chomp)
filter a block-literal scalar in place
basic_substring< char > substr
a mutable string view
Definition: substr.hpp:2355
a null string position
Definition: common.hpp:138
basic_substring< const char > csubstr
an immutable string view
Definition: substr.hpp:2356
holds a source or yaml file position, for example when an error is detected; See also location_format...
Definition: common.hpp:228
No Byte Order Mark was found.
Definition: common.hpp:143
Encoding_e encoding() const
Get the encoding of the latest YAML buffer parsed by this object.
id_type estimate_tree_capacity(csubstr src)
Quickly inspect the source to estimate the number of nodes the resulting tree is likely to have...
Definition: parse.cpp:254
This is the main driver of parsing logic: it scans the YAML or JSON source for tokens, and emits the appropriate sequence of parsing events to its event handler.
Definition: fwd.hpp:17
csubstr filename() const
Get the name of the latest file parsed by this object.
csubstr location_contents(Location const &loc) const
Get the string starting at a particular location, to the end of the parsed source buffer...
Callbacks const & callbacks() const
Get the current callbacks in the parser.
Result for filtering a scalar which not fit in the intended memory.
void parse_in_place(Parser *parser, csubstr filename, substr yaml, Tree *tree, id_type node_id)
(1) parse YAML into an existing tree node.
Definition: parse.cpp:165
Result for filtering a scalar which not fit in the intended memory.
#define RYML_EXPORT
Definition: export.hpp:18
csubstr version()
Definition: version.cpp:6
size_t first_not_of(const C c) const
Definition: substr.hpp:993
FilterResult filter_scalar_squoted(csubstr scalar, substr dst)
filter a single-quoted scalar
FilterResult filter_scalar_plain(csubstr scalar, substr dst, size_t indentation)
filter a plain scalar
FilterResult filter_scalar_dquoted(csubstr scalar, substr dst)
filter a double-quoted scalar
int ParserFlag_t
data type for ParserState_e
ParseEngine(EventHandler *evt_handler, ParserOptions const &opts={})