parsanol 1.3.68 → 1.3.70

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: 5917e9971751a101c27984a0ccec6aa6254813b77d18a45eea7f6a5538ead517
4
- data.tar.gz: 9513f097d858d7c394a10c8002e372523212bf11629813867ec59a91dacbc41d
3
+ metadata.gz: 46a9a34dd51e63ba24ee155131eb13326947d08cf08a9814a6d600046aead88a
4
+ data.tar.gz: 679d459fa6c46a6b27816c042af13b0c6e9f15ccae847786140c473731b25ebd
5
5
  SHA512:
6
- metadata.gz: 65cf06e07d16879100effb50a7ed8012e95774ed6b8c86f97c997d3b6ce7a41525a07e3943d4670dc3c6f65e91d587953e719668decba35f90610bdf29a7c011
7
- data.tar.gz: 84cd53317231f028e6510d90c7c426379566e47570594e960b022766ae45fffb8305afdcc5f044339fbbdb3a30c74e44e25ce03a735c7e3672221f70f26b0ab1
6
+ metadata.gz: 5bddc1e0b863c8f50be6573f29e09b17091c95dbfd0db57c660aea348d090b681bb7d99e3870276f3b05f32fcc1944098f71777bdaf4db2b34f348c1e847fbaa
7
+ data.tar.gz: bbb0ce39775917e628fcc19a04d15e2f252437d6fdcfe9b9b69f33711e096b2cb9a2b9a40908177068f66e54c3dc9ceb4d4ba25396b4cfc0ac465de34667b65d
data/Cargo.lock CHANGED
@@ -310,13 +310,31 @@ name = "parsanol"
310
310
  version = "0.10.2"
311
311
  source = "registry+https://github.com/rust-lang/crates.io-index"
312
312
  checksum = "dd43c104276def3f79a54ce6e8d26642ea330c13f97d66380bd94fc057f0a849"
