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