diff --git a/samples/quickstart.cpp b/samples/quickstart.cpp index 2df63576..411cf1c4 100644 --- a/samples/quickstart.cpp +++ b/samples/quickstart.cpp @@ -867,7 +867,8 @@ I am something: indeed // Getting the location of nodes in the source: // // Location tracking is opt-in: - ryml::Parser parser(ryml::ParserOptions().locations(true)); + ryml::EventHandlerTree evt_handler(ryml::ParserOptions().locations(true)); + ryml::Parser parser(&evt_handler); // Now the parser will start by building the accelerator structure: ryml::Tree tree2; parser.parse_in_arena("expected.yml", expected_result, &tree2); diff --git a/src/c4/yml/common.hpp b/src/c4/yml/common.hpp index b4165175..a24752b7 100644 --- a/src/c4/yml/common.hpp +++ b/src/c4/yml/common.hpp @@ -368,46 +368,6 @@ struct RYML_EXPORT Callbacks /** @} */ -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -typedef enum { - BLOCK_LITERAL, //!< keep newlines (|) - BLOCK_FOLD //!< replace newline with single space (>) -} BlockStyle_e; - -typedef enum { - CHOMP_CLIP, //!< single newline at end (default) - CHOMP_STRIP, //!< no newline at end (-) - CHOMP_KEEP //!< all newlines from end (+) -} BlockChomp_e; - - -/** Abstracts the fact that a filter result may not fit in the intended memory. */ -struct FilterResult -{ - C4_ALWAYS_INLINE bool valid() const noexcept { return str.str != nullptr; } - C4_ALWAYS_INLINE size_t required_len() const noexcept { return str.len; } - C4_ALWAYS_INLINE csubstr get() { RYML_ASSERT(valid()); return str; } - csubstr str; -}; -/** Abstracts the fact that a filter result may not fit in the intended memory. */ -struct FilterResultExtending -{ - C4_ALWAYS_INLINE bool valid() const noexcept { return str.str != nullptr; } - C4_ALWAYS_INLINE size_t required_len() const noexcept { return reqlen; } - C4_ALWAYS_INLINE csubstr get() { RYML_ASSERT(valid()); return str; } - csubstr str; - size_t reqlen; -}; - - //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -469,6 +429,46 @@ do \ } while(0) + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +typedef enum { + BLOCK_LITERAL, //!< keep newlines (|) + BLOCK_FOLD //!< replace newline with single space (>) +} BlockStyle_e; + +typedef enum { + CHOMP_CLIP, //!< single newline at end (default) + CHOMP_STRIP, //!< no newline at end (-) + CHOMP_KEEP //!< all newlines from end (+) +} BlockChomp_e; + + +/** Abstracts the fact that a filter result may not fit in the intended memory. */ +struct FilterResult +{ + C4_ALWAYS_INLINE bool valid() const noexcept { return str.str != nullptr; } + C4_ALWAYS_INLINE size_t required_len() const noexcept { return str.len; } + C4_ALWAYS_INLINE csubstr get() { RYML_ASSERT(valid()); return str; } + csubstr str; +}; +/** Abstracts the fact that a filter result may not fit in the intended memory. */ +struct FilterResultExtending +{ + C4_ALWAYS_INLINE bool valid() const noexcept { return str.str != nullptr; } + C4_ALWAYS_INLINE size_t required_len() const noexcept { return reqlen; } + C4_ALWAYS_INLINE csubstr get() { RYML_ASSERT(valid()); return str; } + csubstr str; + size_t reqlen; +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + namespace detail { template struct _charconstant_t diff --git a/src/c4/yml/event_handler_tree.hpp b/src/c4/yml/event_handler_tree.hpp index 121b1a30..4f18a85c 100644 --- a/src/c4/yml/event_handler_tree.hpp +++ b/src/c4/yml/event_handler_tree.hpp @@ -27,11 +27,16 @@ namespace c4 { namespace yml { +/** @addtogroup doc_event_handlers + * @{ */ +/** See the documentation for @ref doc_event_handlers, which has + * important notes about the event model used by rapidyaml. */ struct EventHandlerTree { - static constexpr const bool is_events = false; // remove - static constexpr const bool is_wtree = true; + + /** @name types + * @{ */ // our internal state must inherit from parser state struct HandlerState : public ParserState @@ -42,8 +47,14 @@ struct EventHandlerTree using state = HandlerState; + /** @} */ + public: + /** @cond dev */ + static constexpr const bool is_events = false; // remove + static constexpr const bool is_wtree = true; + detail::stack m_stack; state *C4_RESTRICT m_curr; state *C4_RESTRICT m_parent; @@ -59,9 +70,13 @@ struct EventHandlerTree #define _disable_(bits) _disable__() #endif #define _has_any_(bits) _has_any__() + /** @endcond */ public: + /** @name construction and resetting + * @{ */ + EventHandlerTree() : m_stack(), m_curr(), m_parent(), m_tree(), m_id(NONE) {} EventHandlerTree(Callbacks const& cb) : m_stack(cb), m_curr(), m_parent(), m_tree(), m_id(NONE) {} EventHandlerTree(Tree *tree, id_type id) : m_stack(tree->callbacks()), m_curr(), m_parent(), m_tree(tree), m_id(id) @@ -98,6 +113,13 @@ struct EventHandlerTree } } + /** @} */ + +public: + + /** @name parse events + * @{ */ + void start_parse(const char* filename) { m_curr->start_parse(filename, m_curr->tr_id); @@ -105,7 +127,7 @@ struct EventHandlerTree void finish_parse() { - /** This pointer is temporary. Remember that: + /* This pointer is temporary. Remember that: * * - this handler object may be held by the user * - it may be used with a temporary tree inside the parse function @@ -125,6 +147,8 @@ struct EventHandlerTree m_tree = nullptr; } + /** @} */ + public: /** @name YAML stream events */ @@ -312,108 +336,8 @@ struct EventHandlerTree /** set the previous val as the first key of a new map, with flow style. * - * For example, consider an implicit map inside a seq: `[a: b, c: - * d]` which is parsed as `[{a: b}, {c: d}]`. The standard event - * sequence for this YAML would be the following: - * - * ```c++ - * handler.begin_seq_val_flow(); - * handler.begin_map_val_flow(); - * handler.set_key_scalar_plain("a"); - * handler.set_val_scalar_plain("b"); - * handler.end_map(); - * handler.add_sibling(); - * handler.begin_map_val_flow(); - * handler.set_key_scalar_plain("c"); - * handler.set_val_scalar_plain("d"); - * handler.end_map(); - * handler.end_seq(); - * ``` - * - * The problem with this event sequence is that it forces the - * parser to delay setting the val scalar (in this case "a" and - * "c") until it knows whether the scalar is a key or a val. This - * would require the parser to store the scalar until this - * time. For instance, in the example above, the parser should - * delay setting "a" and "c", because they are in fact keys and - * not vals. Until then, the parser would have to store "a" and - * "c" in its internal state. The downside is that this complexity - * cost would apply even if there is no implicit map -- every val - * in a seq would have to be delayed until one of the - * disambiguating subsequent tokens ',-]:` is found. - * - * By calling this function, the parser can avoid this complexity, - * by preemptively setting the scalar as a val. Then a call to - * this function will create the map and rearrange the scalar as - * key. Now the cost applies only once: when a seqimap starts. So - * the following (easier and cheaper) event sequence below has the - * same effect as the event sequence above: - * - * ```c++ - * handler.begin_seq_val_flow(); - * handler.set_val_scalar_plain("notmap"); - * handler.set_val_scalar_plain("a"); // preemptively set "a" as val! - * handler.actually_as_new_map_key(); // create a map, move the "a" val as the key of the first child of the new map - * handler.set_val_scalar_plain("b"); // now "a" is a key and "b" the val - * handler.end_map(); - * handler.set_val_scalar_plain("c"); // "c" also as val! - * handler.actually_as_block_flow(); // likewise - * handler.set_val_scalar_plain("d"); // now "c" is a key and "b" the val - * handler.end_map(); - * handler.end_seq(); - * ``` - * - * This also applies to container keys (although ryml's tree - * cannot accomodate these): the parser can preemptively set a - * container as a val, and call this event to turn that container - * into a key. For example, consider this yaml: - * - * ```yaml - * [aa, bb]: [cc, dd] - * ^ ^ ^ - * | | | - * (2) (1) (3) <- event sequence - * ``` - * - * The standard event sequence for this YAML would be the - * following: - * - * ```c++ - * handler.begin_map_val_block(); // (1) - * handler.begin_seq_key_flow(); // (2) - * handler.set_val_scalar_plain("aa"); - * handler.add_sibling(); - * handler.set_val_scalar_plain("bb"); - * handler.end_seq(); - * handler.begin_seq_val_flow(); // (3) - * handler.set_val_scalar_plain("cc"); - * handler.add_sibling(); - * handler.set_val_scalar_plain("dd"); - * handler.end_seq(); - * handler.end_map(); - * ``` - * - * The problem with the sequence above is that, reading from - * left-to-right, the parser can only detect the proper calls at - * (1) and (2) once it reaches (1) in the YAML source. So, the - * parser would have to buffer the entire event sequence starting - * from the beginning until it reaches (1). Using this function, - * the parser can do instead: - * - * ```c++ - * handler.begin_seq_val_flow(); // (2) -- preemptively as val! - * handler.set_val_scalar_plain("aa"); - * handler.add_sibling(); - * handler.set_val_scalar_plain("bb"); - * handler.end_seq(); - * handler.actually_as_new_map_key(); // (1) -- adjust when finding that the prev val was actually a key. - * handler.begin_seq_val_flow(); // (3) -- go on as before - * handler.set_val_scalar_plain("cc"); - * handler.add_sibling(); - * handler.set_val_scalar_plain("dd"); - * handler.end_seq(); - * handler.end_map(); - * ``` + * See the documentation for @ref doc_event_handlers, which has + * important notes about this event. */ void actually_val_is_first_key_of_new_map_flow() { @@ -432,7 +356,11 @@ struct EventHandlerTree } /** like its flow counterpart, but this function can only be - * called after the end of a flow-val at root or doc level. */ + * called after the end of a flow-val at root or doc level. + * + * See the documentation for @ref doc_event_handlers, which has + * important notes about this event. + */ void actually_val_is_first_key_of_new_map_block() { _RYML_CB_ERR_(m_stack.m_callbacks, "ryml trees cannot handle containers as keys", m_curr->pos); @@ -635,6 +563,7 @@ struct EventHandlerTree public: + /** @cond dev */ void _reset_parser_state(state* st, id_type parse_root, id_type node) { _tr_set_state_(st, node); @@ -833,8 +762,12 @@ for(auto const& s : m_stack) printf("popped! state[%zu]: ind=%zu node=%zu\n", s. #undef _enable_ #undef _disable_ #undef _has_any_ + + /** @endcond */ }; +/** @} */ + } // namespace yml } // namespace c4 diff --git a/src/c4/yml/parse_engine.hpp b/src/c4/yml/parse_engine.hpp index a71c252f..85d341c6 100644 --- a/src/c4/yml/parse_engine.hpp +++ b/src/c4/yml/parse_engine.hpp @@ -21,6 +21,179 @@ namespace yml { /** @addtogroup doc_parse * @{ */ +/** @defgroup doc_event_handlers Event Handlers + * + * @brief rapidyaml implements its parsing logic with a two-level + * model, where a @ref ParseEngine object reads through the YAML + * source, and dispatches events to an EventHandler bound to the @ref + * ParseEngine. Because @ref ParseEngine is templated on the event + * handler, the binding uses static polymorphism, without any virtual + * functions. The actual handler object can be changed at run time, + * (but of course needs to be the type of the template parameter). + * This is thus a very efficient architecture, and further enables the + * user to provide his own custom handler if he wishes to bypass the + * rapidyaml @ref Tree. + * + * There are two handlers implemented in this project: + * + * - @ref EventHandlerTree is the handler responsible for creating the + * ryml @ref Tree + * + * - @ref EventHandlerYamlStd is the handler responsible for emitting + * standardized [YAML test suite + * events](https://github.com/yaml/yaml-test-suite), used (only) in + * the CI of this project. + * + * + * ### Event model + * + * The event model used by the parse engine and event handlers follows + * very closely the event model in the [YAML test + * suite](https://github.com/yaml/yaml-test-suite). + * + * Consider for example this YAML, + * ```yaml + * {foo: bar,foo2: bar2} + * ``` + * which would produce these events in the test-suite parlance: + * ``` + * +STR + * +DOC + * +MAP {} + * =VAL :foo + * =VAL :bar + * =VAL :foo2 + * =VAL :bar2 + * -MAP + * -DOC + * -STR + * ``` + * + * For reference, the @ref ParseEngine object will produce this + * sequence of calls to its bound EventHandler: + * ```cpp + * handler.begin_stream(); + * handler.begin_doc(); + * handler.begin_map_val_flow(); + * handler.set_key_scalar_plain("foo"); + * handler.set_val_scalar_plain("bar"); + * handler.add_sibling(); + * handler.set_key_scalar_plain("foo2"); + * handler.set_val_scalar_plain("bar2"); + * handler.end_map(); + * handler.end_doc(); + * handler.end_stream(); + * ``` + * + * + * ### Special events + * + * Most of the parsing events adopted by rapidyaml in its event model + * YAML are fairly obvious, but there are two less-obvious events + * requiring some explanation. + * + * These events exist to make it easier to parse some special YAML cases: + * + * - @ref EventHandlerTree::actually_val_is_first_key_of_new_map_flow() / @ref EventHandlerYamlStd::actually_val_is_first_key_of_new_map_flow() + * - @ref EventHandlerTree::actually_val_is_first_key_of_new_map_block() / @ref EventHandlerYamlStd::actually_val_is_first_key_of_new_map_block() + * + * These events are called by the parser when a just-handled + * value/container is actually the first key of a new map. + * + * For example, consider an implicit map inside a seq: `[a: b, c: + * d]` which is parsed as `[{a: b}, {c: d}]`. The standard event + * sequence for this YAML would be the following: + * ```cpp + * handler.begin_seq_val_flow(); + * handler.begin_map_val_flow(); + * handler.set_key_scalar_plain("a"); + * handler.set_val_scalar_plain("b"); + * handler.end_map(); + * handler.add_sibling(); + * handler.begin_map_val_flow(); + * handler.set_key_scalar_plain("c"); + * handler.set_val_scalar_plain("d"); + * handler.end_map(); + * handler.end_seq(); + * ``` + * The problem with this event sequence is that it forces the + * parser to delay setting the val scalar (in this case "a" and + * "c") until it knows whether the scalar is a key or a val. This + * would require the parser to store the scalar until this + * time. For instance, in the example above, the parser should + * delay setting "a" and "c", because they are in fact keys and + * not vals. Until then, the parser would have to store "a" and + * "c" in its internal state. The downside is that this complexity + * cost would apply even if there is no implicit map -- every val + * in a seq would have to be delayed until one of the + * disambiguating subsequent tokens `,-]:` is found. + * By calling this function, the parser can avoid this complexity, + * by preemptively setting the scalar as a val. Then a call to + * this function will create the map and rearrange the scalar as + * key. Now the cost applies only once: when a seqimap starts. So + * the following (easier and cheaper) event sequence below has the + * same effect as the event sequence above: + * ```cpp + * handler.begin_seq_val_flow(); + * handler.set_val_scalar_plain("notmap"); + * handler.set_val_scalar_plain("a"); // preemptively set "a" as val! + * handler.actually_as_new_map_key(); // create a map, move the "a" val as the key of the first child of the new map + * handler.set_val_scalar_plain("b"); // now "a" is a key and "b" the val + * handler.end_map(); + * handler.set_val_scalar_plain("c"); // "c" also as val! + * handler.actually_as_block_flow(); // likewise + * handler.set_val_scalar_plain("d"); // now "c" is a key and "b" the val + * handler.end_map(); + * handler.end_seq(); + * ``` + * This also applies to container keys (although ryml's tree + * cannot accomodate these): the parser can preemptively set a + * container as a val, and call this event to turn that container + * into a key. For example, consider this yaml: + * ```yaml + * [aa, bb]: [cc, dd] + * # ^ ^ ^ + * # | | | + * # (2) (1) (3) <- event sequence + * ``` + * The standard event sequence for this YAML would be the + * following: + * ```cpp + * handler.begin_map_val_block(); // (1) + * handler.begin_seq_key_flow(); // (2) + * handler.set_val_scalar_plain("aa"); + * handler.add_sibling(); + * handler.set_val_scalar_plain("bb"); + * handler.end_seq(); + * handler.begin_seq_val_flow(); // (3) + * handler.set_val_scalar_plain("cc"); + * handler.add_sibling(); + * handler.set_val_scalar_plain("dd"); + * handler.end_seq(); + * handler.end_map(); + * ``` + * The problem with the sequence above is that, reading from + * left-to-right, the parser can only detect the proper calls at + * (1) and (2) once it reaches (1) in the YAML source. So, the + * parser would have to buffer the entire event sequence starting + * from the beginning until it reaches (1). Using this function, + * the parser can do instead: + * ```cpp + * handler.begin_seq_val_flow(); // (2) -- preemptively as val! + * handler.set_val_scalar_plain("aa"); + * handler.add_sibling(); + * handler.set_val_scalar_plain("bb"); + * handler.end_seq(); + * handler.actually_as_new_map_key(); // (1) -- adjust when finding that the prev val was actually a key. + * handler.begin_seq_val_flow(); // (3) -- go on as before + * handler.set_val_scalar_plain("cc"); + * handler.add_sibling(); + * handler.set_val_scalar_plain("dd"); + * handler.end_seq(); + * handler.end_map(); + * ``` + */ + class Tree; class NodeRef; class ConstNodeRef; @@ -94,14 +267,20 @@ struct RYML_EXPORT ParserOptions /** This is the main driver of parsing logic: it scans the YAML or * JSON source for tokens, and emits the appropriate sequence of * parsing events to its event handler. The parse engine itself has no - * special limitations, and *can* accomodate containers as keys; the - * event handler may introduce additional requirements. There are two implemented handlers: + * special limitations, and *can* accomodate containers as keys; it is the + * event handler may introduce additional constraints. + * + * There are two implemented handlers (see @ref doc_event_handlers, + * which has important notes about the event model): * * - @ref EventHandlerTree is the handler responsible for creating the * ryml @ref Tree * * - @ref EventHandlerYamlStd is the handler responsible for emitting - * standardized YAML test suite events. + * standardized [YAML test suite + * events](https://github.com/yaml/yaml-test-suite), used (only) in + * the CI of this project. This is not part of the library and is + * not installed. */ template class ParseEngine @@ -222,7 +401,7 @@ class ParseEngine /** @name deprecated parse_methods * @{ */ - /** @cond dev + /** @cond dev */ template RYML_DEPRECATED("deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if::type parse_in_place(csubstr filename, substr yaml, Tree *t, size_t node_id); template RYML_DEPRECATED("deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if::type parse_in_place( substr yaml, Tree *t, size_t node_id); template RYML_DEPRECATED("deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if::type parse_in_place(csubstr filename, substr yaml, Tree *t ); diff --git a/test/test_suite/test_suite_event_handler.cpp b/test/test_suite/test_suite_event_handler.cpp index 57794c61..48200a18 100644 --- a/test/test_suite/test_suite_event_handler.cpp +++ b/test/test_suite/test_suite_event_handler.cpp @@ -13,7 +13,7 @@ namespace yml { template class ParseEngine; -inline void EventSink::append_escaped(csubstr val) +inline void EventHandlerYamlStd::EventSink::append_escaped(csubstr val) { #define _c4flush_use_instead(repl, skip) \ do { \ diff --git a/test/test_suite/test_suite_event_handler.hpp b/test/test_suite/test_suite_event_handler.hpp index bcdaca70..6ef23443 100644 --- a/test/test_suite/test_suite_event_handler.hpp +++ b/test/test_suite/test_suite_event_handler.hpp @@ -34,27 +34,25 @@ namespace c4 { namespace yml { -struct EventSink -{ - std::string result; - void reset() noexcept { result.clear(); } - void append(csubstr s) noexcept { result.append(s.str, s.len); } - void append(char c) noexcept { result += c; } - void insert(csubstr s, size_t pos) noexcept { result.insert(pos, s.str, s.len); } - void insert(char c, size_t pos) noexcept { result.insert(pos, 1, c); } - csubstr get() const { return csubstr(&result[0], result.size()); } - substr get() { return substr(&result[0], result.size()); } - size_t find_last(csubstr s) const { return result.rfind(s.str, std::string::npos, s.len); } - void append_escaped(csubstr val); -}; - - - - +/** @addtogroup doc_event_handlers + * @{ */ + + +/** The event handler producing standard YAML events as used in the + * [YAML test suite](https://github.com/yaml/yaml-test-suite). + * See the documentation for @ref doc_event_handlers, which has + * important notes about the event model used by rapidyaml. + * + * This classe is used only in the CI of this project, and in the + * application used as part of the [standard YAML + * playground](https://play.yaml.io/main/parser). This is not part of + * the library and is not installed. * + */ struct EventHandlerYamlStd { - static constexpr const bool is_events = true; - static constexpr const bool is_wtree = false; + + /** @name types + * @{ */ // our internal state must inherit from parser state struct HandlerState : public ParserState @@ -66,14 +64,36 @@ struct EventHandlerYamlStd using state = HandlerState; + struct EventSink + { + std::string result; + void reset() noexcept { result.clear(); } + void append(csubstr s) noexcept { result.append(s.str, s.len); } + void append(char c) noexcept { result += c; } + void insert(csubstr s, size_t pos) noexcept { result.insert(pos, s.str, s.len); } + void insert(char c, size_t pos) noexcept { result.insert(pos, 1, c); } + csubstr get() const { return csubstr(&result[0], result.size()); } + substr get() { return substr(&result[0], result.size()); } + size_t find_last(csubstr s) const { return result.rfind(s.str, std::string::npos, s.len); } + void append_escaped(csubstr val); + }; + + /** @} */ + public: + /** @cond dev */ detail::stack m_stack; state *C4_RESTRICT m_curr; state *C4_RESTRICT m_parent; + /** @endcond */ public: + /** @cond dev */ + static constexpr const bool is_events = true; // remove + static constexpr const bool is_wtree = false; // remove + EventSink *C4_RESTRICT m_ev_sink; std::vector m_ev_val_buffers; // FIXME: don't use std::vector // TODO: use this for both tree and events (ie remove the tree directives) @@ -86,9 +106,13 @@ struct EventHandlerYamlStd #define _enable_(bits) _enable__() #define _disable_(bits) _disable__() #define _has_any_(bits) _has_any__() + /** @endcond */ public: + /** @name construction and resetting + * @{ */ + EventHandlerYamlStd() : m_stack(), m_curr(), m_parent(), m_ev_sink(), m_ev_val_buffers() {} EventHandlerYamlStd(Callbacks const& cb) : m_stack(cb), m_curr(), m_parent(), m_ev_sink(), m_ev_val_buffers() {} EventHandlerYamlStd(EventSink *sink, Callbacks const& cb) : m_stack(cb), m_curr(), m_parent(), m_ev_sink(sink), m_ev_val_buffers() @@ -110,6 +134,13 @@ struct EventHandlerYamlStd m_ev_arena.clear(); } + /** @} */ + +public: + + /** @name parse events + * @{ */ + void start_parse(const char* filename) { m_curr->start_parse(filename, m_curr->tr_id); @@ -126,6 +157,8 @@ struct EventHandlerYamlStd _ev_buf_flush_(); } + /** @} */ + public: /** @name YAML stream events */ @@ -339,6 +372,11 @@ struct EventHandlerYamlStd m_curr->ev_data = {}; } + /** set the previous val as the first key of a new map, with flow style. + * + * See the documentation for @ref doc_event_handlers, which has + * important notes about this event. + */ void actually_val_is_first_key_of_new_map_flow() { // ensure we have a temporary buffer to save the current val @@ -355,7 +393,11 @@ struct EventHandlerYamlStd } /** like its flow counterpart, but this function can only be - * called after the end of a flow-val at root or doc level. */ + * called after the end of a flow-val at root or doc level. + * + * See the documentation for @ref doc_event_handlers, which has + * important notes about this event. + */ void actually_val_is_first_key_of_new_map_block() { EventSink &sink = _ev_buf_(); @@ -798,13 +840,15 @@ struct EventHandlerYamlStd return !is_root && _has_any_(DOC); } - /** @endcond */ - #undef _enable_ #undef _disable_ #undef _has_any_ + + /** @endcond */ }; +/** @} */ + } // namespace yml } // namespace c4 diff --git a/tools/yaml_events.cpp b/tools/yaml_events.cpp index c1659beb..fed25dd9 100644 --- a/tools/yaml_events.cpp +++ b/tools/yaml_events.cpp @@ -107,7 +107,7 @@ std::string emit_events_from_tree(csubstr filename, substr filecontents) std::string emit_events_direct(csubstr filename, substr filecontents) { - EventSink sink = {}; + EventHandlerYamlStd::EventSink sink = {}; EventHandlerYamlStd handler(&sink, create_custom_callbacks()); ParseEngine parser(&handler); parser.parse_in_place_ev(filename, filecontents);