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