1#ifndef C4_YML_EVENT_HANDLER_TREE_HPP_
2#define C4_YML_EVENT_HANDLER_TREE_HPP_
4#ifndef C4_YML_TREE_HPP_
8#ifndef C4_YML_EVENT_HANDLER_STACK_HPP_
12C4_SUPPRESS_WARNING_GCC_PUSH
13C4_SUPPRESS_WARNING_MSVC_PUSH
14C4_SUPPRESS_WARNING_MSVC(4702)
15#if defined(__GNUC__) && __GNUC__ >= 6
16C4_SUPPRESS_WARNING_GCC(
"-Wnull-dereference")
45 using state = EventHandlerTreeState;
53 Tree *C4_RESTRICT m_tree;
58 #define ryml_enable_(bits) enable_<bits>(); _c4dbgpf("node[{}]: enable {}", m_curr->node_id, #bits)
59 #define ryml_disable_(bits) disable_<bits>(); _c4dbgpf("node[{}]: disable {}", m_curr->node_id, #bits)
61 #define ryml_enable_(bits) enable_<bits>()
62 #define ryml_disable_(bits) disable_<bits>()
64 #define ryml_hasany_(bits) has_any_<bits>()
82 RYML_ERR_BASIC_CB_(
m_stack.m_callbacks,
"null tree");
83 if C4_UNLIKELY(
id >= tree->
capacity())
84 RYML_ERR_VISIT_CB_(tree->
callbacks(), tree,
id,
"invalid node");
85 if C4_UNLIKELY(!tree->
is_root(
id))
87 if C4_UNLIKELY(!tree->
has_key(
id))
88 RYML_ERR_BASIC_CB_(tree->
callbacks(),
"destination node belongs to a map and has no key");
90 if(m_tree->is_root(
id))
93 _reset_parser_state(
m_curr,
id, m_tree->root_id());
98 _reset_parser_state(
m_parent,
id, m_tree->parent(
id));
99 _reset_parser_state(
m_curr,
id,
id);
119 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree !=
nullptr);
125 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree !=
nullptr);
168 _c4dbgp(
"begin_doc");
172 _set_root_as_stream();
176 m_curr_doc =
m_curr->node_id;
182 m_curr_doc = m_tree->size();
185 _remove_speculative();
194 _c4dbgp(
"begin_doc_expl");
195 RYML_ASSERT_VISIT_CB_(
m_stack.m_callbacks, m_tree->root_id() ==
m_curr->node_id, m_tree,
m_curr->node_id);
196 if(m_tree->is_stream(m_tree->root_id()))
203 _c4dbgp(
"ensure stream");
204 _set_root_as_stream();
205 const id_type root = m_tree->root_id();
206 const id_type first = m_tree->first_child(root);
207 RYML_ASSERT_VISIT_CB_(
m_stack.m_callbacks, m_tree->is_stream(root), m_tree, root);
208 RYML_ASSERT_VISIT_CB_(
m_stack.m_callbacks, m_tree->num_children(root) == 1u, m_tree, root);
209 if(m_tree->is_container(first) || m_tree->is_val(first))
213 #ifdef RYML_WITH_COMMENTS
214 m_tree->_p(root)->m_first_comment =
NONE;
215 m_tree->_p(root)->m_last_comment =
NONE;
222 _remove_speculative();
223 m_curr->node_id = m_tree->last_child(root);
228 m_curr_doc =
m_curr->node_id;
233 _c4dbgp(
"end_doc_expl");
234 m_curr_doc = m_tree->size();
235 _remove_speculative();
252 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"ryml trees cannot handle containers as keys");
256 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"ryml trees cannot handle containers as keys");
261 _c4dbgpf(
"node[{}]: begin_map_val_flow",
m_curr->node_id);
262 RYML_CHECK_BASIC_CB_(
m_stack.m_callbacks, !ryml_hasany_(
VAL));
269 _c4dbgpf(
"node[{}]: begin_map_val_block",
m_curr->node_id);
270 RYML_CHECK_BASIC_CB_(
m_stack.m_callbacks, !ryml_hasany_(
VAL));
284 _c4dbgpf(
"node[{}]: end_map. multiline={} startline={} endline={}",
m_parent->node_id, multiline,
m_parent->pos.line,
m_curr->pos.line);
289 enable_(multiline_style);
302 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"ryml trees cannot handle containers as keys");
306 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"ryml trees cannot handle containers as keys");
311 _c4dbgpf(
"node[{}]: begin_seq_val_flow",
m_curr->node_id);
312 RYML_CHECK_BASIC_CB_(
m_stack.m_callbacks, !ryml_hasany_(
VAL));
319 _c4dbgpf(
"node[{}]: begin_seq_val_block",
m_curr->node_id);
320 RYML_CHECK_BASIC_CB_(
m_stack.m_callbacks, !ryml_hasany_(
VAL));
334 _c4dbgpf(
"node[{}]: end_seq. multiline={} startline={} endline={}",
m_parent->node_id, multiline,
m_parent->pos.line,
m_curr->pos.line);
339 enable_(multiline_style);
352 #if defined(__GNUC__) && (__GNUC__ >= 6)
353 C4_SUPPRESS_WARNING_GCC_WITH_PUSH(
"-Wnull-dereference")
355 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
357 RYML_ASSERT_VISIT_CB_(
m_stack.m_callbacks, m_tree->has_children(
m_parent->node_id), m_tree,
m_parent->node_id);
358 NodeData const*
const prev = m_tree->m_buf;
359 _set_state_(
m_curr, m_tree->_append_child__unprotected(
m_parent->node_id));
360 if(prev != m_tree->m_buf)
361 _refresh_after_relocation();
362 _c4dbgpf(
"node[{}]: added sibling={} prev={}",
m_parent->node_id,
m_curr->node_id, m_tree->prev_sibling(
m_curr->node_id));
363 #if defined(__GNUC__) && (__GNUC__ >= 6)
364 C4_SUPPRESS_WARNING_GCC_POP
375 if C4_UNLIKELY(m_tree->is_container(
m_curr->node_id))
376 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"ryml trees cannot handle containers as keys");
379 RYML_ASSERT_VISIT_CB_(
m_stack.m_callbacks, !m_tree->is_container(
m_curr->node_id), m_tree,
m_curr->node_id);
380 RYML_ASSERT_VISIT_CB_(
m_stack.m_callbacks, !m_tree->has_key(
m_curr->node_id), m_tree,
m_curr->node_id);
383 m_curr->tr_data->m_val = {};
397 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"ryml trees cannot handle containers as keys");
410 _c4dbgpf(
"node[{}]: set key scalar plain as empty",
m_curr->node_id);
411 m_curr->tr_data->m_key.scalar = {};
416 _c4dbgpf(
"node[{}]: set val scalar plain as empty",
m_curr->node_id);
417 m_curr->tr_data->m_val.scalar = {};
423 _c4dbgpf(
"node[{}]: set key scalar plain: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
424 m_curr->tr_data->m_key.scalar = scalar;
429 _c4dbgpf(
"node[{}]: set val scalar plain: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
430 m_curr->tr_data->m_val.scalar = scalar;
437 _c4dbgpf(
"node[{}]: set key scalar dquot: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
438 m_curr->tr_data->m_key.scalar = scalar;
443 _c4dbgpf(
"node[{}]: set val scalar dquot: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
444 m_curr->tr_data->m_val.scalar = scalar;
451 _c4dbgpf(
"node[{}]: set key scalar squot: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
452 m_curr->tr_data->m_key.scalar = scalar;
457 _c4dbgpf(
"node[{}]: set val scalar squot: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
458 m_curr->tr_data->m_val.scalar = scalar;
465 _c4dbgpf(
"node[{}]: set key scalar literal: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
466 m_curr->tr_data->m_key.scalar = scalar;
471 _c4dbgpf(
"node[{}]: set val scalar literal: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
472 m_curr->tr_data->m_val.scalar = scalar;
479 _c4dbgpf(
"node[{}]: set key scalar folded: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
480 m_curr->tr_data->m_key.scalar = scalar;
485 _c4dbgpf(
"node[{}]: set val scalar folded: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
486 m_curr->tr_data->m_val.scalar = scalar;
509 _c4dbgpf(
"node[{}]: set key anchor: [{}]~~~{}~~~",
m_curr->node_id, anchor.
len, anchor);
510 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
511 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, !ryml_hasany_(
KEYREF));
512 RYML_ASSERT_PARSE_CB_(m_tree->callbacks(), !anchor.
begins_with(
'&'),
m_curr->pos);
514 m_curr->tr_data->m_key.anchor = anchor;
518 _c4dbgpf(
"node[{}]: set val anchor: [{}]~~~{}~~~",
m_curr->node_id, anchor.
len, anchor);
519 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
520 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, !ryml_hasany_(
VALREF));
521 RYML_ASSERT_PARSE_CB_(m_tree->callbacks(), !anchor.
begins_with(
'&'),
m_curr->pos);
523 m_curr->tr_data->m_val.anchor = anchor;
528 _c4dbgpf(
"node[{}]: set key ref: [{}]~~~{}~~~",
m_curr->node_id, ref.
len, ref);
532 m_curr->tr_data->m_key.anchor = ref.
sub(1);
533 m_curr->tr_data->m_key.scalar = ref;
537 _c4dbgpf(
"node[{}]: set val ref: [{}]~~~{}~~~",
m_curr->node_id, ref.
len, ref);
541 m_curr->tr_data->m_val.anchor = ref.
sub(1);
542 m_curr->tr_data->m_val.scalar = ref;
554 _c4dbgpf(
"node[{}]: set key tag: [{}]~~~{}~~~",
m_curr->node_id, tag.
len, tag);
556 m_curr->tr_data->m_key.tag = tag;
560 _c4dbgpf(
"node[{}]: set val tag: [{}]~~~{}~~~",
m_curr->node_id, tag.
len, tag);
562 m_curr->tr_data->m_val.tag = tag;
574 _c4dbgpf(
"%YAML directive! version={}", yaml_version);
580 _c4dbgpf(
"%TAG directive! handle={} prefix={} id={}", handle, prefix, m_curr_doc);
581 if C4_UNLIKELY(!m_tree->m_tag_directives.add(handle, prefix, m_curr_doc))
582 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"too many %TAG directives");
594 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
595 return m_tree->m_arena.first(m_tree->m_arena_pos);
599 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
600 return m_tree->m_arena.sub(m_tree->m_arena_pos);
604 return m_tree->alloc_arena(len);
614 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
615 _set_state_(st, node);
616 const NodeType type = m_tree->type(node);
619 _c4dbgpf(
"resetting state: initial flags={}", detail::_parser_flags_to_str(flagbuf, st->flags));
623 _c4dbgpf(
"node[{}] is notype", node);
624 if(m_tree->is_root(parse_root))
626 _c4dbgpf(
"node[{}] is root", node);
631 _c4dbgpf(
"node[{}] is not root. setting USTY", node);
637 _c4dbgpf(
"node[{}] is map", node);
642 _c4dbgpf(
"node[{}] is map", node);
647 _c4dbgpf(
"node[{}] has key. setting USTY", node);
652 RYML_ERR_VISIT_CB_(m_tree->callbacks(), m_tree, node,
"cannot append to node");
656 _c4dbgpf(
"node[{}] is doc", node);
660 _c4dbgpf(
"resetting state: final flags={}", detail::_parser_flags_to_str(flagbuf, st->flags));
669 NodeData
const* prev = m_tree->m_buf;
670 m_curr->node_id = m_tree->_append_child__unprotected(
m_parent->node_id);
672 if(prev != m_tree->m_buf)
673 _refresh_after_relocation();
679 _remove_speculative_with_parent();
685 C4_ALWAYS_INLINE
void enable_(
type_bits bits)
noexcept
687 m_curr->tr_data->m_type.m_bits |= bits;
689 template<type_bits bits> C4_HOT C4_ALWAYS_INLINE
void enable_() noexcept
691 m_curr->tr_data->m_type.m_bits |= bits;
693 template<type_bits bits> C4_HOT C4_ALWAYS_INLINE
void disable_() noexcept
695 m_curr->tr_data->m_type.m_bits &= ~bits;
697 template<type_bits bits> C4_HOT C4_ALWAYS_INLINE
bool has_any_() const noexcept
699 return (
m_curr->tr_data->m_type.m_bits & bits) != 0;
704 C4_ALWAYS_INLINE
void _set_state_(
state *C4_RESTRICT s,
id_type id)
const noexcept
707 s->tr_data = m_tree->_p(
id);
709 void _refresh_after_relocation()
711 _c4dbgp(
"tree: refreshing stack data after tree data relocation");
713 st.tr_data = m_tree->_p(st.node_id);
716 void _set_root_as_stream()
718 _c4dbgp(
"set root as stream");
719 RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->root_id() == 0u, m_tree, m_tree->root_id());
720 RYML_ASSERT_VISIT_CB_(m_tree->callbacks(),
m_curr->node_id == 0u, m_tree,
m_curr->node_id);
721 m_tree->set_root_as_stream();
722 RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->is_stream(m_tree->root_id()), m_tree, m_tree->root_id());
723 RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->has_children(m_tree->root_id()), m_tree, m_tree->root_id());
724 RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->is_doc(m_tree->first_child(m_tree->root_id())), m_tree, m_tree->root_id());
725 _set_state_(
m_curr, m_tree->root_id());
728 static NodeData _val2key_(NodeData
const& C4_RESTRICT d)
noexcept
734 static_assert((VALMASK_ >> 1u) == KEYMASK_,
"required for this function to work");
736 r.m_type.m_bits = ((d.m_type.m_bits & (VALMASK_|
VAL_STYLE)) >> 1u);
737 r.m_type.m_bits = (r.m_type.m_bits & ~(VALMASK_|
VAL_STYLE));
738 r.m_type.m_bits = (r.m_type.m_bits |
KEY);
739 if(d.m_type.m_bits &
VALNIL)
740 r.m_type.m_bits = (r.m_type.m_bits |
KEYNIL);
744 void _remove_speculative()
746 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
747 RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), !m_tree->empty());
748 const id_type last_added = m_tree->size() - 1;
749 const NodeData *C4_RESTRICT d = m_tree->_p(last_added);
750 if(d->m_parent !=
NONE && d->m_type ==
NOTYPE)
752 _c4dbgpf(
"remove speculative: currparent={} node={} parent(node)={}",
m_parent->node_id, last_added, d->m_parent);
753 m_tree->remove(last_added);
758 void _remove_speculative_with_parent()
760 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
761 RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), !m_tree->empty());
762 const id_type last_added = m_tree->size() - 1;
763 RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->has_parent(last_added), m_tree, last_added);
764 if(m_tree->_p(last_added)->m_type ==
NOTYPE)
766 _c4dbgpf(
"remove speculative node with parent. parent={} node={} parent(node)={}",
m_parent->node_id, last_added, m_tree->parent(last_added));
767 m_tree->remove(last_added);
772 C4_ALWAYS_INLINE
void _save_loc()
774 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
775 RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), m_tree->_p(
m_curr->node_id)->m_val.scalar.len == 0);
776 m_tree->_p(
m_curr->node_id)->m_val.scalar.str =
m_curr->line_contents.rem.str;
792C4_SUPPRESS_WARNING_MSVC_POP
793C4_SUPPRESS_WARNING_GCC_POP
bool is_map(id_type node) const
bool is_root(id_type node) const
bool has_key(id_type node) const
id_type parent(id_type node) const
Callbacks const & callbacks() const
id_type ancestor_doc(id_type node) const
get the document which is a parent document of node i, or the root if the tree is not a stream
uint32_t type_bits
the integral type necessary to cover all the bits for NodeType_e
@ VALANCH
the val has an &anchor
@ NOTYPE
no node type or style is set
@ KEY_DQUO
mark key scalar as double quoted "
@ VALREF
a *reference: the val references an &anchor
@ VALNIL
the val is null (eg {a : } results in a null val)
@ MAP
a map: a parent of KEYVAL/KEYSEQ/KEYMAP nodes
@ KEY
the scalar to the left of : in a map's member
@ FLOW_ML1
mark container with multi-line flow style, 1 element per line
@ VAL_FOLDED
mark val scalar as multiline, block folded >
@ VAL_STYLE
mask of VALQUO|VAL_PLAIN : all the val scalar styles for val (not container styles!...
@ KEYTAG
the key has a tag
@ FLOW_SL
mark container with single-line flow style
@ VAL_UNFILT
the val scalar was left unfiltered; the parser was set not to filter.
@ VAL
a scalar: has a scalar (ie string) value, possibly empty. must be a leaf node, and cannot be MAP or S...
@ VALTAG
the val has a tag
@ SEQ
a seq: a parent of VAL/SEQ/MAP nodes
@ VAL_SQUO
mark val scalar as single quoted '
@ KEY_STYLE
mask of KEYQUO|KEY_PLAIN : all the key scalar styles for key (not container styles!...
@ VAL_PLAIN
mark val scalar as plain scalar (unquoted, even when multiline)
@ KEYREF
a *reference: the key references an &anchor
@ BLOCK
mark container with block style
@ KEYANCH
the key has an &anchor
@ VAL_DQUO
mark val scalar as double quoted "
@ KEY_UNFILT
the key scalar was left unfiltered; the parser was set not to filter.
@ KEY_SQUO
mark key scalar as single quoted '
@ VAL_LITERAL
mark val scalar as multiline, block literal |
@ KEY_LITERAL
mark key scalar as multiline, block literal |
@ KEY_PLAIN
mark key scalar as plain scalar (unquoted, even when multiline)
@ KEY_FOLDED
mark key scalar as multiline, block folded >
@ KEYNIL
the key is null (eg { : b} results in a null key)
basic_substring< char > substr
a mutable string view
basic_substring< const char > csubstr
an immutable string view
@ RTOP
reading at top level
@ RUNK
reading unknown state (when starting): must determine whether scalar, map or seq
@ USTY
reading in unknown style mode - must determine FLOW or BLCK reading an implicit map nested in an expl...
RYML_ID_TYPE id_type
The type of a node id in the YAML tree; to override the default type, define the macro RYML_ID_TYPE t...
bool begins_with(const C c) const noexcept
true if the first character of the string is c
size_t len
the length of the substring
basic_substring sub(size_t first) const noexcept
return [first,len[
A c-style callbacks class to customize behavior on errors or allocation.
void _stack_finish_parse()
void _stack_reset_non_root()
bool _stack_should_push_on_begin_doc() const
void _stack_start_parse(const char *filename, substr ymlsrc)
detail::stack< state > m_stack
bool _stack_should_pop_on_end_doc() const
void set_key_scalar_plain(csubstr scalar) noexcept
void mark_val_scalar_unfiltered() noexcept
void end_doc_expl()
explicit doc end, with ...
void begin_map_key_flow()
void end_doc()
implicit doc end (without ...)
EventHandlerTreeState state
void begin_doc()
implicit doc start (without —)
TagDirectives & tag_directives()
void set_val_scalar_plain(csubstr scalar) noexcept
void set_key_scalar_plain_empty() noexcept
void mark_key_scalar_unfiltered() noexcept
void actually_val_is_first_key_of_new_map_block()
like its flow counterpart, but this function can only be called after the end of a flow-val at root o...
void set_val_scalar_folded(csubstr scalar) noexcept
void set_val_tag(csubstr tag)
void end_seq_flow(bool multiline, type_bits multiline_style=FLOW_ML1)
void begin_doc_expl()
explicit doc start, with —
void set_key_tag(csubstr tag)
void begin_map_val_flow()
void end_stream() const noexcept
void set_val_scalar_dquoted(csubstr scalar) noexcept
void begin_seq_val_flow()
void set_val_scalar_plain_empty() noexcept
void begin_map_key_block()
void start_parse(const char *filename, substr ymlsrc)
void begin_seq_key_block()
void end_map_flow(bool multiline, type_bits multiline_style=FLOW_ML1)
void begin_map_val_block()
void begin_seq_val_block()
EventHandlerTree(Callbacks const &cb) noexcept
Callbacks const & callbacks() const
void set_val_scalar_squoted(csubstr scalar) noexcept
void set_val_ref(csubstr ref)
void set_key_ref(csubstr ref)
void reset(Tree *tree, id_type id)
EventHandlerTree() noexcept
void set_val_anchor(csubstr anchor)
void set_key_scalar_literal(csubstr scalar) noexcept
void actually_val_is_first_key_of_new_map_flow()
reset the previous val as the first key of a new map, with flow style.
EventHandlerTree(Tree *tree, id_type id)
void begin_seq_key_flow()
void set_key_scalar_folded(csubstr scalar) noexcept
void begin_stream() const noexcept
void add_directive_tag(csubstr handle, csubstr prefix)
void add_directive_yaml(csubstr yaml_version)
void set_key_anchor(csubstr anchor)
substr alloc_arena(size_t len)
void set_key_scalar_dquoted(csubstr scalar) noexcept
void set_val_scalar_literal(csubstr scalar) noexcept
@ requires_strings_on_buffers
void set_key_scalar_squoted(csubstr scalar) noexcept
contains the data for each YAML node.
Wraps a type_bits mask of NodeTypeBits flags with some syntactic sugar and predicates.
bool has_key() const noexcept
bool is_doc() const noexcept
bool is_seq() const noexcept
bool is_map() const noexcept
Accelerator structure to reduce memory requirements by enabling reuse of resolved tags.