parsanol 1.3.94 → 1.3.96

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: c413057594b283af6f74b3ca1f6374ed7ad48a0e27c2f3c9f3f4bf180758dab9
4
- data.tar.gz: b1189593e24fb9da236f8146b4b25a57fa027f9059034523670da67244179b18
3
+ metadata.gz: 3361d31dc03ae64b82fbc5ac5429f28984b387f47b6de6f4dc782a192d75a396
4
+ data.tar.gz: '08c24046572b0bff4f377dfd959503d2fd86aa31c442af01e5c354f0a08e6af6'
5
5
  SHA512:
6
- metadata.gz: c3c9075e127783184d55858f0ee318f1ecf07d92bf5654bc8d992c04eb39a62a9d72621fa2ba73610e1f2dfb49a81f768cecf7c077b2583450188e21ca05c4d0
7
- data.tar.gz: 88170ab0ecc62622bf034af9afd013c25d57a7d89ea5bed35be4c0ab53df1a68a08bfb208b819a80ef53262b7da1a644344d25106879b06ed72d243147afc163
6
+ metadata.gz: 87807af6107e2df8acd840da4a815a7e432fb84cdd7f5dce3b6c737122179fe2d808d9cd259829f40b41945e9f20e85d76a99f92b04d4d31e6dc9e497518af44
7
+ data.tar.gz: 0a0250d855f6330a8bd0f559d8a02419f448e0f402801aa9eba97272d0e683f02a8f3557f35ad4b4b6b10ca59f11035542fb55f545048eb7af0d4030c4dd81bc
data/Cargo.lock CHANGED
@@ -307,9 +307,9 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
307
307
 
308
308
  [[package]]
309
309
  name = "parsanol"
310
- version = "0.18.8"
310
+ version = "0.18.9"
311
311
  source = "registry+https://github.com/rust-lang/crates.io-index"
312
- checksum = "92651c1fc80fad436b29aa642bec9f652bb0170f8ff0dc2a9f5b1411532fe732"
312
+ checksum = "456a6ee7850bd50d2254b3232d537af2de447ea354a40a06a33904da899cd57b"
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.8"
329
+ version = "0.18.9"
330
330
  source = "registry+https://github.com/rust-lang/crates.io-index"
