parsanol 1.3.119 → 1.3.121
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/Cargo.lock +4 -4
- data/lib/parsanol/native/parser.rb +15 -1
- data/lib/parsanol/parg/compiler.rb +60 -1
- data/lib/parsanol/parg/document.rb +2 -2
- data/lib/parsanol/parg/parser.rb +8 -4
- data/lib/parsanol/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6b2308bfe0b4b5787cbfac5725e10e2afd01ade41f3ae373dd09df250dbaebd3
|
|
4
|
+
data.tar.gz: b49dc9c6b767be5c8ec34492b3e1a63683aa8bba12f8a4d194ddd107169b51da
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 280e47f635e0319aa01a2b5b4936ddc3aa62c9eae8e6f3c13f07600a5904bafb2e7bbd7521f984e5689f5e550eb964c09ac23c8ca36ab7913d4c776f83fafea4
|
|
7
|
+
data.tar.gz: bab0183da6fcf3b5c837ba9f46e675269b5b567e77a3333ea8b5c41c5c85f5390a3f82cc8d9778b63cd57308fa7b0053d67da12d055f9a70c18e0c7c397b1c7f
|
data/Cargo.lock
CHANGED
|
@@ -307,9 +307,9 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
|
307
307
|
|
|
308
308
|
[[package]]
|
|
309
309
|
name = "parsanol"
|
|
310
|
-
version = "0.19.
|
|
310
|
+
version = "0.19.19"
|
|
311
311
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
312
|
-
checksum = "
|
|
312
|
+
checksum = "7ec17007676a476ef23db64b69c3bf5b36d741763ac11ced3089b90a96c9a716"
|
|
313
313
|
dependencies = [
|
|
314
314
|
"ahash",
|
|
315
315
|
"getrandom 0.3.4",
|
|
@@ -326,9 +326,9 @@ dependencies = [
|
|
|
326
326
|
|
|
327
327
|
[[package]]
|
|
328
328
|
name = "parsanol-derive"
|
|
329
|
-
version = "0.19.
|
|
329
|
+
version = "0.19.19"
|
|
330
330
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
331
|
-
checksum = "
|
|
331
|
+
checksum = "fc342af122d8854b8916eaacc6a6c2ad911419c605ea47b74b575c2ca24ab23d"
|
|
332
332
|
dependencies = [
|
|
333
333
|
"proc-macro2",
|
|
334
334
|
"quote",
|
|
@@ -178,13 +178,27 @@ module Parsanol
|
|
|
178
178
|
|
|
179
179
|
def append_atom_structure(atom, visited = {}.compare_by_identity, output = +"")
|
|
180
180
|
if visited[atom]
|
|
181
|
+
# An Entity's self-reference keeps its name in the
|
|
182
|
+
# fingerprint, mirroring the non-visited branch (ruby#404).
|
|
183
|
+
if atom.is_a?(Parsanol::Atoms::Entity)
|
|
184
|
+
return output << "[:entity, " << atom.rule_name.to_s.inspect << ", [:cycle, " << atom.class.name.inspect << "]]"
|
|
185
|
+
end
|
|
186
|
+
|
|
181
187
|
return output << "[:cycle, " << atom.class.name.inspect << "]"
|
|
182
188
|
end
|
|
183
189
|
|
|
184
190
|
visited[atom] = true
|
|
185
191
|
case atom
|
|
186
192
|
when Parsanol::Atoms::Entity
|
|
187
|
-
|
|
193
|
+
# The rule name belongs in the fingerprint (rs#404-class
|
|
194
|
+
# collision): Entity is transparent in the serialized graph's
|
|
195
|
+
# STRUCTURE only when the target is spelled out, but the JSON
|
|
196
|
+
# keeps the Entity node itself — two webs whose Entities point
|
|
197
|
+
# at different rules share a transparent fingerprint while
|
|
198
|
+
# serializing differently, and the serializer cache then
|
|
199
|
+
# serves one grammar's JSON for the other.
|
|
200
|
+
output << "[:entity, " << atom.rule_name.to_s.inspect << ", "
|
|
201
|
+
append_atom_structure(atom.parslet, visited, output) << "]"
|
|
188
202
|
when Parsanol::Atoms::Str
|
|
189
203
|
output << "[:str, " << atom.str.inspect << "]"
|
|
190
204
|
when Parsanol::Atoms::Re
|
|
@@ -261,6 +261,10 @@ module Parsanol
|
|
|
261
261
|
# the declaration means the tree never sees this subtree named,
|
|
262
262
|
# inside or out.
|
|
263
263
|
return atom if glue_rule?(rule_name)
|
|
264
|
+
# A capture spans the rules it references (ruby#400): their
|
|
265
|
+
# bodies compile unwrapped so the capture's value equals its
|
|
266
|
+
# edition-1 value.
|
|
267
|
+
return atom if captured_reachable?(rule_name)
|
|
264
268
|
|
|
265
269
|
wrap_uncaptured(atom)
|
|
266
270
|
end
|
|
@@ -273,6 +277,57 @@ module Parsanol
|
|
|
273
277
|
@document.glue_rules.include?(name.to_s)
|
|
274
278
|
end
|
|
275
279
|
|
|
280
|
+
# The rules a text capture transitively spans (ruby#400): a
|
|
281
|
+
# capture on a reference captures the referenced rule's whole
|
|
282
|
+
# match, so every rule under a capture atom — the direct
|
|
283
|
+
# reference and everything its body transitively references —
|
|
284
|
+
# compiles unwrapped. Edition 2's Named leaves then attach only
|
|
285
|
+
# to non-captured structure, and a capture's value equals its
|
|
286
|
+
# edition-1 value (`{num: "123"}`, never a leaf array).
|
|
287
|
+
def captured_reachable?(name)
|
|
288
|
+
@capture_reachable ||= begin
|
|
289
|
+
seeds = []
|
|
290
|
+
@document.rules.each_value do |node|
|
|
291
|
+
collect_refs_under_captures(node, seeds)
|
|
292
|
+
end
|
|
293
|
+
reachable = []
|
|
294
|
+
until seeds.empty?
|
|
295
|
+
rule = seeds.pop
|
|
296
|
+
next if reachable.include?(rule) || !@document.rules.key?(rule)
|
|
297
|
+
|
|
298
|
+
reachable << rule
|
|
299
|
+
collect_all_refs(@document.rules[rule], seeds)
|
|
300
|
+
end
|
|
301
|
+
reachable
|
|
302
|
+
end
|
|
303
|
+
@capture_reachable.include?(name.to_s)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def collect_refs_under_captures(node, out)
|
|
307
|
+
walk = lambda do |n, in_capture|
|
|
308
|
+
case n.kind
|
|
309
|
+
when :ref then out << n.a if in_capture
|
|
310
|
+
when :cap then walk.call(n.b, true)
|
|
311
|
+
when :pred then walk.call(n.b, in_capture)
|
|
312
|
+
when :alt, :seq then n.a.each { |child| walk.call(child, in_capture) }
|
|
313
|
+
when :rep, :opt then walk.call(n.a, in_capture)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
walk.call(node, false)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def collect_all_refs(node, out)
|
|
320
|
+
walk = lambda do |n|
|
|
321
|
+
case n.kind
|
|
322
|
+
when :ref then out << n.a
|
|
323
|
+
when :alt, :seq then n.a.each { |child| walk.call(child) }
|
|
324
|
+
when :cap, :pred then walk.call(n.b)
|
|
325
|
+
when :rep, :opt then walk.call(n.a)
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
walk.call(node)
|
|
329
|
+
end
|
|
330
|
+
|
|
276
331
|
def wrap_uncaptured(atom)
|
|
277
332
|
case atom
|
|
278
333
|
when Atoms::Named
|
|
@@ -289,6 +344,7 @@ module Parsanol
|
|
|
289
344
|
# agree by design.
|
|
290
345
|
if trivia_only?(atom.rule_name.to_s, { atom.rule_name.to_s => true }) ||
|
|
291
346
|
glue_rule?(atom.rule_name.to_s) ||
|
|
347
|
+
captured_reachable?(atom.rule_name.to_s) ||
|
|
292
348
|
produces_capture?(atom.parslet)
|
|
293
349
|
atom
|
|
294
350
|
else
|
|
@@ -476,9 +532,12 @@ module Parsanol
|
|
|
476
532
|
when Atoms::Named then nil
|
|
477
533
|
when Atoms::Entity
|
|
478
534
|
# Mirror wrap_uncaptured's verdicts: trivia glue, declared
|
|
479
|
-
# glue,
|
|
535
|
+
# glue, capture-reachable refs (ruby#400 — a capture spans
|
|
536
|
+
# them, so modern mode never names them), and
|
|
537
|
+
# capture-producing refs all stay bare.
|
|
480
538
|
unless trivia_only?(atom.rule_name.to_s, { atom.rule_name.to_s => true }) ||
|
|
481
539
|
glue_rule?(atom.rule_name.to_s) ||
|
|
540
|
+
captured_reachable?(atom.rule_name.to_s) ||
|
|
482
541
|
produces_capture?(atom.parslet)
|
|
483
542
|
lines << "rule #{site}: reference to #{atom.rule_name} produces no capture -- " \
|
|
484
543
|
"modern mode would name it #{atom.rule_name}"
|
|
@@ -37,8 +37,8 @@ module Parsanol
|
|
|
37
37
|
@grammar_name = nil
|
|
38
38
|
# The default capture dialect is v1 (the owner's reversal,
|
|
39
39
|
# 2026-10-28): runtime compiles of unpinned sources keep the
|
|
40
|
-
# legacy shapes — the pubid fleet froze against them.
|
|
41
|
-
#
|
|
40
|
+
# legacy shapes — the pubid fleet froze against them. edition
|
|
41
|
+
# "v2" is the named-trees opt-in via the grammar header.
|
|
42
42
|
@edition = "v1"
|
|
43
43
|
@version = "0.0.0"
|
|
44
44
|
@source = nil
|
data/lib/parsanol/parg/parser.rb
CHANGED
|
@@ -261,12 +261,16 @@ module Parsanol
|
|
|
261
261
|
# the author declares, per grammar, which tree semantics the
|
|
262
262
|
# grammar means. THE DEFAULT IS 1 (the legacy shapes — the
|
|
263
263
|
# owner's 2026-10-28 reversal). 2 = fully named trees +
|
|
264
|
-
# explicit spans.
|
|
265
|
-
#
|
|
266
|
-
#
|
|
267
|
-
|
|
264
|
+
# explicit spans. The edition is canonicalized here —
|
|
265
|
+
# everything downstream only ever sees "v1"/"v2". The
|
|
266
|
+
# pre-1.3.113 `mode` alias was removed (the owner's
|
|
267
|
+
# 2026-10-31 ruling): the header fails with the fix named.
|
|
268
|
+
if peek&.type == :ident && peek.value == "edition"
|
|
268
269
|
advance
|
|
269
270
|
document.edition = edition_value
|
|
271
|
+
elsif peek&.type == :ident && peek.value == "mode"
|
|
272
|
+
raise ParseError,
|
|
273
|
+
'the grammar header keyword is edition; mode (the pre-1.3.113 alias) was removed — write edition "v1" or edition "v2"'
|
|
270
274
|
end
|
|
271
275
|
punct("{")
|
|
272
276
|
skip_newlines
|
data/lib/parsanol/version.rb
CHANGED