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")
49 using state = EventHandlerTreeState;
57 Tree *C4_RESTRICT m_tree;
62 #define ryml_enable_(bits) enable_<bits>(); _c4dbgpf("node[{}]: enable {}", m_curr->node_id, #bits)
63 #define ryml_disable_(bits) disable_<bits>(); _c4dbgpf("node[{}]: disable {}", m_curr->node_id, #bits)
65 #define ryml_enable_(bits) enable_<bits>()
66 #define ryml_disable_(bits) disable_<bits>()
68 #define ryml_hasany_(bits) has_any_<bits>()
86 RYML_ERR_BASIC_CB_(
m_stack.m_callbacks,
"null tree");
87 if C4_UNLIKELY(
id >= tree->
capacity())
88 RYML_ERR_VISIT_CB_(tree->
callbacks(), tree,
id,
"invalid node");
89 if C4_UNLIKELY(!tree->
is_root(
id))
91 if C4_UNLIKELY(!tree->
has_key(
id))
92 RYML_ERR_BASIC_CB_(tree->
callbacks(),
"destination node belongs to a map and has no key");
94 if(m_tree->is_root(
id))
97 _reset_parser_state(
m_curr,
id, m_tree->root_id());
102 _reset_parser_state(
m_parent,
id, m_tree->parent(
id));
103 _reset_parser_state(
m_curr,
id,
id);
123 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree !=
nullptr);
129 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree !=
nullptr);
172 _c4dbgp(
"begin_doc");
176 _set_root_as_stream();
180 m_curr_doc =
m_curr->node_id;
186 m_curr_doc = m_tree->size();
189 _remove_speculative();
198 _c4dbgp(
"begin_doc_expl");
199 RYML_ASSERT_VISIT_CB_(
m_stack.m_callbacks, m_tree->root_id() ==
m_curr->node_id, m_tree,
m_curr->node_id);
200 if(m_tree->is_stream(m_tree->root_id()))
207 _c4dbgp(
"ensure stream");
208 _set_root_as_stream();
209 const id_type root = m_tree->root_id();
210 const id_type first = m_tree->first_child(root);
211 RYML_ASSERT_VISIT_CB_(
m_stack.m_callbacks, m_tree->is_stream(root), m_tree, root);
212 RYML_ASSERT_VISIT_CB_(
m_stack.m_callbacks, m_tree->num_children(root) == 1u, m_tree, root);
213 if(m_tree->is_container(first) || m_tree->is_val(first))
217 #ifdef RYML_WITH_COMMENTS
218 m_tree->_p(root)->m_first_comment =
NONE;
219 m_tree->_p(root)->m_last_comment =
NONE;
226 _remove_speculative();
227 m_curr->node_id = m_tree->last_child(root);
232 m_curr_doc =
m_curr->node_id;
237 _c4dbgp(
"end_doc_expl");
238 m_curr_doc = m_tree->size();
239 _remove_speculative();
256 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"ryml trees cannot handle containers as keys");
260 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"ryml trees cannot handle containers as keys");
265 _c4dbgpf(
"node[{}]: begin_map_val_flow",
m_curr->node_id);
266 RYML_CHECK_BASIC_CB_(
m_stack.m_callbacks, !ryml_hasany_(
VAL));
273 _c4dbgpf(
"node[{}]: begin_map_val_block",
m_curr->node_id);
274 RYML_CHECK_BASIC_CB_(
m_stack.m_callbacks, !ryml_hasany_(
VAL));
288 _c4dbgpf(
"node[{}]: end_map. multiline={} startline={} endline={}",
m_parent->node_id, multiline,
m_parent->pos.line,
m_curr->pos.line);
293 enable_(multiline_style);
306 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"ryml trees cannot handle containers as keys");
310 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"ryml trees cannot handle containers as keys");
315 _c4dbgpf(
"node[{}]: begin_seq_val_flow",
m_curr->node_id);
316 RYML_CHECK_BASIC_CB_(
m_stack.m_callbacks, !ryml_hasany_(
VAL));
323 _c4dbgpf(
"node[{}]: begin_seq_val_block",
m_curr->node_id);
324 RYML_CHECK_BASIC_CB_(
m_stack.m_callbacks, !ryml_hasany_(
VAL));
338 _c4dbgpf(
"node[{}]: end_seq. multiline={} startline={} endline={}",
m_parent->node_id, multiline,
m_parent->pos.line,
m_curr->pos.line);
343 enable_(multiline_style);
356 #if defined(__GNUC__) && (__GNUC__ >= 6)
357 C4_SUPPRESS_WARNING_GCC_WITH_PUSH(
"-Wnull-dereference")
359 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
361 RYML_ASSERT_VISIT_CB_(
m_stack.m_callbacks, m_tree->has_children(
m_parent->node_id), m_tree,
m_parent->node_id);
362 NodeData const*
const prev = m_tree->m_buf;
363 _set_state_(
m_curr, m_tree->_append_child__unprotected(
m_parent->node_id));
364 if(prev != m_tree->m_buf)
365 _refresh_after_relocation();
366 _c4dbgpf(
"node[{}]: added sibling={} prev={}",
m_parent->node_id,
m_curr->node_id, m_tree->prev_sibling(
m_curr->node_id));
367 #if defined(__GNUC__) && (__GNUC__ >= 6)
368 C4_SUPPRESS_WARNING_GCC_POP
379 if C4_UNLIKELY(m_tree->is_container(
m_curr->node_id))
380 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"ryml trees cannot handle containers as keys");
383 RYML_ASSERT_VISIT_CB_(
m_stack.m_callbacks, !m_tree->is_container(
m_curr->node_id), m_tree,
m_curr->node_id);
384 RYML_ASSERT_VISIT_CB_(
m_stack.m_callbacks, !m_tree->has_key(
m_curr->node_id), m_tree,
m_curr->node_id);
387 m_curr->tr_data->m_val = {};
401 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"ryml trees cannot handle containers as keys");
414 _c4dbgpf(
"node[{}]: set key scalar plain as empty",
m_curr->node_id);
415 m_curr->tr_data->m_key.scalar = {};
420 _c4dbgpf(
"node[{}]: set val scalar plain as empty",
m_curr->node_id);
421 m_curr->tr_data->m_val.scalar = {};
427 _c4dbgpf(
"node[{}]: set key scalar plain: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
428 m_curr->tr_data->m_key.scalar = scalar;
433 _c4dbgpf(
"node[{}]: set val scalar plain: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
434 m_curr->tr_data->m_val.scalar = scalar;
441 _c4dbgpf(
"node[{}]: set key scalar dquot: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
442 m_curr->tr_data->m_key.scalar = scalar;
447 _c4dbgpf(
"node[{}]: set val scalar dquot: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
448 m_curr->tr_data->m_val.scalar = scalar;
455 _c4dbgpf(
"node[{}]: set key scalar squot: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
456 m_curr->tr_data->m_key.scalar = scalar;
461 _c4dbgpf(
"node[{}]: set val scalar squot: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
462 m_curr->tr_data->m_val.scalar = scalar;
469 _c4dbgpf(
"node[{}]: set key scalar literal: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
470 m_curr->tr_data->m_key.scalar = scalar;
475 _c4dbgpf(
"node[{}]: set val scalar literal: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
476 m_curr->tr_data->m_val.scalar = scalar;
483 _c4dbgpf(
"node[{}]: set key scalar folded: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
484 m_curr->tr_data->m_key.scalar = scalar;
489 _c4dbgpf(
"node[{}]: set val scalar folded: [{}]~~~{}~~~",
m_curr->node_id, scalar.len, scalar);
490 m_curr->tr_data->m_val.scalar = scalar;
513 _c4dbgpf(
"node[{}]: set key anchor: [{}]~~~{}~~~",
m_curr->node_id, anchor.
len, anchor);
514 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
515 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, !ryml_hasany_(
KEYREF));
516 RYML_ASSERT_PARSE_CB_(m_tree->callbacks(), !anchor.
begins_with(
'&'),
m_curr->pos);
518 m_curr->tr_data->m_key.anchor = anchor;
522 _c4dbgpf(
"node[{}]: set val anchor: [{}]~~~{}~~~",
m_curr->node_id, anchor.
len, anchor);
523 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
524 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, !ryml_hasany_(
VALREF));
525 RYML_ASSERT_PARSE_CB_(m_tree->callbacks(), !anchor.
begins_with(
'&'),
m_curr->pos);
527 m_curr->tr_data->m_val.anchor = anchor;
532 _c4dbgpf(
"node[{}]: set key ref: [{}]~~~{}~~~",
m_curr->node_id, ref.
len, ref);
536 m_curr->tr_data->m_key.anchor = ref.
sub(1);
537 m_curr->tr_data->m_key.scalar = ref;
541 _c4dbgpf(
"node[{}]: set val ref: [{}]~~~{}~~~",
m_curr->node_id, ref.
len, ref);
545 m_curr->tr_data->m_val.anchor = ref.
sub(1);
546 m_curr->tr_data->m_val.scalar = ref;
558 _c4dbgpf(
"node[{}]: set key tag: [{}]~~~{}~~~",
m_curr->node_id, tag.
len, tag);
560 m_curr->tr_data->m_key.tag = tag;
564 _c4dbgpf(
"node[{}]: set val tag: [{}]~~~{}~~~",
m_curr->node_id, tag.
len, tag);
566 m_curr->tr_data->m_val.tag = tag;
578 _c4dbgpf(
"%YAML directive! version={}", yaml_version);
584 _c4dbgpf(
"%TAG directive! handle={} prefix={} id={}", handle, prefix, m_curr_doc);
585 if C4_UNLIKELY(!m_tree->m_tag_directives.add(handle, prefix, m_curr_doc))
586 RYML_ERR_PARSE_CB_(
m_stack.m_callbacks,
m_curr->pos,
"too many %TAG directives");
598 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
599 return m_tree->m_arena.first(m_tree->m_arena_pos);
603 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
604 return m_tree->m_arena.sub(m_tree->m_arena_pos);
608 return m_tree->alloc_arena(len);
618 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
619 _set_state_(st, node);
620 const NodeType type = m_tree->type(node);
623 _c4dbgpf(
"resetting state: initial flags={}", detail::_parser_flags_to_str(flagbuf, st->flags));
627 _c4dbgpf(
"node[{}] is notype", node);
628 if(m_tree->is_root(parse_root))
630 _c4dbgpf(
"node[{}] is root", node);
635 _c4dbgpf(
"node[{}] is not root. setting USTY", node);
641 _c4dbgpf(
"node[{}] is map", node);
646 _c4dbgpf(
"node[{}] is map", node);
651 _c4dbgpf(
"node[{}] has key. setting USTY", node);
656 RYML_ERR_VISIT_CB_(m_tree->callbacks(), m_tree, node,
"cannot append to node");
660 _c4dbgpf(
"node[{}] is doc", node);
664 _c4dbgpf(
"resetting state: final flags={}", detail::_parser_flags_to_str(flagbuf, st->flags));
673 NodeData
const* prev = m_tree->m_buf;
674 m_curr->node_id = m_tree->_append_child__unprotected(
m_parent->node_id);
676 if(prev != m_tree->m_buf)
677 _refresh_after_relocation();
683 _remove_speculative_with_parent();
689 C4_ALWAYS_INLINE
void enable_(
type_bits bits)
noexcept
691 m_curr->tr_data->m_type.m_bits |= bits;
693 template<type_bits bits> C4_HOT C4_ALWAYS_INLINE
void enable_() noexcept
695 m_curr->tr_data->m_type.m_bits |= bits;
697 template<type_bits bits> C4_HOT C4_ALWAYS_INLINE
void disable_() noexcept
699 m_curr->tr_data->m_type.m_bits &= ~bits;
701 template<type_bits bits> C4_HOT C4_ALWAYS_INLINE
bool has_any_() const noexcept
703 return (
m_curr->tr_data->m_type.m_bits & bits) != 0;
708 C4_ALWAYS_INLINE
void _set_state_(
state *C4_RESTRICT s,
id_type id)
const noexcept
711 s->tr_data = m_tree->_p(
id);
713 void _refresh_after_relocation()
715 _c4dbgp(
"tree: refreshing stack data after tree data relocation");
717 st.tr_data = m_tree->_p(st.node_id);
720 void _set_root_as_stream()
722 _c4dbgp(
"set root as stream");
723 RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->root_id() == 0u, m_tree, m_tree->root_id());
724 RYML_ASSERT_VISIT_CB_(m_tree->callbacks(),
m_curr->node_id == 0u, m_tree,
m_curr->node_id);
725 m_tree->set_root_as_stream();
726 RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->is_stream(m_tree->root_id()), m_tree, m_tree->root_id());
727 RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->has_children(m_tree->root_id()), m_tree, m_tree->root_id());
728 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());
729 _set_state_(
m_curr, m_tree->root_id());
732 static NodeData _val2key_(NodeData
const& C4_RESTRICT d)
noexcept
738 static_assert((VALMASK_ >> 1u) == KEYMASK_,
"required for this function to work");
740 r.m_type.m_bits = ((d.m_type.m_bits & (VALMASK_|
VAL_STYLE)) >> 1u);
741 r.m_type.m_bits = (r.m_type.m_bits & ~(VALMASK_|
VAL_STYLE));
742 r.m_type.m_bits = (r.m_type.m_bits |
KEY);
743 if(d.m_type.m_bits &
VALNIL)
744 r.m_type.m_bits = (r.m_type.m_bits |
KEYNIL);
748 void _remove_speculative()
750 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
751 RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), !m_tree->empty());
752 const id_type last_added = m_tree->size() - 1;
753 const NodeData *C4_RESTRICT d = m_tree->_p(last_added);
754 if(d->m_parent !=
NONE && d->m_type ==
NOTYPE)
756 _c4dbgpf(
"remove speculative: currparent={} node={} parent(node)={}",
m_parent->node_id, last_added, d->m_parent);
757 m_tree->remove(last_added);
762 void _remove_speculative_with_parent()
764 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
765 RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), !m_tree->empty());
766 const id_type last_added = m_tree->size() - 1;
767 RYML_ASSERT_VISIT_CB_(m_tree->callbacks(), m_tree->has_parent(last_added), m_tree, last_added);
768 if(m_tree->_p(last_added)->m_type ==
NOTYPE)
770 _c4dbgpf(
"remove speculative node with parent. parent={} node={} parent(node)={}",
m_parent->node_id, last_added, m_tree->parent(last_added));
771 m_tree->remove(last_added);
776 C4_ALWAYS_INLINE
void _save_loc()
778 RYML_ASSERT_BASIC_CB_(
m_stack.m_callbacks, m_tree);
779 RYML_ASSERT_BASIC_CB_(m_tree->callbacks(), m_tree->_p(
m_curr->node_id)->m_val.scalar.len == 0);
780 m_tree->_p(
m_curr->node_id)->m_val.scalar.str =
m_curr->line_contents.rem.str;
796C4_SUPPRESS_WARNING_MSVC_POP
797C4_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()
@ requires_strings_on_buffers
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
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.