rapidyaml  0.7.1
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_DETAIL_PARSER_DBG_HPP_
9 #include "c4/yml/detail/parser_dbg.hpp"
10 #endif
11 
12 #ifndef _C4_YML_PARSER_STATE_HPP_
13 #include "c4/yml/parser_state.hpp"
14 #endif
15 
16 #ifdef RYML_DBG
17 #ifndef _C4_YML_DETAIL_PRINT_HPP_
18 #include "c4/yml/detail/print.hpp"
19 #endif
20 #endif
21 
22 namespace c4 {
23 namespace yml {
24 
25 /** @addtogroup doc_event_handlers
26  * @{ */
27 
28 namespace detail {
29 using pfn_relocate_arena = void (*)(void*, csubstr prev_arena, substr next_arena);
30 } // detail
31 
32 /** Use this class a base of implementations of event handler to
33  * simplify the stack logic. */
34 template<class HandlerImpl, class HandlerState>
36 {
37  static_assert(std::is_base_of<ParserState, HandlerState>::value,
38  "ParserState must be a base of HandlerState");
39 
40  using state = HandlerState;
42 
43 public:
44 
45  detail::stack<state> m_stack;
46  state *C4_RESTRICT m_curr; ///< current stack level: top of the stack. cached here for easier access.
47  state *C4_RESTRICT m_parent; ///< parent of the current stack level.
48  pfn_relocate_arena m_relocate_arena; ///< callback when the arena gets relocated
50 
51 protected:
52 
55 
56 protected:
57 
58  void _stack_start_parse(const char *filename, pfn_relocate_arena relocate_arena, void *relocate_arena_data)
59  {
60  _RYML_CB_ASSERT(m_stack.m_callbacks, m_curr != nullptr);
61  _RYML_CB_ASSERT(m_stack.m_callbacks, relocate_arena != nullptr);
62  _RYML_CB_ASSERT(m_stack.m_callbacks, relocate_arena_data != nullptr);
63  m_curr->start_parse(filename, m_curr->node_id);
64  m_relocate_arena = relocate_arena;
65  m_relocate_arena_data = relocate_arena_data;
66  }
67 
69  {
70  }
71 
72 protected:
73 
75  {
76  m_stack.clear();
77  m_stack.push({});
78  m_parent = nullptr;
79  m_curr = &m_stack.top();
80  }
81 
83  {
84  m_stack.clear();
85  m_stack.push({}); // parent
86  m_stack.push({}); // node
87  m_parent = &m_stack.top(1);
88  m_curr = &m_stack.top();
89  }
90 
91  void _stack_push()
92  {
93  m_stack.push_top();
94  m_parent = &m_stack.top(1); // don't use m_curr. watch out for relocations inside the prev push
95  m_curr = &m_stack.top();
96  m_curr->reset_after_push();
97  }
98 
99  void _stack_pop()
100  {
101  _RYML_CB_ASSERT(m_stack.m_callbacks, m_parent);
102  _RYML_CB_ASSERT(m_stack.m_callbacks, m_stack.size() > 1);
103  m_parent->reset_before_pop(*m_curr);
104  m_stack.pop();
105  m_parent = m_stack.size() > 1 ? &m_stack.top(1) : nullptr;
106  m_curr = &m_stack.top();
107  #ifdef RYML_DBG
108  if(m_parent)
109  _c4dbgpf("popped! top is now node={} (parent={})", m_curr->node_id, m_parent->node_id);
110  else
111  _c4dbgpf("popped! top is now node={} @ ROOT", m_curr->node_id);
112  #endif
113  }
114 
115 protected:
116 
117  // undefined at the end
118  #define _has_any_(bits) (static_cast<HandlerImpl const* C4_RESTRICT>(this)->template _has_any__<bits>())
119 
121  {
122  const bool is_root = (m_stack.size() == 1u);
123  return is_root && (_has_any_(DOC|VAL|MAP|SEQ) || m_curr->has_children);
124  }
125 
127  {
128  const bool is_root = (m_stack.size() == 1u);
129  return !is_root && _has_any_(DOC);
130  }
131 
132 protected:
133 
134  void _stack_relocate_to_new_arena(csubstr prev, substr curr)
135  {
136  for(state &st : m_stack)
137  {
138  if(st.line_contents.rem.is_sub(prev))
139  st.line_contents.rem = _stack_relocate_to_new_arena(st.line_contents.rem, prev, curr);
140  if(st.line_contents.full.is_sub(prev))
141  st.line_contents.full = _stack_relocate_to_new_arena(st.line_contents.full, prev, curr);
142  if(st.line_contents.stripped.is_sub(prev))
143  st.line_contents.stripped = _stack_relocate_to_new_arena(st.line_contents.stripped, prev, curr);
144  }
145  _RYML_CB_ASSERT(m_stack.m_callbacks, m_relocate_arena != nullptr);
146  _RYML_CB_ASSERT(m_stack.m_callbacks, m_relocate_arena_data != nullptr);
148  }
149 
150  substr _stack_relocate_to_new_arena(csubstr s, csubstr prev, substr curr)
151  {
152  _RYML_CB_ASSERT(m_stack.m_callbacks, prev.is_super(s));
153  auto pos = s.str - prev.str;
154  substr out = {curr.str + pos, s.len};
155  _RYML_CB_ASSERT(m_stack.m_callbacks, curr.is_super(out));
156  return out;
157  }
158 
159 public:
160 
161  /** Check whether the current parse tokens are trailing on the
162  * previous doc, and raise an error if they are. This function is
163  * called by the parse engine (not the event handler) before a doc
164  * is started. */
166  {
167  const bool is_root = (m_stack.size() == 1u);
168  const bool isndoc = (m_curr->flags & NDOC) != 0;
169  const bool suspicious = _has_any_(MAP|SEQ|VAL);
170  _c4dbgpf("target={} isroot={} suspicious={} ndoc={}", m_curr->node_id, is_root, suspicious, isndoc);
171  if((is_root || _has_any_(DOC)) && suspicious && !isndoc)
172  _RYML_CB_ERR_(m_stack.m_callbacks, "parse error", m_curr->pos);
173  }
174 
175 protected:
176 
177  #undef _has_any_
178 
179 };
180 
181 /** @} */
182 
183 } // namespace yml
184 } // namespace c4
185 
186 #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:375
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