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