parsanol 1.3.28-arm-linux → 1.3.30-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: 017e97c2ca6e150818f1662a410ee31ad76fa22a2b0b3f55261ec8ed2bd5e5c5
4
- data.tar.gz: 952e25087016352b3633011efb637514b6b375c453418b87b4fe5d87cab264be
3
+ metadata.gz: b4ac5d42ef4718a350220c0d138e4e976e8b636114eb3d1ac19f60813fb8cbed
4
+ data.tar.gz: 25829d9ffbf70bd209f518d480ec96949d72f326e437df69794e1339a2ddb0af
5
5
  SHA512:
6
- metadata.gz: 987de76ae18f3dd0d73da7afde2a02528159384360700f0b579d0656a9c5fbd59824ad7998f1063a0be9020f9d5bbd14989fe6852458cfa70508341c8f72753e
7
- data.tar.gz: 508a9ff925772c33baa5c6e3047d3e61864040f976e5be1a3574629d011ab5e52a106a0dd7a34fee887985291f95363f6afe69562fcb81acc4fad389f2b03360
6
+ metadata.gz: 2f396be06ff1b3bc405badb6f30ca772e13429f2ea4bd686017843a3206c157eef91a6ac618e98e62729f23429596f59ce02d1caff7eacb63bdee2c2affed9cf
7
+ data.tar.gz: d535746fa5d0466276a3dc3f5502ecda5202ddea2d059e3f1bd5af93347745b3149f0369840bf64de8ae245614290c1350b878da605e03853e5e269bcf95af0f
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.28"
4
+ VERSION = "1.3.30"
5
5
  end
data/lib/parsanol/vm.rb CHANGED
@@ -43,6 +43,7 @@ module Parsanol
43
43
  RUN_RE = 24
44
44
  RUN_STR = 25
45
45
  SEQ_SIMPLE = 26
46
+ BYTE_DISPATCH = 27
46
47
 
47
48
  K_ALT = 0
48
49
  K_REP = 1
@@ -247,6 +248,147 @@ module Parsanol
247
248
  @ops.flatten!(1)
248
249
  end
249
250
 
