rapidyaml  0.11.0
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  /** @endcond */
76 
77 public:
78 
79  /** @name construction and resetting
80  * @{ */
81 
82  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() {}
83  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() {}
84  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()
85  {
86  reset();
87  }
89 
90  void reset()
91  {
92  _stack_reset_root();
93  m_curr->flags |= RUNK|RTOP;
94  m_has_yaml_directive = false;
95  for(TagDirective &td : m_tag_directives)
96  td = {};
97  m_val_buffers.clear();
98  m_val_buffers.resize((size_t)m_stack.size());
99  m_arena.clear();
100  m_arena.reserve(1024);
101  m_key_tag_buf.resize(256);
102  m_val_tag_buf.resize(256);
103  m_has_docs = false;
104  m_src = {};
105  }
106 
107  /** @} */
108 
109 public:
110 
111  /** @name parse events
112  * @{ */
113 
114  void start_parse(const char* filename, csubstr src, detail::pfn_relocate_arena relocate_arena, void *relocate_arena_data)
115  {
116  this->_stack_start_parse(filename, src, relocate_arena, relocate_arena_data);
117  }
118 
120  {
121  if((_num_tag_directives() || m_has_yaml_directive) && !m_has_docs)
122  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "directives cannot be used without a document");
123  this->_stack_finish_parse();
124  }
125 
127  {
128  while(m_stack.size() > 1)
129  _pop();
130  _buf_flush_();
131  }
132 
133  /** @} */
134 
135 public:
136 
137  /** @name YAML stream events */
138  /** @{ */
139 
141  {
142  _send_("+STR\n");
143  }
144 
145  void end_stream()
146  {
147  _send_("-STR\n");
148  _buf_flush_();
149  }
150 
151  /** @} */
152 
153 public:
154 
155  /** @name YAML document events */
156  /** @{ */
157 
158  /** implicit doc start (without ---) */
159  void begin_doc()
160  {
161  _c4dbgp("begin_doc");
162  if(_stack_should_push_on_begin_doc())
163  {
164  _c4dbgp("push!");
165  _push();
166  _enable_(DOC);
167  }
168  _send_("+DOC\n");
169  m_has_docs = true;
170  }
171  /** implicit doc end (without ...) */
172  void end_doc()
173  {
174  _c4dbgp("end_doc");
175  _send_("-DOC\n");
176  if(_stack_should_pop_on_end_doc())
177  {
178  _c4dbgp("pop!");
179  _pop();
180  }
181  }
182 
183  /** explicit doc start, with --- */
185  {
186  _c4dbgp("begin_doc_expl");
187  if(_stack_should_push_on_begin_doc())
188  {
189  _c4dbgp("push!");
190  _push();
191  }
192  _send_("+DOC ---\n");
193  _enable_(DOC);
194  m_has_docs = true;
195  }
196  /** explicit doc end, with ... */
198  {
199  _c4dbgp("end_doc_expl");
200  _send_("-DOC ...\n");
201  if(_stack_should_pop_on_end_doc())
202  {
203  _c4dbgp("pop!");
204  _pop();
205  }
206  m_has_yaml_directive = false;
207  }
208 
209  /** @} */
210 
211 public:
212 
213  /** @name YAML map functions */
214  /** @{ */
215 
217  {
218  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
219  _send_("+MAP {}");
220  _send_key_props_();
221  _send_('\n');
222  _mark_parent_with_children_();
223  _enable_(MAP|FLOW_SL);
224  _push();
225  }
227  {
228  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
229  _send_("+MAP");
230  _send_key_props_();
231  _send_('\n');
232  _mark_parent_with_children_();
233  _enable_(MAP|BLOCK);
234  _push();
235  }
236 
238  {
239  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
240  _send_("+MAP {}");
241  _send_val_props_();
242  _send_('\n');
243  _mark_parent_with_children_();
244  _enable_(MAP|FLOW_SL);
245  _push();
246  }
248  {
249  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
250  _send_("+MAP");
251  _send_val_props_();
252  _send_('\n');
253  _mark_parent_with_children_();
254  _enable_(MAP|BLOCK);
255  _push();
256  }
257 
259  {
260  _pop();
261  _send_("-MAP\n");
262  }
263 
264  void end_map_flow(bool /*multiline*/)
265  {
266  _pop();
267  _send_("-MAP\n");
268  }
269 
270  /** @} */
271 
272 public:
273 
274  /** @name YAML seq events */
275  /** @{ */
276 
278  {
279  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
280  _send_("+SEQ []");
281  _send_key_props_();
282  _send_('\n');
283  _mark_parent_with_children_();
284  _enable_(SEQ|FLOW_SL);
285  _push();
286  }
288  {
289  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
290  _send_("+SEQ");
291  _send_key_props_();
292  _send_('\n');
293  _mark_parent_with_children_();
294  _enable_(SEQ|BLOCK);
295  _push();
296  }
297 
299  {
300  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
301  _send_("+SEQ []");
302  _send_val_props_();
303  _send_('\n');
304  _mark_parent_with_children_();
305  _enable_(SEQ|FLOW_SL);
306  _push();
307  }
309  {
310  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !_has_any_(VAL));
311  _send_("+SEQ");
312  _send_val_props_();
313  _send_('\n');
314  _mark_parent_with_children_();
315  _enable_(SEQ|BLOCK);
316  _push();
317  }
318 
320  {
321  _pop();
322  _send_("-SEQ\n");
323  }
324 
325  void end_seq_flow(bool /*multiline*/)
326  {
327  _pop();
328  _send_("-SEQ\n");
329  }
330 
331  /** @} */
332 
333 public:
334 
335  /** @name YAML structure events */
336  /** @{ */
337 
338  void add_sibling()
339  {
340  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, m_parent);
341  _buf_flush_to_(m_curr->level, m_parent->level);
342  m_curr->ev_data = {};
343  }
344 
345  /** set the previous val as the first key of a new map, with flow style.
346  *
347  * See the documentation for @ref doc_event_handlers, which has
348  * important notes about this event.
349  */
351  {
352  // ensure we have a temporary buffer to save the current val
353  const id_type tmp = m_curr->level + id_type(2);
354  _buf_ensure_(tmp + id_type(2));
355  // save the current val to the temporary buffer
356  _buf_flush_to_(m_curr->level, tmp);
357  _disable_(_VALMASK|VAL_STYLE);
358  // create the map.
359  // this will push a new level, and tmp is one further
360  begin_map_val_flow();
361  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, tmp != m_curr->level);
362  // now move the saved val as the first key
363  _buf_flush_to_(tmp, m_curr->level);
364  }
365 
366  /** like its flow counterpart, but this function can only be
367  * called after the end of a flow-val at root or doc level.
368  *
369  * See the documentation for @ref doc_event_handlers, which has
370  * important notes about this event.
371  */
373  {
374  EventSink &sink = _buf_();
375  substr full = sink;(void)full;
376  // interpolate +MAP\n after the last +DOC\n
377  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, full.len);
378  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, !full.count('\r'));
379  size_t docpos = sink.find_last("+DOC\n");
380  if(docpos != npos)
381  {
382  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, (m_stack.size() == 1u) ? (docpos >= 5u) : (docpos == 0u));
383  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, docpos + 5u < full.len);
384  sink.insert("+MAP\n", docpos + 5u);
385  }
386  else
387  {
388  // ... or interpolate +MAP\n after the last +DOC ---\n
389  docpos = sink.find_last("+DOC ---\n");
390  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, docpos != npos);
391  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, (m_stack.size() == 1u) ? (docpos >= 5u) : (docpos == 0u));
392  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, docpos + 9u < full.len);
393  sink.insert("+MAP\n", docpos + 9u);
394  }
395  _push();
396  }
397 
398  /** @} */
399 
400 public:
401 
402  /** @name YAML scalar events */
403  /** @{ */
404 
405 
406  C4_ALWAYS_INLINE void set_key_scalar_plain_empty() noexcept
407  {
408  _c4dbgpf("node[{}]: set key scalar plain as empty", m_curr->node_id);
409  _send_key_scalar_({}, ':');
410  _enable_(KEY|KEY_PLAIN|KEYNIL);
411  }
412  C4_ALWAYS_INLINE void set_val_scalar_plain_empty() noexcept
413  {
414  _c4dbgpf("node[{}]: set val scalar plain as empty", m_curr->node_id);
415  _send_val_scalar_({}, ':');
416  _enable_(VAL|VAL_PLAIN|VALNIL);
417  }
418 
419  C4_ALWAYS_INLINE void set_key_scalar_plain(csubstr scalar)
420  {
421  _c4dbgpf("node[{}]: set key scalar plain: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
422  _send_key_scalar_(scalar, ':');
423  _enable_(KEY|KEY_PLAIN);
424  }
425  C4_ALWAYS_INLINE void set_val_scalar_plain(csubstr scalar)
426  {
427  _c4dbgpf("node[{}]: set val scalar plain: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
428  _send_val_scalar_(scalar, ':');
429  _enable_(VAL|VAL_PLAIN);
430  }
431 
432 
433  C4_ALWAYS_INLINE void set_key_scalar_dquoted(csubstr scalar)
434  {
435  _c4dbgpf("node[{}]: set key scalar dquot: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
436  _send_key_scalar_(scalar, '"');
437  _enable_(KEY|KEY_DQUO);
438  }
439  C4_ALWAYS_INLINE void set_val_scalar_dquoted(csubstr scalar)
440  {
441  _c4dbgpf("node[{}]: set val scalar dquot: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
442  _send_val_scalar_(scalar, '"');
443  _enable_(VAL|VAL_DQUO);
444  }
445 
446 
447  C4_ALWAYS_INLINE void set_key_scalar_squoted(csubstr scalar)
448  {
449  _c4dbgpf("node[{}]: set key scalar squot: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
450  _send_key_scalar_(scalar, '\'');
451  _enable_(KEY|KEY_SQUO);
452  }
453  C4_ALWAYS_INLINE void set_val_scalar_squoted(csubstr scalar)
454  {
455  _c4dbgpf("node[{}]: set val scalar squot: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
456  _send_val_scalar_(scalar, '\'');
457  _enable_(VAL|VAL_SQUO);
458  }
459 
460 
461  C4_ALWAYS_INLINE void set_key_scalar_literal(csubstr scalar)
462  {
463  _c4dbgpf("node[{}]: set key scalar literal: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
464  _send_key_scalar_(scalar, '|');
465  _enable_(KEY|KEY_LITERAL);
466  }
467  C4_ALWAYS_INLINE void set_val_scalar_literal(csubstr scalar)
468  {
469  _c4dbgpf("node[{}]: set val scalar literal: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
470  _send_val_scalar_(scalar, '|');
471  _enable_(VAL|VAL_LITERAL);
472  }
473 
474 
475  C4_ALWAYS_INLINE void set_key_scalar_folded(csubstr scalar)
476  {
477  _c4dbgpf("node[{}]: set key scalar folded: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
478  _send_key_scalar_(scalar, '>');
479  _enable_(KEY|KEY_FOLDED);
480  }
481  C4_ALWAYS_INLINE void set_val_scalar_folded(csubstr scalar)
482  {
483  _c4dbgpf("node[{}]: set val scalar folded: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
484  _send_val_scalar_(scalar, '>');
485  _enable_(VAL|VAL_FOLDED);
486  }
487 
488 
489  C4_ALWAYS_INLINE void mark_key_scalar_unfiltered()
490  {
491  // nothing to do here
492  }
493  C4_ALWAYS_INLINE void mark_val_scalar_unfiltered()
494  {
495  // nothing to do here
496  }
497 
498  /** @} */
499 
500 public:
501 
502  /** @name YAML anchor/reference events */
503  /** @{ */
504 
505  void set_key_anchor(csubstr anchor)
506  {
507  _c4dbgpf("node[{}]: set key anchor: [{}]~~~{}~~~", m_curr->node_id, anchor.len, anchor);
508  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, !_has_any_(KEYREF));
509  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, !anchor.begins_with('&'));
510  _enable_(KEYANCH);
511  m_curr->ev_data.m_key.anchor = anchor;
512  }
513  void set_val_anchor(csubstr anchor)
514  {
515  _c4dbgpf("node[{}]: set val anchor: [{}]~~~{}~~~", m_curr->node_id, anchor.len, anchor);
516  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, !_has_any_(VALREF));
517  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, !anchor.begins_with('&'));
518  _enable_(VALANCH);
519  m_curr->ev_data.m_val.anchor = anchor;
520  }
521 
522  void set_key_ref(csubstr ref)
523  {
524  _c4dbgpf("node[{}]: set key ref: [{}]~~~{}~~~", m_curr->node_id, ref.len, ref);
525  if(C4_UNLIKELY(_has_any_(KEYANCH)))
526  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "key cannot have both anchor and ref");
527  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, ref.begins_with('*'));
528  _enable_(KEY|KEYREF);
529  _send_("=ALI ");
530  _send_(ref);
531  _send_('\n');
532  }
533  void set_val_ref(csubstr ref)
534  {
535  _c4dbgpf("node[{}]: set val ref: [{}]~~~{}~~~", m_curr->node_id, ref.len, ref);
536  if(C4_UNLIKELY(_has_any_(VALANCH)))
537  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "val cannot have both anchor and ref");
538  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, ref.begins_with('*'));
539  _enable_(VAL|VALREF);
540  _send_("=ALI ");
541  _send_(ref);
542  _send_('\n');
543  }
544 
545  /** @} */
546 
547 public:
548 
549  /** @name YAML tag events */
550  /** @{ */
551 
552  void set_key_tag(csubstr tag)
553  {
554  _c4dbgpf("node[{}]: set key tag: [{}]~~~{}~~~", m_curr->node_id, tag.len, tag);
555  _enable_(KEYTAG);
556  m_curr->ev_data.m_key.tag = _transform_directive(tag, &m_key_tag_buf);
557  }
558  void set_val_tag(csubstr tag)
559  {
560  _c4dbgpf("node[{}]: set val tag: [{}]~~~{}~~~", m_curr->node_id, tag.len, tag);
561  _enable_(VALTAG);
562  m_curr->ev_data.m_val.tag = _transform_directive(tag, &m_val_tag_buf);
563  }
564 
565  /** @} */
566 
567 public:
568 
569  /** @name YAML directive events */
570  /** @{ */
571 
572  void add_directive(csubstr directive)
573  {
574  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, directive.begins_with('%'));
575  if(directive.begins_with("%TAG"))
576  {
577  const id_type pos = _num_tag_directives();
578  if(C4_UNLIKELY(pos >= RYML_MAX_TAG_DIRECTIVES))
579  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "too many directives");
580  if(C4_UNLIKELY(!m_tag_directives[pos].create_from_str(directive)))
581  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "failed to add directive");
582  }
583  else if(directive.begins_with("%YAML"))
584  {
585  _c4dbgpf("%YAML directive! ignoring...: {}", directive);
586  if(C4_UNLIKELY(m_has_yaml_directive))
587  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "multiple yaml directives");
588  m_has_yaml_directive = true;
589  }
590  else
591  {
592  _c4dbgpf("unknown directive! ignoring... {}", directive);
593  }
594  }
595 
596  /** @} */
597 
598 public:
599 
600  /** @name YAML arena events */
601  /** @{ */
602 
603  substr alloc_arena(size_t len)
604  {
605  const size_t sz = m_arena.size();
606  csubstr prev = m_arena;
607  m_arena.resize(sz + len);
608  substr out = to_substr(m_arena).sub(sz);
609  substr curr = to_substr(m_arena);
610  if(curr.str != prev.str)
611  _stack_relocate_to_new_arena(prev, curr);
612  return out;
613  }
614 
615  substr alloc_arena(size_t len, substr *relocated)
616  {
617  csubstr prev = m_arena;
618  if(!prev.is_super(*relocated))
619  return alloc_arena(len);
620  substr out = alloc_arena(len);
621  substr curr = to_substr(m_arena);
622  if(curr.str != prev.str)
623  *relocated = _stack_relocate_to_new_arena(*relocated, prev, curr);
624  return out;
625  }
626 
627  /** @} */
628 
629 public:
630 
631  /** @cond dev */
632 
633  /** push a new parent, add a child to the new parent, and set the
634  * child as the current node */
635  void _push()
636  {
637  _stack_push();
638  _buf_ensure_(m_stack.size() + id_type(1));
639  _buf_().clear();
640  m_curr->ev_data = {};
641  _c4dbgpf("pushed! level={}", m_curr->level);
642  }
643 
644  /** end the current scope */
645  void _pop()
646  {
647  _buf_flush_to_(m_curr->level, m_parent->level);
648  _stack_pop();
649  }
650 
651  template<type_bits bits> C4_ALWAYS_INLINE void _enable__() noexcept
652  {
653  m_curr->ev_data.m_type.type = static_cast<NodeType_e>(m_curr->ev_data.m_type.type | bits);
654  }
655  template<type_bits bits> C4_ALWAYS_INLINE void _disable__() noexcept
656  {
657  m_curr->ev_data.m_type.type = static_cast<NodeType_e>(m_curr->ev_data.m_type.type & (~bits));
658  }
659  template<type_bits bits> C4_ALWAYS_INLINE bool _has_any__() const noexcept
660  {
661  return (m_curr->ev_data.m_type.type & bits) != 0;
662  }
663 
664  void _mark_parent_with_children_()
665  {
666  if(m_parent)
667  m_parent->has_children = true;
668  }
669 
670  EventSink& _buf_() noexcept
671  {
672  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, m_curr->level < m_val_buffers.size());
673  return m_val_buffers[m_curr->level];
674  }
675 
676  EventSink& _buf_(id_type level) noexcept
677  {
678  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, level < m_val_buffers.size());
679  return m_val_buffers[level];
680  }
681 
682  EventSink const& _buf_(id_type level) const noexcept
683  {
684  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, level < m_val_buffers.size());
685  return m_val_buffers[level];
686  }
687 
688  static void _buf_flush_to_(EventSink &C4_RESTRICT src, EventSink &C4_RESTRICT dst) noexcept
689  {
690  dst.append(src);
691  src.clear();
692  }
693 
694  void _buf_flush_to_(id_type level_src, id_type level_dst) noexcept
695  {
696  auto &src = _buf_(level_src);
697  auto &dst = _buf_(level_dst);
698  _buf_flush_to_(src, dst);
699  }
700 
701  void _buf_flush_() noexcept
702  {
703  _buf_flush_to_(_buf_(), *m_sink);
704  }
705 
706  void _buf_ensure_(id_type size_needed) noexcept
707  {
708  if(size_needed > m_val_buffers.size())
709  m_val_buffers.resize(size_needed);
710  }
711 
712  C4_ALWAYS_INLINE void _send_(csubstr s) noexcept { _buf_().append(s); }
713  C4_ALWAYS_INLINE void _send_(char c) noexcept { _buf_().append(c); }
714 
715  void _send_key_scalar_(csubstr scalar, char scalar_type_code)
716  {
717  _send_("=VAL");
718  _send_key_props_();
719  _send_(' ');
720  _send_(scalar_type_code);
721  append_scalar_escaped(&_buf_(), scalar);
722  _send_('\n');
723  }
724  void _send_val_scalar_(csubstr scalar, char scalar_type_code)
725  {
726  _send_("=VAL");
727  _send_val_props_();
728  _send_(' ');
729  _send_(scalar_type_code);
730  append_scalar_escaped(&_buf_(), scalar);
731  _send_('\n');
732  }
733 
734  void _send_key_props_()
735  {
737  {
738  _send_(" &");
739  _send_(m_curr->ev_data.m_key.anchor);
740  }
741  if(_has_any_(KEYTAG))
742  {
743  _send_tag_(m_curr->ev_data.m_key.tag);
744  }
745  m_curr->ev_data.m_key = {};
746  _disable_(KEYANCH|KEYREF|KEYTAG);
747  }
748  void _send_val_props_()
749  {
751  {
752  _send_(" &");
753  _send_(m_curr->ev_data.m_val.anchor);
754  }
755  if(m_curr->ev_data.m_type.type & VALTAG)
756  {
757  _send_tag_(m_curr->ev_data.m_val.tag);
758  }
759  m_curr->ev_data.m_val = {};
760  _disable_(VALANCH|VALREF|VALTAG);
761  }
762  void _send_tag_(csubstr tag)
763  {
764  _RYML_ASSERT_BASIC_(m_stack.m_callbacks, !tag.empty());
765  if(tag.str[0] == '<')
766  {
767  _send_(' ');
768  _send_(tag);
769  }
770  else
771  {
772  _send_(" <");
773  _send_(tag);
774  _send_('>');
775  }
776  }
777 
778  void _clear_tag_directives_()
779  {
780  for(TagDirective &td : m_tag_directives)
781  td = {};
782  }
783  id_type _num_tag_directives() const
784  {
785  // this assumes we have a very small number of tag directives
786  for(id_type i = 0; i < RYML_MAX_TAG_DIRECTIVES; ++i)
787  if(m_tag_directives[i].handle.empty())
788  return i;
790  }
791  csubstr _transform_directive(csubstr tag, extra::string *output)
792  {
793  // lookup from the end. We want to find the first directive that
794  // matches the tag and has a target node id leq than the given
795  // node_id.
796  for(id_type i = RYML_MAX_TAG_DIRECTIVES-1; i != NONE; --i)
797  {
798  TagDirective const& td = m_tag_directives[i];
799  if(td.handle.empty())
800  continue;
801  if(tag.begins_with(td.handle))
802  {
803  bool retry = false;
804  again1:
805  size_t len = td.transform(tag, *output, m_stack.m_callbacks);
806  if(len == 0)
807  {
808  if(tag.begins_with("!<"))
809  return tag.sub(1);
810  return tag;
811  }
812  if(len > output->size())
813  {
814  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !retry);
815  retry = true;
816  output->resize(len);
817  output->resize(output->capacity());
818  goto again1;
819  }
820  return csubstr(*output).first(len);
821  }
822  }
823  if(tag.begins_with('!'))
824  {
825  if(is_custom_tag(tag))
826  {
827  _RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "tag not found");
828  }
829  }
830  bool retry = false;
831  again2:
832  csubstr result = normalize_tag_long(tag, *output);
833  if(!result.str)
834  {
835  _RYML_CHECK_BASIC_(m_stack.m_callbacks, !retry);
836  retry = true;
837  output->resize(result.len);
838  output->resize(output->capacity());
839  goto again2;
840  }
841  return result;
842  }
843 
844  #undef _enable_
845  #undef _disable_
846  #undef _has_any_
847 
848  /** @endcond */
849 };
850 
851 /** @} */
852 
853 } // namespace extra
854 } // namespace yml
855 } // namespace c4
856 
857 C4_SUPPRESS_WARNING_GCC_POP
858 
859 #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