rapidyaml  0.9.0
parse and emit YAML, and do it fast
event_handler_stack.hpp
Go to the documentation of this file.
1 #ifndef _C4_YML_EVENT_HANDLER_STACK_HPP_
2 #define _C4_YML_EVENT_HANDLER_STACK_HPP_
3 
4 #ifndef _C4_YML_DETAIL_STACK_HPP_
5 #include "c4/yml/detail/stack.hpp"
6 #endif
7 
8 #ifndef _C4_YML_NODE_TYPE_HPP_
9 #include "c4/yml/node_type.hpp"
10 #endif
11 
12 #ifndef _C4_YML_DETAIL_PARSER_DBG_HPP_
13 #include "c4/yml/detail/parser_dbg.hpp"
14 #endif
15 
16 #ifndef _C4_YML_PARSER_STATE_HPP_
17 #include "c4/yml/parser_state.hpp"
18 #endif
19 
20 #ifdef RYML_DBG
21 #ifndef _C4_YML_DETAIL_PRINT_HPP_
22 #include "c4/yml/detail/print.hpp"
23 #endif
24 #endif
25 
26 // NOLINTBEGIN(hicpp-signed-bitwise)
27 
28 namespace c4 {
29 namespace yml {
30 
31 /** @addtogroup doc_event_handlers
32  * @{ */
33 
34 namespace detail {
35 using pfn_relocate_arena = void (*)(void*, csubstr prev_arena, substr next_arena);
36 } // detail
37 
38 /** Use this class a base of implementations of event handler to
39  * simplify the stack logic. */
40 template<class HandlerImpl, class HandlerState>
42 {
43  static_assert(std::is_base_of<ParserState, HandlerState>::value,
44  "ParserState must be a base of HandlerState");
45 
46  using state = HandlerState;
48 
49 public:
50 
51  detail::stack<state> m_stack;
52  state *C4_RESTRICT m_curr; ///< current stack level: top of the stack. cached here for easier access.
53  state *C4_RESTRICT m_parent; ///< parent of the current stack level.
54  pfn_relocate_arena m_relocate_arena; ///< callback when the arena gets relocated
56 
57 protected:
58 
61 
62 protected:
63 
64  void _stack_start_parse(const char *filename, pfn_relocate_arena relocate_arena, void *relocate_arena_data)
65  {
66  _RYML_CB_ASSERT(m_stack.m_callbacks, m_curr != nullptr);
67  _RYML_CB_ASSERT(m_stack.m_callbacks, relocate_arena != nullptr);
68  _RYML_CB_ASSERT(m_stack.m_callbacks, relocate_arena_data != nullptr);
69  m_curr->start_parse(filename, m_curr->node_id);
70  m_relocate_arena = relocate_arena;
71  m_relocate_arena_data = relocate_arena_data;
72  }
73 
75  {
76  }
77 
78 protected:
79 
81  {
82  m_stack.clear();
83  m_stack.push({});
84  m_parent = nullptr;
85  m_curr = &m_stack.top();
86  }
87 
89  {
90  m_stack.clear();
91  m_stack.push({}); // parent
92  m_stack.push({}); // node
93  m_parent = &m_stack.top(1);
94  m_curr = &m_stack.top();
95  }
96 
97  void _stack_push()
98  {
99  m_stack.push_top();
100  m_parent = &m_stack.top(1); // don't use m_curr. watch out for relocations inside the prev push
101  m_curr = &m_stack.top();
102  m_curr->reset_after_push();
103  }
104 
105  void _stack_pop()
106  {
107  _RYML_CB_ASSERT(m_stack.m_callbacks, m_parent);
108  _RYML_CB_ASSERT(m_stack.m_callbacks, m_stack.size() > 1);
109  m_parent->reset_before_pop(*m_curr);
110  m_stack.pop();
111  m_parent = m_stack.size() > 1 ? &m_stack.top(1) : nullptr;
112  m_curr = &m_stack.top();
113  #ifdef RYML_DBG
114  if(m_parent)
115  _c4dbgpf("popped! top is now node={} (parent={})", m_curr->node_id, m_parent->node_id);
116  else
117  _c4dbgpf("popped! top is now node={} @ ROOT", m_curr->node_id);
118  #endif
119  }
120 
121 protected:
122 
123  // undefined at the end
124  #define _has_any_(bits) (static_cast<HandlerImpl const* C4_RESTRICT>(this)->template _has_any__<bits>())
125 
127  {
128  const bool is_root = (m_stack.size() == 1u);
129  return is_root && (_has_any_(DOC|VAL|MAP|SEQ) || m_curr->has_children);
130  }
131 
133  {
134  const bool is_root = (m_stack.size() == 1u);
135  return !is_root && _has_any_(DOC);
136  }
137 
138 protected:
139 
140  void _stack_relocate_to_new_arena(csubstr prev, substr curr)
141  {
142  for(state &st : m_stack)
143  {
144  if(st.line_contents.rem.is_sub(prev))
145  st.line_contents.rem = _stack_relocate_to_new_arena(st.line_contents.rem, prev, curr);
146  if(st.line_contents.full.is_sub(prev))
147  st.line_contents.full = _stack_relocate_to_new_arena(st.line_contents.full, prev, curr);
148  if(st.line_contents.stripped.is_sub(prev))
149  st.line_contents.stripped = _stack_relocate_to_new_arena(st.line_contents.stripped, prev, curr);
150  }
151  _RYML_CB_ASSERT(m_stack.m_callbacks, m_relocate_arena != nullptr);
152  _RYML_CB_ASSERT(m_stack.m_callbacks, m_relocate_arena_data != nullptr);
154  }
155 
156  substr _stack_relocate_to_new_arena(csubstr s, csubstr prev, substr curr)
157  {
158  _RYML_CB_ASSERT(m_stack.m_callbacks, prev.is_super(s));
159  auto pos = s.str - prev.str;
160  substr out = {curr.str + pos, s.len};
161  _RYML_CB_ASSERT(m_stack.m_callbacks, curr.is_super(out));
162  return out;
163  }
164 
165 public:
166 
167  /** Check whether the current parse tokens are trailing on the
168  * previous doc, and raise an error if they are. This function is
169  * called by the parse engine (not the event handler) before a doc
170  * is started. */
172  {
173  const bool is_root = (m_stack.size() == 1u);
174  const bool isndoc = (m_curr->flags & NDOC) != 0;
175  const bool suspicious = _has_any_(MAP|SEQ|VAL);
176  _c4dbgpf("target={} isroot={} suspicious={} ndoc={}", m_curr->node_id, is_root, suspicious, isndoc);
177  if((is_root || _has_any_(DOC)) && suspicious && !isndoc)
178  _RYML_CB_ERR_(m_stack.m_callbacks, "parse error", m_curr->pos);
179  }
180 
181 protected:
182 
183  #undef _has_any_
184 
185 };
186 
187 /** @} */
188 
189 } // namespace yml
190 } // namespace c4
191 
192 // NOLINTEND(hicpp-signed-bitwise)
193 
194 #endif /* _C4_YML_EVENT_HANDLER_STACK_HPP_ */
#define _has_any_(bits)
@ MAP
a map: a parent of KEYVAL/KEYSEQ/KEYMAP nodes
Definition: node_type.hpp:35
@ VAL
a scalar: has a scalar (ie string) value, possibly empty. must be a leaf node, and cannot be MAP or S...
Definition: node_type.hpp:34
@ SEQ
a seq: a parent of VAL/SEQ/MAP nodes
Definition: node_type.hpp:36
@ DOC
a document
Definition: node_type.hpp:37
void(*)(void *, csubstr prev_arena, substr next_arena) pfn_relocate_arena
@ NDOC
no document mode. a document has ended and another has not started yet.
Definition: common.cpp:12
a c-style callbacks class.
Definition: common.hpp:376
Use this class a base of implementations of event handler to simplify the stack logic.
substr _stack_relocate_to_new_arena(csubstr s, csubstr prev, substr curr)
state * m_curr
current stack level: top of the stack. cached here for easier access.
void _stack_start_parse(const char *filename, pfn_relocate_arena relocate_arena, void *relocate_arena_data)
pfn_relocate_arena m_relocate_arena
callback when the arena gets relocated
EventHandlerStack(Callbacks const &cb)
void check_trailing_doc_token() const
Check whether the current parse tokens are trailing on the previous doc, and raise an error if they a...
detail::stack< state > m_stack
void _stack_relocate_to_new_arena(csubstr prev, substr curr)
state * m_parent
parent of the current stack level.
detail::pfn_relocate_arena pfn_relocate_arena