yeptris 0.6.18.2-arm-linux-eabihf

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/README.adoc +141 -0
  3. data/ext/build_windows_native.rb +41 -0
  4. data/ext/libyeptris/extconf.rb +57 -0
  5. data/ext/yeptris_native/cbor_ruby.c +193 -0
  6. data/ext/yeptris_native/extconf.rb +47 -0
  7. data/ext/yeptris_native/json_ruby.c +354 -0
  8. data/ext/yeptris_native/yeptris_native.c +497 -0
  9. data/lib/yeptris/cbor.rb +130 -0
  10. data/lib/yeptris/document.rb +255 -0
  11. data/lib/yeptris/ffi.rb +426 -0
  12. data/lib/yeptris/json/descriptor.rb +266 -0
  13. data/lib/yeptris/json.rb +394 -0
  14. data/lib/yeptris/materializer.rb +354 -0
  15. data/lib/yeptris/native-3.4.so +0 -0
  16. data/lib/yeptris/node.rb +453 -0
  17. data/lib/yeptris/psych/class_loader.rb +24 -0
  18. data/lib/yeptris/psych/coder_shim.rb +55 -0
  19. data/lib/yeptris/psych/drop_in.rb +63 -0
  20. data/lib/yeptris/psych/encodable.rb +14 -0
  21. data/lib/yeptris/psych/handler.rb +90 -0
  22. data/lib/yeptris/psych/parser.rb +105 -0
  23. data/lib/yeptris/psych/scalar_scanner.rb +149 -0
  24. data/lib/yeptris/psych/visitors.rb +545 -0
  25. data/lib/yeptris/psych.rb +622 -0
  26. data/lib/yeptris/schema.rb +253 -0
  27. data/lib/yeptris/valueml.rb +455 -0
  28. data/lib/yeptris/yaml/descriptor.rb +56 -0
  29. data/lib/yeptris/yaml.rb +422 -0
  30. data/lib/yeptris.rb +113 -0
  31. data/libyeptris.so +0 -0
  32. data/vendor/libyeptris/CMakeLists.txt +254 -0
  33. data/vendor/libyeptris/cmake/yeptris-config.cmake.in +5 -0
  34. data/vendor/libyeptris/cmake/yeptris.pc.in +11 -0
  35. data/vendor/libyeptris/src/CMakeLists.txt +167 -0
  36. data/vendor/libyeptris/src/include/yeptris/api.h +32 -0
  37. data/vendor/libyeptris/src/include/yeptris/cbor.h +101 -0
  38. data/vendor/libyeptris/src/include/yeptris/dom.h +190 -0
  39. data/vendor/libyeptris/src/include/yeptris/emit.h +81 -0
  40. data/vendor/libyeptris/src/include/yeptris/error.h +41 -0
  41. data/vendor/libyeptris/src/include/yeptris/events.h +157 -0
  42. data/vendor/libyeptris/src/include/yeptris/json.h +45 -0
  43. data/vendor/libyeptris/src/include/yeptris/json.hpp +258 -0
  44. data/vendor/libyeptris/src/include/yeptris/jsonc_compat.h +115 -0
  45. data/vendor/libyeptris/src/include/yeptris/marshal.h +52 -0
  46. data/vendor/libyeptris/src/include/yeptris/parse.h +48 -0
  47. data/vendor/libyeptris/src/include/yeptris/plan.h +100 -0
  48. data/vendor/libyeptris/src/include/yeptris/resolve.h +71 -0
  49. data/vendor/libyeptris/src/include/yeptris/schema.h +121 -0
  50. data/vendor/libyeptris/src/include/yeptris/tape.h +123 -0
  51. data/vendor/libyeptris/src/include/yeptris/types.h +38 -0
  52. data/vendor/libyeptris/src/include/yeptris/values.h +102 -0
  53. data/vendor/libyeptris/src/include/yeptris/version.h.in +29 -0
  54. data/vendor/libyeptris/src/include/yeptris/visit.h +77 -0
  55. data/vendor/libyeptris/src/include/yeptris/yajl_compat.h +135 -0
  56. data/vendor/libyeptris/src/include/yeptris.h +23 -0
  57. data/vendor/libyeptris/src/yeptris/build.c +376 -0
  58. data/vendor/libyeptris/src/yeptris/cbor/cbor.h +25 -0
  59. data/vendor/libyeptris/src/yeptris/cbor/decode.c +981 -0
  60. data/vendor/libyeptris/src/yeptris/cbor/encode.c +835 -0
  61. data/vendor/libyeptris/src/yeptris/cbor/sink.h +58 -0
  62. data/vendor/libyeptris/src/yeptris/common/chartype.c +41 -0
  63. data/vendor/libyeptris/src/yeptris/common/chartype.h +77 -0
  64. data/vendor/libyeptris/src/yeptris/common/cpu.c +81 -0
  65. data/vendor/libyeptris/src/yeptris/common/cpu.h +40 -0
  66. data/vendor/libyeptris/src/yeptris/common/error.c +65 -0
  67. data/vendor/libyeptris/src/yeptris/common/error.h +79 -0
  68. data/vendor/libyeptris/src/yeptris/common/mutex.h +45 -0
  69. data/vendor/libyeptris/src/yeptris/common/nametab.c +275 -0
  70. data/vendor/libyeptris/src/yeptris/common/nametab.h +68 -0
  71. data/vendor/libyeptris/src/yeptris/common/port.h +74 -0
  72. data/vendor/libyeptris/src/yeptris/common/simd_text.c +49 -0
  73. data/vendor/libyeptris/src/yeptris/common/simd_text.h +295 -0
  74. data/vendor/libyeptris/src/yeptris/common/simd_text_avx2.c +534 -0
  75. data/vendor/libyeptris/src/yeptris/common/simd_text_neon.c +662 -0
  76. data/vendor/libyeptris/src/yeptris/common/simd_text_scalar.c +298 -0
  77. data/vendor/libyeptris/src/yeptris/common/string_view.c +34 -0
  78. data/vendor/libyeptris/src/yeptris/common/string_view.h +93 -0
  79. data/vendor/libyeptris/src/yeptris/doc.h +56 -0
  80. data/vendor/libyeptris/src/yeptris/dom/dom.c +1192 -0
  81. data/vendor/libyeptris/src/yeptris/dom/dom.h +312 -0
  82. data/vendor/libyeptris/src/yeptris/dom/hpool.c +107 -0
  83. data/vendor/libyeptris/src/yeptris/dom/mapindex.c +173 -0
  84. data/vendor/libyeptris/src/yeptris/dom/mapindex.h +61 -0
  85. data/vendor/libyeptris/src/yeptris/dom/mutate.c +431 -0
  86. data/vendor/libyeptris/src/yeptris/emit/emit.c +434 -0
  87. data/vendor/libyeptris/src/yeptris/emit/float/api.h +38 -0
  88. data/vendor/libyeptris/src/yeptris/emit/float/dragon.c +405 -0
  89. data/vendor/libyeptris/src/yeptris/emit/float/floatint.h +231 -0
  90. data/vendor/libyeptris/src/yeptris/emit/float/print.c +396 -0
  91. data/vendor/libyeptris/src/yeptris/emit/style.c +81 -0
  92. data/vendor/libyeptris/src/yeptris/emit/style.h +24 -0
  93. data/vendor/libyeptris/src/yeptris/emit/writer.c +816 -0
  94. data/vendor/libyeptris/src/yeptris/emit/writer.h +76 -0
  95. data/vendor/libyeptris/src/yeptris/encoding/bom.c +31 -0
  96. data/vendor/libyeptris/src/yeptris/encoding/encoding.h +63 -0
  97. data/vendor/libyeptris/src/yeptris/encoding/transcode.c +148 -0
  98. data/vendor/libyeptris/src/yeptris/encoding/utf8_validate.c +178 -0
  99. data/vendor/libyeptris/src/yeptris/events/capture.c +150 -0
  100. data/vendor/libyeptris/src/yeptris/events/capture.h +39 -0
  101. data/vendor/libyeptris/src/yeptris/events/iterparse.c +208 -0
  102. data/vendor/libyeptris/src/yeptris/events/pull.c +97 -0
  103. data/vendor/libyeptris/src/yeptris/events/push.c +104 -0
  104. data/vendor/libyeptris/src/yeptris/events/recorder.c +107 -0
  105. data/vendor/libyeptris/src/yeptris/events/values.c +527 -0
  106. data/vendor/libyeptris/src/yeptris/events/values_priv.h +51 -0
  107. data/vendor/libyeptris/src/yeptris/events/yaml_compat.c +86 -0
  108. data/vendor/libyeptris/src/yeptris/events/yaml_compat.h +90 -0
  109. data/vendor/libyeptris/src/yeptris/jsonapi/jsonc_compat.c +687 -0
  110. data/vendor/libyeptris/src/yeptris/jsonapi/yajl_compat.c +728 -0
  111. data/vendor/libyeptris/src/yeptris/marshal.c +782 -0
  112. data/vendor/libyeptris/src/yeptris/memory/allocator.c +20 -0
  113. data/vendor/libyeptris/src/yeptris/memory/allocator.h +43 -0
  114. data/vendor/libyeptris/src/yeptris/memory/arena.c +134 -0
  115. data/vendor/libyeptris/src/yeptris/memory/arena.h +52 -0
  116. data/vendor/libyeptris/src/yeptris/memory/pool.c +122 -0
  117. data/vendor/libyeptris/src/yeptris/memory/pool.h +36 -0
  118. data/vendor/libyeptris/src/yeptris/parse/engine.c +3698 -0
  119. data/vendor/libyeptris/src/yeptris/parse/engine.h +65 -0
  120. data/vendor/libyeptris/src/yeptris/parse/events.h +124 -0
  121. data/vendor/libyeptris/src/yeptris/parse/numbers.c +349 -0
  122. data/vendor/libyeptris/src/yeptris/parse/numbers.h +33 -0
  123. data/vendor/libyeptris/src/yeptris/parse/scalars.c +416 -0
  124. data/vendor/libyeptris/src/yeptris/parse/scalars.h +75 -0
  125. data/vendor/libyeptris/src/yeptris/parse.c +737 -0
  126. data/vendor/libyeptris/src/yeptris/plan.c +824 -0
  127. data/vendor/libyeptris/src/yeptris/resolve/compat11.c +334 -0
  128. data/vendor/libyeptris/src/yeptris/resolve/core12.c +143 -0
  129. data/vendor/libyeptris/src/yeptris/resolve/resolver.h +56 -0
  130. data/vendor/libyeptris/src/yeptris/resolve/tags.c +58 -0
  131. data/vendor/libyeptris/src/yeptris/scan/json.c +792 -0
  132. data/vendor/libyeptris/src/yeptris/scan/json.h +109 -0
  133. data/vendor/libyeptris/src/yeptris/scan/scan.c +463 -0
  134. data/vendor/libyeptris/src/yeptris/scan/scan.h +150 -0
  135. data/vendor/libyeptris/src/yeptris/schema.c +308 -0
  136. data/vendor/libyeptris/src/yeptris/tape.c +1308 -0
  137. data/vendor/libyeptris/src/yeptris/tape_in.h +19 -0
  138. data/vendor/libyeptris/src/yeptris/version.c +7 -0
  139. data/vendor/libyeptris/src/yeptris/visit/dom_visit.c +115 -0
  140. data/vendor/libyeptris/src/yeptris/visit/json_visit.c +293 -0
  141. data/vendor/libyeptris/src/yeptris/visit/yaml_visit.c +127 -0
  142. metadata +199 -0
