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