parsanol 1.3.28 → 1.3.30
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 +4 -4
- data/Cargo.lock +2 -2
- data/lib/parsanol/version.rb +1 -1
- data/lib/parsanol/vm.rb +178 -0
- data/parsanol.gemspec +0 -18
- metadata +1 -127
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 21f56a5196775ab90fe91a82e7b71240bc1f48b64ed90886b947285bf760c0b7
|
|
4
|
+
data.tar.gz: 1463e0eb3af171d61f8e09f30deec7d15bc679341d129197cdb5845efd3357b3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e552a716aa3cf2facea589d2310db16e14664ceefd6d1412d97fb945314dadb0ef31dbe2d835414d6d05c8dd1420386a244ce7ff7c1fba42e97ac26180d2db14
|
|
7
|
+
data.tar.gz: 16cf21e24b996b1f1a314e36c29b81c7ba3f16a303d9538886b823c94db1759b662e732fdb68a019e5adfb264e2c381de4aa7be3cc7907538173ef260c173a69
|
data/Cargo.lock
CHANGED
|
@@ -253,7 +253,7 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
|
253
253
|
[[package]]
|
|
254
254
|
name = "parsanol"
|
|
255
255
|
version = "0.5.1"
|
|
256
|
-
source = "git+https://github.com/parsanol/parsanol-rs?branch=main#
|
|
256
|
+
source = "git+https://github.com/parsanol/parsanol-rs?branch=main#32cdbb1d3c04c9280f6fb4d2cf13f1c5befbd00f"
|
|
257
257
|
dependencies = [
|
|
258
258
|
"ahash",
|
|
259
259
|
"getrandom 0.3.4",
|
|
@@ -270,7 +270,7 @@ dependencies = [
|
|
|
270
270
|
[[package]]
|
|
271
271
|
name = "parsanol-derive"
|
|
272
272
|
version = "0.5.1"
|
|
273
|
-
source = "git+https://github.com/parsanol/parsanol-rs?branch=main#
|
|
273
|
+
source = "git+https://github.com/parsanol/parsanol-rs?branch=main#32cdbb1d3c04c9280f6fb4d2cf13f1c5befbd00f"
|
|
274
274
|
dependencies = [
|
|
275
275
|
"proc-macro2",
|
|
276
276
|
"quote",
|
data/lib/parsanol/version.rb
CHANGED
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.
|
|
4
|
+
version: 1.3.30
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -23,132 +23,6 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: 0.9.39
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: rake
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - "~>"
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '13.0'
|
|
33
|
-
type: :development
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - "~>"
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '13.0'
|
|
40
|
-
- !ruby/object:Gem::Dependency
|
|
41
|
-
name: rake-compiler
|
|
42
|
-
requirement: !ruby/object:Gem::Requirement
|
|
43
|
-
requirements:
|
|
44
|
-
- - "~>"
|
|
45
|
-
- !ruby/object:Gem::Version
|
|
46
|
-
version: 1.2.0
|
|
47
|
-
type: :development
|
|
48
|
-
prerelease: false
|
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
-
requirements:
|
|
51
|
-
- - "~>"
|
|
52
|
-
- !ruby/object:Gem::Version
|
|
53
|
-
version: 1.2.0
|
|
54
|
-
- !ruby/object:Gem::Dependency
|
|
55
|
-
name: rdoc
|
|
56
|
-
requirement: !ruby/object:Gem::Requirement
|
|
57
|
-
requirements:
|
|
58
|
-
- - "~>"
|
|
59
|
-
- !ruby/object:Gem::Version
|
|
60
|
-
version: '6.0'
|
|
61
|
-
type: :development
|
|
62
|
-
prerelease: false
|
|
63
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
-
requirements:
|
|
65
|
-
- - "~>"
|
|
66
|
-
- !ruby/object:Gem::Version
|
|
67
|
-
version: '6.0'
|
|
68
|
-
- !ruby/object:Gem::Dependency
|
|
69
|
-
name: rspec
|
|
70
|
-
requirement: !ruby/object:Gem::Requirement
|
|
71
|
-
requirements:
|
|
72
|
-
- - "~>"
|
|
73
|
-
- !ruby/object:Gem::Version
|
|
74
|
-
version: '3.0'
|
|
75
|
-
type: :development
|
|
76
|
-
prerelease: false
|
|
77
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
-
requirements:
|
|
79
|
-
- - "~>"
|
|
80
|
-
- !ruby/object:Gem::Version
|
|
81
|
-
version: '3.0'
|
|
82
|
-
- !ruby/object:Gem::Dependency
|
|
83
|
-
name: rubocop
|
|
84
|
-
requirement: !ruby/object:Gem::Requirement
|
|
85
|
-
requirements:
|
|
86
|
-
- - "~>"
|
|
87
|
-
- !ruby/object:Gem::Version
|
|
88
|
-
version: '1.0'
|
|
89
|
-
type: :development
|
|
90
|
-
prerelease: false
|
|
91
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
-
requirements:
|
|
93
|
-
- - "~>"
|
|
94
|
-
- !ruby/object:Gem::Version
|
|
95
|
-
version: '1.0'
|
|
96
|
-
- !ruby/object:Gem::Dependency
|
|
97
|
-
name: parslet
|
|
98
|
-
requirement: !ruby/object:Gem::Requirement
|
|
99
|
-
requirements:
|
|
100
|
-
- - "~>"
|
|
101
|
-
- !ruby/object:Gem::Version
|
|
102
|
-
version: 2.0.0
|
|
103
|
-
type: :development
|
|
104
|
-
prerelease: false
|
|
105
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
-
requirements:
|
|
107
|
-
- - "~>"
|
|
108
|
-
- !ruby/object:Gem::Version
|
|
109
|
-
version: 2.0.0
|
|
110
|
-
- !ruby/object:Gem::Dependency
|
|
111
|
-
name: benchmark-ips
|
|
112
|
-
requirement: !ruby/object:Gem::Requirement
|
|
113
|
-
requirements:
|
|
114
|
-
- - "~>"
|
|
115
|
-
- !ruby/object:Gem::Version
|
|
116
|
-
version: '2.0'
|
|
117
|
-
type: :development
|
|
118
|
-
prerelease: false
|
|
119
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
-
requirements:
|
|
121
|
-
- - "~>"
|
|
122
|
-
- !ruby/object:Gem::Version
|
|
123
|
-
version: '2.0'
|
|
124
|
-
- !ruby/object:Gem::Dependency
|
|
125
|
-
name: rbs
|
|
126
|
-
requirement: !ruby/object:Gem::Requirement
|
|
127
|
-
requirements:
|
|
128
|
-
- - "~>"
|
|
129
|
-
- !ruby/object:Gem::Version
|
|
130
|
-
version: '3.0'
|
|
131
|
-
type: :development
|
|
132
|
-
prerelease: false
|
|
133
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
-
requirements:
|
|
135
|
-
- - "~>"
|
|
136
|
-
- !ruby/object:Gem::Version
|
|
137
|
-
version: '3.0'
|
|
138
|
-
- !ruby/object:Gem::Dependency
|
|
139
|
-
name: steep
|
|
140
|
-
requirement: !ruby/object:Gem::Requirement
|
|
141
|
-
requirements:
|
|
142
|
-
- - "~>"
|
|
143
|
-
- !ruby/object:Gem::Version
|
|
144
|
-
version: '1.0'
|
|
145
|
-
type: :development
|
|
146
|
-
prerelease: false
|
|
147
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
-
requirements:
|
|
149
|
-
- - "~>"
|
|
150
|
-
- !ruby/object:Gem::Version
|
|
151
|
-
version: '1.0'
|
|
152
26
|
description: A small Ruby library for constructing parsers in the PEG (Parsing Expression
|
|
153
27
|
Grammar) fashion. Parsanol provides Parslet-compatible API with additional features
|
|
154
28
|
including static frozen parsers and dynamic parsers, with optional Rust native extension
|