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