parsanol 1.3.94-aarch64-linux → 1.3.96-aarch64-linux

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: 9e0c1efcdfb93af513ae908d45c90b6f2d6c42015fb643e474bffbb37ae39316
4
- data.tar.gz: 86ad51bce683affc8b5b94426151bf56694c41fa87b91c9e56c98b7e801c1efc
3
+ metadata.gz: e9e43c51b94ac9f13af86b41cf5f09eb8e0a6b96f0db9e08dd9257f39209c061
4
+ data.tar.gz: 41c0c989f1458fa4ac116b33ca7fb6c2ad319b6b0b0f504c2a8c9e57bf6fa8bc
5
5
  SHA512:
6
- metadata.gz: 1b5aea575e016166e39822daa41c36c9ef80da76c121610231c6c359071d31362f4e5ed0efd24b4278d8db896ff70f7702e71cbfdf5d4ff4e2f36469222e5085
7
- data.tar.gz: b89fe36dabaa496b63c7bb220bd5c40c5698fbc3ecac8329359d53d2319ec56df283accc410e15fcfe14d8e0729cbfd876e58c6de382079ce716f1f94e795725
6
+ metadata.gz: 36184b13212025e3db3d3aaf87c12253696b85253801ef351ab509c40ce8d5dfbfe22f66d46119733d3646c90e9d0fca55e9a2af990c2dea7f88e58421ecde73
7
+ data.tar.gz: 5254a021c8b381411b662ab48ec3752e364bb9ad5e50d24f21002a02db7174158c8edced1608d079f80e25ffa7dc15f71388027fd060e6f3a4544c0c4f6cb3b2
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/parsanol/3.2/parsanol_native.so
3
- size: 40550656 bytes
4
- sha256: cff224d02b36baf6455b961827d069556a7fed497e0a8e778009065f12613f00
3
+ size: 40739336 bytes
4
+ sha256: 4cad7bf4894a1cea08ee2b319696f166f2e7cc38ed6eef38dee79057f9fd684a
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/parsanol/3.3/parsanol_native.so
3
- size: 40785744 bytes
4
- sha256: ae67525c0f460813aed0b9fccb58809b517da84d72dae383bc232bd3c0c5d4f0
3
+ size: 40974392 bytes
4
+ sha256: 25df57a50ccd92bd51baaf86008559487f4259b81a899c30c61e8e023ff7ec23
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/parsanol/3.4/parsanol_native.so
3
- size: 40781360 bytes
4
- sha256: e30f6696ef7f343f3edf49240039e9287277f6973d4e0bfee918939d82684056
3
+ size: 40961728 bytes
4
+ sha256: e6e8cae20b2ffc4754570fd4e4e19081e24e4b5c37ccc0e122619ada30ca7d97
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/parsanol/4.0/parsanol_native.so
3
- size: 40791392 bytes
4
- sha256: 2f8c4e6eb46bc01f1271aeb19e5b3beb3b1c81ce1deb99b500094fabf3b11517
3
+ size: 40971784 bytes
4
+ sha256: 4c426cf8bfd77fb270f32e171664208d9b3fd3414bcf22b2d677fddc2ec14e9f
@@ -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
 
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/parsanol/native/libparsanol.so
3
- size: 36352992 bytes
4
- sha256: fc4e4581f2ff40db6d747bd6b6377e34da0d7389eccc7b1ecb30f485f2e59e25
3
+ size: 36519512 bytes
4
+ sha256: e6eab7ae66da64b0abf1e86985786423cce6eba79524e64771a52fd53d541530
@@ -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,14 +1,14 @@
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: aarch64-linux
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-18 00:00:00.000000000 Z
11
+ date: 2026-10-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A small Ruby library for constructing parsers in the PEG (Parsing Expression
14
14
  Grammar) fashion. Parsanol provides Parslet-compatible API with additional features
@@ -133,6 +133,7 @@ files:
133
133
  - lib/parsanol/pools/position_pool.rb
134
134
  - lib/parsanol/pools/slice_pool.rb
135
135
  - lib/parsanol/position.rb
136
+ - lib/parsanol/recovery.rb
136
137
  - lib/parsanol/resettable.rb
137
138
  - lib/parsanol/result.rb
138
139
  - lib/parsanol/result_builder.rb