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