1#ifndef _C4_YML_EMITTER_DEF_HPP_
2#define _C4_YML_EMITTER_DEF_HPP_
6#ifndef _C4_YML_EMITTER_HPP_
9#ifndef _C4_YML_TREE_HPP_
12#ifndef _C4_YML_DETAIL_DBGPRINT_HPP_
13#include "c4/yml/detail/dbgprint.hpp"
15#ifndef _C4_YML_ERROR_HPP_
20C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH(
"-Wold-style-cast")
21C4_SUPPRESS_WARNING_GCC(
"-Wuseless-cast")
28C4_SUPPRESS_WARNING_GCC_PUSH
31#if defined(__GNUC__) && (__GNUC__ < 5) && (!defined(__clang__))
32C4_SUPPRESS_WARNING_GCC(
"-Wparentheses")
47C4_SUPPRESS_WARNING_GCC_POP
55 if(!tree || tree->
empty())
59 _RYML_CHECK_VISIT_(tree->
callbacks(), id < tree->capacity(), tree,
id);
68 else if(type == EMIT_JSON)
71 _RYML_ERR_BASIC_(m_tree->callbacks(),
"unknown emit type");
91void Emitter<Writer>::emit_yaml_(
id_type id)
96 const bool has_parent = !m_tree->is_root(
id);
97 const bool emit_key = has_parent && ty.
has_key() && m_opts.emit_nonroot_key();
98 const bool emit_dash = has_parent && !ty.
has_key() && !ty.
is_doc() && m_opts.emit_nonroot_dash();
99 _RYML_ASSERT_VISIT_(m_tree->m_callbacks, !(emit_key && emit_dash), m_tree,
id);
104 blck_map_open_entry_(
id);
109 blck_seq_open_entry_(
id);
120 _RYML_ASSERT_VISIT_(m_tree->m_callbacks, m_ilevel == 0, m_tree,
id);
125 _RYML_ASSERT_VISIT_(m_tree->m_callbacks, m_ilevel == 0, m_tree,
id);
130 visit_blck_container_(
id);
141 blck_close_entry_(
id);
146 blck_close_entry_(
id);
150 top_close_entry_(
id);
157 else if(m_tree->is_root(
id)
158 || emit_dash || emit_key
162 write_pws_and_pend_(_PWS_NONE);
169template<
class Writer>
170void Emitter<Writer>::visit_stream_(id_type
id)
172 auto write_tag_directives = [
this](
const id_type next_node){
173 const id_type doc = m_tree->ancestor_doc(next_node);
174 const TagDirectiveRange tagds = m_tree->m_tag_directives.lookup_range(doc);
175 if(tagds.e != tagds.b)
177 const id_type parent = m_tree->parent(next_node);
178 if(next_node != m_tree->first_child(parent))
180 write_pws_and_pend_(_PWS_NEWL);
184 for(TagDirective
const& td : tagds)
186 write_pws_and_pend_(_PWS_NONE);
194 const id_type first_child = m_tree->first_child(
id);
195 if(first_child != NONE)
196 write_tag_directives(first_child);
198 for(id_type child = first_child; child !=
NONE; child = m_tree->next_sibling(child))
201 write_pws_and_pend_(_PWS_NONE);
202 top_open_entry_(child);
204 top_close_entry_(child);
205 if(m_tree->is_val(child))
207 if(m_tree->type(child) & VALNIL)
210 else if(m_tree->is_container(child))
212 if(m_tree->is_flow(child))
215 if(m_tree->next_sibling(child) != NONE)
217 write_tag_directives(m_tree->next_sibling(child));
226template<
class Writer>
227void Emitter<Writer>::visit_blck_container_(id_type
id)
229 NodeType ty = m_tree->
type(
id);
230 if(!(ty & CONTAINER_STYLE))
231 ty |= (m_tree->empty(
id) ? FLOW_SL : BLOCK);
232 write_pws_and_pend_(_PWS_NONE);
235 else if(ty.is_flow_mlx())
241template<
class Writer>
242void Emitter<Writer>::visit_flow_container_(id_type
id)
244 NodeType ty = m_tree->type(
id);
245 if(!(ty & CONTAINER_STYLE))
247 write_pws_and_pend_(_PWS_NONE);
257template<
class Writer>
258void Emitter<Writer>::visit_doc_val_(id_type
id)
262 NodeType ty = m_tree->type(
id);
263 const csubstr val = m_tree->val(
id);
265 const bool is_ambiguous = ((ty &
VAL_PLAIN) || !val_style)
266 && (val.begins_with(
"...") || val.begins_with(
"---"));
270 if(m_pws != _PWS_NONE)
275 write_pws_and_pend_(_PWS_NONE);
276 if(m_tree->is_val_ref(
id))
284 blck_write_scalar_(val, val_style);
295template<
class Writer>
296void Emitter<Writer>::visit_doc_(id_type
id)
298 const NodeType ty = m_tree->type(
id);
299 _RYML_ASSERT_VISIT_(m_tree->m_callbacks, ty.is_doc(), m_tree,
id);
300 _RYML_ASSERT_VISIT_(m_tree->m_callbacks, !ty.has_key(), m_tree,
id);
301 if(ty.is_container())
303 visit_blck_container_(
id);
315template<
class Writer>
316void Emitter<Writer>::top_open_entry_(id_type node)
318 NodeType ty = m_tree->type(node);
319 if(ty.is_doc() && !m_tree->is_root(node))
324 if(ty.has_val_anchor())
326 write_pws_and_pend_(_PWS_SPACE);
328 write_(m_tree->val_anchor(node));
332 write_pws_and_pend_(_PWS_SPACE);
333 write_tag_(m_tree->val_tag(node));
335 if(m_pws == _PWS_SPACE)
339 if(ty.is_val_plain() && !m_tree->val(node).len)
344 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.is_container(), m_tree, node);
354template<
class Writer>
355void Emitter<Writer>::top_close_entry_(id_type node)
357 if(m_tree->is_val(node) && !(m_tree->type(node) & VALNIL))
366template<
class Writer>
367void Emitter<Writer>::flow_seq_open_entry_(id_type node)
369 NodeType ty = m_tree->type(node);
370 write_pws_and_pend_(_PWS_NONE);
373 write_pws_and_pend_(_PWS_SPACE);
375 write_(m_tree->val_anchor(node));
379 write_pws_and_pend_(_PWS_SPACE);
380 write_tag_(m_tree->val_tag(node));
387template<
class Writer>
388void Emitter<Writer>::flow_map_open_entry_(id_type node)
390 NodeType ty = m_tree->type(node);
391 write_pws_and_pend_(_PWS_NONE);
392 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.has_key(), m_tree, node);
395 write_pws_and_pend_(_PWS_SPACE);
397 write_(m_tree->key_anchor(node));
401 write_pws_and_pend_(_PWS_SPACE);
402 write_tag_(m_tree->key_tag(node));
406 write_pws_and_pend_(_PWS_SPACE);
407 write_ref_(m_tree->key(node));
411 write_pws_and_pend_(_PWS_NONE);
413 if(!(ty & (NodeType_e)detail::styles_flow_key_))
415 flow_write_scalar_(key, ty & (NodeType_e)detail::styles_flow_key_);
417 write_pws_and_pend_(_PWS_SPACE);
421 write_pws_and_pend_(_PWS_SPACE);
423 write_(m_tree->val_anchor(node));
427 write_pws_and_pend_(_PWS_SPACE);
428 write_tag_(m_tree->val_tag(node));
435template<
class Writer>
436C4_NODISCARD
bool Emitter<Writer>::maybe_start_flow_pws_ml_(id_type node)
noexcept
438 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->type(node) & (FLOW_ML1|FLOW_MLN), m_tree, node);
439 if(m_flow_pws.active)
441 NodeType ty = m_tree->type(node);
442 if(m_opts.force_flow_spc())
444 m_flow_pws.start(ty, m_opts.max_cols());
448template<
class Writer>
449C4_NODISCARD
typename Emitter<Writer>::flow_pws Emitter<Writer>::setup_flow_pws_sl_(id_type node)
noexcept
452 if(m_flow_pws.active)
458 NodeType ty = m_tree->type(node);
459 if(m_opts.force_flow_spc())
466template<
class Writer>
467void Emitter<Writer>::flow_pws::start(NodeType ty,
size_t max_cols_)
noexcept
470 pend_after_comma = ty &
FLOW_SPC ? _PWS_SPACE : _PWS_NONE;
473 max_cols_ = max_cols_ >= 2 ? max_cols_ : 2;
475 max_cols = max_cols_ - 1 - pend_after_comma;
477 static_assert((size_t)_PWS_NONE == 0 && (size_t)_PWS_SPACE == 1,
"invalid assumptions");
480 else if(ty & FLOW_ML1)
482 pend_after_comma = _PWS_NEWL;
486template<
class Writer>
487void Emitter<Writer>::flow_close_entry_sl_(id_type node, id_type last_sibling, Pws_e pend_after)
489 if(node != last_sibling)
491 write_pws_and_pend_(pend_after);
496template<
class Writer>
497void Emitter<Writer>::flow_close_entry_ml_(id_type node, id_type last_sibling, Pws_e pend_after)
499 if(node != last_sibling)
501 write_pws_and_pend_(pend_after);
513template<
class Writer>
514void Emitter<Writer>::blck_seq_open_entry_(id_type node)
516 NodeType ty = m_tree->type(node);
517 write_pws_and_pend_(_PWS_NONE);
518 write_pws_and_pend_(_PWS_SPACE);
520 bool has_tag_or_anchor =
false;
523 has_tag_or_anchor =
true;
524 write_pws_and_pend_(_PWS_SPACE);
526 write_(m_tree->val_anchor(node));
530 has_tag_or_anchor =
true;
531 write_pws_and_pend_(_PWS_SPACE);
532 write_tag_(m_tree->val_tag(node));
534 if(has_tag_or_anchor && ty.is_container())
536 if(!(ty & CONTAINER_STYLE))
538 if((ty & BLOCK) && m_tree->has_children(node))
546template<
class Writer>
547void Emitter<Writer>::blck_map_open_entry_(id_type node)
549 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->has_key(node), m_tree, node);
550 NodeType ty = m_tree->type(node);
552 if(!(ty & (KEY_STYLE|KEYREF)))
554 write_pws_and_pend_(_PWS_NONE);
557 write_pws_and_pend_(_PWS_SPACE);
559 write_(m_tree->key_anchor(node));
563 write_pws_and_pend_(_PWS_SPACE);
564 write_tag_(m_tree->key_tag(node));
568 write_pws_and_pend_(_PWS_SPACE);
573 write_pws_and_pend_(_PWS_NONE);
577 blck_write_scalar_(key, ty & KEY_STYLE);
582 blck_write_scalar_(key, ty & KEY_STYLE);
586 write_pws_and_pend_(_PWS_SPACE);
590 write_pws_and_pend_(_PWS_SPACE);
592 write_(m_tree->val_anchor(node));
596 write_pws_and_pend_(_PWS_SPACE);
597 write_tag_(m_tree->val_tag(node));
599 if(ty.is_container() && m_tree->has_children(node))
601 if(!(ty & CONTAINER_STYLE))
611template<
class Writer>
612void Emitter<Writer>::blck_close_entry_(id_type node)
621template<
class Writer>
622void Emitter<Writer>::visit_blck_seq_(id_type node)
624 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_seq(node), m_tree, node);
626 for(id_type child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))
629 NodeType ty = m_tree->type(child);
630 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.is_val() || ty.is_container() || ty == NOTYPE, m_tree, node);
631 blck_seq_open_entry_(child);
634 write_pws_and_pend_(_PWS_NONE);
635 csubstr val = m_tree->val(child);
638 if(!(ty & VAL_STYLE))
640 blck_write_scalar_(val, ty & VAL_STYLE);
647 else if(ty.is_container())
651 visit_blck_container_(child);
655 blck_close_entry_(child);
659 write_pws_and_pend_(_PWS_NONE);
667template<
class Writer>
668void Emitter<Writer>::visit_blck_map_(id_type node)
670 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_map(node), m_tree, node);
672 for(id_type child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))
675 NodeType ty = m_tree->type(child);
676 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.is_keyval() || ty.is_container() || ty == NOTYPE, m_tree, node);
677 blck_map_open_entry_(child);
680 write_pws_and_pend_(_PWS_NONE);
681 csubstr val = m_tree->val(child);
684 if(!(ty & VAL_STYLE))
686 blck_write_scalar_(val, ty & VAL_STYLE);
693 else if(ty.is_container())
697 visit_blck_container_(child);
701 blck_close_entry_(child);
705 write_pws_and_pend_(_PWS_NONE);
713template<
class Writer>
714void Emitter<Writer>::visit_flow_sl_seq_(id_type node)
716 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_seq(node), m_tree, node);
717 const flow_pws pws = setup_flow_pws_sl_(node);
719 for(id_type child = m_tree->first_child(node), last = m_tree->last_child(node); child != NONE; child = m_tree->next_sibling(child))
721 NodeType ty = m_tree->type(child);
722 _RYML_ASSERT_VISIT_(m_tree->callbacks(), (ty & (VAL|SEQ|MAP)) || ty == NOTYPE, m_tree, node);
723 flow_seq_open_entry_(child);
726 write_pws_and_pend_(_PWS_NONE);
727 csubstr val = m_tree->val(child);
730 if(!(ty & (NodeType_e)detail::styles_flow_val_))
732 flow_write_scalar_(val, ty & (NodeType_e)detail::styles_flow_val_);
739 else if(ty & (SEQ|MAP))
742 visit_flow_container_(child);
745 flow_close_entry_sl_(child, last, pws.next_pws(m_col));
753template<
class Writer>
754void Emitter<Writer>::visit_flow_ml_seq_(id_type node)
756 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_seq(node), m_tree, node);
759 if(m_opts.indent_flow_ml()) ++m_ilevel;
760 const bool stop_at_end = maybe_start_flow_pws_ml_(node);
761 for(id_type child = m_tree->first_child(node), last = m_tree->last_child(node); child != NONE; child = m_tree->next_sibling(child))
763 NodeType ty = m_tree->type(child);
764 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.is_val() || ty.is_container() || ty == NOTYPE, m_tree, node);
765 flow_seq_open_entry_(child);
768 write_pws_and_pend_(_PWS_NONE);
769 csubstr val = m_tree->val(child);
772 if(!(ty & (NodeType_e)detail::styles_flow_val_))
774 flow_write_scalar_(val, ty & (NodeType_e)detail::styles_flow_val_);
781 else if(ty.is_container())
784 visit_flow_container_(child);
787 flow_close_entry_ml_(child, last, m_flow_pws.next_pws(m_col));
791 if(m_opts.indent_flow_ml()) --m_ilevel;
792 write_pws_and_pend_(_PWS_NONE);
799template<
class Writer>
800void Emitter<Writer>::visit_flow_sl_map_(id_type node)
802 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_map(node), m_tree, node);
803 flow_pws pws = setup_flow_pws_sl_(node);
805 for(id_type child = m_tree->first_child(node), last = m_tree->last_child(node); child != NONE; child = m_tree->next_sibling(child))
807 NodeType ty = m_tree->type(child);
808 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.has_key() && (ty.has_val() || ty.is_container() || ty == NOTYPE), m_tree, node);
809 flow_map_open_entry_(child);
812 write_pws_and_pend_(_PWS_NONE);
813 csubstr val = m_tree->val(child);
816 if(!(ty & (NodeType_e)detail::styles_flow_val_))
818 flow_write_scalar_(val, ty & (NodeType_e)detail::styles_flow_val_);
825 else if(ty.is_container())
828 visit_flow_container_(child);
831 flow_close_entry_sl_(child, last, pws.next_pws(m_col));
833 write_pws_and_pend_(_PWS_NONE);
840template<
class Writer>
841void Emitter<Writer>::visit_flow_ml_map_(id_type node)
843 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_map(node), m_tree, node);
846 if(m_opts.indent_flow_ml()) ++m_ilevel;
847 const bool stop_at_end = maybe_start_flow_pws_ml_(node);
848 for(id_type child = m_tree->first_child(node), last = m_tree->last_child(node); child != NONE; child = m_tree->next_sibling(child))
850 NodeType ty = m_tree->type(child);
851 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.has_key() && (ty.has_val() || ty.is_container() || ty == NOTYPE), m_tree, node);
852 flow_map_open_entry_(child);
855 write_pws_and_pend_(_PWS_NONE);
856 csubstr val = m_tree->val(child);
859 if(!(ty & (NodeType_e)detail::styles_flow_val_))
861 flow_write_scalar_(val, ty & (NodeType_e)detail::styles_flow_val_);
868 else if(ty.is_container())
871 visit_flow_container_(child);
874 flow_close_entry_ml_(child, last, m_flow_pws.next_pws(m_col));
878 if(m_opts.indent_flow_ml()) --m_ilevel;
879 write_pws_and_pend_(_PWS_NONE);
886template<
class Writer>
887void Emitter<Writer>::visit_blck_(id_type node)
889 _RYML_ASSERT_VISIT_(m_tree->callbacks(), !m_tree->is_stream(node), m_tree, node);
890 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_container(node) || m_tree->is_doc(node), m_tree, node);
891 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)), m_tree, node);
892 if(C4_UNLIKELY(m_depth > m_opts.max_depth()))
893 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree, node,
"max depth exceeded");
894 const NodeType ty = m_tree->type(node);
897 visit_blck_seq_(node);
901 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.is_map(), m_tree, node);
902 visit_blck_map_(node);
909template<
class Writer>
910void Emitter<Writer>::visit_flow_sl_(id_type node)
912 _RYML_ASSERT_VISIT_(m_tree->callbacks(), !m_tree->is_stream(node), m_tree, node);
913 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_container(node) || m_tree->is_doc(node), m_tree, node);
914 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)), m_tree, node);
915 if(C4_UNLIKELY(m_depth > m_opts.max_depth()))
916 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree, node,
"max depth exceeded");
917 const NodeType ty = m_tree->type(node);
920 visit_flow_sl_seq_(node);
924 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.is_map(), m_tree, node);
925 visit_flow_sl_map_(node);
932template<
class Writer>
933void Emitter<Writer>::visit_flow_ml_(id_type node)
935 _RYML_ASSERT_VISIT_(m_tree->callbacks(), !m_tree->is_stream(node), m_tree, node);
936 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_container(node) || m_tree->is_doc(node), m_tree, node);
937 _RYML_ASSERT_VISIT_(m_tree->callbacks(), m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)), m_tree, node);
938 if(C4_UNLIKELY(m_depth > m_opts.max_depth()))
939 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree, node,
"max depth exceeded");
940 const NodeType ty = m_tree->type(node);
943 visit_flow_ml_seq_(node);
947 _RYML_ASSERT_VISIT_(m_tree->callbacks(), ty.is_map(), m_tree, node);
948 visit_flow_ml_map_(node);
955template<
class Writer>
956void Emitter<Writer>::flow_write_scalar_(csubstr str, type_bits ty)
958 _RYML_ASSERT_BASIC_(m_tree->callbacks(), !(ty & detail::styles_block_));
959 if((ty & detail::styles_plain_) || !(ty & SCALAR_STYLE))
961 write_scalar_plain_(str, m_ilevel);
963 else if(ty & detail::styles_squo_)
965 write_scalar_squo_(str, m_ilevel);
969 write_scalar_dquo_(str, m_ilevel);
973template<
class Writer>
974void Emitter<Writer>::blck_write_scalar_(csubstr str, type_bits ty)
976 if((ty & detail::styles_plain_) || !(ty & SCALAR_STYLE))
978 write_scalar_plain_(str, m_ilevel);
980 else if(ty & detail::styles_squo_)
982 write_scalar_squo_(str, m_ilevel);
984 else if(ty & detail::styles_dquo_)
986 write_scalar_dquo_(str, m_ilevel);
988 else if(ty & detail::styles_literal_)
990 write_scalar_literal_(str, m_ilevel);
994 write_scalar_folded_(str, m_ilevel);
998template<
class Writer>
999size_t Emitter<Writer>::write_escaped_newlines_(csubstr s,
size_t i)
1001 _RYML_ASSERT_BASIC_(m_tree->callbacks(), s.len > i);
1002 _RYML_ASSERT_BASIC_(m_tree->callbacks(), s.str[i] ==
'\n');
1011 }
while(i < s.len && s.str[i] ==
'\n');
1012 _RYML_ASSERT_BASIC_(m_tree->callbacks(), i > 0);
1014 _RYML_ASSERT_BASIC_(m_tree->callbacks(), s.str[i] ==
'\n');
1019inline bool _is_indented_block(csubstr s,
size_t prev,
size_t i)
noexcept
1021 if(prev == 0 && s.begins_with_any(
" \t"))
1023 const size_t pos = s.first_not_of(
'\n', i);
1024 return (pos != npos) && (s.str[pos] ==
' ' || s.str[pos] ==
'\t');
1028template<
class Writer>
1029size_t Emitter<Writer>::write_indented_block_(csubstr s,
size_t i, id_type ilevel)
1032 _RYML_ASSERT_BASIC_(m_tree->callbacks(), i > 0);
1033 _RYML_ASSERT_BASIC_(m_tree->callbacks(), s.str[i-1] ==
'\n');
1034 _RYML_ASSERT_BASIC_(m_tree->callbacks(), i < s.len);
1035 _RYML_ASSERT_BASIC_(m_tree->callbacks(), s.str[i] ==
' ' || s.str[i] ==
'\t' || s.str[i] ==
'\n');
1037 size_t pos = s.find(
"\n ", i);
1039 pos = s.find(
"\n\t", i);
1044 indent_(ilevel + 1);
1045 write_(s.range(i, pos));
1051 pos = s.find(
'\n', i);
1054 const size_t pos2 = s.first_not_of(
'\n', pos);
1055 pos = (pos2 !=
npos) ? pos2 : pos;
1057 indent_(ilevel + 1);
1058 write_(s.range(i, pos));
1064template<
class Writer>
1065void Emitter<Writer>::write_scalar_literal_(csubstr s, id_type ilevel)
1067 _RYML_ASSERT_BASIC_(m_tree->callbacks(), s.find(
"\r") == csubstr::npos);
1069 const size_t numnewlines_at_end = s.
len - trimmed.len;
1070 const bool is_newline_only = (trimmed.len == 0 && (s.len > 0));
1071 const bool explicit_indentation = s.triml(
"\n\r").begins_with_any(
" \t");
1074 if(explicit_indentation)
1077 if(numnewlines_at_end > 1 || is_newline_only)
1079 else if(numnewlines_at_end == 0)
1086 for(
size_t i = 0; i < trimmed.len; ++i)
1088 if(trimmed[i] !=
'\n')
1092 indent_(ilevel + 1);
1096 if(pos < trimmed.len)
1098 indent_(ilevel + 1);
1099 write_(trimmed.sub(pos));
1102 for(
size_t i = !is_newline_only; i < numnewlines_at_end; ++i)
1106template<
class Writer>
1107void Emitter<Writer>::write_scalar_folded_(csubstr s, id_type ilevel)
1109 _RYML_ASSERT_BASIC_(m_tree->callbacks(), s.find(
"\r") == csubstr::npos);
1111 const size_t numnewlines_at_end = s.
len - trimmed.len;
1112 const bool is_newline_only = (trimmed.len == 0 && (s.len > 0));
1113 const bool explicit_indentation = s.triml(
"\n\r").begins_with_any(
" \t");
1116 if(explicit_indentation)
1119 if(numnewlines_at_end == 0)
1121 else if(numnewlines_at_end > 1 || is_newline_only)
1128 for(
size_t i = 0; i < trimmed.len; ++i)
1130 if(trimmed[i] !=
'\n')
1133 if( ! _is_indented_block(s, pos, i))
1137 indent_(ilevel + 1);
1138 write_(s.range(pos, i));
1139 i = write_escaped_newlines_(s, i);
1146 if(s.str[i+1] ==
'\n')
1149 i = write_escaped_newlines_(s, i);
1163 indent_(ilevel + 1);
1164 write_(s.range(pos, i));
1165 if(pos > 0 || !s.begins_with_any(
" \t"))
1166 i = write_indented_block_(s, i, ilevel);
1170 if(pos < trimmed.len)
1172 indent_(ilevel + 1);
1173 write_(trimmed.sub(pos));
1176 for(
size_t i = !is_newline_only; i < numnewlines_at_end; ++i)
1180template<
class Writer>
1181void Emitter<Writer>::write_scalar_squo_(csubstr s, id_type ilevel)
1185 for(
size_t i = 0; i < s.len; ++i)
1189 write_(s.range(pos, i));
1191 i = write_escaped_newlines_(s, i);
1194 indent_(ilevel + 1);
1197 else if(s[i] ==
'\'')
1212template<
class Writer>
1213void Emitter<Writer>::write_scalar_dquo_(csubstr s, id_type ilevel)
1217 for(
size_t i = 0; i < s.len; ++i)
1219 const char curr = s.str[i];
1232#ifndef prefer_writing_newlines_as_double_newlines
1247 _write(s.range(pos, i));
1248 i = _write_escaped_newlines(s, i);
1254 _indent(ilevel + 1);
1256 size_t first = s.first_not_of(
" \t", i);
1263 _write(s.range(i, first));
1275 const size_t next = s.first_not_of(
" \t\r", i);
1276 if(next != npos && s.str[next] ==
'\n')
1310template<
class Writer>
1311void Emitter<Writer>::write_scalar_plain_(csubstr s, id_type ilevel)
1313 if(C4_UNLIKELY(ilevel == 0 && (s.begins_with(
"...") || s.begins_with(
"---"))))
1315 indent_(ilevel + 1);
1319 for(
size_t i = 0; i < s.len; ++i)
1321 const char curr = s.str[i];
1326 i = write_escaped_newlines_(s, i);
1329 indent_(ilevel + 1);
1341inline type_bits json_type_(type_bits ty)
1345 sl_bits = (CONTAINER_STYLE & ~FLOW_SPC),
1352 else if((ty & (SEQ|MAP)) && !(ty & sl_bits))
1361template<
class Writer>
1362void Emitter<Writer>::json_emit_(id_type
id)
1364 NodeType ty = m_tree->type(
id);
1366 if(C4_UNLIKELY(ty.is_stream() && m_opts.json_err_on_stream()))
1367 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"found stream node");
1368 static_assert(
STREAM &
SEQ,
"STREAM must be a SEQ");
1369 ty = detail::json_type_(ty);
1370 if(ty.is_flow_mlx())
1372 json_visit_ml_(
id, ty, 0);
1377 json_visit_sl_(
id, ty, 0);
1381template<
class Writer>
1382void Emitter<Writer>::json_visit_sl_(id_type
id, NodeType ty, id_type depth)
1384 if(C4_UNLIKELY(depth > m_opts.max_depth()))
1385 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"max depth exceeded");
1388 json_writev_(
id, ty);
1390 else if(ty.is_keyval())
1392 json_writek_(
id, ty);
1394 json_writev_(
id, ty);
1396 else if(ty.is_container())
1398 ty = detail::json_type_(ty);
1401 json_writek_(
id, ty);
1406 else if(ty.is_map())
1409 for(id_type child = m_tree->first_child(
id); child != NONE; child = m_tree->next_sibling(child))
1411 if(child != m_tree->first_child(
id))
1413 if((ty & FLOW_SPC) || m_opts.force_flow_spc())
1418 json_visit_sl_(child, m_tree->type(child), depth+1);
1423 else if(ty.is_map())
1428template<
class Writer>
1429void Emitter<Writer>::json_visit_ml_(id_type
id, NodeType ty, id_type depth)
1431 if(C4_UNLIKELY(depth > m_opts.max_depth()))
1432 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"max depth exceeded");
1435 json_writev_(
id, ty);
1437 else if(ty.is_keyval())
1439 json_writek_(
id, ty);
1441 json_writev_(
id, ty);
1443 else if(ty.is_container())
1445 ty = detail::json_type_(ty);
1448 json_writek_(
id, ty);
1453 else if(ty.is_map())
1456 if(m_tree->has_children(
id))
1459 if(m_opts.indent_flow_ml()) ++m_ilevel;
1462 for(id_type first = m_tree->first_child(
id), child = first;
1464 child = m_tree->next_sibling(child))
1469 const size_t maxcols = m_opts.max_cols();
1470 if((ty & FLOW_MLN) && (m_col+1 < maxcols))
1472 if((ty & FLOW_SPC) || m_opts.force_flow_spc())
1475 else if((ty & FLOW_ML1) || (m_col+1 >= maxcols))
1481 NodeType chty = m_tree->type(child);
1482 if(chty.is_flow_sl())
1483 json_visit_sl_(child, chty, depth);
1485 json_visit_ml_(child, chty, depth);
1487 if(m_opts.indent_flow_ml()) --m_ilevel;
1495 else if(ty.is_map())
1500template<
class Writer>
1501bool Emitter<Writer>::json_maybe_write_naninf_(csubstr s)
1505 case 3:
case 4:
case 5:
1511 const char first = s.str[0];
1513 if(s.len == 4 && first ==
'.')
1516 goto write_inf_positive;
1520 else if(first ==
'-' || first ==
'+')
1523 if((rest.len == 4 && rest.str[0] ==
'.' &&
scalar_is_inf3(rest.sub(1)))
1527 || (rest.len == 8 && (0 == memcmp(rest.str,
"infinity", 8))))
1530 goto write_inf_negative;
1532 goto write_inf_positive;
1535 else if(s.len == 8 && (0 == memcmp(s.str,
"infinity", 8)))
1537 goto write_inf_positive;
1542 goto write_inf_positive;
1551 write_(
"\"-.inf\"");
1558template<
class Writer>
1559void Emitter<Writer>::json_writek_(id_type
id, NodeType ty)
1561 if(C4_UNLIKELY(ty.has_key_tag() && m_opts.json_err_on_tag()))
1562 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"JSON does not have tags");
1563 if(C4_UNLIKELY(ty.has_key_anchor() && m_opts.json_err_on_anchor()))
1564 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"JSON does not have anchors");
1568 if(json_maybe_write_naninf_(key))
1571 json_write_scalar_dquo_(key);
1579template<
class Writer>
1580void Emitter<Writer>::json_writev_(id_type
id, NodeType ty)
1582 if(C4_UNLIKELY(ty.has_val_tag() && m_opts.json_err_on_tag()))
1583 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"JSON does not have tags");
1584 if(C4_UNLIKELY(ty.has_val_anchor() && m_opts.json_err_on_anchor()))
1585 _RYML_ERR_VISIT_(m_tree->callbacks(), m_tree,
id,
"JSON does not have anchors");
1586 csubstr val = m_tree->val(
id);
1590 bool dquoted = ((ty &
VALQUO)
1593 json_write_scalar_dquo_(val);
1594 else if(json_maybe_write_naninf_(val))
1596 else if(val.is_number())
1597 json_write_number_(val);
1603 if(val.str || (ty & (VALQUO|VALTAG)))
1611template<
class Writer>
1612void Emitter<Writer>::json_write_scalar_dquo_(csubstr s)
1616 for(
size_t i = 0; i < s.len; ++i)
1621 write_(s.range(pos, i));
1626 write_(s.range(pos, i));
1631 write_(s.range(pos, i));
1636 write_(s.range(pos, i));
1641 write_(s.range(pos, i));
1646 write_(s.range(pos, i));
1651 write_(s.range(pos, i));
1665template<
class Writer>
1666void Emitter<Writer>::json_write_number_(csubstr s)
1674 if(s.begins_with(
'-') && s.len > 1)
1677 if(rest.begins_with(
'.'))
1682 else if(rest.ends_with(
'.'))
1692 else if(s.begins_with(
'.'))
1697 else if(s.ends_with(
'.'))
1715C4_SUPPRESS_WARNING_GCC_CLANG_POP
void emit_as(EmitType_e type, Tree const *tree, id_type id=NONE)
emit!
id_type root_id() const
Get the id of the root node. The tree must not be empty. The tree can be empty only when constructed ...
bool empty() const
Query for zero size.
Callbacks const & callbacks() const
Error utilities used by ryml.
bool scalar_is_nan3(csubstr s) noexcept
Query if a scalar is inf (inf, Inf, INF).
NodeType_e scalar_style_choose_flow(csubstr s) noexcept
choose a YAML scalar style based on the scalar's contents, while in flow mode.
NodeType_e scalar_style_choose_block(csubstr s) noexcept
choose a YAML scalar style based on the scalar's contents, while in block mode.
NodeType_e
a bit mask for marking node types and styles
bool scalar_is_inf3(csubstr s) noexcept
Query if a scalar is nan (nan, NaN, NAN).
NodeType_e scalar_style_choose_json(csubstr s) noexcept
choose a json scalar style based on the scalar's contents
uint32_t type_bits
the integral type necessary to cover all the bits for NodeType_e
@ KEY_DQUO
mark key scalar as double quoted "
@ STREAM
a stream: a seq of docs
@ FLOW_ML1
mark container with multi-line flow style, 1 element per line
@ VAL_FOLDED
mark val scalar as multiline, block folded >
@ VAL_STYLE
mask of VALQUO|VAL_PLAIN : all the val scalar styles for val (not container styles!...
@ FLOW_SL
mark container with single-line flow style
@ SEQ
a seq: a parent of VAL/SEQ/MAP nodes
@ SCALAR_DQUO
mask of KEY_DQUO|VAL_DQUO,
@ VAL_SQUO
mark val scalar as single quoted '
@ KEY_STYLE
mask of KEYQUO|KEY_PLAIN : all the key scalar styles for key (not container styles!...
@ VAL_PLAIN
mark val scalar as plain scalar (unquoted, even when multiline)
@ BLOCK
mark container with block style
@ FLOW_SPC
mark container with spaces after comma when in flow mode. Applies to both FLOW_SL and FLOW_MLN (but n...
@ VALQUO
val style is one of '">|. mask of VAL_SQUO|VAL_DQUO|VAL_FOLDED|VAL_LITERAL
@ VAL_DQUO
mark val scalar as double quoted "
@ KEY_SQUO
mark key scalar as single quoted '
@ VAL_LITERAL
mark val scalar as multiline, block literal |
@ KEY_LITERAL
mark key scalar as multiline, block literal |
@ KEY_PLAIN
mark key scalar as plain scalar (unquoted, even when multiline)
@ KEY_FOLDED
mark key scalar as multiline, block folded >
basic_substring< const char > csubstr
an immutable string view
@ npos
a null string position
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...
(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