251
+ # Conservative lead-byte table for a branch: table[b] true iff the
252
+ # atom can start with byte b. nil = cannot prove a constraint (no
253
+ # guard emitted). Conservative in the safe direction only: a guard
254
+ # may allow a branch that then fails normally, never reject one
255
+ # that could match.
256
+ def pairwise_disjoint?(tables)
257
+ seen = Array.new(256, false)
258
+ tables.each do |t|
259
+ i = 0
260
+ while i < 256
261
+ return false if t[i] && seen[i]
262
+
263
+ seen[i] = true if t[i]
264
+ i += 1
265
+ end
266
+ end
267
+ true
268
+ end
269
+
270
+ def first_byte_table(atom, depth = 0)
271
+ return nil if depth > 10
272
+
273
+ case atom
274
+ when Parsanol::Atoms::Str
275
+ b = atom.str.getbyte(0)
276
+ return nil unless b
277
+
278
+ t = Array.new(256, false)
279
+ t[b] = true
280
+ t
281
+ when Parsanol::Atoms::Re
282
+ byte_table(atom.re)
283
+ when Parsanol::Atoms::Sequence
284
+ t = nil
285
+ nullable_all = true
286
+ atom.parslets.each do |child|
287
+ ct = first_byte_table(child, depth + 1)
288
+ return nil if ct.nil?
289
+
290
+ if t
291
+ i = 0
292
+ while i < 256
293
+ t[i] ||= ct[i]
294
+ i += 1
295
+ end
296
+ else
297
+ t = ct
298
+ end
299
+ unless nullable?(child, depth + 1)
300
+ nullable_all = false
301
+ break
302
+ end
303
+ end
304
+ nullable_all ? nil : t
305
+ when Parsanol::Atoms::Alternative
306
+ t = nil
307
+ atom.alternatives.each do |branch|
308
+ bt = first_byte_table(branch, depth + 1)
309
+ return nil if bt.nil?
310
+
311
+ if t
312
+ i = 0
313
+ while i < 256
314
+ t[i] ||= bt[i]
315
+ i += 1
316
+ end
317
+ else
318
+ t = bt
319
+ end
320
+ end
321
+ t
322
+ when Parsanol::Atoms::Named
323
+ first_byte_table(atom.parslet, depth + 1)
324
+ when Parsanol::Atoms::Ignored
325
+ first_byte_table(atom.wrapped_atom, depth + 1)
326
+ when Parsanol::Atoms::Lookahead
327
+ # Positive: body's lead bytes. Negative: body's lead bytes are
328
+ # a safe superset (a failing !-check just fails the branch
329
+ # normally).
330
+ first_byte_table(atom.bound_parslet, depth + 1)
331
+ when Parsanol::Atoms::Repetition
332
+ # min 0 can match empty — no byte constraint exists.
333
+ return nil if atom.min.zero?
334
+
335
+ first_byte_table(atom.parslet, depth + 1)
336
+ when Parsanol::Atoms::Entity
337
+ inner = begin
338
+ atom.parslet
339
+ rescue StandardError
340
+ nil
341
+ end
342
+ return nil if inner.nil?
343
+
344
+ first_byte_table(inner, depth + 1)
345
+ when Parsanol::Atoms::Scope
346
+ inner = begin
347
+ atom.block.call
348
+ rescue StandardError
349
+ nil
350
+ end
351
+ return nil if inner.nil?
352
+
353
+ first_byte_table(inner, depth + 1)
354
+ end
355
+ end
356
+
357
+ # Wrapper branches (Entity/Scope) share body shapes by design.
358
+ # rubocop:disable-next Lint/DuplicateBranch
359
+ def nullable?(atom, depth = 0)
360
+ return false if depth > 10
361
+
362
+ case atom
363
+ when Parsanol::Atoms::Str then atom.str.bytesize.zero?
364
+ when Parsanol::Atoms::Re then false
365
+ when Parsanol::Atoms::Sequence
366
+ atom.parslets.all? { |c| nullable?(c, depth + 1) }
367
+ when Parsanol::Atoms::Alternative
368
+ atom.alternatives.any? { |c| nullable?(c, depth + 1) }
369
+ when Parsanol::Atoms::Repetition then atom.min.zero?
370
+ when Parsanol::Atoms::Named then nullable?(atom.parslet, depth + 1)
371
+ when Parsanol::Atoms::Ignored then nullable?(atom.wrapped_atom, depth + 1)
372
+ when Parsanol::Atoms::Lookahead then true
373
+ when Parsanol::Atoms::Entity, Parsanol::Atoms::Scope
374
+ nullable_wrapped?( # rubocop:disable Lint/DuplicateBranch -- Entity/Scope differ only in accessor
375
+ -> { atom.is_a?(Parsanol::Atoms::Entity) ? atom.parslet : atom.block.call }, depth
376
+ )
377
+ else false
378
+ end
379
+ end
380
+
381
+ # Entity and Scope wrap lazily-resolvable inners through different
382
+ # accessors; the nullability logic is shared.
383
+ def nullable_wrapped?(resolver, depth)
384
+ inner = begin
385
+ resolver.call
386
+ rescue StandardError
387
+ nil
388
+ end
389
+ !inner.nil? && nullable?(inner, depth + 1)
390
+ end
391
+
250
392
  def compile_atom(atom, consume_all)
251
393
  case atom
252
394
  when Parsanol::Atoms::Str
@@ -282,6 +424,37 @@ module Parsanol
282
424
  count = alts.size
283
425
  return nil if count.zero?
284
426
 
