rapidyaml 0.16.0
parse and emit YAML, and do it fast
Loading...
Searching...
No Matches
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
22namespace c4 {
23namespace 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 */
225RYML_EXPORT id_type estimate_tree_capacity(csubstr src); // NOLINT(readability-redundant-declaration)
226
227
228
229/** @cond dev */
230struct FilterResult;
232typedef 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;
237struct ScannedBlock
238{
239 substr scalar;
240 size_t indentation;
241 BlockChomp_e chomp;
242};
243struct ScannedScalar
244{
245 substr scalar;
246 bool needs_filter;
247};
248/** store pending tag or anchor/ref annotations */
249struct 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 */
289template<class EventHandler>
291{
292public:
293
294 using handler_type = EventHandler;
295
296public:
297
298 /** @name construction and assignment */
299 /** @{ */
300
301 ParseEngine(EventHandler *evt_handler, ParserOptions const& opts={});
302 ~ParseEngine() noexcept;
303
306 ParseEngine& operator=(ParseEngine &&) noexcept;
307 ParseEngine& operator=(ParseEngine const&);
308
309 /** @} */
310
311public:
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
348public:
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
374public:
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
387public:
388
389 /** @name locations */
390 /** @{ */
391
392 /** Get the string starting at a particular location, to the end
393 * of the parsed source buffer. */
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
403public:
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
435private:
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
464public: // 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
486private:
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
556public:
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
566public:
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
594private:
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
608private:
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
629private:
630
631 void _prepare_locations();
632 void _resize_locations(size_t sz);
633 bool _locations_dirty() const;
634
635private:
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
650private:
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
675private:
676
677 ParserOptions m_options;
678
679public:
680
681 /** @cond dev */
682 EventHandler *C4_RESTRICT m_evt_handler; // NOLINT
683 /** @endcond */
684
685private:
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
696private:
697
698 size_t m_bom_len;
699 size_t m_bom_line;
700 Encoding_e m_encoding;
701
702private:
703
704 size_t *m_newline_offsets;
705 size_t m_newline_offsets_size;
706 size_t m_newline_offsets_capacity;
707
708public:
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 */
755C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wattributes")
756C4_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}
773C4_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_ */
FilterResult filter_scalar_plain(csubstr scalar, substr dst, size_t indentation)
filter a plain scalar
csubstr location_contents(Location const &loc) const
Get the string starting at a particular location, to the end of the parsed source buffer.
FilterResult filter_scalar_squoted(csubstr scalar, substr dst)
filter a single-quoted scalar
FilterResult filter_scalar_dquoted(csubstr scalar, substr dst)
filter a double-quoted scalar
void parse_json_in_place_ev(csubstr filename, substr src)
parse JSON in place, emitting events to the current handler
Location val_location(const char *val) const
Given a pointer to a buffer position, get the location.
FilterResult filter_scalar_plain_in_place(substr scalar, size_t cap, size_t indentation)
filter a plain scalar in place
FilterResult filter_scalar_squoted_in_place(substr scalar, size_t cap)
filter a single-quoted scalar in place
FilterResultExtending filter_scalar_dquoted_in_place(substr scalar, size_t cap)
filter a double-quoted scalar in place
Encoding_e encoding() const
Get the encoding of the latest YAML buffer parsed by this object.
ParserOptions const & options() const
Get the options used to build this parser object.
size_t locations_capacity() const
void parse_in_place_ev(csubstr filename, substr src)
parse YAML in place, emitting events to the current handler
csubstr source() const
Get the latest YAML buffer parsed by this object.
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
ParseEngine(EventHandler *evt_handler, ParserOptions const &opts={})
FilterResult filter_scalar_block_literal(csubstr scalar, substr dst, size_t indentation, BlockChomp_e chomp)
filter a block-literal scalar
id_type stack_capacity() const
Callbacks const & callbacks() const
Get the current callbacks in the parser.
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
csubstr filename() const
Get the name of the latest file parsed by this object.
void reserve_locations(size_t num_source_lines)
Reserve a certain capacity for the array used to track node locations in the source buffer.
FilterResult filter_scalar_block_folded(csubstr scalar, substr dst, size_t indentation, BlockChomp_e chomp)
filter a block-folded scalar
#define RYML_EXPORT
Definition export.hpp:18
forward declarations
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
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
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
basic_substring< char > substr
a mutable string view
Definition substr.hpp:2355
basic_substring< const char > csubstr
an immutable string view
Definition substr.hpp:2356
int ParserFlag_t
data type for ParserState_e
@ npos
a null string position
Definition common.hpp:138
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
@ UTF8
UTF8.
Definition common.hpp:144
@ NOBOM
No Byte Order Mark was found.
Definition common.hpp:143
enum c4::yml::Encoding_ Encoding_e
csubstr version()
Definition version.cpp:6
A c-style callbacks class to customize behavior on errors or allocation.
Definition common.hpp:374
Result for filtering a scalar which not fit in the intended memory.
Result for filtering a scalar which not fit in the intended memory.
holds a source or yaml file position, for example when an error is detected; See also location_format...
Definition common.hpp:229
Options to give to the ParseEngine to control its behavior.