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