331
- checksum = "4423949424ed9894fe79f6df7fec71d5fa7549249390e9d52e34c77a78cdad13"
331
+ checksum = "280663d376aefe51ad6850535618f67fbb281d48f6ac024fad560ee39029ace1"
332
332
  dependencies = [
333
333
  "proc-macro2",
334
334
  "quote",
@@ -72,6 +72,10 @@ module Parsanol
72
72
  # and the NEXT successful Named capture attaches (and drains)
73
73
  # them under `comments:`.
74
74
  @pending_trivia = []
75
+
76
+ # Recovery journal (TODO.sota/1): non-nil only in recovery
77
+ # re-parses; Named captures append to the shared array.
78
+ @recovery_journal = Parsanol::Recovery.journal if Parsanol::Recovery.journaling?
75
79
  @evict_threshold = 200
76
80
  @evict_counter = 0
77
81
  @evict_interval = 100
@@ -327,6 +331,10 @@ module Parsanol
327
331
  # @return [Parsanol::Scope] capture variable bindings
328
332
  attr_reader :captures
329
333
 
334
+ # Recovery journal (TODO.sota/1): a shared array when this
335
+ # context was created inside a recovery re-parse; nil otherwise.
336
+ attr_accessor :recovery_journal
337
+
330
338
  # @return [Parsanol::Pools::ArrayPool] array object pool
331
339
  attr_reader :array_pool
332
340
 
@@ -42,6 +42,8 @@ module Parsanol
42
42
  comments = context.take_pending_trivia
43
43
  result = wrap_result(value)
44
44
  result = attach_comments(result, comments, source) unless comments.empty?
45
+ # Recovery journal (TODO.sota/1): nil on every normal parse.
46
+ context.recovery_journal << [@name, result] if context.recovery_journal
45
47
  ok(result)
46
48
  end
47
49
 
@@ -382,6 +382,20 @@ module Parsanol
382
382
  :unavailable
383
383
  end
384
384
  end
385
+ name_cache_version if @native_state == :loaded
386
+ @native_state
387
+ end
388
+
389
+ private
390
+
391
+ # Compiled VM programs are cached on disk across processes; naming
392
+ # this gem release in the cache key means a new release never runs
393
+ # a program an older one compiled. Extensions built before the
394
+ # engine grew the hook simply skip it.
395
+ def name_cache_version
396
+ return unless respond_to?(:_set_cache_version)
397
+
398
+ _set_cache_version("parsanol-ruby #{Parsanol::VERSION}")
385
399
  end
386
400
  end
387
401
  end
@@ -197,6 +197,53 @@ module Parsanol
197
197
  end
198
198
  end
199
199
 
200
+ # Best-effort recovery (TODO.sota/1, R1): on success returns
201
+ # {"ok": true, "shape": tree}; on ParseFailed returns
202
+ # {"ok": false, "error": {"offset", "message"}, "captures":
203
+ # {name => [values...]}} — the captures observed before the
204
+ # failure, from a journaling re-parse on the Ruby engine (tree
205
+ # parity with the native engine is enforced by the differential
206
+ # specs). The journal is best-effort: it may include captures
207
+ # from branches that later failed.
208
+ def parse_recovering(entry_name, input, mode: :native)
209
+ shape = parse(entry_name, input, mode: mode)
210
+ { "ok" => true, "shape" => shape }
211
+ rescue Parsanol::ParseFailed => e
212
+ { "ok" => false,
213
+ "error" => recovery_error(e),
214
+ "captures" => recovery_journal(entry_name, input, mode) }
215
+ end
216
+
217
+ def recovery_error(err)
218
+ pos = err.parse_failure_cause&.position
219
+ { "offset" => pos, "message" => err.message }
220
+ end
221
+
222
+ # Journaling re-parse — always on the Ruby engine (journaling
223
+ # rides the interpreter's diagnostic reparse; the native engine
224
+ # has no journal). Tree parity with the native engine is enforced
225
+ # by the differential specs, so the observed captures agree.
226
+ def recovery_journal(entry_name, input, _mode)
227
+ Parsanol::Recovery.start_journal
228
+ begin
229
+ parse(entry_name, input, mode: :ruby)
230
+ rescue Parsanol::ParseFailed
231
+ # expected: the journaling pass fails at the same point
232
+ end
233
+ Parsanol::Recovery.stop_journal.each_with_object({}) do |(name, value), out|
234
+ # Both the initial attempt and the diagnostic re-parse journal
235
+ # into the shared thread-local; identical observations dedupe.
236
+ wire = value.is_a?(Slice) ? slice_wire(value) : value
237
+ (out[name.to_s] ||= []) << wire
238
+ out[name.to_s].uniq!
239
+ end
240
+ end
241
+
242
+ def slice_wire(slice)
243
+ { "value" => slice.to_s, "offset" => slice.offset,
244
+ "length" => slice.content.length }
245
+ end
246
+
200
247
  def test_to_hash(test)
201
248
  {
202
249
  "entry" => test.entry,
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Parsanol
4
+ # Recovery journaling (TODO.sota/1): when enabled for the current
5
+ # thread, every Named capture appends to a shared journal for the
6
+ # duration of the recovery re-parse. Success paths never enable it —
7
+ # normal parses see only a nil-context check.
8
+ module Recovery
9
+ module_function
10
+
11
+ def start_journal
12
+ Thread.current[:parsanol_recovery_journal] = []
13
+ Thread.current[:parsanol_recovery_journaling] = true
14
+ end
15
+
16
+ # Stops journaling and returns the journal: [name, value] pairs in
17
+ # capture order, including captures from branches that later failed
18
+ # (best-effort superset — same semantics as the rs recovery
19
+ # journal).
20
+ def stop_journal
21
+ Thread.current[:parsanol_recovery_journaling] = false
22
+ journal
23
+ end
24
+
25
+ def journal
26
+ Thread.current[:parsanol_recovery_journal] || []
27
+ end
28
+
29
+ def journaling?
30
+ Thread.current[:parsanol_recovery_journaling] == true
31
+ end
32
+ end
33
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parsanol
4
- VERSION = "1.3.94"
4
+ VERSION = "1.3.96"
5
5
  end
data/lib/parsanol.rb CHANGED
@@ -273,6 +273,7 @@ module Parsanol
273
273
  end
274
274
  module_function :exp
275
275
 
276
+ autoload :Recovery, "parsanol/recovery"
276
277
  autoload :Resettable, "parsanol/resettable"
277
278
  autoload :Result, "parsanol/result"
278
279
  autoload :Slice, "parsanol/slice"
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.94
4
+ version: 1.3.96
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -150,6 +150,7 @@ files:
150
150
  - lib/parsanol/pools/position_pool.rb
151
151
  - lib/parsanol/pools/slice_pool.rb
152
152
  - lib/parsanol/position.rb
153
+ - lib/parsanol/recovery.rb
153
154
  - lib/parsanol/resettable.rb
154
155
  - lib/parsanol/result.rb
155
156
  - lib/parsanol/result_builder.rb