Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/rapidyaml/include/c4/yml/emit.def.hpp
4264 views
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
namespace c4 {
9
namespace yml {
10
11
template<class Writer>
12
substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& t, size_t id, bool error_on_excess)
13
{
14
if(t.empty())
15
{
16
_RYML_CB_ASSERT(t.callbacks(), id == NONE);
17
return {};
18
}
19
_RYML_CB_CHECK(t.callbacks(), id < t.capacity());
20
m_tree = &t;
21
if(type == EMIT_YAML)
22
_emit_yaml(id);
23
else if(type == EMIT_JSON)
24
_do_visit_json(id);
25
else
26
_RYML_CB_ERR(m_tree->callbacks(), "unknown emit type");
27
return this->Writer::_get(error_on_excess);
28
}
29
30
template<class Writer>
31
substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& t, bool error_on_excess)
32
{
33
if(t.empty())
34
return {};
35
return this->emit_as(type, t, t.root_id(), error_on_excess);
36
}
37
38
template<class Writer>
39
substr Emitter<Writer>::emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess)
40
{
41
_RYML_CB_CHECK(n.tree()->callbacks(), n.valid());
42
return this->emit_as(type, *n.tree(), n.id(), error_on_excess);
43
}
44
45
46
//-----------------------------------------------------------------------------
47
48
template<class Writer>
49
void Emitter<Writer>::_emit_yaml(size_t id)
50
{
51
// save branches in the visitor by doing the initial stream/doc
52
// logic here, sparing the need to check stream/val/keyval inside
53
// the visitor functions
54
auto dispatch = [this](size_t node){
55
NodeType ty = m_tree->type(node);
56
if(ty.marked_flow_sl())
57
_do_visit_flow_sl(node, 0);
58
else if(ty.marked_flow_ml())
59
_do_visit_flow_ml(node, 0);
60
else
61
{
62
_do_visit_block(node, 0);
63
}
64
};
65
if(!m_tree->is_root(id))
66
{
67
if(m_tree->is_container(id) && !m_tree->type(id).marked_flow())
68
{
69
size_t ilevel = 0;
70
if(m_tree->has_key(id))
71
{
72
this->Writer::_do_write(m_tree->key(id));
73
this->Writer::_do_write(":\n");
74
++ilevel;
75
}
76
_do_visit_block_container(id, ilevel, ilevel);
77
return;
78
}
79
}
80
81
auto *btd = m_tree->tag_directives().b;
82
auto *etd = m_tree->tag_directives().e;
83
auto write_tag_directives = [&btd, etd, this](size_t next_node){
84
auto end = btd;
85
while(end < etd)
86
{
87
if(end->next_node_id > next_node)
88
break;
89
++end;
90
}
91
for( ; btd != end; ++btd)
92
{
93
if(next_node != m_tree->first_child(m_tree->parent(next_node)))
94
this->Writer::_do_write("...\n");
95
this->Writer::_do_write("%TAG ");
96
this->Writer::_do_write(btd->handle);
97
this->Writer::_do_write(' ');
98
this->Writer::_do_write(btd->prefix);
99
this->Writer::_do_write('\n');
100
}
101
};
102
if(m_tree->is_stream(id))
103
{
104
if(m_tree->first_child(id) != NONE)
105
write_tag_directives(m_tree->first_child(id));
106
for(size_t child = m_tree->first_child(id); child != NONE; child = m_tree->next_sibling(child))
107
{
108
dispatch(child);
109
if(m_tree->next_sibling(child) != NONE)
110
write_tag_directives(m_tree->next_sibling(child));
111
}
112
}
113
else if(m_tree->is_container(id))
114
{
115
dispatch(id);
116
}
117
else if(m_tree->is_doc(id))
118
{
119
_RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->is_container(id)); // checked above
120
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_val(id)); // so it must be a val
121
_write_doc(id);
122
}
123
else if(m_tree->is_keyval(id))
124
{
125
_writek(id, 0);
126
this->Writer::_do_write(": ");
127
_writev(id, 0);
128
if(!m_tree->type(id).marked_flow())
129
this->Writer::_do_write('\n');
130
}
131
else if(m_tree->is_val(id))
132
{
133
//this->Writer::_do_write("- ");
134
_writev(id, 0);
135
if(!m_tree->type(id).marked_flow())
136
this->Writer::_do_write('\n');
137
}
138
else if(m_tree->type(id) == NOTYPE)
139
{
140
;
141
}
142
else
143
{
144
_RYML_CB_ERR(m_tree->callbacks(), "unknown type");
145
}
146
}
147
148
template<class Writer>
149
void Emitter<Writer>::_write_doc(size_t id)
150
{
151
RYML_ASSERT(m_tree->is_doc(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
if(!m_tree->has_val(id)) // this is more frequent
158
{
159
if(m_tree->has_val_tag(id))
160
{
161
if(!m_tree->is_root(id))
162
this->Writer::_do_write(' ');
163
_write_tag(m_tree->val_tag(id));
164
}
165
if(m_tree->has_val_anchor(id))
166
{
167
if(!m_tree->is_root(id))
168
this->Writer::_do_write(' ');
169
this->Writer::_do_write('&');
170
this->Writer::_do_write(m_tree->val_anchor(id));
171
}
172
}
173
else // docval
174
{
175
RYML_ASSERT(m_tree->has_val(id));
176
RYML_ASSERT(!m_tree->has_key(id));
177
if(!m_tree->is_root(id))
178
this->Writer::_do_write(' ');
179
_writev(id, 0);
180
}
181
this->Writer::_do_write('\n');
182
}
183
184
template<class Writer>
185
void Emitter<Writer>::_do_visit_flow_sl(size_t node, size_t ilevel)
186
{
187
RYML_ASSERT(!m_tree->is_stream(node));
188
RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));
189
RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
190
191
if(m_tree->is_doc(node))
192
{
193
_write_doc(node);
194
if(!m_tree->has_children(node))
195
return;
196
}
197
else if(m_tree->is_container(node))
198
{
199
RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));
200
201
bool spc = false; // write a space
202
203
if(m_tree->has_key(node))
204
{
205
_writek(node, ilevel);
206
this->Writer::_do_write(':');
207
spc = true;
208
}
209
210
if(m_tree->has_val_tag(node))
211
{
212
if(spc)
213
this->Writer::_do_write(' ');
214
_write_tag(m_tree->val_tag(node));
215
spc = true;
216
}
217
218
if(m_tree->has_val_anchor(node))
219
{
220
if(spc)
221
this->Writer::_do_write(' ');
222
this->Writer::_do_write('&');
223
this->Writer::_do_write(m_tree->val_anchor(node));
224
spc = true;
225
}
226
227
if(spc)
228
this->Writer::_do_write(' ');
229
230
if(m_tree->is_map(node))
231
{
232
this->Writer::_do_write('{');
233
}
234
else
235
{
236
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_seq(node));
237
this->Writer::_do_write('[');
238
}
239
} // container
240
241
for(size_t child = m_tree->first_child(node), count = 0; child != NONE; child = m_tree->next_sibling(child))
242
{
243
if(count++)
244
this->Writer::_do_write(',');
245
if(m_tree->is_keyval(child))
246
{
247
_writek(child, ilevel);
248
this->Writer::_do_write(": ");
249
_writev(child, ilevel);
250
}
251
else if(m_tree->is_val(child))
252
{
253
_writev(child, ilevel);
254
}
255
else
256
{
257
// with single-line flow, we can never go back to block
258
_do_visit_flow_sl(child, ilevel + 1);
259
}
260
}
261
262
if(m_tree->is_map(node))
263
{
264
this->Writer::_do_write('}');
265
}
266
else if(m_tree->is_seq(node))
267
{
268
this->Writer::_do_write(']');
269
}
270
}
271
272
template<class Writer>
273
void Emitter<Writer>::_do_visit_flow_ml(size_t id, size_t ilevel, size_t do_indent)
274
{
275
C4_UNUSED(id);
276
C4_UNUSED(ilevel);
277
C4_UNUSED(do_indent);
278
RYML_CHECK(false/*not implemented*/);
279
}
280
281
template<class Writer>
282
void Emitter<Writer>::_do_visit_block_container(size_t node, size_t next_level, size_t do_indent)
283
{
284
RepC ind = indent_to(do_indent * next_level);
285
286
if(m_tree->is_seq(node))
287
{
288
for(size_t child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))
289
{
290
_RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->has_key(child));
291
if(m_tree->is_val(child))
292
{
293
this->Writer::_do_write(ind);
294
this->Writer::_do_write("- ");
295
_writev(child, next_level);
296
this->Writer::_do_write('\n');
297
}
298
else
299
{
300
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(child));
301
NodeType ty = m_tree->type(child);
302
if(ty.marked_flow_sl())
303
{
304
this->Writer::_do_write(ind);
305
this->Writer::_do_write("- ");
306
_do_visit_flow_sl(child, 0u);
307
this->Writer::_do_write('\n');
308
}
309
else if(ty.marked_flow_ml())
310
{
311
this->Writer::_do_write(ind);
312
this->Writer::_do_write("- ");
313
_do_visit_flow_ml(child, next_level, do_indent);
314
this->Writer::_do_write('\n');
315
}
316
else
317
{
318
_do_visit_block(child, next_level, do_indent);
319
}
320
}
321
do_indent = true;
322
ind = indent_to(do_indent * next_level);
323
}
324
}
325
else // map
326
{
327
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_map(node));
328
for(size_t ich = m_tree->first_child(node); ich != NONE; ich = m_tree->next_sibling(ich))
329
{
330
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->has_key(ich));
331
if(m_tree->is_keyval(ich))
332
{
333
this->Writer::_do_write(ind);
334
_writek(ich, next_level);
335
this->Writer::_do_write(": ");
336
_writev(ich, next_level);
337
this->Writer::_do_write('\n');
338
}
339
else
340
{
341
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(ich));
342
NodeType ty = m_tree->type(ich);
343
if(ty.marked_flow_sl())
344
{
345
this->Writer::_do_write(ind);
346
_do_visit_flow_sl(ich, 0u);
347
this->Writer::_do_write('\n');
348
}
349
else if(ty.marked_flow_ml())
350
{
351
this->Writer::_do_write(ind);
352
_do_visit_flow_ml(ich, 0u);
353
this->Writer::_do_write('\n');
354
}
355
else
356
{
357
_do_visit_block(ich, next_level, do_indent);
358
}
359
}
360
do_indent = true;
361
ind = indent_to(do_indent * next_level);
362
}
363
}
364
}
365
366
template<class Writer>
367
void Emitter<Writer>::_do_visit_block(size_t node, size_t ilevel, size_t do_indent)
368
{
369
RYML_ASSERT(!m_tree->is_stream(node));
370
RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));
371
RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
372
RepC ind = indent_to(do_indent * ilevel);
373
374
if(m_tree->is_doc(node))
375
{
376
_write_doc(node);
377
if(!m_tree->has_children(node))
378
return;
379
}
380
else if(m_tree->is_container(node))
381
{
382
RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));
383
384
bool spc = false; // write a space
385
bool nl = false; // write a newline
386
387
if(m_tree->has_key(node))
388
{
389
this->Writer::_do_write(ind);
390
_writek(node, ilevel);
391
this->Writer::_do_write(':');
392
spc = true;
393
}
394
else if(!m_tree->is_root(node))
395
{
396
this->Writer::_do_write(ind);
397
this->Writer::_do_write('-');
398
spc = true;
399
}
400
401
if(m_tree->has_val_tag(node))
402
{
403
if(spc)
404
this->Writer::_do_write(' ');
405
_write_tag(m_tree->val_tag(node));
406
spc = true;
407
nl = true;
408
}
409
410
if(m_tree->has_val_anchor(node))
411
{
412
if(spc)
413
this->Writer::_do_write(' ');
414
this->Writer::_do_write('&');
415
this->Writer::_do_write(m_tree->val_anchor(node));
416
spc = true;
417
nl = true;
418
}
419
420
if(m_tree->has_children(node))
421
{
422
if(m_tree->has_key(node))
423
nl = true;
424
else
425
if(!m_tree->is_root(node) && !nl)
426
spc = true;
427
}
428
else
429
{
430
if(m_tree->is_seq(node))
431
this->Writer::_do_write(" []\n");
432
else if(m_tree->is_map(node))
433
this->Writer::_do_write(" {}\n");
434
return;
435
}
436
437
if(spc && !nl)
438
this->Writer::_do_write(' ');
439
440
do_indent = 0;
441
if(nl)
442
{
443
this->Writer::_do_write('\n');
444
do_indent = 1;
445
}
446
} // container
447
448
size_t next_level = ilevel + 1;
449
if(m_tree->is_root(node) || m_tree->is_doc(node))
450
next_level = ilevel; // do not indent at top level
451
452
_do_visit_block_container(node, next_level, do_indent);
453
}
454
455
template<class Writer>
456
void Emitter<Writer>::_do_visit_json(size_t id)
457
{
458
_RYML_CB_CHECK(m_tree->callbacks(), !m_tree->is_stream(id)); // JSON does not have streams
459
if(m_tree->is_keyval(id))
460
{
461
_writek_json(id);
462
this->Writer::_do_write(": ");
463
_writev_json(id);
464
}
465
else if(m_tree->is_val(id))
466
{
467
_writev_json(id);
468
}
469
else if(m_tree->is_container(id))
470
{
471
if(m_tree->has_key(id))
472
{
473
_writek_json(id);
474
this->Writer::_do_write(": ");
475
}
476
if(m_tree->is_seq(id))
477
this->Writer::_do_write('[');
478
else if(m_tree->is_map(id))
479
this->Writer::_do_write('{');
480
} // container
481
482
for(size_t ich = m_tree->first_child(id); ich != NONE; ich = m_tree->next_sibling(ich))
483
{
484
if(ich != m_tree->first_child(id))
485
this->Writer::_do_write(',');
486
_do_visit_json(ich);
487
}
488
489
if(m_tree->is_seq(id))
490
this->Writer::_do_write(']');
491
else if(m_tree->is_map(id))
492
this->Writer::_do_write('}');
493
}
494
495
template<class Writer>
496
void Emitter<Writer>::_write(NodeScalar const& C4_RESTRICT sc, NodeType flags, size_t ilevel)
497
{
498
if( ! sc.tag.empty())
499
{
500
_write_tag(sc.tag);
501
this->Writer::_do_write(' ');
502
}
503
if(flags.has_anchor())
504
{
505
RYML_ASSERT(flags.is_ref() != flags.has_anchor());
506
RYML_ASSERT( ! sc.anchor.empty());
507
this->Writer::_do_write('&');
508
this->Writer::_do_write(sc.anchor);
509
this->Writer::_do_write(' ');
510
}
511
else if(flags.is_ref())
512
{
513
if(sc.anchor != "<<")
514
this->Writer::_do_write('*');
515
this->Writer::_do_write(sc.anchor);
516
return;
517
}
518
519
// ensure the style flags only have one of KEY or VAL
520
_RYML_CB_ASSERT(m_tree->callbacks(), ((flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE)) == 0) || (((flags&_WIP_KEY_STYLE) == 0) != ((flags&_WIP_VAL_STYLE) == 0)));
521
522
auto style_marks = flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE);
523
if(style_marks & (_WIP_KEY_LITERAL|_WIP_VAL_LITERAL))
524
{
525
_write_scalar_literal(sc.scalar, ilevel, flags.has_key());
526
}
527
else if(style_marks & (_WIP_KEY_FOLDED|_WIP_VAL_FOLDED))
528
{
529
_write_scalar_folded(sc.scalar, ilevel, flags.has_key());
530
}
531
else if(style_marks & (_WIP_KEY_SQUO|_WIP_VAL_SQUO))
532
{
533
_write_scalar_squo(sc.scalar, ilevel);
534
}
535
else if(style_marks & (_WIP_KEY_DQUO|_WIP_VAL_DQUO))
536
{
537
_write_scalar_dquo(sc.scalar, ilevel);
538
}
539
else if(style_marks & (_WIP_KEY_PLAIN|_WIP_VAL_PLAIN))
540
{
541
_write_scalar_plain(sc.scalar, ilevel);
542
}
543
else if(!style_marks)
544
{
545
size_t first_non_nl = sc.scalar.first_not_of('\n');
546
bool all_newlines = first_non_nl == npos;
547
bool has_leading_ws = (!all_newlines) && sc.scalar.sub(first_non_nl).begins_with_any(" \t");
548
bool do_literal = ((!sc.scalar.empty() && all_newlines) || (has_leading_ws && !sc.scalar.trim(' ').empty()));
549
if(do_literal)
550
{
551
_write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);
552
}
553
else
554
{
555
for(size_t i = 0; i < sc.scalar.len; ++i)
556
{
557
if(sc.scalar.str[i] == '\n')
558
{
559
_write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);
560
goto wrote_special;
561
}
562
// todo: check for escaped characters requiring double quotes
563
}
564
_write_scalar(sc.scalar, flags.is_quoted());
565
wrote_special:
566
;
567
}
568
}
569
else
570
{
571
_RYML_CB_ERR(m_tree->callbacks(), "not implemented");
572
}
573
}
574
template<class Writer>
575
void Emitter<Writer>::_write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags)
576
{
577
if(C4_UNLIKELY( ! sc.tag.empty()))
578
_RYML_CB_ERR(m_tree->callbacks(), "JSON does not have tags");
579
if(C4_UNLIKELY(flags.has_anchor()))
580
_RYML_CB_ERR(m_tree->callbacks(), "JSON does not have anchors");
581
_write_scalar_json(sc.scalar, flags.has_key(), flags.is_quoted());
582
}
583
584
#define _rymlindent_nextline() for(size_t lv = 0; lv < ilevel+1; ++lv) { this->Writer::_do_write(' '); this->Writer::_do_write(' '); }
585
586
template<class Writer>
587
void Emitter<Writer>::_write_scalar_literal(csubstr s, size_t ilevel, bool explicit_key, bool explicit_indentation)
588
{
589
if(explicit_key)
590
this->Writer::_do_write("? ");
591
csubstr trimmed = s.trimr("\n\r");
592
size_t numnewlines_at_end = s.len - trimmed.len - s.sub(trimmed.len).count('\r');
593
//
594
if(!explicit_indentation)
595
this->Writer::_do_write('|');
596
else
597
this->Writer::_do_write("|2");
598
//
599
if(numnewlines_at_end > 1 || (trimmed.len == 0 && s.len > 0)/*only newlines*/)
600
this->Writer::_do_write("+\n");
601
else if(numnewlines_at_end == 1)
602
this->Writer::_do_write('\n');
603
else
604
this->Writer::_do_write("-\n");
605
//
606
if(trimmed.len)
607
{
608
size_t pos = 0; // tracks the last character that was already written
609
for(size_t i = 0; i < trimmed.len; ++i)
610
{
611
if(trimmed[i] != '\n')
612
continue;
613
// write everything up to this point
614
csubstr since_pos = trimmed.range(pos, i+1); // include the newline
615
_rymlindent_nextline()
616
this->Writer::_do_write(since_pos);
617
pos = i+1; // already written
618
}
619
if(pos < trimmed.len)
620
{
621
_rymlindent_nextline()
622
this->Writer::_do_write(trimmed.sub(pos));
623
}
624
if(numnewlines_at_end)
625
{
626
this->Writer::_do_write('\n');
627
--numnewlines_at_end;
628
}
629
}
630
for(size_t i = 0; i < numnewlines_at_end; ++i)
631
{
632
_rymlindent_nextline()
633
if(i+1 < numnewlines_at_end || explicit_key)
634
this->Writer::_do_write('\n');
635
}
636
if(explicit_key && !numnewlines_at_end)
637
this->Writer::_do_write('\n');
638
}
639
640
template<class Writer>
641
void Emitter<Writer>::_write_scalar_folded(csubstr s, size_t ilevel, bool explicit_key)
642
{
643
if(explicit_key)
644
{
645
this->Writer::_do_write("? ");
646
}
647
RYML_ASSERT(s.find("\r") == csubstr::npos);
648
csubstr trimmed = s.trimr('\n');
649
size_t numnewlines_at_end = s.len - trimmed.len;
650
if(numnewlines_at_end == 0)
651
{
652
this->Writer::_do_write(">-\n");
653
}
654
else if(numnewlines_at_end == 1)
655
{
656
this->Writer::_do_write(">\n");
657
}
658
else if(numnewlines_at_end > 1)
659
{
660
this->Writer::_do_write(">+\n");
661
}
662
if(trimmed.len)
663
{
664
size_t pos = 0; // tracks the last character that was already written
665
for(size_t i = 0; i < trimmed.len; ++i)
666
{
667
if(trimmed[i] != '\n')
668
continue;
669
// write everything up to this point
670
csubstr since_pos = trimmed.range(pos, i+1); // include the newline
671
pos = i+1; // because of the newline
672
_rymlindent_nextline()
673
this->Writer::_do_write(since_pos);
674
this->Writer::_do_write('\n'); // write the newline twice
675
}
676
if(pos < trimmed.len)
677
{
678
_rymlindent_nextline()
679
this->Writer::_do_write(trimmed.sub(pos));
680
}
681
if(numnewlines_at_end)
682
{
683
this->Writer::_do_write('\n');
684
--numnewlines_at_end;
685
}
686
}
687
for(size_t i = 0; i < numnewlines_at_end; ++i)
688
{
689
_rymlindent_nextline()
690
if(i+1 < numnewlines_at_end || explicit_key)
691
this->Writer::_do_write('\n');
692
}
693
if(explicit_key && !numnewlines_at_end)
694
this->Writer::_do_write('\n');
695
}
696
697
template<class Writer>
698
void Emitter<Writer>::_write_scalar_squo(csubstr s, size_t ilevel)
699
{
700
size_t pos = 0; // tracks the last character that was already written
701
this->Writer::_do_write('\'');
702
for(size_t i = 0; i < s.len; ++i)
703
{
704
if(s[i] == '\n')
705
{
706
csubstr sub = s.range(pos, i+1);
707
this->Writer::_do_write(sub); // write everything up to (including) this char
708
this->Writer::_do_write('\n'); // write the character again
709
if(i + 1 < s.len)
710
_rymlindent_nextline() // indent the next line
711
pos = i+1;
712
}
713
else if(s[i] == '\'')
714
{
715
csubstr sub = s.range(pos, i+1);
716
this->Writer::_do_write(sub); // write everything up to (including) this char
717
this->Writer::_do_write('\''); // write the character again
718
pos = i+1;
719
}
720
}
721
// write missing characters at the end of the string
722
if(pos < s.len)
723
this->Writer::_do_write(s.sub(pos));
724
this->Writer::_do_write('\'');
725
}
726
727
template<class Writer>
728
void Emitter<Writer>::_write_scalar_dquo(csubstr s, size_t ilevel)
729
{
730
size_t pos = 0; // tracks the last character that was already written
731
this->Writer::_do_write('"');
732
for(size_t i = 0; i < s.len; ++i)
733
{
734
const char curr = s.str[i];
735
if(curr == '"' || curr == '\\')
736
{
737
csubstr sub = s.range(pos, i);
738
this->Writer::_do_write(sub); // write everything up to (excluding) this char
739
this->Writer::_do_write('\\'); // write the escape
740
this->Writer::_do_write(curr); // write the char
741
pos = i+1;
742
}
743
else if(s[i] == '\n')
744
{
745
csubstr sub = s.range(pos, i+1);
746
this->Writer::_do_write(sub); // write everything up to (including) this newline
747
this->Writer::_do_write('\n'); // write the newline again
748
if(i + 1 < s.len)
749
_rymlindent_nextline() // indent the next line
750
pos = i+1;
751
if(i+1 < s.len) // escape leading whitespace after the newline
752
{
753
const char next = s.str[i+1];
754
if(next == ' ' || next == '\t')
755
this->Writer::_do_write('\\');
756
}
757
}
758
else if(curr == ' ' || curr == '\t')
759
{
760
// escape trailing whitespace before a newline
761
size_t next = s.first_not_of(" \t\r", i);
762
if(next != npos && s[next] == '\n')
763
{
764
csubstr sub = s.range(pos, i);
765
this->Writer::_do_write(sub); // write everything up to (excluding) this char
766
this->Writer::_do_write('\\'); // escape the whitespace
767
pos = i;
768
}
769
}
770
else if(C4_UNLIKELY(curr == '\r'))
771
{
772
csubstr sub = s.range(pos, i);
773
this->Writer::_do_write(sub); // write everything up to (excluding) this char
774
this->Writer::_do_write("\\r"); // write the escaped char
775
pos = i+1;
776
}
777
}
778
// write missing characters at the end of the string
779
if(pos < s.len)
780
{
781
csubstr sub = s.sub(pos);
782
this->Writer::_do_write(sub);
783
}
784
this->Writer::_do_write('"');
785
}
786
787
template<class Writer>
788
void Emitter<Writer>::_write_scalar_plain(csubstr s, size_t ilevel)
789
{
790
size_t pos = 0; // tracks the last character that was already written
791
for(size_t i = 0; i < s.len; ++i)
792
{
793
const char curr = s.str[i];
794
if(curr == '\n')
795
{
796
csubstr sub = s.range(pos, i+1);
797
this->Writer::_do_write(sub); // write everything up to (including) this newline
798
this->Writer::_do_write('\n'); // write the newline again
799
if(i + 1 < s.len)
800
_rymlindent_nextline() // indent the next line
801
pos = i+1;
802
}
803
}
804
// write missing characters at the end of the string
805
if(pos < s.len)
806
{
807
csubstr sub = s.sub(pos);
808
this->Writer::_do_write(sub);
809
}
810
}
811
812
#undef _rymlindent_nextline
813
814
template<class Writer>
815
void Emitter<Writer>::_write_scalar(csubstr s, bool was_quoted)
816
{
817
// this block of code needed to be moved to before the needs_quotes
818
// assignment to work around a g++ optimizer bug where (s.str != nullptr)
819
// was evaluated as true even if s.str was actually a nullptr (!!!)
820
if(s.len == size_t(0))
821
{
822
if(was_quoted || s.str != nullptr)
823
this->Writer::_do_write("''");
824
return;
825
}
826
827
const bool needs_quotes = (
828
was_quoted
829
||
830
(
831
( ! s.is_number())
832
&&
833
(
834
// has leading whitespace
835
// looks like reference or anchor
836
// would be treated as a directive
837
// see https://www.yaml.info/learn/quote.html#noplain
838
s.begins_with_any(" \n\t\r*&%@`")
839
||
840
s.begins_with("<<")
841
||
842
// has trailing whitespace
843
s.ends_with_any(" \n\t\r")
844
||
845
// has special chars
846
(s.first_of("#:-?,\n{}[]'\"") != npos)
847
)
848
)
849
);
850
851
if( ! needs_quotes)
852
{
853
this->Writer::_do_write(s);
854
}
855
else
856
{
857
const bool has_dquotes = s.first_of( '"') != npos;
858
const bool has_squotes = s.first_of('\'') != npos;
859
if(!has_squotes && has_dquotes)
860
{
861
this->Writer::_do_write('\'');
862
this->Writer::_do_write(s);
863
this->Writer::_do_write('\'');
864
}
865
else if(has_squotes && !has_dquotes)
866
{
867
RYML_ASSERT(s.count('\n') == 0);
868
this->Writer::_do_write('"');
869
this->Writer::_do_write(s);
870
this->Writer::_do_write('"');
871
}
872
else
873
{
874
_write_scalar_squo(s, /*FIXME FIXME FIXME*/0);
875
}
876
}
877
}
878
template<class Writer>
879
void Emitter<Writer>::_write_scalar_json(csubstr s, bool as_key, bool use_quotes)
880
{
881
if((!use_quotes)
882
// json keys require quotes
883
&& (!as_key)
884
&& (
885
// do not quote special cases
886
(s == "true" || s == "false" || s == "null")
887
|| (
888
// do not quote numbers
889
(s.is_number()
890
&& (
891
// quote integral numbers if they have a leading 0
892
// https://github.com/biojppm/rapidyaml/issues/291
893
(!(s.len > 1 && s.begins_with('0')))
894
// do not quote reals with leading 0
895
// https://github.com/biojppm/rapidyaml/issues/313
896
|| (s.find('.') != csubstr::npos) ))
897
)
898
)
899
)
900
{
901
this->Writer::_do_write(s);
902
}
903
else
904
{
905
size_t pos = 0;
906
this->Writer::_do_write('"');
907
for(size_t i = 0; i < s.len; ++i)
908
{
909
switch(s.str[i])
910
{
911
case '"':
912
this->Writer ::_do_write(s.range(pos, i));
913
this->Writer ::_do_write("\\\"");
914
pos = i + 1;
915
break;
916
case '\n':
917
this->Writer ::_do_write(s.range(pos, i));
918
this->Writer ::_do_write("\\n");
919
pos = i + 1;
920
break;
921
case '\t':
922
this->Writer ::_do_write(s.range(pos, i));
923
this->Writer ::_do_write("\\t");
924
pos = i + 1;
925
break;
926
case '\\':
927
this->Writer ::_do_write(s.range(pos, i));
928
this->Writer ::_do_write("\\\\");
929
pos = i + 1;
930
break;
931
case '\r':
932
this->Writer ::_do_write(s.range(pos, i));
933
this->Writer ::_do_write("\\r");
934
pos = i + 1;
935
break;
936
case '\b':
937
this->Writer ::_do_write(s.range(pos, i));
938
this->Writer ::_do_write("\\b");
939
pos = i + 1;
940
break;
941
case '\f':
942
this->Writer ::_do_write(s.range(pos, i));
943
this->Writer ::_do_write("\\f");
944
pos = i + 1;
945
break;
946
}
947
}
948
if(pos < s.len)
949
{
950
csubstr sub = s.sub(pos);
951
this->Writer::_do_write(sub);
952
}
953
this->Writer::_do_write('"');
954
}
955
}
956
957
} // namespace yml
958
} // namespace c4
959
960
#endif /* _C4_YML_EMIT_DEF_HPP_ */
961
962