parsanol 1.3.120 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e61dcbc90916d0c5bc6d82ecfa1f4c1f76987df549b49460a20e01622bb0a159
4
- data.tar.gz: 3a4214dcc35b10e44819910f78cc1905dce036c1789ab7162c8819f92ffc15a6
3
+ metadata.gz: 6b2308bfe0b4b5787cbfac5725e10e2afd01ade41f3ae373dd09df250dbaebd3
4
+ data.tar.gz: b49dc9c6b767be5c8ec34492b3e1a63683aa8bba12f8a4d194ddd107169b51da
5
5
  SHA512:
6
- metadata.gz: b9be0ac6018e475fbaba69dc3f95a0ca55567f8664049aaab3b7ffa20a5dafc7c2508da7d3aa66768010373a6b926426761e134ce39b490e0663b0394ea0ffb7
7
- data.tar.gz: 0373d7ad8a5e0c5dad37702b59c22e12fd75646a2de59fbe1dbaa7d02dcd9a297b099c8a32759a3a048acf0c63e9d7b7a8f212a616e4f7c8caf41fa8be29776c
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.17"
310
+ version = "0.19.19"
311
311
  source = "registry+https://github.com/rust-lang/crates.io-index"
312
- checksum = "761b572291fd7bad4514b55492cc402d297dd1586e48d1daad6ca25a927968b1"
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.17"
329
+ version = "0.19.19"
330
330
  source = "registry+https://github.com/rust-lang/crates.io-index"
331
- checksum = "3831668982431ef76e72800bd4cb399c38f0b51554541d4e4263a09fe461f460"
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
- append_atom_structure(atom.parslet, visited, output)
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, and capture-producing refs all stay bare.
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}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parsanol
4
- VERSION = "1.3.120"
4
+ VERSION = "1.3.121"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parsanol
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.120
4
+ version: 1.3.121
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.