parsanol 1.3.103 → 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 +4 -4
- data/lib/parsanol/parg/artifact.rb +88 -1
- data/lib/parsanol/parg/compiler.rb +4 -0
- 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: ebed37c7bde0d80cb4e89167f63c53cfb2158362fbb058a3f486cddd0e8d0ec8
|
|
4
|
+
data.tar.gz: e9b7e329d1bbd7308c3d974e82a604c52127e5ee3a3690770f887d9946c7e542
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 72cd356e1e03e260fe71c9fdbc32997b7f687bd3d47177231644ec3492a62f087a5e50377c14101e3f5224f251f88fb580c18295fbeca0003165439ba393f7e5
|
|
7
|
+
data.tar.gz: dc110763f9635a3698da2ec4663a1383c26bc110937264de005f1699e5441e3eaf475bf69c93c5def5f1b98d02dad65f9aa7e682902c70e06227bb3ea4985e24
|
|
@@ -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" =>
|
|
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
|
data/lib/parsanol/version.rb
CHANGED