parsanol 1.3.44-x86_64-linux → 1.3.45-x86_64-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/batch_decoder.rb +7 -0
- data/lib/parsanol/native/transformer.rb +76 -286
- data/lib/parsanol/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6ed8f95c36bf9e3dd85b870a405ccbcec380716c96289d93cd86b7c37dee85a7
|
|
4
|
+
data.tar.gz: 862a88b04453dff2b2d592cd0738f7a7f7eae8d33ebf8f12840d3866284a0b95
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b44f418c7e8ad987128ee60d3b75fba74cd20fa424690966343c761908a7054882f76a84b756303794d01c2a2a1082586b8f0894c4964681fceb8b6b5d78971f
|
|
7
|
+
data.tar.gz: 20d795c4f67853769740817322ade2714d3978cbf6ffa81bb2cb7ace226d682b3165daf3d7888e99c0778df370da79d3a3888b80793fa9f5849528b9549f8615
|
|
@@ -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: 34568360 bytes
|
|
4
|
+
sha256: c2f5a5efc114a59720cc992e300aeede1934333de9066e5ad5e502c7e795b78d
|
|
@@ -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: 34797976 bytes
|
|
4
|
+
sha256: 4d0b945dc2434399b9bb4aebc0a25d39cfef140091eab772320afe027b519a2d
|
|
@@ -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: 34788784 bytes
|
|
4
|
+
sha256: 9392d2b6618f0a15b0701863b9f5c316eb55274cf67ef5975bfd467719ff90af
|
|
@@ -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: 34798712 bytes
|
|
4
|
+
sha256: 2b0a6f998b61524c94579f09d5aa7dafa1d5961f156110699ff9352bf97f845d
|
|
@@ -169,6 +169,13 @@ module Parsanol
|
|
|
169
169
|
@pos += 1
|
|
170
170
|
str = decode_inline_string_bytes(len)
|
|
171
171
|
str.to_sym
|
|
172
|
+
when TAG_INLINE_STRING
|
|
173
|
+
# Inline string: len, then u64 chunks (same wire form as
|
|
174
|
+
# TAG_SYMBOL without the to_sym). Emitted by the rs 0.8.x
|
|
175
|
+
# engine; previously unhandled here (parsanol-ruby#83).
|
|
176
|
+
len = @data[@pos]
|
|
177
|
+
@pos += 1
|
|
178
|
+
decode_inline_string_bytes(len)
|
|
172
179
|
when TAG_REPETITION
|
|
173
180
|
inner = decode_value
|
|
174
181
|
[:repetition, inner].compact
|
|
@@ -83,7 +83,8 @@ module Parsanol
|
|
|
83
83
|
elsif [REPETITION_SYM, REPETITION_TAG].include?(first)
|
|
84
84
|
# Optimized: transform items starting from index 1
|
|
85
85
|
len = arr.length
|
|
86
|
-
|
|
86
|
+
# Empty repetition: named → [], unnamed → "" (CanFlatten#flatten_repetition)
|
|
87
|
+
return (named ? EMPTY_ARRAY : EMPTY_STRING) if len == 1
|
|
87
88
|
|
|
88
89
|
items = Array.new(len - 1)
|
|
89
90
|
i = 0
|
|
@@ -91,7 +92,7 @@ module Parsanol
|
|
|
91
92
|
items[i] = transform(arr[i + 1])
|
|
92
93
|
i += 1
|
|
93
94
|
end
|
|
94
|
-
flatten_repetition(items)
|
|
95
|
+
flatten_repetition(items, named: named)
|
|
95
96
|
elsif [MAYBE_SYM, MAYBE_TAG].include?(first)
|
|
96
97
|
# Maybe flattens to nil-or-value (named) or ""-or-value (unnamed),
|
|
97
98
|
# never to an array
|
|
@@ -290,309 +291,98 @@ module Parsanol
|
|
|
290
291
|
result
|
|
291
292
|
end
|
|
292
293
|
|
|
293
|
-
#
|
|
294
|
-
#
|
|
295
|
-
#
|
|
296
|
-
#
|
|
297
|
-
#
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
# match('[a-z]').as(:x).repeat(2)
|
|
305
|
-
# returns: [{:x => "a"}, {:x => "b"}] (array of hashes, NOT merged!)
|
|
306
|
-
#
|
|
307
|
-
# Optimized: Single-pass with direct result building
|
|
294
|
+
# Exact port of Parslet::Atoms::CanFlatten#flatten_sequence /
|
|
295
|
+
# #merge_fold / #flatten_repetition / #foldl. Heuristic single-pass
|
|
296
|
+
# rewrites diverged from this (parsanol-ruby#83); stay byte-for-byte
|
|
297
|
+
# with parslet's fold. Hot shapes (all-Hash, all-stringlike)
|
|
298
|
+
# short-circuit before the fold to avoid per-step allocations.
|
|
299
|
+
def self.foldl(list, &)
|
|
300
|
+
return EMPTY_STRING if list.empty?
|
|
301
|
+
|
|
302
|
+
list.drop(1).inject(list.first, &)
|
|
303
|
+
end
|
|
304
|
+
|
|
308
305
|
def self.flatten_sequence(items)
|
|
309
|
-
|
|
310
|
-
return
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
has_non_empty_array = false
|
|
318
|
-
|
|
319
|
-
items.each do |item|
|
|
320
|
-
case item
|
|
321
|
-
when Hash
|
|
322
|
-
merged_hash.merge!(item)
|
|
323
|
-
hash_count += 1
|
|
324
|
-
total_items += 1
|
|
325
|
-
when ::Parsanol::Slice, String
|
|
326
|
-
slice_or_string_parts << item
|
|
327
|
-
total_items += 1
|
|
328
|
-
when Array
|
|
329
|
-
# Check if this is a non-empty array (repetition result with content)
|
|
330
|
-
# Parslet behavior: when a sequence contains a non-empty repetition,
|
|
331
|
-
# the WHOLE sequence should be kept as array, not merged.
|
|
332
|
-
if item.empty?
|
|
333
|
-
# Empty repetition - skip (sequence semantics: merge rest)
|
|
334
|
-
# Don't increment total_items for empty arrays
|
|
335
|
-
else
|
|
336
|
-
# Check if array contains only hashes (repetition wrapper pattern)
|
|
337
|
-
# In this case, merge the inner hashes into merged_hash
|
|
338
|
-
non_hash_items = item.grep_v(Hash)
|
|
339
|
-
all_items_are_hashes = non_hash_items.empty?
|
|
340
|
-
|
|
341
|
-
if all_items_are_hashes
|
|
342
|
-
# Check if merging would overwrite existing keys in merged_hash.
|
|
343
|
-
# If so, this is a repetition pattern (item >> (sep >> item).repeat)
|
|
344
|
-
# and should be kept as array, not merged.
|
|
345
|
-
# Example: merged_hash={namedTypeOrRename: A}, array=[{namedTypeOrRename: B}]
|
|
346
|
-
# → should produce [{namedTypeOrRename: A}, {namedTypeOrRename: B}]
|
|
347
|
-
existing_keys = merged_hash.keys
|
|
348
|
-
repeated_keys = item.flat_map(&:keys).tally.any? { |_key, count| count > 1 }
|
|
349
|
-
shares_keys = repeated_keys || item.any? do |sub_item|
|
|
350
|
-
sub_item.is_a?(Hash) && sub_item.keys.intersect?(existing_keys)
|
|
351
|
-
end
|
|
352
|
-
|
|
353
|
-
if shares_keys
|
|
354
|
-
has_non_empty_array = true
|
|
355
|
-
item.each do |sub_item|
|
|
356
|
-
hash_count += 1 if sub_item.is_a?(Hash)
|
|
357
|
-
end
|
|
358
|
-
total_items += 1
|
|
359
|
-
else
|
|
360
|
-
item.each do |sub_item|
|
|
361
|
-
merged_hash.merge!(sub_item) if sub_item.is_a?(Hash)
|
|
362
|
-
end
|
|
363
|
-
end
|
|
364
|
-
else
|
|
365
|
-
# Non-empty repetition with non-hash items - mark that we should keep as array
|
|
366
|
-
has_non_empty_array = true
|
|
367
|
-
# Still collect items for potential array result
|
|
368
|
-
item.each do |sub_item|
|
|
369
|
-
case sub_item
|
|
370
|
-
when Hash
|
|
371
|
-
hash_count += 1
|
|
372
|
-
when ::Parsanol::Slice, String
|
|
373
|
-
slice_or_string_parts << sub_item
|
|
374
|
-
end
|
|
375
|
-
end
|
|
376
|
-
total_items += 1
|
|
377
|
-
end
|
|
378
|
-
end
|
|
379
|
-
when nil
|
|
380
|
-
# Skip nil values (from lookahead or optional that didn't match)
|
|
381
|
-
else
|
|
382
|
-
total_items += 1
|
|
383
|
-
end
|
|
306
|
+
list = items.compact
|
|
307
|
+
return EMPTY_STRING if list.empty?
|
|
308
|
+
return list.first if list.length == 1
|
|
309
|
+
|
|
310
|
+
# Hot path: all-hash sequence. Single-pass merge preserves
|
|
311
|
+
# parslet's merge_fold(Hash, Hash) last-wins semantics.
|
|
312
|
+
if list.all?(Hash)
|
|
313
|
+
return list.reduce { |acc, hash| acc.merge(hash) }
|
|
384
314
|
end
|
|
385
315
|
|
|
386
|
-
#
|
|
387
|
-
#
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if has_non_empty_array
|
|
393
|
-
# Flatten the items: top-level hashes + array items
|
|
394
|
-
result = []
|
|
395
|
-
items.each do |item|
|
|
396
|
-
case item
|
|
397
|
-
when Hash
|
|
398
|
-
result << item
|
|
399
|
-
when Array
|
|
400
|
-
result.concat(item)
|
|
401
|
-
when ::Parsanol::Slice, String
|
|
402
|
-
# Skip unnamed Slices/strings when we have named captures
|
|
403
|
-
end
|
|
404
|
-
end
|
|
405
|
-
return result.length == 1 ? result.first : result
|
|
316
|
+
# Hot path: all stringlike (String/Slice). Build one Slice
|
|
317
|
+
# from the first Slice's offset, joining contents in place.
|
|
318
|
+
if list.all? { |x| x.is_a?(::Parsanol::Slice) || x.is_a?(String) }
|
|
319
|
+
first_slice = list.find { |x| x.is_a?(::Parsanol::Slice) }
|
|
320
|
+
content = list.map { |x| x.is_a?(::Parsanol::Slice) ? x.content : x.to_s }.join
|
|
321
|
+
return first_slice ? ::Parsanol::Slice.new(first_slice.offset, content, first_slice.input) : content
|
|
406
322
|
end
|
|
407
323
|
|
|
408
|
-
#
|
|
409
|
-
#
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
# {:syntax => {:schemaDecl => [...]}}]
|
|
413
|
-
# Result: {:syntax => {:spaces => {...}, :schemaDecl => [...]}}
|
|
414
|
-
#
|
|
415
|
-
# 2. REPETITION PATTERN: All hashes have the SAME single key, but values are SIMPLE
|
|
416
|
-
# => Keep as array (this is a repetition result)
|
|
417
|
-
# Example: [{:letter => "a"}, {:letter => "b"}, {:letter => "c"}]
|
|
418
|
-
# Result: [{:letter => "a"}, {:letter => "b"}, {:letter => "c"}]
|
|
419
|
-
#
|
|
420
|
-
# 3. MIXED KEYS: Hashes have DIFFERENT keys
|
|
421
|
-
# => Merge into single hash
|
|
422
|
-
# Example: [{:explicitAttr => {...}}, {:whereClause => {...}}]
|
|
423
|
-
# Result: {:explicitAttr => {...}, :whereClause => {...}}
|
|
424
|
-
if hash_count == total_items && hash_count > 1
|
|
425
|
-
# Check if all hashes have the same single key
|
|
426
|
-
first_item = items.first
|
|
427
|
-
if first_item.is_a?(Hash) && first_item.keys.length == 1
|
|
428
|
-
wrapper_key = first_item.keys.first
|
|
429
|
-
|
|
430
|
-
# Verify all items are hashes with the same single key
|
|
431
|
-
all_same_wrapper = items.all? do |item|
|
|
432
|
-
item.is_a?(Hash) && item.keys.length == 1 && item.keys.first == wrapper_key
|
|
433
|
-
end
|
|
324
|
+
# Cold path: parslet's exact fold (Hash/Slice/String/Array
|
|
325
|
+
# mixtures, including the #83 paragraph cases).
|
|
326
|
+
foldl(list) { |acc, item| merge_fold(acc, item) }
|
|
327
|
+
end
|
|
434
328
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
# REPETITION pattern (same keys like entity_decl): keep as array
|
|
445
|
-
# WRAPPER pattern (different keys like spaces vs schemaDecl): merge
|
|
446
|
-
first_inner_keys = items.first[wrapper_key].keys.to_set
|
|
447
|
-
items.all? do |item|
|
|
448
|
-
item[wrapper_key].keys.to_set == first_inner_keys
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
# Check if items have single keys or multiple keys
|
|
452
|
-
# - Single key items with repeated outer key = true repetition (keep array)
|
|
453
|
-
# - Multiple key items with repeated outer key = duplicate labels in sequence (merge)
|
|
454
|
-
max_keys_per_item = items.map do |item|
|
|
455
|
-
item.is_a?(Hash) ? item.keys.length : 0
|
|
456
|
-
end.max || 0
|
|
457
|
-
|
|
458
|
-
# Check if inner values are hashes with different keys
|
|
459
|
-
# This distinguishes:
|
|
460
|
-
# - True repetition: [{letter: 'a'}, {letter: 'b'}] - inner is string
|
|
461
|
-
# - Duplicate labels: [{group: {char: 'a'}}, {group: {digit: '5'}}] - inner is hash with different keys
|
|
462
|
-
inner_keys_all_same = true
|
|
463
|
-
first_inner_keys = nil
|
|
464
|
-
if items.all? do |item|
|
|
465
|
-
item.is_a?(Hash) && item[wrapper_key].is_a?(Hash)
|
|
466
|
-
end
|
|
467
|
-
first_inner_keys = items.first[wrapper_key].keys.to_set
|
|
468
|
-
inner_keys_all_same = items.all? do |item|
|
|
469
|
-
item[wrapper_key].keys.to_set == first_inner_keys
|
|
470
|
-
end
|
|
471
|
-
end
|
|
472
|
-
|
|
473
|
-
# DUPLICATE LABELS IN SEQUENCE: multiple keys per item with repeated outer key
|
|
474
|
-
# OR inner hashes with different keys
|
|
475
|
-
# Example: [{group: {char: 'a'}}, {group: {digit: '5'}}]
|
|
476
|
-
# Ruby semantics: merge with last value wins for the outer key
|
|
477
|
-
# This is different from true repetition where each item has exactly one key
|
|
478
|
-
has_duplicate_labels = max_keys_per_item > 1 || (first_inner_keys && !inner_keys_all_same)
|
|
479
|
-
|
|
480
|
-
# Check if inner hashes have the same keys or different keys
|
|
481
|
-
first_inner_keys ||= items.first[wrapper_key].keys.to_set
|
|
482
|
-
all_same_keys = items.all? do |item|
|
|
483
|
-
item[wrapper_key].keys.to_set == first_inner_keys
|
|
484
|
-
end
|
|
485
|
-
|
|
486
|
-
if has_duplicate_labels
|
|
487
|
-
# DUPLICATE LABELS PATTERN: items have multiple keys with repeated outer key
|
|
488
|
-
# OR inner hashes have different keys
|
|
489
|
-
# This is a SEQUENCE with duplicate .as() labels
|
|
490
|
-
# Ruby semantics: merge and keep last value for the outer key
|
|
491
|
-
merged = {}
|
|
492
|
-
items.each do |item|
|
|
493
|
-
item.each do |k, v|
|
|
494
|
-
merged[k] = v # Last value wins
|
|
495
|
-
end
|
|
496
|
-
end
|
|
497
|
-
# Return only the wrapper key with its last value
|
|
498
|
-
return { wrapper_key => merged[wrapper_key] }
|
|
499
|
-
elsif all_same_keys
|
|
500
|
-
# TRUE REPETITION: each item has exactly one key
|
|
501
|
-
# Keep as array of hashes
|
|
502
|
-
# Example: [{letter: 'a'}, {letter: 'b'}] or [{schemaDecl: ...}, {schemaDecl: ...}]
|
|
503
|
-
return items
|
|
504
|
-
else
|
|
505
|
-
# DIFFERENT INNER KEYS with single keys: Same outer key with different inner keys
|
|
506
|
-
# This is a WRAPPER pattern - keep all items as array
|
|
507
|
-
# Example: [{:syntax => {:entityDecl => ...}}, {:syntax => {:typeDecl => ...}}]
|
|
508
|
-
# Should NOT merge or drop items - keep all declarations
|
|
509
|
-
return items
|
|
510
|
-
end
|
|
511
|
-
|
|
512
|
-
return items unless all_values_are_hashes
|
|
513
|
-
|
|
514
|
-
# Repetition pattern: keep as array
|
|
329
|
+
# Parslet compares exact classes (`left.class == right.class`) and
|
|
330
|
+
# uses `instance_of?` below — not `is_a?` — so Slice subclasses and
|
|
331
|
+
# Hash subclasses do not take the wrong branch.
|
|
332
|
+
def self.merge_fold(left, right)
|
|
333
|
+
# rubocop:disable Style/ClassEqualityComparison
|
|
334
|
+
if left.class == right.class
|
|
335
|
+
# rubocop:enable Style/ClassEqualityComparison
|
|
336
|
+
return left.is_a?(Hash) ? left.merge(right) : left + right
|
|
337
|
+
end
|
|
515
338
|
|
|
516
|
-
|
|
517
|
-
|
|
339
|
+
if left.respond_to?(:to_str) && right.respond_to?(:to_str)
|
|
340
|
+
return right if right.respond_to?(:to_slice)
|
|
341
|
+
return left if left.respond_to?(:to_slice)
|
|
518
342
|
|
|
519
|
-
|
|
520
|
-
# Parslet sequence semantics: merge into single hash
|
|
521
|
-
return merged_hash
|
|
343
|
+
return left.to_str + right.to_str
|
|
522
344
|
end
|
|
523
345
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
# return ONLY the merged hash (discard unnamed Slices/strings)
|
|
527
|
-
return merged_hash unless merged_hash.empty?
|
|
528
|
-
|
|
529
|
-
# No named captures - handle Slices/strings and other items
|
|
530
|
-
if slice_or_string_parts.any?
|
|
531
|
-
# Join Slices/strings, preserving position from first Slice
|
|
532
|
-
first_slice = slice_or_string_parts.find { |i| i.is_a?(::Parsanol::Slice) }
|
|
533
|
-
content = slice_or_string_parts.map do |i|
|
|
534
|
-
i.is_a?(::Parsanol::Slice) ? i.content : i
|
|
535
|
-
end.join
|
|
536
|
-
|
|
537
|
-
if first_slice
|
|
538
|
-
# Create new Slice with combined content, preserving position from first
|
|
539
|
-
return ::Parsanol::Slice.new(first_slice.offset, content,
|
|
540
|
-
first_slice.input)
|
|
541
|
-
else
|
|
542
|
-
# All plain strings (shouldn't happen with new decode_flat, but handle it)
|
|
543
|
-
return slice_or_string_parts.length == 1 ? slice_or_string_parts.first : content
|
|
544
|
-
end
|
|
545
|
-
end
|
|
346
|
+
return left if right.respond_to?(:to_str)
|
|
347
|
+
return right if left.respond_to?(:to_str)
|
|
546
348
|
|
|
547
|
-
|
|
548
|
-
return
|
|
349
|
+
return left + [right] if right.is_a?(Hash)
|
|
350
|
+
return [left] + right if left.is_a?(Hash)
|
|
549
351
|
|
|
550
|
-
|
|
352
|
+
# Fallback: hoist both sides into an array (defensive; parslet
|
|
353
|
+
# raises here, but native trees can carry nested Arrays that
|
|
354
|
+
# parslet would already have folded).
|
|
355
|
+
Array(left) + Array(right)
|
|
551
356
|
end
|
|
552
357
|
|
|
553
|
-
#
|
|
554
|
-
#
|
|
555
|
-
#
|
|
556
|
-
#
|
|
557
|
-
def self.flatten_repetition(items)
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
flat_items = []
|
|
562
|
-
all_slices_or_strings = true
|
|
563
|
-
|
|
564
|
-
items.each do |item|
|
|
565
|
-
if item.is_a?(Array)
|
|
566
|
-
item.each do |sub|
|
|
567
|
-
flat_items << sub
|
|
568
|
-
all_slices_or_strings = false unless slice_or_string?(sub)
|
|
569
|
-
end
|
|
570
|
-
else
|
|
571
|
-
flat_items << item
|
|
572
|
-
all_slices_or_strings = false unless slice_or_string?(item)
|
|
573
|
-
end
|
|
358
|
+
# Exact port of Parslet::Atoms::CanFlatten#flatten_repetition.
|
|
359
|
+
# `named` is true only when this repetition is the direct child of
|
|
360
|
+
# a Named (.as(...)); it controls empty-list folding ([] vs "").
|
|
361
|
+
# `instance_of?` (not `is_a?`/`any?(Hash)`) matches parslet.
|
|
362
|
+
def self.flatten_repetition(items, named: false)
|
|
363
|
+
# rubocop:disable Style/PredicateWithKind
|
|
364
|
+
if items.any? { |e| e.instance_of?(Hash) }
|
|
365
|
+
return items.select { |e| e.instance_of?(Hash) }
|
|
574
366
|
end
|
|
575
367
|
|
|
576
|
-
|
|
368
|
+
if items.any? { |e| e.instance_of?(Array) }
|
|
369
|
+
return items.select { |e| e.instance_of?(Array) }.flatten(1)
|
|
370
|
+
end
|
|
371
|
+
# rubocop:enable Style/PredicateWithKind
|
|
577
372
|
|
|
578
|
-
|
|
579
|
-
if all_slices_or_strings
|
|
580
|
-
first_slice = flat_items.find { |i| i.is_a?(::Parsanol::Slice) }
|
|
581
|
-
content = flat_items.map do |i|
|
|
582
|
-
i.is_a?(::Parsanol::Slice) ? i.content : i
|
|
583
|
-
end.join
|
|
373
|
+
return EMPTY_ARRAY if named && items.empty?
|
|
584
374
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
else
|
|
594
|
-
flat_items
|
|
375
|
+
# Hot path: all-stringlike repetition → foldl via concat.
|
|
376
|
+
# Cold path: parslet's exact fold.
|
|
377
|
+
if items.all? { |x| x.is_a?(::Parsanol::Slice) || x.is_a?(String) }
|
|
378
|
+
return EMPTY_STRING if items.empty?
|
|
379
|
+
|
|
380
|
+
first_slice = items.find { |x| x.is_a?(::Parsanol::Slice) }
|
|
381
|
+
content = items.map { |x| x.is_a?(::Parsanol::Slice) ? x.content : x.to_s }.join
|
|
382
|
+
return first_slice ? ::Parsanol::Slice.new(first_slice.offset, content, first_slice.input) : content
|
|
595
383
|
end
|
|
384
|
+
|
|
385
|
+
foldl(items.compact) { |acc, item| acc + item }
|
|
596
386
|
end
|
|
597
387
|
|
|
598
388
|
# Check if value is a Slice or String
|
data/lib/parsanol/version.rb
CHANGED