rapidyaml 0.15.2
parse and emit YAML, and do it fast
Loading...
Searching...
No Matches
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
11namespace c4 {
12namespace yml {
13
14/** @cond dev */
15
16id_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
30void 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
103void 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
132id_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
169void 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
179void 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
287void 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
Common utilities and infrastructure used by ryml.
uint32_t type_bits
the integral type necessary to cover all the bits for NodeType_e
Definition node_type.hpp:26
@ VALANCH
the val has an &anchor
Definition node_type.hpp:42
@ NOTYPE
no node type or style is set
Definition node_type.hpp:32
@ VALREF
a *reference: the val references an &anchor
Definition node_type.hpp:40
@ KEY
the scalar to the left of : in a map's member
Definition node_type.hpp:33
@ VAL_STYLE
mask of VALQUO|VAL_PLAIN : all the val scalar styles for val (not container styles!...
@ 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
@ KEY_STYLE
mask of KEYQUO|KEY_PLAIN : all the key scalar styles for key (not container styles!...
@ KEYREF
a *reference: the key references an &anchor
Definition node_type.hpp:39
@ KEYANCH
the key has an &anchor
Definition node_type.hpp:41
basic_substring< const char > csubstr
an immutable string view
Definition substr.hpp:2356
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
@ NONE
an index to none
Definition common.hpp:131
#define _c4dbg_tree(...)
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 ...