comments wip [ci skip]

This commit is contained in:
Joao Paulo Magalhaes
2025-11-10 18:33:23 +00:00
parent 1dc73277d0
commit b0bffa57b3
12 changed files with 596 additions and 119 deletions

View File

@@ -43,6 +43,7 @@ c4_add_library(ryml
c4/yml/detail/print.hpp
c4/yml/detail/stack.hpp
c4/yml/comment_type.hpp
c4/yml/comment_type.cpp
c4/yml/common.hpp
c4/yml/common.cpp
c4/yml/emit.def.hpp

View File

@@ -0,0 +1,23 @@
#ifdef RYML_WITH_COMMENTS
#include "c4/yml/comment_type.hpp"
namespace c4 {
namespace yml {
const char *comment_type_str(CommentType_e type)
{
switch(type)
{
#define _c4comm(comm_symbol, bit) case COMM_##comm_symbol: return #comm_symbol;
_RYML_DEFINE_COMMENTS(_c4comm)
default: break;
#undef _c4comm
}
return "";
}
} // namespace yml
} // namespace c4
#endif // RYML_WITH_COMMENTS

View File

@@ -152,6 +152,9 @@ typedef enum : comment_data_type {
#undef _c4comm
} CommentType_e;
const char *comment_type_str(CommentType_e type);
/** @} */
} // namespace yml

View File

@@ -183,6 +183,16 @@ static_assert(RYML_LOGBUF_SIZE < RYML_ERRMSG_SIZE, "invalid size");
#define RYML_DEPRECATED(msg) C4_DEPRECATED(msg)
#if defined(RYML_WITH_COMMENTS)
#define _RYML_WITH_COMMENTS(...) __VA_ARGS__
#define _RYML_WITHOUT_COMMENTS(...)
#define _RYML_WITH_OR_WITHOUT_COMMENTS(with, without) with
#else
#define _RYML_WITH_COMMENTS(...)
#define _RYML_WITHOUT_COMMENTS(...) __VA_ARGS__
#define _RYML_WITH_OR_WITHOUT_COMMENTS(with, without) without
#endif
/** @endcond */
@@ -321,15 +331,17 @@ private:
DETECT_FLOW_ML = (1u << 2u),
#ifdef RYML_WITH_COMMENTS
WITH_COMMENTS = (1u << 3u),
WITH_COMMENT_SEP = (1u << 4u),
#endif
DEFAULTS = SCALAR_FILTERING|DETECT_FLOW_ML,
DEFAULTS = SCALAR_FILTERING|DETECT_FLOW_ML _RYML_WITH_COMMENTS(|WITH_COMMENT_SEP),
} Flags_e;
uint32_t flags = DEFAULTS;
uint32_t m_flags;
char m_comment_sep;
public:
ParserOptions() = default;
ParserOptions() noexcept : m_flags(DEFAULTS), m_comment_sep('~') {}
public:
@@ -340,13 +352,13 @@ public:
ParserOptions& locations(bool enabled) noexcept
{
if(enabled)
flags |= LOCATIONS;
m_flags |= LOCATIONS;
else
flags &= ~LOCATIONS;
m_flags &= ~LOCATIONS;
return *this;
}
/** query source location tracking status */
C4_ALWAYS_INLINE bool locations() const noexcept { return (flags & LOCATIONS); }
C4_ALWAYS_INLINE bool locations() const noexcept { return (m_flags & LOCATIONS); }
/** @} */
@@ -362,13 +374,13 @@ public:
ParserOptions& detect_flow_ml(bool enabled) noexcept
{
if(enabled)
flags |= DETECT_FLOW_ML;
m_flags |= DETECT_FLOW_ML;
else
flags &= ~DETECT_FLOW_ML;
m_flags &= ~DETECT_FLOW_ML;
return *this;
}
/** query status of detection of @ref FLOW_ML container style. */
C4_ALWAYS_INLINE bool detect_flow_ml() const noexcept { return (flags & DETECT_FLOW_ML); }
C4_ALWAYS_INLINE bool detect_flow_ml() const noexcept { return (m_flags & DETECT_FLOW_ML); }
/** @} */
@@ -381,13 +393,13 @@ public:
ParserOptions& scalar_filtering(bool enabled) noexcept
{
if(enabled)
flags |= SCALAR_FILTERING;
m_flags |= SCALAR_FILTERING;
else
flags &= ~SCALAR_FILTERING;
m_flags &= ~SCALAR_FILTERING;
return *this;
}
/** query scalar filtering status */
C4_ALWAYS_INLINE bool scalar_filtering() const noexcept { return (flags & SCALAR_FILTERING); }
C4_ALWAYS_INLINE bool scalar_filtering() const noexcept { return (m_flags & SCALAR_FILTERING); }
/** @} */
@@ -402,14 +414,39 @@ public:
ParserOptions& with_comments(bool enabled) noexcept
{
if(enabled)
flags |= WITH_COMMENTS;
m_flags |= WITH_COMMENTS;
else
flags &= ~WITH_COMMENTS;
m_flags &= ~WITH_COMMENTS;
return *this;
}
/** query comments parsing status. available only when @ref
* RYML_WITH_COMMENTS is defined */
C4_ALWAYS_INLINE bool with_comments() const noexcept { return (flags & WITH_COMMENTS); }
C4_ALWAYS_INLINE bool with_comments() const noexcept { return (m_flags & WITH_COMMENTS); }
/** enable/disable comment separation token (see @ref
* comment_sep()). available only when @ref RYML_WITH_COMMENTS is
* defined */
ParserOptions& with_comment_sep(bool enabled) noexcept
{
if(enabled)
m_flags |= WITH_COMMENT_SEP;
else
m_flags &= ~WITH_COMMENT_SEP;
return *this;
}
/** query status of comment separation token (see @ref
* comment_sep()). available only when @ref RYML_WITH_COMMENTS is
* defined */
C4_ALWAYS_INLINE bool with_comment_sep() const noexcept { return (m_flags & WITH_COMMENT_SEP); }
/** set the comment separation character. default is `~` */
ParserOptions& comment_sep(char sep) noexcept
{
m_comment_sep = sep;
return *this;
}
/** get the comment separation character. */
C4_ALWAYS_INLINE char comment_sep() const noexcept { return m_comment_sep; }
/** @} */
#endif // RYML_WITH_COMMENTS
@@ -609,16 +646,6 @@ inline csubstr _c4prc(const char &C4_RESTRICT c) // pass by reference!
}
}
#if defined(RYML_WITH_COMMENTS)
#define _RYML_WITH_COMMENTS(...) __VA_ARGS__
#define _RYML_WITHOUT_COMMENTS(...)
#define _RYML_WITH_OR_WITHOUT_COMMENTS(with, without) with
#else
#define _RYML_WITH_COMMENTS(...)
#define _RYML_WITHOUT_COMMENTS(...) __VA_ARGS__
#define _RYML_WITH_OR_WITHOUT_COMMENTS(with, without) without
#endif
/// @endcond
C4_SUPPRESS_WARNING_GCC_POP

