yeptris 0.6.51.0 → 0.6.52.0
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.
- checksums.yaml +4 -4
- data/lib/yeptris/document.rb +12 -5
- data/lib/yeptris/psych/parser.rb +4 -0
- data/lib/yeptris/psych.rb +15 -2
- data/lib/yeptris/valueml.rb +23 -31
- data/lib/yeptris.rb +1 -1
- data/vendor/libyeptris/src/yeptris/parse/engine.c +12 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 39841fd37a06d428edeecabd2ec9b89ebbf3b0576e3a113a43b36eca0a0c67d0
|
|
4
|
+
data.tar.gz: 3fae8c03db5dbb3ddc31f0711a36cf6851b3abb7883e334dc161105070c6f6df
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 288cf0885ea87923b8974793617a8420ac6115df05b03615170b6d4abb7d779227412904d324951dcd81009e99a3240b9cc0bb77be2087fd6d4b03d43f0c572c
|
|
7
|
+
data.tar.gz: 1556db96baf38b002a65a0b79e0dc112de10a5bddd808e66600ada366c33c6fd408590da118f7d2d47d2f605c427bd2ae886b0d0246b2785bea7eaab2882e836
|
data/lib/yeptris/document.rb
CHANGED
|
@@ -16,7 +16,7 @@ class Yeptris::Document
|
|
|
16
16
|
# The schema this document was parsed with (a parse property).
|
|
17
17
|
attr_reader :parse_schema
|
|
18
18
|
|
|
19
|
-
def initialize(c_ptr = nil, freed = Freed.new(:alive), parse_schema = :core_12)
|
|
19
|
+
def initialize(c_ptr = nil, freed = Freed.new(:alive), parse_schema = :core_12, source = nil)
|
|
20
20
|
# The pointer is produced by the FFI parse/create calls; a nil (or
|
|
21
21
|
# non-pointer) argument is an API misuse — Document.parse and
|
|
22
22
|
# Document.create are the entry points. Fail with that guidance
|
|
@@ -30,6 +30,13 @@ class Yeptris::Document
|
|
|
30
30
|
@c_ptr = c_ptr
|
|
31
31
|
@freed = freed
|
|
32
32
|
@parse_schema = parse_schema
|
|
33
|
+
# the C document BORROWS the input: string values are zero-copy
|
|
34
|
+
# views over the caller's bytes (parse.h/doc.h) — the FFI call only
|
|
35
|
+
# pins the String for the call's duration, so the document must
|
|
36
|
+
# retain it or the next GC frees memory every later node.value/
|
|
37
|
+
# tag read still points into (audit 2026-10-31: parse(io.read) was
|
|
38
|
+
# collectable immediately)
|
|
39
|
+
@source = source
|
|
33
40
|
@readonly = false
|
|
34
41
|
# Strong wrapper cache keyed on the C node address: the same C node
|
|
35
42
|
# always yields the same Ruby object (identity for aliases/eql?),
|
|
@@ -61,7 +68,7 @@ class Yeptris::Document
|
|
|
61
68
|
raise Yeptris::ParseError,
|
|
62
69
|
"parse failed: #{Yeptris::FFI.last_error_message}"
|
|
63
70
|
end
|
|
64
|
-
wrap_schema(doc, schema)
|
|
71
|
+
wrap_schema(doc, schema, yaml)
|
|
65
72
|
end
|
|
66
73
|
|
|
67
74
|
def self.parse_json(json)
|
|
@@ -71,7 +78,7 @@ class Yeptris::Document
|
|
|
71
78
|
raise Yeptris::ParseError,
|
|
72
79
|
"json parse failed: #{Yeptris::FFI.last_error_message}" if doc.null?
|
|
73
80
|
|
|
74
|
-
wrap_schema(doc, :core_12) # strict JSON is core by construction
|
|
81
|
+
wrap_schema(doc, :core_12, json) # strict JSON is core by construction
|
|
75
82
|
end
|
|
76
83
|
|
|
77
84
|
# An empty document for from-scratch construction (TODO.impl/11 p3).
|
|
@@ -99,8 +106,8 @@ class Yeptris::Document
|
|
|
99
106
|
# host scalar policies (Psych's dot-required float) are conditioned
|
|
100
107
|
# on it. The parse constructors set the real one; plain wrap keeps
|
|
101
108
|
# the core_12 default (yeptris_parse's default).
|
|
102
|
-
def self.wrap_schema(c_ptr, schema)
|
|
103
|
-
new(c_ptr, Freed.new(:alive), schema)
|
|
109
|
+
def self.wrap_schema(c_ptr, schema, source = nil)
|
|
110
|
+
new(c_ptr, Freed.new(:alive), schema, source)
|
|
104
111
|
end
|
|
105
112
|
|
|
106
113
|
# #182: the stream-children wrapper owns NOTHING (the parent
|
data/lib/yeptris/psych/parser.rb
CHANGED
|
@@ -25,6 +25,10 @@ module Yeptris
|
|
|
25
25
|
yaml = yaml.to_s
|
|
26
26
|
@filename = filename
|
|
27
27
|
flat, arena = Materializer.drain(yaml, schema: :compat_11)
|
|
28
|
+
# the engine's arena is UTF-8 bytes; without the tag every
|
|
29
|
+
# scalar reached handlers as ASCII-8BIT (stdlib Psych hands
|
|
30
|
+
# UTF-8; TreeBuilder then propagated binary into node values)
|
|
31
|
+
arena.force_encoding(Encoding::UTF_8)
|
|
28
32
|
dispatch(flat, arena)
|
|
29
33
|
self
|
|
30
34
|
rescue ParseError => e
|
data/lib/yeptris/psych.rb
CHANGED
|
@@ -143,8 +143,21 @@ module Yeptris
|
|
|
143
143
|
# Psych 5: load is safe — plain data structures only. Tagged
|
|
144
144
|
# nodes raise DisallowedClass unless their class is permitted
|
|
145
145
|
# (Date/Time/Symbol are built in; they are plain data here).
|
|
146
|
-
def load(yaml, permitted_classes: [::Date, ::Time], aliases: false,
|
|
147
|
-
|
|
146
|
+
def load(yaml, permitted_classes: [::Date, ::Time], aliases: false,
|
|
147
|
+
symbolize_names: false, fallback: nil, filename: nil, **)
|
|
148
|
+
res = safe_load(yaml, permitted_classes: permitted_classes, aliases: aliases,
|
|
149
|
+
symbolize_names: symbolize_names)
|
|
150
|
+
if res.nil? && !fallback.nil?
|
|
151
|
+
# stdlib's fallback: answers when the stream is empty or has
|
|
152
|
+
# NO documents — a document whose content IS nil still loads
|
|
153
|
+
# as nil. The safe path cannot tell those two apart, so the
|
|
154
|
+
# corner re-checks the count (only when a fallback rides).
|
|
155
|
+
doc = ::Yeptris::Document.parse(yaml, schema: :compat_11)
|
|
156
|
+
empty = doc.nil? || doc.document_count.zero?
|
|
157
|
+
doc&.free
|
|
158
|
+
return fallback if empty
|
|
159
|
+
end
|
|
160
|
+
res
|
|
148
161
|
end
|
|
149
162
|
|
|
150
163
|
# Full revival over the Nodes tree: !ruby/object, !ruby/struct,
|
data/lib/yeptris/valueml.rb
CHANGED
|
@@ -20,6 +20,25 @@ module Yeptris
|
|
|
20
20
|
# int64 the C resolver leaves the scalar a string; Psych
|
|
21
21
|
# materializes Integer (issue #31) — every walk rebuilds it.
|
|
22
22
|
PSYCH_INT_SHAPE = /\A[-+]?(?:0|[1-9](?:[0-9]|,[0-9]|_[0-9])*)\z/.freeze
|
|
23
|
+
|
|
24
|
+
# The typed-string conversion law, ONE place: a big-int-shaped
|
|
25
|
+
# string rebuilds to Integer, an implicit-plain ':name' scans to a
|
|
26
|
+
# Symbol (Psych's ScalarScanner; quoted ':x' stays a String).
|
|
27
|
+
# Every str-column conversion rides this — the pair fast path
|
|
28
|
+
# drifted once and handed bignums/symbol-keys through as Strings
|
|
29
|
+
# (audit 2026-10-31).
|
|
30
|
+
def self.str_to_value(text, typed)
|
|
31
|
+
return text unless typed
|
|
32
|
+
|
|
33
|
+
if text.length > 18 && PSYCH_INT_SHAPE.match?(text)
|
|
34
|
+
text.delete(",_").to_i
|
|
35
|
+
elsif text.length > 1 && text.start_with?(":") && !text.start_with?("::")
|
|
36
|
+
text[1..].to_sym
|
|
37
|
+
else
|
|
38
|
+
text
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
23
42
|
V_TS = 6
|
|
24
43
|
SEQ_OPEN = 7
|
|
25
44
|
MAP_OPEN = 8
|
|
@@ -155,15 +174,7 @@ module Yeptris
|
|
|
155
174
|
text = arena.byteslice(flat[i + OFF], flat[i + LEN])
|
|
156
175
|
# implicit-plain ':name' scans to a Symbol (Psych's
|
|
157
176
|
# ScalarScanner); quoted ':x' stays a String
|
|
158
|
-
v =
|
|
159
|
-
PSYCH_INT_SHAPE.match?(text)
|
|
160
|
-
text.delete(",_").to_i
|
|
161
|
-
elsif flat[i + B] == 1 && text.length > 1 &&
|
|
162
|
-
text.start_with?(":") && !text.start_with?("::")
|
|
163
|
-
text[1..].to_sym
|
|
164
|
-
else
|
|
165
|
-
text
|
|
166
|
-
end
|
|
177
|
+
v = str_to_value(text, flat[i + B] == 1)
|
|
167
178
|
if pending_anchor
|
|
168
179
|
anchors[pending_anchor] = v
|
|
169
180
|
pending_anchor = nil
|
|
@@ -284,20 +295,9 @@ module Yeptris
|
|
|
284
295
|
pending_anchor.nil? && !stack.empty? && stack.last.is_a?(Hash)
|
|
285
296
|
kt = arena.byteslice(offs[i], lens[i])
|
|
286
297
|
if kt != "<<"
|
|
287
|
-
key =
|
|
288
|
-
!kt.start_with?("::")
|
|
289
|
-
kt[1..].to_sym
|
|
290
|
-
else
|
|
291
|
-
kt
|
|
292
|
-
end
|
|
298
|
+
key = str_to_value(kt, bools[i] == 1)
|
|
293
299
|
vt = arena.byteslice(offs[i + 1], lens[i + 1])
|
|
294
|
-
stack.last[key] =
|
|
295
|
-
if bools[i + 1] == 1 && vt.length > 1 && vt.start_with?(":") &&
|
|
296
|
-
!vt.start_with?("::")
|
|
297
|
-
vt[1..].to_sym
|
|
298
|
-
else
|
|
299
|
-
vt
|
|
300
|
-
end
|
|
300
|
+
stack.last[key] = str_to_value(vt, bools[i + 1] == 1)
|
|
301
301
|
i += 2
|
|
302
302
|
next
|
|
303
303
|
end
|
|
@@ -305,15 +305,7 @@ module Yeptris
|
|
|
305
305
|
case kinds[i]
|
|
306
306
|
when V_STR
|
|
307
307
|
text = arena.byteslice(offs[i], lens[i])
|
|
308
|
-
v =
|
|
309
|
-
PSYCH_INT_SHAPE.match?(text)
|
|
310
|
-
text.delete(",_").to_i
|
|
311
|
-
elsif bools[i] == 1 && text.length > 1 &&
|
|
312
|
-
text.start_with?(":") && !text.start_with?("::")
|
|
313
|
-
text[1..].to_sym
|
|
314
|
-
else
|
|
315
|
-
text
|
|
316
|
-
end
|
|
308
|
+
v = str_to_value(text, bools[i] == 1)
|
|
317
309
|
if pending_anchor
|
|
318
310
|
anchors[pending_anchor] = v
|
|
319
311
|
pending_anchor = nil
|
data/lib/yeptris.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Yeptris
|
|
4
4
|
# The gem's version lives in the parent namespace's file — the last
|
|
5
5
|
# internal require (yeptris/version) retired with it.
|
|
6
|
-
VERSION = "0.6.
|
|
6
|
+
VERSION = "0.6.52.0".freeze
|
|
7
7
|
|
|
8
8
|
# Informational notices are OPT-IN (yeptris-ruby#217): consumers
|
|
9
9
|
# embed yeptris inside CLIs that assert clean stderr, and a
|
|
@@ -379,7 +379,8 @@ static int e_comment_at(const yep_engine* e) {
|
|
|
379
379
|
}
|
|
380
380
|
|
|
381
381
|
static int e_at_eol(yep_engine* e) {
|
|
382
|
-
return e->pos >= e->len || e->p[e->pos] == '\n' || e->p[e->pos] == '\r' || e_comment_at(e)
|
|
382
|
+
return e->pos >= e->len || e->p[e->pos] == '\n' || e->p[e->pos] == '\r' || e_comment_at(e) ||
|
|
383
|
+
yep_scan_mb_break_len(e->p, e->len, e->pos) > 0;
|
|
383
384
|
}
|
|
384
385
|
|
|
385
386
|
static int e_skip_to_eol(yep_engine* e) {
|
|
@@ -3890,6 +3891,16 @@ static int e_parse_value(yep_engine* e, yep_ctx ctx, uint16_t floor_col) {
|
|
|
3890
3891
|
if (e->pos >= e->len) {
|
|
3891
3892
|
goto empty_value;
|
|
3892
3893
|
}
|
|
3894
|
+
/* a raw NEL/LS/PS here is this line's break: consume it exactly
|
|
3895
|
+
* as the BLANK branch below consumes an LF-ended empty line —
|
|
3896
|
+
* yep_scan_line's line model does not treat a break byte at the
|
|
3897
|
+
* loop head as a line end, so without this the props-only line
|
|
3898
|
+
* re-entered e_node on the break byte (rows 1-2) and the marker
|
|
3899
|
+
* became the scalar's value (row 5's consume-first form) */
|
|
3900
|
+
if (yep_scan_mb_break_len(e->p, e->len, e->pos) > 0) {
|
|
3901
|
+
e_line_done(e, e->pos);
|
|
3902
|
+
continue;
|
|
3903
|
+
}
|
|
3893
3904
|
yep_line_info li;
|
|
3894
3905
|
if (e->pos == e->line_start) {
|
|
3895
3906
|
/* seed the memo: the fast arms re-read this line's facts —
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yeptris
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.52.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-31 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ffi
|