Path: blob/master/dep/rapidyaml/include/c4/yml/emit.def.hpp
4264 views
#ifndef _C4_YML_EMIT_DEF_HPP_1#define _C4_YML_EMIT_DEF_HPP_23#ifndef _C4_YML_EMIT_HPP_4#include "c4/yml/emit.hpp"5#endif67namespace c4 {8namespace yml {910template<class Writer>11substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& t, size_t id, bool error_on_excess)12{13if(t.empty())14{15_RYML_CB_ASSERT(t.callbacks(), id == NONE);16return {};17}18_RYML_CB_CHECK(t.callbacks(), id < t.capacity());19m_tree = &t;20if(type == EMIT_YAML)21_emit_yaml(id);22else if(type == EMIT_JSON)23_do_visit_json(id);24else25_RYML_CB_ERR(m_tree->callbacks(), "unknown emit type");26return this->Writer::_get(error_on_excess);27}2829template<class Writer>30substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& t, bool error_on_excess)31{32if(t.empty())33return {};34return this->emit_as(type, t, t.root_id(), error_on_excess);35}3637template<class Writer>38substr Emitter<Writer>::emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess)39{40_RYML_CB_CHECK(n.tree()->callbacks(), n.valid());41return this->emit_as(type, *n.tree(), n.id(), error_on_excess);42}434445//-----------------------------------------------------------------------------4647template<class Writer>48void Emitter<Writer>::_emit_yaml(size_t id)49{50// save branches in the visitor by doing the initial stream/doc51// logic here, sparing the need to check stream/val/keyval inside52// the visitor functions53auto dispatch = [this](size_t node){54NodeType ty = m_tree->type(node);55if(ty.marked_flow_sl())56_do_visit_flow_sl(node, 0);57else if(ty.marked_flow_ml())58_do_visit_flow_ml(node, 0);59else60{61_do_visit_block(node, 0);62}63};64if(!m_tree->is_root(id))65{66if(m_tree->is_container(id) && !m_tree->type(id).marked_flow())67{68size_t ilevel = 0;69if(m_tree->has_key(id))70{71this->Writer::_do_write(m_tree->key(id));72this->Writer::_do_write(":\n");73++ilevel;74}75_do_visit_block_container(id, ilevel, ilevel);76return;77}78}7980auto *btd = m_tree->tag_directives().b;81auto *etd = m_tree->tag_directives().e;82auto write_tag_directives = [&btd, etd, this](size_t next_node){83auto end = btd;84while(end < etd)85{86if(end->next_node_id > next_node)87break;88++end;89}90for( ; btd != end; ++btd)91{92if(next_node != m_tree->first_child(m_tree->parent(next_node)))93this->Writer::_do_write("...\n");94this->Writer::_do_write("%TAG ");95this->Writer::_do_write(btd->handle);96this->Writer::_do_write(' ');97this->Writer::_do_write(btd->prefix);98this->Writer::_do_write('\n');99}100};101if(m_tree->is_stream(id))102{103if(m_tree->first_child(id) != NONE)104write_tag_directives(m_tree->first_child(id));105for(size_t child = m_tree->first_child(id); child != NONE; child = m_tree->next_sibling(child))106{107dispatch(child);108if(m_tree->next_sibling(child) != NONE)109write_tag_directives(m_tree->next_sibling(child));110}111}112else if(m_tree->is_container(id))113{114dispatch(id);115}116else if(m_tree->is_doc(id))117{118_RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->is_container(id)); // checked above119_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_val(id)); // so it must be a val120_write_doc(id);121}122else if(m_tree->is_keyval(id))123{124_writek(id, 0);125this->Writer::_do_write(": ");126_writev(id, 0);127if(!m_tree->type(id).marked_flow())128this->Writer::_do_write('\n');129}130else if(m_tree->is_val(id))131{132//this->Writer::_do_write("- ");133_writev(id, 0);134if(!m_tree->type(id).marked_flow())135this->Writer::_do_write('\n');136}137else if(m_tree->type(id) == NOTYPE)138{139;140}141else142{143_RYML_CB_ERR(m_tree->callbacks(), "unknown type");144}145}146147template<class Writer>148void Emitter<Writer>::_write_doc(size_t id)149{150RYML_ASSERT(m_tree->is_doc(id));151if(!m_tree->is_root(id))152{153RYML_ASSERT(m_tree->is_stream(m_tree->parent(id)));154this->Writer::_do_write("---");155}156if(!m_tree->has_val(id)) // this is more frequent157{158if(m_tree->has_val_tag(id))159{160if(!m_tree->is_root(id))161this->Writer::_do_write(' ');162_write_tag(m_tree->val_tag(id));163}164if(m_tree->has_val_anchor(id))165{166if(!m_tree->is_root(id))167this->Writer::_do_write(' ');168this->Writer::_do_write('&');169this->Writer::_do_write(m_tree->val_anchor(id));170}171}172else // docval173{174RYML_ASSERT(m_tree->has_val(id));175RYML_ASSERT(!m_tree->has_key(id));176if(!m_tree->is_root(id))177this->Writer::_do_write(' ');178_writev(id, 0);179}180this->Writer::_do_write('\n');181}182183template<class Writer>184void Emitter<Writer>::_do_visit_flow_sl(size_t node, size_t ilevel)185{186RYML_ASSERT(!m_tree->is_stream(node));187RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));188RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));189190if(m_tree->is_doc(node))191{192_write_doc(node);193if(!m_tree->has_children(node))194return;195}196else if(m_tree->is_container(node))197{198RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));199200bool spc = false; // write a space201202if(m_tree->has_key(node))203{204_writek(node, ilevel);205this->Writer::_do_write(':');206spc = true;207}208209if(m_tree->has_val_tag(node))210{211if(spc)212this->Writer::_do_write(' ');213_write_tag(m_tree->val_tag(node));214spc = true;215}216217if(m_tree->has_val_anchor(node))218{219if(spc)220this->Writer::_do_write(' ');221this->Writer::_do_write('&');222this->Writer::_do_write(m_tree->val_anchor(node));223spc = true;224}225226if(spc)227this->Writer::_do_write(' ');228229if(m_tree->is_map(node))230{231this->Writer::_do_write('{');232}233else234{235_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_seq(node));236this->Writer::_do_write('[');237}238} // container239240for(size_t child = m_tree->first_child(node), count = 0; child != NONE; child = m_tree->next_sibling(child))241{242if(count++)243this->Writer::_do_write(',');244if(m_tree->is_keyval(child))245{246_writek(child, ilevel);247this->Writer::_do_write(": ");248_writev(child, ilevel);249}250else if(m_tree->is_val(child))251{252_writev(child, ilevel);253}254else255{256// with single-line flow, we can never go back to block257_do_visit_flow_sl(child, ilevel + 1);258}259}260261if(m_tree->is_map(node))262{263this->Writer::_do_write('}');264}265else if(m_tree->is_seq(node))266{267this->Writer::_do_write(']');268}269}270271template<class Writer>272void Emitter<Writer>::_do_visit_flow_ml(size_t id, size_t ilevel, size_t do_indent)273{274C4_UNUSED(id);275C4_UNUSED(ilevel);276C4_UNUSED(do_indent);277RYML_CHECK(false/*not implemented*/);278}279280template<class Writer>281void Emitter<Writer>::_do_visit_block_container(size_t node, size_t next_level, size_t do_indent)282{283RepC ind = indent_to(do_indent * next_level);284285if(m_tree->is_seq(node))286{287for(size_t child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))288{289_RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->has_key(child));290if(m_tree->is_val(child))291{292this->Writer::_do_write(ind);293this->Writer::_do_write("- ");294_writev(child, next_level);295this->Writer::_do_write('\n');296}297else298{299_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(child));300NodeType ty = m_tree->type(child);301if(ty.marked_flow_sl())302{303this->Writer::_do_write(ind);304this->Writer::_do_write("- ");305_do_visit_flow_sl(child, 0u);306this->Writer::_do_write('\n');307}308else if(ty.marked_flow_ml())309{310this->Writer::_do_write(ind);311this->Writer::_do_write("- ");312_do_visit_flow_ml(child, next_level, do_indent);313this->Writer::_do_write('\n');314}315else316{317_do_visit_block(child, next_level, do_indent);318}319}320do_indent = true;321ind = indent_to(do_indent * next_level);322}323}324else // map325{326_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_map(node));327for(size_t ich = m_tree->first_child(node); ich != NONE; ich = m_tree->next_sibling(ich))328{329_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->has_key(ich));330if(m_tree->is_keyval(ich))331{332this->Writer::_do_write(ind);333_writek(ich, next_level);334this->Writer::_do_write(": ");335_writev(ich, next_level);336this->Writer::_do_write('\n');337}338else339{340_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(ich));341NodeType ty = m_tree->type(ich);342if(ty.marked_flow_sl())343{344this->Writer::_do_write(ind);345_do_visit_flow_sl(ich, 0u);346this->Writer::_do_write('\n');347}348else if(ty.marked_flow_ml())349{350this->Writer::_do_write(ind);351_do_visit_flow_ml(ich, 0u);352this->Writer::_do_write('\n');353}354else355{356_do_visit_block(ich, next_level, do_indent);357}358}359do_indent = true;360ind = indent_to(do_indent * next_level);361}362}363}364365template<class Writer>366void Emitter<Writer>::_do_visit_block(size_t node, size_t ilevel, size_t do_indent)367{368RYML_ASSERT(!m_tree->is_stream(node));369RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));370RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));371RepC ind = indent_to(do_indent * ilevel);372373if(m_tree->is_doc(node))374{375_write_doc(node);376if(!m_tree->has_children(node))377return;378}379else if(m_tree->is_container(node))380{381RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));382383bool spc = false; // write a space384bool nl = false; // write a newline385386if(m_tree->has_key(node))387{388this->Writer::_do_write(ind);389_writek(node, ilevel);390this->Writer::_do_write(':');391spc = true;392}393else if(!m_tree->is_root(node))394{395this->Writer::_do_write(ind);396this->Writer::_do_write('-');397spc = true;398}399400if(m_tree->has_val_tag(node))401{402if(spc)403this->Writer::_do_write(' ');404_write_tag(m_tree->val_tag(node));405spc = true;406nl = true;407}408409if(m_tree->has_val_anchor(node))410{411if(spc)412this->Writer::_do_write(' ');413this->Writer::_do_write('&');414this->Writer::_do_write(m_tree->val_anchor(node));415spc = true;416nl = true;417}418419if(m_tree->has_children(node))420{421if(m_tree->has_key(node))422nl = true;423else424if(!m_tree->is_root(node) && !nl)425spc = true;426}427else428{429if(m_tree->is_seq(node))430this->Writer::_do_write(" []\n");431else if(m_tree->is_map(node))432this->Writer::_do_write(" {}\n");433return;434}435436if(spc && !nl)437this->Writer::_do_write(' ');438439do_indent = 0;440if(nl)441{442this->Writer::_do_write('\n');443do_indent = 1;444}445} // container446447size_t next_level = ilevel + 1;448if(m_tree->is_root(node) || m_tree->is_doc(node))449next_level = ilevel; // do not indent at top level450451_do_visit_block_container(node, next_level, do_indent);452}453454template<class Writer>455void Emitter<Writer>::_do_visit_json(size_t id)456{457_RYML_CB_CHECK(m_tree->callbacks(), !m_tree->is_stream(id)); // JSON does not have streams458if(m_tree->is_keyval(id))459{460_writek_json(id);461this->Writer::_do_write(": ");462_writev_json(id);463}464else if(m_tree->is_val(id))465{466_writev_json(id);467}468else if(m_tree->is_container(id))469{470if(m_tree->has_key(id))471{472_writek_json(id);473this->Writer::_do_write(": ");474}475if(m_tree->is_seq(id))476this->Writer::_do_write('[');477else if(m_tree->is_map(id))478this->Writer::_do_write('{');479} // container480481for(size_t ich = m_tree->first_child(id); ich != NONE; ich = m_tree->next_sibling(ich))482{483if(ich != m_tree->first_child(id))484this->Writer::_do_write(',');485_do_visit_json(ich);486}487488if(m_tree->is_seq(id))489this->Writer::_do_write(']');490else if(m_tree->is_map(id))491this->Writer::_do_write('}');492}493494template<class Writer>495void Emitter<Writer>::_write(NodeScalar const& C4_RESTRICT sc, NodeType flags, size_t ilevel)496{497if( ! sc.tag.empty())498{499_write_tag(sc.tag);500this->Writer::_do_write(' ');501}502if(flags.has_anchor())503{504RYML_ASSERT(flags.is_ref() != flags.has_anchor());505RYML_ASSERT( ! sc.anchor.empty());506this->Writer::_do_write('&');507this->Writer::_do_write(sc.anchor);508this->Writer::_do_write(' ');509}510else if(flags.is_ref())511{512if(sc.anchor != "<<")513this->Writer::_do_write('*');514this->Writer::_do_write(sc.anchor);515return;516}517518// ensure the style flags only have one of KEY or VAL519_RYML_CB_ASSERT(m_tree->callbacks(), ((flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE)) == 0) || (((flags&_WIP_KEY_STYLE) == 0) != ((flags&_WIP_VAL_STYLE) == 0)));520521auto style_marks = flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE);522if(style_marks & (_WIP_KEY_LITERAL|_WIP_VAL_LITERAL))523{524_write_scalar_literal(sc.scalar, ilevel, flags.has_key());525}526else if(style_marks & (_WIP_KEY_FOLDED|_WIP_VAL_FOLDED))527{528_write_scalar_folded(sc.scalar, ilevel, flags.has_key());529}530else if(style_marks & (_WIP_KEY_SQUO|_WIP_VAL_SQUO))531{532_write_scalar_squo(sc.scalar, ilevel);533}534else if(style_marks & (_WIP_KEY_DQUO|_WIP_VAL_DQUO))535{536_write_scalar_dquo(sc.scalar, ilevel);537}538else if(style_marks & (_WIP_KEY_PLAIN|_WIP_VAL_PLAIN))539{540_write_scalar_plain(sc.scalar, ilevel);541}542else if(!style_marks)543{544size_t first_non_nl = sc.scalar.first_not_of('\n');545bool all_newlines = first_non_nl == npos;546bool has_leading_ws = (!all_newlines) && sc.scalar.sub(first_non_nl).begins_with_any(" \t");547bool do_literal = ((!sc.scalar.empty() && all_newlines) || (has_leading_ws && !sc.scalar.trim(' ').empty()));548if(do_literal)549{550_write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);551}552else553{554for(size_t i = 0; i < sc.scalar.len; ++i)555{556if(sc.scalar.str[i] == '\n')557{558_write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);559goto wrote_special;560}561// todo: check for escaped characters requiring double quotes562}563_write_scalar(sc.scalar, flags.is_quoted());564wrote_special:565;566}567}568else569{570_RYML_CB_ERR(m_tree->callbacks(), "not implemented");571}572}573template<class Writer>574void Emitter<Writer>::_write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags)575{576if(C4_UNLIKELY( ! sc.tag.empty()))577_RYML_CB_ERR(m_tree->callbacks(), "JSON does not have tags");578if(C4_UNLIKELY(flags.has_anchor()))579_RYML_CB_ERR(m_tree->callbacks(), "JSON does not have anchors");580_write_scalar_json(sc.scalar, flags.has_key(), flags.is_quoted());581}582583#define _rymlindent_nextline() for(size_t lv = 0; lv < ilevel+1; ++lv) { this->Writer::_do_write(' '); this->Writer::_do_write(' '); }584585template<class Writer>586void Emitter<Writer>::_write_scalar_literal(csubstr s, size_t ilevel, bool explicit_key, bool explicit_indentation)587{588if(explicit_key)589this->Writer::_do_write("? ");590csubstr trimmed = s.trimr("\n\r");591size_t numnewlines_at_end = s.len - trimmed.len - s.sub(trimmed.len).count('\r');592//593if(!explicit_indentation)594this->Writer::_do_write('|');595else596this->Writer::_do_write("|2");597//598if(numnewlines_at_end > 1 || (trimmed.len == 0 && s.len > 0)/*only newlines*/)599this->Writer::_do_write("+\n");600else if(numnewlines_at_end == 1)601this->Writer::_do_write('\n');602else603this->Writer::_do_write("-\n");604//605if(trimmed.len)606{607size_t pos = 0; // tracks the last character that was already written608for(size_t i = 0; i < trimmed.len; ++i)609{610if(trimmed[i] != '\n')611continue;612// write everything up to this point613csubstr since_pos = trimmed.range(pos, i+1); // include the newline614_rymlindent_nextline()615this->Writer::_do_write(since_pos);616pos = i+1; // already written617}618if(pos < trimmed.len)619{620_rymlindent_nextline()621this->Writer::_do_write(trimmed.sub(pos));622}623if(numnewlines_at_end)624{625this->Writer::_do_write('\n');626--numnewlines_at_end;627}628}629for(size_t i = 0; i < numnewlines_at_end; ++i)630{631_rymlindent_nextline()632if(i+1 < numnewlines_at_end || explicit_key)633this->Writer::_do_write('\n');634}635if(explicit_key && !numnewlines_at_end)636this->Writer::_do_write('\n');637}638639template<class Writer>640void Emitter<Writer>::_write_scalar_folded(csubstr s, size_t ilevel, bool explicit_key)641{642if(explicit_key)643{644this->Writer::_do_write("? ");645}646RYML_ASSERT(s.find("\r") == csubstr::npos);647csubstr trimmed = s.trimr('\n');648size_t numnewlines_at_end = s.len - trimmed.len;649if(numnewlines_at_end == 0)650{651this->Writer::_do_write(">-\n");652}653else if(numnewlines_at_end == 1)654{655this->Writer::_do_write(">\n");656}657else if(numnewlines_at_end > 1)658{659this->Writer::_do_write(">+\n");660}661if(trimmed.len)662{663size_t pos = 0; // tracks the last character that was already written664for(size_t i = 0; i < trimmed.len; ++i)665{666if(trimmed[i] != '\n')667continue;668// write everything up to this point669csubstr since_pos = trimmed.range(pos, i+1); // include the newline670pos = i+1; // because of the newline671_rymlindent_nextline()672this->Writer::_do_write(since_pos);673this->Writer::_do_write('\n'); // write the newline twice674}675if(pos < trimmed.len)676{677_rymlindent_nextline()678this->Writer::_do_write(trimmed.sub(pos));679}680if(numnewlines_at_end)681{682this->Writer::_do_write('\n');683--numnewlines_at_end;684}685}686for(size_t i = 0; i < numnewlines_at_end; ++i)687{688_rymlindent_nextline()689if(i+1 < numnewlines_at_end || explicit_key)690this->Writer::_do_write('\n');691}692if(explicit_key && !numnewlines_at_end)693this->Writer::_do_write('\n');694}695696template<class Writer>697void Emitter<Writer>::_write_scalar_squo(csubstr s, size_t ilevel)698{699size_t pos = 0; // tracks the last character that was already written700this->Writer::_do_write('\'');701for(size_t i = 0; i < s.len; ++i)702{703if(s[i] == '\n')704{705csubstr sub = s.range(pos, i+1);706this->Writer::_do_write(sub); // write everything up to (including) this char707this->Writer::_do_write('\n'); // write the character again708if(i + 1 < s.len)709_rymlindent_nextline() // indent the next line710pos = i+1;711}712else if(s[i] == '\'')713{714csubstr sub = s.range(pos, i+1);715this->Writer::_do_write(sub); // write everything up to (including) this char716this->Writer::_do_write('\''); // write the character again717pos = i+1;718}719}720// write missing characters at the end of the string721if(pos < s.len)722this->Writer::_do_write(s.sub(pos));723this->Writer::_do_write('\'');724}725726template<class Writer>727void Emitter<Writer>::_write_scalar_dquo(csubstr s, size_t ilevel)728{729size_t pos = 0; // tracks the last character that was already written730this->Writer::_do_write('"');731for(size_t i = 0; i < s.len; ++i)732{733const char curr = s.str[i];734if(curr == '"' || curr == '\\')735{736csubstr sub = s.range(pos, i);737this->Writer::_do_write(sub); // write everything up to (excluding) this char738this->Writer::_do_write('\\'); // write the escape739this->Writer::_do_write(curr); // write the char740pos = i+1;741}742else if(s[i] == '\n')743{744csubstr sub = s.range(pos, i+1);745this->Writer::_do_write(sub); // write everything up to (including) this newline746this->Writer::_do_write('\n'); // write the newline again747if(i + 1 < s.len)748_rymlindent_nextline() // indent the next line749pos = i+1;750if(i+1 < s.len) // escape leading whitespace after the newline751{752const char next = s.str[i+1];753if(next == ' ' || next == '\t')754this->Writer::_do_write('\\');755}756}757else if(curr == ' ' || curr == '\t')758{759// escape trailing whitespace before a newline760size_t next = s.first_not_of(" \t\r", i);761if(next != npos && s[next] == '\n')762{763csubstr sub = s.range(pos, i);764this->Writer::_do_write(sub); // write everything up to (excluding) this char765this->Writer::_do_write('\\'); // escape the whitespace766pos = i;767}768}769else if(C4_UNLIKELY(curr == '\r'))770{771csubstr sub = s.range(pos, i);772this->Writer::_do_write(sub); // write everything up to (excluding) this char773this->Writer::_do_write("\\r"); // write the escaped char774pos = i+1;775}776}777// write missing characters at the end of the string778if(pos < s.len)779{780csubstr sub = s.sub(pos);781this->Writer::_do_write(sub);782}783this->Writer::_do_write('"');784}785786template<class Writer>787void Emitter<Writer>::_write_scalar_plain(csubstr s, size_t ilevel)788{789size_t pos = 0; // tracks the last character that was already written790for(size_t i = 0; i < s.len; ++i)791{792const char curr = s.str[i];793if(curr == '\n')794{795csubstr sub = s.range(pos, i+1);796this->Writer::_do_write(sub); // write everything up to (including) this newline797this->Writer::_do_write('\n'); // write the newline again798if(i + 1 < s.len)799_rymlindent_nextline() // indent the next line800pos = i+1;801}802}803// write missing characters at the end of the string804if(pos < s.len)805{806csubstr sub = s.sub(pos);807this->Writer::_do_write(sub);808}809}810811#undef _rymlindent_nextline812813template<class Writer>814void Emitter<Writer>::_write_scalar(csubstr s, bool was_quoted)815{816// this block of code needed to be moved to before the needs_quotes817// assignment to work around a g++ optimizer bug where (s.str != nullptr)818// was evaluated as true even if s.str was actually a nullptr (!!!)819if(s.len == size_t(0))820{821if(was_quoted || s.str != nullptr)822this->Writer::_do_write("''");823return;824}825826const bool needs_quotes = (827was_quoted828||829(830( ! s.is_number())831&&832(833// has leading whitespace834// looks like reference or anchor835// would be treated as a directive836// see https://www.yaml.info/learn/quote.html#noplain837s.begins_with_any(" \n\t\r*&%@`")838||839s.begins_with("<<")840||841// has trailing whitespace842s.ends_with_any(" \n\t\r")843||844// has special chars845(s.first_of("#:-?,\n{}[]'\"") != npos)846)847)848);849850if( ! needs_quotes)851{852this->Writer::_do_write(s);853}854else855{856const bool has_dquotes = s.first_of( '"') != npos;857const bool has_squotes = s.first_of('\'') != npos;858if(!has_squotes && has_dquotes)859{860this->Writer::_do_write('\'');861this->Writer::_do_write(s);862this->Writer::_do_write('\'');863}864else if(has_squotes && !has_dquotes)865{866RYML_ASSERT(s.count('\n') == 0);867this->Writer::_do_write('"');868this->Writer::_do_write(s);869this->Writer::_do_write('"');870}871else872{873_write_scalar_squo(s, /*FIXME FIXME FIXME*/0);874}875}876}877template<class Writer>878void Emitter<Writer>::_write_scalar_json(csubstr s, bool as_key, bool use_quotes)879{880if((!use_quotes)881// json keys require quotes882&& (!as_key)883&& (884// do not quote special cases885(s == "true" || s == "false" || s == "null")886|| (887// do not quote numbers888(s.is_number()889&& (890// quote integral numbers if they have a leading 0891// https://github.com/biojppm/rapidyaml/issues/291892(!(s.len > 1 && s.begins_with('0')))893// do not quote reals with leading 0894// https://github.com/biojppm/rapidyaml/issues/313895|| (s.find('.') != csubstr::npos) ))896)897)898)899{900this->Writer::_do_write(s);901}902else903{904size_t pos = 0;905this->Writer::_do_write('"');906for(size_t i = 0; i < s.len; ++i)907{908switch(s.str[i])909{910case '"':911this->Writer ::_do_write(s.range(pos, i));912this->Writer ::_do_write("\\\"");913pos = i + 1;914break;915case '\n':916this->Writer ::_do_write(s.range(pos, i));917this->Writer ::_do_write("\\n");918pos = i + 1;919break;920case '\t':921this->Writer ::_do_write(s.range(pos, i));922this->Writer ::_do_write("\\t");923pos = i + 1;924break;925case '\\':926this->Writer ::_do_write(s.range(pos, i));927this->Writer ::_do_write("\\\\");928pos = i + 1;929break;930case '\r':931this->Writer ::_do_write(s.range(pos, i));932this->Writer ::_do_write("\\r");933pos = i + 1;934break;935case '\b':936this->Writer ::_do_write(s.range(pos, i));937this->Writer ::_do_write("\\b");938pos = i + 1;939break;940case '\f':941this->Writer ::_do_write(s.range(pos, i));942this->Writer ::_do_write("\\f");943pos = i + 1;944break;945}946}947if(pos < s.len)948{949csubstr sub = s.sub(pos);950this->Writer::_do_write(sub);951}952this->Writer::_do_write('"');953}954}955956} // namespace yml957} // namespace c4958959#endif /* _C4_YML_EMIT_DEF_HPP_ */960961962