emit.def.hpp (29539B)
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_ */