rapidyaml  0.16.0
parseandemitYAML,anddoitfast
emitter.def.hpp
Go to the documentation of this file.
1 #ifndef C4_YML_EMITTER_DEF_HPP_
2 #define C4_YML_EMITTER_DEF_HPP_
3 
4 /** @file emitter.def.hpp */
5 
6 #ifndef C4_YML_EMITTER_HPP_
7 #include "c4/yml/emitter.hpp"
8 #endif
9 #ifndef C4_YML_TREE_HPP_
10 #include "c4/yml/tree.hpp"
11 #endif
12 #ifndef C4_YML_SCALAR_STYLE_HPP_
13 #include "c4/yml/scalar_style.hpp"
14 #endif
15 #ifndef C4_YML_DETAIL_DBGPRINT_HPP_
16 #include "c4/yml/detail/dbgprint.hpp"
17 #endif
18 #ifndef C4_YML_ERROR_HPP_
19 #include "c4/yml/error.hpp"
20 #endif
21 
22 
23 C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
24 C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
25 // NOLINTBEGIN(modernize-avoid-c-style-cast)
26 
27 namespace c4 {
28 namespace yml {
29 
30 namespace detail {
31 enum : type_bits { // NOLINT
43 };
44 } // namespace detail
45 
46 
47 template<class Writer>
48 void Emitter<Writer>::emit_as(EmitType_e type, Tree const* tree, id_type id)
49 {
50  RYML_ASSERT_BASIC_CB_(tree->callbacks(), !tree || !tree->empty() || id == NONE);
51  if(!tree || tree->empty())
52  return;
53  if(id == NONE)
54  id = tree->root_id();
55  RYML_CHECK_VISIT_CB_(tree->callbacks(), id < tree->capacity(), tree, id);
56  m_tree = tree;
57  m_col = 0;
58  m_depth = 0;
59  m_ilevel = 0;
60  m_pws = PWS_NONE_;
61  m_flow_pws = {};
62  if(type == EMIT_YAML)
63  emit_yaml_(id);
64  else if(type == EMIT_JSON)
65  json_emit_(id);
66  else
67  RYML_ERR_BASIC_CB_(m_tree->callbacks(), "unknown emit type"); // LCOV_EXCL_LINE
68  m_tree = nullptr;
69 }
70 
71 /** @cond dev */
72 
73 
74 //-----------------------------------------------------------------------------
75 
76 // The startup logic is made complicated from it having to accept
77 // initial non-root nodes, and having to deal with tricky tokens like
78 // doc separators, anchors, tags, optional keys or dashes, and
79 // comments.
80 //
81 // This function kickstarts the tree descent by handling all the
82 // initial and final logic at the top-level scope, thus avoiding
83 // top-level kickstart branches in the recursive descending code
84 // (which should be oblivious of such logic). This makes the recursive
85 // descending code a lot simpler.
86 template<class Writer>
88 {
89  const NodeType ty = m_tree->type(id);
90 
91  // emit leading tokens, such as keys or comments
92  const bool has_parent = !m_tree->is_root(id);
93  const bool emit_key = has_parent && ty.has_key() && m_opts.emit_nonroot_key();
94  const bool emit_dash = has_parent && !ty.has_key() && !ty.is_doc() && m_opts.emit_nonroot_dash();
95  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, !(emit_key && emit_dash), m_tree, id);
96 
97  // emit opening tokens (such as tags, anchors or comments)
98  if(emit_key)
99  {
100  blck_map_open_entry_(id);
101  ++m_ilevel;
102  }
103  else if(emit_dash)
104  {
105  blck_seq_open_entry_(id);
106  ++m_ilevel;
107  }
108  else
109  {
110  top_open_entry_(id);
111  }
112 
113  // emit the payload
114  if(ty.is_stream())
115  {
116  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, m_ilevel == 0, m_tree, id);
117  visit_stream_(id);
118  }
119  else if(ty.is_doc())
120  {
121  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, m_ilevel == 0, m_tree, id);
122  visit_doc_(id);
123  }
124  else if(ty.is_container())
125  {
126  visit_blck_container_(id);
127  }
128  else if(ty.has_val())
129  {
130  visit_doc_val_(id);
131  }
132 
133  // emit closing tokens (such as comments)
134  if(emit_key)
135  {
136  --m_ilevel;
137  blck_close_entry_(id);
138  }
139  else if(emit_dash)
140  {
141  --m_ilevel;
142  blck_close_entry_(id);
143  }
144  else
145  {
146  top_close_entry_(id);
147  }
148 
149  if(ty.is_flow_mlx())
150  {
151  newl_();
152  }
153  else if(m_tree->is_root(id)
154  || emit_dash || emit_key
155  || !ty.is_val()
156  || !ty.is_val_plain())
157  {
158  write_pws_and_pend_(PWS_NONE_);
159  }
160 }
161 
162 
163 //-----------------------------------------------------------------------------
164 
165 template<class Writer>
166 void Emitter<Writer>::visit_stream_(id_type id)
167 {
168  auto write_tag_directives = [this](const id_type next_node){
169  const id_type doc = m_tree->ancestor_doc(next_node);
170  const TagDirectiveRange tagds = m_tree->m_tag_directives.lookup_range(doc);
171  if(tagds.e != tagds.b)
172  {
173  const id_type parent = m_tree->parent(next_node);
174  if(next_node != m_tree->first_child(parent))
175  {
176  write_pws_and_pend_(PWS_NEWL_);
177  write_("...");
178  }
179  }
180  for(TagDirective const& td : tagds)
181  {
182  write_pws_and_pend_(PWS_NONE_);
183  write_("%TAG ");
184  write_(td.handle);
185  write_(' ');
186  write_(td.prefix);
187  pend_newl_();
188  }
189  };
190  const id_type first_child = m_tree->first_child(id);
191  if(first_child != NONE)
192  write_tag_directives(first_child);
193  ++m_depth;
194  for(id_type child = first_child; child != NONE; child = m_tree->next_sibling(child))
195  {
196  NodeType ty = m_tree->type(child);
197  m_ilevel = 0;
198  write_pws_and_pend_(PWS_NONE_);
199  top_open_entry_(child);
200  visit_doc_(child);
201  top_close_entry_(child);
202  if(ty.is_val())
203  {
204  if(ty.m_bits & VALNIL)
205  pend_newl_();
206  }
207  else if(ty.is_container())
208  {
209  if(ty.is_flow())
210  pend_newl_();
211  }
212  if(m_tree->next_sibling(child) != NONE)
213  {
214  write_tag_directives(m_tree->next_sibling(child));
215  }
216  }
217  --m_depth;
218 }
219 
220 
221 //-----------------------------------------------------------------------------
222 
223 template<class Writer>
224 void Emitter<Writer>::visit_blck_container_(id_type id)
225 {
226  NodeType ty = m_tree->type(id);
227  if(!(ty.m_bits & CONTAINER_STYLE))
228  ty.m_bits |= (m_tree->empty(id) ? FLOW_SL : BLOCK);
229  write_pws_and_pend_(PWS_NONE_);
230  if(ty.is_flow_sl())
231  visit_flow_sl_(id);
232  else if(ty.is_flow_mlx())
233  visit_flow_ml_(id);
234  else
235  visit_blck_(id);
236 }
237 
238 template<class Writer>
239 void Emitter<Writer>::visit_flow_container_(id_type id)
240 {
241  NodeType ty = m_tree->type(id);
242  if(!(ty.m_bits & CONTAINER_STYLE))
243  ty.m_bits |= FLOW_SL;
244  write_pws_and_pend_(PWS_NONE_);
245  if(ty.is_flow_mlx())
246  visit_flow_ml_(id);
247  else // if(ty.is_flow_sl())
248  visit_flow_sl_(id);
249 }
250 
251 
252 //-----------------------------------------------------------------------------
253 
254 template<class Writer>
255 void Emitter<Writer>::visit_doc_val_(id_type id)
256 {
257  // some plain scalars such as '...' and '---' must not
258  // appear at 0-indentation
259  NodeType ty = m_tree->type(id);
260  const csubstr val = m_tree->val(id);
261  type_bits val_style = ty.m_bits & VAL_STYLE;
262  const bool is_ambiguous = ((ty.m_bits & VAL_PLAIN) || !val_style)
263  && (val.begins_with("...") || val.begins_with("---"));
264  if(is_ambiguous)
265  {
266  ++m_ilevel;
267  if(m_pws != PWS_NONE_)
268  pend_newl_();
269  else
270  indent_(m_ilevel);
271  }
272  write_pws_and_pend_(PWS_NONE_);
273  if(m_tree->is_val_ref(id))
274  {
275  write_ref_(val);
276  }
277  else
278  {
279  if(!val_style)
280  val_style = scalar_style_choose_block(val);
281  blck_write_scalar_(val, val_style);
282  }
283  if(is_ambiguous)
284  {
285  --m_ilevel;
286  }
287 }
288 
289 
290 //-----------------------------------------------------------------------------
291 
292 template<class Writer>
293 void Emitter<Writer>::visit_doc_(id_type id)
294 {
295  const NodeType ty = m_tree->type(id);
296  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, ty.is_doc(), m_tree, id);
297  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, !ty.has_key(), m_tree, id);
298  if(ty.is_container()) // this is more frequent
299  {
300  visit_blck_container_(id);
301  }
302  else if(ty.is_val())
303  {
304  visit_doc_val_(id);
305  }
306 }
307 
308 
309 //-----------------------------------------------------------------------------
310 
311 // to be called only at top level
312 template<class Writer>
313 void Emitter<Writer>::top_open_entry_(id_type node)
314 {
315  NodeType ty = m_tree->type(node);
316  if(ty.is_doc() && !m_tree->is_root(node))
317  {
318  write_("---");
319  pend_space_();
320  }
321  if(ty.has_val_anchor())
322  {
323  write_pws_and_pend_(PWS_SPACE_);
324  write_('&');
325  write_(m_tree->val_anchor(node));
326  }
327  if(ty.has_val_tag())
328  {
329  write_pws_and_pend_(PWS_SPACE_);
330  write_tag_(m_tree->val_tag(node));
331  }
332  if(m_pws == PWS_SPACE_)
333  {
334  if(ty.has_val())
335  {
336  if(ty.is_val_plain() && !m_tree->val(node).len)
337  pend_none_();
338  }
339  else
340  {
341  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.is_container(), m_tree, node);
342  if(!ty.is_flow())
343  pend_newl_();
344  }
345  }
346 }
347 
348 
349 //-----------------------------------------------------------------------------
350 
351 template<class Writer>
352 void Emitter<Writer>::top_close_entry_(id_type node)
353 {
354  NodeType ty = m_tree->type(node);
355  if(ty.is_val() && !(ty.m_bits & VALNIL))
356  {
357  pend_newl_();
358  }
359 }
360 
361 
362 //-----------------------------------------------------------------------------
363 
364 template<class Writer>
365 void Emitter<Writer>::flow_seq_open_entry_(id_type node)
366 {
367  NodeType ty = m_tree->type(node);
368  write_pws_and_pend_(PWS_NONE_);
369  if(ty.m_bits & VALANCH)
370  {
371  write_pws_and_pend_(PWS_SPACE_);
372  write_('&');
373  write_(m_tree->val_anchor(node));
374  }
375  if(ty.m_bits & VALTAG)
376  {
377  write_pws_and_pend_(PWS_SPACE_);
378  write_tag_(m_tree->val_tag(node));
379  }
380 }
381 
382 
383 //-----------------------------------------------------------------------------
384 
385 template<class Writer>
386 void Emitter<Writer>::flow_map_open_entry_(id_type node)
387 {
388  NodeType ty = m_tree->type(node);
389  write_pws_and_pend_(PWS_NONE_);
390  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.has_key(), m_tree, node);
391  if(ty.m_bits & KEYANCH)
392  {
393  write_pws_and_pend_(PWS_SPACE_);
394  write_("&");
395  write_(m_tree->key_anchor(node));
396  }
397  if(ty.m_bits & KEYTAG)
398  {
399  write_pws_and_pend_(PWS_SPACE_);
400  write_tag_(m_tree->key_tag(node));
401  }
402  if(ty.m_bits & KEYREF)
403  {
404  write_pws_and_pend_(PWS_SPACE_);
405  write_ref_(m_tree->key(node));
406  }
407  else
408  {
409  write_pws_and_pend_(PWS_NONE_);
410  csubstr key = m_tree->key(node);
411  if(!(ty.m_bits & detail::styles_flow_key_))
413  flow_write_scalar_(key, ty.m_bits & detail::styles_flow_key_);
414  }
415  write_pws_and_pend_(PWS_SPACE_);
416  write_(':');
417  if(ty.m_bits & VALANCH)
418  {
419  write_pws_and_pend_(PWS_SPACE_);
420  write_('&');
421  write_(m_tree->val_anchor(node));
422  }
423  if(ty.m_bits & VALTAG)
424  {
425  write_pws_and_pend_(PWS_SPACE_);
426  write_tag_(m_tree->val_tag(node));
427  }
428 }
429 
430 
431 //-----------------------------------------------------------------------------
432 
433 template<class Writer>
434 C4_NODISCARD bool Emitter<Writer>::maybe_start_flow_pws_ml_(id_type node) noexcept
435 {
436  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->type(node) & (FLOW_ML1|FLOW_MLN), m_tree, node);
437  if(m_flow_pws.active)
438  return false;
439  NodeType ty = m_tree->type(node);
440  if(m_opts.force_flow_spc())
441  ty |= FLOW_SPC;
442  m_flow_pws.start(ty, m_opts.max_cols());
443  return true;
444 }
445 
446 template<class Writer>
447 C4_NODISCARD typename Emitter<Writer>::flow_pws Emitter<Writer>::setup_flow_pws_sl_(id_type node) noexcept
448 {
449  flow_pws ret = {};
450  if(m_flow_pws.active)
451  {
452  ret = m_flow_pws;
453  }
454  else
455  {
456  NodeType ty = m_tree->type(node);
457  if(m_opts.force_flow_spc())
458  ty |= FLOW_SPC;
459  ret.start(ty, 0);
460  }
461  return ret;
462 }
463 
464 template<class Writer>
465 void Emitter<Writer>::flow_pws::start(NodeType ty, size_t max_cols_) noexcept
466 {
467  max_cols = 0;
468  pend_after_comma = (ty.m_bits & FLOW_SPC) ? PWS_SPACE_ : PWS_NONE_;
469  if(ty.m_bits & FLOW_MLN)
470  {
471  max_cols_ = max_cols_ >= 2 ? max_cols_ : 2;
472  // subtract 1 for the comma, and maybe the space from pend_after_comma
473  max_cols = max_cols_ - 1 - pend_after_comma;
474  // line above only works if:
475  static_assert((size_t)PWS_NONE_ == 0 && (size_t)PWS_SPACE_ == 1, "invalid assumptions");
476  active = true;
477  }
478  else if(ty.m_bits & FLOW_ML1)
479  {
480  pend_after_comma = PWS_NEWL_;
481  }
482 }
483 
484 template<class Writer>
485 void Emitter<Writer>::flow_close_entry_sl_(id_type node, id_type last_sibling, Pws_e pend_after)
486 {
487  if(node != last_sibling)
488  {
489  write_pws_and_pend_(pend_after);
490  write_(',');
491  }
492 }
493 
494 template<class Writer>
495 void Emitter<Writer>::flow_close_entry_ml_(id_type node, id_type last_sibling, Pws_e pend_after)
496 {
497  if(node != last_sibling)
498  {
499  write_pws_and_pend_(pend_after);
500  write_(',');
501  }
502  else
503  {
504  pend_newl_();
505  }
506 }
507 
508 
509 //-----------------------------------------------------------------------------
510 
511 template<class Writer>
512 void Emitter<Writer>::blck_seq_open_entry_(id_type node)
513 {
514  NodeType ty = m_tree->type(node);
515  write_pws_and_pend_(PWS_NONE_);
516  write_pws_and_pend_(PWS_SPACE_); // pend the space after the following dash
517  write_('-');
518  bool has_tag_or_anchor = false;
519  if(ty.m_bits & VALANCH)
520  {
521  has_tag_or_anchor = true;
522  write_pws_and_pend_(PWS_SPACE_);
523  write_('&');
524  write_(m_tree->val_anchor(node));
525  }
526  if(ty.m_bits & VALTAG)
527  {
528  has_tag_or_anchor = true;
529  write_pws_and_pend_(PWS_SPACE_);
530  write_tag_(m_tree->val_tag(node));
531  }
532  if(has_tag_or_anchor && ty.is_container())
533  {
534  if(!(ty.m_bits & CONTAINER_STYLE))
535  ty |= BLOCK;
536  if((ty.m_bits & BLOCK) && m_tree->has_children(node))
537  pend_newl_();
538  }
539 }
540 
541 
542 //-----------------------------------------------------------------------------
543 
544 template<class Writer>
545 void Emitter<Writer>::blck_map_open_entry_(id_type node)
546 {
547  NodeType ty = m_tree->type(node);
548  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.has_key(), m_tree, node);
549  csubstr key = m_tree->key(node);
550  if(!(ty.m_bits & (KEY_STYLE|KEYREF)))
551  ty.m_bits |= (scalar_style_choose_block(key).m_bits & KEY_STYLE);
552  write_pws_and_pend_(PWS_NONE_);
553  if(ty.m_bits & KEYANCH)
554  {
555  write_pws_and_pend_(PWS_SPACE_);
556  write_('&');
557  write_(m_tree->key_anchor(node));
558  }
559  if(ty.m_bits & KEYTAG)
560  {
561  write_pws_and_pend_(PWS_SPACE_);
562  write_tag_(m_tree->key_tag(node));
563  }
564  if(ty.m_bits & KEYREF)
565  {
566  write_pws_and_pend_(PWS_SPACE_);
567  write_ref_(key);
568  }
569  else
570  {
571  write_pws_and_pend_(PWS_NONE_);
572  const type_bits use_qmrk = ty.m_bits & detail::styles_block_key_;
573  if(!use_qmrk)
574  {
575  blck_write_scalar_(key, ty.m_bits & KEY_STYLE);
576  }
577  else
578  {
579  write_("? ");
580  blck_write_scalar_(key, ty.m_bits & KEY_STYLE);
581  pend_newl_();
582  }
583  }
584  write_pws_and_pend_(PWS_SPACE_); // pend the space after the colon
585  write_(':');
586  if(ty.m_bits & VALANCH)
587  {
588  write_pws_and_pend_(PWS_SPACE_);
589  write_('&');
590  write_(m_tree->val_anchor(node));
591  }
592  if(ty.m_bits & VALTAG)
593  {
594  write_pws_and_pend_(PWS_SPACE_);
595  write_tag_(m_tree->val_tag(node));
596  }
597  if(ty.is_container() && m_tree->has_children(node))
598  {
599  if(!(ty.m_bits & CONTAINER_STYLE))
600  ty |= BLOCK;
601  if(ty.is_block())
602  pend_newl_();
603  }
604 }
605 
606 
607 //-----------------------------------------------------------------------------
608 
609 template<class Writer>
610 void Emitter<Writer>::blck_close_entry_(id_type node)
611 {
612  (void)node;
613  pend_newl_();
614 }
615 
616 
617 //-----------------------------------------------------------------------------
618 
619 template<class Writer>
620 void Emitter<Writer>::visit_blck_seq_(id_type node)
621 {
622  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->is_seq(node), m_tree, node);
623  bool empty = true;
624  for(id_type child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))
625  {
626  empty = false;
627  NodeType ty = m_tree->type(child);
628  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.is_val() || ty.is_container() || ty == NOTYPE, m_tree, node);
629  blck_seq_open_entry_(child);
630  if(ty.is_val())
631  {
632  write_pws_and_pend_(PWS_NONE_);
633  csubstr val = m_tree->val(child);
634  if(!ty.is_val_ref())
635  {
636  if(!(ty.m_bits & VAL_STYLE))
637  ty.m_bits |= (scalar_style_choose_block(val).m_bits & VAL_STYLE);
638  blck_write_scalar_(val, ty.m_bits & VAL_STYLE);
639  }
640  else
641  {
642  write_ref_(val);
643  }
644  }
645  else if(ty.is_container())
646  {
647  ++m_depth;
648  ++m_ilevel;
649  visit_blck_container_(child);
650  --m_depth;
651  --m_ilevel;
652  }
653  blck_close_entry_(child);
654  }
655  if(empty)
656  {
657  write_pws_and_pend_(PWS_NONE_);
658  write_("[]");
659  }
660 }
661 
662 
663 //-----------------------------------------------------------------------------
664 
665 template<class Writer>
666 void Emitter<Writer>::visit_blck_map_(id_type node)
667 {
668  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->is_map(node), m_tree, node);
669  bool empty = true;
670  for(id_type child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))
671  {
672  empty = false;
673  NodeType ty = m_tree->type(child);
674  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.is_keyval() || ty.is_container() || ty == NOTYPE, m_tree, node);
675  blck_map_open_entry_(child); // also writes the key
676  if(ty.is_keyval())
677  {
678  write_pws_and_pend_(PWS_NONE_);
679  csubstr val = m_tree->val(child);
680  if(!ty.is_val_ref())
681  {
682  if(!(ty.m_bits & VAL_STYLE))
684  blck_write_scalar_(val, ty.m_bits & VAL_STYLE);
685  }
686  else
687  {
688  write_ref_(val);
689  }
690  }
691  else if(ty.is_container())
692  {
693  ++m_depth;
694  ++m_ilevel;
695  visit_blck_container_(child);
696  --m_depth;
697  --m_ilevel;
698  }
699  blck_close_entry_(child);
700  }
701  if(empty)
702  {
703  write_pws_and_pend_(PWS_NONE_);
704  write_("{}");
705  }
706 }
707 
708 
709 //-----------------------------------------------------------------------------
710 
711 template<class Writer>
712 void Emitter<Writer>::visit_flow_sl_seq_(id_type node)
713 {
714  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->is_seq(node), m_tree, node);
715  const flow_pws pws = setup_flow_pws_sl_(node);
716  write_('[');
717  for(id_type child = m_tree->first_child(node), last = m_tree->last_child(node); child != NONE; child = m_tree->next_sibling(child))
718  {
719  NodeType ty = m_tree->type(child);
720  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), (ty & (VAL|SEQ|MAP)) || ty == NOTYPE, m_tree, node);
721  flow_seq_open_entry_(child);
722  if(ty.m_bits & VAL)
723  {
724  write_pws_and_pend_(PWS_NONE_);
725  csubstr val = m_tree->val(child);
726  if(!ty.is_val_ref())
727  {
728  if(!(ty.m_bits & detail::styles_flow_val_))
730  flow_write_scalar_(val, ty.m_bits & detail::styles_flow_val_);
731  }
732  else
733  {
734  write_ref_(val);
735  }
736  }
737  else if(ty.m_bits & (SEQ|MAP))
738  {
739  ++m_depth;
740  visit_flow_container_(child);
741  --m_depth;
742  }
743  flow_close_entry_sl_(child, last, pws.next_pws(m_col));
744  }
745  write_(']');
746 }
747 
748 
749 //-----------------------------------------------------------------------------
750 
751 template<class Writer>
752 void Emitter<Writer>::visit_flow_ml_seq_(id_type node)
753 {
754  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->is_seq(node), m_tree, node);
755  write_('[');
756  pend_newl_();
757  if(m_opts.indent_flow_ml()) ++m_ilevel;
758  const bool stop_at_end = maybe_start_flow_pws_ml_(node);
759  for(id_type child = m_tree->first_child(node), last = m_tree->last_child(node); child != NONE; child = m_tree->next_sibling(child))
760  {
761  NodeType ty = m_tree->type(child);
762  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.is_val() || ty.is_container() || ty == NOTYPE, m_tree, node);
763  flow_seq_open_entry_(child);
764  if(ty.is_val())
765  {
766  write_pws_and_pend_(PWS_NONE_);
767  csubstr val = m_tree->val(child);
768  if(!ty.is_val_ref())
769  {
770  if(!(ty.m_bits & detail::styles_flow_val_))
772  flow_write_scalar_(val, ty.m_bits & detail::styles_flow_val_);
773  }
774  else
775  {
776  write_ref_(val);
777  }
778  }
779  else if(ty.is_container())
780  {
781  ++m_depth;
782  visit_flow_container_(child);
783  --m_depth;
784  }
785  flow_close_entry_ml_(child, last, m_flow_pws.next_pws(m_col));
786  }
787  if(stop_at_end)
788  m_flow_pws.stop();
789  if(m_opts.indent_flow_ml()) --m_ilevel;
790  write_pws_and_pend_(PWS_NONE_);
791  write_(']');
792 }
793 
794 
795 //-----------------------------------------------------------------------------
796 
797 template<class Writer>
798 void Emitter<Writer>::visit_flow_sl_map_(id_type node)
799 {
800  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->is_map(node), m_tree, node);
801  flow_pws pws = setup_flow_pws_sl_(node);
802  write_('{');
803  for(id_type child = m_tree->first_child(node), last = m_tree->last_child(node); child != NONE; child = m_tree->next_sibling(child))
804  {
805  NodeType ty = m_tree->type(child);
806  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.has_key() && (ty.has_val() || ty.is_container() || ty == NOTYPE), m_tree, node);
807  flow_map_open_entry_(child);
808  if(ty.has_val())
809  {
810  write_pws_and_pend_(PWS_NONE_);
811  csubstr val = m_tree->val(child);
812  if(!ty.is_val_ref())
813  {
814  if(!(ty.m_bits & detail::styles_flow_val_))
816  flow_write_scalar_(val, ty.m_bits & detail::styles_flow_val_);
817  }
818  else
819  {
820  write_ref_(val);
821  }
822  }
823  else if(ty.is_container())
824  {
825  ++m_depth;
826  visit_flow_container_(child);
827  --m_depth;
828  }
829  flow_close_entry_sl_(child, last, pws.next_pws(m_col));
830  }
831  write_pws_and_pend_(PWS_NONE_);
832  write_('}');
833 }
834 
835 
836 //-----------------------------------------------------------------------------
837 
838 template<class Writer>
839 void Emitter<Writer>::visit_flow_ml_map_(id_type node)
840 {
841  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->is_map(node), m_tree, node);
842  write_('{');
843  pend_newl_();
844  if(m_opts.indent_flow_ml()) ++m_ilevel;
845  const bool stop_at_end = maybe_start_flow_pws_ml_(node);
846  for(id_type child = m_tree->first_child(node), last = m_tree->last_child(node); child != NONE; child = m_tree->next_sibling(child))
847  {
848  NodeType ty = m_tree->type(child);
849  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.has_key() && (ty.has_val() || ty.is_container() || ty == NOTYPE), m_tree, node);
850  flow_map_open_entry_(child);
851  if(ty.has_val())
852  {
853  write_pws_and_pend_(PWS_NONE_);
854  csubstr val = m_tree->val(child);
855  if(!ty.is_val_ref())
856  {
857  if(!(ty.m_bits & detail::styles_flow_val_))
859  flow_write_scalar_(val, ty.m_bits & detail::styles_flow_val_);
860  }
861  else
862  {
863  write_ref_(val);
864  }
865  }
866  else if(ty.is_container())
867  {
868  ++m_depth;
869  visit_flow_container_(child);
870  --m_depth;
871  }
872  flow_close_entry_ml_(child, last, m_flow_pws.next_pws(m_col));
873  }
874  if(stop_at_end)
875  m_flow_pws.stop();
876  if(m_opts.indent_flow_ml()) --m_ilevel;
877  write_pws_and_pend_(PWS_NONE_);
878  write_('}');
879 }
880 
881 
882 //-----------------------------------------------------------------------------
883 
884 template<class Writer>
885 void Emitter<Writer>::visit_blck_(id_type node)
886 {
887  const NodeType ty = m_tree->type(node);
888  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), !ty.is_stream(), m_tree, node);
889  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.is_container() || ty.is_doc(), m_tree, node);
890  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)), m_tree, node);
891  if C4_UNLIKELY(m_depth > m_opts.max_depth())
892  RYML_ERR_VISIT_CB_(m_tree->callbacks(), m_tree, node, "max depth exceeded");
893  if(ty.is_seq())
894  {
895  visit_blck_seq_(node);
896  }
897  else
898  {
899  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.is_map(), m_tree, node);
900  visit_blck_map_(node);
901  }
902 }
903 
904 
905 //-----------------------------------------------------------------------------
906 
907 template<class Writer>
908 void Emitter<Writer>::visit_flow_sl_(id_type node)
909 {
910  const NodeType ty = m_tree->type(node);
911  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), !ty.is_stream(), m_tree, node);
912  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.is_container() || ty.is_doc(), m_tree, node);
913  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)), m_tree, node);
914  if C4_UNLIKELY(m_depth > m_opts.max_depth())
915  RYML_ERR_VISIT_CB_(m_tree->callbacks(), m_tree, node, "max depth exceeded");
916  if(ty.m_bits & SEQ)
917  {
918  visit_flow_sl_seq_(node);
919  }
920  else
921  {
922  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.is_map(), m_tree, node);
923  visit_flow_sl_map_(node);
924  }
925 }
926 
927 
928 //-----------------------------------------------------------------------------
929 
930 template<class Writer>
931 void Emitter<Writer>::visit_flow_ml_(id_type node)
932 {
933  const NodeType ty = m_tree->type(node);
934  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), !ty.is_stream(), m_tree, node);
935  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.is_container() || ty.is_doc(), m_tree, node);
936  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)), m_tree, node);
937  if C4_UNLIKELY(m_depth > m_opts.max_depth())
938  RYML_ERR_VISIT_CB_(m_tree->callbacks(), m_tree, node, "max depth exceeded");
939  if(ty.m_bits & SEQ)
940  {
941  visit_flow_ml_seq_(node);
942  }
943  else
944  {
945  RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), ty.is_map(), m_tree, node);
946  visit_flow_ml_map_(node);
947  }
948 }
949 
950 
951 //-----------------------------------------------------------------------------
952 
953 template<class Writer>
954 void Emitter<Writer>::flow_write_scalar_(csubstr str, type_bits ty)
955 {
956  RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), !(ty & detail::styles_block_));
957  if((ty & detail::styles_plain_) || !(ty & SCALAR_STYLE))
958  {
959  write_scalar_plain_(str, m_ilevel);
960  }
961  else if(ty & detail::styles_squo_)
962  {
963  write_scalar_squo_(str, m_ilevel);
964  }
965  else // if(ty & detail::styles_dquo_)
966  {
967  write_scalar_dquo_(str, m_ilevel);
968  }
969 }
970 
971 template<class Writer>
972 void Emitter<Writer>::blck_write_scalar_(csubstr str, type_bits ty)
973 {
974  if((ty & detail::styles_plain_) || !(ty & SCALAR_STYLE))
975  {
976  write_scalar_plain_(str, m_ilevel);
977  }
978  else if(ty & detail::styles_squo_)
979  {
980  write_scalar_squo_(str, m_ilevel);
981  }
982  else if(ty & detail::styles_dquo_)
983  {
984  write_scalar_dquo_(str, m_ilevel);
985  }
986  else if(ty & detail::styles_literal_)
987  {
988  write_scalar_literal_(str, m_ilevel);
989  }
990  else // if(ty & detail::styles_folded_)
991  {
992  write_scalar_folded_(str, m_ilevel);
993  }
994 }
995 
996 template<class Writer>
997 size_t Emitter<Writer>::write_escaped_newlines_(csubstr s, size_t i)
998 {
999  RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), s.len > i);
1000  RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), s.str[i] == '\n');
1001  //_c4dbgpf("nl@i={} rem=[{}]~~~{}~~~", i, s.sub(i).len, s.sub(i));
1002  // add an extra newline for each sequence of consecutive
1003  // newline/whitespace
1004  newl_();
1005  do
1006  {
1007  newl_(); // write the newline again
1008  ++i; // increase the outer loop counter!
1009  } while(i < s.len && s.str[i] == '\n');
1010  RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), i > 0);
1011  --i;
1012  RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), s.str[i] == '\n');
1013  return i;
1014 }
1015 
1016 
1017 inline bool _is_indented_block(csubstr s, size_t prev, size_t i) noexcept
1018 {
1019  if(prev == 0 && s.begins_with_any(" \t"))
1020  return true;
1021  const size_t pos = s.first_not_of('\n', i);
1022  return (pos != npos) && (s.str[pos] == ' ' || s.str[pos] == '\t');
1023 }
1024 
1025 
1026 template<class Writer>
1027 size_t Emitter<Writer>::write_indented_block_(csubstr s, size_t i, id_type ilevel)
1028 {
1029  //_c4dbgpf("indblock@i={} rem=[{}]~~~\n{}~~~", i, s.sub(i).len, s.sub(i));
1030  RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), i > 0);
1031  RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), s.str[i-1] == '\n');
1032  RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), i < s.len);
1033  RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), s.str[i] == ' ' || s.str[i] == '\t' || s.str[i] == '\n');
1034 again:
1035  size_t pos = s.find("\n ", i);
1036  if(pos == npos)
1037  pos = s.find("\n\t", i);
1038  if(pos != npos)
1039  {
1040  ++pos;
1041  //_c4dbgpf("indblock line@i={} rem=[{}]~~~\n{}~~~", i, s.range(i, pos).len, s.range(i, pos));
1042  indent_(ilevel + 1);
1043  write_(s.range(i, pos));
1044  i = pos;
1045  goto again; // NOLINT
1046  }
1047  // consume the newlines after the indented block
1048  // to prevent them from being escaped
1049  pos = s.find('\n', i);
1050  if(pos != npos)
1051  {
1052  const size_t pos2 = s.first_not_of('\n', pos);
1053  pos = (pos2 != npos) ? pos2 : pos;
1054  //_c4dbgpf("indblock line@i={} rem=[{}]~~~\n{}~~~", i, s.range(i, pos).len, s.range(i, pos));
1055  indent_(ilevel + 1);
1056  write_(s.range(i, pos));
1057  i = pos;
1058  }
1059  return i;
1060 }
1061 
1062 template<class Writer>
1063 void Emitter<Writer>::write_scalar_literal_(csubstr s, id_type ilevel)
1064 {
1065  RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), s.find("\r") == csubstr::npos);
1066  csubstr trimmed = s.trimr('\n');
1067  const size_t numnewlines_at_end = s.len - trimmed.len;
1068  const bool is_newline_only = (trimmed.len == 0 && (s.len > 0));
1069  const bool explicit_indentation = s.triml("\n\r").begins_with_any(" \t");
1070  //
1071  write_('|');
1072  if(explicit_indentation)
1073  write_('2');
1074  //
1075  if(numnewlines_at_end > 1 || is_newline_only)
1076  write_('+');
1077  else if(numnewlines_at_end == 0)
1078  write_('-');
1079  //
1080  if(trimmed.len)
1081  {
1082  newl_();
1083  size_t pos = 0; // tracks the last character that was already written
1084  for(size_t i = 0; i < trimmed.len; ++i)
1085  {
1086  if(trimmed[i] != '\n')
1087  continue;
1088  // write everything up to this point
1089  csubstr since_pos = trimmed.range(pos, i+1); // include the newline
1090  indent_(ilevel + 1);
1091  write_(since_pos);
1092  pos = i+1; // already written
1093  }
1094  if(pos < trimmed.len)
1095  {
1096  indent_(ilevel + 1);
1097  write_(trimmed.sub(pos));
1098  }
1099  }
1100  for(size_t i = !is_newline_only; i < numnewlines_at_end; ++i)
1101  newl_();
1102 }
1103 
1104 template<class Writer>
1105 void Emitter<Writer>::write_scalar_folded_(csubstr s, id_type ilevel)
1106 {
1107  RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), s.find("\r") == csubstr::npos);
1108  csubstr trimmed = s.trimr('\n');
1109  const size_t numnewlines_at_end = s.len - trimmed.len;
1110  const bool is_newline_only = (trimmed.len == 0 && (s.len > 0));
1111  const bool explicit_indentation = s.triml("\n\r").begins_with_any(" \t");
1112  //
1113  write_('>');
1114  if(explicit_indentation)
1115  write_('2');
1116  //
1117  if(numnewlines_at_end == 0)
1118  write_('-');
1119  else if(numnewlines_at_end > 1 || is_newline_only)
1120  write_('+');
1121  //
1122  if(trimmed.len)
1123  {
1124  newl_();
1125  size_t pos = 0; // tracks the last character that was already written
1126  for(size_t i = 0; i < trimmed.len; ++i)
1127  {
1128  if(trimmed[i] != '\n')
1129  continue;
1130  // escape newline sequences
1131  if( ! _is_indented_block(s, pos, i))
1132  {
1133  if(pos < i)
1134  {
1135  indent_(ilevel + 1);
1136  write_(s.range(pos, i));
1137  i = write_escaped_newlines_(s, i);
1138  pos = i + 1;
1139  }
1140  else
1141  {
1142  if(i+1 < s.len)
1143  {
1144  if(s.str[i+1] == '\n')
1145  {
1146  ++i;
1147  i = write_escaped_newlines_(s, i);
1148  pos = i+1;
1149  }
1150  else
1151  {
1152  newl_();
1153  pos = i+1;
1154  }
1155  }
1156  }
1157  }
1158  else // do not escape newlines in indented blocks
1159  {
1160  ++i;
1161  indent_(ilevel + 1);
1162  write_(s.range(pos, i));
1163  if(pos > 0 || !s.begins_with_any(" \t"))
1164  i = write_indented_block_(s, i, ilevel);
1165  pos = i;
1166  }
1167  }
1168  if(pos < trimmed.len)
1169  {
1170  indent_(ilevel + 1);
1171  write_(trimmed.sub(pos));
1172  }
1173  }
1174  for(size_t i = !is_newline_only; i < numnewlines_at_end; ++i)
1175  newl_();
1176 }
1177 
1178 template<class Writer>
1179 void Emitter<Writer>::write_scalar_squo_(csubstr s, id_type ilevel)
1180 {
1181  size_t pos = 0; // tracks the last character that was already written
1182  write_('\'');
1183  for(size_t i = 0; i < s.len; ++i)
1184  {
1185  if(s[i] == '\n')
1186  {
1187  write_(s.range(pos, i)); // write everything up to (excluding) this char
1188  //_c4dbgpf("newline at {}. writing ~~~{}~~~", i, s.range(pos, i));
1189  i = write_escaped_newlines_(s, i);
1190  //_c4dbgpf("newline --> {}", i);
1191  if(i < s.len)
1192  indent_(ilevel + 1);
1193  pos = i+1;
1194  }
1195  else if(s[i] == '\'')
1196  {
1197  csubstr sub = s.range(pos, i+1);
1198  //_c4dbgpf("squote at {}. writing ~~~{}~~~", i, sub);
1199  write_(sub); // write everything up to (including) this squote
1200  write_('\''); // write the squote again
1201  pos = i+1;
1202  }
1203  }
1204  // write remaining characters at the end of the string
1205  if(pos < s.len)
1206  write_(s.sub(pos));
1207  write_('\'');
1208 }
1209 
1210 template<class Writer>
1211 void Emitter<Writer>::write_scalar_dquo_(csubstr s, id_type ilevel)
1212 {
1213  size_t pos = 0; // tracks the last character that was already written
1214  write_('"');
1215  for(size_t i = 0; i < s.len; ++i)
1216  {
1217  const char curr = s.str[i];
1218  switch(curr) // NOLINT
1219  {
1220  case '"':
1221  case '\\':
1222  {
1223  csubstr sub = s.range(pos, i);
1224  write_(sub); // write everything up to (excluding) this char
1225  write_('\\'); // write the escape
1226  write_(curr); // write the char
1227  pos = i+1;
1228  break;
1229  }
1230 #ifndef prefer_writing_newlines_as_double_newlines
1231  case '\n':
1232  {
1233  csubstr sub = s.range(pos, i);
1234  write_(sub); // write everything up to (excluding) this char
1235  write_("\\n"); // write the escape
1236  pos = i+1;
1237  (void)ilevel;
1238  break;
1239  }
1240 #else
1241  case '\n':
1242  {
1243  // write everything up to (excluding) this newline
1244  //_c4dbgpf("nl@i={} rem=[{}]~~~{}~~~", i, s.sub(i).len, s.sub(i));
1245  _write(s.range(pos, i));
1246  i = _write_escaped_newlines(s, i);
1247  ++i;
1248  pos = i;
1249  // as for the next line...
1250  if(i < s.len)
1251  {
1252  _indent(ilevel + 1); // indent the next line
1253  // escape leading whitespace, and flush it
1254  size_t first = s.first_not_of(" \t", i);
1255  //_c4dbgpf("@i={} first={} rem=[{}]~~~{}~~~", i, first, s.sub(i).len, s.sub(i));
1256  if(first > i)
1257  {
1258  if(first == npos)
1259  first = s.len;
1260  _write('\\');
1261  _write(s.range(i, first));
1262  _write('\\');
1263  i = first-1;
1264  pos = first;
1265  }
1266  }
1267  break;
1268  }
1269  // escape trailing whitespace before a newline
1270  case ' ':
1271  case '\t':
1272  {
1273  const size_t next = s.first_not_of(" \t\r", i);
1274  if(next != npos && s.str[next] == '\n')
1275  {
1276  csubstr sub = s.range(pos, i);
1277  _write(sub); // write everything up to (excluding) this char
1278  _write('\\'); // escape the whitespace
1279  pos = i;
1280  }
1281  break;
1282  }
1283 #endif
1284  case '\r':
1285  {
1286  csubstr sub = s.range(pos, i);
1287  write_(sub); // write everything up to (excluding) this char
1288  write_("\\r"); // write the escaped char
1289  pos = i+1;
1290  break;
1291  }
1292  case '\b':
1293  {
1294  csubstr sub = s.range(pos, i);
1295  write_(sub); // write everything up to (excluding) this char
1296  write_("\\b"); // write the escaped char
1297  pos = i+1;
1298  break;
1299  }
1300  }
1301  }
1302  // write remaining characters at the end of the string
1303  if(pos < s.len)
1304  write_(s.sub(pos));
1305  write_('"');
1306 }
1307 
1308 template<class Writer>
1309 void Emitter<Writer>::write_scalar_plain_(csubstr s, id_type ilevel)
1310 {
1311  if C4_UNLIKELY(ilevel == 0 && (s.begins_with("...") || s.begins_with("---")))
1312  {
1313  indent_(ilevel + 1); // indent the next line
1314  ++ilevel;
1315  }
1316  size_t pos = 0; // tracks the last character that was already written
1317  for(size_t i = 0; i < s.len; ++i)
1318  {
1319  const char curr = s.str[i];
1320  if(curr == '\n')
1321  {
1322  csubstr sub = s.range(pos, i);
1323  write_(sub); // write everything up to (including) this newline
1324  i = write_escaped_newlines_(s, i);
1325  pos = i+1;
1326  if(pos < s.len)
1327  indent_(ilevel + 1); // indent the next line
1328  }
1329  }
1330  // write remaining characters at the end of the string
1331  if(pos < s.len)
1332  write_(s.sub(pos));
1333 }
1334 
1335 
1336 //-----------------------------------------------------------------------------
1337 
1338 namespace detail {
1339 inline type_bits json_type_(type_bits ty)
1340 {
1341  enum : type_bits { // NOLINT
1342  ml_bits = (BLOCK|(STREAM & ~SEQ)), // remove SEQ from STREAM to test
1343  sl_bits = (CONTAINER_STYLE & ~FLOW_SPC),
1344  };
1345  if(ty & ml_bits)
1346  {
1347  ty &= ~BLOCK;
1348  ty |= FLOW_ML1;
1349  }
1350  else if((ty & (SEQ|MAP)) && !(ty & sl_bits))
1351  {
1352  ty |= FLOW_SL;
1353  }
1354  return ty;
1355 }
1356 } // namespace detail
1357 
1358 
1359 template<class Writer>
1360 void Emitter<Writer>::json_emit_(id_type id)
1361 {
1362  NodeType ty = m_tree->type(id);
1363  // JSON does not have streams
1364  if C4_UNLIKELY(ty.is_stream() && m_opts.json_err_on_stream())
1365  RYML_ERR_VISIT_CB_(m_tree->callbacks(), m_tree, id, "found stream node");
1366  static_assert(STREAM & SEQ, "STREAM must be a SEQ");
1367  ty = detail::json_type_(ty);
1368  if(ty.is_flow_mlx())
1369  {
1370  json_visit_ml_(id, ty, 0);
1371  newl_();
1372  }
1373  else
1374  {
1375  json_visit_sl_(id, ty, 0);
1376  }
1377 }
1378 
1379 template<class Writer>
1380 void Emitter<Writer>::json_visit_sl_(id_type id, NodeType ty, id_type depth)
1381 {
1382  if C4_UNLIKELY(depth > m_opts.max_depth())
1383  RYML_ERR_VISIT_CB_(m_tree->callbacks(), m_tree, id, "max depth exceeded");
1384  if(ty.is_val())
1385  {
1386  json_writev_(id, ty);
1387  }
1388  else if(ty.is_keyval())
1389  {
1390  json_writek_(id, ty);
1391  write_(": ");
1392  json_writev_(id, ty);
1393  }
1394  else if(ty.is_container())
1395  {
1396  ty = detail::json_type_(ty);
1397  if(ty.has_key())
1398  {
1399  json_writek_(id, ty);
1400  write_(": ");
1401  }
1402  if(ty.is_seq())
1403  write_('[');
1404  else if(ty.is_map())
1405  write_('{');
1406 
1407  for(id_type child = m_tree->first_child(id); child != NONE; child = m_tree->next_sibling(child))
1408  {
1409  if(child != m_tree->first_child(id))
1410  {
1411  if((ty & FLOW_SPC) || m_opts.force_flow_spc())
1412  write_(", ");
1413  else
1414  write_(',');
1415  }
1416  json_visit_sl_(child, m_tree->type(child), depth+1);
1417  }
1418 
1419  if(ty.is_seq())
1420  write_(']');
1421  else if(ty.is_map())
1422  write_('}');
1423  } // container
1424 }
1425 
1426 template<class Writer>
1427 void Emitter<Writer>::json_visit_ml_(id_type id, NodeType ty, id_type depth)
1428 {
1429  if C4_UNLIKELY(depth > m_opts.max_depth())
1430  RYML_ERR_VISIT_CB_(m_tree->callbacks(), m_tree, id, "max depth exceeded");
1431  if(ty.is_val())
1432  {
1433  json_writev_(id, ty);
1434  }
1435  else if(ty.is_keyval())
1436  {
1437  json_writek_(id, ty);
1438  write_(": ");
1439  json_writev_(id, ty);
1440  }
1441  else if(ty.is_container())
1442  {
1443  ty = detail::json_type_(ty);
1444  if(ty.has_key())
1445  {
1446  json_writek_(id, ty);
1447  write_(": ");
1448  }
1449  if(ty.is_seq())
1450  write_('[');
1451  else if(ty.is_map())
1452  write_('{');
1453 
1454  if(m_tree->has_children(id))
1455  {
1456  ++depth;
1457  if(m_opts.indent_flow_ml()) ++m_ilevel;
1458  newl_();
1459  indent_(m_ilevel);
1460  for(id_type first = m_tree->first_child(id), child = first;
1461  child != NONE;
1462  child = m_tree->next_sibling(child))
1463  {
1464  if(child != first)
1465  {
1466  write_(',');
1467  const size_t maxcols = m_opts.max_cols();
1468  if((ty.m_bits & FLOW_MLN) && (m_col+1 < maxcols))
1469  {
1470  if((ty.m_bits & FLOW_SPC) || m_opts.force_flow_spc())
1471  write_(' ');
1472  }
1473  else if((ty.m_bits & FLOW_ML1) || (m_col+1 >= maxcols))
1474  {
1475  newl_();
1476  indent_(m_ilevel);
1477  }
1478  }
1479  NodeType chty = m_tree->type(child);
1480  if(chty.is_flow_sl())
1481  json_visit_sl_(child, chty, depth);
1482  else
1483  json_visit_ml_(child, chty, depth);
1484  }
1485  if(m_opts.indent_flow_ml()) --m_ilevel;
1486  --depth;
1487  newl_();
1488  indent_(m_ilevel);
1489  }
1490 
1491  if(ty.is_seq())
1492  write_(']');
1493  else if(ty.is_map())
1494  write_('}');
1495  }
1496 }
1497 
1498 template<class Writer>
1499 bool Emitter<Writer>::json_maybe_write_naninf_(csubstr s)
1500 {
1501  switch(s.len)
1502  {
1503  case 3: case 4: case 5: // inf, nan, .nan, -.inf
1504  case 8: case 9: // infinity, -infinity
1505  break;
1506  default:
1507  return false;
1508  }
1509  const char first = s.str[0];
1510  csubstr rest = s.sub(1);
1511  if(s.len == 4 && first == '.')
1512  {
1513  if(scalar_is_inf3(rest.str))
1514  goto write_inf_positive; // NOLINT
1515  else if(scalar_is_nan3(rest.str))
1516  goto write_nan; // NOLINT
1517  }
1518  else if(first == '-' || first == '+') // begins with sign: must be inf
1519  {
1520  // match [-+].inf
1521  if((rest.len == 4 && rest.str[0] == '.' && scalar_is_inf3(rest.str + 1))
1522  // match [-+]inf
1523  || (rest.len == 3 && scalar_is_inf3(rest.str))
1524  // match [-+]infinity
1525  || (rest.len == 8 && (0 == memcmp(rest.str, "infinity", 8))))
1526  {
1527  if(first == '-')
1528  goto write_inf_negative; // NOLINT
1529  else
1530  goto write_inf_positive; // NOLINT
1531  }
1532  }
1533  else if(s.len == 8 && (0 == memcmp(s.str, "infinity", 8)))
1534  {
1535  goto write_inf_positive; // NOLINT
1536  }
1537  else if(s.len == 3)
1538  {
1539  if(scalar_is_inf3(s.str))
1540  goto write_inf_positive; // NOLINT
1541  else if(scalar_is_nan3(s.str))
1542  goto write_nan; // NOLINT
1543  }
1544  return false;
1545 write_inf_positive:
1546  write_("\".inf\"");
1547  return true;
1548 write_inf_negative:
1549  write_("\"-.inf\"");
1550  return true;
1551 write_nan:
1552  write_("\".nan\"");
1553  return true;
1554 }
1555 
1556 template<class Writer>
1557 void Emitter<Writer>::json_writek_(id_type id, NodeType ty)
1558 {
1559  if C4_UNLIKELY(ty.has_key_tag() && m_opts.json_err_on_tag())
1560  RYML_ERR_VISIT_CB_(m_tree->callbacks(), m_tree, id, "JSON does not have tags");
1561  if C4_UNLIKELY(ty.has_key_anchor() && m_opts.json_err_on_anchor())
1562  RYML_ERR_VISIT_CB_(m_tree->callbacks(), m_tree, id, "JSON does not have anchors");
1563  csubstr key = m_tree->key(id);
1564  if(key.len)
1565  {
1566  if(json_maybe_write_naninf_(key))
1567  ;
1568  else
1569  json_write_scalar_dquo_(key);
1570  }
1571  else
1572  {
1573  write_("\"\"");
1574  }
1575 }
1576 
1577 template<class Writer>
1578 void Emitter<Writer>::json_writev_(id_type id, NodeType ty)
1579 {
1580  if C4_UNLIKELY(ty.has_val_tag() && m_opts.json_err_on_tag())
1581  RYML_ERR_VISIT_CB_(m_tree->callbacks(), m_tree, id, "JSON does not have tags");
1582  if C4_UNLIKELY(ty.has_val_anchor() && m_opts.json_err_on_anchor())
1583  RYML_ERR_VISIT_CB_(m_tree->callbacks(), m_tree, id, "JSON does not have anchors");
1584  csubstr val = m_tree->val(id);
1585  if(val.len)
1586  {
1587  // use double quoted style if the style is marked quoted
1588  bool dquoted = ((ty.m_bits & VALQUO)
1589  || (scalar_style_choose_json(val).m_bits & SCALAR_DQUO)); // choose the style
1590  if(dquoted)
1591  json_write_scalar_dquo_(val);
1592  else if(json_maybe_write_naninf_(val))
1593  ;
1594  else if(val.is_number())
1595  json_write_number_(val);
1596  else
1597  write_(val);
1598  }
1599  else
1600  {
1601  if(val.str || (ty.m_bits & (VALQUO|VALTAG)))
1602  write_("\"\"");
1603  else
1604  write_("null");
1605  }
1606 }
1607 
1608 
1609 template<class Writer>
1610 void Emitter<Writer>::json_write_scalar_dquo_(csubstr s)
1611 {
1612  size_t pos = 0;
1613  write_('"');
1614  for(size_t i = 0; i < s.len; ++i)
1615  {
1616  switch(s.str[i])
1617  {
1618  case '"':
1619  write_(s.range(pos, i));
1620  write_("\\\"");
1621  pos = i + 1;
1622  break;
1623  case '\n':
1624  write_(s.range(pos, i));
1625  write_("\\n");
1626  pos = i + 1;
1627  break;
1628  case '\t':
1629  write_(s.range(pos, i));
1630  write_("\\t");
1631  pos = i + 1;
1632  break;
1633  case '\\':
1634  write_(s.range(pos, i));
1635  write_("\\\\");
1636  pos = i + 1;
1637  break;
1638  case '\r':
1639  write_(s.range(pos, i));
1640  write_("\\r");
1641  pos = i + 1;
1642  break;
1643  case '\b':
1644  write_(s.range(pos, i));
1645  write_("\\b");
1646  pos = i + 1;
1647  break;
1648  case '\f':
1649  write_(s.range(pos, i));
1650  write_("\\f");
1651  pos = i + 1;
1652  break;
1653  }
1654  }
1655  if(pos < s.len)
1656  {
1657  csubstr sub = s.sub(pos);
1658  write_(sub);
1659  }
1660  write_('"');
1661 }
1662 
1663 template<class Writer>
1664 void Emitter<Writer>::json_write_number_(csubstr s)
1665 {
1666  if(s.is_integer())
1667  {
1668  write_(s);
1669  }
1670  else
1671  {
1672  if(s.begins_with('-') && s.len > 1)
1673  {
1674  csubstr rest = s.sub(1);
1675  if(rest.begins_with('.'))
1676  {
1677  write_("-0");
1678  write_(rest);
1679  }
1680  else if(rest.ends_with('.'))
1681  {
1682  write_(s);
1683  write_('0');
1684  }
1685  else
1686  {
1687  write_(s);
1688  }
1689  }
1690  else if(s.begins_with('.'))
1691  {
1692  write_('0');
1693  write_(s);
1694  }
1695  else if(s.ends_with('.'))
1696  {
1697  write_(s);
1698  write_('0');
1699  }
1700  else
1701  {
1702  write_(s);
1703  }
1704  }
1705 }
1706 
1707 /** @endcond */
1708 
1709 } // namespace yml
1710 } // namespace c4
1711 
1712 // NOLINTEND(modernize-avoid-c-style-cast)
1713 C4_SUPPRESS_WARNING_GCC_CLANG_POP
1714 
1715 #endif /* C4_YML_EMITTTER_DEF_HPP_ */
mark val scalar as plain scalar (unquoted, even when multiline)
Definition: node_type.hpp:156
basic_substring trimr(const C c) const
trim the character c from the right
Definition: substr.hpp:653
the val has an &anchor
Definition: node_type.hpp:42
the key has a tag
Definition: node_type.hpp:43
an index to none
Definition: common.hpp:131
mark key scalar as double quoted "
Definition: node_type.hpp:153
bool is_doc() const noexcept
Definition: node_type.hpp:292
A YAML/JSON emitter, templated on a writer class such as WriterBuf, WriterFile, or WriterOStream...
Definition: emitter.hpp:34
id_type capacity() const
Definition: tree.hpp:346
mark key scalar as multiline, block literal |
Definition: node_type.hpp:147
mark val scalar as single quoted '
Definition: node_type.hpp:152
val style is one of `'">|`. mask of VAL_SQUO|VAL_DQUO|VAL_FOLDED|VAL_LITERAL
Definition: node_type.hpp:175
bool empty() const
Query for zero size.
Definition: tree.hpp:343
NodeType scalar_style_choose_json(csubstr scalar) noexcept
choose a json scalar style based on the scalar's contents
bool is_val() const noexcept
Definition: node_type.hpp:298
the val has a tag
Definition: node_type.hpp:44
const char * str
Definition: node_type.cpp:70
mask of CONTAINER_STYLE_FLOW|CONTAINER_STYLE_BLOCK : all container style flags
Definition: node_type.hpp:182
mark val scalar as double quoted "
Definition: node_type.hpp:154
bool is_stream() const noexcept
Definition: node_type.hpp:291
mark container with multi-line flow style, n elements per line, < wrapped (as set by EmitOptions::max...
Definition: node_type.hpp:90
mask of KEY_STYLE|VAL_STYLE : all the key+val scalar styles
Definition: node_type.hpp:178
id_type root_id() const
Get the id of the root node. The tree must not be empty. The tree can be empty only when constructed ...
Definition: tree.hpp:386
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:34
the key has an &anchor
Definition: node_type.hpp:41
bool has_val() const noexcept
Definition: node_type.hpp:297
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
mark val scalar as multiline, block folded >
Definition: node_type.hpp:150
NodeType scalar_style_choose_flow(csubstr scalar) noexcept
choose a YAML scalar style based on the scalar's contents, while in flow mode.
bool scalar_is_inf3(const char *s) noexcept
Query if a scalar is inf (inf, Inf, INF)
bool scalar_is_nan3(const char *s) noexcept
Query if a scalar is nan (nan, NaN, Nan, NAN)
mark key scalar as multiline, block folded >
Definition: node_type.hpp:149
basic_substring sub(size_t first) const noexcept
return [first,len[
Definition: substr.hpp:502
Callbacks const & callbacks() const
Definition: tree.hpp:349
a null string position
Definition: common.hpp:138
basic_substring< const char > csubstr
an immutable string view
Definition: substr.hpp:2356
bool is_val_plain() const noexcept
Definition: node_type.hpp:341
the val is null (eg {a : } results in a null val)
Definition: node_type.hpp:46
mark container with block style < - seqs as <
Definition: node_type.hpp:136
basic_substring range(size_t first, size_t last=npos) const noexcept
return [first,last[.
Definition: substr.hpp:519
a *reference: the key references an &anchor
Definition: node_type.hpp:39
mask of KEYQUO|KEY_PLAIN : all the key scalar styles for key (not container styles!) ...
Definition: node_type.hpp:176
no node type or style is set
Definition: node_type.hpp:32
a stream: a seq of docs
Definition: node_type.hpp:38
size_t len
the length of the substring
Definition: substr.hpp:218
bool is_flow_mlx() const noexcept
Definition: node_type.hpp:326
mark container with spaces after comma when in flow mode. < Applies to both FLOW_SL and FLOW_MLN (but...
Definition: node_type.hpp:132
mark key scalar as plain scalar (unquoted, even when multiline)
Definition: node_type.hpp:155
Error utilities used by ryml.
bool has_key() const noexcept
Definition: node_type.hpp:296
mask of KEY_DQUO|VAL_DQUO,
Definition: node_type.hpp:172
mark val scalar as multiline, block literal |
Definition: node_type.hpp:148
uint32_t type_bits
the integral type necessary to cover all the bits for NodeType_e
Definition: node_type.hpp:26
NodeType scalar_style_choose_block(csubstr scalar) noexcept
choose a YAML scalar style based on the scalar's contents, while in block mode.
a map: a parent of KEYVAL/KEYSEQ/KEYMAP nodes
Definition: node_type.hpp:35
type_bits m_bits
Definition: node_type.hpp:217
bool is_container() const noexcept
Definition: node_type.hpp:293
Wraps a type_bits mask of NodeTypeBits flags with some syntactic sugar and predicates.
Definition: node_type.hpp:213
mark key scalar as single quoted '
Definition: node_type.hpp:151
mark container with multi-line flow style, 1 element per line < - seqs as <
Definition: node_type.hpp:75
mask of VALQUO|VAL_PLAIN : all the val scalar styles for val (not container styles!) ...
Definition: node_type.hpp:177
a seq: a parent of VAL/SEQ/MAP nodes
Definition: node_type.hpp:36
mark container with single-line flow style < - seqs as <
Definition: node_type.hpp:56