427
+ # First-set discrimination: when every branch has a provable
428
+ # lead-byte table and the tables are pairwise disjoint, at most
429
+ # one branch can match any input — dispatch directly to the
430
+ # viable branch (or fail) in one lookup. Shared lead bytes
431
+ # fall back to the plain CHOICE machinery untouched.
432
+ tables = alts.map { |a| first_byte_table(a) }
433
+ if tables.all? && pairwise_disjoint?(tables)
434
+ dispatch_idx = emit(BYTE_DISPATCH, nil, nil, nil)
435
+ starts = []
436
+ idx = 0
437
+ while idx < count
438
+ starts << flat(@ops.size)
439
+ return nil unless compile_atom(alts[idx], consume_all)
440
+
441
+ idx += 1
442
+ end
443
+ table = Array.new(256, -1)
444
+ tables.each_with_index do |t, bi|
445
+ pc_i = starts[bi]
446
+ bi2 = bi
447
+ j = 0
448
+ while j < 256
449
+ table[j] = pc_i if t[j]
450
+ j += 1
451
+ end
452
+ bi2
453
+ end
454
+ patch(dispatch_idx, 1, table)
455
+ return self
456
+ end
457
+
285
458
  jumps = []
286
459
  idx = 0
287
460
  while idx < count
@@ -793,6 +966,11 @@ module Parsanol
793
966
  frames.slice!(cbase..)
794
967
  pc += 4
795
968
  end
969
+ when BYTE_DISPATCH
970
+ table = ops[pc + 1]
971
+ b = pos < n ? bytes[pos] : -1
972
+ target = b == -1 ? -1 : table[b]
973
+ pc = target >= 0 ? target : FAIL
796
974
  when SEQ_SIMPLE
797
975
  kids = ops[pc + 1]
798
976
  kn = kids.size
data/parsanol.gemspec CHANGED
@@ -46,22 +46,4 @@ Gem::Specification.new do |spec|
46
46
 
47
47
  # Required for Rust extension
48
48
  spec.add_dependency "rb_sys", "~> 0.9.39"
49
-
50
- spec.add_development_dependency "rake", "~> 13.0"
51
- spec.add_development_dependency "rake-compiler", "~> 1.2.0"
52
- spec.add_development_dependency "rdoc", "~> 6.0"
53
- spec.add_development_dependency "rspec", "~> 3.0"
54
-
55
- # For code style checking
56
- spec.add_development_dependency "rubocop", "~> 1.0"
57
-
58
- # For Parslet compatibility verification
59
- spec.add_development_dependency "parslet", "~> 2.0.0"
60
-
61
- # For benchmarking
62
- spec.add_development_dependency "benchmark-ips", "~> 2.0"
63
-
64
- # For type checking
65
- spec.add_development_dependency "rbs", "~> 3.0"
66
- spec.add_development_dependency "steep", "~> 1.0"
67
49
  end
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.28
4
+ version: 1.3.30
5
5
  platform: arm-linux
6
6
  authors:
7
7
  - Ribose Inc.
@@ -9,133 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
  date: 2026-09-17 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rake
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '13.0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '13.0'
27
- - !ruby/object:Gem::Dependency
28
- name: rake-compiler
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: 1.2.0
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: 1.2.0
41
- - !ruby/object:Gem::Dependency
42
- name: rdoc
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '6.0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '6.0'
55
- - !ruby/object:Gem::Dependency
56
- name: rspec
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '3.0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '3.0'
69
- - !ruby/object:Gem::Dependency
70
- name: rubocop
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '1.0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '1.0'
83
- - !ruby/object:Gem::Dependency
84
- name: parslet
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: 2.0.0
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: 2.0.0
97
- - !ruby/object:Gem::Dependency
98
- name: benchmark-ips
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '2.0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '2.0'
111
- - !ruby/object:Gem::Dependency
112
- name: rbs
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: '3.0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: '3.0'
125
- - !ruby/object:Gem::Dependency
126
- name: steep
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - "~>"
130
- - !ruby/object:Gem::Version
131
- version: '1.0'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - "~>"
137
- - !ruby/object:Gem::Version
138
- version: '1.0'
12
+ dependencies: []
139
13
  description: A small Ruby library for constructing parsers in the PEG (Parsing Expression
140
14
  Grammar) fashion. Parsanol provides Parslet-compatible API with additional features
141
15
  including static frozen parsers and dynamic parsers, with optional Rust native extension