parsanol 1.3.97-arm-linux → 1.3.99-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 +4 -4
- data/lib/parsanol/3.2/parsanol_native.so +2 -2
- data/lib/parsanol/3.3/parsanol_native.so +2 -2
- data/lib/parsanol/3.4/parsanol_native.so +2 -2
- data/lib/parsanol/4.0/parsanol_native.so +2 -2
- data/lib/parsanol/native/libparsanol.so +0 -0
- data/lib/parsanol/parg/artifact.rb +10 -2
- data/lib/parsanol/parg/bindings.rb +6 -1
- data/lib/parsanol/parg/compiler.rb +139 -8
- data/lib/parsanol/parg/document.rb +1 -1
- data/lib/parsanol/parg/lexer.rb +1 -1
- data/lib/parsanol/parg/parser.rb +81 -8
- data/lib/parsanol/parg/selfhost.rb +2 -0
- data/lib/parsanol/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0449928649d5a003cea029230dccdb8d4a1cf5e15a3115d713396f256e5a4cbf'
|
|
4
|
+
data.tar.gz: 6f78bd36cb506e26a808d16d0e82cab33d1d4af239ff2850247b9ff29925e51a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dc51f046f4d0e11b562f8dc35d37eed3476cb6d6bc77f0692d5363b87a8707444f04f4416a1dfef85db1a6e3dbb551f3477145831e2daa1cf21159646c086ef6
|
|
7
|
+
data.tar.gz: 02bcffde2c4565099de61ffc31de674f24c244bbb1ac2bb5820ae5c4636d780f775ce092f57c64c2d8a47706a275899e7ea4358ffe26a93ee6b204a7b9609859
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: data/lib/parsanol/3.2/parsanol_native.so
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 34547268 bytes
|
|
4
|
+
sha256: 5ffe08395738b583a3472e832256ff294510921a25ff6e53dc90c951905191ec
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: data/lib/parsanol/3.3/parsanol_native.so
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 34732408 bytes
|
|
4
|
+
sha256: c187d2bd208b1d8aef9c66f4d3188496789e48b93dc6fb59c65cbbfcd300a6ca
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: data/lib/parsanol/3.4/parsanol_native.so
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 34726616 bytes
|
|
4
|
+
sha256: 14dcfeb7a0bad418583ac54f7430c5212c273c5df0a3e0ae3eaba8ed14156907
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: data/lib/parsanol/4.0/parsanol_native.so
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 34736332 bytes
|
|
4
|
+
sha256: 8eb3b9649e9070ee5ec045863c45cdc50a4ae76faf0c0c45fa6f26f15e503db4
|
|
Binary file
|
|
@@ -165,6 +165,13 @@ module Parsanol
|
|
|
165
165
|
case hash["kind"]
|
|
166
166
|
when "reject"
|
|
167
167
|
"test #{hash['input'].inspect}: expected the input to be rejected"
|
|
168
|
+
when "shape"
|
|
169
|
+
actual = Bindings.canonical(shape)
|
|
170
|
+
expected = Bindings.canonical(hash["expect"])
|
|
171
|
+
next if actual == expected
|
|
172
|
+
|
|
173
|
+
"test #{hash['input'].inspect}: expected shape #{expected.inspect}, " \
|
|
174
|
+
"got #{actual.inspect}"
|
|
168
175
|
when "example"
|
|
169
176
|
bound = apply_bindings(entry_name, shape)
|
|
170
177
|
expect = hash["expect"].to_h { |key, value| [key.to_sym, value] }
|
|
@@ -178,8 +185,9 @@ module Parsanol
|
|
|
178
185
|
when "capture"
|
|
179
186
|
captures = Bindings.collect_captures(shape)
|
|
180
187
|
mismatched = hash["expect"].reject do |key, value|
|
|
181
|
-
|
|
182
|
-
!
|
|
188
|
+
values = captures[key.to_sym]
|
|
189
|
+
values && !values.empty? &&
|
|
190
|
+
Bindings.canonical(values.first) == Bindings.canonical(value)
|
|
183
191
|
end
|
|
184
192
|
next if mismatched.empty?
|
|
185
193
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
3
5
|
module Parsanol
|
|
4
6
|
module PARG
|
|
5
7
|
# Applies an entry's artifact bindings to a parsanol-shape parse tree,
|
|
@@ -32,6 +34,7 @@ module Parsanol
|
|
|
32
34
|
when Hash then value.to_h { |key, item| [key.to_s, canonical(item)] }
|
|
33
35
|
when Array then value.map { |item| canonical(item) }
|
|
34
36
|
when nil, true, false, Integer then value
|
|
37
|
+
when Float then JSON.generate(value) # B6: the JSON number text
|
|
35
38
|
else String(value)
|
|
36
39
|
end
|
|
37
40
|
end
|
|
@@ -64,7 +67,9 @@ module Parsanol
|
|
|
64
67
|
count = group.map { |binding| captures[binding["capture"].to_sym].length }.max || 0
|
|
65
68
|
return if count.zero?
|
|
66
69
|
|
|
67
|
-
|
|
70
|
+
# Field names are symbols everywhere else (scalar bindings, element
|
|
71
|
+
# fields); the array prefix must match or `example` lookups miss it.
|
|
72
|
+
list = (out[prefix.to_sym] ||= [])
|
|
68
73
|
count.times do |index|
|
|
69
74
|
element = {}
|
|
70
75
|
group.each do |binding|
|
|
@@ -53,8 +53,13 @@ module Parsanol
|
|
|
53
53
|
@dynamic = false
|
|
54
54
|
@tables = {}
|
|
55
55
|
@warnings = []
|
|
56
|
+
@expanded = false
|
|
56
57
|
end
|
|
57
58
|
|
|
59
|
+
# Reserved prefix for the tier rules synthesized by the direct
|
|
60
|
+
# left-recursion expansion (see expand_left_recursion!).
|
|
61
|
+
LEFT_RECURSION_PREFIX = "__left_"
|
|
62
|
+
|
|
58
63
|
# QoL shorthands (ABNF core rules): materialized on reference when
|
|
59
64
|
# the grammar does not define its own rule of that name.
|
|
60
65
|
BUILTIN_CLASSES = {
|
|
@@ -67,6 +72,7 @@ module Parsanol
|
|
|
67
72
|
}.freeze
|
|
68
73
|
|
|
69
74
|
def compile
|
|
75
|
+
expand_left_recursion!
|
|
70
76
|
@document.rules.each_key { |name| atom_for(name) }
|
|
71
77
|
errors = Lints.errors(@document, self)
|
|
72
78
|
raise CompileError, errors.join("\n") unless errors.empty?
|
|
@@ -121,6 +127,7 @@ module Parsanol
|
|
|
121
127
|
end
|
|
122
128
|
|
|
123
129
|
def atom_for(name)
|
|
130
|
+
expand_left_recursion! unless @expanded
|
|
124
131
|
@atom_cache[name] ||= begin
|
|
125
132
|
atom = build_atom(fetch_rule(name))
|
|
126
133
|
# Atomic rules build without injection entirely: token-shaped
|
|
@@ -348,6 +355,106 @@ module Parsanol
|
|
|
348
355
|
# In-file grammar tests run at compile time: the build fails when an
|
|
349
356
|
# accept/example test does not parse or an example's expected capture
|
|
350
357
|
# pairs do not match the bound result.
|
|
358
|
+
# Direct left recursion (owner decision 2026-10-21, parsanol-ruby#251):
|
|
359
|
+
# rewrite `r = r op1 r / r op2 r / ... / primary` into a chain of
|
|
360
|
+
# ordinary left-folding tier rules — the ANTLR 4 model. Branches in
|
|
361
|
+
# source order are precedence tiers, earlier binding tighter; the
|
|
362
|
+
# non-recursive alternatives are the primary (loosest operand). The
|
|
363
|
+
# rewritten tiers are plain rules, so every engine inherits the
|
|
364
|
+
# feature through the unchanged artifact format and the fold value is
|
|
365
|
+
# exactly the flattening of the equivalent loop grammar (V1/V3).
|
|
366
|
+
# Indirect left recursion keeps its compile error.
|
|
367
|
+
def expand_left_recursion!
|
|
368
|
+
return if @expanded
|
|
369
|
+
|
|
370
|
+
@expanded = true
|
|
371
|
+
if @document.rules.any? { |name, _| name.start_with?(LEFT_RECURSION_PREFIX) }
|
|
372
|
+
raise CompileError,
|
|
373
|
+
"rule names beginning with #{LEFT_RECURSION_PREFIX.inspect} are reserved " \
|
|
374
|
+
"for the compiler's left-recursion expansion"
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Snapshot: expansion adds tier rules to @document.rules.
|
|
378
|
+
@document.rules.dup.each_key do |name|
|
|
379
|
+
body = @document.rules[name]
|
|
380
|
+
next unless left_recursive_body?(name, body)
|
|
381
|
+
|
|
382
|
+
expand_left_recursive_rule(name, body)
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def left_recursive_body?(name, body)
|
|
387
|
+
branches = body.kind == :alt ? body.a : [body]
|
|
388
|
+
branches.any? { |branch| leftmost_refs(branch, foldable: true).include?(name) }
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def expand_left_recursive_rule(name, body)
|
|
392
|
+
branches = body.kind == :alt ? body.a : [body]
|
|
393
|
+
left, primary =
|
|
394
|
+
branches.partition { |branch| leftmost_refs(branch, foldable: true).include?(name) }
|
|
395
|
+
if primary.empty?
|
|
396
|
+
raise CompileError,
|
|
397
|
+
"rule #{name.inspect} is left-recursive and has no " \
|
|
398
|
+
"non-recursive alternative to anchor the rewrite"
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
rules = @document.rules
|
|
402
|
+
prev = "#{LEFT_RECURSION_PREFIX}#{name}_primary"
|
|
403
|
+
rules[prev] = primary.length == 1 ? primary.first : Node.new(:alt, primary)
|
|
404
|
+
left.each_with_index do |branch, index|
|
|
405
|
+
tier = "#{LEFT_RECURSION_PREFIX}#{name}_#{index + 1}"
|
|
406
|
+
suffix = left_recursive_suffix(name, branch)
|
|
407
|
+
suffix = substitute_refs(suffix, name, prev)
|
|
408
|
+
rules[tier] = Node.new(:seq, [Node.new(:ref, prev), Node.new(:rep, suffix, 0, nil)])
|
|
409
|
+
prev = tier
|
|
410
|
+
end
|
|
411
|
+
rules[name] = Node.new(:ref, prev)
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# A left-recursive alternative must BEGIN with a bare reference to the
|
|
415
|
+
# rule itself; everything after that reference is the per-iteration
|
|
416
|
+
# suffix, which must consume input (a nullable suffix would loop
|
|
417
|
+
# forever at the fold).
|
|
418
|
+
def left_recursive_suffix(name, branch)
|
|
419
|
+
if branch.kind == :ref
|
|
420
|
+
raise CompileError,
|
|
421
|
+
"left-recursive alternative in rule #{name.inspect} consists only of the " \
|
|
422
|
+
"recursive reference and would loop forever"
|
|
423
|
+
end
|
|
424
|
+
unless branch.kind == :seq && (head = branch.a.first)&.kind == :ref && head.a == name
|
|
425
|
+
raise CompileError,
|
|
426
|
+
"left-recursive alternative in rule #{name.inspect} must begin with a " \
|
|
427
|
+
"bare reference to #{name.inspect} (write `#{name} <rest>`; captures and " \
|
|
428
|
+
"predicates around the leading reference are not supported)"
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
rest = branch.a[1..]
|
|
432
|
+
rest_node = rest.length == 1 ? rest.first : Node.new(:seq, rest)
|
|
433
|
+
if nullable?(rest_node)
|
|
434
|
+
raise CompileError,
|
|
435
|
+
"left-recursive alternative in rule #{name.inspect} can match empty after " \
|
|
436
|
+
"the leading reference and would loop forever at the fold"
|
|
437
|
+
end
|
|
438
|
+
rest_node
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
def substitute_refs(node, from, to)
|
|
442
|
+
if node.kind == :ref
|
|
443
|
+
return Node.new(:ref, to) if node.a == from
|
|
444
|
+
|
|
445
|
+
return node
|
|
446
|
+
end
|
|
447
|
+
case node.kind
|
|
448
|
+
when :seq then Node.new(:seq, node.a.map { |child| substitute_refs(child, from, to) })
|
|
449
|
+
when :alt then Node.new(:alt, node.a.map { |child| substitute_refs(child, from, to) })
|
|
450
|
+
when :rep then Node.new(:rep, substitute_refs(node.a, from, to), node.b, node.c)
|
|
451
|
+
when :opt then Node.new(:opt, substitute_refs(node.a, from, to))
|
|
452
|
+
when :pred then Node.new(:pred, node.a, substitute_refs(node.b, from, to), node.c)
|
|
453
|
+
when :cap then Node.new(:cap, node.a, substitute_refs(node.b, from, to))
|
|
454
|
+
else node
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
351
458
|
def run_tests
|
|
352
459
|
return [] if @document.tests.empty?
|
|
353
460
|
|
|
@@ -360,6 +467,13 @@ module Parsanol
|
|
|
360
467
|
case test.kind
|
|
361
468
|
when :reject
|
|
362
469
|
"test #{test.input.inspect}: expected the input to be rejected"
|
|
470
|
+
when :shape
|
|
471
|
+
actual = Bindings.canonical(shape)
|
|
472
|
+
expected = Bindings.canonical(test.expect)
|
|
473
|
+
next if actual == expected
|
|
474
|
+
|
|
475
|
+
"test #{test.input.inspect}: expected shape #{expected.inspect}, " \
|
|
476
|
+
"got #{actual.inspect}"
|
|
363
477
|
when :example
|
|
364
478
|
bindings = binding_list(rule)
|
|
365
479
|
if bindings.empty?
|
|
@@ -378,8 +492,9 @@ module Parsanol
|
|
|
378
492
|
when :capture
|
|
379
493
|
captures = Bindings.collect_captures(shape)
|
|
380
494
|
mismatched = test.expect.reject do |key, value|
|
|
381
|
-
|
|
382
|
-
!
|
|
495
|
+
values = captures[key.to_sym]
|
|
496
|
+
values && !values.empty? &&
|
|
497
|
+
Bindings.canonical(values.first) == Bindings.canonical(value)
|
|
383
498
|
end
|
|
384
499
|
next if mismatched.empty?
|
|
385
500
|
|
|
@@ -703,19 +818,24 @@ module Parsanol
|
|
|
703
818
|
nil
|
|
704
819
|
end
|
|
705
820
|
|
|
706
|
-
|
|
821
|
+
# A self-reference under a predicate cannot anchor a fold (and the
|
|
822
|
+
# lookbehind validation rejects rule bodies outright), so the
|
|
823
|
+
# rewrite's scan stops there; cycle detection still walks through.
|
|
824
|
+
def leftmost_refs(node, foldable: false)
|
|
707
825
|
case node.kind
|
|
708
826
|
when :ref then [node.a]
|
|
709
827
|
when :seq
|
|
710
828
|
refs = []
|
|
711
829
|
node.a.each do |item|
|
|
712
|
-
refs.concat(leftmost_refs(item))
|
|
830
|
+
refs.concat(leftmost_refs(item, foldable: foldable))
|
|
713
831
|
break unless nullable?(item)
|
|
714
832
|
end
|
|
715
833
|
refs.uniq
|
|
716
|
-
when :alt then node.a.flat_map { |branch| leftmost_refs(branch) }.uniq
|
|
717
|
-
when :rep, :opt then leftmost_refs(node.a)
|
|
718
|
-
when :pred
|
|
834
|
+
when :alt then node.a.flat_map { |branch| leftmost_refs(branch, foldable: foldable) }.uniq
|
|
835
|
+
when :rep, :opt then leftmost_refs(node.a, foldable: foldable)
|
|
836
|
+
when :pred
|
|
837
|
+
foldable ? [] : leftmost_refs(node.b)
|
|
838
|
+
when :cap then leftmost_refs(node.b, foldable: foldable)
|
|
719
839
|
else []
|
|
720
840
|
end
|
|
721
841
|
end
|
|
@@ -789,6 +909,17 @@ module Parsanol
|
|
|
789
909
|
|
|
790
910
|
# ---- artifact envelope ----------------------------------------------
|
|
791
911
|
|
|
912
|
+
# Test expectations are JSON-shaped: pair expectations are maps with
|
|
913
|
+
# symbol keys; `shape` expectations may be any value. Normalize keys
|
|
914
|
+
# recursively so the envelope is plain JSON.
|
|
915
|
+
def normalize_expect(value)
|
|
916
|
+
case value
|
|
917
|
+
when Hash then value.to_h { |key, item| [key.to_s, normalize_expect(item)] }
|
|
918
|
+
when Array then value.map { |item| normalize_expect(item) }
|
|
919
|
+
else value
|
|
920
|
+
end
|
|
921
|
+
end
|
|
922
|
+
|
|
792
923
|
def build_envelope
|
|
793
924
|
entries = @document.entries.to_h do |name, rule|
|
|
794
925
|
[name, entry_envelope(rule)]
|
|
@@ -818,7 +949,7 @@ module Parsanol
|
|
|
818
949
|
"entry" => test.entry || (@document.own_entries.first if @document.own_entries.size == 1),
|
|
819
950
|
"kind" => test.kind.to_s,
|
|
820
951
|
"input" => test.input,
|
|
821
|
-
"expect" => test.expect
|
|
952
|
+
"expect" => normalize_expect(test.expect),
|
|
822
953
|
}
|
|
823
954
|
end,
|
|
824
955
|
"docs" => @document.docs,
|
|
@@ -11,7 +11,7 @@ module Parsanol
|
|
|
11
11
|
# `capture` = phase 1 (grammar layer): asserts the raw capture tree,
|
|
12
12
|
# no bindings section needed. `example` = phase 2 (bindings layer):
|
|
13
13
|
# asserts the bound, preprocessed attribute hash.
|
|
14
|
-
TEST_KINDS = %i[accept reject example capture].freeze
|
|
14
|
+
TEST_KINDS = %i[accept reject example capture shape].freeze
|
|
15
15
|
# Test kinds whose expectations are `{ key: value }` pairs.
|
|
16
16
|
PAIR_KINDS = %w[example capture].freeze
|
|
17
17
|
|
data/lib/parsanol/parg/lexer.rb
CHANGED
data/lib/parsanol/parg/parser.rb
CHANGED
|
@@ -83,7 +83,12 @@ module Parsanol
|
|
|
83
83
|
kind = ident.value
|
|
84
84
|
input = unquote(expect(:str).value)
|
|
85
85
|
expect_pairs = {}
|
|
86
|
-
if
|
|
86
|
+
if kind == "shape"
|
|
87
|
+
# Whole-tree assertion: the expectation is an arbitrary value
|
|
88
|
+
# (text, array, object), not a capture-name map.
|
|
89
|
+
expect_pairs = parse_test_value
|
|
90
|
+
elsif Document::PAIR_KINDS.include?(kind) &&
|
|
91
|
+
peek&.type == :punct && peek.value == "{"
|
|
87
92
|
advance
|
|
88
93
|
skip_newlines
|
|
89
94
|
until peek&.type == :punct && peek.value == "}"
|
|
@@ -91,6 +96,8 @@ module Parsanol
|
|
|
91
96
|
punct(":")
|
|
92
97
|
expect_pairs[key.to_sym] = parse_test_value
|
|
93
98
|
skip_newlines
|
|
99
|
+
advance if peek&.type == :punct && peek.value == ","
|
|
100
|
+
skip_newlines
|
|
94
101
|
end
|
|
95
102
|
punct("}")
|
|
96
103
|
end
|
|
@@ -101,18 +108,84 @@ module Parsanol
|
|
|
101
108
|
skip_newlines
|
|
102
109
|
end
|
|
103
110
|
|
|
111
|
+
# Expectation values: strings, numbers, true/false/null, arrays, and
|
|
112
|
+
# nested objects. Nested structure is what pins array bindings,
|
|
113
|
+
# absent-optional nil, and flatten/merge results in the corpus;
|
|
114
|
+
# before this the test grammar only accepted scalars.
|
|
104
115
|
def parse_test_value
|
|
105
|
-
token =
|
|
116
|
+
token = peek
|
|
106
117
|
case token&.type
|
|
107
|
-
when :str then unquote(
|
|
108
|
-
when :num then
|
|
109
|
-
when :
|
|
110
|
-
|
|
118
|
+
when :str then unquote(advance.value)
|
|
119
|
+
when :num then parse_test_number
|
|
120
|
+
when :punct
|
|
121
|
+
case token.value
|
|
122
|
+
when "[" then parse_test_array
|
|
123
|
+
when "{" then parse_test_object
|
|
124
|
+
when "-"
|
|
125
|
+
advance
|
|
126
|
+
unless peek&.type == :num
|
|
127
|
+
raise ParseError, "expected a number after '-' in a test value"
|
|
128
|
+
end
|
|
111
129
|
|
|
112
|
-
|
|
130
|
+
-parse_test_number
|
|
131
|
+
else
|
|
132
|
+
raise ParseError,
|
|
133
|
+
"expected a test value (string, number, true, false, null, array, object)"
|
|
134
|
+
end
|
|
135
|
+
when :ident
|
|
136
|
+
advance
|
|
137
|
+
case token.value
|
|
138
|
+
when "true" then true
|
|
139
|
+
when "false" then false
|
|
140
|
+
when "null" then nil
|
|
141
|
+
else
|
|
142
|
+
raise ParseError,
|
|
143
|
+
"expected true, false, or null, got #{token.value.inspect}"
|
|
144
|
+
end
|
|
113
145
|
else
|
|
114
|
-
raise ParseError,
|
|
146
|
+
raise ParseError,
|
|
147
|
+
"expected a test value (string, number, true, false, null, array, object)"
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# The lexer emits integers only; a decimal is num "." num.
|
|
152
|
+
def parse_test_number
|
|
153
|
+
text = advance.value
|
|
154
|
+
if peek&.type == :punct && peek.value == "." && @tokens[@pos + 1]&.type == :num
|
|
155
|
+
advance
|
|
156
|
+
text = "#{text}.#{advance.value}"
|
|
115
157
|
end
|
|
158
|
+
text.include?(".") ? Float(text) : Integer(text, 10)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def parse_test_array
|
|
162
|
+
punct("[")
|
|
163
|
+
values = []
|
|
164
|
+
skip_newlines
|
|
165
|
+
until peek&.type == :punct && peek.value == "]"
|
|
166
|
+
values << parse_test_value
|
|
167
|
+
skip_newlines
|
|
168
|
+
advance if peek&.type == :punct && peek.value == ","
|
|
169
|
+
skip_newlines
|
|
170
|
+
end
|
|
171
|
+
punct("]")
|
|
172
|
+
values
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def parse_test_object
|
|
176
|
+
punct("{")
|
|
177
|
+
value = {}
|
|
178
|
+
skip_newlines
|
|
179
|
+
until peek&.type == :punct && peek.value == "}"
|
|
180
|
+
key = ident.value
|
|
181
|
+
punct(":")
|
|
182
|
+
value[key.to_sym] = parse_test_value
|
|
183
|
+
skip_newlines
|
|
184
|
+
advance if peek&.type == :punct && peek.value == ","
|
|
185
|
+
skip_newlines
|
|
186
|
+
end
|
|
187
|
+
punct("}")
|
|
188
|
+
value
|
|
116
189
|
end
|
|
117
190
|
|
|
118
191
|
def eof? = @pos >= @tokens.length
|
data/lib/parsanol/version.rb
CHANGED
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.
|
|
4
|
+
version: 1.3.99
|
|
5
5
|
platform: arm-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-21 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
|