313
+ dependencies = [
314
+ "ahash",
315
+ "getrandom 0.3.4",
316
+ "getrandom 0.4.3",
317
+ "hashbrown",
318
+ "parsanol-derive 0.10.2",
319
+ "regex",
320
+ "regex-syntax",
321
+ "serde",
322
+ "serde_json",
323
+ "sha2",
324
+ ]
325
+
326
+ [[package]]
327
+ name = "parsanol"
328
+ version = "0.12.0"
329
+ source = "registry+https://github.com/rust-lang/crates.io-index"
330
+ checksum = "b3754baf8f7f9761748bac497cd60efd1bab93ff54c4212e899e27cf3f09100a"
313
331
  dependencies = [
314
332
  "ahash",
315
333
  "getrandom 0.3.4",
316
334
  "getrandom 0.4.3",
317
335
  "hashbrown",
318
336
  "magnus",
319
- "parsanol-derive",
337
+ "parsanol-derive 0.12.0",
320
338
  "regex",
321
339
  "regex-syntax",
322
340
  "serde",
@@ -335,11 +353,22 @@ dependencies = [
335
353
  "syn 3.0.6",
336
354
  ]
337
355
 
356
+ [[package]]
357
+ name = "parsanol-derive"
358
+ version = "0.12.0"
359
+ source = "registry+https://github.com/rust-lang/crates.io-index"
360
+ checksum = "7cdc8438988a014ec5a7cb6d7891c3c467154de134b353e8790bd42d4ae2ddd5"
361
+ dependencies = [
362
+ "proc-macro2",
363
+ "quote",
364
+ "syn 3.0.6",
365
+ ]
366
+
338
367
  [[package]]
339
368
  name = "parsanol_cdylib"
340
369
  version = "1.3.52"
341
370
  dependencies = [
342
- "parsanol",
371
+ "parsanol 0.10.2",
343
372
  ]
344
373
 
345
374
  [[package]]
@@ -348,7 +377,7 @@ version = "1.0.2"
348
377
  dependencies = [
349
378
  "log",
350
379
  "magnus",
351
- "parsanol",
380
+ "parsanol 0.12.0",
352
381
  "rb-sys",
353
382
  ]
354
383
 
@@ -27,8 +27,9 @@ rb-sys = { version = "0.9.124", features = ["global-allocator"] }
27
27
  # magnus for Ruby bindings (from crates.io, patched locally for Ruby 4.0 support)
28
28
  magnus = { version = "0.9" }
29
29
 
30
- # parsanol parser library (crates.io; 0.10 carries the class fast-path fix)
31
- parsanol = { version = "0.10.2", features = ["ruby"] }
30
+ # parsanol parser library (crates.io; 0.12 carries the rs#174 native
31
+ # backtracking fix: dynamic-dependent memoization + outcome cache)
32
+ parsanol = { version = "0.12.0", features = ["ruby"] }
32
33
 
33
34
  # Logging
34
35
  log = "0.4"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parsanol
4
- VERSION = "1.3.68"
4
+ VERSION = "1.3.70"
5
5
  end
data/lib/parsanol/vm.rb CHANGED
@@ -84,6 +84,14 @@ module Parsanol
84
84
  STEP_BUDGET_FIXED = 10_000
85
85
  MEMO_BUDGET_FACTOR = 4_096
86
86
  MEMO_BUDGET_CELLS_FACTOR = 16
87
+ # Wall-clock ceiling for the memoized retry pass. Step budgets assume
88
+ # sub-microsecond steps; pathological grammars make each step
89
+ # expensive enough that a step budget alone mispredicts wall time by
90
+ # orders of magnitude (parsanol-ruby#147: a 6-line skip-injected
91
+ # document ground ~146s inside its memo budget where the interpreter
92
+ # answers in milliseconds). Bounded wall time surrenders the grammar
93
+ # to the interpreter, the source of truth, instead of burning CPU.
94
+ MEMO_WALL_BUDGET_SECONDS = 5.0
87
95
 
88
96
  # Compiled-program and heavy-flag caches hold at most this many
89
97
  # grammars. A process rarely uses more than a handful concurrently;
@@ -130,7 +138,10 @@ module Parsanol
130
138
  end
131
139
  cache[root] = program
132
140
  trim_cache(cache)
133
- program.equal?(:fallback) ? nil : program
141
+ # :fallback = VM-incompatible; :oversize even non-inlined means
142
+ # the grammar is beyond the program cap — the interpreter
143
+ # handles both.
144
+ program.equal?(:fallback) || program.equal?(:oversize) ? nil : program
134
145
  end
135
146
 
136
147
  # Marks a grammar as VM-incompatible after a runtime failure.
@@ -197,6 +208,10 @@ module Parsanol
197
208
  end
198
209
 
199
210
  compiler.to_program
211
+ rescue Compiler::ProgramTooLarge
212
+ # Emission aborted at the cap: discard and let the caller
213
+ # decide (compile retries non-inlined).
214
+ :oversize
200
215
  end
201
216
 
202
217
  # Returns violation descriptions for this atom's ordered-choice
@@ -679,7 +694,19 @@ module Parsanol
679
694
 
680
695
  private
681
696
 
697
+ # Raised mid-emission once the program passes MAX_PROGRAM: abort
698
+ # the build immediately instead of materializing millions of
699
+ # instructions only to discover the overflow in to_program
700
+ # (parsanol-ruby#147: an inline pass on a skip-injected grammar
701
+ # reached 6.3M instructions / 16M with subroutines before the
702
+ # post-hoc check discarded them — minutes of compile time and
703
+ # gigabytes of transient allocations for a program that is
704
+ # thrown away and rebuilt non-inlined anyway).
705
+ class ProgramTooLarge < StandardError; end
706
+
682
707
  def emit(op, a = nil, b = nil, c = nil) # rubocop:disable Naming/MethodParameterName -- opcode operand slots
708
+ raise ProgramTooLarge if @ops.size > MAX_PROGRAM
709
+
683
710
  @ops << [op, a, b, c]
684
711
  @ops.size - 1
685
712
  end
@@ -902,6 +929,9 @@ module Parsanol
902
929
  (STEP_BUDGET_FACTOR * n) + STEP_BUDGET_FIXED
903
930
  end
904
931
  steps = 0
932
+ wall_deadline = if @memoize
933
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) + MEMO_WALL_BUDGET_SECONDS
934
+ end
905
935
 
906
936
  pc = 0
907
937
  pos = 0
@@ -917,6 +947,13 @@ module Parsanol
917
947
  # rubocop:disable Metrics/BlockLength -- the dispatch loop IS execute
918
948
  loop do
919
949
  steps += 1
950
+ # Checked every 1024 steps: a monotonic clock read per
951
+ # millisecond of ordinary work costs nothing measurable.
952
+ if wall_deadline && steps.nobits?(0x3FF) &&
953
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) > wall_deadline
954
+ return BUDGET
955
+ end
956
+
920
957
  if steps > budget
921
958
  if trace
922
959
  warn "BUDGET at steps=#{steps} pc=#{pc} pos=#{pos} rstack=#{rstack.size} bt=#{bt.size} calls=#{calls.size}"
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.68
4
+ version: 1.3.70
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.