rapidyaml  0.11.1
parse and emit YAML, and do it fast
event_handler_testsuite.hpp
Go to the documentation of this file.
1 #ifndef _C4_YML_EXTRA_EVENT_HANDLER_TESTSUITE_HPP_
2 #define _C4_YML_EXTRA_EVENT_HANDLER_TESTSUITE_HPP_
3 
4 #ifndef RYML_SINGLE_HEADER
5 #ifndef _C4_YML_EVENT_HANDLER_STACK_HPP_
7 #endif
8 #endif
9 #ifndef _C4_YML_EXTRA_STRING_HPP_
10 #include "c4/yml/extra/string.hpp"
11 #endif
12 
13 C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
14 C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")
15 C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
16 
17 
18 namespace c4 {
19 namespace yml {
20 namespace extra {
21 
22 
23 /** @addtogroup doc_event_handlers
24  * @{ */
25 
26 
27 /** @cond dev */
28 struct EventHandlerTestSuiteState : public ParserState
29 {
30  NodeData ev_data;
31 };
32 void append_scalar_escaped(extra::string *s, csubstr val);
33 /** @endcond */
34 
35 /** This event produces standard YAML events as used in the
36  * [YAML test suite](https://github.com/yaml/yaml-test-suite).
37  * See the documentation for @ref doc_event_handlers, which has
38  * important notes about the event model used by rapidyaml.
39  *
40  * This class is used only in the CI of this project, and in the
41  * application used as part of the [standard YAML
42  * playground](https://play.yaml.io/main/parser). It is not part of
43  * the library and is not installed. *
44  */
45 struct EventHandlerTestSuite : public EventHandlerStack<EventHandlerTestSuite, EventHandlerTestSuiteState>
46 {
47 
48  /** @name types
49  * @{ */
50 
51  // our internal state must inherit from parser state
52  using state = EventHandlerTestSuiteState;
53 
55 
56  /** @} */
57 
58 public:
59 
60  /** @cond dev */
61  EventSink *C4_RESTRICT m_sink;
62  extra::string_vector m_val_buffers;
63  extra::string m_key_tag_buf;
64  extra::string m_val_tag_buf;
65  TagDirective m_tag_directives[RYML_MAX_TAG_DIRECTIVES];
66  bool m_has_yaml_directive;
67  extra::string m_arena;
68  bool m_has_docs;
69  csubstr m_src;
70 
71  // undefined at the end
72  #define _enable_(bits) _enable__<bits>()
73  #define _disable_(bits) _disable__<bits>()
74  #define _has_any_(bits) _has_any__<bits>()
75 
76  void _dbg_print() const
77  {
78  #ifdef RYML_DBG
79  auto indent = [](id_type n){
80  for(id_type level = 0; level < n; ++level)
81  {
82  _dbg_printf(" ");
83  }
84  };
85  for(id_type i = 0; i < m_stack.size(); ++i)
86  {
87  csubstr const& str = _buf_(i);
88  indent(i);
89  _dbg_printf("[{}]\n", i);
90  for(csubstr ln : str.split('\n'))
91  {
92  indent(i);
93  _dbg_printf("{}\n", ln);
94  }
95  }
96  #endif
97  }
98  /** @endcond */
99 
100 public:
101 
102  /** @name construction and resetting
103  * @{ */
104 
105  EventHandlerTestSuite() : EventHandlerStack(), m_sink(), m_val_buffers(), m_key_tag_buf(), m_val_tag_buf(), m_tag_directives(), m_has_yaml_directive(), m_arena(), m_has_docs(), m_src() {}
106  EventHandlerTestSuite(Callbacks const& cb) : EventHandlerStack(cb), m_sink(), m_val_buffers(), m_key_tag_buf(), m_val_tag_buf(), m_tag_directives(), m_has_yaml_directive(), m_arena(), m_has_docs(), m_src() {}
107  EventHandlerTestSuite(EventSink *sink, Callbacks const& cb) : EventHandlerStack(cb), m_sink(sink), m_val_buffers(), m_key_tag_buf(), m_val_tag_buf(), m_tag_directives(), m_has_yaml_directive(), m_arena(), m_has_docs(), m_src()
108  {
109  reset();
110  }
112 
113  void reset()
114  {
115  _stack_reset_root();
116  m_curr->flags |= RUNK|RTOP;
117  m_has_yaml_directive = false;
118  for(TagDirective &td : m_tag_directives)
119  td = {};
120  m_val_buffers.clear();
121  m_val_buffers.resize((size_t)m_stack.size());
122  m_arena.clear();
123  m_arena.reserve(1024);
124  m_key_tag_buf.resize(256);
125  m_val_tag_buf.resize(256);
126  m_has_docs = false;
127  m_src = {};
128  }
129 
130  /** @} */
131 
132 public:
133 
134  /** @name parse events
135  * @{ */
136 
137  void start_parse(const char* filename, csubstr src, detail::pfn_relocate_arena relocate_arena, void *relocate_arena_data)
138  {
139  this->_stack_start_parse(filename, src, relocate_arena, relocate_arena_data);
140  }
141 
143  {
144  if((_num_tag_directives() || m_has_yaml_directive) && !m_has_docs)
145  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "directives cannot be used without a document");
146  this->_stack_finish_parse();
147  }
148 
150  {
151  while(m_stack.size() > 1)
152  _pop();
153  _buf_flush_();
154  }
155 
156  /** @} */
157 
158 public:
159 
160  /** @name YAML stream events */
161  /** @{ */
162 
164  {
165  _send_("+STR\n");
166  }
167 
168  void end_stream()
169  {
170  _send_("-STR\n");
171  _buf_flush_();
172  }
173 
174  /** @} */
175 
176 public:
177 
178  /** @name YAML document events */
179  /** @{ */
180 
181  /** implicit doc start (without ---) */
182  void begin_doc()
183  {
184  _c4dbgp("begin_doc");
185  if(_stack_should_push_on_begin_doc())
186  {
187  _c4dbgp("push!");
188  _push();
189  _enable_(DOC);
190  }
191  _send_("+DOC\n");
192  m_has_docs = true;
193  }
194  /** implicit doc end (without ...) */
195  void end_doc()
196  {
197  _c4dbgp("end_doc");
198  _send_("-DOC\n");
199  if(_stack_should_pop_on_end_doc())
200  {
201  _c4dbgp("pop!");
202  _pop();
203  }
204  }
205 
206  /** explicit doc start, with --- */
208  {
209  _c4dbgp("begin_doc_expl");
210  if(_stack_should_push_on_begin_doc())
211  {
212  _c4dbgp("push!");
213  _push();
214  }
215  _send_("+DOC ---\n");
216  _enable_(DOC);
217  m_has_docs = true;
218  }
219  /** explicit doc end, with ... */
221  {
222  _c4dbgp("end_doc_expl");
223  _send_("-DOC ...\n");
224  if(_stack_should_pop_on_end_doc())
225  {
226  _c4dbgp("pop!");
227  _pop();
228  }
229  m_has_yaml_directive = false;
230  }
231 
232  /** @} */
233 
234 public:
235 
236  /** @name YAML map functions */
237  /** @{ */
238 
240  {
241  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
242  _send_("+MAP {}");
243  _send_key_props_();
244  _send_('\n');
245  _mark_parent_with_children_();
246  _enable_(MAP|FLOW_SL);
247  _push();
248  }
250  {
251  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
252  _send_("+MAP");
253  _send_key_props_();
254  _send_('\n');
255  _mark_parent_with_children_();
256  _enable_(MAP|BLOCK);
257  _push();
258  }
259 
261  {
262  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
263  _send_("+MAP {}");
264  _send_val_props_();
265  _send_('\n');
266  _mark_parent_with_children_();
267  _enable_(MAP|FLOW_SL);
268  _push();
269  }
271  {
272  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
273  _send_("+MAP");
274  _send_val_props_();
275  _send_('\n');
276  _mark_parent_with_children_();
277  _enable_(MAP|BLOCK);
278  _push();
279  }
280 
282  {
283  _pop();
284  _send_("-MAP\n");
285  }
286 
287  void end_map_flow(bool /*multiline*/)
288  {
289  _pop();
290  _send_("-MAP\n");
291  }
292 
293  /** @} */
294 
295 public:
296 
297  /** @name YAML seq events */
298  /** @{ */
299 
301  {
302  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
303  _send_("+SEQ []");
304  _send_key_props_();
305  _send_('\n');
306  _mark_parent_with_children_();
307  _enable_(SEQ|FLOW_SL);
308  _push();
309  }
311  {
312  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
313  _send_("+SEQ");
314  _send_key_props_();
315  _send_('\n');
316  _mark_parent_with_children_();
317  _enable_(SEQ|BLOCK);
318  _push();
319  }
320 
322  {
323  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
324  _send_("+SEQ []");
325  _send_val_props_();
326  _send_('\n');
327  _mark_parent_with_children_();
328  _enable_(SEQ|FLOW_SL);
329  _push();
330  }
332  {
333  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
334  _send_("+SEQ");
335  _send_val_props_();
336  _send_('\n');
337  _mark_parent_with_children_();
338  _enable_(SEQ|BLOCK);
339  _push();
340  }
341 
343  {
344  _pop();
345  _send_("-SEQ\n");
346  }
347 
348  void end_seq_flow(bool /*multiline*/)
349  {
350  _pop();
351  _send_("-SEQ\n");
352  }
353 
354  /** @} */
355 
356 public:
357 
358  /** @name YAML structure events */
359  /** @{ */
360 
361  void add_sibling()
362  {
363  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, m_parent);
364  _buf_flush_to_(m_curr->level, m_parent->level);
365  m_curr->ev_data = {};
366  }
367 
368  /** set the previous val as the first key of a new map, with flow style.
369  *
370  * See the documentation for @ref doc_event_handlers, which has
371  * important notes about this event.
372  */
374  {
375  _c4dbgpf("node[{}]: actually_val_is_first_key_of_new_map_flow", m_curr->node_id);
376  // ensure we have a temporary buffer to save the current val
377  const id_type tmp = m_curr->level + id_type(2);
378  _buf_ensure_(tmp + id_type(2));
379  // save the current val to the temporary buffer
380  _buf_flush_to_(m_curr->level, tmp);
381  _disable_(_VALMASK|VAL_STYLE);
382  // create the map.
383  // this will push a new level, and tmp is one further
384  begin_map_val_flow();
385  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, tmp != m_curr->level);
386  // now move the saved val as the first key
387  _buf_flush_to_(tmp, m_curr->level);
388  }
389 
390  /** like its flow counterpart, but this function can only be
391  * called after the end of a flow-val at root or doc level.
392  *
393  * See the documentation for @ref doc_event_handlers, which has
394  * important notes about this event.
395  */
397  {
398  _c4dbgpf("node[{}]: actually_val_is_first_key_of_new_map_block", m_curr->node_id);
399  EventSink &sink = _buf_();
400  csubstr full = sink;(void)full;
401  // interpolate +MAP\n after the last +DOC\n
402  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, full.len);
403  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, !full.count('\r'));
404  size_t docpos = sink.find_last("+DOC\n");
405  if(docpos != npos)
406  {
407  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, (m_stack.size() == 1u) ? (docpos >= 5u) : (docpos == 0u));
408  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, docpos + 5u < full.len);
409  sink.insert("+MAP\n", docpos + 5u);
410  }
411  else
412  {
413  // ... or interpolate +MAP\n after the last +DOC ---\n
414  docpos = sink.find_last("+DOC ---\n");
415  if(docpos != npos)
416  {
417  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, (m_stack.size() == 1u) ? (docpos >= 5u) : (docpos == 0u));
418  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, docpos + 9u < full.len);
419  sink.insert("+MAP\n", docpos + 9u);
420  }
421  else
422  {
423  sink.insert("+MAP\n", 0u);
424  }
425  }
426  _push();
427  }
428 
429  /** @} */
430 
431 public:
432 
433  /** @name YAML scalar events */
434  /** @{ */
435 
436 
437  C4_ALWAYS_INLINE void set_key_scalar_plain_empty() noexcept
438  {
439  _c4dbgpf("node[{}]: set key scalar plain as empty", m_curr->node_id);
440  _send_key_scalar_({}, ':');
441  _enable_(KEY|KEY_PLAIN|KEYNIL);
442  }
443  C4_ALWAYS_INLINE void set_val_scalar_plain_empty() noexcept
444  {
445  _c4dbgpf("node[{}]: set val scalar plain as empty", m_curr->node_id);
446  _send_val_scalar_({}, ':');
447  _enable_(VAL|VAL_PLAIN|VALNIL);
448  }
449 
450  C4_ALWAYS_INLINE void set_key_scalar_plain(csubstr scalar)
451  {
452  _c4dbgpf("node[{}]: set key scalar plain: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
453  _send_key_scalar_(scalar, ':');
454  _enable_(KEY|KEY_PLAIN);
455  }
456  C4_ALWAYS_INLINE void set_val_scalar_plain(csubstr scalar)
457  {
458  _c4dbgpf("node[{}]: set val scalar plain: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
459  _send_val_scalar_(scalar, ':');
460  _enable_(VAL|VAL_PLAIN);
461  }
462 
463 
464  C4_ALWAYS_INLINE void set_key_scalar_dquoted(csubstr scalar)
465  {
466  _c4dbgpf("node[{}]: set key scalar dquot: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
467  _send_key_scalar_(scalar, '"');
468  _enable_(KEY|KEY_DQUO);
469  }
470  C4_ALWAYS_INLINE void set_val_scalar_dquoted(csubstr scalar)
471  {
472  _c4dbgpf("node[{}]: set val scalar dquot: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
473  _send_val_scalar_(scalar, '"');
474  _enable_(VAL|VAL_DQUO);
475  }
476 
477 
478  C4_ALWAYS_INLINE void set_key_scalar_squoted(csubstr scalar)
479  {
480  _c4dbgpf("node[{}]: set key scalar squot: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
481  _send_key_scalar_(scalar, '\'');
482  _enable_(KEY|KEY_SQUO);
483  }
484  C4_ALWAYS_INLINE void set_val_scalar_squoted(csubstr scalar)
485  {
486  _c4dbgpf("node[{}]: set val scalar squot: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
487  _send_val_scalar_(scalar, '\'');
488  _enable_(VAL|VAL_SQUO);
489  }
490 
491 
492  C4_ALWAYS_INLINE void set_key_scalar_literal(csubstr scalar)
493  {
494  _c4dbgpf("node[{}]: set key scalar literal: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
495  _send_key_scalar_(scalar, '|');
496  _enable_(KEY|KEY_LITERAL);
497  }
498  C4_ALWAYS_INLINE void set_val_scalar_literal(csubstr scalar)
499  {
500  _c4dbgpf("node[{}]: set val scalar literal: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
501  _send_val_scalar_(scalar, '|');
502  _enable_(VAL|VAL_LITERAL);
503  }
504 
505 
506  C4_ALWAYS_INLINE void set_key_scalar_folded(csubstr scalar)
507  {
508  _c4dbgpf("node[{}]: set key scalar folded: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
509  _send_key_scalar_(scalar, '>');
510  _enable_(KEY|KEY_FOLDED);
511  }
512  C4_ALWAYS_INLINE void set_val_scalar_folded(csubstr scalar)
513  {
514  _c4dbgpf("node[{}]: set val scalar folded: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
515  _send_val_scalar_(scalar, '>');
516  _enable_(VAL|VAL_FOLDED);
517  }
518 
519 
520  C4_ALWAYS_INLINE void mark_key_scalar_unfiltered()
521  {
522  // nothing to do here
523  }
524  C4_ALWAYS_INLINE void mark_val_scalar_unfiltered()
525  {
526  // nothing to do here
527  }
528 
529  /** @} */
530 
531 public:
532 
533  /** @name YAML anchor/reference events */
534  /** @{ */
535 
536  void set_key_anchor(csubstr anchor)
537  {
538  _c4dbgpf("node[{}]: set key anchor: [{}]~~~{}~~~", m_curr->node_id, anchor.len, anchor);
539  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, !_has_any_(KEYREF));
540  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, !anchor.begins_with('&'));
541  _enable_(KEYANCH);
542  m_curr->ev_data.m_key.anchor = anchor;
543  }
544  void set_val_anchor(csubstr anchor)
545  {
546  _c4dbgpf("node[{}]: set val anchor: [{}]~~~{}~~~", m_curr->node_id, anchor.len, anchor);
547  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, !_has_any_(VALREF));
548  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, !anchor.begins_with('&'));
549  _enable_(VALANCH);
550  m_curr->ev_data.m_val.anchor = anchor;
551  }
552 
553  void set_key_ref(csubstr ref)
554  {
555  _c4dbgpf("node[{}]: set key ref: [{}]~~~{}~~~", m_curr->node_id, ref.len, ref);
556  if(C4_UNLIKELY(_has_any_(KEYANCH)))
557  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "key cannot have both anchor and ref");
558  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, ref.begins_with('*'));
559  _enable_(KEY|KEYREF);
560  _send_("=ALI ");
561  _send_(ref);
562  _send_('\n');
563  }
564  void set_val_ref(csubstr ref)
565  {
566  _c4dbgpf("node[{}]: set val ref: [{}]~~~{}~~~", m_curr->node_id, ref.len, ref);
567  if(C4_UNLIKELY(_has_any_(VALANCH)))
568  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "val cannot have both anchor and ref");
569  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, ref.begins_with('*'));
570  _enable_(VAL|VALREF);
571  _send_("=ALI ");
572  _send_(ref);
573  _send_('\n');
574  }
575 
576  /** @} */
577 
578 public:
579 
580  /** @name YAML tag events */
581  /** @{ */
582 
583  void set_key_tag(csubstr tag)
584  {
585  _c4dbgpf("node[{}]: set key tag: [{}]~~~{}~~~", m_curr->node_id, tag.len, tag);
586  _enable_(KEYTAG);
587  m_curr->ev_data.m_key.tag = _transform_directive(tag, &m_key_tag_buf);
588  }
589  void set_val_tag(csubstr tag)
590  {
591  _c4dbgpf("node[{}]: set val tag: [{}]~~~{}~~~", m_curr->node_id, tag.len, tag);
592  _enable_(VALTAG);
593  m_curr->ev_data.m_val.tag = _transform_directive(tag, &m_val_tag_buf);
594  }
595 
596  /** @} */
597 
598 public:
599 
600  /** @name YAML directive events */
601  /** @{ */
602 
603  void add_directive(csubstr directive)
604  {
605  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, directive.begins_with('%'));
606  if(directive.begins_with("%TAG"))
607  {
608  const id_type pos = _num_tag_directives();
609  if(C4_UNLIKELY(pos >= RYML_MAX_TAG_DIRECTIVES))
610  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "too many directives");
611  if(C4_UNLIKELY(!m_tag_directives[pos].create_from_str(directive)))
612  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "failed to add directive");
613  }
614  else if(directive.begins_with("%YAML"))
615  {
616  _c4dbgpf("%YAML directive! ignoring...: {}", directive);
617  if(C4_UNLIKELY(m_has_yaml_directive))
618  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "multiple yaml directives");
619  m_has_yaml_directive = true;
620  }
621  else
622  {
623  _c4dbgpf("unknown directive! ignoring... {}", directive);
624  }
625  }
626 
627  /** @} */
628 
629 public:
630 
631  /** @name YAML arena events */
632  /** @{ */
633 
634  substr alloc_arena(size_t len)
635  {
636  const size_t sz = m_arena.size();
637  csubstr prev = m_arena;
638  m_arena.resize(sz + len);
639  substr out = to_substr(m_arena).sub(sz);
640  substr curr = to_substr(m_arena);
641  if(curr.str != prev.str)
642  _stack_relocate_to_new_arena(prev, curr);
643  return out;
644  }
645 
646  substr alloc_arena(size_t len, substr *relocated)
647  {
648  csubstr prev = m_arena;
649  if(!prev.is_super(*relocated))
650  return alloc_arena(len);
651  substr out = alloc_arena(len);
652  substr curr = to_substr(m_arena);
653  if(curr.str != prev.str)
654  *relocated = _stack_relocate_to_new_arena(*relocated, prev, curr);
655  return out;
656  }
657 
658  /** @} */
659 
660 public:
661 
662  /** @cond dev */
663 
664  /** push a new parent, add a child to the new parent, and set the
665  * child as the current node */
666  void _push()
667  {
668  _stack_push();
669  _buf_ensure_(m_stack.size() + id_type(1));
670  _buf_().clear();
671  m_curr->ev_data = {};
672  _c4dbgpf("pushed! level={}", m_curr->level);
673  }
674 
675  /** end the current scope */
676  void _pop()
677  {
678  _buf_flush_to_(m_curr->level, m_parent->level);
679  _stack_pop();
680  }
681 
682  template<type_bits bits> C4_ALWAYS_INLINE void _enable__() noexcept
683  {
684  m_curr->ev_data.m_type.type = static_cast<NodeType_e>(m_curr->ev_data.m_type.type | bits);
685  }
686  template<type_bits bits> C4_ALWAYS_INLINE void _disable__() noexcept
687  {
688  m_curr->ev_data.m_type.type = static_cast<NodeType_e>(m_curr->ev_data.m_type.type & (~bits));
689  }
690  template<type_bits bits> C4_ALWAYS_INLINE bool _has_any__() const noexcept
691  {
692  return (m_curr->ev_data.m_type.type & bits) != 0;
693  }
694 
695  void _mark_parent_with_children_()
696  {
697  if(m_parent)
698  m_parent->has_children = true;
699  }
700 
701  EventSink& _buf_() noexcept
702  {
703  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, m_curr->level < m_val_buffers.size());
704  return m_val_buffers[m_curr->level];
705  }
706 
707  EventSink& _buf_(id_type level) noexcept
708  {
709  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, level < m_val_buffers.size());
710  return m_val_buffers[level];
711  }
712 
713  EventSink const& _buf_(id_type level) const noexcept
714  {
715  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, level < m_val_buffers.size());
716  return m_val_buffers[level];
717  }
718 
719  static void _buf_flush_to_(EventSink &C4_RESTRICT src, EventSink &C4_RESTRICT dst) noexcept
720  {
721  dst.append(src);
722  src.clear();
723  }
724 
725  void _buf_flush_to_(id_type level_src, id_type level_dst) noexcept
726  {
727  auto &src = _buf_(level_src);
728  auto &dst = _buf_(level_dst);
729  _buf_flush_to_(src, dst);
730  }
731 
732  void _buf_flush_() noexcept
733  {
734  _buf_flush_to_(_buf_(), *m_sink);
735  }
736 
737  void _buf_ensure_(id_type size_needed) noexcept
738  {
739  if(size_needed > m_val_buffers.size())
740  m_val_buffers.resize(size_needed);
741  }
742 
743  C4_ALWAYS_INLINE void _send_(csubstr s) noexcept { _buf_().append(s); }
744  C4_ALWAYS_INLINE void _send_(char c) noexcept { _buf_().append(c); }
745 
746  void _send_key_scalar_(csubstr scalar, char scalar_type_code)
747  {
748  _send_("=VAL");
749  _send_key_props_();
750  _send_(' ');
751  _send_(scalar_type_code);
752  append_scalar_escaped(&_buf_(), scalar);
753  _send_('\n');
754  }
755  void _send_val_scalar_(csubstr scalar, char scalar_type_code)
756  {
757  _send_("=VAL");
758  _send_val_props_();
759  _send_(' ');
760  _send_(scalar_type_code);
761  append_scalar_escaped(&_buf_(), scalar);
762  _send_('\n');
763  }
764 
765  void _send_key_props_()
766  {
768  {
769  _send_(" &");
770  _send_(m_curr->ev_data.m_key.anchor);
771  }
772  if(_has_any_(KEYTAG))
773  {
774  _send_tag_(m_curr->ev_data.m_key.tag);
775  }
776  m_curr->ev_data.m_key = {};
777  _disable_(KEYANCH|KEYREF|KEYTAG);
778  }
779  void _send_val_props_()
780  {
782  {
783  _send_(" &");
784  _send_(m_curr->ev_data.m_val.anchor);
785  }
786  if(m_curr->ev_data.m_type.type & VALTAG)
787  {
788  _send_tag_(m_curr->ev_data.m_val.tag);
789  }
790  m_curr->ev_data.m_val = {};
791  _disable_(VALANCH|VALREF|VALTAG);
792  }
793  void _send_tag_(csubstr tag)
794  {
795  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, !tag.empty());
796  if(tag.str[0] == '<')
797  {
798  _send_(' ');
799  _send_(tag);
800  }
801  else
802  {
803  _send_(" <");
804  _send_(tag);
805  _send_('>');
806  }
807  }
808 
809  void _clear_tag_directives_()
810  {
811  for(TagDirective &td : m_tag_directives)
812  td = {};
813  }
814  id_type _num_tag_directives() const
815  {
816  // this assumes we have a very small number of tag directives
817  for(id_type i = 0; i < RYML_MAX_TAG_DIRECTIVES; ++i)
818  if(m_tag_directives[i].handle.empty())
819  return i;
821  }
822  csubstr _transform_directive(csubstr tag, extra::string *output)
823  {
824  // lookup from the end. We want to find the first directive that
825  // matches the tag and has a target node id leq than the given
826  // node_id.
827  for(id_type i = RYML_MAX_TAG_DIRECTIVES-1; i != NONE; --i)
828  {
829  TagDirective const& td = m_tag_directives[i];
830  if(td.handle.empty())
831  continue;
832  if(tag.begins_with(td.handle))
833  {
834  bool retry = false;
835  again1:
836  size_t len = td.transform(tag, *output, m_stack.m_callbacks);
837  if(len == 0)
838  {
839  if(tag.begins_with("!<"))
840  return tag.sub(1);
841  return tag;
842  }
843  if(len > output->size())
844  {
845  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !retry);
846  retry = true;
847  output->resize(len);
848  output->resize(output->capacity());
849  goto again1;
850  }
851  return csubstr(*output).first(len);
852  }
853  }
854  if(tag.begins_with('!'))
855  {
856  if(is_custom_tag(tag))
857  {
858  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "tag not found");
859  }
860  }
861  bool retry = false;
862  again2:
863  csubstr result = normalize_tag_long(tag, *output);
864  if(!result.str)
865  {
866  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !retry);
867  retry = true;
868  output->resize(result.len);
869  output->resize(output->capacity());
870  goto again2;
871  }
872  return result;
873  }
874 
875  #undef _enable_
876  #undef _disable_
877  #undef _has_any_
878 
879  /** @endcond */
880 };
881 
882 /** @} */
883 
884 } // namespace extra
885 } // namespace yml
886 } // namespace c4
887 
888 C4_SUPPRESS_WARNING_GCC_POP
889 
890 #endif /* _C4_YML_EVT_EXTRA_EVENT_HANDLER_TESTSUITE_HPP_ */
#define _has_any_(bits)
Callbacks const & get_callbacks()
get the global callbacks
Definition: common.cpp:94
NodeType_e
a bit mask for marking node types and styles
Definition: node_type.hpp:33
@ VALANCH
the val has an &anchor
Definition: node_type.hpp:45
@ KEY_DQUO
mark key scalar as double quoted "
Definition: node_type.hpp:68
@ VALREF
a *reference: the val references an &anchor
Definition: node_type.hpp:43
@ VALNIL
the val is null (eg {a : } results in a null val)
Definition: node_type.hpp:49
@ MAP
a map: a parent of KEYVAL/KEYSEQ/KEYMAP nodes
Definition: node_type.hpp:38
@ KEY
is member of a map
Definition: node_type.hpp:36
@ VAL_FOLDED
mark val scalar as multiline, block folded >
Definition: node_type.hpp:65
@ VAL_STYLE
mask of all the scalar styles for val (not container styles!)
Definition: node_type.hpp:92
@ KEYTAG
the key has a tag
Definition: node_type.hpp:46
@ FLOW_SL
mark container with single-line flow style (seqs as '[val1,val2], maps as '{key: val,...
Definition: node_type.hpp:59
@ VAL
a scalar: has a scalar (ie string) value, possibly empty. must be a leaf node, and cannot be MAP or S...
Definition: node_type.hpp:37
@ VALTAG
the val has a tag
Definition: node_type.hpp:47
@ SEQ
a seq: a parent of VAL/SEQ/MAP nodes
Definition: node_type.hpp:39
@ VAL_SQUO
mark val scalar as single quoted '
Definition: node_type.hpp:67
@ VAL_PLAIN
mark val scalar as plain scalar (unquoted, even when multiline)
Definition: node_type.hpp:71
@ KEYREF
a *reference: the key references an &anchor
Definition: node_type.hpp:42
@ BLOCK
mark container with block style (seqs as '- val ', maps as 'key: val')
Definition: node_type.hpp:61
@ KEYANCH
the key has an &anchor
Definition: node_type.hpp:44
@ VAL_DQUO
mark val scalar as double quoted "
Definition: node_type.hpp:69
@ KEY_SQUO
mark key scalar as single quoted '
Definition: node_type.hpp:66
@ VAL_LITERAL
mark val scalar as multiline, block literal |
Definition: node_type.hpp:63
@ KEY_LITERAL
mark key scalar as multiline, block literal |
Definition: node_type.hpp:62
@ KEY_PLAIN
mark key scalar as plain scalar (unquoted, even when multiline)
Definition: node_type.hpp:70
@ KEY_FOLDED
mark key scalar as multiline, block folded >
Definition: node_type.hpp:64
@ KEYNIL
the key is null (eg { : b} results in a null key)
Definition: node_type.hpp:48
@ DOC
a document
Definition: node_type.hpp:40
substr to_substr(substr s) noexcept
neutral version for use in generic code
Definition: substr.hpp:2208
bool is_custom_tag(csubstr tag)
Definition: tag.cpp:9
csubstr normalize_tag_long(csubstr tag)
Definition: tag.cpp:31
#define RYML_MAX_TAG_DIRECTIVES
the maximum number of tag directives in a Tree
Definition: tag.hpp:19
void(*)(void *, csubstr prev_arena, substr next_arena) pfn_relocate_arena
void append_scalar_escaped(extra::string *es, csubstr val)
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:244
@ npos
a null string position
Definition: common.hpp:258
@ RTOP
reading at top level
@ RUNK
reading unknown state (when starting): must determine whether scalar, map or seq
@ NONE
an index to none
Definition: common.hpp:251
(Undefined by default) Use shorter error message from checks/asserts: do not show the check condition...
Definition: common.cpp:14
A c-style callbacks class to customize behavior on errors or allocation.
Definition: common.hpp:511
Use this class a base of implementations of event handler to simplify the stack logic.
size_t transform(csubstr tag, substr output, Callbacks const &callbacks, bool with_brackets=true) const
Definition: tag.cpp:230
This event produces standard YAML events as used in the YAML test suite.
EventHandlerTestSuite(EventSink *sink, Callbacks const &cb)
void end_doc()
implicit doc end (without ...)
void start_parse(const char *filename, csubstr src, detail::pfn_relocate_arena relocate_arena, void *relocate_arena_data)
void begin_doc_expl()
explicit doc start, with —
void actually_val_is_first_key_of_new_map_flow()
set the previous val as the first key of a new map, with flow style.
substr alloc_arena(size_t len, substr *relocated)
void begin_doc()
implicit doc start (without —)
void end_doc_expl()
explicit doc end, with ...
void actually_val_is_first_key_of_new_map_block()
like its flow counterpart, but this function can only be called after the end of a flow-val at root o...
a string collection used by the event handler.
Definition: string.hpp:269
id_type size() const noexcept
Definition: string.hpp:346
void resize(id_type sz)
Definition: string.hpp:354
an owning string class used by the yaml std event handler (and the YamlScript handler).
Definition: string.hpp:33
void resize(id_type sz)
Definition: string.hpp:93
void reserve(id_type sz)
Definition: string.hpp:99
void insert(char c, id_type pos)
Definition: string.hpp:138
id_type size() const noexcept
Definition: string.hpp:85
size_t find_last(csubstr pattern) RYML_NOEXCEPT
Definition: string.hpp:176