mirror of
https://github.com/biojppm/rapidyaml.git
synced 2026-01-18 13:31:19 +01:00
1377 lines
48 KiB
C++
1377 lines
48 KiB
C++
#ifndef _C4_YML_EXTRA_EVENT_HANDLER_INTS_HPP_
|
|
#define _C4_YML_EXTRA_EVENT_HANDLER_INTS_HPP_
|
|
|
|
/** @file event_handler_ints.hpp An event handler that creates an
|
|
* integer buffer with a very compact representation of the YAML tree
|
|
* in a source buffer. This is not part of the main rapidyaml library.
|
|
*
|
|
* @see c4::yml::extra::ievt::EventFlags
|
|
* @see c4::yml::extra::EventHandlerInts
|
|
* */
|
|
|
|
#ifndef RYML_SINGLE_HEADER
|
|
#ifndef _C4_YML_NODE_TYPE_HPP_
|
|
#include <c4/yml/node_type.hpp>
|
|
#endif
|
|
#ifndef _C4_YML_EVENT_HANDLER_STACK_HPP_
|
|
#include "c4/yml/event_handler_stack.hpp"
|
|
#endif
|
|
#ifndef _C4_YML_TAG_HPP_
|
|
#include <c4/yml/tag.hpp>
|
|
#endif
|
|
#ifndef _C4_YML_DETAIL_DBGPRINT_HPP_
|
|
#include <c4/yml/detail/dbgprint.hpp>
|
|
#endif
|
|
#endif
|
|
|
|
// NOLINTBEGIN(hicpp-signed-bitwise)
|
|
|
|
namespace c4 {
|
|
namespace yml {
|
|
namespace extra {
|
|
|
|
/** @addtogroup doc_event_handlers
|
|
* @{ */
|
|
|
|
namespace ievt {
|
|
|
|
/** data type for integer events. This is set to a 32 bit signed
|
|
* integer to allow compatibility with a wide range of processing
|
|
* languages. */
|
|
using DataType = int32_t;
|
|
|
|
/** enumeration of integer event bits. */
|
|
typedef enum : DataType {
|
|
|
|
// Structure flags
|
|
KEY_ = (1 << 0), ///< as key
|
|
VAL_ = (1 << 1), ///< as value
|
|
/// special flag to enable look-back in the event array. it
|
|
/// signifies that the previous event has a string, meaning that
|
|
/// the jump back to that event is 3 positions. without this flag it
|
|
/// would be impossible to jump to the previous event.
|
|
/// see also @ref WSTR
|
|
PSTR = (1 << 2),
|
|
/// IMPORTANT. Marks events whose string was placed in the
|
|
/// arena. This happens when the filtered string is larger than the
|
|
/// original string in the YAML code (eg from tags that resolve to
|
|
/// a larger string, or from "\L" or "\P" in double quotes, which
|
|
/// expand from two to three bytes). Because of this size
|
|
/// expansion, the filtered string cannot be placed in the original
|
|
/// source and needs to be placed in the arena.
|
|
AREN = (1 << 3),
|
|
|
|
// Event scopes
|
|
BEG_ = (1 << 5), ///< scope: begin
|
|
END_ = (1 << 6), ///< scope: end
|
|
SEQ_ = (1 << 7), ///< scope: seq
|
|
MAP_ = (1 << 8), ///< scope: map
|
|
DOC_ = (1 << 9), ///< scope: doc
|
|
EXPL = (1 << 10), ///< `---` (with BDOC) or `...` (with EDOC)
|
|
STRM = (1 << 11), ///< scope: stream
|
|
BSEQ = BEG_|SEQ_, ///< begin seq (+SEQ in test suite events)
|
|
ESEQ = END_|SEQ_, ///< end seq (-SEQ in test suite events)
|
|
BMAP = BEG_|MAP_, ///< begin map (+MAP in test suite events)
|
|
EMAP = END_|MAP_, ///< end map (-MAP in test suite events)
|
|
BSTR = BEG_|STRM, ///< begin stream (+STR in test suite events)
|
|
ESTR = END_|STRM, ///< end stream (-STR in test suite events)
|
|
BDOC = BEG_|DOC_, ///< begin doc (+DOC in test suite events)
|
|
EDOC = END_|DOC_, ///< end doc (-DOC in test suite events)
|
|
|
|
// Single events
|
|
SCLR = (1 << 12), ///< scalar (=VAL in test suite events)
|
|
ALIA = (1 << 13), ///< *ref (reference)
|
|
ANCH = (1 << 14), ///< &anchor
|
|
TAG_ = (1 << 15), ///< !tag
|
|
|
|
// Style flags
|
|
PLAI = (1 << 16), ///< scalar: plain
|
|
SQUO = (1 << 17), ///< scalar: single-quoted (')
|
|
DQUO = (1 << 18), ///< scalar: double-quoted ("")
|
|
LITL = (1 << 19), ///< scalar: block literal (|)
|
|
FOLD = (1 << 20), ///< scalar: block folded (>)
|
|
FLOW = (1 << 21), ///< container: flow: [] for seqs or {} for maps
|
|
BLCK = (1 << 22), ///< container: block
|
|
|
|
// Directive flags
|
|
YAML = (1 << 23), ///< yaml directive: `\%YAML <version>`
|
|
TAGD = (1 << 24), ///< tag directive, name : `\%TAG <name> .......`
|
|
TAGV = (1 << 25), ///< tag directive, value: `\%TAG ...... <value>`
|
|
|
|
/// special flag to mark a scalar as unfiltered (when the parser
|
|
/// is set not to filter).
|
|
UNFILT = (1 << 26),
|
|
|
|
// Utility flags/masks
|
|
/// the last flag defined above
|
|
LAST = UNFILT,
|
|
/// a mask of all bits in this enumeration
|
|
MASK = (LAST << 1) - 1,
|
|
|
|
/// WithSTRing: mask of all the events that encode a string
|
|
/// following the event. For such events, the next two integers
|
|
/// will provide respectively the string's offset and length. See
|
|
/// also @ref PSTR.
|
|
WSTR = SCLR|ALIA|ANCH|TAG_|TAGD|TAGV|YAML,
|
|
|
|
} EventFlags;
|
|
|
|
} // namespace ievt
|
|
|
|
/** @} */
|
|
|
|
} // namespace extra
|
|
} // namespace yml
|
|
} // namespace c4
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace c4 {
|
|
namespace yml {
|
|
namespace extra {
|
|
|
|
/** @addtogroup doc_event_handlers
|
|
* @{ */
|
|
|
|
/** Read YAML source and, without undergoing a full parse, estimate
|
|
* the size of the integer buffer required for @ref
|
|
* EventHandlerInts. This estimation is meant to exceed the actual
|
|
* number of required events.
|
|
*
|
|
* @note This function must overpredict. It does so for every case in
|
|
* the hundreds/thousands of extensive tests of rapidyaml -- both for
|
|
* the YAML test suite and the internal cases. If you find a case
|
|
* where that does not hold, it is a bug. Please report it at
|
|
* https://github.com/biojppm/rapidyaml/issues! */
|
|
RYML_EXPORT int32_t estimate_events_ints_size(csubstr src);
|
|
|
|
/** @} */
|
|
|
|
} // namespace extra
|
|
} // namespace yml
|
|
} // namespace c4
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
|
|
C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
|
|
C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")
|
|
C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
|
|
|
|
namespace c4 {
|
|
namespace yml {
|
|
namespace extra {
|
|
|
|
|
|
/** @addtogroup doc_event_handlers
|
|
* @{ */
|
|
|
|
/** @cond dev */
|
|
struct EventHandlerIntsState : public c4::yml::ParserState
|
|
{
|
|
c4::yml::type_bits evt_type;
|
|
int32_t evt_id;
|
|
};
|
|
/** @endcond */
|
|
|
|
|
|
/** A parser event handler that creates a compact representation of
|
|
* the YAML tree in a buffer of integers (see @ref ievt::EventFlags)
|
|
* containing masks (to represent events) and offset+length (to
|
|
* represent strings in the source buffer).
|
|
*
|
|
* This is meant for use by other programming languages, and supports
|
|
* container keys (unlike the ryml tree). It parses faster than the ryml
|
|
* tree parser, because the resulting data structure is much simpler.
|
|
*
|
|
* The resulting integer buffer is a linear array of integers containing
|
|
* events (as a mask of @ref ievt::EventFlags), which in some cases (see
|
|
* @ref ievt::WSTR) are followed by an encoded string (encoded as an
|
|
* offset and length to the parsed source buffer).
|
|
*
|
|
* For example, parsing `[a, bb, ccc]` results in the following event
|
|
* buffer (grouped to highlight the event sequence structure):
|
|
*
|
|
* ```c++
|
|
* using namespace c4::yml::extra::ievt;
|
|
* const DataType arr[] = { // result of parsing: [a, bb, ccc]
|
|
* BSTR, // begin stream
|
|
* BDOC, // begin doc
|
|
* VAL_|BSEQ|FLOW, // begin seq as val, flow
|
|
* VAL_|SCLR|PLAI, 1, 1, // val scalar, plain style: "a" starts at offset 1 and has length 1
|
|
* VAL_|SCLR|PLAI|PSTR, 4, 2, // val scalar, plain style: "bb" starts at offset 4 and has length 2
|
|
* VAL_|SCLR|PLAI|PSTR, 8, 3, // val scalar, plain style: "ccc" starts at offset 8 and has length 3
|
|
* ESEQ|PSTR, // end seq
|
|
* EDOC, // end doc
|
|
* ESTR, // end stream
|
|
* };
|
|
* ```
|
|
*
|
|
* Here is a sketch clarifying the meaning of this event sequence:
|
|
*
|
|
@code
|
|
source : [a, bb, ccc]
|
|
has a string........
|
|
| offset "a"
|
|
| | length "a"
|
|
| | |
|
|
event0 event1 event2 [ event3 "a"......|..|
|
|
| | | | | |
|
|
(start) +--------+-------+------------------+---------------+--+-----> (continued)
|
|
arr[i] : BSTR BDOC VAL_|BSEQ|FLOW VAL_|SCLR|PLAI..1..1
|
|
i : 0 1 2 3 4 5
|
|
|
|
|
|
has a string............. has a string.............
|
|
| offset "bb" | offset "ccc"
|
|
| | length "bb" | | length "ccc"
|
|
| | | | | |
|
|
event4 "bb"..........|..| event5 "ccc".........|..|
|
|
| | | | | |
|
|
(cont)--> -----+--------------------+--+--------------+--------------------+--+-----> (continued)
|
|
arr[i] : VAL_|SCLR|PLAI|PSTR..4..2 VAL_|SCLR|PLAI|PSTR..8..3
|
|
i : 6 | 7 8 9 | 10 11
|
|
| |
|
|
prev event has string prev event has string
|
|
(to get to prev, jump (to get to prev, jump
|
|
back 3: ie 6->3) back 3: ie 9->6)
|
|
|
|
|
|
|
|
event6 ] event7 event8
|
|
| | |
|
|
(cont)--> -----+-------------+--------+-----| (end)
|
|
arr[i] : ESEQ|PSTR EDOC ESTR
|
|
i : 12 | 13 14
|
|
|
|
|
prev event has string
|
|
(to get to it, jump
|
|
back 3: ie 12->9)
|
|
@endcode
|
|
*
|
|
* Note that the buffer contains both events and strings encoded as
|
|
* integer pairs. That is, events that have an associated string are
|
|
* immediately followed by two integers providing the offset and length
|
|
* of that string in the source buffer. (In the example above, this
|
|
* happens in the events for the strings `a`, `bb`, and `ccc` at
|
|
* positions 3, 6 and 9, respectively).
|
|
*
|
|
* The flag @ref ievt::PSTR and the mask @ref ievt::WSTR are provided to
|
|
* enable easier iteration over the array: you can use them to test for
|
|
* presence of a string when iterating over the array.
|
|
*
|
|
* The flag @ref ievt::PSTR announces that an event is *preceded* by a
|
|
* string. That is, the previous event has a string, so that when this
|
|
* flag is found while iterating right-to-left, a jump of -3 should be
|
|
* used to get at the bitmask of the previous event. (In the example
|
|
* above, this flag is present for the events for `bb` and `ccc`, but not
|
|
* `a` because it is not preceded by a string).
|
|
*
|
|
* Likewise, to signify that the current event is *followed* by a string,
|
|
* there is the mask @ref ievt::WSTR, which is a mask of all the flags of
|
|
* events that have a string: @ref ievt::SCLR, @ref ievt::ALIA, @ref
|
|
* ievt::ANCH and @ref ievt::TAG_. While iterating left-to-right in the
|
|
* array, presence of any of the bits in the mask @ref ievt::WSTR means
|
|
* that a jump of +3 should be employed to get at the bitmask of the next
|
|
* event.
|
|
*
|
|
* Here's another example with the result of parsing `a: bb`
|
|
* ```c++
|
|
* const DataType arr[] = { // result of parsing: `a: bb`
|
|
* BSTR, // begin stream
|
|
* BDOC, // begin doc
|
|
* VAL_|BMAP|BLCK, // begin map as val, block
|
|
* KEY_|SCLR|PLAI, 0, 1, // key scalar, plain style: "a" starts at offset 0 and has length 1
|
|
* VAL_|SCLR|PLAI|PSTR, 3, 2, // val scalar, plain style: "bb" starts at offset 3 and has length 2
|
|
* EMAP|PSTR, // end map
|
|
* EDOC, // end doc
|
|
* ESTR, // end stream
|
|
* };
|
|
* ```
|
|
*
|
|
* Typical code to iterate left-to-right over the array will look like
|
|
* this:
|
|
*
|
|
* ```c++
|
|
* // source buffer, modified in place during parsing (IMPORTANT!)
|
|
* substr src = ...;
|
|
* substr arena = ...; // arena used for scalars/tags that are extended during filtering
|
|
* // events resulting from parsing
|
|
* const int events[] = {...};
|
|
* int events_size = ...;
|
|
* for(int i = 0; i < events_size; ++i)
|
|
* {
|
|
* if(events[i] & ievt::WSTR) // this event has a string following it
|
|
* {
|
|
* size_t offset = (size_t)events[i+1];
|
|
* size_t length = (size_t)events[i+2];
|
|
* csubstr region = (events[i] & ievt::AREN) ? arena : src; // is the string in the arena?
|
|
* csubstr str = region.sub(offset, length); // get the string
|
|
* ...
|
|
* i += 2; // skip the two ints of the string
|
|
* // (the jump is three places; the loop adds the other place)
|
|
* }
|
|
* else // this is a single-int event
|
|
* {
|
|
* ...
|
|
* }
|
|
* }
|
|
* ```
|
|
*
|
|
* This handler must be initialized with the input source buffer, the
|
|
* output arena, and the output event buffer. This handler will not take
|
|
* ownership nor attempt to resize the output buffer. If the size
|
|
* required for the output buffer or arena are larger than their actual
|
|
* size, parsing goes all way to the end, determining the required buffer
|
|
* sizes without writing anything past the end of the respective
|
|
* buffer. After parsing is finished, the user must ensure that the
|
|
* buffer size was enough to accomodate all the data that needs to be
|
|
* written into it, or react accordingly (eg, throw an error, or resize
|
|
* the buffer then retry the parse).
|
|
*
|
|
* A couple of functions will be helpful to do this. After parsing, @ref
|
|
* EventHandlerInts::fits_buffers() must be used to verify that the
|
|
* output buffers were enough to accomodate the results. Then, @ref
|
|
* EventHandlerInts::required_size_events() and @ref
|
|
* EventHandlerInts::required_size_arena() can be used to retrieve to
|
|
* necessary information. To get an estimation of the number of events
|
|
* before parsing, see @ref estimate_events_ints_size().
|
|
*
|
|
* Typical code to parse YAML with this handler will look like this:
|
|
*
|
|
* ```c++
|
|
* csubstr filename = ...;
|
|
* substr src = ...;
|
|
* // estimate the size required for the events buffer,
|
|
* // overpredicting it to be safe.
|
|
* int estimated_size = extra::estimate_events_ints_size(src);
|
|
* extra::EventHandlerInts handler;
|
|
* ParseEngine<extra::EventHandlerInts> parser(&handler);
|
|
* // example with a vector
|
|
* std::vector<int> evts;
|
|
* // ensure we have a fighting chance to acommodate the events
|
|
* evts.resize((size_t)estimated_size);
|
|
* // arena to place scalars/tags that may have been extended after filtering
|
|
* std::vector<char> arena;
|
|
* arena.resize(src.len); // this is generally enough
|
|
* // initialize the handler
|
|
* handler.reset(src, arena, evts.data(), (int)evts.size());
|
|
* // parse the YAML
|
|
* parser.parse_in_place_ev(filename, src);
|
|
* if(handler.fits_buffers()) // were the buffer sizes enough?
|
|
* {
|
|
* evts.resize((size_t)handler.required_size_events()); // trim the vector
|
|
* ...
|
|
* }
|
|
* else
|
|
* {
|
|
* // event size estimation underpredicted, or arena is too small!
|
|
* // for the first case, open an issue at
|
|
* // https://github.com/biojppm/rapidyaml/issues
|
|
* error("buffer could not accomodate all the events");
|
|
* // NOTE: see below for notes on doing a parse retry.
|
|
* }
|
|
* ```
|
|
*
|
|
* The result of @ref estimate_events_ints_size() (click to see more
|
|
* info) must be an overprediction: it overpredicts for every single case
|
|
* among the many hundreds of cases covered in the unit tests. This is
|
|
* deliberate, and aims at ensuring that a retry parse is not needed. But
|
|
* conceivably, it may underpredict in some instances not found in the out
|
|
* tests. What to do then?
|
|
*
|
|
* First, [open an issue](https://github.com/biojppm/rapidyaml/issues) to
|
|
* allow the estimation to be improved! Second, there are two ways to
|
|
* handle this situation in code:
|
|
*
|
|
* 1) throw an error (as sketched above)
|
|
*
|
|
* 2) grow the buffer to the required size (see @ref
|
|
* EventHandlerInts::required_size_events()), and then parse
|
|
* again
|
|
*
|
|
* If your code must be able to handle any case including where the
|
|
* prediction undershoots before the estimate function is fixed (after
|
|
* you open the issue), that is, if you are considering a parse retry,
|
|
* there is something important that needs attention. The YAML source
|
|
* buffer is mutated in-place during the parse, and cannot be used to
|
|
* parse again. So if you want to retry, you need to keep a pristine
|
|
* copy of the source, and use it for the retry:
|
|
*
|
|
* ```c++
|
|
* const std::string src = ...; // the YAML code to be parsed
|
|
* std::string parsed_src = src; // this is where we will parse (filter during parsring)
|
|
* std::vector<int> evts((size_t)estimated_size); // ensure we have a fighting change to acommodate the events
|
|
* std::vector<char> arena(src.size()); // ensure we have a fighting change to acommodate the events
|
|
* ParseEngine<extra::EventHandlerInts> parser(&handler);
|
|
* handler.reset(to_substr(parsed_src), to_substr(arena), evts.data(), (int)evts.size());
|
|
* parser.parse_in_place_ev(filename, to_substr(parsed_src));
|
|
* if(handler.fits_buffers()) // were the buffer sizes enough?
|
|
* {
|
|
* evts.resize((size_t)handler.required_size()); // trim the vector
|
|
* ...
|
|
* }
|
|
* else
|
|
* {
|
|
* evts.resize((size_t)handler.required_size_events()); // buffer size was not enough.
|
|
* arena.resize(handler.required_size_arena()); // buffer size was not enough.
|
|
* // copy again
|
|
* parsed_src = src;
|
|
* // retry parse
|
|
* handler.reset(to_substr(parsed_src), to_substr(arena), evts.data(), (int)evts.size());
|
|
* parser.parse_in_place_ev(filename, to_substr(parsed_src));
|
|
* assert((size_t)handler.fits_buffers()); // must always be true
|
|
* }
|
|
* ```
|
|
*
|
|
* When bringing this to other programming languages, the semantics
|
|
* will be very similar to this.
|
|
*/
|
|
struct EventHandlerInts : public c4::yml::EventHandlerStack<EventHandlerInts, EventHandlerIntsState>
|
|
{
|
|
|
|
/** @name types
|
|
* @{ */
|
|
|
|
using value_type = ievt::DataType;
|
|
using state = EventHandlerIntsState; // our internal state must inherit from parser state
|
|
|
|
/** @} */
|
|
|
|
public:
|
|
|
|
/** @cond dev */
|
|
ievt::DataType * m_evt;
|
|
int32_t m_evt_pos;
|
|
int32_t m_evt_prev;
|
|
int32_t m_evt_size;
|
|
substr m_arena;
|
|
size_t m_arena_pos;
|
|
TagDirective m_tag_directives[RYML_MAX_TAG_DIRECTIVES];
|
|
bool m_has_yaml_directive;
|
|
bool m_has_docs;
|
|
|
|
// undefined at the end
|
|
#define _enable_(bits) _enable__<bits>()
|
|
#define _disable_(bits) _disable__<bits>()
|
|
#define _has_any_(bits) _has_any__<bits>()
|
|
/** @endcond */
|
|
|
|
public:
|
|
|
|
/** @name construction and resetting
|
|
* @{ */
|
|
|
|
EventHandlerInts(c4::yml::Callbacks const& cb)
|
|
: EventHandlerStack(cb)
|
|
{
|
|
reset(csubstr{}, substr{}, nullptr, 0);
|
|
}
|
|
EventHandlerInts()
|
|
: EventHandlerInts(c4::yml::get_callbacks())
|
|
{
|
|
}
|
|
|
|
void reset(csubstr str, substr arena, ievt::DataType *dst, int32_t dst_size)
|
|
{
|
|
_stack_reset_root();
|
|
m_curr->flags |= c4::yml::RUNK|c4::yml::RTOP;
|
|
m_curr->evt_type = {};
|
|
m_curr->evt_id = 0;
|
|
m_arena = arena;
|
|
m_arena_pos = 0;
|
|
m_src = str;
|
|
m_evt = dst;
|
|
m_evt_size = dst_size;
|
|
m_evt_pos = 0;
|
|
m_evt_prev = 0;
|
|
m_has_docs = false;
|
|
m_has_yaml_directive = false;
|
|
for(TagDirective &td : m_tag_directives)
|
|
td = {};
|
|
}
|
|
|
|
/** get the size needed for the event buffer from the previous parse
|
|
* @warning this is valid only until the next parse */
|
|
int required_size_events() const
|
|
{
|
|
return m_evt_pos;
|
|
}
|
|
|
|
/** get the size needed for the arena from the previous parse
|
|
* @warning this is valid only until the next parse */
|
|
size_t required_size_arena() const
|
|
{
|
|
return m_arena_pos;
|
|
}
|
|
|
|
/** Predicate to test if the event buffer successfully accomodated
|
|
* all the parse events.
|
|
* @warning this is valid only until the next parse */
|
|
bool fits_buffers() const
|
|
{
|
|
return m_evt_pos <= m_evt_size && m_arena_pos <= m_arena.len;
|
|
}
|
|
|
|
void reserve_arena(int /*arena_size*/)
|
|
{
|
|
// does not apply here
|
|
}
|
|
|
|
/** @} */
|
|
|
|
public:
|
|
|
|
/** @name parse events
|
|
* @{ */
|
|
|
|
void start_parse(const char* filename, csubstr src, c4::yml::detail::pfn_relocate_arena relocate_arena, void *relocate_arena_data)
|
|
{
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, src.str == m_src.str);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, src.len == m_src.len);
|
|
this->_stack_start_parse(filename, src, relocate_arena, relocate_arena_data);
|
|
}
|
|
|
|
void finish_parse()
|
|
{
|
|
if((_num_tag_directives() || m_has_yaml_directive) && !m_has_docs)
|
|
_RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "directives cannot be used without a document");
|
|
this->_stack_finish_parse();
|
|
}
|
|
|
|
void cancel_parse()
|
|
{
|
|
while(m_stack.size() > 1)
|
|
_pop();
|
|
}
|
|
|
|
/** @} */
|
|
|
|
public:
|
|
|
|
/** @name YAML stream events */
|
|
/** @{ */
|
|
|
|
void begin_stream()
|
|
{
|
|
_send_flag_only_(ievt::BSTR);
|
|
}
|
|
|
|
void end_stream()
|
|
{
|
|
_send_flag_only_(ievt::ESTR);
|
|
}
|
|
|
|
/** @} */
|
|
|
|
public:
|
|
|
|
/** @name YAML document events */
|
|
/** @{ */
|
|
|
|
/** implicit doc start (without ---) */
|
|
void begin_doc()
|
|
{
|
|
_c4dbgpf("{}/{}: begin_doc", m_evt_pos, m_evt_size);
|
|
_send_flag_only_(ievt::BDOC);
|
|
if(_stack_should_push_on_begin_doc())
|
|
{
|
|
_c4dbgp("push!");
|
|
_push();
|
|
_enable_(DOC);
|
|
}
|
|
m_has_docs = true;
|
|
}
|
|
/** implicit doc end (without ...) */
|
|
void end_doc()
|
|
{
|
|
_c4dbgpf("{}/{}: end_doc", m_evt_pos, m_evt_size);
|
|
_send_flag_only_(ievt::EDOC);
|
|
if(_stack_should_pop_on_end_doc())
|
|
{
|
|
_c4dbgp("pop!");
|
|
_pop();
|
|
}
|
|
}
|
|
|
|
/** explicit doc start, with --- */
|
|
void begin_doc_expl()
|
|
{
|
|
_c4dbgpf("{}/{}: begin_doc_expl", m_evt_pos, m_evt_size);
|
|
_send_flag_only_(ievt::BDOC|ievt::EXPL);
|
|
_c4dbgp("push!");
|
|
_push();
|
|
_enable_(DOC);
|
|
m_has_docs = true;
|
|
}
|
|
/** explicit doc end, with ... */
|
|
void end_doc_expl()
|
|
{
|
|
_c4dbgpf("{}/{}: end_doc_expl", m_evt_pos, m_evt_size);
|
|
_send_flag_only_(ievt::EDOC|ievt::EXPL);
|
|
if(_stack_should_pop_on_end_doc())
|
|
{
|
|
_c4dbgp("pop!");
|
|
_pop();
|
|
}
|
|
m_has_yaml_directive = false;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
public:
|
|
|
|
/** @name YAML map functions */
|
|
/** @{ */
|
|
|
|
void begin_map_key_flow()
|
|
{
|
|
_c4dbgpf("{}/{}: bmap key flow", m_evt_pos, m_evt_size);
|
|
_send_flag_only_(ievt::KEY_|ievt::BMAP|ievt::FLOW);
|
|
_mark_parent_with_children_();
|
|
_enable_(c4::yml::KEY|c4::yml::MAP|c4::yml::FLOW_SL);
|
|
_push();
|
|
}
|
|
void begin_map_key_block()
|
|
{
|
|
_c4dbgpf("{}/{}: bmap key block", m_evt_pos, m_evt_size);
|
|
_send_flag_only_(ievt::KEY_|ievt::BMAP|ievt::BLCK);
|
|
_mark_parent_with_children_();
|
|
_enable_(c4::yml::KEY|c4::yml::MAP|c4::yml::BLOCK);
|
|
_push();
|
|
}
|
|
|
|
void begin_map_val_flow()
|
|
{
|
|
_c4dbgpf("{}/{}: bmap flow", m_evt_pos, m_evt_size);
|
|
_send_flag_only_(ievt::VAL_|ievt::BMAP|ievt::FLOW);
|
|
_mark_parent_with_children_();
|
|
_enable_(c4::yml::MAP|c4::yml::FLOW_SL);
|
|
_push();
|
|
}
|
|
void begin_map_val_block()
|
|
{
|
|
_c4dbgpf("{}/{}: bmap block", m_evt_pos, m_evt_size);
|
|
_send_flag_only_(ievt::VAL_|ievt::BMAP|ievt::BLCK);
|
|
_mark_parent_with_children_();
|
|
_enable_(c4::yml::MAP|c4::yml::BLOCK);
|
|
_push();
|
|
}
|
|
|
|
void end_map_block()
|
|
{
|
|
_pop();
|
|
_send_flag_only_(ievt::EMAP);
|
|
}
|
|
|
|
void end_map_flow(bool /*multiline*/)
|
|
{
|
|
_pop();
|
|
_send_flag_only_(ievt::EMAP);
|
|
}
|
|
|
|
/** @} */
|
|
|
|
public:
|
|
|
|
/** @name YAML seq events */
|
|
/** @{ */
|
|
|
|
void begin_seq_key_flow()
|
|
{
|
|
_c4dbgpf("{}/{}: bseq key flow", m_evt_pos, m_evt_size);
|
|
_send_flag_only_(ievt::KEY_|ievt::BSEQ|ievt::FLOW);
|
|
_mark_parent_with_children_();
|
|
_enable_(c4::yml::KEY|c4::yml::SEQ|c4::yml::FLOW_SL);
|
|
_push();
|
|
}
|
|
void begin_seq_key_block()
|
|
{
|
|
_c4dbgpf("{}/{}: bseq key block", m_evt_pos, m_evt_size);
|
|
_send_flag_only_(ievt::KEY_|ievt::BSEQ|ievt::BLCK);
|
|
_mark_parent_with_children_();
|
|
_enable_(c4::yml::KEY|c4::yml::SEQ|c4::yml::BLOCK);
|
|
_push();
|
|
}
|
|
|
|
void begin_seq_val_flow()
|
|
{
|
|
_c4dbgpf("{}/{}: bseq flow", m_evt_pos, m_evt_size);
|
|
_send_flag_only_(ievt::VAL_|ievt::BSEQ|ievt::FLOW);
|
|
_mark_parent_with_children_();
|
|
_enable_(c4::yml::SEQ|c4::yml::FLOW_SL);
|
|
_push();
|
|
}
|
|
void begin_seq_val_block()
|
|
{
|
|
_c4dbgpf("{}/{}: bseq block", m_evt_pos, m_evt_size);
|
|
_send_flag_only_(ievt::VAL_|ievt::BSEQ|ievt::BLCK);
|
|
_mark_parent_with_children_();
|
|
_enable_(c4::yml::SEQ|c4::yml::BLOCK);
|
|
_push();
|
|
}
|
|
|
|
void end_seq_block()
|
|
{
|
|
_pop();
|
|
_send_flag_only_(ievt::ESEQ);
|
|
}
|
|
|
|
void end_seq_flow(bool /*multiline*/)
|
|
{
|
|
_pop();
|
|
_send_flag_only_(ievt::ESEQ);
|
|
}
|
|
|
|
/** @} */
|
|
|
|
public:
|
|
|
|
/** @name YAML structure events */
|
|
/** @{ */
|
|
|
|
void add_sibling()
|
|
{
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, m_parent);
|
|
m_curr->evt_type = {};
|
|
}
|
|
|
|
/** 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()
|
|
{
|
|
_c4dbgpf("{}/{}: prev={} actually_val_is_first_key_of_new_map_flow", m_evt_pos, m_evt_size, m_evt_prev);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, m_evt_pos > 2);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, m_evt_prev > 0);
|
|
// BEFORE
|
|
// ... flag start len (free)
|
|
// | |
|
|
// prev curr
|
|
// AFTER
|
|
// ... BMAP flag start len (free)
|
|
// | |
|
|
// prev curr
|
|
if(m_evt_prev + 1 < m_evt_size)
|
|
{
|
|
if(m_evt[m_evt_prev] & ievt::WSTR)
|
|
{
|
|
_c4dbgpf("{}/{}: WSTR", m_evt_pos, m_evt_size);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, m_evt_prev > 0);
|
|
int32_t pos = _extend_left_to_include_tag_and_or_anchor(m_evt_prev);
|
|
if(m_evt_pos + 1 < m_evt_size)
|
|
{
|
|
for(int32_t i = pos; i <= m_evt_prev; i = _next(i))
|
|
{
|
|
m_evt[i] |= ievt::KEY_;
|
|
m_evt[i] &= ~ievt::VAL_;
|
|
}
|
|
int32_t num_move = m_evt_pos + 1 - pos;
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, num_move > 0);
|
|
memmove(m_evt + pos + 1, m_evt + pos, (size_t)num_move * sizeof(ievt::DataType));
|
|
}
|
|
m_evt[pos] = ievt::BMAP|ievt::FLOW|ievt::VAL_;
|
|
// move PSTR to prev
|
|
if(m_evt[pos + 1] & ievt::PSTR)
|
|
{
|
|
m_evt[pos ] |= ievt::PSTR;
|
|
m_evt[pos + 1] &= ~ievt::PSTR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_c4dbgpf("{}/{}: container key", m_evt_pos, m_evt_size);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, (m_evt[m_evt_prev] & (ievt::EMAP|ievt::ESEQ)));
|
|
int32_t pos;
|
|
_c4dbgpf("{}/{}: find matching open for {}", m_evt_pos, m_evt_size, m_evt_prev);
|
|
if((m_evt[m_evt_prev] & ievt::EMAP) == ievt::EMAP)
|
|
{
|
|
pos = _find_matching_open(ievt::BMAP, ievt::EMAP, m_evt_prev);
|
|
}
|
|
else
|
|
{
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, (m_evt[m_evt_prev] & ievt::ESEQ));
|
|
pos = _find_matching_open(ievt::BSEQ, ievt::ESEQ, m_evt_prev);
|
|
}
|
|
_c4dbgpf("{}/{}: matching open for {}={}", m_evt_pos, m_evt_size, m_evt_prev, pos);
|
|
_RYML_CHECK_BASIC_(m_stack.m_callbacks, pos >= 0); // internal error
|
|
_RYML_CHECK_BASIC_(m_stack.m_callbacks, pos < m_evt_prev); // internal error
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, (m_evt[pos] & ievt::ESEQ) == (m_evt[m_evt_prev] & ievt::BSEQ));
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, (m_evt[pos] & ievt::EMAP) == (m_evt[m_evt_prev] & ievt::BMAP));
|
|
// shift the array one position to the right, starting at pos
|
|
int32_t posp1 = pos + 1;
|
|
if(m_evt_pos + 1 < m_evt_size)
|
|
{
|
|
int32_t num_move = m_evt_pos + 1 - pos;
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, num_move > 0);
|
|
memmove(m_evt + posp1, m_evt + pos, (size_t)num_move * sizeof(ievt::DataType));
|
|
}
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, posp1 < m_evt_pos);
|
|
// start the map
|
|
m_evt[pos] = ievt::BMAP|ievt::FLOW|ievt::VAL_;
|
|
// set next as key, not val
|
|
m_evt[posp1] |= ievt::KEY_;
|
|
m_evt[posp1] &= ~ievt::VAL_;
|
|
// move PSTR to pos
|
|
if(m_evt[posp1] & ievt::PSTR)
|
|
{
|
|
m_evt[pos] |= ievt::PSTR;
|
|
m_evt[posp1] &= ~ievt::PSTR;
|
|
}
|
|
}
|
|
}
|
|
m_curr->evt_id = m_evt_pos - 2;
|
|
++m_evt_prev;
|
|
++m_evt_pos;
|
|
_enable_(c4::yml::MAP|c4::yml::FLOW_SL);
|
|
_push();
|
|
}
|
|
|
|
/** like its flow counterpart, but this function can only be
|
|
* 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()
|
|
{
|
|
if(m_evt_prev < m_evt_size)
|
|
{
|
|
// interpolate BMAP|VAL|BLCK after the last BDOC
|
|
int32_t pos = _find_last_bdoc(m_evt_pos);
|
|
if(pos >= 0)
|
|
{
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, pos < m_evt_size);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, pos < m_evt_pos);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, (m_evt[pos] & ievt::BDOC) == ievt::BDOC);
|
|
if(m_evt_pos < m_evt_size)
|
|
{
|
|
++pos; // add 1 to write after BDOC
|
|
int32_t num_move = m_evt_pos - pos;
|
|
int32_t posp1 = pos + 1;
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, ((m_evt[pos] & ievt::BSEQ) == ievt::BSEQ) || ((m_evt[pos] & ievt::BMAP) == ievt::BMAP));
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, num_move > 0);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, 0 == (m_evt[posp1] & ievt::PSTR));
|
|
memmove(m_evt + posp1, m_evt + pos, (size_t)num_move * sizeof(ievt::DataType));
|
|
m_evt[pos] = ievt::VAL_|ievt::BMAP|ievt::BLCK;
|
|
m_evt[posp1] &= ~ievt::VAL_;
|
|
m_evt[posp1] |= ievt::KEY_;
|
|
}
|
|
}
|
|
}
|
|
++m_curr->evt_id;
|
|
++m_evt_prev;
|
|
++m_evt_pos;
|
|
_push();
|
|
}
|
|
|
|
/** @} */
|
|
|
|
public:
|
|
|
|
/** @cond dev */
|
|
int32_t _find_last_bdoc(int32_t pos) const
|
|
{
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, pos < m_evt_size); // it's safe to read from the array
|
|
while(pos >= 0)
|
|
{
|
|
ievt::DataType e = m_evt[pos];
|
|
if((e & ievt::BDOC) == ievt::BDOC)
|
|
return pos;
|
|
pos -= (e & ievt::PSTR) ? 3 : 1;
|
|
}
|
|
return -1; // LCOV_EXCL_LINE
|
|
}
|
|
int32_t _find_matching_open(ievt::DataType open, ievt::DataType close, int32_t pos) const
|
|
{
|
|
_c4dbgpf("find_matching: start at {}", pos);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, pos < m_evt_size);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, (m_evt[pos] & close) == close);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, (m_evt[pos] & open) == (close & ~ievt::END_));
|
|
pos = _prev(pos); // don't count the starting close token
|
|
uint32_t count = 0;
|
|
while(pos >= 0)
|
|
{
|
|
ievt::DataType e = m_evt[pos];
|
|
_c4dbgpf("find_matching: pos={} count={} e={}", pos, count, m_evt[pos]);
|
|
if((e & close) == close)
|
|
{
|
|
_c4dbgpf(".............: pos={} close! count={} e={}", pos, count, m_evt[pos]);
|
|
++count;
|
|
}
|
|
else if((e & open) == open)
|
|
{
|
|
_c4dbgpf(".............: pos={} open! count={} e={}", pos, count, m_evt[pos]);
|
|
if(!count)
|
|
return pos;
|
|
else
|
|
--count;
|
|
}
|
|
pos = _prev(pos);
|
|
}
|
|
_c4dbgpf("find_matching: not found!", 0); // LCOV_EXCL_LINE
|
|
return -1; // LCOV_EXCL_LINE
|
|
}
|
|
int32_t _extend_left_to_include_tag_and_or_anchor(int32_t pos) const
|
|
{
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, pos < m_evt_size);
|
|
int32_t prev = _prev(pos);
|
|
while((prev > 0) && (m_evt[prev] & (ievt::TAG_|ievt::ANCH)))
|
|
{
|
|
_c4dbgpf("{}/{}: {} is anchor/tag. extend to {}", m_evt_pos, m_evt_size, prev, prev);
|
|
pos = prev;
|
|
prev = _prev(prev);
|
|
}
|
|
return pos;
|
|
}
|
|
C4_ALWAYS_INLINE int32_t _next(int32_t pos) const noexcept
|
|
{
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, pos < m_evt_size);
|
|
return pos + ((m_evt[pos] & ievt::WSTR) ? 3 : 1);
|
|
}
|
|
C4_ALWAYS_INLINE int32_t _prev(int32_t pos) const noexcept
|
|
{
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, pos < m_evt_size);
|
|
return pos - ((m_evt[pos] & ievt::PSTR) ? 3 : 1);
|
|
}
|
|
/** @endcond */
|
|
|
|
public:
|
|
|
|
/** @name YAML scalar events */
|
|
/** @{ */
|
|
|
|
|
|
C4_ALWAYS_INLINE void set_key_scalar_plain_empty()
|
|
{
|
|
_c4dbgpf("{}/{}: set_key_scalar_plain_empty", m_evt_pos, m_evt_size);
|
|
_send_key_scalar_(_get_latest_empty_scalar(), ievt::PLAI);
|
|
_enable_(c4::yml::KEY|c4::yml::KEY_PLAIN|c4::yml::KEYNIL);
|
|
}
|
|
C4_ALWAYS_INLINE void set_val_scalar_plain_empty()
|
|
{
|
|
_c4dbgpf("{}/{}: set_val_scalar_plain_empty", m_evt_pos, m_evt_size);
|
|
_send_val_scalar_(_get_latest_empty_scalar(), ievt::PLAI);
|
|
_enable_(c4::yml::VAL|c4::yml::VAL_PLAIN|c4::yml::VALNIL);
|
|
}
|
|
C4_ALWAYS_INLINE csubstr _get_latest_empty_scalar() const
|
|
{
|
|
// ideally we should search back in the latest event that has
|
|
// a scalar, then select a zero-length scalar immediately
|
|
// after that scalar. But this also works for now:
|
|
return m_src.first(0);
|
|
}
|
|
|
|
|
|
C4_ALWAYS_INLINE void set_key_scalar_plain(csubstr scalar)
|
|
{
|
|
_c4dbgpf("{}/{}: set_key_scalar_plain: @{} [{}]~~~{}~~~", m_evt_pos, m_evt_size, scalar.str-m_src.str, scalar.len, scalar);
|
|
_send_key_scalar_(scalar, ievt::PLAI);
|
|
_enable_(c4::yml::KEY|c4::yml::KEY_PLAIN);
|
|
}
|
|
C4_ALWAYS_INLINE void set_val_scalar_plain(csubstr scalar)
|
|
{
|
|
_c4dbgpf("{}/{}: set_val_scalar_plain: @{} [{}]~~~{}~~~", m_evt_pos, m_evt_size, scalar.str-m_src.str, scalar.len, scalar);
|
|
_send_val_scalar_(scalar, ievt::PLAI);
|
|
_enable_(c4::yml::VAL|c4::yml::VAL_PLAIN);
|
|
}
|
|
|
|
|
|
C4_ALWAYS_INLINE void set_key_scalar_dquoted(csubstr scalar)
|
|
{
|
|
_c4dbgpf("{}/{}: set_key_scalar_dquo: @{} [{}]~~~{}~~~", m_evt_pos, m_evt_size, scalar.str?size_t(scalar.str-m_src.str):m_src.len, scalar.len, scalar.str?scalar:csubstr{});
|
|
_send_key_scalar_(scalar, ievt::DQUO);
|
|
_enable_(c4::yml::KEY|c4::yml::KEY_DQUO);
|
|
}
|
|
C4_ALWAYS_INLINE void set_val_scalar_dquoted(csubstr scalar)
|
|
{
|
|
_c4dbgpf("{}/{}: set_val_scalar_dquo: @{} [{}]~~~{}~~~", m_evt_pos, m_evt_size, scalar.str?size_t(scalar.str-m_src.str):m_src.len, scalar.len, scalar.str?scalar:csubstr{});
|
|
_send_val_scalar_(scalar, ievt::DQUO);
|
|
_enable_(c4::yml::VAL|c4::yml::VAL_DQUO);
|
|
}
|
|
|
|
|
|
C4_ALWAYS_INLINE void set_key_scalar_squoted(csubstr scalar)
|
|
{
|
|
_c4dbgpf("{}/{}: set_key_scalar_squo: @{} [{}]~~~{}~~~", m_evt_pos, m_evt_size, scalar.str-m_src.str, scalar.len, scalar);
|
|
_send_key_scalar_(scalar, ievt::SQUO);
|
|
_enable_(c4::yml::KEY|c4::yml::KEY_SQUO);
|
|
}
|
|
C4_ALWAYS_INLINE void set_val_scalar_squoted(csubstr scalar)
|
|
{
|
|
_c4dbgpf("{}/{}: set_val_scalar_squo: @{} [{}]~~~{}~~~", m_evt_pos, m_evt_size, scalar.str-m_src.str, scalar.len, scalar);
|
|
_send_val_scalar_(scalar, ievt::SQUO);
|
|
_enable_(c4::yml::VAL|c4::yml::VAL_SQUO);
|
|
}
|
|
|
|
|
|
C4_ALWAYS_INLINE void set_key_scalar_literal(csubstr scalar)
|
|
{
|
|
_c4dbgpf("{}/{}: set_key_scalar_literal: @{} [{}]~~~{}~~~", m_evt_pos, m_evt_size, scalar.str?size_t(scalar.str-m_src.str):m_src.len, scalar.len, scalar.str?scalar:csubstr{});
|
|
_send_key_scalar_(scalar, ievt::LITL);
|
|
_enable_(c4::yml::KEY|c4::yml::KEY_LITERAL);
|
|
}
|
|
C4_ALWAYS_INLINE void set_val_scalar_literal(csubstr scalar)
|
|
{
|
|
_c4dbgpf("{}/{}: set_val_scalar_literal: @{} [{}]~~~{}~~~", m_evt_pos, m_evt_size, scalar.str?size_t(scalar.str-m_src.str):m_src.len, scalar.len, scalar.str?scalar:csubstr{});
|
|
_send_val_scalar_(scalar, ievt::LITL);
|
|
_enable_(c4::yml::VAL|c4::yml::VAL_LITERAL);
|
|
}
|
|
|
|
|
|
C4_ALWAYS_INLINE void set_key_scalar_folded(csubstr scalar)
|
|
{
|
|
_c4dbgpf("{}/{}: set_key_scalar_folded: @{} [{}]~~~{}~~~", m_evt_pos, m_evt_size, scalar.str?size_t(scalar.str-m_src.str):m_src.len, scalar.len, scalar.str?scalar:csubstr{});
|
|
_send_key_scalar_(scalar, ievt::FOLD);
|
|
_enable_(c4::yml::KEY|c4::yml::KEY_FOLDED);
|
|
}
|
|
C4_ALWAYS_INLINE void set_val_scalar_folded(csubstr scalar)
|
|
{
|
|
_c4dbgpf("{}/{}: set_val_scalar_folded: @{} [{}]~~~{}~~~", m_evt_pos, m_evt_size, scalar.str?size_t(scalar.str-m_src.str):m_src.len, scalar.len, scalar.str?scalar:csubstr{});
|
|
_send_val_scalar_(scalar, ievt::FOLD);
|
|
_enable_(c4::yml::VAL|c4::yml::VAL_FOLDED);
|
|
}
|
|
|
|
|
|
C4_ALWAYS_INLINE void mark_key_scalar_unfiltered()
|
|
{
|
|
_c4dbgpf("{}/{}: mark_key_scalar_unfiltered", m_evt_pos, m_evt_size);
|
|
if(m_evt_pos < m_evt_size)
|
|
m_evt[m_evt_pos] |= ievt::UNFILT;
|
|
}
|
|
C4_ALWAYS_INLINE void mark_val_scalar_unfiltered()
|
|
{
|
|
_c4dbgpf("{}/{}: mark_val_scalar_unfiltered", m_evt_pos, m_evt_size);
|
|
if(m_evt_pos < m_evt_size)
|
|
m_evt[m_evt_pos] |= ievt::UNFILT;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
public:
|
|
|
|
/** @cond dev*/
|
|
#define _add_scalar_(i, scalar) \
|
|
_c4dbgpf("{}/{}: scalar!", i, m_evt_size); \
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, scalar.is_sub(m_src) || scalar.is_sub(m_arena) || (scalar.str == nullptr)); \
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, m_evt[i] & ievt::WSTR); \
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, i + 3 < m_evt_size); \
|
|
if(C4_LIKELY(scalar.is_sub(m_src))) \
|
|
{ \
|
|
m_evt[i + 1] = (ievt::DataType)(scalar.str - m_src.str); \
|
|
} \
|
|
else \
|
|
{ \
|
|
m_evt[i] |= ievt::AREN; \
|
|
m_evt[i + 1] = (ievt::DataType)(scalar.str - m_arena.str); \
|
|
_c4dbgpf("{}/{}: arena! ->{}", i, m_evt_size, m_evt[i+1]); \
|
|
} \
|
|
m_evt[i + 2] = (ievt::DataType)scalar.len; \
|
|
m_evt[i + 3] = ievt::PSTR
|
|
/** @endcond */
|
|
|
|
/** @name YAML anchor/reference events */
|
|
/** @{ */
|
|
|
|
void set_key_anchor(csubstr anchor)
|
|
{
|
|
_c4dbgpf("{}/{}: set_key_anchor", m_evt_pos, m_evt_size);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, !_has_any_(KEYREF));
|
|
_enable_(c4::yml::KEYANCH);
|
|
if(m_evt_pos + 3 < m_evt_size)
|
|
{
|
|
m_evt[m_evt_pos] |= ievt::KEY_|ievt::ANCH;
|
|
_add_scalar_(m_evt_pos, anchor);
|
|
}
|
|
m_evt_prev = m_evt_pos;
|
|
m_evt_pos += 3;
|
|
}
|
|
void set_val_anchor(csubstr anchor)
|
|
{
|
|
_c4dbgpf("{}/{}: set_val_anchor", m_evt_pos, m_evt_size);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, !_has_any_(VALREF));
|
|
_enable_(c4::yml::VALANCH);
|
|
if(m_evt_pos + 3 < m_evt_size)
|
|
{
|
|
m_evt[m_evt_pos] |= ievt::VAL_|ievt::ANCH;
|
|
_add_scalar_(m_evt_pos, anchor);
|
|
}
|
|
m_evt_prev = m_evt_pos;
|
|
m_evt_pos += 3;
|
|
}
|
|
|
|
void set_key_ref(csubstr ref)
|
|
{
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, ref.begins_with('*'));
|
|
if(C4_UNLIKELY(_has_any_(KEYANCH)))
|
|
_RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "key cannot have both anchor and ref");
|
|
_enable_(c4::yml::KEY|c4::yml::KEYREF);
|
|
_send_str_(ref.sub(1), ievt::KEY_|ievt::ALIA); // skip the leading *
|
|
}
|
|
void set_val_ref(csubstr ref)
|
|
{
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, ref.begins_with('*'));
|
|
if(C4_UNLIKELY(_has_any_(VALANCH)))
|
|
_RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "val cannot have both anchor and ref");
|
|
_enable_(c4::yml::VAL|c4::yml::VALREF);
|
|
_send_str_(ref.sub(1), ievt::VAL_|ievt::ALIA); // skip the leading *
|
|
}
|
|
|
|
/** @} */
|
|
|
|
public:
|
|
|
|
/** @name YAML tag events */
|
|
/** @{ */
|
|
|
|
void set_key_tag(csubstr tag)
|
|
{
|
|
_c4dbgpf("{}/{}: set key tag ~~~{}~~~", m_evt_pos, m_evt_size, tag);
|
|
_enable_(c4::yml::KEYTAG);
|
|
_set_tag(tag, ievt::KEY_);
|
|
}
|
|
void set_val_tag(csubstr tag)
|
|
{
|
|
_c4dbgpf("{}/{}: set val tag [{}]~~~{}~~~", m_evt_pos, m_evt_size, tag.len, tag);
|
|
_enable_(c4::yml::VALTAG);
|
|
_set_tag(tag, ievt::VAL_);
|
|
}
|
|
void _set_tag(csubstr tag, ievt::DataType which)
|
|
{
|
|
csubstr ttag = _transform_directive(tag);
|
|
_c4dbgpf("{}/{}: transformed_tag [{}]~~~{}~~~", m_evt_pos, m_evt_size, ttag.len, ttag);
|
|
if(m_evt_pos + 3 < m_evt_size)
|
|
{
|
|
m_evt[m_evt_pos] |= which|ievt::TAG_;
|
|
_add_scalar_(m_evt_pos, ttag);
|
|
}
|
|
m_evt_prev = m_evt_pos;
|
|
m_evt_pos += 3;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
public:
|
|
|
|
/** @name YAML directive events */
|
|
/** @{ */
|
|
|
|
void add_directive(csubstr directive)
|
|
{
|
|
_c4dbgpf("{}/{}: add directive ~~~{}~~~", m_evt_pos, m_evt_size, directive);
|
|
_RYML_ASSERT_BASIC_(m_stack.m_callbacks, directive.begins_with('%'));
|
|
if(directive.begins_with("%TAG"))
|
|
{
|
|
const id_type pos = _num_tag_directives();
|
|
if(C4_UNLIKELY(pos >= RYML_MAX_TAG_DIRECTIVES))
|
|
_RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "too many directives");
|
|
TagDirective &td = m_tag_directives[pos];
|
|
if(C4_UNLIKELY(!td.create_from_str(directive)))
|
|
_RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "failed to add directive");
|
|
td.next_node_id = (id_type)m_evt_pos;
|
|
_send_str_(td.handle, ievt::TAGD);
|
|
_send_str_(td.prefix, ievt::TAGV);
|
|
}
|
|
else if(directive.begins_with("%YAML"))
|
|
{
|
|
_c4dbgpf("%YAML directive! ignoring...: {}", directive);
|
|
if(C4_UNLIKELY(m_has_yaml_directive))
|
|
_RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "multiple yaml directives");
|
|
m_has_yaml_directive = true;
|
|
csubstr rest = directive.sub(5).triml(' ');
|
|
_send_str_(rest, ievt::YAML);
|
|
}
|
|
else
|
|
{
|
|
_c4dbgpf("unknown directive! ignoring... {}", directive);
|
|
}
|
|
}
|
|
|
|
/** @} */
|
|
|
|
public:
|
|
|
|
/** @name YAML arena events */
|
|
/** @{ */
|
|
|
|
substr arena_rem()
|
|
{
|
|
return C4_LIKELY(m_arena_pos <= m_arena.len) ? m_arena.sub(m_arena_pos) : m_arena.last(0);
|
|
}
|
|
|
|
/** this may fail, in which case a an empty string is returned */
|
|
substr alloc_arena(size_t len)
|
|
{
|
|
substr s = arena_rem();
|
|
if(C4_LIKELY(len <= s.len))
|
|
s = s.first(len);
|
|
else
|
|
s.str = nullptr;
|
|
m_arena_pos += len;
|
|
return s;
|
|
}
|
|
|
|
/** this may fail, in which case an empty string is returned */
|
|
C4_ALWAYS_INLINE substr alloc_arena(size_t len, substr *relocated)
|
|
{
|
|
(void)relocated;
|
|
return alloc_arena(len);
|
|
}
|
|
|
|
/** @} */
|
|
|
|
public:
|
|
|
|
/** push a new parent, add a child to the new parent, and set the
|
|
* child as the current node */
|
|
void _push()
|
|
{
|
|
_stack_push();
|
|
m_curr->evt_type = {};
|
|
}
|
|
|
|
/** end the current scope */
|
|
void _pop()
|
|
{
|
|
_stack_pop();
|
|
}
|
|
|
|
template<c4::yml::type_bits bits> C4_ALWAYS_INLINE void _enable__() noexcept
|
|
{
|
|
m_curr->evt_type |= bits;
|
|
}
|
|
template<c4::yml::type_bits bits> C4_ALWAYS_INLINE void _disable__() noexcept
|
|
{
|
|
m_curr->evt_type &= ~bits;
|
|
}
|
|
template<c4::yml::type_bits bits> C4_ALWAYS_INLINE bool _has_any__() const noexcept
|
|
{
|
|
return (m_curr->evt_type & bits) != c4::yml::type_bits(0);
|
|
}
|
|
|
|
void _mark_parent_with_children_()
|
|
{
|
|
if(m_parent)
|
|
m_parent->has_children = true;
|
|
}
|
|
|
|
C4_ALWAYS_INLINE void _send_flag_only_(ievt::DataType flags)
|
|
{
|
|
_c4dbgpf("{}/{}: flag only", m_evt_pos, m_evt_size);
|
|
if(m_evt_pos < m_evt_size)
|
|
m_evt[m_evt_pos] |= flags;
|
|
m_curr->evt_id = m_evt_pos;
|
|
m_evt_prev = m_evt_pos;
|
|
++m_evt_pos;
|
|
if(m_evt_pos < m_evt_size)
|
|
m_evt[m_evt_pos] = {};
|
|
}
|
|
|
|
C4_ALWAYS_INLINE void _send_key_scalar_(csubstr scalar, ievt::DataType flags)
|
|
{
|
|
_c4dbgpf("{}/{}: key scalar", m_evt_pos, m_evt_size);
|
|
if(m_evt_pos + 3 < m_evt_size)
|
|
{
|
|
m_evt[m_evt_pos] |= ievt::SCLR|ievt::KEY_|flags;
|
|
_add_scalar_(m_evt_pos, scalar);
|
|
}
|
|
m_curr->evt_id = m_evt_pos;
|
|
m_evt_prev = m_evt_pos;
|
|
m_evt_pos += 3;
|
|
}
|
|
|
|
C4_ALWAYS_INLINE void _send_val_scalar_(csubstr scalar, ievt::DataType flags)
|
|
{
|
|
_c4dbgpf("{}/{}: val scalar", m_evt_pos, m_evt_size);
|
|
if(m_evt_pos + 3 < m_evt_size)
|
|
{
|
|
m_evt[m_evt_pos] |= ievt::SCLR|ievt::VAL_|flags;
|
|
_add_scalar_(m_evt_pos, scalar);
|
|
}
|
|
m_curr->evt_id = m_evt_pos;
|
|
m_evt_prev = m_evt_pos;
|
|
m_evt_pos += 3;
|
|
}
|
|
|
|
C4_ALWAYS_INLINE void _send_str_(csubstr scalar, ievt::DataType flags)
|
|
{
|
|
_c4dbgpf("{}/{}: send str", m_evt_pos, m_evt_size);
|
|
if(m_evt_pos + 3 < m_evt_size)
|
|
{
|
|
m_evt[m_evt_pos] |= flags;
|
|
_add_scalar_(m_evt_pos, scalar);
|
|
}
|
|
m_curr->evt_id = m_evt_pos;
|
|
m_evt_prev = m_evt_pos;
|
|
m_evt_pos += 3;
|
|
}
|
|
|
|
void _clear_tag_directives_()
|
|
{
|
|
for(TagDirective &td : m_tag_directives)
|
|
td = {};
|
|
}
|
|
C4_NODISCARD id_type _num_tag_directives() const
|
|
{
|
|
// this assumes we have a very small number of tag directives
|
|
id_type i = 0;
|
|
for(; i < RYML_MAX_TAG_DIRECTIVES; ++i)
|
|
if(m_tag_directives[i].handle.empty())
|
|
break;
|
|
return i;
|
|
}
|
|
csubstr _transform_directive(csubstr tag)
|
|
{
|
|
// lookup from the end. We want to find the first directive that
|
|
// matches the tag and has a target node id leq than the given
|
|
// node_id.
|
|
for(id_type i = RYML_MAX_TAG_DIRECTIVES-1; i != NONE; --i)
|
|
{
|
|
TagDirective const& td = m_tag_directives[i];
|
|
if(td.handle.empty())
|
|
continue;
|
|
if(tag.begins_with(td.handle) && (td.handle != td.prefix))
|
|
{
|
|
substr rem = arena_rem();
|
|
size_t len = td.transform(tag, rem, m_stack.m_callbacks, /*with_brackets*/false);
|
|
if(len == 0)
|
|
return tag;
|
|
alloc_arena(len);
|
|
return rem.first(len <= rem.len ? len : 0);
|
|
}
|
|
}
|
|
if(tag.begins_with('!'))
|
|
{
|
|
if(is_custom_tag(tag))
|
|
{
|
|
_RYML_ERR_PARSE_(m_stack.m_callbacks, m_curr->pos, "tag not found");
|
|
}
|
|
}
|
|
return tag;
|
|
}
|
|
#undef _enable_
|
|
#undef _disable_
|
|
#undef _has_any_
|
|
#undef _add_scalar_
|
|
|
|
};
|
|
|
|
/** @} */
|
|
|
|
} // namespace extra
|
|
} // namespace yml
|
|
} // namespace c4
|
|
|
|
|
|
// NOLINTEND(hicpp-signed-bitwise)
|
|
C4_SUPPRESS_WARNING_GCC_CLANG_POP
|
|
|
|
#endif /* _C4_YML_EXTRA_EVENT_HANDLER_INTS_HPP_ */
|