rapidyaml  0.10.0
parse and emit YAML, and do it fast
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 
13 namespace c4 {
14 namespace yml {
15 
16 template<class Writer>
17 substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& t, id_type id, bool error_on_excess)
18 {
19  if(t.empty())
20  {
21  _RYML_CB_ASSERT(t.callbacks(), id == NONE);
22  return {};
23  }
24  if(id == NONE)
25  id = t.root_id();
26  _RYML_CB_CHECK(t.callbacks(), id < t.capacity());
27  m_tree = &t;
28  m_flow = false;
29  if(type == EMIT_YAML)
30  _emit_yaml(id);
31  else if(type == EMIT_JSON)
32  _do_visit_json(id, 0);
33  else
34  _RYML_CB_ERR(m_tree->callbacks(), "unknown emit type");
35  m_tree = nullptr;
36  return this->Writer::_get(error_on_excess);
37 }
38 
39 
40 //-----------------------------------------------------------------------------
41 
42 template<class Writer>
44 {
45  // save branches in the visitor by doing the initial stream/doc
46  // logic here, sparing the need to check stream/val/keyval inside
47  // the visitor functions
48  auto dispatch = [this](id_type node){
49  NodeType ty = m_tree->type(node);
50  if(ty.is_flow_sl())
51  _do_visit_flow_sl(node, 0);
52  else if(ty.is_flow_ml())
53  _do_visit_flow_ml(node, 0);
54  else
55  {
56  _do_visit_block(node, 0);
57  }
58  };
59  if(!m_tree->is_root(id))
60  {
61  if(m_tree->is_container(id) && !m_tree->type(id).is_flow())
62  {
63  id_type ilevel = 0;
64  if(m_tree->has_key(id))
65  {
66  this->Writer::_do_write(m_tree->key(id));
67  this->Writer::_do_write(":\n");
68  ++ilevel;
69  }
70  _do_visit_block_container(id, 0, ilevel, ilevel);
71  return;
72  }
73  }
74 
75  TagDirectiveRange tagds = m_tree->tag_directives();
76  auto write_tag_directives = [&tagds, this](const id_type next_node){
77  TagDirective const* C4_RESTRICT end = tagds.b;
78  while(end < tagds.e)
79  {
80  if(end->next_node_id > next_node)
81  break;
82  ++end;
83  }
84  const id_type parent = m_tree->parent(next_node);
85  for( ; tagds.b != end; ++tagds.b)
86  {
87  if(next_node != m_tree->first_child(parent))
88  this->Writer::_do_write("...\n");
89  this->Writer::_do_write("%TAG ");
90  this->Writer::_do_write(tagds.b->handle);
91  this->Writer::_do_write(' ');
92  this->Writer::_do_write(tagds.b->prefix);
93  this->Writer::_do_write('\n');
94  }
95  };
96  if(m_tree->is_stream(id))
97  {
98  const id_type first_child = m_tree->first_child(id);
99  if(first_child != NONE)
100  write_tag_directives(first_child);
101  for(id_type child = first_child; child != NONE; child = m_tree->next_sibling(child))
102  {
103  dispatch(child);
104  if(m_tree->is_doc(child) && m_tree->type(child).is_flow_sl())
105  this->Writer::_do_write('\n');
106  if(m_tree->next_sibling(child) != NONE)
107  write_tag_directives(m_tree->next_sibling(child));
108  }
109  }
110  else if(m_tree->is_container(id))
111  {
112  dispatch(id);
113  }
114  else if(m_tree->is_doc(id))
115  {
116  _RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->is_container(id)); // checked above
117  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_val(id)); // so it must be a val
118  _write_doc(id);
119  }
120  else if(m_tree->is_keyval(id))
121  {
122  _writek(id, 0);
123  this->Writer::_do_write(": ");
124  _writev(id, 0);
125  if(!m_tree->type(id).is_flow())
126  this->Writer::_do_write('\n');
127  }
128  else if(m_tree->is_val(id))
129  {
130  //this->Writer::_do_write("- ");
131  _writev(id, 0);
132  if(!m_tree->type(id).is_flow())
133  this->Writer::_do_write('\n');
134  }
135  else if(m_tree->type(id) == NOTYPE)
136  {
137  ;
138  }
139  else
140  {
141  _RYML_CB_ERR(m_tree->callbacks(), "unknown type");
142  }
143 }
144 
145 #define _rymlindent_nextline() this->_indent(ilevel + 1);
146 
147 template<class Writer>
148 void Emitter<Writer>::_write_doc(id_type id)
149 {
150  const NodeType ty = m_tree->type(id);
151  RYML_ASSERT(ty.is_doc());
152  RYML_ASSERT(!ty.has_key());
153  if(!m_tree->is_root(id))
154  {
155  RYML_ASSERT(m_tree->is_stream(m_tree->parent(id)));
156  this->Writer::_do_write("---");
157  }
158  //
159  if(!ty.has_val()) // this is more frequent
160  {
161  const bool tag = ty.has_val_tag();
162  const bool anchor = ty.has_val_anchor();
163  if(!tag && !anchor)
164  {
165  ;
166  }
167  else if(!tag && anchor)
168  {
169  if(!m_tree->is_root(id))
170  this->Writer::_do_write(' ');
171  this->Writer::_do_write('&');
172  this->Writer::_do_write(m_tree->val_anchor(id));
173  #ifdef RYML_NO_COVERAGE__TO_BE_DELETED
174  if(m_tree->has_children(id) && m_tree->is_root(id))
175  this->Writer::_do_write('\n');
176  #endif
177  }
178  else if(tag && !anchor)
179  {
180  if(!m_tree->is_root(id))
181  this->Writer::_do_write(' ');
182  _write_tag(m_tree->val_tag(id));
183  #ifdef RYML_NO_COVERAGE__TO_BE_DELETED
184  if(m_tree->has_children(id) && m_tree->is_root(id))
185  this->Writer::_do_write('\n');
186  #endif
187  }
188  else // tag && anchor
189  {
190  if(!m_tree->is_root(id))
191  this->Writer::_do_write(' ');
192  _write_tag(m_tree->val_tag(id));
193  this->Writer::_do_write(" &");
194  this->Writer::_do_write(m_tree->val_anchor(id));
195  #ifdef RYML_NO_COVERAGE__TO_BE_DELETED
196  if(m_tree->has_children(id) && m_tree->is_root(id))
197  this->Writer::_do_write('\n');
198  #endif
199  }
200  }
201  else // docval
202  {
203  _RYML_CB_ASSERT(m_tree->callbacks(), ty.has_val());
204  // some plain scalars such as '...' and '---' must not
205  // appear at 0-indentation
206  const csubstr val = m_tree->val(id);
207  const bool preceded_by_3_dashes = !m_tree->is_root(id);
208  const type_bits style_marks = ty & VAL_STYLE;
209  const bool is_plain = ty.is_val_plain();
210  const bool is_ambiguous = (is_plain || !style_marks)
211  && ((val.begins_with("...") || val.begins_with("---"))
212  ||
213  (val.find('\n') != npos));
214  if(preceded_by_3_dashes)
215  {
216  if(is_plain && val.len == 0 && !ty.has_val_anchor() && !ty.has_val_tag())
217  {
218  this->Writer::_do_write('\n');
219  return;
220  }
221  else if(val.len && is_ambiguous)
222  {
223  this->Writer::_do_write('\n');
224  }
225  else
226  {
227  this->Writer::_do_write(' ');
228  }
229  }
230  id_type ilevel = 0u;
231  if(is_ambiguous)
232  {
234  ++ilevel;
235  }
236  _writev(id, ilevel);
237  if(val.len && m_tree->is_root(id))
238  this->Writer::_do_write('\n');
239  }
240  if(!m_tree->is_root(id))
241  this->Writer::_do_write('\n');
242 }
243 
244 template<class Writer>
245 void Emitter<Writer>::_do_visit_flow_sl(id_type node, id_type depth, id_type ilevel)
246 {
247  const bool prev_flow = m_flow;
248  m_flow = true;
249  _RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->is_stream(node));
250  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(node) || m_tree->is_doc(node));
251  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
252  if(C4_UNLIKELY(depth > m_opts.max_depth()))
253  _RYML_CB_ERR(m_tree->callbacks(), "max depth exceeded");
254 
255  if(m_tree->is_doc(node))
256  {
257  _write_doc(node);
258  #ifdef RYML_NO_COVERAGE__TO_BE_DELETED
259  if(!m_tree->has_children(node))
260  return;
261  else
262  #endif
263  {
264  if(m_tree->is_map(node))
265  {
266  this->Writer::_do_write('{');
267  }
268  else
269  {
270  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_seq(node));
271  this->Writer::_do_write('[');
272  }
273  }
274  }
275  else if(m_tree->is_container(node))
276  {
277  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_map(node) || m_tree->is_seq(node));
278 
279  bool spc = false; // write a space
280 
281  if(m_tree->has_key(node))
282  {
283  _writek(node, ilevel);
284  this->Writer::_do_write(':');
285  spc = true;
286  }
287 
288  if(m_tree->has_val_tag(node))
289  {
290  if(spc)
291  this->Writer::_do_write(' ');
292  _write_tag(m_tree->val_tag(node));
293  spc = true;
294  }
295 
296  if(m_tree->has_val_anchor(node))
297  {
298  if(spc)
299  this->Writer::_do_write(' ');
300  this->Writer::_do_write('&');
301  this->Writer::_do_write(m_tree->val_anchor(node));
302  spc = true;
303  }
304 
305  if(spc)
306  this->Writer::_do_write(' ');
307 
308  if(m_tree->is_map(node))
309  {
310  this->Writer::_do_write('{');
311  }
312  else
313  {
314  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_seq(node));
315  this->Writer::_do_write('[');
316  }
317  } // container
318 
319  for(id_type child = m_tree->first_child(node), count = 0; child != NONE; child = m_tree->next_sibling(child))
320  {
321  if(count++)
322  this->Writer::_do_write(',');
323  if(m_tree->is_keyval(child))
324  {
325  _writek(child, ilevel);
326  this->Writer::_do_write(": ");
327  _writev(child, ilevel);
328  }
329  else if(m_tree->is_val(child))
330  {
331  _writev(child, ilevel);
332  }
333  else
334  {
335  // with single-line flow, we can never go back to block
336  _do_visit_flow_sl(child, depth + 1, ilevel + 1);
337  }
338  }
339 
340  if(m_tree->is_map(node))
341  {
342  this->Writer::_do_write('}');
343  }
344  else if(m_tree->is_seq(node))
345  {
346  this->Writer::_do_write(']');
347  }
348  m_flow = prev_flow;
349 }
350 
351 C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4702) // unreachable error, triggered by flow_ml not implemented
352 
353 template<class Writer>
354 void Emitter<Writer>::_do_visit_flow_ml(id_type id, id_type depth, id_type ilevel, id_type do_indent)
355 {
356  C4_UNUSED(id);
357  C4_UNUSED(depth);
358  C4_UNUSED(ilevel);
359  C4_UNUSED(do_indent);
360  c4::yml::error("not implemented");
361  #ifdef THIS_IS_A_WORK_IN_PROGRESS
362  if(C4_UNLIKELY(depth > m_opts.max_depth()))
363  _RYML_CB_ERR(m_tree->callbacks(), "max depth exceeded");
364  const bool prev_flow = m_flow;
365  m_flow = true;
366  // do it...
367  m_flow = prev_flow;
368  #endif
369 }
370 
371 template<class Writer>
372 void Emitter<Writer>::_do_visit_block_container(id_type node, id_type depth, id_type level, bool do_indent)
373 {
374  if(m_tree->is_seq(node))
375  {
376  for(id_type child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))
377  {
378  _RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->has_key(child));
379  if(m_tree->is_val(child))
380  {
381  _indent(level, do_indent);
382  this->Writer::_do_write("- ");
383  _writev(child, level);
384  this->Writer::_do_write('\n');
385  }
386  else
387  {
388  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(child));
389  NodeType ty = m_tree->type(child);
390  if(ty.is_flow_sl())
391  {
392  _indent(level, do_indent);
393  this->Writer::_do_write("- ");
394  _do_visit_flow_sl(child, depth+1, 0u);
395  this->Writer::_do_write('\n');
396  }
397  else if(ty.is_flow_ml())
398  {
399  _indent(level, do_indent);
400  this->Writer::_do_write("- ");
401  _do_visit_flow_ml(child, depth+1, 0u, do_indent);
402  this->Writer::_do_write('\n');
403  }
404  else
405  {
406  _do_visit_block(child, depth+1, level, do_indent); // same indentation level
407  }
408  }
409  do_indent = true;
410  }
411  }
412  else // map
413  {
414  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_map(node));
415  for(id_type ich = m_tree->first_child(node); ich != NONE; ich = m_tree->next_sibling(ich))
416  {
417  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->has_key(ich));
418  if(m_tree->is_keyval(ich))
419  {
420  _indent(level, do_indent);
421  _writek(ich, level);
422  this->Writer::_do_write(": ");
423  _writev(ich, level);
424  this->Writer::_do_write('\n');
425  }
426  else
427  {
428  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(ich));
429  NodeType ty = m_tree->type(ich);
430  if(ty.is_flow_sl())
431  {
432  _indent(level, do_indent);
433  _do_visit_flow_sl(ich, depth+1, 0u);
434  this->Writer::_do_write('\n');
435  }
436  else if(ty.is_flow_ml())
437  {
438  _indent(level, do_indent);
439  _do_visit_flow_ml(ich, depth+1, 0u);
440  this->Writer::_do_write('\n');
441  }
442  else
443  {
444  _do_visit_block(ich, depth+1, level, do_indent); // same level!
445  }
446  } // keyval vs container
447  do_indent = true;
448  } // for children
449  } // seq vs map
450 }
451 
452 template<class Writer>
453 void Emitter<Writer>::_do_visit_block(id_type node, id_type depth, id_type ilevel, id_type do_indent)
454 {
455  _RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->is_stream(node));
456  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(node) || m_tree->is_doc(node));
457  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
458  if(C4_UNLIKELY(depth > m_opts.max_depth()))
459  _RYML_CB_ERR(m_tree->callbacks(), "max depth exceeded");
460  if(m_tree->is_doc(node))
461  {
462  _write_doc(node);
463  if(!m_tree->has_children(node))
464  return;
465  }
466  else if(m_tree->is_container(node))
467  {
468  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_map(node) || m_tree->is_seq(node));
469  bool spc = false; // write a space
470  bool nl = false; // write a newline
471  if(m_tree->has_key(node))
472  {
473  _indent(ilevel, do_indent);
474  _writek(node, ilevel);
475  this->Writer::_do_write(':');
476  spc = true;
477  }
478  else if(!m_tree->is_root(node))
479  {
480  _indent(ilevel, do_indent);
481  this->Writer::_do_write('-');
482  spc = true;
483  }
484 
485  if(m_tree->has_val_tag(node))
486  {
487  if(spc)
488  this->Writer::_do_write(' ');
489  _write_tag(m_tree->val_tag(node));
490  spc = true;
491  nl = true;
492  }
493 
494  if(m_tree->has_val_anchor(node))
495  {
496  if(spc)
497  this->Writer::_do_write(' ');
498  this->Writer::_do_write('&');
499  this->Writer::_do_write(m_tree->val_anchor(node));
500  spc = true;
501  nl = true;
502  }
503 
504  if(m_tree->has_children(node))
505  {
506  if(m_tree->has_key(node))
507  nl = true;
508  else
509  if(!m_tree->is_root(node) && !nl)
510  spc = true;
511  }
512  else
513  {
514  if(m_tree->is_seq(node))
515  this->Writer::_do_write(" []\n");
516  else if(m_tree->is_map(node))
517  this->Writer::_do_write(" {}\n");
518  return;
519  }
520 
521  if(spc && !nl)
522  this->Writer::_do_write(' ');
523 
524  do_indent = 0;
525  if(nl)
526  {
527  this->Writer::_do_write('\n');
528  do_indent = 1;
529  }
530  } // container
531 
532  id_type next_level = ilevel + 1;
533  if(m_tree->is_root(node) || m_tree->is_doc(node))
534  next_level = ilevel; // do not indent at top level
535 
536  _do_visit_block_container(node, depth, next_level, do_indent);
537 }
538 
539 C4_SUPPRESS_WARNING_MSVC_POP
540 
541 
542 template<class Writer>
543 void Emitter<Writer>::_do_visit_json(id_type id, id_type depth)
544 {
545  _RYML_CB_CHECK(m_tree->callbacks(), !m_tree->is_stream(id)); // JSON does not have streams
546  if(C4_UNLIKELY(depth > m_opts.max_depth()))
547  _RYML_CB_ERR(m_tree->callbacks(), "max depth exceeded");
548  if(m_tree->is_keyval(id))
549  {
550  _writek_json(id);
551  this->Writer::_do_write(": ");
552  _writev_json(id);
553  }
554  else if(m_tree->is_val(id))
555  {
556  _writev_json(id);
557  }
558  else if(m_tree->is_container(id))
559  {
560  if(m_tree->has_key(id))
561  {
562  _writek_json(id);
563  this->Writer::_do_write(": ");
564  }
565  if(m_tree->is_seq(id))
566  this->Writer::_do_write('[');
567  else if(m_tree->is_map(id))
568  this->Writer::_do_write('{');
569  } // container
570 
571  for(id_type ich = m_tree->first_child(id); ich != NONE; ich = m_tree->next_sibling(ich))
572  {
573  if(ich != m_tree->first_child(id))
574  this->Writer::_do_write(',');
575  _do_visit_json(ich, depth+1);
576  }
577 
578  if(m_tree->is_seq(id))
579  this->Writer::_do_write(']');
580  else if(m_tree->is_map(id))
581  this->Writer::_do_write('}');
582 }
583 
584 template<class Writer>
585 void Emitter<Writer>::_write(NodeScalar const& C4_RESTRICT sc, NodeType flags, id_type ilevel)
586 {
587  if( ! sc.tag.empty())
588  {
589  _write_tag(sc.tag);
590  this->Writer::_do_write(' ');
591  }
592  if(flags.has_anchor())
593  {
594  RYML_ASSERT(flags.is_ref() != flags.has_anchor());
595  RYML_ASSERT( ! sc.anchor.empty());
596  this->Writer::_do_write('&');
597  this->Writer::_do_write(sc.anchor);
598  this->Writer::_do_write(' ');
599  }
600  else if(flags.is_ref())
601  {
602  if(sc.anchor != "<<")
603  this->Writer::_do_write('*');
604  this->Writer::_do_write(sc.anchor);
605  if(flags.is_key_ref())
606  this->Writer::_do_write(' ');
607  return;
608  }
609 
610  // ensure the style flags only have one of KEY or VAL
611  _RYML_CB_ASSERT(m_tree->callbacks(), ((flags & SCALAR_STYLE) == 0) || (((flags & KEY_STYLE) == 0) != ((flags & VAL_STYLE) == 0)));
612  type_bits style_marks = flags & SCALAR_STYLE;
613  if(!style_marks)
614  style_marks = scalar_style_choose(sc.scalar);
615  if(style_marks & (KEY_LITERAL|VAL_LITERAL))
616  {
617  _write_scalar_literal(sc.scalar, ilevel, flags.has_key());
618  }
619  else if(style_marks & (KEY_FOLDED|VAL_FOLDED))
620  {
621  _write_scalar_folded(sc.scalar, ilevel, flags.has_key());
622  }
623  else if(style_marks & (KEY_SQUO|VAL_SQUO))
624  {
625  _write_scalar_squo(sc.scalar, ilevel);
626  }
627  else if(style_marks & (KEY_DQUO|VAL_DQUO))
628  {
629  _write_scalar_dquo(sc.scalar, ilevel);
630  }
631  else if(style_marks & (KEY_PLAIN|VAL_PLAIN))
632  {
633  if(C4_LIKELY(!(sc.scalar.begins_with(": ") || sc.scalar.begins_with(":\t"))))
634  _write_scalar_plain(sc.scalar, ilevel);
635  else
636  _write_scalar_squo(sc.scalar, ilevel);
637  }
638  else
639  {
640  _RYML_CB_ERR(m_tree->callbacks(), "not implemented");
641  }
642 }
643 
644 template<class Writer>
645 void Emitter<Writer>::_write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags)
646 {
647  if(flags & (KEYTAG|VALTAG))
648  if(m_opts.json_error_flags() & EmitOptions::JSON_ERR_ON_TAG)
649  _RYML_CB_ERR(m_tree->callbacks(), "JSON does not have tags");
650  if(C4_UNLIKELY(flags.has_anchor()))
651  if(m_opts.json_error_flags() & EmitOptions::JSON_ERR_ON_ANCHOR)
652  _RYML_CB_ERR(m_tree->callbacks(), "JSON does not have anchors");
653  if(sc.scalar.len)
654  {
655  // use double quoted style...
656  // if it is a key (mandatory in JSON)
657  // if the style is marked quoted
658  bool dquoted = ((flags & (KEY|VALQUO))
659  || (scalar_style_json_choose(sc.scalar) & SCALAR_DQUO)); // choose the style
660  if(dquoted)
661  _write_scalar_json_dquo(sc.scalar);
662  else
663  this->Writer::_do_write(sc.scalar);
664  }
665  else
666  {
667  if(sc.scalar.str || (flags & (KEY|VALQUO|KEYTAG|VALTAG)))
668  this->Writer::_do_write("\"\"");
669  else
670  this->Writer::_do_write("null");
671  }
672 }
673 
674 template<class Writer>
675 size_t Emitter<Writer>::_write_escaped_newlines(csubstr s, size_t i)
676 {
677  RYML_ASSERT(s.len > i);
678  RYML_ASSERT(s.str[i] == '\n');
679  //_c4dbgpf("nl@i={} rem=[{}]~~~{}~~~", i, s.sub(i).len, s.sub(i));
680  // add an extra newline for each sequence of consecutive
681  // newline/whitespace
682  this->Writer::_do_write('\n');
683  do
684  {
685  this->Writer::_do_write('\n'); // write the newline again
686  ++i; // increase the outer loop counter!
687  } while(i < s.len && s.str[i] == '\n');
688  _RYML_CB_ASSERT(m_tree->callbacks(), i > 0);
689  --i;
690  _RYML_CB_ASSERT(m_tree->callbacks(), s.str[i] == '\n');
691  return i;
692 }
693 
694 inline bool _is_indented_block(csubstr s, size_t prev, size_t i) noexcept
695 {
696  if(prev == 0 && s.begins_with_any(" \t"))
697  return true;
698  const size_t pos = s.first_not_of('\n', i);
699  return (pos != npos) && (s.str[pos] == ' ' || s.str[pos] == '\t');
700 }
701 
702 template<class Writer>
703 size_t Emitter<Writer>::_write_indented_block(csubstr s, size_t i, id_type ilevel)
704 {
705  //_c4dbgpf("indblock@i={} rem=[{}]~~~\n{}~~~", i, s.sub(i).len, s.sub(i));
706  _RYML_CB_ASSERT(m_tree->callbacks(), i > 0);
707  _RYML_CB_ASSERT(m_tree->callbacks(), s.str[i-1] == '\n');
708  _RYML_CB_ASSERT(m_tree->callbacks(), i < s.len);
709  _RYML_CB_ASSERT(m_tree->callbacks(), s.str[i] == ' ' || s.str[i] == '\t' || s.str[i] == '\n');
710 again:
711  size_t pos = s.find("\n ", i);
712  if(pos == npos)
713  pos = s.find("\n\t", i);
714  if(pos != npos)
715  {
716  ++pos;
717  //_c4dbgpf("indblock line@i={} rem=[{}]~~~\n{}~~~", i, s.range(i, pos).len, s.range(i, pos));
719  this->Writer::_do_write(s.range(i, pos));
720  i = pos;
721  goto again; // NOLINT
722  }
723  // consume the newlines after the indented block
724  // to prevent them from being escaped
725  pos = s.find('\n', i);
726  if(pos != npos)
727  {
728  const size_t pos2 = s.first_not_of('\n', pos);
729  pos = (pos2 != npos) ? pos2 : pos;
730  //_c4dbgpf("indblock line@i={} rem=[{}]~~~\n{}~~~", i, s.range(i, pos).len, s.range(i, pos));
732  this->Writer::_do_write(s.range(i, pos));
733  i = pos;
734  }
735  return i;
736 }
737 
738 template<class Writer>
739 void Emitter<Writer>::_write_scalar_literal(csubstr s, id_type ilevel, bool explicit_key)
740 {
741  _RYML_CB_ASSERT(m_tree->callbacks(), s.find("\r") == csubstr::npos);
742  if(explicit_key)
743  this->Writer::_do_write("? ");
744  csubstr trimmed = s.trimr('\n');
745  const size_t numnewlines_at_end = s.len - trimmed.len;
746  const bool is_newline_only = (trimmed.len == 0 && (s.len > 0));
747  const bool explicit_indentation = s.triml("\n\r").begins_with_any(" \t");
748  //
749  this->Writer::_do_write('|');
750  if(explicit_indentation)
751  this->Writer::_do_write('2');
752  //
753  if(numnewlines_at_end > 1 || is_newline_only)
754  this->Writer::_do_write('+');
755  else if(numnewlines_at_end == 0)
756  this->Writer::_do_write('-');
757  //
758  if(trimmed.len)
759  {
760  this->Writer::_do_write('\n');
761  size_t pos = 0; // tracks the last character that was already written
762  for(size_t i = 0; i < trimmed.len; ++i)
763  {
764  if(trimmed[i] != '\n')
765  continue;
766  // write everything up to this point
767  csubstr since_pos = trimmed.range(pos, i+1); // include the newline
769  this->Writer::_do_write(since_pos);
770  pos = i+1; // already written
771  }
772  if(pos < trimmed.len)
773  {
775  this->Writer::_do_write(trimmed.sub(pos));
776  }
777  }
778  for(size_t i = !is_newline_only; i < numnewlines_at_end; ++i)
779  this->Writer::_do_write('\n');
780  if(explicit_key)
781  {
782  this->Writer::_do_write('\n');
783  this->_indent(ilevel);
784  }
785 }
786 
787 template<class Writer>
788 void Emitter<Writer>::_write_scalar_folded(csubstr s, id_type ilevel, bool explicit_key)
789 {
790  if(explicit_key)
791  this->Writer::_do_write("? ");
792  _RYML_CB_ASSERT(m_tree->callbacks(), s.find("\r") == csubstr::npos);
793  csubstr trimmed = s.trimr('\n');
794  const size_t numnewlines_at_end = s.len - trimmed.len;
795  const bool is_newline_only = (trimmed.len == 0 && (s.len > 0));
796  const bool explicit_indentation = s.triml("\n\r").begins_with_any(" \t");
797  //
798  this->Writer::_do_write('>');
799  if(explicit_indentation)
800  this->Writer::_do_write('2');
801  //
802  if(numnewlines_at_end == 0)
803  this->Writer::_do_write('-');
804  else if(numnewlines_at_end > 1 || is_newline_only)
805  this->Writer::_do_write('+');
806  //
807  if(trimmed.len)
808  {
809  this->Writer::_do_write('\n');
810  size_t pos = 0; // tracks the last character that was already written
811  for(size_t i = 0; i < trimmed.len; ++i)
812  {
813  if(trimmed[i] != '\n')
814  continue;
815  // escape newline sequences
816  if( ! _is_indented_block(s, pos, i))
817  {
818  if(pos < i)
819  {
821  this->Writer::_do_write(s.range(pos, i));
822  i = _write_escaped_newlines(s, i);
823  pos = i+1;
824  }
825  else
826  {
827  if(i+1 < s.len)
828  {
829  if(s.str[i+1] == '\n')
830  {
831  ++i;
832  i = _write_escaped_newlines(s, i);
833  pos = i+1;
834  }
835  else
836  {
837  this->Writer::_do_write('\n');
838  pos = i+1;
839  }
840  }
841  }
842  }
843  else // do not escape newlines in indented blocks
844  {
845  ++i;
847  this->Writer::_do_write(s.range(pos, i));
848  if(pos > 0 || !s.begins_with_any(" \t"))
849  i = _write_indented_block(s, i, ilevel);
850  pos = i;
851  }
852  }
853  if(pos < trimmed.len)
854  {
856  this->Writer::_do_write(trimmed.sub(pos));
857  }
858  }
859  for(size_t i = !is_newline_only; i < numnewlines_at_end; ++i)
860  this->Writer::_do_write('\n');
861  if(explicit_key)
862  {
863  this->Writer::_do_write('\n');
864  this->_indent(ilevel);
865  }
866 }
867 
868 template<class Writer>
869 void Emitter<Writer>::_write_scalar_squo(csubstr s, id_type ilevel)
870 {
871  size_t pos = 0; // tracks the last character that was already written
872  this->Writer::_do_write('\'');
873  for(size_t i = 0; i < s.len; ++i)
874  {
875  if(s[i] == '\n')
876  {
877  this->Writer::_do_write(s.range(pos, i)); // write everything up to (excluding) this char
878  //_c4dbgpf("newline at {}. writing ~~~{}~~~", i, s.range(pos, i));
879  i = _write_escaped_newlines(s, i);
880  //_c4dbgpf("newline --> {}", i);
881  if(i < s.len)
883  pos = i+1;
884  }
885  else if(s[i] == '\'')
886  {
887  csubstr sub = s.range(pos, i+1);
888  //_c4dbgpf("squote at {}. writing ~~~{}~~~", i, sub);
889  this->Writer::_do_write(sub); // write everything up to (including) this squote
890  this->Writer::_do_write('\''); // write the squote again
891  pos = i+1;
892  }
893  }
894  // write missing characters at the end of the string
895  if(pos < s.len)
896  this->Writer::_do_write(s.sub(pos));
897  this->Writer::_do_write('\'');
898 }
899 
900 template<class Writer>
901 void Emitter<Writer>::_write_scalar_dquo(csubstr s, id_type ilevel)
902 {
903  size_t pos = 0; // tracks the last character that was already written
904  this->Writer::_do_write('"');
905  for(size_t i = 0; i < s.len; ++i)
906  {
907  const char curr = s.str[i];
908  switch(curr) // NOLINT
909  {
910  case '"':
911  case '\\':
912  {
913  csubstr sub = s.range(pos, i);
914  this->Writer::_do_write(sub); // write everything up to (excluding) this char
915  this->Writer::_do_write('\\'); // write the escape
916  this->Writer::_do_write(curr); // write the char
917  pos = i+1;
918  break;
919  }
920 #ifndef prefer_writing_newlines_as_double_newlines
921  case '\n':
922  {
923  csubstr sub = s.range(pos, i);
924  this->Writer::_do_write(sub); // write everything up to (excluding) this char
925  this->Writer::_do_write("\\n"); // write the escape
926  pos = i+1;
927  (void)ilevel;
928  break;
929  }
930 #else
931  case '\n':
932  {
933  // write everything up to (excluding) this newline
934  //_c4dbgpf("nl@i={} rem=[{}]~~~{}~~~", i, s.sub(i).len, s.sub(i));
935  this->Writer::_do_write(s.range(pos, i));
936  i = _write_escaped_newlines(s, i);
937  ++i;
938  pos = i;
939  // as for the next line...
940  if(i < s.len)
941  {
942  _rymlindent_nextline() // indent the next line
943  // escape leading whitespace, and flush it
944  size_t first = s.first_not_of(" \t", i);
945  _c4dbgpf("@i={} first={} rem=[{}]~~~{}~~~", i, first, s.sub(i).len, s.sub(i));
946  if(first > i)
947  {
948  if(first == npos)
949  first = s.len;
950  this->Writer::_do_write('\\');
951  this->Writer::_do_write(s.range(i, first));
952  this->Writer::_do_write('\\');
953  i = first-1;
954  pos = first;
955  }
956  }
957  break;
958  }
959  // escape trailing whitespace before a newline
960  case ' ':
961  case '\t':
962  {
963  const size_t next = s.first_not_of(" \t\r", i);
964  if(next != npos && s.str[next] == '\n')
965  {
966  csubstr sub = s.range(pos, i);
967  this->Writer::_do_write(sub); // write everything up to (excluding) this char
968  this->Writer::_do_write('\\'); // escape the whitespace
969  pos = i;
970  }
971  break;
972  }
973 #endif
974  case '\r':
975  {
976  csubstr sub = s.range(pos, i);
977  this->Writer::_do_write(sub); // write everything up to (excluding) this char
978  this->Writer::_do_write("\\r"); // write the escaped char
979  pos = i+1;
980  break;
981  }
982  case '\b':
983  {
984  csubstr sub = s.range(pos, i);
985  this->Writer::_do_write(sub); // write everything up to (excluding) this char
986  this->Writer::_do_write("\\b"); // write the escaped char
987  pos = i+1;
988  break;
989  }
990  }
991  }
992  // write missing characters at the end of the string
993  if(pos < s.len)
994  this->Writer::_do_write(s.sub(pos));
995  this->Writer::_do_write('"');
996 }
997 
998 template<class Writer>
999 void Emitter<Writer>::_write_scalar_plain(csubstr s, id_type ilevel)
1000 {
1001  if(C4_UNLIKELY(ilevel == 0 && (s.begins_with("...") || s.begins_with("---"))))
1002  {
1003  _rymlindent_nextline() // indent the next line
1004  ++ilevel;
1005  }
1006  size_t pos = 0; // tracks the last character that was already written
1007  for(size_t i = 0; i < s.len; ++i)
1008  {
1009  const char curr = s.str[i];
1010  if(curr == '\n')
1011  {
1012  csubstr sub = s.range(pos, i);
1013  this->Writer::_do_write(sub); // write everything up to (including) this newline
1014  i = _write_escaped_newlines(s, i);
1015  pos = i+1;
1016  if(pos < s.len)
1017  _rymlindent_nextline() // indent the next line
1018  }
1019  }
1020  // write missing characters at the end of the string
1021  if(pos < s.len)
1022  this->Writer::_do_write(s.sub(pos));
1023 }
1024 
1025 #undef _rymlindent_nextline
1026 
1027 template<class Writer>
1028 void Emitter<Writer>::_write_scalar_json_dquo(csubstr s)
1029 {
1030  size_t pos = 0;
1031  this->Writer::_do_write('"');
1032  for(size_t i = 0; i < s.len; ++i)
1033  {
1034  switch(s.str[i])
1035  {
1036  case '"':
1037  this->Writer ::_do_write(s.range(pos, i));
1038  this->Writer ::_do_write("\\\"");
1039  pos = i + 1;
1040  break;
1041  case '\n':
1042  this->Writer ::_do_write(s.range(pos, i));
1043  this->Writer ::_do_write("\\n");
1044  pos = i + 1;
1045  break;
1046  case '\t':
1047  this->Writer ::_do_write(s.range(pos, i));
1048  this->Writer ::_do_write("\\t");
1049  pos = i + 1;
1050  break;
1051  case '\\':
1052  this->Writer ::_do_write(s.range(pos, i));
1053  this->Writer ::_do_write("\\\\");
1054  pos = i + 1;
1055  break;
1056  case '\r':
1057  this->Writer ::_do_write(s.range(pos, i));
1058  this->Writer ::_do_write("\\r");
1059  pos = i + 1;
1060  break;
1061  case '\b':
1062  this->Writer ::_do_write(s.range(pos, i));
1063  this->Writer ::_do_write("\\b");
1064  pos = i + 1;
1065  break;
1066  case '\f':
1067  this->Writer ::_do_write(s.range(pos, i));
1068  this->Writer ::_do_write("\\f");
1069  pos = i + 1;
1070  break;
1071  }
1072  }
1073  if(pos < s.len)
1074  {
1075  csubstr sub = s.sub(pos);
1076  this->Writer::_do_write(sub);
1077  }
1078  this->Writer::_do_write('"');
1079 }
1080 
1081 } // namespace yml
1082 } // namespace c4
1083 
1084 #endif /* _C4_YML_EMIT_DEF_HPP_ */
A stateful emitter, for use with a writer such as WriterBuf, WriterFile, or WriterOStream.
Definition: emit.hpp:118
Callbacks const & callbacks() const
Definition: tree.hpp:241
bool empty() const
Definition: tree.hpp:235
id_type root_id()
Get the id of the root node.
Definition: tree.hpp:288
id_type capacity() const
Definition: tree.hpp:238
#define _rymlindent_nextline()
Definition: emit.def.hpp:145
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:17
EmitType_e
Specifies the type of content to emit.
Definition: emit.hpp:51
@ EMIT_YAML
emit YAML
Definition: emit.hpp:52
@ EMIT_JSON
emit JSON
Definition: emit.hpp:53
NodeType_e scalar_style_json_choose(csubstr s) noexcept
choose a json style based on the scalar's contents
Definition: node_type.cpp:191
NodeType_e scalar_style_choose(csubstr s) noexcept
choose a YAML emitting style based on the scalar's contents
Definition: node_type.cpp:171
uint32_t type_bits
the integral type necessary to cover all the bits for NodeType_e
Definition: node_type.hpp:29
@ NOTYPE
no node type or style is set
Definition: node_type.hpp:35
@ KEY_DQUO
mark key scalar as double quoted "
Definition: node_type.hpp:68
@ KEY
is member of a map
Definition: node_type.hpp:36
@ VAL_FOLDED
mark val scalar as multiline, block folded >
Definition: node_type.hpp:65
@ VAL_STYLE
mask of all the scalar styles for val (not container styles!)
Definition: node_type.hpp:92
@ KEYTAG
the key has a tag
Definition: node_type.hpp:46
@ VALTAG
the val has a tag
Definition: node_type.hpp:47
@ SCALAR_STYLE
Definition: node_type.hpp:93
@ SCALAR_DQUO
Definition: node_type.hpp:87
@ VAL_SQUO
mark val scalar as single quoted '
Definition: node_type.hpp:67
@ KEY_STYLE
mask of all the scalar styles for key (not container styles!)
Definition: node_type.hpp:91
@ VAL_PLAIN
mark val scalar as plain scalar (unquoted, even when multiline)
Definition: node_type.hpp:71
@ VALQUO
val style is one of ', ", > or |
Definition: node_type.hpp:90
@ VAL_DQUO
mark val scalar as double quoted "
Definition: node_type.hpp:69
@ KEY_SQUO
mark key scalar as single quoted '
Definition: node_type.hpp:66
@ VAL_LITERAL
mark val scalar as multiline, block literal |
Definition: node_type.hpp:63
@ KEY_LITERAL
mark key scalar as multiline, block literal |
Definition: node_type.hpp:62
@ KEY_PLAIN
mark key scalar as plain scalar (unquoted, even when multiline)
Definition: node_type.hpp:70
@ KEY_FOLDED
mark key scalar as multiline, block folded >
Definition: node_type.hpp:64
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:253
@ npos
a null string position
Definition: common.hpp:267
void error(Callbacks const &cb, const char *msg, size_t msg_len, Location loc)
Definition: common.cpp:130
bool _is_indented_block(csubstr s, size_t prev, size_t i) noexcept
Definition: emit.def.hpp:694
@ NONE
an index to none
Definition: common.hpp:260
Definition: common.cpp:12
wraps a NodeType_e element with some syntactic sugar and predicates
Definition: node_type.hpp:120
bool is_flow_ml() const noexcept
Definition: node_type.hpp:205
bool is_flow_sl() const noexcept
Definition: node_type.hpp:204
NodeType_e type
Definition: node_type.hpp:123