rapidyaml  0.16.0
parseandemitYAML,anddoitfast
reference_resolver.cpp
Go to the documentation of this file.
2 #include "c4/yml/common.hpp"
3 #include "c4/yml/detail/dbgprint.hpp"
4 #ifdef RYML_DBG
5 #include "c4/yml/detail/print.hpp"
6 #else
7 #define _c4dbg_tree(...)
8 #define _c4dbg_node(...)
9 #endif
10 
11 namespace c4 {
12 namespace yml {
13 
14 /** @cond dev */
15 
16 id_type ReferenceResolver::count_anchors_and_refs_(id_type n)
17 {
18  id_type c = 0;
19  NodeType ty = m_tree->type(n);
20  c += ty.has_key_anchor();
21  c += ty.has_val_anchor();
22  c += ty.is_key_ref();
23  c += ty.is_val_ref();
24  c += ty.has_key() && m_tree->key(n) == "<<";
25  for(id_type ch = m_tree->first_child(n); ch != NONE; ch = m_tree->next_sibling(ch))
26  c += count_anchors_and_refs_(ch);
27  return c;
28 }
29 
30 void ReferenceResolver::gather_anchors_and_refs_(id_type n)
31 {
32  NodeType ty = m_tree->type(n);
33  // insert key refs BEFORE inserting val refs
34  if(ty.has_key())
35  {
36  if(!ty.is_key_quoted() && m_tree->key(n) == "<<")
37  {
38  _c4dbgpf("node[{}]: key is <<", n);
39  if(ty.has_val())
40  {
41  if(ty.is_val_ref())
42  {
43  _c4dbgpf("node[{}]: instance[{}]: val ref, inheriting! '{}'", n, m_refs.size(), m_tree->val_ref(n));
44  m_refs.push({VALREF, n, NONE, NONE, NONE, NONE});
45  //m_refs.push({KEYREF, n, NONE, NONE, NONE, NONE});
46  }
47  else
48  {
49  _c4dbgpf("node[{}]: not ref!", n);
50  }
51  }
52  else if(ty.is_seq())
53  {
54  // for merging multiple inheritance targets
55  // <<: [ *CENTER, *BIG ]
56  _c4dbgpf("node[{}]: is seq!", n);
57  for(id_type ich = m_tree->first_child(n); ich != NONE; ich = m_tree->next_sibling(ich))
58  {
59  _c4dbgpf("node[{}]: instance [{}]: val ref, inheriting multiple: {} '{}'", n, m_refs.size(), ich, m_tree->val_ref(ich));
60  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, !m_tree->is_container(ich), m_tree, ich);
61  m_refs.push({VALREF, ich, NONE, NONE, n, m_tree->next_sibling(n)});
62  }
63  return; // don't descend into the seq
64  }
65  else
66  {
67  RYML_ERR_VISIT_CB_(m_tree->m_callbacks, m_tree, n, "refs for << must be either val or seq");
68  }
69  }
70  else if(ty.is_key_ref())
71  {
72  _c4dbgpf("node[{}]: instance[{}]: key ref: '{}', key='{}'", n, m_refs.size(), m_tree->key_ref(n), m_tree->has_key(n) ? m_tree->key(n) : csubstr{"-"});
73  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, m_tree->key(n) != "<<", m_tree, n);
74  RYML_CHECK_VISIT_CB_(m_tree->m_callbacks, m_tree->key(n).ends_with(m_tree->key_ref(n)), m_tree, n);
75  m_refs.push({KEYREF, n, NONE, NONE, NONE, NONE});
76  }
77  }
78  // val ref
79  if(ty.is_val_ref() && (!ty.has_key() || m_tree->key(n) != "<<"))
80  {
81  _c4dbgpf("node[{}]: instance[{}]: val ref: '{}'", n, m_refs.size(), m_tree->val_ref(n));
82  RYML_CHECK_VISIT_CB_(m_tree->m_callbacks, (!ty.has_val()) || m_tree->val(n).ends_with(m_tree->val_ref(n)), m_tree, n);
83  m_refs.push({VALREF, n, NONE, NONE, NONE, NONE});
84  }
85  // anchors
86  if(ty.has_key_anchor())
87  {
88  _c4dbgpf("node[{}]: instance[{}]: key anchor: '{}'", n, m_refs.size(), m_tree->key_anchor(n));
89  RYML_CHECK_VISIT_CB_(m_tree->m_callbacks, ty.has_key(), m_tree, n);
90  m_refs.push({KEYANCH, n, NONE, NONE, NONE, NONE});
91  }
92  if(ty.has_val_anchor())
93  {
94  _c4dbgpf("node[{}]: instance[{}]: val anchor: '{}'", n, m_refs.size(), m_tree->val_anchor(n));
95  RYML_CHECK_VISIT_CB_(m_tree->m_callbacks, ty.has_val() || ty.is_container(), m_tree, n);
96  m_refs.push({VALANCH, n, NONE, NONE, NONE, NONE});
97  }
98  // recurse
99  for(id_type ch = m_tree->first_child(n); ch != NONE; ch = m_tree->next_sibling(ch))
100  gather_anchors_and_refs_(ch);
101 }
102 
103 void ReferenceResolver::gather_anchors_and_refs_()
104 {
105  _c4dbgp("gathering anchors and refs...");
106 
107  // minimize (re-)allocations by counting first
108  id_type num_anchors_and_refs = count_anchors_and_refs_(m_tree->root_id());
109  if(!num_anchors_and_refs)
110  return;
111  m_refs.reserve(num_anchors_and_refs);
112  m_refs.clear();
113 
114  // now descend through the hierarchy
115  gather_anchors_and_refs_(m_tree->root_id());
116 
117  _c4dbgpf("found {} anchors/refs", m_refs.size());
118 
119  // finally connect the reference list
120  id_type prev_anchor = NONE;
121  id_type count = 0;
122  for(auto &rd : m_refs)
123  {
124  rd.prev_anchor = prev_anchor;
125  if(rd.type.has_anchor())
126  prev_anchor = count;
127  ++count;
128  }
129  _c4dbgp("gathering anchors and refs: finished");
130 }
131 
132 id_type ReferenceResolver::lookup_(RefData const* C4_RESTRICT ra)
133 {
134  #ifdef RYML_DBG
135  id_type instance = static_cast<id_type>(ra-m_refs.m_stack);
136  id_type node = ra->node;
137  #endif
138  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, ra->type.is_key_ref() || ra->type.is_val_ref(), m_tree, ra->node);
139  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, ra->type.is_key_ref() != ra->type.is_val_ref(), m_tree, ra->node);
140  csubstr refname;
141  _c4dbgpf("instance[{}:node{}]: lookup from node={}...", instance, node, ra->node);
142  if(ra->type.is_val_ref())
143  {
144  refname = m_tree->val_ref(ra->node);
145  _c4dbgpf("instance[{}:node{}]: valref: '{}'", instance, node, refname);
146  }
147  else
148  {
149  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, ra->type.is_key_ref(), m_tree, ra->node);
150  refname = m_tree->key_ref(ra->node);
151  _c4dbgpf("instance[{}:node{}]: keyref: '{}'", instance, node, refname);
152  }
153  while(ra->prev_anchor != NONE)
154  {
155  ra = &m_refs[ra->prev_anchor];
156  _c4dbgpf("instance[{}:node{}]: lookup '{}' at [{}:node{}]: keyref='{}' valref='{}'", instance, node, refname, ra-m_refs.m_stack, ra->node,
157  (m_tree->has_key_anchor(ra->node) ? m_tree->key_anchor(ra->node) : csubstr("~")),
158  (m_tree->has_val_anchor(ra->node) ? m_tree->val_anchor(ra->node) : csubstr("~")));
159  if(m_tree->has_anchor(ra->node, refname))
160  {
161  _c4dbgpf("instance[{}:node{}]: got it at [{}:node{}]!", instance, node, ra-m_refs.m_stack, ra->node);
162  return ra->node;
163  }
164  }
165  RYML_ERR_VISIT_CB_(m_tree->m_callbacks, m_tree, ra->node, "anchor not found: '{}'", refname);
166  C4_UNREACHABLE_AFTER_ERR();
167 }
168 
169 void ReferenceResolver::reset_(Tree *t_)
170 {
171  if(t_->callbacks() != m_refs.m_callbacks)
172  {
173  m_refs.m_callbacks = t_->callbacks();
174  }
175  m_tree = t_;
176  m_refs.clear();
177 }
178 
179 void ReferenceResolver::resolve_()
180 {
181  /* from the specs: "an alias node refers to the most recent
182  * node in the serialization having the specified anchor". So
183  * we need to start looking upward from ref nodes.
184  *
185  * @see http://yaml.org/spec/1.2/spec.html#id2765878 */
186  _c4dbgp("matching anchors/refs...");
187  for(id_type i = 0, e = m_refs.size(); i < e; ++i)
188  {
189  RefData &C4_RESTRICT refdata = m_refs.top(i);
190  if( ! refdata.type.is_ref())
191  continue;
192  refdata.target = lookup_(&refdata);
193  }
194  _c4dbgp("matching anchors/refs: finished");
195 
196  // insert the resolved references
197  _c4dbgp("modifying tree...");
198  id_type prev_parent_ref = NONE;
199  id_type prev_parent_ref_after = NONE;
200  for(id_type i = 0, e = m_refs.size(); i < e; ++i)
201  {
202  RefData const& C4_RESTRICT refdata = m_refs[i];
203  _c4dbgpf("instance[{}:node{}]: {}/{}...", i, refdata.node, i+1, e);
204  if( ! refdata.type.is_ref())
205  continue;
206  _c4dbgpf("instance[{}:node{}]: is reference!", i, refdata.node);
207  NodeType nty = m_tree->type(refdata.node);
208  if(refdata.parent_ref != NONE)
209  {
210  _c4dbgpf("instance[{}:node{}] has parent: {}", i, refdata.node, refdata.parent_ref);
211  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, m_tree->is_seq(refdata.parent_ref), m_tree, refdata.node);
212  const id_type p = m_tree->parent(refdata.parent_ref);
213  const id_type after = (prev_parent_ref != refdata.parent_ref) ?
214  refdata.parent_ref//prev_sibling(rd.parent_ref_sibling)
215  :
216  prev_parent_ref_after;
217  prev_parent_ref = refdata.parent_ref;
218  prev_parent_ref_after = m_tree->duplicate_children_no_rep(refdata.target, p, after);
219  m_tree->remove(refdata.node);
220  }
221  else
222  {
223  _c4dbgpf("instance[{}:node{}] has no parent", i, refdata.node, refdata.parent_ref);
224  if(nty.has_key() && m_tree->key(refdata.node) == "<<")
225  {
226  _c4dbgpf("instance[{}:node{}] is inheriting", i, refdata.node);
227  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, nty.is_keyval(), m_tree, refdata.node);
228  const id_type p = m_tree->parent(refdata.node);
229  const id_type after = m_tree->prev_sibling(refdata.node);
230  _c4dbgpf("instance[{}:node{}] p={} after={}", i, refdata.node, p, after);
231  m_tree->duplicate_children_no_rep(refdata.target, p, after);
232  m_tree->remove(refdata.node);
233  }
234  else if(refdata.type.is_key_ref())
235  {
236  _c4dbgpf("instance[{}:node{}] is key ref", i, refdata.node);
237  NodeType tty = m_tree->type(refdata.target);
238  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, nty.is_key_ref(), m_tree, refdata.node);
239  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, tty.has_key_anchor() || tty.has_val_anchor(), m_tree, refdata.node);
240  if(tty.has_val_anchor() && m_tree->val_anchor(refdata.target) == m_tree->key_ref(refdata.node))
241  {
242  _c4dbgpf("instance[{}:node{}] target.anchor==val.anchor=={}", i, refdata.node, m_tree->val_anchor(refdata.target));
243  RYML_CHECK_VISIT_CB_(m_tree->m_callbacks, !m_tree->is_container(refdata.target), m_tree, refdata.target);
244  RYML_CHECK_VISIT_CB_(m_tree->m_callbacks, m_tree->has_val(refdata.target), m_tree, refdata.target);
245  const type_bits existing_style_flags = VAL_STYLE & m_tree->_p(refdata.target)->m_type.m_bits;
246  static_assert((VAL_STYLE >> 1u) == (KEY_STYLE), "bad flags");
247  m_tree->_p(refdata.node)->m_key.scalar = m_tree->val(refdata.target);
248  m_tree->_add_flags(refdata.node, KEY | (existing_style_flags >> 1u));
249  }
250  else
251  {
252  _c4dbgpf("instance[{}:node{}] don't inherit container flags", i, refdata.node);
253  RYML_CHECK_BASIC_CB_(m_tree->m_callbacks, m_tree->key_anchor(refdata.target) == m_tree->key_ref(refdata.node));
254  m_tree->_p(refdata.node)->m_key.scalar = m_tree->key(refdata.target);
255  // keys cannot be containers, so don't inherit container flags
256  const type_bits existing_style_flags = KEY_STYLE & m_tree->_p(refdata.target)->m_type.m_bits;
257  m_tree->_add_flags(refdata.node, KEY | existing_style_flags);
258  }
259  }
260  else // val ref
261  {
262  _c4dbgpf("instance[{}:node{}] is val ref", i, refdata.node);
263  NodeType tty = m_tree->type(refdata.target);
264  RYML_ASSERT_VISIT_CB_(m_tree->m_callbacks, refdata.type.is_val_ref(), m_tree, refdata.node);
265  if(tty.has_key_anchor() && m_tree->key_anchor(refdata.target) == m_tree->val_ref(refdata.node))
266  {
267  _c4dbgpf("instance[{}:node{}] target.anchor==key.anchor=={}", i, refdata.node, m_tree->key_anchor(refdata.target));
268  RYML_CHECK_BASIC_CB_(m_tree->m_callbacks, !tty.is_container());
269  RYML_CHECK_BASIC_CB_(m_tree->m_callbacks, tty.has_val());
270  // keys cannot be containers, so don't inherit container flags
271  const type_bits existing_style_flags = (KEY_STYLE) & m_tree->_p(refdata.target)->m_type.m_bits;
272  static_assert((KEY_STYLE << 1u) == (VAL_STYLE), "bad flags");
273  m_tree->_p(refdata.node)->m_val.scalar = m_tree->key(refdata.target);
274  m_tree->_add_flags(refdata.node, VAL | (existing_style_flags << 1u));
275  }
276  else
277  {
278  _c4dbgpf("instance[{}:node{}] duplicate contents", i, refdata.node);
279  m_tree->duplicate_contents(refdata.target, refdata.node);
280  }
281  }
282  }
283  _c4dbg_tree("after insertion", *m_tree);
284  }
285 }
286 
287 void ReferenceResolver::resolve(Tree *t_, bool clear_anchors)
288 {
289  _c4dbgp("resolving references...");
290 
291  reset_(t_);
292 
293  _c4dbg_tree("unresolved tree", *m_tree);
294 
295  gather_anchors_and_refs_();
296  if(m_refs.empty())
297  return;
298  resolve_();
299  _c4dbg_tree("resolved tree", *m_tree);
300 
301  // clear anchors and refs
302  if(clear_anchors)
303  {
304  _c4dbgp("clearing anchors/refs");
305  auto clear_ = [this]{
306  for(auto const& C4_RESTRICT ar : m_refs)
307  {
308  m_tree->rem_anchor_ref(ar.node);
309  if(ar.parent_ref != NONE)
310  if(m_tree->type(ar.parent_ref) != NOTYPE)
311  m_tree->remove(ar.parent_ref);
312  }
313  };
314  clear_();
315  // some of the elements injected during the resolution may
316  // have nested anchors; these anchors will have been newly
317  // injected during the resolution; collect again, and clear
318  // again, to ensure those are also cleared:
319  gather_anchors_and_refs_();
320  clear_();
321  _c4dbgp("clearing anchors/refs: finished");
322  }
323 
324  _c4dbg_tree("final resolved tree", *m_tree);
325 
326  m_tree = nullptr;
327  _c4dbgp("resolving references: finished");
328 }
329 
330 /** @endcond */
331 
332 } // namespace ryml
333 } // namespace c4
the val has an &anchor
Definition: node_type.hpp:42
an index to none
Definition: common.hpp:131
#define _c4dbg_tree(...)
Common utilities and infrastructure used by ryml.
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
the key has an &anchor
Definition: node_type.hpp:41
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...
Definition: common.hpp:124
void resolve(Tree *tree, bool clear_anchors=true)
Resolve references: for each reference, look for a matching anchor, and copy its contents to the ref ...
basic_substring< const char > csubstr
an immutable string view
Definition: substr.hpp:2356
a *reference: the val references an &anchor
Definition: node_type.hpp:40
the scalar to the left of : in a map's member
Definition: node_type.hpp:33
a *reference: the key references an &anchor
Definition: node_type.hpp:39
mask of KEYQUO|KEY_PLAIN : all the key scalar styles for key (not container styles!) ...
Definition: node_type.hpp:176
no node type or style is set
Definition: node_type.hpp:32
uint32_t type_bits
the integral type necessary to cover all the bits for NodeType_e
Definition: node_type.hpp:26
mask of VALQUO|VAL_PLAIN : all the val scalar styles for val (not container styles!) ...
Definition: node_type.hpp:177