parsanol 1.3.68 → 1.3.69

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: 263a808dabf8497185a7c4712796b5cb2e29e4318092a8b0eaeccbb5f7d4d7fd
4
+ data.tar.gz: d4fa28dc6c85cbb2b7de27e983e5920571c34f638c1ec1d2f12ed86286453a40
5
5
  SHA512:
6
- metadata.gz: 65cf06e07d16879100effb50a7ed8012e95774ed6b8c86f97c997d3b6ce7a41525a07e3943d4670dc3c6f65e91d587953e719668decba35f90610bdf29a7c011
7
- data.tar.gz: 84cd53317231f028e6510d90c7c426379566e47570594e960b022766ae45fffb8305afdcc5f044339fbbdb3a30c74e44e25ce03a735c7e3672221f70f26b0ab1
6
+ metadata.gz: de302ffc7dce0ad7f949267421ad6277d82096a1a3269ea60dafcf95d6383a8c35f7bd6387e0bc7ca19f065dd59589e618a1115938e4d4d2bd20cd9bc3c04947
7
+ data.tar.gz: 0741e977cb3deaba13193c67e8c37ec9e4d24b904667ddd984a7659bbcd67fd928e8256a6bb80a82e47afe47ebfdd2cf0582727e544f97c4f02a4f15f4c351e7
@@ -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.69"
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.69
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.