parsanol 1.3.53-arm-linux → 1.3.54-arm-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: 96c0f5cd32c8f5e27833af6e17558ad07add4632840fbe6fb3500268d3da8f0c
4
- data.tar.gz: c6b0e63b4591be106dde0fa4b7aab5a7314352374104214a7f558392d2151a96
3
+ metadata.gz: e49b5e2d3931d3587bc0c152c8ee65bf46063e2537513691aa174d433a5e6eb3
4
+ data.tar.gz: 8a2b5e3db4e0a4a389253366e2a6e3576871e70e8894f8fa95ab037f31f188b9
5
5
  SHA512:
6
- metadata.gz: 99791e381750652480f579b1a0dca4978b6ccd4471939e918c488b568a94875795a5ee1fb06cc0c285439729d82cb95a8882c5c2dbe051d4372c1c9f52a758d1
7
- data.tar.gz: dc4ff8df0c53abffc2ab84cafd59944643c90738a4cb092bcf372483d42d3c6bffe1ade8e4c6142d363dbb8f87551d67802ce23a950ef32dcea157701d16cc33
6
+ metadata.gz: cb986c7b091990733d47ad8065b82697e9062edce5964d41714520f22ab07154ba1a8683598e5c5dce7377ceb28e9fec46004013c013df8a727f55ee71c112c4
7
+ data.tar.gz: c459ffa3b22bb9a056f0582fd4ffa06f2ded2e46e9b3683556c0dab7467e61ba9667d62d5272fea5595206d942d8e55db822294646c561088fbc0c413bbd96e4
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parsanol
4
- VERSION = "1.3.53"
4
+ VERSION = "1.3.54"
5
5
  end
data/lib/parsanol/vm.rb CHANGED
@@ -156,6 +156,21 @@ module Parsanol
156
156
  compiler.ops << [HALT, nil, nil, nil]
157
157
  return nil unless compiler.append_subroutines
158
158
 
159
+ # Terminal failures jump to pc = FAIL without testing the pc
160
+ # register on the hot path; the dispatch case instead lands on
161
+ # this dedicated instruction (the last slot in the flat program),
162
+ # whose opcode is FAIL itself.
163
+ compiler.ops << [FAIL, nil, nil, nil]
164
+
165
+ # Structure-seeded memoization: a grammar containing repeat/maybe
166
+ # starts memoized on its very first parse (fresh parser instances
167
+ # included — the seed is recomputed at every compile), so the
168
+ # cold-start never pays the doomed unmemoized exploration.
169
+ if compiler.backtracking_prone
170
+ # rubocop:disable-next Lint/HashCompareByIdentity -- object_id keys match run_for's heavy flag; would otherwise pin every grammar alive
171
+ (@heavy ||= {})[root.object_id] = true
172
+ end
173
+
159
174
  compiler.to_program
160
175
  end
161
176
 
@@ -248,9 +263,10 @@ module Parsanol
248
263
  @inline = inline
249
264
  @subs = {} # [obj_id, flag] => pc or :pending
250
265
  @pending = {} # [obj_id, flag] => [[call_idx, body], ...]
266
+ @backtracking_prone = false
251
267
  end
252
268
 
253
- attr_reader :ops
269
+ attr_reader :ops, :backtracking_prone
254
270
 
255
271
  def to_program
256
272
  return :oversize if @ops.size > MAX_PROGRAM
@@ -608,6 +624,11 @@ module Parsanol
608
624
  # * unbounded-plus of a single terminal (non-spine sites) -> greedy
609
625
  # RUN_* op
610
626
  def compile_repetition(atom, consume_all)
627
+ # Any repetition (repeat, maybe, repeat(0)) can re-explore the
628
+ # same rule+position pairs when a later sibling fails; grammars
629
+ # containing one memoize from the first parse instead of waiting
630
+ # for the naive pass to prove the need.
631
+ @backtracking_prone = true
611
632
  min = atom.min
612
633
  max = atom.max
613
634
  return nil if max&.zero?
@@ -737,6 +758,10 @@ module Parsanol
737
758
  def execute # rubocop:disable Metrics/MethodLength, Metrics/BlockLength, Metrics/BlockNesting -- single dispatch loop; hot path
738
759
  ops = @ops
739
760
  input = @input
