mirror of https://github.com/stenzek/duckstation
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			961 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
			
		
		
	
	
			961 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
#ifndef _C4_YML_EMIT_DEF_HPP_
 | 
						|
#define _C4_YML_EMIT_DEF_HPP_
 | 
						|
 | 
						|
#ifndef _C4_YML_EMIT_HPP_
 | 
						|
#include "c4/yml/emit.hpp"
 | 
						|
#endif
 | 
						|
 | 
						|
namespace c4 {
 | 
						|
namespace yml {
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& t, size_t id, bool error_on_excess)
 | 
						|
{
 | 
						|
    if(t.empty())
 | 
						|
    {
 | 
						|
        _RYML_CB_ASSERT(t.callbacks(), id == NONE);
 | 
						|
        return {};
 | 
						|
    }
 | 
						|
    _RYML_CB_CHECK(t.callbacks(), id < t.capacity());
 | 
						|
    m_tree = &t;
 | 
						|
    if(type == EMIT_YAML)
 | 
						|
        _emit_yaml(id);
 | 
						|
    else if(type == EMIT_JSON)
 | 
						|
        _do_visit_json(id);
 | 
						|
    else
 | 
						|
        _RYML_CB_ERR(m_tree->callbacks(), "unknown emit type");
 | 
						|
    return this->Writer::_get(error_on_excess);
 | 
						|
}
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& t, bool error_on_excess)
 | 
						|
{
 | 
						|
    if(t.empty())
 | 
						|
        return {};
 | 
						|
    return this->emit_as(type, t, t.root_id(), error_on_excess);
 | 
						|
}
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
substr Emitter<Writer>::emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess)
 | 
						|
{
 | 
						|
    _RYML_CB_CHECK(n.tree()->callbacks(), n.valid());
 | 
						|
    return this->emit_as(type, *n.tree(), n.id(), error_on_excess);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_emit_yaml(size_t id)
 | 
						|
{
 | 
						|
    // save branches in the visitor by doing the initial stream/doc
 | 
						|
    // logic here, sparing the need to check stream/val/keyval inside
 | 
						|
    // the visitor functions
 | 
						|
    auto dispatch = [this](size_t node){
 | 
						|
        NodeType ty = m_tree->type(node);
 | 
						|
        if(ty.marked_flow_sl())
 | 
						|
            _do_visit_flow_sl(node, 0);
 | 
						|
        else if(ty.marked_flow_ml())
 | 
						|
            _do_visit_flow_ml(node, 0);
 | 
						|
        else
 | 
						|
        {
 | 
						|
            _do_visit_block(node, 0);
 | 
						|
        }
 | 
						|
    };
 | 
						|
    if(!m_tree->is_root(id))
 | 
						|
    {
 | 
						|
        if(m_tree->is_container(id) && !m_tree->type(id).marked_flow())
 | 
						|
        {
 | 
						|
            size_t ilevel = 0;
 | 
						|
            if(m_tree->has_key(id))
 | 
						|
            {
 | 
						|
                this->Writer::_do_write(m_tree->key(id));
 | 
						|
                this->Writer::_do_write(":\n");
 | 
						|
                ++ilevel;
 | 
						|
            }
 | 
						|
            _do_visit_block_container(id, ilevel, ilevel);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    auto *btd = m_tree->tag_directives().b;
 | 
						|
    auto *etd = m_tree->tag_directives().e;
 | 
						|
    auto write_tag_directives = [&btd, etd, this](size_t next_node){
 | 
						|
        auto end = btd;
 | 
						|
        while(end < etd)
 | 
						|
        {
 | 
						|
            if(end->next_node_id > next_node)
 | 
						|
                break;
 | 
						|
            ++end;
 | 
						|
        }
 | 
						|
        for( ; btd != end; ++btd)
 | 
						|
        {
 | 
						|
            if(next_node != m_tree->first_child(m_tree->parent(next_node)))
 | 
						|
                this->Writer::_do_write("...\n");
 | 
						|
            this->Writer::_do_write("%TAG ");
 | 
						|
            this->Writer::_do_write(btd->handle);
 | 
						|
            this->Writer::_do_write(' ');
 | 
						|
            this->Writer::_do_write(btd->prefix);
 | 
						|
            this->Writer::_do_write('\n');
 | 
						|
        }
 | 
						|
    };
 | 
						|
    if(m_tree->is_stream(id))
 | 
						|
    {
 | 
						|
        if(m_tree->first_child(id) != NONE)
 | 
						|
            write_tag_directives(m_tree->first_child(id));
 | 
						|
        for(size_t child = m_tree->first_child(id); child != NONE; child = m_tree->next_sibling(child))
 | 
						|
        {
 | 
						|
            dispatch(child);
 | 
						|
            if(m_tree->next_sibling(child) != NONE)
 | 
						|
                write_tag_directives(m_tree->next_sibling(child));
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if(m_tree->is_container(id))
 | 
						|
    {
 | 
						|
        dispatch(id);
 | 
						|
    }
 | 
						|
    else if(m_tree->is_doc(id))
 | 
						|
    {
 | 
						|
        _RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->is_container(id)); // checked above
 | 
						|
        _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_val(id)); // so it must be a val
 | 
						|
        _write_doc(id);
 | 
						|
    }
 | 
						|
    else if(m_tree->is_keyval(id))
 | 
						|
    {
 | 
						|
        _writek(id, 0);
 | 
						|
        this->Writer::_do_write(": ");
 | 
						|
        _writev(id, 0);
 | 
						|
        if(!m_tree->type(id).marked_flow())
 | 
						|
            this->Writer::_do_write('\n');
 | 
						|
    }
 | 
						|
    else if(m_tree->is_val(id))
 | 
						|
    {
 | 
						|
        //this->Writer::_do_write("- ");
 | 
						|
        _writev(id, 0);
 | 
						|
        if(!m_tree->type(id).marked_flow())
 | 
						|
            this->Writer::_do_write('\n');
 | 
						|
    }
 | 
						|
    else if(m_tree->type(id) == NOTYPE)
 | 
						|
    {
 | 
						|
        ;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        _RYML_CB_ERR(m_tree->callbacks(), "unknown type");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_write_doc(size_t id)
 | 
						|
{
 | 
						|
    RYML_ASSERT(m_tree->is_doc(id));
 | 
						|
    if(!m_tree->is_root(id))
 | 
						|
    {
 | 
						|
        RYML_ASSERT(m_tree->is_stream(m_tree->parent(id)));
 | 
						|
        this->Writer::_do_write("---");
 | 
						|
    }
 | 
						|
    if(!m_tree->has_val(id)) // this is more frequent
 | 
						|
    {
 | 
						|
        if(m_tree->has_val_tag(id))
 | 
						|
        {
 | 
						|
            if(!m_tree->is_root(id))
 | 
						|
                this->Writer::_do_write(' ');
 | 
						|
            _write_tag(m_tree->val_tag(id));
 | 
						|
        }
 | 
						|
        if(m_tree->has_val_anchor(id))
 | 
						|
        {
 | 
						|
            if(!m_tree->is_root(id))
 | 
						|
                this->Writer::_do_write(' ');
 | 
						|
            this->Writer::_do_write('&');
 | 
						|
            this->Writer::_do_write(m_tree->val_anchor(id));
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else // docval
 | 
						|
    {
 | 
						|
        RYML_ASSERT(m_tree->has_val(id));
 | 
						|
        RYML_ASSERT(!m_tree->has_key(id));
 | 
						|
        if(!m_tree->is_root(id))
 | 
						|
            this->Writer::_do_write(' ');
 | 
						|
        _writev(id, 0);
 | 
						|
    }
 | 
						|
    this->Writer::_do_write('\n');
 | 
						|
}
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_do_visit_flow_sl(size_t node, size_t ilevel)
 | 
						|
{
 | 
						|
    RYML_ASSERT(!m_tree->is_stream(node));
 | 
						|
    RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));
 | 
						|
    RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
 | 
						|
 | 
						|
    if(m_tree->is_doc(node))
 | 
						|
    {
 | 
						|
        _write_doc(node);
 | 
						|
        if(!m_tree->has_children(node))
 | 
						|
            return;
 | 
						|
    }
 | 
						|
    else if(m_tree->is_container(node))
 | 
						|
    {
 | 
						|
        RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));
 | 
						|
 | 
						|
        bool spc = false; // write a space
 | 
						|
 | 
						|
        if(m_tree->has_key(node))
 | 
						|
        {
 | 
						|
            _writek(node, ilevel);
 | 
						|
            this->Writer::_do_write(':');
 | 
						|
            spc = true;
 | 
						|
        }
 | 
						|
 | 
						|
        if(m_tree->has_val_tag(node))
 | 
						|
        {
 | 
						|
            if(spc)
 | 
						|
                this->Writer::_do_write(' ');
 | 
						|
            _write_tag(m_tree->val_tag(node));
 | 
						|
            spc = true;
 | 
						|
        }
 | 
						|
 | 
						|
        if(m_tree->has_val_anchor(node))
 | 
						|
        {
 | 
						|
            if(spc)
 | 
						|
                this->Writer::_do_write(' ');
 | 
						|
            this->Writer::_do_write('&');
 | 
						|
            this->Writer::_do_write(m_tree->val_anchor(node));
 | 
						|
            spc = true;
 | 
						|
        }
 | 
						|
 | 
						|
        if(spc)
 | 
						|
            this->Writer::_do_write(' ');
 | 
						|
 | 
						|
        if(m_tree->is_map(node))
 | 
						|
        {
 | 
						|
            this->Writer::_do_write('{');
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_seq(node));
 | 
						|
            this->Writer::_do_write('[');
 | 
						|
        }
 | 
						|
    } // container
 | 
						|
 | 
						|
    for(size_t child = m_tree->first_child(node), count = 0; child != NONE; child = m_tree->next_sibling(child))
 | 
						|
    {
 | 
						|
        if(count++)
 | 
						|
            this->Writer::_do_write(',');
 | 
						|
        if(m_tree->is_keyval(child))
 | 
						|
        {
 | 
						|
            _writek(child, ilevel);
 | 
						|
            this->Writer::_do_write(": ");
 | 
						|
            _writev(child, ilevel);
 | 
						|
        }
 | 
						|
        else if(m_tree->is_val(child))
 | 
						|
        {
 | 
						|
            _writev(child, ilevel);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // with single-line flow, we can never go back to block
 | 
						|
            _do_visit_flow_sl(child, ilevel + 1);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if(m_tree->is_map(node))
 | 
						|
    {
 | 
						|
        this->Writer::_do_write('}');
 | 
						|
    }
 | 
						|
    else if(m_tree->is_seq(node))
 | 
						|
    {
 | 
						|
        this->Writer::_do_write(']');
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_do_visit_flow_ml(size_t id, size_t ilevel, size_t do_indent)
 | 
						|
{
 | 
						|
    C4_UNUSED(id);
 | 
						|
    C4_UNUSED(ilevel);
 | 
						|
    C4_UNUSED(do_indent);
 | 
						|
    RYML_CHECK(false/*not implemented*/);
 | 
						|
}
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_do_visit_block_container(size_t node, size_t next_level, size_t do_indent)
 | 
						|
{
 | 
						|
    RepC ind = indent_to(do_indent * next_level);
 | 
						|
 | 
						|
    if(m_tree->is_seq(node))
 | 
						|
    {
 | 
						|
        for(size_t child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))
 | 
						|
        {
 | 
						|
            _RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->has_key(child));
 | 
						|
            if(m_tree->is_val(child))
 | 
						|
            {
 | 
						|
                this->Writer::_do_write(ind);
 | 
						|
                this->Writer::_do_write("- ");
 | 
						|
                _writev(child, next_level);
 | 
						|
                this->Writer::_do_write('\n');
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(child));
 | 
						|
                NodeType ty = m_tree->type(child);
 | 
						|
                if(ty.marked_flow_sl())
 | 
						|
                {
 | 
						|
                    this->Writer::_do_write(ind);
 | 
						|
                    this->Writer::_do_write("- ");
 | 
						|
                    _do_visit_flow_sl(child, 0u);
 | 
						|
                    this->Writer::_do_write('\n');
 | 
						|
                }
 | 
						|
                else if(ty.marked_flow_ml())
 | 
						|
                {
 | 
						|
                    this->Writer::_do_write(ind);
 | 
						|
                    this->Writer::_do_write("- ");
 | 
						|
                    _do_visit_flow_ml(child, next_level, do_indent);
 | 
						|
                    this->Writer::_do_write('\n');
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    _do_visit_block(child, next_level, do_indent);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            do_indent = true;
 | 
						|
            ind = indent_to(do_indent * next_level);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else // map
 | 
						|
    {
 | 
						|
        _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_map(node));
 | 
						|
        for(size_t ich = m_tree->first_child(node); ich != NONE; ich = m_tree->next_sibling(ich))
 | 
						|
        {
 | 
						|
            _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->has_key(ich));
 | 
						|
            if(m_tree->is_keyval(ich))
 | 
						|
            {
 | 
						|
                this->Writer::_do_write(ind);
 | 
						|
                _writek(ich, next_level);
 | 
						|
                this->Writer::_do_write(": ");
 | 
						|
                _writev(ich, next_level);
 | 
						|
                this->Writer::_do_write('\n');
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(ich));
 | 
						|
                NodeType ty = m_tree->type(ich);
 | 
						|
                if(ty.marked_flow_sl())
 | 
						|
                {
 | 
						|
                    this->Writer::_do_write(ind);
 | 
						|
                    _do_visit_flow_sl(ich, 0u);
 | 
						|
                    this->Writer::_do_write('\n');
 | 
						|
                }
 | 
						|
                else if(ty.marked_flow_ml())
 | 
						|
                {
 | 
						|
                    this->Writer::_do_write(ind);
 | 
						|
                    _do_visit_flow_ml(ich, 0u);
 | 
						|
                    this->Writer::_do_write('\n');
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    _do_visit_block(ich, next_level, do_indent);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            do_indent = true;
 | 
						|
            ind = indent_to(do_indent * next_level);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_do_visit_block(size_t node, size_t ilevel, size_t do_indent)
 | 
						|
{
 | 
						|
    RYML_ASSERT(!m_tree->is_stream(node));
 | 
						|
    RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));
 | 
						|
    RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
 | 
						|
    RepC ind = indent_to(do_indent * ilevel);
 | 
						|
 | 
						|
    if(m_tree->is_doc(node))
 | 
						|
    {
 | 
						|
        _write_doc(node);
 | 
						|
        if(!m_tree->has_children(node))
 | 
						|
            return;
 | 
						|
    }
 | 
						|
    else if(m_tree->is_container(node))
 | 
						|
    {
 | 
						|
        RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));
 | 
						|
 | 
						|
        bool spc = false; // write a space
 | 
						|
        bool nl = false;  // write a newline
 | 
						|
 | 
						|
        if(m_tree->has_key(node))
 | 
						|
        {
 | 
						|
            this->Writer::_do_write(ind);
 | 
						|
            _writek(node, ilevel);
 | 
						|
            this->Writer::_do_write(':');
 | 
						|
            spc = true;
 | 
						|
        }
 | 
						|
        else if(!m_tree->is_root(node))
 | 
						|
        {
 | 
						|
            this->Writer::_do_write(ind);
 | 
						|
            this->Writer::_do_write('-');
 | 
						|
            spc = true;
 | 
						|
        }
 | 
						|
 | 
						|
        if(m_tree->has_val_tag(node))
 | 
						|
        {
 | 
						|
            if(spc)
 | 
						|
                this->Writer::_do_write(' ');
 | 
						|
            _write_tag(m_tree->val_tag(node));
 | 
						|
            spc = true;
 | 
						|
            nl = true;
 | 
						|
        }
 | 
						|
 | 
						|
        if(m_tree->has_val_anchor(node))
 | 
						|
        {
 | 
						|
            if(spc)
 | 
						|
                this->Writer::_do_write(' ');
 | 
						|
            this->Writer::_do_write('&');
 | 
						|
            this->Writer::_do_write(m_tree->val_anchor(node));
 | 
						|
            spc = true;
 | 
						|
            nl = true;
 | 
						|
        }
 | 
						|
 | 
						|
        if(m_tree->has_children(node))
 | 
						|
        {
 | 
						|
            if(m_tree->has_key(node))
 | 
						|
                nl = true;
 | 
						|
            else
 | 
						|
                if(!m_tree->is_root(node) && !nl)
 | 
						|
                    spc = true;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            if(m_tree->is_seq(node))
 | 
						|
                this->Writer::_do_write(" []\n");
 | 
						|
            else if(m_tree->is_map(node))
 | 
						|
                this->Writer::_do_write(" {}\n");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        if(spc && !nl)
 | 
						|
            this->Writer::_do_write(' ');
 | 
						|
 | 
						|
        do_indent = 0;
 | 
						|
        if(nl)
 | 
						|
        {
 | 
						|
            this->Writer::_do_write('\n');
 | 
						|
            do_indent = 1;
 | 
						|
        }
 | 
						|
    } // container
 | 
						|
 | 
						|
    size_t next_level = ilevel + 1;
 | 
						|
    if(m_tree->is_root(node) || m_tree->is_doc(node))
 | 
						|
        next_level = ilevel; // do not indent at top level
 | 
						|
 | 
						|
    _do_visit_block_container(node, next_level, do_indent);
 | 
						|
}
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_do_visit_json(size_t id)
 | 
						|
{
 | 
						|
    _RYML_CB_CHECK(m_tree->callbacks(), !m_tree->is_stream(id)); // JSON does not have streams
 | 
						|
    if(m_tree->is_keyval(id))
 | 
						|
    {
 | 
						|
        _writek_json(id);
 | 
						|
        this->Writer::_do_write(": ");
 | 
						|
        _writev_json(id);
 | 
						|
    }
 | 
						|
    else if(m_tree->is_val(id))
 | 
						|
    {
 | 
						|
        _writev_json(id);
 | 
						|
    }
 | 
						|
    else if(m_tree->is_container(id))
 | 
						|
    {
 | 
						|
        if(m_tree->has_key(id))
 | 
						|
        {
 | 
						|
            _writek_json(id);
 | 
						|
            this->Writer::_do_write(": ");
 | 
						|
        }
 | 
						|
        if(m_tree->is_seq(id))
 | 
						|
            this->Writer::_do_write('[');
 | 
						|
        else if(m_tree->is_map(id))
 | 
						|
            this->Writer::_do_write('{');
 | 
						|
    }  // container
 | 
						|
 | 
						|
    for(size_t ich = m_tree->first_child(id); ich != NONE; ich = m_tree->next_sibling(ich))
 | 
						|
    {
 | 
						|
        if(ich != m_tree->first_child(id))
 | 
						|
            this->Writer::_do_write(',');
 | 
						|
        _do_visit_json(ich);
 | 
						|
    }
 | 
						|
 | 
						|
    if(m_tree->is_seq(id))
 | 
						|
        this->Writer::_do_write(']');
 | 
						|
    else if(m_tree->is_map(id))
 | 
						|
        this->Writer::_do_write('}');
 | 
						|
}
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_write(NodeScalar const& C4_RESTRICT sc, NodeType flags, size_t ilevel)
 | 
						|
{
 | 
						|
    if( ! sc.tag.empty())
 | 
						|
    {
 | 
						|
        _write_tag(sc.tag);
 | 
						|
        this->Writer::_do_write(' ');
 | 
						|
    }
 | 
						|
    if(flags.has_anchor())
 | 
						|
    {
 | 
						|
        RYML_ASSERT(flags.is_ref() != flags.has_anchor());
 | 
						|
        RYML_ASSERT( ! sc.anchor.empty());
 | 
						|
        this->Writer::_do_write('&');
 | 
						|
        this->Writer::_do_write(sc.anchor);
 | 
						|
        this->Writer::_do_write(' ');
 | 
						|
    }
 | 
						|
    else if(flags.is_ref())
 | 
						|
    {
 | 
						|
        if(sc.anchor != "<<")
 | 
						|
            this->Writer::_do_write('*');
 | 
						|
        this->Writer::_do_write(sc.anchor);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // ensure the style flags only have one of KEY or VAL
 | 
						|
    _RYML_CB_ASSERT(m_tree->callbacks(), ((flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE)) == 0) || (((flags&_WIP_KEY_STYLE) == 0) != ((flags&_WIP_VAL_STYLE) == 0)));
 | 
						|
 | 
						|
    auto style_marks = flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE);
 | 
						|
    if(style_marks & (_WIP_KEY_LITERAL|_WIP_VAL_LITERAL))
 | 
						|
    {
 | 
						|
        _write_scalar_literal(sc.scalar, ilevel, flags.has_key());
 | 
						|
    }
 | 
						|
    else if(style_marks & (_WIP_KEY_FOLDED|_WIP_VAL_FOLDED))
 | 
						|
    {
 | 
						|
        _write_scalar_folded(sc.scalar, ilevel, flags.has_key());
 | 
						|
    }
 | 
						|
    else if(style_marks & (_WIP_KEY_SQUO|_WIP_VAL_SQUO))
 | 
						|
    {
 | 
						|
        _write_scalar_squo(sc.scalar, ilevel);
 | 
						|
    }
 | 
						|
    else if(style_marks & (_WIP_KEY_DQUO|_WIP_VAL_DQUO))
 | 
						|
    {
 | 
						|
        _write_scalar_dquo(sc.scalar, ilevel);
 | 
						|
    }
 | 
						|
    else if(style_marks & (_WIP_KEY_PLAIN|_WIP_VAL_PLAIN))
 | 
						|
    {
 | 
						|
        _write_scalar_plain(sc.scalar, ilevel);
 | 
						|
    }
 | 
						|
    else if(!style_marks)
 | 
						|
    {
 | 
						|
        size_t first_non_nl = sc.scalar.first_not_of('\n');
 | 
						|
        bool all_newlines = first_non_nl == npos;
 | 
						|
        bool has_leading_ws = (!all_newlines) && sc.scalar.sub(first_non_nl).begins_with_any(" \t");
 | 
						|
        bool do_literal = ((!sc.scalar.empty() && all_newlines) || (has_leading_ws && !sc.scalar.trim(' ').empty()));
 | 
						|
        if(do_literal)
 | 
						|
        {
 | 
						|
            _write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            for(size_t i = 0; i < sc.scalar.len; ++i)
 | 
						|
            {
 | 
						|
                if(sc.scalar.str[i] == '\n')
 | 
						|
                {
 | 
						|
                    _write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);
 | 
						|
                    goto wrote_special;
 | 
						|
                }
 | 
						|
                // todo: check for escaped characters requiring double quotes
 | 
						|
            }
 | 
						|
            _write_scalar(sc.scalar, flags.is_quoted());
 | 
						|
        wrote_special:
 | 
						|
            ;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        _RYML_CB_ERR(m_tree->callbacks(), "not implemented");
 | 
						|
    }
 | 
						|
}
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags)
 | 
						|
{
 | 
						|
    if(C4_UNLIKELY( ! sc.tag.empty()))
 | 
						|
        _RYML_CB_ERR(m_tree->callbacks(), "JSON does not have tags");
 | 
						|
    if(C4_UNLIKELY(flags.has_anchor()))
 | 
						|
        _RYML_CB_ERR(m_tree->callbacks(), "JSON does not have anchors");
 | 
						|
    _write_scalar_json(sc.scalar, flags.has_key(), flags.is_quoted());
 | 
						|
}
 | 
						|
 | 
						|
#define _rymlindent_nextline() for(size_t lv = 0; lv < ilevel+1; ++lv) { this->Writer::_do_write(' '); this->Writer::_do_write(' '); }
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_write_scalar_literal(csubstr s, size_t ilevel, bool explicit_key, bool explicit_indentation)
 | 
						|
{
 | 
						|
    if(explicit_key)
 | 
						|
        this->Writer::_do_write("? ");
 | 
						|
    csubstr trimmed = s.trimr("\n\r");
 | 
						|
    size_t numnewlines_at_end = s.len - trimmed.len - s.sub(trimmed.len).count('\r');
 | 
						|
    //
 | 
						|
    if(!explicit_indentation)
 | 
						|
        this->Writer::_do_write('|');
 | 
						|
    else
 | 
						|
        this->Writer::_do_write("|2");
 | 
						|
    //
 | 
						|
    if(numnewlines_at_end > 1 || (trimmed.len == 0 && s.len > 0)/*only newlines*/)
 | 
						|
        this->Writer::_do_write("+\n");
 | 
						|
    else if(numnewlines_at_end == 1)
 | 
						|
        this->Writer::_do_write('\n');
 | 
						|
    else
 | 
						|
        this->Writer::_do_write("-\n");
 | 
						|
    //
 | 
						|
    if(trimmed.len)
 | 
						|
    {
 | 
						|
        size_t pos = 0; // tracks the last character that was already written
 | 
						|
        for(size_t i = 0; i < trimmed.len; ++i)
 | 
						|
        {
 | 
						|
            if(trimmed[i] != '\n')
 | 
						|
                continue;
 | 
						|
            // write everything up to this point
 | 
						|
            csubstr since_pos = trimmed.range(pos, i+1); // include the newline
 | 
						|
            _rymlindent_nextline()
 | 
						|
            this->Writer::_do_write(since_pos);
 | 
						|
            pos = i+1; // already written
 | 
						|
        }
 | 
						|
        if(pos < trimmed.len)
 | 
						|
        {
 | 
						|
            _rymlindent_nextline()
 | 
						|
            this->Writer::_do_write(trimmed.sub(pos));
 | 
						|
        }
 | 
						|
        if(numnewlines_at_end)
 | 
						|
        {
 | 
						|
            this->Writer::_do_write('\n');
 | 
						|
            --numnewlines_at_end;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    for(size_t i = 0; i < numnewlines_at_end; ++i)
 | 
						|
    {
 | 
						|
        _rymlindent_nextline()
 | 
						|
        if(i+1 < numnewlines_at_end || explicit_key)
 | 
						|
            this->Writer::_do_write('\n');
 | 
						|
    }
 | 
						|
    if(explicit_key && !numnewlines_at_end)
 | 
						|
        this->Writer::_do_write('\n');
 | 
						|
}
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_write_scalar_folded(csubstr s, size_t ilevel, bool explicit_key)
 | 
						|
{
 | 
						|
    if(explicit_key)
 | 
						|
    {
 | 
						|
        this->Writer::_do_write("? ");
 | 
						|
    }
 | 
						|
    RYML_ASSERT(s.find("\r") == csubstr::npos);
 | 
						|
    csubstr trimmed = s.trimr('\n');
 | 
						|
    size_t numnewlines_at_end = s.len - trimmed.len;
 | 
						|
    if(numnewlines_at_end == 0)
 | 
						|
    {
 | 
						|
        this->Writer::_do_write(">-\n");
 | 
						|
    }
 | 
						|
    else if(numnewlines_at_end == 1)
 | 
						|
    {
 | 
						|
        this->Writer::_do_write(">\n");
 | 
						|
    }
 | 
						|
    else if(numnewlines_at_end > 1)
 | 
						|
    {
 | 
						|
        this->Writer::_do_write(">+\n");
 | 
						|
    }
 | 
						|
    if(trimmed.len)
 | 
						|
    {
 | 
						|
        size_t pos = 0; // tracks the last character that was already written
 | 
						|
        for(size_t i = 0; i < trimmed.len; ++i)
 | 
						|
        {
 | 
						|
            if(trimmed[i] != '\n')
 | 
						|
                continue;
 | 
						|
            // write everything up to this point
 | 
						|
            csubstr since_pos = trimmed.range(pos, i+1); // include the newline
 | 
						|
            pos = i+1; // because of the newline
 | 
						|
            _rymlindent_nextline()
 | 
						|
            this->Writer::_do_write(since_pos);
 | 
						|
            this->Writer::_do_write('\n'); // write the newline twice
 | 
						|
        }
 | 
						|
        if(pos < trimmed.len)
 | 
						|
        {
 | 
						|
            _rymlindent_nextline()
 | 
						|
            this->Writer::_do_write(trimmed.sub(pos));
 | 
						|
        }
 | 
						|
        if(numnewlines_at_end)
 | 
						|
        {
 | 
						|
            this->Writer::_do_write('\n');
 | 
						|
            --numnewlines_at_end;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    for(size_t i = 0; i < numnewlines_at_end; ++i)
 | 
						|
    {
 | 
						|
        _rymlindent_nextline()
 | 
						|
        if(i+1 < numnewlines_at_end || explicit_key)
 | 
						|
            this->Writer::_do_write('\n');
 | 
						|
    }
 | 
						|
    if(explicit_key && !numnewlines_at_end)
 | 
						|
        this->Writer::_do_write('\n');
 | 
						|
}
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_write_scalar_squo(csubstr s, size_t ilevel)
 | 
						|
{
 | 
						|
    size_t pos = 0; // tracks the last character that was already written
 | 
						|
    this->Writer::_do_write('\'');
 | 
						|
    for(size_t i = 0; i < s.len; ++i)
 | 
						|
    {
 | 
						|
        if(s[i] == '\n')
 | 
						|
        {
 | 
						|
            csubstr sub = s.range(pos, i+1);
 | 
						|
            this->Writer::_do_write(sub);  // write everything up to (including) this char
 | 
						|
            this->Writer::_do_write('\n'); // write the character again
 | 
						|
            if(i + 1 < s.len)
 | 
						|
                _rymlindent_nextline()     // indent the next line
 | 
						|
            pos = i+1;
 | 
						|
        }
 | 
						|
        else if(s[i] == '\'')
 | 
						|
        {
 | 
						|
            csubstr sub = s.range(pos, i+1);
 | 
						|
            this->Writer::_do_write(sub); // write everything up to (including) this char
 | 
						|
            this->Writer::_do_write('\''); // write the character again
 | 
						|
            pos = i+1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // write missing characters at the end of the string
 | 
						|
    if(pos < s.len)
 | 
						|
        this->Writer::_do_write(s.sub(pos));
 | 
						|
    this->Writer::_do_write('\'');
 | 
						|
}
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_write_scalar_dquo(csubstr s, size_t ilevel)
 | 
						|
{
 | 
						|
    size_t pos = 0; // tracks the last character that was already written
 | 
						|
    this->Writer::_do_write('"');
 | 
						|
    for(size_t i = 0; i < s.len; ++i)
 | 
						|
    {
 | 
						|
        const char curr = s.str[i];
 | 
						|
        if(curr == '"' || curr == '\\')
 | 
						|
        {
 | 
						|
            csubstr sub = s.range(pos, i);
 | 
						|
            this->Writer::_do_write(sub);  // write everything up to (excluding) this char
 | 
						|
            this->Writer::_do_write('\\'); // write the escape
 | 
						|
            this->Writer::_do_write(curr); // write the char
 | 
						|
            pos = i+1;
 | 
						|
        }
 | 
						|
        else if(s[i] == '\n')
 | 
						|
        {
 | 
						|
            csubstr sub = s.range(pos, i+1);
 | 
						|
            this->Writer::_do_write(sub);  // write everything up to (including) this newline
 | 
						|
            this->Writer::_do_write('\n'); // write the newline again
 | 
						|
            if(i + 1 < s.len)
 | 
						|
                _rymlindent_nextline()     // indent the next line
 | 
						|
            pos = i+1;
 | 
						|
            if(i+1 < s.len) // escape leading whitespace after the newline
 | 
						|
            {
 | 
						|
                const char next = s.str[i+1];
 | 
						|
                if(next == ' ' || next == '\t')
 | 
						|
                    this->Writer::_do_write('\\');
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if(curr == ' ' || curr == '\t')
 | 
						|
        {
 | 
						|
            // escape trailing whitespace before a newline
 | 
						|
            size_t next = s.first_not_of(" \t\r", i);
 | 
						|
            if(next != npos && s[next] == '\n')
 | 
						|
            {
 | 
						|
                csubstr sub = s.range(pos, i);
 | 
						|
                this->Writer::_do_write(sub);  // write everything up to (excluding) this char
 | 
						|
                this->Writer::_do_write('\\'); // escape the whitespace
 | 
						|
                pos = i;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if(C4_UNLIKELY(curr == '\r'))
 | 
						|
        {
 | 
						|
            csubstr sub = s.range(pos, i);
 | 
						|
            this->Writer::_do_write(sub);  // write everything up to (excluding) this char
 | 
						|
            this->Writer::_do_write("\\r"); // write the escaped char
 | 
						|
            pos = i+1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // write missing characters at the end of the string
 | 
						|
    if(pos < s.len)
 | 
						|
    {
 | 
						|
        csubstr sub = s.sub(pos);
 | 
						|
        this->Writer::_do_write(sub);
 | 
						|
    }
 | 
						|
    this->Writer::_do_write('"');
 | 
						|
}
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_write_scalar_plain(csubstr s, size_t ilevel)
 | 
						|
{
 | 
						|
    size_t pos = 0; // tracks the last character that was already written
 | 
						|
    for(size_t i = 0; i < s.len; ++i)
 | 
						|
    {
 | 
						|
        const char curr = s.str[i];
 | 
						|
        if(curr == '\n')
 | 
						|
        {
 | 
						|
            csubstr sub = s.range(pos, i+1);
 | 
						|
            this->Writer::_do_write(sub);  // write everything up to (including) this newline
 | 
						|
            this->Writer::_do_write('\n'); // write the newline again
 | 
						|
            if(i + 1 < s.len)
 | 
						|
                _rymlindent_nextline()     // indent the next line
 | 
						|
            pos = i+1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // write missing characters at the end of the string
 | 
						|
    if(pos < s.len)
 | 
						|
    {
 | 
						|
        csubstr sub = s.sub(pos);
 | 
						|
        this->Writer::_do_write(sub);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#undef _rymlindent_nextline
 | 
						|
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_write_scalar(csubstr s, bool was_quoted)
 | 
						|
{
 | 
						|
    // this block of code needed to be moved to before the needs_quotes
 | 
						|
    // assignment to work around a g++ optimizer bug where (s.str != nullptr)
 | 
						|
    // was evaluated as true even if s.str was actually a nullptr (!!!)
 | 
						|
    if(s.len == size_t(0))
 | 
						|
    {
 | 
						|
        if(was_quoted || s.str != nullptr)
 | 
						|
            this->Writer::_do_write("''");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    const bool needs_quotes = (
 | 
						|
        was_quoted
 | 
						|
        ||
 | 
						|
        (
 | 
						|
            ( ! s.is_number())
 | 
						|
            &&
 | 
						|
            (
 | 
						|
                // has leading whitespace
 | 
						|
                // looks like reference or anchor
 | 
						|
                // would be treated as a directive
 | 
						|
                // see https://www.yaml.info/learn/quote.html#noplain
 | 
						|
                s.begins_with_any(" \n\t\r*&%@`")
 | 
						|
                ||
 | 
						|
                s.begins_with("<<")
 | 
						|
                ||
 | 
						|
                // has trailing whitespace
 | 
						|
                s.ends_with_any(" \n\t\r")
 | 
						|
                ||
 | 
						|
                // has special chars
 | 
						|
                (s.first_of("#:-?,\n{}[]'\"") != npos)
 | 
						|
            )
 | 
						|
        )
 | 
						|
    );
 | 
						|
 | 
						|
    if( ! needs_quotes)
 | 
						|
    {
 | 
						|
        this->Writer::_do_write(s);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        const bool has_dquotes = s.first_of( '"') != npos;
 | 
						|
        const bool has_squotes = s.first_of('\'') != npos;
 | 
						|
        if(!has_squotes && has_dquotes)
 | 
						|
        {
 | 
						|
            this->Writer::_do_write('\'');
 | 
						|
            this->Writer::_do_write(s);
 | 
						|
            this->Writer::_do_write('\'');
 | 
						|
        }
 | 
						|
        else if(has_squotes && !has_dquotes)
 | 
						|
        {
 | 
						|
            RYML_ASSERT(s.count('\n') == 0);
 | 
						|
            this->Writer::_do_write('"');
 | 
						|
            this->Writer::_do_write(s);
 | 
						|
            this->Writer::_do_write('"');
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            _write_scalar_squo(s, /*FIXME FIXME FIXME*/0);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
template<class Writer>
 | 
						|
void Emitter<Writer>::_write_scalar_json(csubstr s, bool as_key, bool use_quotes)
 | 
						|
{
 | 
						|
    if((!use_quotes)
 | 
						|
       // json keys require quotes
 | 
						|
       && (!as_key)
 | 
						|
       && (
 | 
						|
           // do not quote special cases
 | 
						|
           (s == "true" || s == "false" || s == "null")
 | 
						|
           || (
 | 
						|
               // do not quote numbers
 | 
						|
               (s.is_number()
 | 
						|
                && (
 | 
						|
                    // quote integral numbers if they have a leading 0
 | 
						|
                    // https://github.com/biojppm/rapidyaml/issues/291
 | 
						|
                    (!(s.len > 1 && s.begins_with('0')))
 | 
						|
                    // do not quote reals with leading 0
 | 
						|
                    // https://github.com/biojppm/rapidyaml/issues/313
 | 
						|
                    || (s.find('.') != csubstr::npos) ))
 | 
						|
               )
 | 
						|
           )
 | 
						|
        )
 | 
						|
    {
 | 
						|
        this->Writer::_do_write(s);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        size_t pos = 0;
 | 
						|
        this->Writer::_do_write('"');
 | 
						|
        for(size_t i = 0; i < s.len; ++i)
 | 
						|
        {
 | 
						|
            switch(s.str[i])
 | 
						|
            {
 | 
						|
            case '"':
 | 
						|
              this->Writer ::_do_write(s.range(pos, i));
 | 
						|
              this->Writer ::_do_write("\\\"");
 | 
						|
              pos = i + 1;
 | 
						|
              break;
 | 
						|
            case '\n':
 | 
						|
              this->Writer ::_do_write(s.range(pos, i));
 | 
						|
              this->Writer ::_do_write("\\n");
 | 
						|
              pos = i + 1;
 | 
						|
              break;
 | 
						|
            case '\t':
 | 
						|
              this->Writer ::_do_write(s.range(pos, i));
 | 
						|
              this->Writer ::_do_write("\\t");
 | 
						|
              pos = i + 1;
 | 
						|
              break;
 | 
						|
            case '\\':
 | 
						|
              this->Writer ::_do_write(s.range(pos, i));
 | 
						|
              this->Writer ::_do_write("\\\\");
 | 
						|
              pos = i + 1;
 | 
						|
              break;
 | 
						|
            case '\r':
 | 
						|
              this->Writer ::_do_write(s.range(pos, i));
 | 
						|
              this->Writer ::_do_write("\\r");
 | 
						|
              pos = i + 1;
 | 
						|
              break;
 | 
						|
            case '\b':
 | 
						|
              this->Writer ::_do_write(s.range(pos, i));
 | 
						|
              this->Writer ::_do_write("\\b");
 | 
						|
              pos = i + 1;
 | 
						|
              break;
 | 
						|
            case '\f':
 | 
						|
              this->Writer ::_do_write(s.range(pos, i));
 | 
						|
              this->Writer ::_do_write("\\f");
 | 
						|
              pos = i + 1;
 | 
						|
              break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if(pos < s.len)
 | 
						|
        {
 | 
						|
            csubstr sub = s.sub(pos);
 | 
						|
            this->Writer::_do_write(sub);
 | 
						|
        }
 | 
						|
        this->Writer::_do_write('"');
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
} // namespace yml
 | 
						|
} // namespace c4
 | 
						|
 | 
						|
#endif /* _C4_YML_EMIT_DEF_HPP_ */
 |