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