761
+ # Dedicated FAIL landing instruction appended after HALT and the
762
+ # subroutines: `pc = fail_pc` dispatches straight into when-FAIL
763
+ # on the next cycle, no sentinel comparison per step.
764
+ fail_pc = ops.size - 4
740
765
  bytes = input.unpack("C*")
741
766
  # Regexp#match?(str, pos) searches FORWARD from pos (unanchored);
742
767
  # StringScanner#match? is position-anchored, matching the
@@ -800,7 +825,7 @@ module Parsanol
800
825
  pos += ops[pc + 3]
801
826
  pc += 4
802
827
  else
803
- pc = FAIL
828
+ pc = fail_pc
804
829
  end
805
830
  when RE
806
831
  b = bytes[pos]
@@ -819,7 +844,7 @@ module Parsanol
819
844
  pos += w
820
845
  pc += 4
821
846
  else
822
- pc = FAIL
847
+ pc = fail_pc
823
848
  end
824
849
  when ANY
825
850
  if pos < n
@@ -829,7 +854,7 @@ module Parsanol
829
854
  pos += w
830
855
  pc += 4
831
856
  else
832
- pc = FAIL
857
+ pc = fail_pc
833
858
  end
834
859
  when SEQ_BEGIN
835
860
  rstack << SEQ_MARK
@@ -878,7 +903,7 @@ module Parsanol
878
903
  # PENDING — re-entry at the same rule+position (left
879
904
  # recursion); the interpreter loops forever here, so
880
905
  # failing this path keeps the VM bounded.
881
- pc = FAIL
906
+ pc = fail_pc
882
907
  next
883
908
  end
884
909
  # Memo hit: replay the subroutine's stack effect without
@@ -926,7 +951,7 @@ module Parsanol
926
951
  rlen = bt[-BT_STRIDE + 2]
927
952
  rstack.slice!(rlen..) if rstack.size > rlen
928
953
  bt.pop(BT_STRIDE)
929
- pc = FAIL
954
+ pc = fail_pc
930
955
  when DROP
931
956
  rstack.pop
932
957
  rstack << nil
@@ -967,7 +992,7 @@ module Parsanol
967
992
 
968
993
  if ops[pc + 2] == 1 && pos != n
969
994
  frames.slice!(cbase..)
970
- pc = FAIL
995
+ pc = fail_pc
971
996
  else
972
997
  rbase = frames[cbase + 2]
973
998
  values = rstack.pop(rstack.size - rbase)
@@ -980,7 +1005,7 @@ module Parsanol
980
1005
  table = ops[pc + 1]
981
1006
  b = pos < n ? bytes[pos] : -1
982
1007
  target = b == -1 ? -1 : table[b]
983
- pc = target >= 0 ? target : FAIL
1008
+ pc = target >= 0 ? target : fail_pc
984
1009
  when SEQ_SIMPLE
985
1010
  kids = ops[pc + 1]
986
1011
  kn = kids.size
@@ -1115,7 +1140,7 @@ module Parsanol
1115
1140
  ki += 4
1116
1141
  end
1117
1142
  if failed
1118
- pc = FAIL
1143
+ pc = fail_pc
1119
1144
  else
1120
1145
  rstack << values
1121
1146
  pc += 4
@@ -1180,7 +1205,7 @@ module Parsanol
1180
1205
  pos += w
1181
1206
  end
1182
1207
  if min && values.size - 1 < min
1183
- pc = FAIL
1208
+ pc = fail_pc
1184
1209
  else
1185
1210
  rstack << values
1186
1211
  pc += 4
@@ -1206,14 +1231,14 @@ module Parsanol
1206
1231
  pos += ln
1207
1232
  end
1208
1233
  if min && values.size - 1 < min
1209
- pc = FAIL
1234
+ pc = fail_pc
1210
1235
  else
1211
1236
  rstack << values
1212
1237
  pc += 4
1213
1238
  end
1214
1239
  when HALT
1215
1240
  if @consume_all && pos != n
1216
- pc = FAIL
1241
+ pc = fail_pc
1217
1242
  next
1218
1243
  end
1219
1244
  value = VM.materialize(rstack[0], input)
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.53
4
+ version: 1.3.54
5
5
  platform: arm-linux
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-09-23 00:00:00.000000000 Z
11
+ date: 2026-09-24 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