Emitter: internal cleanup

This commit is contained in:
Joao Paulo Magalhaes
2025-10-21 02:29:32 +01:00
parent 6833a4b570
commit 07d02b925e
4 changed files with 104 additions and 79 deletions

View File

@@ -38,7 +38,7 @@ substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& tree, id_type id, b
_emit_json(id);
}
else
_RYML_ERR_BASIC_(m_tree->callbacks(), "unknown emit type");
_RYML_ERR_BASIC_(m_tree->callbacks(), "unknown emit type"); // LCOV_EXCL_LINE
m_tree = nullptr;
return this->Writer::_get(error_on_excess);
}
@@ -823,14 +823,14 @@ template<class Writer>
void Emitter<Writer>::_flow_write_scalar_key(id_type id)
{
enum : type_bits {
_block_styles = KEY_LITERAL|KEY_FOLDED,
_flow_styles = KEY_STYLE & ~_block_styles,
_block_styles = (KEY_LITERAL|KEY_FOLDED),
_flow_styles = KEY_STYLE & ~(KEY_LITERAL|KEY_FOLDED),
};
csubstr str = m_tree->key(id);
NodeType ty = m_tree->type(id) & static_cast<NodeType_e>(_flow_styles);
if(!(ty & KEY_STYLE))
ty |= scalar_style_choose(str);
_RYML_ASSERT_VISIT_(m_tree->callbacks(), !(ty & _block_styles), m_tree, id);
_RYML_ASSERT_VISIT_(m_tree->callbacks(), !(ty & (KEY_LITERAL|KEY_FOLDED)), m_tree, id);
if(ty & KEY_PLAIN)
{
if(C4_LIKELY(!(str.begins_with(": ") || str.begins_with(":\t"))))
@@ -857,13 +857,13 @@ void Emitter<Writer>::_flow_write_scalar_val(id_type id)
{
enum : type_bits {
_block_styles = VAL_LITERAL|VAL_FOLDED,
_flow_styles = VAL_STYLE & ~_block_styles,
_flow_styles = (VAL_STYLE) & (~(VAL_LITERAL|VAL_FOLDED)),
};
csubstr str = m_tree->val(id);
NodeType ty = m_tree->type(id) & static_cast<NodeType_e>(_flow_styles);
if(!(ty & VAL_STYLE))
ty |= scalar_style_choose(str);
_RYML_ASSERT_VISIT_(m_tree->callbacks(), !(ty & static_cast<NodeType_e>(_block_styles)), m_tree, id);
_RYML_ASSERT_VISIT_(m_tree->callbacks(), !(ty & (VAL_LITERAL|VAL_FOLDED)), m_tree, id);
if(ty & VAL_PLAIN)
{
if(C4_LIKELY(!(str.begins_with(": ") || str.begins_with(":\t"))))
@@ -889,7 +889,7 @@ template<class Writer>
void Emitter<Writer>::_blck_write_scalar_key(id_type id)
{
csubstr key = m_tree->key(id);
NodeType ty = m_tree->type(id) & static_cast<NodeType_e>(_keysc);
NodeType ty = m_tree->type(id);
if(!(ty & KEY_STYLE))
ty |= scalar_style_choose(key);
if(ty & KEY_PLAIN)
@@ -925,7 +925,7 @@ template<class Writer>
void Emitter<Writer>::_blck_write_scalar_val(id_type id)
{
csubstr str = m_tree->val(id);
NodeType ty = m_tree->type(id) & static_cast<NodeType_e>(_valsc);
NodeType ty = m_tree->type(id);
if(!(ty & VAL_STYLE))
ty |= scalar_style_choose(str);
if(ty & VAL_PLAIN)
@@ -1305,8 +1305,8 @@ void Emitter<Writer>::_write_scalar_plain(csubstr s, id_type ilevel)
}
//-----------------------------------------------------------------------------
template<class Writer>
void Emitter<Writer>::_emit_json(id_type id)
{
@@ -1331,19 +1331,19 @@ void Emitter<Writer>::_visit_json_sl(id_type id, id_type depth)
NodeType ty = m_tree->type(id);
if(ty.is_keyval())
{
_writek_json(id);
_writek_json(id, ty);
_write(": ");
_writev_json(id);
_writev_json(id, ty);
}
else if(ty.is_val())
{
_writev_json(id);
_writev_json(id, ty);
}
else if(ty.is_container())
{
if(ty.has_key())
{
_writek_json(id);
_writek_json(id, ty);
_write(": ");
}
if(ty.is_seq())
@@ -1374,19 +1374,19 @@ void Emitter<Writer>::_visit_json_ml(id_type id, id_type depth)
NodeType ty = m_tree->type(id);
if(ty.is_keyval())
{
_writek_json(id);
_writek_json(id, ty);
_write(": ");
_writev_json(id);
_writev_json(id, ty);
}
else if(ty.is_val())
{
_writev_json(id);
_writev_json(id, ty);
}
else if(ty.is_container())
{
if(ty.has_key())
{
_writek_json(id);
_writek_json(id, ty);
_write(": ");
}
if(ty.is_seq())
@@ -1426,29 +1426,44 @@ void Emitter<Writer>::_visit_json_ml(id_type id, id_type depth)
}
template<class Writer>
void Emitter<Writer>::_write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags)
void Emitter<Writer>::_writek_json(id_type id, NodeType ty)
{
if(m_opts.json_error_flags() & EmitOptions::JSON_ERR_ON_TAG)
if(C4_UNLIKELY(flags & (KEYTAG|VALTAG)))
_RYML_ERR_VISIT_(m_tree->callbacks(), m_tree, NONE, "JSON does not have tags");
if(C4_UNLIKELY(ty.has_key_tag()))
_RYML_ERR_VISIT_(m_tree->callbacks(), m_tree, id, "JSON does not have tags");
if(m_opts.json_error_flags() & EmitOptions::JSON_ERR_ON_ANCHOR)
if(C4_UNLIKELY(flags.has_anchor()))
_RYML_ERR_VISIT_(m_tree->callbacks(), m_tree, NONE, "JSON does not have anchors");
if(sc.scalar.len)
if(C4_UNLIKELY(ty.has_key_anchor()))
_RYML_ERR_VISIT_(m_tree->callbacks(), m_tree, id, "JSON does not have anchors");
csubstr key = m_tree->key(id);
if(key.len)
_write_scalar_json_dquo(key);
else
_write("\"\"");
}
template<class Writer>
void Emitter<Writer>::_writev_json(id_type id, NodeType ty)
{
if(m_opts.json_error_flags() & EmitOptions::JSON_ERR_ON_TAG)
if(C4_UNLIKELY(ty.has_val_tag()))
_RYML_ERR_VISIT_(m_tree->callbacks(), m_tree, id, "JSON does not have tags");
if(m_opts.json_error_flags() & EmitOptions::JSON_ERR_ON_ANCHOR)
if(C4_UNLIKELY(ty.has_val_anchor()))
_RYML_ERR_VISIT_(m_tree->callbacks(), m_tree, id, "JSON does not have anchors");
csubstr val = m_tree->val(id);
if(val.len)
{
// use double quoted style...
// if it is a key (mandatory in JSON)
// if the style is marked quoted
bool dquoted = ((flags & (KEY|VALQUO))
|| (scalar_style_json_choose(sc.scalar) & SCALAR_DQUO)); // choose the style
// use double quoted style if the style is marked quoted
bool dquoted = ((ty & VALQUO)
|| (scalar_style_json_choose(val) & SCALAR_DQUO)); // choose the style
if(dquoted)
_write_scalar_json_dquo(sc.scalar);
_write_scalar_json_dquo(val);
else
_write(sc.scalar);
_write(val);
}
else
{
if(sc.scalar.str || (flags & (KEY|VALQUO|KEYTAG|VALTAG)))
if(val.str || (ty & (VALQUO|VALTAG)))
_write("\"\"");
else
_write("null");

View File

@@ -131,19 +131,35 @@ class Emitter : public Writer
{
public:
/** Construct the emitter and its internal Writer state, using default emit options.
* @param args arguments to be forwarded to the constructor of the writer.
*/
template<class ...Args>
Emitter(Args &&...args) : Writer(std::forward<Args>(args)...), m_tree(), m_opts() {}
/** Construct the emitter and its internal Writer state.
*
* @param opts @ref EmitOptions
* @param args arguments to be forwarded to the constructor of the writer.
*/
template<class ...Args>
Emitter(EmitOptions const& opts, Args &&...args) : Writer(std::forward<Args>(args)...), m_tree(), m_opts(opts) {}
Emitter(EmitOptions const& opts, Args &&...args)
: Writer(std::forward<Args>(args)...)
, m_tree()
, m_opts(opts)
, m_col()
, m_depth()
, m_ilevel()
, m_pws()
{}
/** Construct the emitter and its internal Writer state, using default emit options.
* @param args arguments to be forwarded to the constructor of the writer.
*/
template<class ...Args>
Emitter(Args &&...args)
: Writer(std::forward<Args>(args)...)
, m_tree()
, m_opts()
, m_col()
, m_depth()
, m_ilevel()
, m_pws()
{}
public:
@@ -158,7 +174,7 @@ public:
* When writing to a file, the returned substr will be null, but its
* length will be set to the number of bytes written.
*
* @param type specify what to emit
* @param type specify what to emit (YAML or JSON)
* @param t the tree to emit
* @param id the id of the node to emit
* @param error_on_excess when true, an error is raised when the
@@ -192,21 +208,8 @@ public:
/** get the max depth for emitted trees (to prevent a stack overflow) */
id_type max_depth() const noexcept { return m_opts.max_depth(); }
private:
/** @cond dev */
typedef enum : uint32_t { _PWS_NONE, _PWS_SPACE, _PWS_NEWL } Pws_e; ///< pending whitespace
private:
Tree const* C4_RESTRICT m_tree;
EmitOptions m_opts;
size_t m_col;
id_type m_depth;
id_type m_ilevel;
Pws_e m_pws;
private:
void _emit_yaml(id_type id);
@@ -229,12 +232,12 @@ private:
void _visit_blck_seq(id_type id);
void _visit_blck_map(id_type id);
void _top_open_entry(id_type id);
void _top_close_entry(id_type id);
void _blck_seq_open_entry(id_type id);
void _blck_seq_close_entry(id_type id);
void _blck_map_open_entry(id_type id);
void _blck_map_close_entry(id_type id);
void _top_open_entry(id_type id);
void _top_close_entry(id_type id);
void _blck_write_scalar_key(id_type id);
void _blck_write_scalar_val(id_type id);
@@ -251,20 +254,24 @@ private:
private:
void _emit_json(id_type id);
void _visit_json_ml(id_type id, id_type depth);
void _visit_json_sl(id_type id, id_type depth);
void _write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags);
void _write_scalar_json_dquo(csubstr s);
void _write_scalar_literal(csubstr s, id_type level, bool as_key);
void _write_scalar_folded(csubstr s, id_type level, bool as_key);
void _write_scalar_squo(csubstr s, id_type level);
void _write_scalar_dquo(csubstr s, id_type level);
void _write_scalar_plain(csubstr s, id_type level);
size_t _write_escaped_newlines(csubstr s, size_t i);
size_t _write_indented_block(csubstr s, size_t i, id_type level);
private:
void _visit_json_ml(id_type id, id_type depth);
void _visit_json_sl(id_type id, id_type depth);
void _writek_json(id_type id, NodeType ty);
void _writev_json(id_type id, NodeType ty);
void _write_scalar_json_dquo(csubstr s);
private:
void _write_tag(csubstr tag)
{
if(!tag.begins_with('!'))
@@ -281,21 +288,6 @@ private:
}
}
enum : type_bits {
_keysc = (KEY|KEYREF|KEYANCH|KEYQUO|KEY_STYLE) | ~(VAL|VALREF|VALANCH|VALQUO|VAL_STYLE) | CONTAINER_STYLE,
_valsc = ~(KEY|KEYREF|KEYANCH|KEYQUO|KEY_STYLE) | (VAL|VALREF|VALANCH|VALQUO|VAL_STYLE) | CONTAINER_STYLE,
_keysc_json = (KEY) | ~(VAL),
_valsc_json = ~(KEY) | (VAL),
};
C4_ALWAYS_INLINE void _writek_json(id_type id) { _write_json(m_tree->keysc(id), m_tree->_p(id)->m_type.type & ~(VAL)); }
C4_ALWAYS_INLINE void _writev_json(id_type id) { _write_json(m_tree->valsc(id), m_tree->_p(id)->m_type.type & ~(KEY)); }
void _indent(id_type level, bool enabled)
{
if(enabled)
_write(' ', 2u * (size_t)level);
}
void _indent(id_type level)
{
_write(' ', 2u * (size_t)level);
@@ -329,7 +321,9 @@ private:
this->Writer::_do_write(c, num);
}
private:
private: // pending whitespace
typedef enum : uint32_t { _PWS_NONE, _PWS_SPACE, _PWS_NEWL } Pws_e; ///< pending whitespace
/// set pending whitespace, ignoring pending
C4_ALWAYS_INLINE void _pend_none() noexcept
@@ -361,6 +355,15 @@ private:
m_pws = next;
}
private:
Tree const* C4_RESTRICT m_tree;
EmitOptions m_opts;
size_t m_col;
id_type m_depth;
id_type m_ilevel;
Pws_e m_pws;
/** @endcond */
};

View File

@@ -169,7 +169,7 @@ RYML_EXPORT Tree parse_json_in_place( substr json
* substr mutable_buffer = ...;
* parser.parse_in_arena(mutable_buffer); // linker error
*
* csubstr immutable_buffer = ...;
* csubstr immutable_buffer = mutable_buffer; // convert first to csubstr
* parser.parse_in_arena(immutable_buffer); // ok
* ```
*
@@ -263,8 +263,8 @@ RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) Tree parse_json_in_arena(Parser
* substr mutable_buffer = ...;
* parser.parse_in_arena(mutable_buffer); // linker error
*
* csubstr immutable_buffer = ...;
* parser.parse_in_arena(immutable_buffer); // ok
* csubstr immutable_buffer = mutable_buffer; // convert first to csubstr
* parser.parse_in_arena(immutable_buffer); // ok now
* ```
*
* @{

View File

@@ -731,6 +731,13 @@ TEST(parse_json, seq_nested_on_seq_with_trailing_comma)
}
TEST(emit_json, empty_val)
{
Tree t = parse_in_arena("a: \nb: \"\"\nc: !!tag\nd: !!tag e");
EXPECT_EQ(emitrs_json<std::string>(t), "{\n \"a\": null,\n \"b\": \"\",\n \"c\": \"\",\n \"d\": \"e\"\n}\n");
}
//-------------------------------------------
// this is needed to use the test case library
Case const* get_case(csubstr /*name*/)