1#ifndef _C4_YML_EMIT_DEF_HPP_
2#define _C4_YML_EMIT_DEF_HPP_
4#ifndef _C4_YML_EMIT_HPP_
9#ifndef _C4_YML_DETAIL_DBGPRINT_HPP_
10#include "c4/yml/detail/dbgprint.hpp"
13C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH(
"-Wold-style-cast")
41 _RYML_ERR_BASIC_(m_tree->callbacks(),
"unknown emit type");
43 return this->Writer::_get(error_on_excess);
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);
75 _blck_map_open_entry(
id);
80 _blck_seq_open_entry(
id);
91 _RYML_ASSERT_VISIT_(m_tree->m_callbacks, m_ilevel == 0, m_tree,
id);
96 _RYML_ASSERT_VISIT_(m_tree->m_callbacks, m_ilevel == 0, m_tree,
id);
101 _visit_blck_container(
id);
112 _blck_close_entry(
id);
117 _blck_close_entry(
id);
121 _top_close_entry(
id);
128 else if(m_tree->is_root(
id)
129 || emit_dash || emit_key
133 _write_pws_and_pend(_PWS_NONE);
140template<
class Writer>
141void Emitter<Writer>::_visit_stream(id_type
id)
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)
148 const id_type parent = m_tree->parent(next_node);
149 if(next_node != m_tree->first_child(parent))
151 _write_pws_and_pend(_PWS_NEWL);
155 for(TagDirective
const& td : tagds)
157 _write_pws_and_pend(_PWS_NONE);
165 const id_type first_child = m_tree->first_child(
id);
166 if(first_child != NONE)
167 write_tag_directives(first_child);
169 for(id_type child = first_child; child !=
NONE; child = m_tree->next_sibling(child))
172 _write_pws_and_pend(_PWS_NONE);
173 _top_open_entry(child);
175 _top_close_entry(child);
176 if(m_tree->is_val(child))
178 if(m_tree->type(child) & VALNIL)
181 else if(m_tree->is_container(child))
183 if(m_tree->is_flow(child))
186 if(m_tree->next_sibling(child) != NONE)
188 write_tag_directives(m_tree->next_sibling(child));
197template<
class Writer>
198void Emitter<Writer>::_visit_blck_container(id_type
id)
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);
206 else if(ty.is_flow_mlx())
212template<
class Writer>
213void Emitter<Writer>::_visit_flow_container(id_type
id)
215 NodeType ty = m_tree->type(
id);
216 if(!(ty & CONTAINER_STYLE))
218 _write_pws_and_pend(_PWS_NONE);
228template<
class Writer>
229void Emitter<Writer>::_visit_doc_val(id_type
id)
233 NodeType ty = m_tree->type(
id);
234 const csubstr val = m_tree->val(
id);
236 const bool is_ambiguous = ((ty &
VAL_PLAIN) || !val_style)
237 && (val.begins_with(
"...") || val.begins_with(
"---"));
241 if(m_pws != _PWS_NONE)
246 _write_pws_and_pend(_PWS_NONE);
247 if(m_tree->is_val_ref(
id))
255 _blck_write_scalar(val, val_style);
266template<
class Writer>
267void Emitter<Writer>::_visit_doc(id_type
id)
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())
274 _visit_blck_container(
id);
286template<
class Writer>
287void Emitter<Writer>::_top_open_entry(id_type node)
289 NodeType ty = m_tree->type(node);
290 if(ty.is_doc() && !m_tree->is_root(node))
295 if(ty.has_val_anchor())
297 _write_pws_and_pend(_PWS_SPACE);
299 _write(m_tree->val_anchor(node));
303 _write_pws_and_pend(_PWS_SPACE);
304 _write_tag(m_tree->val_tag(node));
306 if(m_pws == _PWS_SPACE)
310 if(ty.is_val_plain() && !m_tree->val(node).len)
315 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.is_container(), m_tree, node);
325template<
class Writer>
326void Emitter<Writer>::_top_close_entry(id_type node)
328 if(m_tree->is_val(node) && !(m_tree->type(node) & VALNIL))
337template<
class Writer>
338void Emitter<Writer>::_flow_seq_open_entry(id_type node)
340 NodeType ty = m_tree->type(node);
341 _write_pws_and_pend(_PWS_NONE);
344 _write_pws_and_pend(_PWS_SPACE);
346 _write(m_tree->val_anchor(node));
350 _write_pws_and_pend(_PWS_SPACE);
351 _write_tag(m_tree->val_tag(node));
358template<
class Writer>
359void Emitter<Writer>::_flow_map_open_entry(id_type node)
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);
366 _write_pws_and_pend(_PWS_SPACE);
368 _write(m_tree->key_anchor(node));
372 _write_pws_and_pend(_PWS_SPACE);
373 _write_tag(m_tree->key_tag(node));
377 _write_pws_and_pend(_PWS_SPACE);
378 _write_ref(m_tree->key(node));
382 _write_pws_and_pend(_PWS_NONE);
384 if(!(ty & (NodeType_e)_styles_flow_key))
386 _flow_write_scalar(key, ty & (NodeType_e)_styles_flow_key);
388 _write_pws_and_pend(_PWS_SPACE);
392 _write_pws_and_pend(_PWS_SPACE);
394 _write(m_tree->val_anchor(node));
398 _write_pws_and_pend(_PWS_SPACE);
399 _write_tag(m_tree->val_tag(node));
406template<
class Writer>
407void Emitter<Writer>::flow_pws::start(NodeType ty,
size_t max_cols_)
noexcept
410 pend_after_comma = ty &
FLOW_SPC ? _PWS_SPACE : _PWS_NONE;
413 max_cols_ = max_cols_ >= 2 ? max_cols_ : 2;
415 max_cols = max_cols_ - 1 - pend_after_comma;
417 static_assert((size_t)_PWS_NONE == 0 && (size_t)_PWS_SPACE == 1,
"invalid assumptions");
420 else if(ty & FLOW_ML1)
422 pend_after_comma = _PWS_NEWL;
426template<
class Writer>
427void Emitter<Writer>::_flow_close_entry_sl(id_type node, id_type last_sibling, Pws_e pend_after)
429 if(node != last_sibling)
431 _write_pws_and_pend(pend_after);
436template<
class Writer>
437void Emitter<Writer>::_flow_close_entry_ml(id_type node, id_type last_sibling, Pws_e pend_after)
439 if(node != last_sibling)
441 _write_pws_and_pend(pend_after);
453template<
class Writer>
454void Emitter<Writer>::_blck_seq_open_entry(id_type node)
456 NodeType ty = m_tree->type(node);
457 _write_pws_and_pend(_PWS_NONE);
458 _write_pws_and_pend(_PWS_SPACE);
460 bool has_tag_or_anchor =
false;
463 has_tag_or_anchor =
true;
464 _write_pws_and_pend(_PWS_SPACE);
466 _write(m_tree->val_anchor(node));
470 has_tag_or_anchor =
true;
471 _write_pws_and_pend(_PWS_SPACE);
472 _write_tag(m_tree->val_tag(node));
474 if(has_tag_or_anchor && ty.is_container())
476 if(!(ty & CONTAINER_STYLE))
478 if((ty & BLOCK) && m_tree->has_children(node))
486template<
class Writer>
487void Emitter<Writer>::_blck_map_open_entry(id_type node)
489 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->has_key(node), m_tree, node);
490 NodeType ty = m_tree->type(node);
492 if(!(ty & (KEY_STYLE|KEYREF)))
494 _write_pws_and_pend(_PWS_NONE);
497 _write_pws_and_pend(_PWS_SPACE);
499 _write(m_tree->key_anchor(node));
503 _write_pws_and_pend(_PWS_SPACE);
504 _write_tag(m_tree->key_tag(node));
508 _write_pws_and_pend(_PWS_SPACE);
513 _write_pws_and_pend(_PWS_NONE);
517 _blck_write_scalar(key, ty & KEY_STYLE);
522 _blck_write_scalar(key, ty & KEY_STYLE);
526 _write_pws_and_pend(_PWS_SPACE);
530 _write_pws_and_pend(_PWS_SPACE);
532 _write(m_tree->val_anchor(node));
536 _write_pws_and_pend(_PWS_SPACE);
537 _write_tag(m_tree->val_tag(node));
539 if(ty.is_container() && m_tree->has_children(node))
541 if(!(ty & CONTAINER_STYLE))
551template<
class Writer>
552void Emitter<Writer>::_blck_close_entry(id_type node)
561template<
class Writer>
562void Emitter<Writer>::_visit_blck_seq(id_type node)
564 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_seq(node), m_tree, node);
566 for(id_type child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))
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);
574 _write_pws_and_pend(_PWS_NONE);
575 csubstr val = m_tree->val(child);
578 if(!(ty & VAL_STYLE))
580 _blck_write_scalar(val, ty & VAL_STYLE);
587 else if(ty.is_container())
591 _visit_blck_container(child);
595 _blck_close_entry(child);
599 _write_pws_and_pend(_PWS_NONE);
607template<
class Writer>
608void Emitter<Writer>::_visit_blck_map(id_type node)
610 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_map(node), m_tree, node);
612 for(id_type child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))
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);
620 _write_pws_and_pend(_PWS_NONE);
621 csubstr val = m_tree->val(child);
624 if(!(ty & VAL_STYLE))
626 _blck_write_scalar(val, ty & VAL_STYLE);
633 else if(ty.is_container())
637 _visit_blck_container(child);
641 _blck_close_entry(child);
645 _write_pws_and_pend(_PWS_NONE);
653template<
class Writer>
654void Emitter<Writer>::_visit_flow_sl_seq(id_type node)
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);
659 for(id_type child = m_tree->first_child(node), last = m_tree->last_child(node); child != NONE; child = m_tree->next_sibling(child))
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);
666 _write_pws_and_pend(_PWS_NONE);
667 csubstr val = m_tree->val(child);
670 if(!(ty & (NodeType_e)_styles_flow_val))
672 _flow_write_scalar(val, ty & (NodeType_e)_styles_flow_val);
679 else if(ty & (SEQ|MAP))
682 _visit_flow_container(child);
685 _flow_close_entry_sl(child, last, pws.next_pws(m_col));
693template<
class Writer>
694void Emitter<Writer>::_visit_flow_ml_seq(id_type node)
696 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_seq(node), m_tree, node);
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))
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);
708 _write_pws_and_pend(_PWS_NONE);
709 csubstr val = m_tree->val(child);
712 if(!(ty & (NodeType_e)_styles_flow_val))
714 _flow_write_scalar(val, ty & (NodeType_e)_styles_flow_val);
721 else if(ty.is_container())
724 _visit_flow_container(child);
727 _flow_close_entry_ml(child, last, m_flow_pws.next_pws(m_col));
731 if(m_opts.indent_flow_ml()) --m_ilevel;
732 _write_pws_and_pend(_PWS_NONE);
739template<
class Writer>
740void Emitter<Writer>::_visit_flow_sl_map(id_type node)
742 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_map(node), m_tree, node);
743 flow_pws pws = _setup_flow_pws_sl(node);
745 for(id_type child = m_tree->first_child(node), last = m_tree->last_child(node); child != NONE; child = m_tree->next_sibling(child))
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);
752 _write_pws_and_pend(_PWS_NONE);
753 csubstr val = m_tree->val(child);
756 if(!(ty & (NodeType_e)_styles_flow_val))
758 _flow_write_scalar(val, ty & (NodeType_e)_styles_flow_val);
765 else if(ty.is_container())
768 _visit_flow_container(child);
771 _flow_close_entry_sl(child, last, pws.next_pws(m_col));
773 _write_pws_and_pend(_PWS_NONE);
780template<
class Writer>
781void Emitter<Writer>::_visit_flow_ml_map(id_type node)
783 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_map(node), m_tree, node);
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))
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);
795 _write_pws_and_pend(_PWS_NONE);
796 csubstr val = m_tree->val(child);
799 if(!(ty & (NodeType_e)_styles_flow_val))
801 _flow_write_scalar(val, ty & (NodeType_e)_styles_flow_val);
808 else if(ty.is_container())
811 _visit_flow_container(child);
814 _flow_close_entry_ml(child, last, m_flow_pws.next_pws(m_col));
818 if(m_opts.indent_flow_ml()) --m_ilevel;
819 _write_pws_and_pend(_PWS_NONE);
826template<
class Writer>
827void Emitter<Writer>::_visit_blck(id_type node)
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);
837 _visit_blck_seq(node);
841 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.is_map(), m_tree, node);
842 _visit_blck_map(node);
849template<
class Writer>
850void Emitter<Writer>::_visit_flow_sl(id_type node)
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);
860 _visit_flow_sl_seq(node);
864 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.is_map(), m_tree, node);
865 _visit_flow_sl_map(node);
872template<
class Writer>
873void Emitter<Writer>::_visit_flow_ml(id_type node)
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);
883 _visit_flow_ml_seq(node);
887 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.is_map(), m_tree, node);
888 _visit_flow_ml_map(node);
895template<
class Writer>
896void Emitter<Writer>::_flow_write_scalar(csubstr str, type_bits ty)
898 _RYML_ASSERT_BASIC_(m_tree->callbacks(), !(ty & _styles_block));
899 if((ty & _styles_plain) || !(ty & SCALAR_STYLE))
901 _write_scalar_plain(str, m_ilevel);
903 else if(ty & _styles_squo)
905 _write_scalar_squo(str, m_ilevel);
909 _write_scalar_dquo(str, m_ilevel);
913template<
class Writer>
914void Emitter<Writer>::_blck_write_scalar(csubstr str, type_bits ty)
916 if((ty & _styles_plain) || !(ty & SCALAR_STYLE))
918 _write_scalar_plain(str, m_ilevel);
920 else if(ty & _styles_squo)
922 _write_scalar_squo(str, m_ilevel);
924 else if(ty & _styles_dquo)
926 _write_scalar_dquo(str, m_ilevel);
928 else if(ty & _styles_literal)
930 _write_scalar_literal(str, m_ilevel);
934 _write_scalar_folded(str, m_ilevel);
938template<
class Writer>
939size_t Emitter<Writer>::_write_escaped_newlines(csubstr s,
size_t i)
941 _RYML_ASSERT_BASIC_(m_tree->callbacks(), s.len > i);
942 _RYML_ASSERT_BASIC_(m_tree->callbacks(), s.str[i] ==
'\n');
951 }
while(i < s.len && s.str[i] ==
'\n');
952 _RYML_ASSERT_BASIC_(m_tree->callbacks(), i > 0);
954 _RYML_ASSERT_BASIC_(m_tree->callbacks(), s.str[i] ==
'\n');
959inline bool _is_indented_block(csubstr s,
size_t prev,
size_t i)
noexcept
961 if(prev == 0 && s.begins_with_any(
" \t"))
963 const size_t pos = s.first_not_of(
'\n', i);
964 return (pos != npos) && (s.str[pos] ==
' ' || s.str[pos] ==
'\t');
968template<
class Writer>
969size_t Emitter<Writer>::_write_indented_block(csubstr s,
size_t i, id_type ilevel)
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');
977 size_t pos = s.find(
"\n ", i);
979 pos = s.find(
"\n\t", i);
985 _write(s.range(i, pos));
991 pos = s.find(
'\n', i);
994 const size_t pos2 = s.first_not_of(
'\n', pos);
995 pos = (pos2 !=
npos) ? pos2 : pos;
998 _write(s.range(i, pos));
1004template<
class Writer>
1005void Emitter<Writer>::_write_scalar_literal(csubstr s, id_type ilevel)
1007 _RYML_ASSERT_BASIC_(m_tree->callbacks(), s.find(
"\r") == csubstr::npos);
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");
1014 if(explicit_indentation)
1017 if(numnewlines_at_end > 1 || is_newline_only)
1019 else if(numnewlines_at_end == 0)
1026 for(
size_t i = 0; i < trimmed.len; ++i)
1028 if(trimmed[i] !=
'\n')
1032 _indent(ilevel + 1);
1036 if(pos < trimmed.len)
1038 _indent(ilevel + 1);
1039 _write(trimmed.sub(pos));
1042 for(
size_t i = !is_newline_only; i < numnewlines_at_end; ++i)
1046template<
class Writer>
1047void Emitter<Writer>::_write_scalar_folded(csubstr s, id_type ilevel)
1049 _RYML_ASSERT_BASIC_(m_tree->callbacks(), s.find(
"\r") == csubstr::npos);
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");
1056 if(explicit_indentation)
1059 if(numnewlines_at_end == 0)
1061 else if(numnewlines_at_end > 1 || is_newline_only)
1068 for(
size_t i = 0; i < trimmed.len; ++i)
1070 if(trimmed[i] !=
'\n')
1073 if( ! _is_indented_block(s, pos, i))
1077 _indent(ilevel + 1);
1078 _write(s.range(pos, i));
1079 i = _write_escaped_newlines(s, i);
1086 if(s.str[i+1] ==
'\n')
1089 i = _write_escaped_newlines(s, 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);
1110 if(pos < trimmed.len)
1112 _indent(ilevel + 1);
1113 _write(trimmed.sub(pos));
1116 for(
size_t i = !is_newline_only; i < numnewlines_at_end; ++i)
1120template<
class Writer>
1121void Emitter<Writer>::_write_scalar_squo(csubstr s, id_type ilevel)
1125 for(
size_t i = 0; i < s.len; ++i)
1129 _write(s.range(pos, i));
1131 i = _write_escaped_newlines(s, i);
1134 _indent(ilevel + 1);
1137 else if(s[i] ==
'\'')
1152template<
class Writer>
1153void Emitter<Writer>::_write_scalar_dquo(csubstr s, id_type ilevel)
1157 for(
size_t i = 0; i < s.len; ++i)
1159 const char curr = s.str[i];
1172#ifndef prefer_writing_newlines_as_double_newlines
1187 _write(s.range(pos, i));
1188 i = _write_escaped_newlines(s, i);
1194 _indent(ilevel + 1);
1196 size_t first = s.first_not_of(
" \t", i);
1203 _write(s.range(i, first));
1215 const size_t next = s.first_not_of(
" \t\r", i);
1216 if(next != npos && s.str[next] ==
'\n')
1250template<
class Writer>
1251void Emitter<Writer>::_write_scalar_plain(csubstr s, id_type ilevel)
1253 if(C4_UNLIKELY(ilevel == 0 && (s.begins_with(
"...") || s.begins_with(
"---"))))
1255 _indent(ilevel + 1);
1259 for(
size_t i = 0; i < s.len; ++i)
1261 const char curr = s.str[i];
1266 i = _write_escaped_newlines(s, i);
1269 _indent(ilevel + 1);
1281inline type_bits json_type_(type_bits ty)
1285 sl_bits = (CONTAINER_STYLE & ~FLOW_SPC),
1292 else if((ty & (SEQ|MAP)) && !(ty & sl_bits))
1301template<
class Writer>
1302void Emitter<Writer>::_json_emit(id_type
id)
1304 NodeType ty = m_tree->type(
id);
1306 if(C4_UNLIKELY(ty.is_stream() && m_opts.json_err_on_stream()))
1307 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"found stream node");
1308 static_assert(
STREAM &
SEQ,
"STREAM must be a SEQ");
1309 ty = detail::json_type_(ty);
1310 if(ty.is_flow_mlx())
1312 _json_visit_ml(
id, ty, 0);
1317 _json_visit_sl(
id, ty, 0);
1321template<
class Writer>
1322void Emitter<Writer>::_json_visit_sl(id_type
id, NodeType ty, id_type depth)
1324 if(C4_UNLIKELY(depth > m_opts.max_depth()))
1325 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"max depth exceeded");
1328 _json_writev(
id, ty);
1330 else if(ty.is_keyval())
1332 _json_writek(
id, ty);
1334 _json_writev(
id, ty);
1336 else if(ty.is_container())
1338 ty = detail::json_type_(ty);
1341 _json_writek(
id, ty);
1346 else if(ty.is_map())
1349 for(id_type child = m_tree->first_child(
id); child != NONE; child = m_tree->next_sibling(child))
1351 if(child != m_tree->first_child(
id))
1353 if((ty & FLOW_SPC) || m_opts.force_flow_spc())
1358 _json_visit_sl(child, m_tree->type(child), depth+1);
1363 else if(ty.is_map())
1368template<
class Writer>
1369void Emitter<Writer>::_json_visit_ml(id_type
id, NodeType ty, id_type depth)
1371 if(C4_UNLIKELY(depth > m_opts.max_depth()))
1372 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"max depth exceeded");
1375 _json_writev(
id, ty);
1377 else if(ty.is_keyval())
1379 _json_writek(
id, ty);
1381 _json_writev(
id, ty);
1383 else if(ty.is_container())
1385 ty = detail::json_type_(ty);
1388 _json_writek(
id, ty);
1393 else if(ty.is_map())
1396 if(m_tree->has_children(
id))
1399 if(m_opts.indent_flow_ml()) ++m_ilevel;
1402 for(id_type first = m_tree->first_child(
id), child = first;
1404 child = m_tree->next_sibling(child))
1409 const size_t maxcols = m_opts.max_cols();
1410 if((ty & FLOW_MLN) && (m_col+1 < maxcols))
1412 if((ty & FLOW_SPC) || m_opts.force_flow_spc())
1415 else if((ty & FLOW_ML1) || (m_col+1 >= maxcols))
1421 NodeType chty = m_tree->type(child);
1422 if(chty.is_flow_sl())
1423 _json_visit_sl(child, chty, depth);
1425 _json_visit_ml(child, chty, depth);
1427 if(m_opts.indent_flow_ml()) --m_ilevel;
1435 else if(ty.is_map())
1440template<
class Writer>
1441bool Emitter<Writer>::_json_maybe_write_naninf(csubstr s)
1443 if(s ==
"nan" || s ==
".nan" || s ==
".NaN" || s ==
".NAN")
1448 else if(s ==
"inf" || s ==
".inf" || s ==
".Inf" || s ==
".INF" || s ==
"infinity")
1453 else if(s ==
"-inf" || s ==
"-.inf" || s ==
"-.Inf" || s ==
"-.INF" || s ==
"-infinity")
1455 _write(
"\"-.inf\"");
1461template<
class Writer>
1462void Emitter<Writer>::_json_writek(id_type
id, NodeType ty)
1464 if(C4_UNLIKELY(ty.has_key_tag() && m_opts.json_err_on_tag()))
1465 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"JSON does not have tags");
1466 if(C4_UNLIKELY(ty.has_key_anchor() && m_opts.json_err_on_anchor()))
1467 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"JSON does not have anchors");
1471 if(_json_maybe_write_naninf(key))
1474 _json_write_scalar_dquo(key);
1482template<
class Writer>
1483void Emitter<Writer>::_json_writev(id_type
id, NodeType ty)
1485 if(C4_UNLIKELY(ty.has_val_tag() && m_opts.json_err_on_tag()))
1486 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"JSON does not have tags");
1487 if(C4_UNLIKELY(ty.has_val_anchor() && m_opts.json_err_on_anchor()))
1488 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"JSON does not have anchors");
1489 csubstr val = m_tree->val(
id);
1493 bool dquoted = ((ty &
VALQUO)
1496 _json_write_scalar_dquo(val);
1497 else if(_json_maybe_write_naninf(val))
1499 else if(val.is_number())
1500 _json_write_number(val);
1506 if(val.str || (ty & (VALQUO|VALTAG)))
1514template<
class Writer>
1515void Emitter<Writer>::_json_write_scalar_dquo(csubstr s)
1519 for(
size_t i = 0; i < s.len; ++i)
1524 _write(s.range(pos, i));
1529 _write(s.range(pos, i));
1534 _write(s.range(pos, i));
1539 _write(s.range(pos, i));
1544 _write(s.range(pos, i));
1549 _write(s.range(pos, i));
1554 _write(s.range(pos, i));
1568template<
class Writer>
1569void Emitter<Writer>::_json_write_number(csubstr s)
1577 if(s.begins_with(
'-') && s.len > 1)
1580 if(rest.begins_with(
'.'))
1585 else if(rest.ends_with(
'.'))
1595 else if(s.begins_with(
'.'))
1600 else if(s.ends_with(
'.'))
1618C4_SUPPRESS_WARNING_GCC_CLANG_POP
A stateful emitter, for use with a writer such as WriterBuf, WriterFile, or WriterOStream.
id_type root_id() const
Get the id of the root node. The tree must not be empty.
Callbacks const & callbacks() const
Utilities to emit YAML and JSON.
substr emit_as(EmitType_e type, Tree const &t, id_type id, bool error_on_excess)
emit!
EmitType_e
Specifies the type of content to emit.
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
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
@ STREAM
a stream: a seq of docs
@ FLOW_ML1
mark container with multi-line flow style, 1 element per line
@ 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
@ SEQ
a seq: a parent of VAL/SEQ/MAP nodes
@ 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
basic_substring< char > substr
a mutable string view
basic_substring< const char > csubstr
an immutable string view
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...
@ npos
a null string position
(Undefined by default) Use shorter error message from checks/asserts: do not show the check condition...
basic_substring range(size_t first, size_t last=npos) const noexcept
return [first,last[.
size_t len
the length of the substring
basic_substring sub(size_t first) const noexcept
return [first,len[
basic_substring trimr(const C c) const
trim the character c from the right
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