@@ -0,0 +1,545 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "time"
5
+ require "set"
6
+
7
+ module Yeptris
8
+ module Psych
9
+ module Visitors
10
+ # The dump-side visitor (Psych's YAMLTree core): an arbitrary
11
+ # Ruby object graph becomes DOM nodes — anchors for shared
12
+ # objects, aliases for repeats, the Encodable protocol, and the
13
+ # !ruby/... tags Psych's own emitter produces.
14
+ class YAMLTree
15
+ # stdlib's factory (relaton's db cache calls it): the options
16
+ # ride our visitor defaults
17
+ def self.create(options = {}, emitter = nil)
18
+ new
19
+ end
20
+
21
+ def initialize
22
+ @tree = ::Yeptris::Document.create
23
+ @names = {} # object_id -> anchor name
24
+ @counter = 0
25
+ end
26
+
27
+ def push(obj)
28
+ @refs = Hash.new(0)
29
+ @refs[obj.object_id] += 1 # the root counts as a reference
30
+ count_refs(obj, {})
31
+ root = visit(obj)
32
+ @tree.set_root(root) if root
33
+ self
34
+ end
35
+ alias_method :<<, :push # stdlib's dump body pushes via <<
36
+
37
+ # stdlib Psych.dump's tail: `visitor.tree.yaml io, options` —
38
+ # the tree wraps the built document; yaml serializes it
39
+ # (string return, or writes + returns the io)
40
+ def tree
41
+ BuiltDocument.new(@tree)
42
+ end
43
+
44
+ # The raw document (the CBOR encoder consumes the tree
45
+ # directly; #tree wraps it for stdlib's yaml face)
46
+ def document
47
+ @tree
48
+ end
49
+
50
+ def finish
51
+ @tree.serialize(explicit_doc_start: true)
52
+ end
53
+
54
+ # The built-document handle stdlib's dump tail serializes
55
+ class BuiltDocument
56
+ def initialize(doc)
57
+ @doc = doc
58
+ end
59
+
60
+ def yaml(io = nil, _options = {})
61
+ out = @doc.serialize(explicit_doc_start: true)
62
+ return out unless io
63
+
64
+ io.write(out)
65
+ io
66
+ end
67
+ end
68
+
69
+ # stdlib register()s every object — scalars included — so a
70
+ # repeated scalar dumps &name/*name (test_float_references,
71
+ # test_alias_with_time). Containers already route through
72
+ # anchor_for; this is the scalar arm of the same machinery.
73
+ def anchored_scalar(obj, text: nil, tag: nil)
74
+ state, name = anchor_for(obj)
75
+ return alias_of(obj, name) if state == :alias
76
+
77
+ n = scalar(text || obj, tag: tag)
78
+ n.set_anchor(name) if name
79
+ remember(obj, n)
80
+ n
81
+ end
82
+
83
+ def visit(obj)
84
+ case obj
85
+ # strings/floats/ints: stdlib yaml_tree does NOT anchor them
86
+ # (its float test is load-side only) — and CBOR encode
87
+ # rejects the anchors the cbor profile feeds it
88
+ when nil, true, false, ::Integer, ::Float, ::String then scalar(obj)
89
+ when ::Symbol then @tree.new_scalar(":#{obj}", :plain) # psych emits symbols bare, never through visit_String's quoting rules
90
+ # stdlib visit_DateTime: the class tag carries the type (a
91
+ # plain timestamp re-loads as Time) — "!ruby/object:DateTime
92
+ # 2017-04-13 12:00:00.500000000 +09:00", to_s text, one line
93
+ when ::DateTime
94
+ anchored_scalar(obj, text: obj.strftime("%F %H:%M:%S.%9N %:z"),
95
+ tag: "!ruby/object:DateTime")
96
+ when ::Date, ::Time then anchored_scalar(obj) # timestamp text, never ivars
97
+ when ::Hash then visit_hash(obj)
98
+ when ::Array then visit_array(obj)
99
+ when ::Struct then visit_struct(obj)
100
+ when ::Set then visit_set(obj)
101
+ when Encodable then visit_encode_with(obj)
102
+ else
103
+ if obj.instance_of?(::Object)
104
+ # A BARE Object has no declared state to lose — the
105
+ # empty !ruby/object form is correct by construction
106
+ # (an Object with ivars set from outside its class is
107
+ # outside the encapsulation contract; use a real class
108
+ # with Encodable for stateful objects).
109
+ return visit_bare_object(obj)
110
+ end
111
+ refuse_dump(obj)
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ # Psych anchors only objects that appear more than once;
118
+ # single-occurrence containers dump clean (no &o1 noise)
119
+ def anchor_for(obj)
120
+ oid = obj.object_id
121
+ name = @names[oid]
122
+ return [:alias, name] if name
123
+
124
+ if @refs[oid] < 2
125
+ return [:none, nil]
126
+ end
127
+ name = (@counter += 1).to_s # stdlib: &1, *1
128
+ @names[oid] = name
129
+ [:new, name]
130
+ end
131
+
132
+ def count_refs(obj, seen)
133
+ return if seen.key?(obj.object_id)
134
+
135
+ seen[obj.object_id] = true
136
+ case obj
137
+ when Hash
138
+ obj.each_value { |v| @refs[v.object_id] += 1; count_refs(v, seen) }
139
+ when Array
140
+ obj.each { |v| @refs[v.object_id] += 1; count_refs(v, seen) }
141
+ when Struct
142
+ obj.each_pair { |_k, v| @refs[v.object_id] += 1; count_refs(v, seen) }
143
+ when Set
144
+ obj.each { |v| @refs[v.object_id] += 1; count_refs(v, seen) }
145
+ when ::String, ::Integer, ::Float, ::Symbol, ::Date, ::Time, true, false, nil, ::Numeric
146
+ # immutables: identity anchors are meaningless
147
+ else
148
+ unless obj.instance_of?(::Object)
149
+ refuse_dump(obj) unless obj.is_a?(Encodable)
150
+ # Encodable: descend through the PUBLIC protocol so
151
+ # shared objects inside coder contents still get
152
+ # anchors — encode_with must be pure (it runs once more
153
+ # at dump).
154
+ coder = ::Yeptris::Psych::CoderShim.new(nil)
155
+ obj.encode_with(coder)
156
+ case coder.type
157
+ when :seq
158
+ coder.seq.each { |v| @refs[v.object_id] += 1; count_refs(v, seen) }
159
+ when :map
160
+ coder.each { |_k, v| @refs[v.object_id] += 1; count_refs(v, seen) }
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ def refuse_dump(obj)
167
+ raise ::Yeptris::DumpError,
168
+ "cannot dump #{obj.class}: include Yeptris::Psych::Encodable and implement encode_with(coder)"
169
+ end
170
+
171
+ def scalar(obj, tag: nil)
172
+ # strings route through the builder's plain-safety helper
173
+ # (one quoting rule, DRY); other scalars are their own text
174
+ text =
175
+ if obj.is_a?(::Time)
176
+ # psych-5's format_time (#300 family 1) — the FIRST cut
177
+ # put this branch after the Date||Time iso8601 one, dead
178
+ # code: Psych.dump of a Hash still emitted iso8601 (the
179
+ # neutral surface alone was pinned)
180
+ ::Yeptris::YAML::BulkBuilder.time_text(obj)
181
+ elsif obj.is_a?(::Date)
182
+ obj.iso8601 # canonical calendar form
183
+ elsif obj.nil?
184
+ "" # libyaml's null rendering rides bare (issue #290)
185
+ elsif obj.is_a?(::Float)
186
+ ::Yeptris::YAML::BulkBuilder.float_text(obj)
187
+ else
188
+ obj.to_s
189
+ end
190
+ n =
191
+ if obj.is_a?(::String) && !tag
192
+ ::Yeptris::YAML::Builder.build_string(@tree, obj)
193
+ else
194
+ @tree.new_scalar(text, :plain)
195
+ end
196
+ n.set_tag(tag) if tag
197
+ n
198
+ end
199
+
200
+ def visit_hash(hash)
201
+ state, name = anchor_for(hash)
202
+ return alias_of(hash, name) if state == :alias
203
+
204
+ m = @tree.new_mapping
205
+ remember(hash, m)
206
+ m.set_anchor(name) if name
207
+ hash.each do |k, v|
208
+ if k.nil?
209
+ # Psych's nil-key form: `! ''` — the explicit tag on an
210
+ # empty single-quoted key (#300 family 2)
211
+ key_node = @tree.new_scalar("", :single_quoted)
212
+ key_node.set_tag("!")
213
+ m.map_add_node(key_node, visit(v))
214
+ elsif k.is_a?(::String) && k == "<<"
215
+ # stdlib yaml_tree: a literal chevron key carries the
216
+ # explicit !!str tag (single-quoted) so it does not
217
+ # re-load as a merge
218
+ key_node = @tree.new_scalar("<<", :single_quoted)
219
+ key_node.set_tag("!!str")
220
+ m.map_add_node(key_node, visit(v))
221
+ else
222
+ m.map_add(key_text(k), visit(v))
223
+ end
224
+ end
225
+ m
226
+ end
227
+
228
+ def visit_array(ary)
229
+ state, name = anchor_for(ary)
230
+ return alias_of(ary, name) if state == :alias
231
+
232
+ s = @tree.new_sequence
233
+ remember(ary, s)
234
+ s.set_anchor(name) if name
235
+ ary.each { |e| s.seq_add(visit(e)) }
236
+ s
237
+ end
238
+
239
+ def visit_struct(strct)
240
+ state, name = anchor_for(strct)
241
+ return alias_of(strct, name) if state == :alias
242
+
243
+ m = @tree.new_mapping
244
+ remember(strct, m)
245
+ m.set_anchor(name) if name
246
+ m.set_tag(strct.class.name.to_s.empty? ? "!ruby/struct" : "!ruby/struct:#{strct.class.name}")
247
+ strct.each_pair { |k, v| m.map_add(key_text(k), visit(v)) }
248
+ m
249
+ end
250
+
251
+ def visit_set(set)
252
+ state, name = anchor_for(set)
253
+ return alias_of(set, name) if state == :alias
254
+
255
+ s = @tree.new_sequence
256
+ remember(set, s)
257
+ s.set_anchor(name) if name
258
+ s.set_tag("!ruby/set")
259
+ set.each { |e| s.seq_add(visit(e)) }
260
+ s
261
+ end
262
+
263
+ def visit_encode_with(obj)
264
+ state, name = anchor_for(obj)
265
+ return alias_of(obj, name) if state == :alias
266
+
267
+ coder = ::Yeptris::Psych::CoderShim.new(obj.class.name)
268
+ obj.encode_with(coder) # the typed public surface
269
+ # Wrap coder contents in a __init__ sub-mapping so the load
270
+ # side can dispatch back into obj.init_with(coder) without
271
+ # library-side ivar reflection (the encapsulation law).
272
+ init = @tree.new_mapping
273
+ case coder.type
274
+ when :scalar
275
+ init.map_add("__scalar__", scalar(coder.scalar))
276
+ init.map_add("__tag__", scalar(coder.tag)) if coder.tag
277
+ when :seq
278
+ inner = @tree.new_sequence
279
+ inner.set_tag(coder.tag) if coder.tag
280
+ coder.seq.each { |e| inner.seq_add(visit(e)) }
281
+ init.seq_add(inner)
282
+ else
283
+ coder.each { |k, v| init.map_add(key_text(k), visit(v)) }
284
+ init.set_tag(coder.tag) if coder.tag
285
+ end
286
+ m = @tree.new_mapping
287
+ m.set_anchor(name) if name
288
+ custom = ::Yeptris::Psych.dump_tags[obj.class]
289
+ m.set_tag(custom || "!ruby/object:#{obj.class.name}")
290
+ m.map_add("__init__", init)
291
+ remember(obj, m)
292
+ m
293
+ end
294
+
295
+ def visit_bare_object(obj)
296
+ state, name = anchor_for(obj)
297
+ return alias_of(obj, name) if state == :alias
298
+
299
+ m = @tree.new_mapping
300
+ remember(obj, m)
301
+ m.set_anchor(name) if name
302
+ m.set_tag("!ruby/object")
303
+ m
304
+ end
305
+
306
+ def alias_of(obj, name)
307
+ @tree.new_alias(@nodes.fetch(obj.object_id), name)
308
+ end
309
+
310
+ def remember(obj, node)
311
+ (@nodes ||= {})[obj.object_id] = node
312
+ end
313
+
314
+ def key_text(k)
315
+ k.is_a?(Symbol) ? ":#{k}" : k.to_s
316
+ end
317
+
318
+ end
319
+
320
+ # Load side: revival of !ruby/... tags over the parsed Nodes
321
+ # tree. The Materializer stays the plain-data fast path.
322
+ class ToRuby
323
+ def self.visit(node)
324
+ new.visit(node)
325
+ end
326
+
327
+ # stdlib ToRuby#accept's domain post-pass: normalize the tag
328
+ # (strip leading !//, ensure tag: unless x-private) and run the
329
+ # registered block over the revived result (#95 thread)
330
+ def apply_domain_type(result, node)
331
+ return result if node.tag.nil? || node.tag.empty?
332
+
333
+ key = node.tag.sub(/\A[!\/]*/, "").sub(/(,\d+)\//, '\1:')
334
+ key = "tag:#{key}" unless key.match?(/\A(?:tag:|x-private)/)
335
+ entry = ::Yeptris::Psych.domain_types[key]
336
+ return result unless entry
337
+
338
+ _value, block = entry
339
+ block.call(key, result)
340
+ end
341
+
342
+ def visit(node)
343
+ @root ||= node
344
+ result =
345
+ case node
346
+ when ::Yeptris::Psych::Nodes::Alias then visit_alias(node)
347
+ when ::Yeptris::Psych::Nodes::Scalar then visit_scalar(node)
348
+ when ::Yeptris::Psych::Nodes::Sequence then visit_sequence(node)
349
+ when ::Yeptris::Psych::Nodes::Mapping then visit_mapping(node)
350
+ else node&.to_ruby
351
+ end
352
+ # stdlib register(): an anchored node's result is THE object
353
+ # for every alias to that anchor (scalars included)
354
+ anchors[node.anchor] = result if node.respond_to?(:anchor) && node.anchor
355
+ # the domain-types post-pass (stdlib ToRuby#accept's tail):
356
+ # registered blocks transform the revived result per node
357
+ if node && !::Yeptris::Psych.domain_types.empty?
358
+ result = apply_domain_type(result, node)
359
+ end
360
+ result
361
+ end
362
+
363
+ private
364
+
365
+ def anchors
366
+ @anchors ||= {}
367
+ end
368
+
369
+ def visit_alias(node)
370
+ name = node.anchor
371
+ unless anchors.key?(name)
372
+ target = find_anchored(root_container(node), name)
373
+ anchors[name] = target ? visit(target) : nil
374
+ end
375
+ anchors[name]
376
+ end
377
+
378
+ # the walk for alias targets starts at the first node this
379
+ # visitor was handed (the document's root)
380
+ def root_container(_node)
381
+ @root
382
+ end
383
+
384
+ def find_anchored(node, name)
385
+ return node if node.anchor == name
386
+
387
+ node.children.each do |c|
388
+ found = find_anchored(c, name)
389
+ return found if found
390
+ end
391
+ nil
392
+ end
393
+
394
+ def visit_scalar(node)
395
+ node.to_ruby
396
+ end
397
+
398
+ def visit_sequence(node)
399
+ if node.tag == "!ruby/set"
400
+ set = ::Set.new
401
+ anchors[node.anchor] = set if node.anchor
402
+ node.children.each { |c| set << visit(c) }
403
+ return set
404
+ end
405
+ ary = []
406
+ anchors[node.anchor] = ary if node.anchor
407
+ node.children.each { |c| ary << visit(c) }
408
+ ary
409
+ end
410
+
411
+ def visit_mapping(node)
412
+ tag = node.tag
413
+ return hash_into({}, node) if tag.nil? || tag.empty?
414
+
415
+ case tag
416
+ when "!ruby/set"
417
+ set = Set.new
418
+ anchors[node.anchor] = set if node.anchor
419
+ node.children.each_slice(2) { |_k, v| set << visit(v) }
420
+ set
421
+ when /\A!ruby\/struct(?::(.+))?\z/
422
+ klass = resolve_class(Regexp.last_match(1).to_s)
423
+ pairs = {}
424
+ anchors[node.anchor] = nil # placeholder: filled below
425
+ node.children.each_slice(2) do |k, v|
426
+ pairs[visit(k).to_s.sub(/\A:/, "")] = visit(v)
427
+ end
428
+ keys = pairs.keys.map(&:to_sym)
429
+ built =
430
+ if klass && klass <= Struct
431
+ klass.new(*pairs.values)
432
+ else
433
+ Struct.new(*keys).new(*pairs.values)
434
+ end
435
+ anchors[node.anchor] = built if node.anchor
436
+ built
437
+ when /\A!ruby\/object(?::(.+))?\z/
438
+ klass = resolve_class(Regexp.last_match(1).to_s)
439
+ revive_object(klass, node)
440
+ else
441
+ # Psych.load_tags first (the class registry, #95 bug 4);
442
+ # anything else stays the plain mapping
443
+ if (klass = ::Yeptris::Psych.load_tags[tag])
444
+ revive_tagged(klass, node)
445
+ else
446
+ hash_into({}, node)
447
+ end
448
+ end
449
+ end
450
+
451
+ # stdlib visit_hash's merge branch, verbatim in structure: the
452
+ # key's SHAPE decides (an explicit !!str '<<' is a literal key,
453
+ # not a merge), the VALUE's node kind picks the arm, and every
454
+ # merge is TypeError-guarded (a bad element keeps the whole
455
+ # '<<' pair literal — merges are all-or-nothing).
456
+ def hash_into(h, node)
457
+ anchors[node.anchor] = h if node.anchor
458
+ node.children.each_slice(2) do |k, v|
459
+ key = visit(k)
460
+ val = visit(v)
461
+ if key == "<<" && k.tag != "tag:yaml.org,2002:str"
462
+ case v
463
+ when ::Yeptris::Psych::Nodes::Alias, ::Yeptris::Psych::Nodes::Mapping
464
+ begin
465
+ h.merge!(val)
466
+ rescue ::TypeError
467
+ h[key] = val
468
+ end
469
+ when ::Yeptris::Psych::Nodes::Sequence
470
+ begin
471
+ merged = {}
472
+ val.reverse_each { |value| merged.merge!(value) }
473
+ h.merge!(merged)
474
+ rescue ::TypeError
475
+ h[key] = val
476
+ end
477
+ else
478
+ h[key] = val
479
+ end
480
+ else
481
+ h[key] = val
482
+ end
483
+ end
484
+ h
485
+ end
486
+
487
+ # A load_tags-registered class revives from the mapping's own
488
+ # top-level pairs (Psych's coder semantics — no __init__
489
+ # wrapper, which is OUR encoder's internal convention)
490
+ def revive_tagged(klass, node)
491
+ raise ::Yeptris::DumpError,
492
+ "#{klass} is not Yeptris::Psych::Encodable: implement init_with(coder)" unless klass <= ::Yeptris::Psych::Encodable
493
+
494
+ obj = klass.allocate
495
+ anchors[node.anchor] = obj if node.anchor
496
+ coder = ::Yeptris::Psych::CoderShim.new
497
+ node.children.each_slice(2) do |k, v|
498
+ coder[k.to_ruby.to_s] = visit(v)
499
+ end
500
+ obj.init_with(coder)
501
+ obj
502
+ end
503
+
504
+ def revive_object(klass, node)
505
+ # bare !ruby/object (no class): Psych.dump(Object.new)'s form.
506
+ # An Object has no declared state — allocation is the whole
507
+ # revival (no reflection, no protocol needed).
508
+ return ::Object.allocate if klass.nil?
509
+
510
+ raise ::Yeptris::DumpError, "#{klass} is not Yeptris::Psych::Encodable: implement init_with(coder)" unless klass <= ::Yeptris::Psych::Encodable
511
+
512
+ obj = klass.allocate
513
+ anchors[node.anchor] = obj if node.anchor
514
+ # The class opts in via init_with(coder); the encoder wrote
515
+ # `coder["__init__"] = the mapping` so we can pass it back
516
+ # without library-side ivar reflection.
517
+ node.children.each_slice(2) do |k, v|
518
+ key = k.to_ruby.to_s
519
+ next unless key == "__init__"
520
+
521
+ coder = ::Yeptris::Psych::CoderShim.new
522
+ if v.is_a?(::Yeptris::Psych::Nodes::Mapping)
523
+ v.children.each_slice(2) do |ck, cv|
524
+ coder[ck.to_ruby.to_s] = visit(cv)
525
+ end
526
+ end
527
+ obj.init_with(coder)
528
+ end
529
+ obj
530
+ end
531
+
532
+ def resolve_class(name)
533
+ return nil if name.nil? || name.empty?
534
+
535
+ klass = ::Object.const_get(name)
536
+ raise DisallowedClass, name unless klass.is_a?(Class) || klass.is_a?(Module)
537
+
538
+ klass
539
+ rescue ::NameError
540
+ nil
541
+ end
542
+ end
543
+ end
544
+ end
545
+ end