parsanol 1.3.121-aarch64-linux → 1.3.123-aarch64-linux
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/parsanol/parg/bindings.rb +94 -14
- data/lib/parsanol/parg/error.rb +4 -0
- data/lib/parsanol/parg.rb +1 -0
- data/lib/parsanol/version.rb +1 -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: 6a05aa3803a0a35921ce270f05e7375af46c1c008c9b2afb98ed7e63701fe014
|
|
4
|
+
data.tar.gz: bb80e21f834f580e0a21f604fde26208d49679dcd889730da0f8f670d703eb82
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5c307c932d5e3aa2927b03c090e23b50cc1de8cf1979100a3e120ac928b38b88dd16af55eee668e491467b70b33f250f77e673356cdc10f67c67222679abb773
|
|
7
|
+
data.tar.gz: bb2b3e5fe203e6e97594209576e7651e62d38b61f86f3c1a4860c6a06a1369a0e7b4784b6cb82ef29812766d77a98347482f9e38b5c61c6691b94e039cf3d8f9
|
|
@@ -13,6 +13,16 @@ module Parsanol
|
|
|
13
13
|
def apply(artifact, entry, shape)
|
|
14
14
|
bindings = entry["bindings"] || []
|
|
15
15
|
captures = collect_captures(shape)
|
|
16
|
+
# Snapshot before the assignment pass: reading a missing key off
|
|
17
|
+
# `captures` inserts it (default-proc hash), which would make the
|
|
18
|
+
# #413 lost-check below see phantom presence. Presence means a
|
|
19
|
+
# NON-NIL value somewhere in the tree: a fired-but-empty optional
|
|
20
|
+
# capture binds null in the parslet shape, and binding nothing for
|
|
21
|
+
# null is legitimate optionality, not loss (parsanol-ruby#418 —
|
|
22
|
+
# CIE trailing_lang, JIS reaffirmed).
|
|
23
|
+
present = captures.keys.select do |key|
|
|
24
|
+
captures[key].any? { |value| !value.nil? }
|
|
25
|
+
end
|
|
16
26
|
out = {}
|
|
17
27
|
scalar, arrays = bindings.partition { |binding| !binding["path"].to_s.include?("[]") }
|
|
18
28
|
scalar.each do |binding|
|
|
@@ -22,9 +32,38 @@ module Parsanol
|
|
|
22
32
|
arrays.group_by { |binding| binding["path"].split("[]").first }.each do |prefix, group|
|
|
23
33
|
assign_array(out, artifact, prefix, group, captures, shape)
|
|
24
34
|
end
|
|
35
|
+
# Every declared capture that the tree carries must reach the
|
|
36
|
+
# bound map (parsanol-ruby#413): a capture present in the parse
|
|
37
|
+
# but bound nowhere is silent data loss, the one case the
|
|
38
|
+
# binding layer's other checks miss. Absence in the TREE is
|
|
39
|
+
# legitimate optionality and never raises.
|
|
40
|
+
lost = bindings.select do |binding|
|
|
41
|
+
capture = binding["capture"].to_sym
|
|
42
|
+
present.include?(capture) && !bound?(out, binding)
|
|
43
|
+
end
|
|
44
|
+
unless lost.empty?
|
|
45
|
+
raise BindError,
|
|
46
|
+
"entry #{entry['name'].inspect}: capture #{lost.map { |b| b['capture'].inspect }.join(', ')} " \
|
|
47
|
+
"is present in the parse tree but bound nowhere"
|
|
48
|
+
end
|
|
25
49
|
out
|
|
26
50
|
end
|
|
27
51
|
|
|
52
|
+
# Did this binding contribute anything to the bound map? Scalars
|
|
53
|
+
# bind under their leaf key; array groups bind under their prefix.
|
|
54
|
+
def bound?(out, binding)
|
|
55
|
+
path = binding["path"].to_s
|
|
56
|
+
if path.include?("[]")
|
|
57
|
+
prefix = path.split("[]").first.to_sym
|
|
58
|
+
rows = out[prefix]
|
|
59
|
+
rows.is_a?(Array) && rows.any? do |row|
|
|
60
|
+
row.is_a?(Hash) && row.key?(leaf_key(binding))
|
|
61
|
+
end
|
|
62
|
+
else
|
|
63
|
+
out.key?(leaf_key(binding))
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
28
67
|
# Canonical test-value form: one equality surface for capture and
|
|
29
68
|
# example expectations on both sides of the comparison (Slice vs
|
|
30
69
|
# source literal was comparing heterogeneous types). Terminals
|
|
@@ -65,24 +104,30 @@ module Parsanol
|
|
|
65
104
|
|
|
66
105
|
def assign_array(out, artifact, prefix, group, captures, shape = nil)
|
|
67
106
|
# Position-aware pairing (parsanol-ruby#381): when the shape
|
|
68
|
-
# nests the group's instances under the
|
|
107
|
+
# nests the group's instances under the PREFIX KEY, each
|
|
69
108
|
# instance's subtree owns its captures — pairing by OCCURRENCE
|
|
70
109
|
# index landed a sparse capture (one that fires in only some
|
|
71
110
|
# instances) on the wrong instance. Per-instance collection
|
|
72
111
|
# fixes the pairing and is identical to the old flat pairing
|
|
73
|
-
# when every instance fires every capture.
|
|
74
|
-
#
|
|
112
|
+
# when every instance fires every capture.
|
|
113
|
+
#
|
|
114
|
+
# Identification is strictly keyed: the bare top array of a
|
|
115
|
+
# repetition-root entry is NOT claimed as instances
|
|
116
|
+
# (parsanol-ruby#408/#412 — that fallback claimed per-token rule
|
|
117
|
+
# siblings as group instances, splitting paired captures into
|
|
118
|
+
# single-field rows and sprouting phantom rows for unrelated
|
|
119
|
+
# siblings). A capture outside a keyed instance array keeps the
|
|
120
|
+
# legacy occurrence pairing. Phantom rows materialize as {}
|
|
121
|
+
# (the #408 contract: the engines agree by always emitting the
|
|
122
|
+
# row; the contracted phantom representation is {} , never nil).
|
|
75
123
|
instances = shape && group_instances(shape, prefix)
|
|
76
124
|
if instances
|
|
77
|
-
#
|
|
78
|
-
# that fired zero iterations (the tree carries `parts: []`)
|
|
125
|
+
# parsanol-rs#370: a repetition that fired zero iterations
|
|
79
126
|
# binds no key — the recorded native expectations and the
|
|
80
|
-
# pubid builders rely on the absent reading
|
|
81
|
-
# binding regression, caught by the pubid corpus gate; see
|
|
82
|
-
# parsanol-rs#370).
|
|
127
|
+
# pubid builders rely on the absent reading.
|
|
83
128
|
return if instances.empty?
|
|
84
129
|
|
|
85
|
-
list =
|
|
130
|
+
list = []
|
|
86
131
|
instances.each do |instance|
|
|
87
132
|
found = collect_captures(instance)
|
|
88
133
|
element = {}
|
|
@@ -94,6 +139,7 @@ module Parsanol
|
|
|
94
139
|
end
|
|
95
140
|
list << element
|
|
96
141
|
end
|
|
142
|
+
out[prefix.to_sym] = list
|
|
97
143
|
return
|
|
98
144
|
end
|
|
99
145
|
|
|
@@ -115,10 +161,12 @@ module Parsanol
|
|
|
115
161
|
end
|
|
116
162
|
end
|
|
117
163
|
|
|
118
|
-
# The `prefix[]` group's instance subtrees: the
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
164
|
+
# The `prefix[]` group's instance subtrees: the array under a key
|
|
165
|
+
# naming the prefix, walked in collect_captures' BFS order. There
|
|
166
|
+
# is deliberately NO fallback for a repetition-root entry — a bare
|
|
167
|
+
# top array cannot be told apart from per-token rule siblings
|
|
168
|
+
# (#408/#412), so claiming it is magic; those shapes pair by
|
|
169
|
+
# occurrence instead.
|
|
122
170
|
def group_instances(shape, prefix)
|
|
123
171
|
queue = [shape]
|
|
124
172
|
until queue.empty?
|
|
@@ -133,7 +181,7 @@ module Parsanol
|
|
|
133
181
|
when Array then queue.concat(node)
|
|
134
182
|
end
|
|
135
183
|
end
|
|
136
|
-
|
|
184
|
+
nil
|
|
137
185
|
end
|
|
138
186
|
|
|
139
187
|
def validate_path!(binding)
|
|
@@ -199,10 +247,42 @@ module Parsanol
|
|
|
199
247
|
# string-keyed hashes, repeats as arrays — so (any)-bound keys
|
|
200
248
|
# survive a cross-engine gate without consumer shims.
|
|
201
249
|
plain(value)
|
|
250
|
+
when "text"
|
|
251
|
+
# The matched span as text (ruby#382): a capture whose body
|
|
252
|
+
# carries inner captures binds a hash of them; (text) returns
|
|
253
|
+
# the span those captures jointly matched while the inner
|
|
254
|
+
# captures still fire as their own bindings.
|
|
255
|
+
span_text(value)
|
|
202
256
|
else value
|
|
203
257
|
end
|
|
204
258
|
end
|
|
205
259
|
|
|
260
|
+
# The matched span of a capture value: the byte range from the
|
|
261
|
+
# first to the last slice in the subtree, cut from the input the
|
|
262
|
+
# slices carry. A plain Slice or String is already its span.
|
|
263
|
+
def span_text(value)
|
|
264
|
+
return value.to_s if value.is_a?(Parsanol::Slice) || value.is_a?(String)
|
|
265
|
+
|
|
266
|
+
slices = []
|
|
267
|
+
walk = lambda do |node|
|
|
268
|
+
case node
|
|
269
|
+
when Parsanol::Slice then slices << node
|
|
270
|
+
when Hash then node.each_value { |item| Array(item).each(&walk) }
|
|
271
|
+
when Array then node.each(&walk)
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
walk.call(value)
|
|
275
|
+
return plain(value) if slices.empty?
|
|
276
|
+
|
|
277
|
+
start = slices.map(&:offset).min
|
|
278
|
+
stop = slices.map { |slice| slice.offset + slice.content.bytesize }.max
|
|
279
|
+
input = slices.first.input
|
|
280
|
+
return plain(value) unless input
|
|
281
|
+
|
|
282
|
+
span = input.byteslice(start, stop - start)
|
|
283
|
+
span&.force_encoding(Encoding::UTF_8)
|
|
284
|
+
end
|
|
285
|
+
|
|
206
286
|
# The engine-neutral reduction behind (any): capture leaves
|
|
207
287
|
# (Parsanol::Slice, native wire objects) and scalars render as
|
|
208
288
|
# their text, hashes as string-keyed plain hashes, arrays
|
data/lib/parsanol/parg/error.rb
CHANGED
|
@@ -14,5 +14,9 @@ module Parsanol
|
|
|
14
14
|
|
|
15
15
|
# Raised when an artifact envelope is corrupt or its checksum mismatches.
|
|
16
16
|
class ArtifactError < Error; end
|
|
17
|
+
|
|
18
|
+
# A declared capture is present in the parse tree but reached the
|
|
19
|
+
# bound map nowhere (parsanol-ruby#413): silent data loss made loud.
|
|
20
|
+
class BindError < ArtifactError; end
|
|
17
21
|
end
|
|
18
22
|
end
|
data/lib/parsanol/parg.rb
CHANGED
|
@@ -17,6 +17,7 @@ module Parsanol
|
|
|
17
17
|
autoload :ParseError, "parsanol/parg/error"
|
|
18
18
|
autoload :CompileError, "parsanol/parg/error"
|
|
19
19
|
autoload :ArtifactError, "parsanol/parg/error"
|
|
20
|
+
autoload :BindError, "parsanol/parg/error"
|
|
20
21
|
autoload :Node, "parsanol/parg/node"
|
|
21
22
|
autoload :Export, "parsanol/parg/export"
|
|
22
23
|
autoload :Lexer, "parsanol/parg/lexer"
|
data/lib/parsanol/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: parsanol
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.123
|
|
5
5
|
platform: aarch64-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-11-
|
|
11
|
+
date: 2026-11-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A small Ruby library for constructing parsers in the PEG (Parsing Expression
|
|
14
14
|
Grammar) fashion. Parsanol provides Parslet-compatible API with additional features
|