View File

@@ -197,7 +197,7 @@ void Emitter<Writer>::_visit_stream(id_type id)
{
_write_pws_and_pend(_PWS_NONE);
_write("...");
_write_comm_trailing(comm);
_write_comm_trailing(comm, /*with_sep*/false);
}
_write_comm_leading(id, COMM_FOOTER);
#endif
@@ -458,10 +458,28 @@ void Emitter<Writer>::_flow_close_entry_sl(id_type node, id_type last_sibling)
{
_RYML_WITH_COMMENTS(CommentData const *commvt = _comm_get(node, COMM_VAL_TRAILING));
_RYML_WITH_COMMENTS(CommentData const *commcl = _comm_get(node, COMM_COMMA_LEADING));
_write("!!!");
_write(commvt ? "!!!" : "---");
if(node != last_sibling _RYML_WITH_COMMENTS(|| commvt || commcl))
{
_RYML_WITH_COMMENTS(if(commvt) _write_comm_trailing(commvt));
_RYML_WITH_COMMENTS(if(commcl) _write_comm_leading(commcl));
_write("???");
#ifdef RYML_WITH_COMMENTS
if(commvt && commcl)
{
_write("wtf");
_write_comm_trailing(commvt, true);
_write_comm_leading(commcl, false);
_write("crl");
}
else if(commvt)
{
_write_comm_trailing(commvt, false);
}
else if(commcl)
{
_write_comm_leading(commcl, false);
}
#endif
_write_pws_and_pend(_PWS_NONE);
_write(',');
}
@@ -541,7 +559,8 @@ void Emitter<Writer>::_blck_write_qmrk(id_type node, csubstr key, type_bits ty,
nested = true; \
++m_ilevel; \
} \
_write_comm_leading(comm); \
bool with_sep = (m_opts.comments_sep() && _comm_needs_sep(node, commtype)); \
_write_comm_leading(comm, with_sep); \
} \
} while(0)
#define _ryml_write_comm_trailing_and_nest(node, commtype) \
@@ -1470,7 +1489,7 @@ void Emitter<Writer>::_write_scalar_plain(csubstr s, id_type ilevel)
#ifdef RYML_WITH_COMMENTS
template<class Writer>
void Emitter<Writer>::_write_comm(csubstr s, id_type indentation)
void Emitter<Writer>::_write_comm(csubstr s, id_type indentation, bool with_sep)
{
_write('#');
size_t pos = 0; // last character that was already written
@@ -1492,10 +1511,14 @@ void Emitter<Writer>::_write_comm(csubstr s, id_type indentation)
csubstr sub = s.sub(pos);
_write(sub);
}
if(with_sep)
{
_write(" ~");
}
}
template<class Writer>
void Emitter<Writer>::_write_comm_leadspace(csubstr s, id_type indentation)
void Emitter<Writer>::_write_comm_leadspace(csubstr s, id_type indentation, bool with_sep)
{
_write('#');
size_t pos = 0; // last character that was already written
@@ -1521,32 +1544,10 @@ void Emitter<Writer>::_write_comm_leadspace(csubstr s, id_type indentation)
_write(' ');
_write(sub);
}
}
template<class Writer>
void Emitter<Writer>::_write_comm_trailing(CommentData const* comm)
{
_write(' ');
if(!m_opts.comments_add_leading_space())
_write_comm(comm->m_text, m_col);
else
_write_comm_leadspace(comm->m_text, m_col);
_pend_newl();
}
template<class Writer>
void Emitter<Writer>::_write_comm_leading(CommentData const* comm)
{
if(!m_wsonly)
if(with_sep)
{
_newl();
_indent(m_ilevel);
_write(" ~");
}
if(!m_opts.comments_add_leading_space())
_write_comm(comm->m_text, m_col);
else
_write_comm_leadspace(comm->m_text, m_col);
_pend_newl();
}
template<class Writer>
@@ -1554,8 +1555,11 @@ CommentData const* Emitter<Writer>::_comm_get(id_type node, CommentType_e type,
{
if(!m_opts.comments())
return nullptr;
CommState *result = &m_comm_state.top();
#ifdef RYML_USE_ASSERT // ensure the queries are in order of CommentType
CommentState *result = &m_comm_state.top();
#ifdef RYML_USE_ASSERT
// ensure the queries are in order of CommentType. if this
// assertion fires, it means the emit code is looking for comments
// in the wrong order.
_RYML_ASSERT_VISIT_(m_tree->callbacks(), type >= result->latest_query, m_tree, node);
result->latest_query = type;
#endif
@@ -1570,6 +1574,50 @@ CommentData const* Emitter<Writer>::_comm_get(id_type node, CommentType_e type,
}
}
return result->comm;
}
template<class Writer>
void Emitter<Writer>::_write_comm_trailing(CommentData const* comm, bool with_sep)
{
_write(' ');
if(!m_opts.comments_add_leading_space())
_write_comm(comm->m_text, m_col, with_sep);
else
_write_comm_leadspace(comm->m_text, m_col, with_sep);
_pend_newl();
}
template<class Writer>
void Emitter<Writer>::_write_comm_leading(CommentData const* comm, bool with_sep)
{
if(!m_wsonly)
{
_newl();
_indent(m_ilevel);
}
if(!m_opts.comments_add_leading_space())
_write_comm(comm->m_text, m_col, with_sep);
else
_write_comm_leadspace(comm->m_text, m_col, with_sep);
_pend_newl();
}
template<class Writer>
bool Emitter<Writer>::_comm_needs_sep(id_type node, comment_data_type type) const
{
_RYML_ASSERT_BASIC_(m_tree->callbacks(), ((type) & (type - 1u)) == 0); // needs to be power of two
const comment_data_type consecutive_types = type | (type << 1u);
NodeData const* nd = m_tree->_p(node);
if((nd->m_comments & consecutive_types) == consecutive_types)
return true;
else if(type != COMM_TRAILING)
return false;
else if(nd->m_next_sibling != NONE)
return m_tree->_p(nd->m_next_sibling)->m_comments & COMM_LEADING;
else if(nd->m_parent != NONE)
return m_tree->_p(nd->m_parent)->m_comments & (COMM_LEADING|COMM_VAL_BRACKET_LEADING);
return false;
}
template<class Writer>
@@ -1577,7 +1625,10 @@ CommentData const* Emitter<Writer>::_write_comm_trailing(id_type node, CommentTy
{
CommentData const* comm = _comm_get(node, type, indent_extra);
if(comm)
_write_comm_trailing(comm);
{
bool with_sep = (m_opts.comments_sep() && _comm_needs_sep(node, type));
_write_comm_trailing(comm, with_sep);
}
return comm;
}
@@ -1586,7 +1637,9 @@ CommentData const* Emitter<Writer>::_write_comm_leading(id_type node, CommentTyp
{
CommentData const* comm = _comm_get(node, type, indent_extra);
if(comm)
_write_comm_leading(comm);
{
_write_comm_leading(comm, false);
}
return comm;
}
#endif // RYML_WITH_COMMENTS

View File

@@ -72,11 +72,12 @@ public:
EMIT_NONROOT_MARKUP = EMIT_NONROOT_KEY|EMIT_NONROOT_DASH,
INDENT_FLOW_ML = 1u << 2u,
COMMENTS = 1u << 3u, ///< enable commments in emitted code
COMMENTS_ADD_LEADING_SPACE = 1u << 4u, ///< ensure every comment # is followed by a space, even if the comment does not start with space
JSON_ERR_ON_TAG = 1u << 5u,
JSON_ERR_ON_ANCHOR = 1u << 6u,
COMMENTS_ADD_LEADING_SPACE = 1u << 4u, ///< ensure every non-empty comment line has a space after #, even if the comment does not start with space
COMMENTS_SEP = 1u << 5u, ///< add comment separator for consecutive comments that would be ambiguous otherwise
JSON_ERR_ON_TAG = 1u << 6u,
JSON_ERR_ON_ANCHOR = 1u << 7u,
_JSON_ERR_MASK = JSON_ERR_ON_TAG|JSON_ERR_ON_ANCHOR,
DEFAULT_FLAGS = EMIT_NONROOT_KEY|INDENT_FLOW_ML|COMMENTS|COMMENTS_ADD_LEADING_SPACE,
DEFAULT_FLAGS = EMIT_NONROOT_KEY|INDENT_FLOW_ML|COMMENTS|COMMENTS_ADD_LEADING_SPACE|COMMENTS_SEP,
} EmitOptionFlags_e;
public:
@@ -97,6 +98,9 @@ public:
C4_ALWAYS_INLINE bool comments_add_leading_space() const noexcept { return (m_option_flags & COMMENTS_ADD_LEADING_SPACE) != 0; }
EmitOptions& comments_add_leading_space(bool enabled) noexcept { m_option_flags = (EmitOptionFlags_e)(enabled ? (m_option_flags | COMMENTS_ADD_LEADING_SPACE) : (m_option_flags & ~COMMENTS_ADD_LEADING_SPACE)); return *this; }
C4_ALWAYS_INLINE bool comments_sep() const noexcept { return (m_option_flags & COMMENTS_SEP) != 0; }
EmitOptions& comments_sep(bool enabled) noexcept { m_option_flags = (EmitOptionFlags_e)(enabled ? (m_option_flags | COMMENTS_SEP) : (m_option_flags & ~COMMENTS_SEP)); return *this; }
C4_ALWAYS_INLINE bool indent_flow_ml() const noexcept { return (m_option_flags & INDENT_FLOW_ML) != 0; }
EmitOptions& indent_flow_ml(bool enabled) noexcept { m_option_flags = (EmitOptionFlags_e)(enabled ? (m_option_flags | INDENT_FLOW_ML) : (m_option_flags & ~INDENT_FLOW_ML)); return *this; }
@@ -288,15 +292,16 @@ private:
private: // comments
#ifdef RYML_WITH_COMMENTS
C4_ALWAYS_INLINE void _comm_push() { m_comm_state.push(CommState{}); }
C4_ALWAYS_INLINE void _comm_push() { m_comm_state.push(CommentState{}); }
C4_ALWAYS_INLINE void _comm_pop() { m_ilevel -= m_comm_state.pop().extra_indentation; }
CommentData const* _comm_get(id_type node, CommentType_e type, bool indent_extra=false);
CommentData const* _write_comm_trailing(id_type node, CommentType_e type, bool indent_extra=false);
CommentData const* _write_comm_leading(id_type node, CommentType_e type, bool indent_extra=false);
void _write_comm_trailing(CommentData const* comm);
void _write_comm_leading(CommentData const* comm);
void _write_comm(csubstr s, id_type indentation);
void _write_comm_leadspace(csubstr s, id_type indentation);
void _write_comm_trailing(CommentData const* comm, bool with_sep);
void _write_comm_leading(CommentData const* comm, bool with_sep);
void _write_comm(csubstr s, id_type indentation, bool with_sep);
void _write_comm_leadspace(csubstr s, id_type indentation, bool with_sep);
bool _comm_needs_sep(id_type node, comment_data_type type) const;
#endif
private:
@@ -405,7 +410,7 @@ private:
Pws_e m_pws;
#ifdef RYML_WITH_COMMENTS
bool m_wsonly; ///< line contains only whitespace
struct CommState
struct CommentState
{
CommentData const* latest;
CommentData const* comm;
@@ -414,7 +419,7 @@ private:
CommentType_e latest_query;
#endif
};
detail::stack<CommState> m_comm_state;
detail::stack<CommentState> m_comm_state;
#endif
private:

View File

@@ -598,7 +598,7 @@ public:
void add_comment(csubstr txt, CommentType_e type)
{
NodeData * d = m_curr->tr_data;
_c4dbgpf("node[{}]: comment! type={} txt=[{}]~~~{}~~~", m_tree->id(d), type, txt.len, txt);
_c4dbgpf("node[{}]: comment! type={}({}) txt=[{}]~~~{}~~~", m_tree->id(d), comment_type_str(type), type, txt.len, txt);
if(type == COMM_VAL_BRACKET_TRAILING || type == COMM_VAL_BRACKET_LEADING)
{
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, m_parent && m_parent->tr_data->m_type.is_flow());

View File

@@ -266,7 +266,7 @@ ParseEngine<EventHandler>::ParseEngine(EventHandler *evt_handler, ParserOptions
, m_evt_handler(evt_handler)
, m_pending_anchors()
, m_pending_tags()
_RYML_WITH_COMMENTS(, m_pending_comment())
_RYML_WITH_COMMENTS(, m_pending_comments())
, m_was_inside_qmrk(false)
, m_doc_empty(false)
, m_prev_colon(npos)
@@ -287,7 +287,7 @@ ParseEngine<EventHandler>::ParseEngine(ParseEngine &&that) noexcept
, m_evt_handler(that.m_evt_handler)
, m_pending_anchors(that.m_pending_anchors)
, m_pending_tags(that.m_pending_tags)
_RYML_WITH_COMMENTS(, m_pending_comment(that.m_pending_comment))
_RYML_WITH_COMMENTS(, m_pending_comments(that.m_pending_comments))
, m_was_inside_qmrk(false)
, m_doc_empty(false)
, m_prev_colon(npos)
@@ -308,7 +308,7 @@ ParseEngine<EventHandler>::ParseEngine(ParseEngine const& that)
, m_evt_handler(that.m_evt_handler)
, m_pending_anchors(that.m_pending_anchors)
, m_pending_tags(that.m_pending_tags)
_RYML_WITH_COMMENTS(, m_pending_comment(that.m_pending_comment))
_RYML_WITH_COMMENTS(, m_pending_comments(that.m_pending_comments))
, m_was_inside_qmrk(false)
, m_doc_empty(false)
, m_prev_colon(npos)
@@ -337,7 +337,7 @@ ParseEngine<EventHandler>& ParseEngine<EventHandler>::operator=(ParseEngine &&th
m_evt_handler = that.m_evt_handler;
m_pending_anchors = that.m_pending_anchors;
m_pending_tags = that.m_pending_tags;
_RYML_WITH_COMMENTS(m_pending_comment = that.m_pending_comment;)
_RYML_WITH_COMMENTS(m_pending_comments = that.m_pending_comments;)
m_was_inside_qmrk = that.m_was_inside_qmrk;
m_doc_empty = that.m_doc_empty;
m_prev_colon = that.m_prev_colon;
@@ -362,7 +362,7 @@ ParseEngine<EventHandler>& ParseEngine<EventHandler>::operator=(ParseEngine cons
m_evt_handler = that.m_evt_handler;
m_pending_anchors = that.m_pending_anchors;
m_pending_tags = that.m_pending_tags;
_RYML_WITH_COMMENTS(m_pending_comment = that.m_pending_comment;)
_RYML_WITH_COMMENTS(m_pending_comments = that.m_pending_comments;)
m_was_inside_qmrk = that.m_was_inside_qmrk;
m_doc_empty = that.m_doc_empty;
m_prev_colon = that.m_prev_colon;
@@ -387,7 +387,7 @@ void ParseEngine<EventHandler>::_clr()
m_evt_handler = {};
m_pending_anchors = {};
m_pending_tags = {};
_RYML_WITH_COMMENTS(m_pending_comment = {};)
_RYML_WITH_COMMENTS(m_pending_comments = {};)
m_was_inside_qmrk = false;
m_doc_empty = true;
m_prev_colon = npos;
@@ -419,7 +419,7 @@ void ParseEngine<EventHandler>::_reset()
{
m_pending_anchors = {};
m_pending_tags = {};
_RYML_WITH_COMMENTS(m_pending_comment = {};)
_RYML_WITH_COMMENTS(m_pending_comments = {};)
m_doc_empty = true;
m_was_inside_qmrk = false;
m_prev_colon = npos;
@@ -807,9 +807,20 @@ substr ParseEngine<EventHandler>::_scan_comment_flow()
end = m_evt_handler->m_curr->pos.offset;
if(!_finished_file())
{
_c4dbgp("next line!");
_line_ended();
_scan_line();
if(!m_options.with_comment_sep() || !rem.ends_with(m_options.comment_sep()))
{
_c4dbgp("next line!");
_line_ended();
_scan_line();
}
else
{
_c4dbgpf("found comment separation character '{}' -- ending comment", m_options.comment_sep());
--end;
if(rem.offs(0, 1).ends_with(' '))
--end;
break;
}
}
else
{
@@ -829,7 +840,9 @@ substr ParseEngine<EventHandler>::_scan_comment_flow()
}
// continue only if the next character is #
if(c != '#')
{
break;
}
} while(true);
substr result = m_buf.range(beg, end);
_c4dbgpf("scanned comment: [{}]~~~{}~~~", result.len, result);
@@ -1620,7 +1633,7 @@ template<class EventHandler>
void ParseEngine<EventHandler>::_end_map_flow()
{
#ifdef RYML_WITH_COMMENTS
if(m_pending_comment.type != COMM_NONE)
if(m_pending_comments.num_entries)
{
_c4dbgp("mapflow: end: add pending comment");
_handle_flow_end_comment();
@@ -1643,7 +1656,7 @@ template<class EventHandler>
void ParseEngine<EventHandler>::_end_seq_flow()
{
#ifdef RYML_WITH_COMMENTS
if(m_pending_comment.type != COMM_NONE)
if(m_pending_comments.num_entries)
{
_c4dbgp("seqflow: end: add pending comment");
_handle_flow_end_comment();
@@ -4619,60 +4632,87 @@ void ParseEngine<EventHandler>::_handle_bom(Encoding_e enc)
template<class EventHandler>
void ParseEngine<EventHandler>::_pend_comment(csubstr txt, CommentType_e type)
{
_c4dbgpf("pend comment! {}", (uint32_t)type);
_RYML_ASSERT_BASIC_(callbacks(), m_pending_comment.type == COMM_NONE);
_RYML_ASSERT_BASIC_(callbacks(), type != COMM_NONE);
m_pending_comment.txt = txt;
m_pending_comment.type = type;
_c4dbgpf("pend comment! {}({})", comment_type_str(type), (uint32_t)type);
_RYML_ASSERT_PARSE_(callbacks(), type != COMM_NONE, m_evt_handler->m_curr->pos);
if(C4_UNLIKELY(m_pending_comments.num_entries >= max_pending_comments))
_c4err("too many pending comments");
m_pending_comments.entries[m_pending_comments.num_entries].txt = txt;
m_pending_comments.entries[m_pending_comments.num_entries].type = type;
++m_pending_comments.num_entries;
}
template<class EventHandler>
void ParseEngine<EventHandler>::_maybe_apply_pending_comment()
{
if(m_pending_comment.type != COMM_NONE)
if(m_pending_comments.num_entries)
{
_c4dbgpf("apply pending comment! {}", (uint32_t)m_pending_comment.type);
m_evt_handler->add_comment(m_pending_comment.txt, m_pending_comment.type);
m_pending_comment.type = COMM_NONE;
_RYML_ASSERT_PARSE_(callbacks(), m_pending_comments.num_entries == 1, m_evt_handler->m_curr->pos);
auto const& entry = m_pending_comments.entries[m_pending_comments.num_entries];
_RYML_ASSERT_PARSE_(callbacks(), entry.type != COMM_NONE, m_evt_handler->m_curr->pos);
_c4dbgpf("apply pending comment! {}({})", comment_type_str(entry.type), entry.type);
m_evt_handler->add_comment(entry.txt, entry.type);
--m_pending_comments.num_entries;
}
}
template<class EventHandler>
void ParseEngine<EventHandler>::_apply_pending_comment(CommentType_e expect_type, CommentType_e actual_type)
{
_c4dbgpf("apply pending comment! {} -> {}", (uint32_t)expect_type, (uint32_t)actual_type);
_RYML_ASSERT_BASIC_(callbacks(), m_pending_comment.type == expect_type);
m_evt_handler->add_comment(m_pending_comment.txt, actual_type);
m_pending_comment.type = COMM_NONE;
_c4dbgpf("apply pending comment! {}({}) -> {}({})", comment_type_str(expect_type), expect_type, comment_type_str(actual_type), actual_type);
_RYML_ASSERT_PARSE_(callbacks(), m_pending_comments.num_entries == 1, m_evt_handler->m_curr->pos);
auto const& entry = m_pending_comments.entries[m_pending_comments.num_entries];
_RYML_ASSERT_PARSE_(callbacks(), entry.type != COMM_NONE, m_evt_handler->m_curr->pos);
_RYML_ASSERT_PARSE_(callbacks(), entry.type == expect_type, m_evt_handler->m_curr->pos);
m_evt_handler->add_comment(entry.txt, actual_type);
--m_pending_comments.num_entries;
}
template<class EventHandler>
void ParseEngine<EventHandler>::_maybe_apply_pending_comment(CommentType_e expect_type, CommentType_e actual_type)
{
if(m_pending_comment.type != COMM_NONE)
if(m_pending_comments.num_entries)
_apply_pending_comment(expect_type, actual_type);
}
template<class EventHandler>
void ParseEngine<EventHandler>::_maybe_apply_pending_comment_matching(CommentType_e match_type, CommentType_e actual_type)
{
if(m_pending_comments.num_entries)
if(m_pending_comments.entries[m_pending_comments.num_entries].type == match_type)
_apply_pending_comment(match_type, actual_type);
}
template<class EventHandler>
void ParseEngine<EventHandler>::_handle_flow_end_comment()
{
if(m_pending_comment.type == COMM_LEADING)
_apply_pending_comment(COMM_LEADING, COMM_VAL_BRACKET_LEADING);
else if(m_pending_comment.type == COMM_VAL_TRAILING)
_apply_pending_comment(COMM_VAL_TRAILING, COMM_TRAILING);
else
_RYML_ASSERT_BASIC_(m_evt_handler->m_stack.m_callbacks, false && "not implemented");
if(m_pending_comments.num_entries)
{
_RYML_ASSERT_PARSE_(callbacks(), m_pending_comments.num_entries == 1, m_evt_handler->m_curr->pos);
auto const& entry = m_pending_comments.entries[m_pending_comments.num_entries];
if(entry.type == COMM_LEADING)
{
_apply_pending_comment(COMM_LEADING, COMM_VAL_BRACKET_LEADING);
}
else
{
_RYML_ASSERT_PARSE_(callbacks(), entry.type == COMM_VAL_TRAILING, m_evt_handler->m_curr->pos);
_apply_pending_comment(COMM_VAL_TRAILING, COMM_TRAILING);
}
}
}
template<class EventHandler>
void ParseEngine<EventHandler>::_maybe_handle_leading_comment(CommentType_e current)
{
if(m_pending_comment.type != COMM_NONE)
if(m_pending_comments.num_entries)
{
_RYML_ASSERT_PARSE_(callbacks(), m_pending_comments.num_entries == 1, m_evt_handler->m_curr->pos);
auto const& entry = m_pending_comments.entries[m_pending_comments.num_entries];
_RYML_ASSERT_PARSE_(callbacks(), entry.type != COMM_NONE, m_evt_handler->m_curr->pos);
bool leading_exists = (m_evt_handler->m_curr->leading_comments & COMM_LEADING);
_RYML_ASSERT_BASIC_(m_evt_handler->m_stack.m_callbacks, m_pending_comment.type == COMM_LEADING);
_RYML_ASSERT_PARSE_(m_evt_handler->m_stack.m_callbacks, entry.type == COMM_LEADING, m_evt_handler->m_curr->pos);
CommentType_e actual = leading_exists ? current : COMM_LEADING;
_c4dbgpf("pending leading comment! context={} leading_exists={} actual={}", current, leading_exists, actual);
_c4dbgpf("pending leading comment! context={}({}) leading_exists={} actual={}({})", comment_type_str(current), current, leading_exists, comment_type_str(actual), actual);
_apply_pending_comment(COMM_LEADING, actual);
m_evt_handler->m_curr->leading_comments |= actual;
}
@@ -5469,8 +5509,7 @@ seqflow_start:
{
_c4dbgp("seqflow[RNXT]: expect next val");
#ifdef RYML_WITH_COMMENTS
if(m_pending_comment.type == COMM_LEADING)
_apply_pending_comment(COMM_LEADING, COMM_COMMA_LEADING);
_maybe_apply_pending_comment_matching(COMM_LEADING, COMM_COMMA_LEADING);
m_evt_handler->m_curr->leading_comments = 0;
#endif
addrem_flags(RVAL, RNXT);
@@ -5479,9 +5518,8 @@ seqflow_start:
if(_maybe_advance_to_trailing_comment())
{
_c4dbgp("seqflow[RNXT]: comment!");
_maybe_apply_pending_comment_matching(COMM_VAL_TRAILING, COMM_VAL_TRAILING);
csubstr comm = _filter_comment(_scan_comment_flow());
if(m_pending_comment.type == COMM_VAL_TRAILING)
_apply_pending_comment(COMM_VAL_TRAILING, COMM_VAL_TRAILING);
m_evt_handler->add_comment(comm, COMM_TRAILING);
}
#endif
@@ -5508,6 +5546,7 @@ seqflow_start:
{
_c4dbgp("seqflow[RNXT]: comment!");
_RYML_ASSERT_BASIC_(m_evt_handler->m_stack.m_callbacks, m_options.with_comments());
_maybe_apply_pending_comment_matching(COMM_VAL_TRAILING, COMM_TRAILING);
csubstr comm = _filter_comment(_scan_comment_flow());
_pend_comment(comm, COMM_LEADING);
}

View File

@@ -725,6 +725,7 @@ private:
void _pend_comment(csubstr txt, CommentType_e type);
void _maybe_apply_pending_comment();
void _maybe_apply_pending_comment(CommentType_e expect_type, CommentType_e actual_type);
void _maybe_apply_pending_comment_matching(CommentType_e match_type, CommentType_e actual_type);
void _apply_pending_comment(CommentType_e expect_type, CommentType_e actual_type);
void _handle_flow_end_comment();
void _maybe_handle_leading_comment(CommentType_e current);
@@ -748,12 +749,17 @@ private:
Annotation m_pending_anchors;
Annotation m_pending_tags;
#ifdef RYML_WITH_COMMENTS
struct PendingComment
enum : size_t { max_pending_comments = 2 };
struct PendingComments
{
operator bool() const noexcept { return type != COMM_NONE; }
csubstr txt; CommentType_e type;
struct Entry
{
csubstr txt;
CommentType_e type;
} entries[max_pending_comments];
size_t num_entries;
};
PendingComment m_pending_comment;
PendingComments m_pending_comments;
#endif
bool m_was_inside_qmrk;

View File

@@ -305,7 +305,7 @@ void Tree::_relocate(substr next_arena)
td.handle = _relocated(td.handle, next_arena);
}
#ifdef RYML_WITH_COMMENTS
for(CommentData *C4_RESTRICT c = m_comments_buf, *e = m_comments_buf + m_comments_cap; c != e; ++c)
for(CommentData *C4_RESTRICT c = m_comments_buf, *e = m_comments_buf + m_comments_size; c != e; ++c)
{
if(in_arena(c->m_text))
c->m_text = _relocated(c->m_text, next_arena);
@@ -1964,8 +1964,9 @@ id_type Tree::_claim_comment()
CommentData const* Tree::comment(id_type node_id, id_type comment_id, comment_data_type type) const
{
(void)node_id;
_RYML_ASSERT_VISIT_(m_callbacks, node_id < m_cap, this, node_id);
if((m_buf[node_id].m_comments & type) == 0)
return nullptr;
_RYML_ASSERT_VISIT_(m_callbacks, comment_id == NONE || comment_id == 0 || comment_id < m_comments_size, this, node_id);
for(id_type cid = comment_id; cid != NONE; cid = m_comments_buf[cid].m_next)
{
@@ -1981,6 +1982,8 @@ CommentData const* Tree::comment(id_type node_id, id_type comment_id, comment_da
CommentData const* Tree::comment(id_type node_id, CommentData const* prev, comment_data_type type_flags) const
{
_RYML_ASSERT_VISIT_(m_callbacks, node_id < m_cap, this, node_id);
if((m_buf[node_id].m_comments & type_flags) == 0)
return nullptr;
id_type comment_id = prev ? prev->m_next : _p(node_id)->m_first_comment;
return comment(node_id, comment_id, type_flags);
}
@@ -1988,6 +1991,8 @@ CommentData const* Tree::comment(id_type node_id, CommentData const* prev, comme
CommentData const* Tree::comment(id_type node_id, comment_data_type type) const
{
_RYML_ASSERT_VISIT_(m_callbacks, node_id < m_cap, this, node_id);
if((m_buf[node_id].m_comments & type) == 0)
return nullptr;
return comment(node_id, _p(node_id)->m_first_comment, type);
}
@@ -2012,6 +2017,7 @@ void Tree::set_comment(NodeData *n, CommentType_e type, csubstr const& txt)
{
comid = _insert_comment(n, prev);
}
n->m_comments |= type;
m_comments_buf[comid].m_type = type;
m_comments_buf[comid].m_text = txt;
}

View File

@@ -206,6 +206,7 @@ struct NodeData
#ifdef RYML_WITH_COMMENTS
id_type m_first_comment;
id_type m_last_comment;
comment_data_type m_comments;
#endif // RYML_WITH_COMMENTS
};
C4_MUST_BE_TRIVIAL_COPY(NodeData);
@@ -894,7 +895,7 @@ public:
* @see alloc_arena() */
template<class T>
auto to_arena(T const& C4_RESTRICT a)
-> typename std::enable_if<!std::is_floating_point<T>::value, csubstr>::type
-> typename std::enable_if< ! std::is_floating_point<T>::value, csubstr>::type
{
substr rem(m_arena.sub(m_arena_pos));
size_t num = to_chars(rem, a);
@@ -1000,8 +1001,9 @@ public:
* @see reserve_arena() */
substr alloc_arena(size_t sz)
{
if(sz > arena_slack())
_grow_arena(sz - arena_slack());
size_t slack = arena_slack();
if(sz > slack)
_grow_arena(sz - slack);
substr s = _request_span(sz);
return s;
}

View File

@@ -74,6 +74,42 @@ COMMENT_TEST(FlowSeqMinimalBaseSingleLine1,
___(ps.end_stream());
}
COMMENT_TEST(FlowSeqMinimalBaseSingleLine1WithSep,
"# 1" "\n"
"[val1, val2] # 2 ~" "\n"
"# 3" "\n"
,
"# 1" "\n"
"[val1,val2] # 2 ~" "\n"
"# 3" "\n"
,
"+STR" "\n"
"=COMM #[LEADING] 1" "\n"
"+DOC" "\n"
"+SEQ []" "\n"
"=VAL :val1" "\n"
"=VAL :val2" "\n"
"-SEQ" "\n"
"=COMM #[TRAILING] 2" "\n"
"=COMM #[FOOTER] 3" "\n"
"-DOC" "\n"
"-STR" "\n"
)
{
___(ps.begin_stream());
___(ps.add_comment(" 1", COMM_LEADING));
___(ps.begin_doc());
___(ps.begin_seq_val_flow());
___(ps.set_val_scalar_plain("val1"));
___(ps.add_sibling());
___(ps.set_val_scalar_plain("val2"));
___(ps.end_seq_flow(singleline));
___(ps.add_comment(" 2", COMM_TRAILING));
___(ps.add_comment(" 3", COMM_FOOTER));
___(ps.end_doc());
___(ps.end_stream());
}
COMMENT_TEST(FlowSeqMinimalBaseSingleLine2,
"# 1" "\n"
"[val1,val2] # 2" "\n"
@@ -195,6 +231,46 @@ COMMENT_TEST(FlowSeqMinimal0,
___(ps.end_stream());
}
COMMENT_TEST(FlowSeqMinimal1WithSep,
"[" "\n"
" # 1" "\n"
" val1, # 2 ~" "\n"
" # 3" "\n"
" val2 # 4 ~" "\n"
" # 5" "\n"
"]" "\n"
,
"+STR" "\n"
"+DOC" "\n"
"+SEQ []" "\n"
"=COMM #[LEADING] 1" "\n"
"=VAL :val1" "\n"
"=COMM #[TRAILING] 2" "\n"
"=COMM #[LEADING] 3" "\n"
"=VAL :val2" "\n"
"=COMM #[TRAILING] 4" "\n"
"=COMM #[VAL_BRACKET_LEADING] 5" "\n"
"-SEQ" "\n"
"-DOC" "\n"
"-STR" "\n"
)
{
___(ps.begin_stream());
___(ps.begin_doc());
___(ps.begin_seq_val_flow());
___(ps.add_comment(" 1", COMM_LEADING));
___(ps.set_val_scalar_plain("val1"));
___(ps.add_comment(" 2", COMM_TRAILING));
___(ps.add_sibling());
___(ps.add_comment(" 3", COMM_LEADING));
___(ps.set_val_scalar_plain("val2"));
___(ps.add_comment(" 4", COMM_TRAILING));
___(ps.add_comment(" 5", COMM_VAL_BRACKET_LEADING));
___(ps.end_seq_flow(multiline));
___(ps.end_doc());
___(ps.end_stream());
}
COMMENT_TEST(FlowSeqMinimal1WithTrailingComma,
"[" "\n"
" # 1" "\n"
@@ -239,6 +315,54 @@ COMMENT_TEST(FlowSeqMinimal1WithTrailingComma,
___(ps.end_stream());
}
COMMENT_TEST(FlowSeqMinimal1WithTrailingCommaWithSep,
"[" "\n"
" # 1" "\n"
" val1, # 2 ~" "\n"
" # 3" "\n"
" val2, # 4 ~" "\n" // note the trailing comma
" # 5" "\n"
"]" "\n"
,
"[" "\n"
" # 1" "\n"
" val1, # 2 ~" "\n"
" # 3" "\n"
" val2 # 4 ~" "\n"
" # 5" "\n"
"]" "\n"
,
"+STR" "\n"
"+DOC" "\n"
"+SEQ []" "\n"
"=COMM #[LEADING] 1" "\n"
"=VAL :val1" "\n"
"=COMM #[TRAILING] 2" "\n"
"=COMM #[LEADING] 3" "\n"
"=VAL :val2" "\n"
"=COMM #[TRAILING] 4" "\n"
"=COMM #[VAL_BRACKET_LEADING] 5" "\n"
"-SEQ" "\n"
"-DOC" "\n"
"-STR" "\n"
)
{
___(ps.begin_stream());
___(ps.begin_doc());
___(ps.begin_seq_val_flow());
___(ps.add_comment(" 1", COMM_LEADING));
___(ps.set_val_scalar_plain("val1"));
___(ps.add_comment(" 2", COMM_TRAILING));
___(ps.add_sibling());
___(ps.add_comment(" 3", COMM_LEADING));
___(ps.set_val_scalar_plain("val2"));
___(ps.add_comment(" 4", COMM_TRAILING));
___(ps.add_comment(" 5", COMM_VAL_BRACKET_LEADING));
___(ps.end_seq_flow(multiline));
___(ps.end_doc());
___(ps.end_stream());
}
COMMENT_TEST(FlowSeqBasic0,
"# 1" "\n"
"[ # 2" "\n"
@@ -331,6 +455,67 @@ COMMENT_TEST(FlowSeqBasic1,
___(ps.end_stream());
}
COMMENT_TEST(FlowSeqBasic1WithSep,
"# 1" "\n"
"[ # 2 ~" "\n"
" # 3" "\n"
" a # 4 ~" "\n"
" # 5" "\n"
" , # 6 ~" "\n"
" # 7" "\n"
" b # 8 ~" "\n"
" # 9" "\n"
" , # 10 ~" "\n"
" # 11" "\n"
"] # 12 ~" "\n"
"# 13" "\n"
,
"+STR" "\n"
"=COMM #[LEADING] 1" "\n"
"+DOC" "\n"
"+SEQ []" "\n"
"=COMM #[VAL_BRACKET_TRAILING] 2" "\n"
"=COMM #[LEADING] 3" "\n"
"=VAL :a" "\n"
"=COMM #[VAL_TRAILING] 4" "\n"
"=COMM #[COMMA_LEADING] 5" "\n"
"=COMM #[TRAILING] 6" "\n"
"=COMM #[LEADING] 7" "\n"
"=VAL :b" "\n"
"=COMM #[VAL_TRAILING] 8" "\n"
"=COMM #[COMMA_LEADING] 9" "\n"
"=COMM #[TRAILING] 10" "\n"
"=COMM #[LEADING] 11" "\n"
"-SEQ" "\n"
"=COMM #[TRAILING] 12" "\n"
"=COMM #[FOOTER] 13" "\n"
"-DOC" "\n"
"-STR" "\n"
)
{
___(ps.begin_stream());
___(ps.add_comment(" 1", COMM_LEADING));
___(ps.begin_doc());
___(ps.begin_seq_val_flow());
___(ps.add_comment(" 2", COMM_VAL_BRACKET_TRAILING));
___(ps.add_comment(" 3", COMM_LEADING));
___(ps.set_val_scalar_plain("a"));
___(ps.add_comment(" 4", COMM_VAL_TRAILING));
___(ps.add_comment(" 5", COMM_COMMA_LEADING));
___(ps.add_comment(" 6", COMM_TRAILING));
___(ps.add_sibling());
___(ps.add_comment(" 7", COMM_LEADING));
___(ps.set_val_scalar_plain("b"));
___(ps.add_comment(" 8", COMM_VAL_TRAILING));
___(ps.add_comment(" 9", COMM_COMMA_LEADING));
___(ps.add_comment(" 10", COMM_TRAILING));
___(ps.end_seq_flow(multiline));
___(ps.add_comment(" 12", COMM_TRAILING));
___(ps.add_comment(" 13", COMM_FOOTER));
___(ps.end_doc());
___(ps.end_stream());
}
COMMENT_TEST(FlowSeqBasic2,
"# 1" "\n"
"[" "\n"
@@ -629,6 +814,133 @@ COMMENT_TEST(FlowSeqWithTagAndAnchor1,
___(ps.end_stream());
}
COMMENT_TEST(FlowSeqWithTagAndAnchor1WithSep,
"# 1" "\n"
"[ # 2 ~" "\n"
" # 3 ~" "\n"
" # 4" "\n"
" &a # 5 ~" "\n"
" # 6" "\n"
" !atag # 7 ~" "\n"
" # 8" "\n"
" a # 9 ~" "\n"
" # 10" "\n"
" , # 11" "\n"
" # 12" "\n"
"] # 13 ~" "\n"
"# 14" "\n"
,
"+STR" "\n"
"=COMM #[LEADING] 1" "\n"
"+DOC" "\n"
"+SEQ []" "\n"
"=COMM #[VAL_BRACKET_TRAILING] 2\\n 3" "\n"
"=COMM #[VAL_ANCHOR_TRAILING] 4\\n 5" "\n"
"=COMM #[VAL_TAG_TRAILING] 6\\n 7" "\n"
"=VAL &a <!atag> :a" "\n"
"=COMM #[TRAILING] 8\\n 9" "\n"
"-SEQ" "\n"
"=COMM #[TRAILING] 10\\n 11" "\n"
"-DOC" "\n"
"-STR" "\n"
)
{
___(ps.begin_stream());
___(ps.add_comment(" 1", COMM_LEADING));
___(ps.begin_doc());
___(ps.begin_seq_val_flow());
___(ps.add_comment(" 2", COMM_VAL_BRACKET_TRAILING));
___(ps.add_comment(" 3", COMM_LEADING));
___(ps.add_comment(" 4", COMM_VAL_ANCHOR_LEADING));
___(ps.set_val_anchor("a"));
___(ps.add_comment(" 5", COMM_VAL_ANCHOR_TRAILING));
___(ps.add_comment(" 6", COMM_VAL_TAG_LEADING));
___(ps.set_val_tag("!atag"));
___(ps.add_comment(" 7", COMM_VAL_TAG_TRAILING));
___(ps.add_comment(" 8", COMM_VAL_LEADING));
___(ps.set_val_scalar_plain("a"));
___(ps.add_comment(" 9", COMM_VAL_TRAILING));
___(ps.add_comment(" 10", COMM_COMMA_LEADING));
___(ps.add_comment(" 11", COMM_TRAILING));
___(ps.add_comment(" 12", COMM_VAL_BRACKET_LEADING));
___(ps.end_seq_flow(multiline));
___(ps.add_comment(" 13", COMM_TRAILING));
___(ps.add_comment(" 14", COMM_FOOTER));
___(ps.end_doc());
___(ps.end_stream());
}
COMMENT_TEST(FlowSeqWithTagAndAnchor1WithSep2,
"# 1" "\n"
"[ # 2 ~" "\n"
" # 3 ~" "\n"
" # 6" "\n"
" !atag # 7 ~" "\n"
" # 4" "\n"
" &a # 5 ~" "\n"
" # 8" "\n"
" a # 9 ~" "\n"
" # 10" "\n"
" , # 11" "\n"
" # 12" "\n"
"] # 13 ~" "\n"
"# 14" "\n"
,
"# 1" "\n"
"[ # 2 ~" "\n"
" # 3 ~" "\n"
" # 4" "\n"
" &a # 5 ~" "\n"
" # 6" "\n"
" !atag # 7 ~" "\n"
" # 8" "\n"
" a # 9 ~" "\n"
" # 10" "\n"
" , # 11" "\n"
" # 12" "\n"
"] # 13 ~" "\n"
"# 14" "\n"
,
"+STR" "\n"
"=COMM #[LEADING] 1" "\n"
"+DOC" "\n"
"+SEQ []" "\n"
"=COMM #[VAL_BRACKET_TRAILING] 2\\n 3" "\n"
"=COMM #[VAL_ANCHOR_TRAILING] 4\\n 5" "\n"
"=COMM #[VAL_TAG_TRAILING] 6\\n 7" "\n"
"=VAL &a <!atag> :a" "\n"
"=COMM #[TRAILING] 8\\n 9" "\n"
"-SEQ" "\n"
"=COMM #[TRAILING] 10\\n 11" "\n"
"-DOC" "\n"
"-STR" "\n"
)
{
___(ps.begin_stream());
___(ps.add_comment(" 1", COMM_LEADING));
___(ps.begin_doc());
___(ps.begin_seq_val_flow());
___(ps.add_comment(" 2", COMM_VAL_BRACKET_TRAILING));
___(ps.add_comment(" 3", COMM_LEADING));
___(ps.add_comment(" 4", COMM_VAL_TAG_LEADING));
___(ps.set_val_tag("!atag"));
___(ps.add_comment(" 5", COMM_VAL_TAG_TRAILING));
___(ps.add_comment(" 6", COMM_VAL_ANCHOR_LEADING));
___(ps.set_val_anchor("a"));
___(ps.add_comment(" 7", COMM_VAL_ANCHOR_TRAILING));
___(ps.add_comment(" 8", COMM_VAL_LEADING));
___(ps.set_val_scalar_plain("a"));
___(ps.add_comment(" 9", COMM_VAL_TRAILING));
___(ps.add_comment(" 10", COMM_COMMA_LEADING));
___(ps.add_comment(" 11", COMM_TRAILING));
___(ps.add_comment(" 12", COMM_VAL_BRACKET_LEADING));
___(ps.end_seq_flow(multiline));
___(ps.add_comment(" 13", COMM_TRAILING));
___(ps.add_comment(" 14", COMM_FOOTER));
___(ps.end_doc());
___(ps.end_stream());
}
COMMENT_TEST(FlowSeqWithTagAndAnchor2,
"# 1" "\n"
"[" "\n"