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,354 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "time"
5
+
6
+ module Yeptris
7
+ # Recorder-driven Ruby materialization (TODO.impl/15 phase B).
8
+ #
9
+ # One bulk drain: the record array and string arena are read in two
10
+ # calls, then a pure-Ruby stack machine walks fixed-layout records —
11
+ # the FFI tax is O(chunks), never O(events). The DOM-walk
12
+ # materializer (Node#to_ruby) stays for node-based use; this is the
13
+ # Yeptris::YAML fast path.
14
+ class Materializer
15
+ RECORD_SIZE = 36 # YeptrisEventRecord layout (events.h, ABI-pinned)
16
+ FIELDS = 12 # unpacked values per record
17
+ RECORD_UNPACK = "C4V8" # type/style/flags/tag_id, line..tag_len
18
+
19
+ STREAM_START = 1
20
+ STREAM_END = 2
21
+ DOCUMENT_START = 3
22
+ DOCUMENT_END = 4
23
+ SEQUENCE_START = 5
24
+ SEQUENCE_END = 6
25
+ MAPPING_START = 7
26
+ MAPPING_END = 8
27
+ SCALAR = 9
28
+ ALIAS = 10
29
+
30
+ EF_IMPLICIT = 1 << 2
31
+ STYLE_PLAIN = 1
32
+
33
+ # YeptrisTagId values (resolve.h; FFI mirrors them)
34
+ TAG_STR = Yeptris::FFI::TAG_STR
35
+ TAG_INT = Yeptris::FFI::TAG_INT
36
+ TAG_FLOAT = Yeptris::FFI::TAG_FLOAT
37
+ TAG_BOOL = Yeptris::FFI::TAG_BOOL
38
+ TAG_NULL = Yeptris::FFI::TAG_NULL
39
+ TAG_TIMESTAMP = Yeptris::FFI::TAG_TIMESTAMP
40
+ TAG_BINARY = Yeptris::FFI::TAG_BINARY
41
+ TAG_MERGE = 9 # YEPTRIS_TAG_MERGE (resolve.h): a plain '<<' key
42
+
43
+ INF_WORDS = {
44
+ ".inf" => Float::INFINITY, ".Inf" => Float::INFINITY, ".INF" => Float::INFINITY,
45
+ "+.inf" => Float::INFINITY, "+.Inf" => Float::INFINITY, "+.INF" => Float::INFINITY,
46
+ "-.inf" => -Float::INFINITY, "-.Inf" => -Float::INFINITY, "-.INF" => -Float::INFINITY,
47
+ ".nan" => Float::NAN, ".NaN" => Float::NAN, ".NAN" => Float::NAN,
48
+ }.freeze
49
+ SEXAGESIMAL_INT = /\A[-+]?[1-9][0-9_]*(:[0-5]?[0-9])+\z/
50
+ SEXAGESIMAL_FLOAT = /\A[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+:[0-5]?[0-9]\.[0-9_]*\z/
51
+
52
+ class << self
53
+ # First document of the stream, or nil when the stream is empty.
54
+ def load(yaml, schema: :compat_11)
55
+ docs = load_stream(yaml, schema: schema)
56
+ docs.empty? ? nil : docs.first
57
+ end
58
+
59
+ # Every document in the stream, in order.
60
+ def load_stream(yaml, schema: :compat_11)
61
+ new(schema: schema).materialize(yaml)
62
+ end
63
+
64
+ # The shared drain seam: one parse, records + arena read once,
65
+ # ONE unpack into a flat Integer array (12 fields per record:
66
+ # type style flags tag_id line col v_off v_len a_off a_len
67
+ # t_off t_len). Both consumers ride it — the stack machine
68
+ # (materialize) and the Psych::Parser dispatch loop.
69
+ def drain(yaml, schema: :compat_11)
70
+ rec = Yeptris::FFI.yeptris_recorder_new_ex(
71
+ schema == :compat_11 ? Yeptris::FFI::SCHEMA_11_COMPAT : Yeptris::FFI::SCHEMA_12_CORE
72
+ )
73
+ begin
74
+ status = Yeptris::FFI.yeptris_recorder_feed(rec, yaml, yaml.bytesize, 1)
75
+ if status != Yeptris::FFI::OK
76
+ raise Yeptris::ParseError,
77
+ "parse failed: #{Yeptris::FFI.last_error_message}"
78
+ end
79
+ count_p = ::FFI::MemoryPointer.new(:size_t)
80
+ records = Yeptris::FFI.yeptris_recorder_records(rec, count_p)
81
+ count = Yeptris::FFI.read_c_size_t(count_p)
82
+ arena_len = ::FFI::MemoryPointer.new(:size_t)
83
+ arena_ptr = Yeptris::FFI.yeptris_recorder_arena(rec, arena_len)
84
+ arena_len_v = Yeptris::FFI.read_c_size_t(arena_len)
85
+ arena = arena_ptr.null? || arena_len_v.zero? ? +"" : arena_ptr.read_bytes(arena_len_v)
86
+ flat = records.read_bytes(count * RECORD_SIZE)
87
+ .unpack(RECORD_UNPACK * count)
88
+ [flat, arena]
89
+ ensure
90
+ Yeptris::FFI.yeptris_recorder_free(rec)
91
+ end
92
+ end
93
+
94
+ # The Ruby value of a scalar per schema — the ScalarScanner rule
95
+ # set, spec-verified against Psych case by case. Only implicit
96
+ # PLAIN scalars scan; quoting is the escape hatch.
97
+ # The record's tag_id IS the resolver's verdict (the typing
98
+ # SSOT): conversion by tag, Kernel#Integer/Float for the bytes —
99
+ # no host-side grammar. Two Psych-quirk overrides where Psych's
100
+ # scanner disagrees with the 1.1 resolver: single-char y/Y/n/N
101
+ # are STRINGS in Psych (libyaml says bool), and values the
102
+ # resolver tagged INT but Kernel rejects (mixed forms) fall back
103
+ # through sexagesimal to String.
104
+ PSYCH_TRUE = %w[y yes true on].freeze
105
+
106
+ # '<<' merge: existing keys win; sequences merge in order.
107
+ # Shared by the record walk and the value-stream walk.
108
+ def merge_into(map, obj)
109
+ case obj
110
+ when Hash
111
+ obj.each { |k, v| map[k] = v unless map.key?(k) }
112
+ when Array
113
+ obj.each { |e| merge_into(map, e) if e.is_a?(Hash) }
114
+ end
115
+ end
116
+
117
+ def parse_timestamp(v)
118
+ # stdlib scalar_scanner: date-only strings parse with
119
+ # strptime('%F', Date::GREGORIAN) — the PROLEPTIC calendar —
120
+ # not Date.parse's default ITALY reform (a 1582 cycle fails
121
+ # the ported test_julian_date otherwise)
122
+ return ::Date.strptime(v, "%F", ::Date::GREGORIAN) unless v.match?(/[Tt ]\d/)
123
+
124
+ # normalize the YAML 1.1 space forms onto iso8601 for
125
+ # xmlschema: "2001-12-14 21:59:43.10 -05:00" ->
126
+ # "2001-12-14T21:59:43.10-05:00" (Psych's scanner does the
127
+ # same dance)
128
+ ts = v.sub(/ (\d)/, 'T\1').sub(/ ([+-]\d)/, '\1').sub(/ +Z\z/i, 'Z')
129
+ # Psych reads a NAIVE timestamp (no zone, no Z) as a UTC
130
+ # instant expressed in the local zone — one wall-clock hour
131
+ # off from reading it as local time (pinned by spec against
132
+ # Psych.unsafe_load across tz shapes)
133
+ if ts.match?(/(Z|[+-]\d\d:?\d\d)\z/i)
134
+ Time.xmlschema(ts)
135
+ else
136
+ Time.xmlschema(ts + "Z").getlocal
137
+ end
138
+ rescue ArgumentError
139
+ v
140
+ end
141
+
142
+ def scan_by_tag(value, tag_id, implicit)
143
+ case tag_id
144
+ when TAG_STR
145
+ return value unless implicit
146
+
147
+ value.start_with?(":") && !value.start_with?("::") &&
148
+ value.length > 1 ? value[1..].to_sym : value
149
+ when TAG_NULL then nil
150
+ when TAG_BOOL
151
+ return value if value.length == 1 # Psych: "y"/"n" stay Strings
152
+
153
+ PSYCH_TRUE.include?(value.downcase)
154
+ when TAG_INT
155
+ int_or_string(value)
156
+ when TAG_FLOAT
157
+ float_or_string(value)
158
+ when TAG_TIMESTAMP
159
+ parse_timestamp(value)
160
+ when TAG_BINARY
161
+ value.unpack1("m") # psych to_ruby: !binary -> decode64, ASCII-8BIT (#168)
162
+ else
163
+ value
164
+ end
165
+ end
166
+
167
+ def int_or_string(value)
168
+ Integer(value.tr("_", ""))
169
+ rescue ArgumentError
170
+ # the resolver said INT but Kernel disagrees (mixed form):
171
+ # sexagesimal or back to String
172
+ return sexagesimal(value) if SEXAGESIMAL_INT.match?(value) ||
173
+ SEXAGESIMAL_FLOAT.match?(value)
174
+
175
+ value
176
+ end
177
+
178
+ def float_or_string(value)
179
+ return INF_WORDS[value] if INF_WORDS.key?(value)
180
+ return value unless value.include?(".")
181
+
182
+ # Psych's FLOAT: the exponent carries a mandatory sign —
183
+ # "1e3" and "1.5e3" are Strings (resolver quirk override)
184
+ return value if /[eE][^+-]/.match?(value)
185
+
186
+ Float(value.tr("_", ""))
187
+ rescue ArgumentError
188
+ return sexagesimal(value) if SEXAGESIMAL_FLOAT.match?(value)
189
+
190
+ value
191
+ end
192
+
193
+ private
194
+
195
+ # Psych's scanner IS Kernel#Integer on the plain digits: Ruby
196
+ # reads leading-zero strings as octal, rejects "018", takes
197
+ # 0x/0b/bases — verified case by case against Psych.
198
+ def sexagesimal(v)
199
+ is_float = v.include?(".")
200
+ total = 0
201
+ v.split(":").each_with_index do |n, e|
202
+ total += (is_float ? n.to_f : n.to_i) * (60**(e - 2).abs)
203
+ end
204
+ total
205
+ end
206
+
207
+
208
+ end
209
+
210
+ def initialize(schema: :compat_11)
211
+ @schema = schema
212
+ end
213
+
214
+ def materialize(yaml)
215
+ yaml = Yeptris.read_input(yaml)
216
+ yaml = yaml.to_s
217
+ flat, arena = Materializer.drain(yaml, schema: @schema)
218
+ walk(flat, arena)
219
+ end
220
+
221
+ private
222
+
223
+ # The stack machine: containers on a stack, a pending-key slot per
224
+ # open mapping, anchors by name (first definition wins; an alias
225
+ # yields the SAME Ruby object — identity preserved). Merge keys
226
+ # (<<) resolve inline: existing keys win, sequences merge in
227
+ # order — Psych load-time semantics.
228
+ #
229
+ # flat: one Integer per unpack field, 12 per record:
230
+ # 0 type, 1 style, 2 flags, 3 tag_id, 4 line, 5 col, 6 value_off,
231
+ # 7 value_len, 8 anchor_off, 9 anchor_len, 10 tag_off, 11 tag_len.
232
+ def walk(flat, arena)
233
+ docs = []
234
+ stack = []
235
+ anchors = {}
236
+ pending_key = []
237
+ pending_key_merge = []
238
+ # per open container: the map to merge into when this container
239
+ # closes (inline `<<:` values), nil otherwise
240
+ merge_target = []
241
+
242
+ # the arena is UTF-8 by construction (validated at parse), so
243
+ # forcing its encoding ONCE makes every slice UTF-8 for free —
244
+ # no per-scalar force_encoding
245
+ arena.force_encoding(Encoding::UTF_8)
246
+ # (each_slice DESTRUCTURING measured SLOWER than plain indexing
247
+ # here: it allocates a 12-slot Array per record. Index away.)
248
+ i = 0
249
+ n = flat.length
250
+ while i < n
251
+ type = flat[i]
252
+ case type
253
+ when SCALAR
254
+ # a plain, resolver-tagged '<<' (TAG_MERGE) merges; a
255
+ # QUOTED '<<' is a literal key — Psych merges on the tag,
256
+ # not the text
257
+ tag_id = flat[i + 3]
258
+ v = Materializer.scan_by_tag(
259
+ arena[flat[i + 6], flat[i + 7]], tag_id, (flat[i + 2] & EF_IMPLICIT) != 0
260
+ )
261
+ l = flat[i + 9]
262
+ anchors[arena[flat[i + 8], l]] = v if l != 0
263
+ # place(): the scalar fast path inlined — the overwhelming
264
+ # majority of events land here
265
+ if stack.empty?
266
+ docs[-1] = v
267
+ else
268
+ parent = stack.last
269
+ if parent.is_a?(Hash)
270
+ if (key = pending_key[-1]).nil?
271
+ pending_key[-1] = v
272
+ pending_key_merge[-1] = flat[i + 3] == TAG_MERGE
273
+ else
274
+ pending_key[-1] = nil
275
+ if pending_key_merge[-1]
276
+ merge_into(parent, v)
277
+ else
278
+ parent[key] = v
279
+ end
280
+ end
281
+ else
282
+ parent.push(v)
283
+ end
284
+ end
285
+ when MAPPING_START
286
+ h = {}
287
+ l = flat[i + 9]
288
+ anchors[arena[flat[i + 8], l]] = h if l != 0
289
+ merge_target.push(place(docs, stack, pending_key, pending_key_merge, h))
290
+ stack.push(h)
291
+ pending_key.push(nil)
292
+ pending_key_merge.push(nil)
293
+ when SEQUENCE_START
294
+ a = []
295
+ l = flat[i + 9]
296
+ anchors[arena[flat[i + 8], l]] = a if l != 0
297
+ merge_target.push(place(docs, stack, pending_key, pending_key_merge, a))
298
+ stack.push(a)
299
+ pending_key.push(nil)
300
+ pending_key_merge.push(nil)
301
+ when ALIAS
302
+ name = arena[flat[i + 6], flat[i + 7]]
303
+ raise Yeptris::ParseError, "unknown anchor: #{name.inspect}" unless anchors.key?(name)
304
+
305
+ place(docs, stack, pending_key, pending_key_merge, anchors[name])
306
+ when MAPPING_END, SEQUENCE_END
307
+ done = stack.pop
308
+ pending_key.pop
309
+ pending_key_merge.pop
310
+ target = merge_target.pop
311
+ merge_into(target, done) if target
312
+ when DOCUMENT_START
313
+ docs.push(nil)
314
+ end
315
+ i += FIELDS
316
+ end
317
+ docs
318
+ end
319
+
320
+ # Attaches obj: as the current document's root when nothing is
321
+ # open, as a sequence entry, or as the pending mapping key/value.
322
+ # Returns the merge TARGET when obj is a container placed under a
323
+ # "<<" key — the caller defers the merge to the container's END
324
+ # (an inline map's contents arrive after its start event); scalar
325
+ # and alias merges apply immediately.
326
+ def place(docs, stack, pending_key, pending_key_merge, obj)
327
+ if stack.empty?
328
+ docs[-1] = obj
329
+ return nil
330
+ end
331
+ parent = stack.last
332
+ if parent.is_a?(Hash)
333
+ if pending_key.last.nil?
334
+ pending_key[-1] = obj
335
+ pending_key_merge[-1] = false
336
+ return nil
337
+ end
338
+ key = pending_key.last
339
+ pending_key[-1] = nil
340
+ if pending_key_merge[-1]
341
+ merge_into(parent, obj)
342
+ parent
343
+ else
344
+ parent[key] = obj
345
+ nil
346
+ end
347
+ else # Array
348
+ parent.push(obj)
349
+ nil
350
+ end
351
+ end
352
+
353
+ end
354
+ end
Binary file