parsanol 1.3.102 → 1.3.104

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: 01e6dfd6d0e397462e91b772baa049ab4e3b8a3dd4b4a16fa49e6ae599a93e48
4
- data.tar.gz: 5c9028ae8ad50dcfa25a2e5c1e93bd1c56b93afdefec97d3fdf4d0bf20a604f9
3
+ metadata.gz: ebed37c7bde0d80cb4e89167f63c53cfb2158362fbb058a3f486cddd0e8d0ec8
4
+ data.tar.gz: e9b7e329d1bbd7308c3d974e82a604c52127e5ee3a3690770f887d9946c7e542
5
5
  SHA512:
6
- metadata.gz: f7426c9cda16fa16f6d1edbcc2fc8218b8b3e2c7c60ee5322b565148d8bd5de9d076cff3a3b202679ea41a3c0cf7a21649827d1a765c43def870b81ea0e0c69d
7
- data.tar.gz: 4551fd5e365e33eaf3249756248225a8c549340f36beea7e283841eb35e24fa2943c43e4a4628962153e7d1b9c0bbe272207ea95b44f7472a8d312a16b4f7ca1
6
+ metadata.gz: 72cd356e1e03e260fe71c9fdbc32997b7f687bd3d47177231644ec3492a62f087a5e50377c14101e3f5224f251f88fb580c18295fbeca0003165439ba393f7e5
7
+ data.tar.gz: dc110763f9635a3698da2ec4663a1383c26bc110937264de005f1699e5441e3eaf475bf69c93c5def5f1b98d02dad65f9aa7e682902c70e06227bb3ea4985e24
data/Cargo.lock CHANGED
@@ -307,9 +307,9 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
307
307
 
308
308
  [[package]]
309
309
  name = "parsanol"
310
- version = "0.18.15"
310
+ version = "0.18.16"
311
311
  source = "registry+https://github.com/rust-lang/crates.io-index"
312
- checksum = "782e03b2061221e0e3823365974743020dadc1229c1937af0a3f4e1874c5b9ef"
312
+ checksum = "597e0971251e10f0d9fac99a9f217dc2ce8e5607df9f93d2a12a5e4d823d0ee9"
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.18.15"
329
+ version = "0.18.16"
330
330
  source = "registry+https://github.com/rust-lang/crates.io-index"
331
- checksum = "679f09b2b9c9c61a20ba25bede3c48a00c2c39619125e14b10a2651a8d3d25f8"
331
+ checksum = "342e298363272c74478384205a3d6def7905d8277945d65ad203606596de7fe6"
332
332
  dependencies = [
333
333
  "proc-macro2",
334
334
  "quote",
@@ -222,10 +222,97 @@ module Parsanol
222
222
  # initial parse's (the native FFI's coarse failure position
223
223
  # loses the deepest offset).
224
224
  captures, reparse_error = recovery_journal(entry_name, input, mode)
225
+ partial = recovery_partial(entry_name)
226
+ resyncs = []
227
+ if partial && reparse_error
228
+ items = partial.values.first
229
+ extended, resyncs = resync_extension(entry_name, input,
230
+ reparse_error["offset"])
231
+ items.concat(extended) if extended
232
+ end
225
233
  { "ok" => false,
226
234
  "error" => reparse_error || recovery_error(e),
227
235
  "captures" => captures,
228
- "partial" => recovery_partial(entry_name) }
236
+ "partial" => partial,
237
+ "resyncs" => resyncs }
238
+ end
239
+
240
+ # R2 (TODO.sota/1), inferred resync on the interpreter — the same
241
+ # policy as the rs engine (parsanol-rs#280): from the failure
242
+ # offset, scan forward for the next byte position where a
243
+ # spine-body iteration matches with progress; the skipped span is
244
+ # annotated and the recovered items join the partial tree.
245
+ # Bounded (8 rounds, each must advance) so recovery stays linear.
246
+ # Returns [items, resyncs]; [nil, []] when the grammar has no
247
+ # resolvable spine.
248
+ def resync_extension(entry_name, input, cursor)
249
+ body_rule = spine_body_rule(entry_name)
250
+ return [[], []] unless body_rule
251
+
252
+ body_atom = compiler.atom_for(body_rule)
253
+ max = input.length
254
+ resyncs = []
255
+ items = []
256
+ rounds = 0
257
+ while rounds < 8
258
+ found = nil
259
+ scan = cursor
260
+ while scan < max
261
+ attempt = try_iteration(body_atom, input, scan)
262
+ if attempt && attempt[1] > scan
263
+ found = [scan, attempt[1], attempt[0]]
264
+ break
265
+ end
266
+ scan += 1
267
+ end
268
+ break unless found
269
+
270
+ skip_start, resume, value = found
271
+ resyncs << {
272
+ "offset" => cursor,
273
+ "skipped" => input[cursor...skip_start],
274
+ "resumed_at" => skip_start,
275
+ }
276
+ items << root_atom(entry_name).flatten(value)
277
+ cursor = resume
278
+ rounds += 1
279
+ until cursor >= max
280
+ attempt = try_iteration(body_atom, input, cursor)
281
+ break unless attempt && attempt[1] > cursor
282
+
283
+ items << root_atom(entry_name).flatten(attempt[0])
284
+ cursor = attempt[1]
285
+ end
286
+ break if cursor >= max
287
+ end
288
+ [items, resyncs]
289
+ end
290
+
291
+ # Apply the body atom at one position: [value, end_pos] on a
292
+ # progressing match, nil otherwise.
293
+ def try_iteration(atom, input, pos)
294
+ source = Parsanol::Source.new(input)
295
+ source.bytepos = pos
296
+ context = Parsanol::Atoms::Context.new(nil, parser_class: atom.class)
297
+ success, value = atom.apply(source, context, false)
298
+ success && source.bytepos > pos ? [value, source.bytepos] : nil
299
+ end
300
+
301
+ # The root rule's repetition-body rule name, when the grammar is
302
+ # a repetition spine (root = rep of a single rule reference).
303
+ # Conservative: anything but rep/opt/cap wrapping a bare ref
304
+ # returns nil and resync is skipped.
305
+ SPINE_UNWRAP_KINDS = %i[rep opt cap].freeze
306
+
307
+ def spine_body_rule(entry_name)
308
+ root_rule = entry(entry_name)["root"]
309
+ node = compiler.document.rules[root_rule.to_sym] ||
310
+ compiler.document.rules[root_rule]
311
+ while node.is_a?(Parsanol::PARG::Node) &&
312
+ SPINE_UNWRAP_KINDS.include?(node.kind)
313
+ node = node.a
314
+ end
315
+ node.is_a?(Parsanol::PARG::Node) && node.kind == :ref ? node.a : nil
229
316
  end
230
317
 
231
318
  # R1.5 (TODO.sota/1): the deepest repetition prefix the failed
@@ -40,6 +40,10 @@ module Parsanol
40
40
  end
41
41
  end
42
42
 
43
+ # Read access for recovery tooling (the artifact's inferred
44
+ # resync walks the rule graph to find the spine body).
45
+ attr_reader :document
46
+
43
47
  def initialize(document, tables_dir, bindings: nil)
44
48
  @document = document
45
49
  @tables_dir = tables_dir
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parsanol
4
- VERSION = "1.3.102"
4
+ VERSION = "1.3.104"
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.102
4
+ version: 1.3.104
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.