@@ -72,17 +72,22 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
72
72
case tupleField = 0x2 // A concrete tuple element: syntax e.g. `2`
73
73
case enumCase = 0x3 // A concrete enum case (with payload): syntax e.g. `e4'
74
74
case classField = 0x4 // A concrete class field: syntax e.g. `c1`
75
- case tailElements = 0x5 // A tail allocated element of a class : syntax `ct`
76
- case anyValueFields = 0x6 // Any number of any value fields (struct, tuple, enum): syntax `v**`
75
+ case indexedElement = 0x5 // A constant offset into an array of elements : syntax e.g. 'i2'
76
+ // The index must not be 0 and there must not be two successive element indices in the path.
77
77
78
78
// "Large" kinds: starting from here the low 3 bits must be 1.
79
79
// This and all following kinds (we'll add in the future) cannot have a field index.
80
- case anyClassField = 0x7 // Any class field, including tail elements: syntax `c*`
81
- case anything = 0xf // Any number of any fields: syntax `**`
80
+ case tailElements = 0x07 // (0 << 3) | 0x7 A tail allocated element of a class: syntax `ct`
81
+ case existential = 0x0f // (1 << 3) | 0x7 A concrete value projected out of an existential: synatx 'x'
82
+ case anyClassField = 0x17 // (2 << 3) | 0x7 Any class field, including tail elements: syntax `c*`
83
+ case anyIndexedElement = 0x1f // (3 << 3) | 0x7 An unknown offset into an array of elements.
84
+ // There must not be two successive element indices in the path.
85
+ case anyValueFields = 0x27 // (4 << 3) | 0x7 Any number of any value fields (struct, tuple, enum): syntax `v**`
86
+ case anything = 0x2f // (5 << 3) | 0x7 Any number of any fields: syntax `**`
82
87
83
88
public var isValueField : Bool {
84
89
switch self {
85
- case . anyValueFields, . structField, . tupleField, . enumCase:
90
+ case . anyValueFields, . structField, . tupleField, . enumCase, . indexedElement , . anyIndexedElement , . existential :
86
91
return true
87
92
case . root, . anything, . anyClassField, . classField, . tailElements:
88
93
return false
@@ -93,10 +98,19 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
93
98
switch self {
94
99
case . anyClassField, . classField, . tailElements:
95
100
return true
96
- case . root, . anything, . anyValueFields, . structField, . tupleField, . enumCase:
101
+ case . root, . anything, . anyValueFields, . structField, . tupleField, . enumCase, . indexedElement , . anyIndexedElement , . existential :
97
102
return false
98
103
}
99
104
}
105
+
106
+ var isIndexedElement : Bool {
107
+ switch self {
108
+ case . anyIndexedElement, . indexedElement:
109
+ return true
110
+ default :
111
+ return false
112
+ }
113
+ }
100
114
}
101
115
102
116
public init ( ) { self . bytes = 0 }
@@ -121,6 +135,9 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
121
135
case . enumCase: s = " e \( idx) "
122
136
case . classField: s = " c \( idx) "
123
137
case . tailElements: s = " ct "
138
+ case . existential: s = " x "
139
+ case . indexedElement: s = " i \( idx) "
140
+ case . anyIndexedElement: s = " i* "
124
141
case . anything: s = " ** "
125
142
case . anyValueFields: s = " v** "
126
143
case . anyClassField: s = " c* "
@@ -182,6 +199,17 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
182
199
/// For example, pushing `s0` to `c3.e1` returns `s0.c3.e1`.
183
200
public func push( _ kind: FieldKind , index: Int = 0 ) -> SmallProjectionPath {
184
201
assert ( kind != . anything || bytes == 0 , " 'anything' only allowed in last path component " )
202
+ if ( kind. isIndexedElement) {
203
+ if kind == . indexedElement && index == 0 {
204
+ // Ignore zero indices
205
+ return self
206
+ }
207
+ // "Merge" two successive indexed elements
208
+ let ( k, _, numBits) = top
209
+ if ( k. isIndexedElement) {
210
+ return pop ( numBits: numBits) . push ( . anyIndexedElement)
211
+ }
212
+ }
185
213
var idx = index
186
214
var b = bytes
187
215
if ( b >> 56 ) != 0 {
@@ -234,6 +262,11 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
234
262
return pop ( numBits: numBits)
235
263
}
236
264
return nil
265
+ case . anyIndexedElement:
266
+ if kind. isIndexedElement {
267
+ return self
268
+ }
269
+ return pop ( numBits: numBits) . popIfMatches ( kind, index: index)
237
270
case kind:
238
271
if let i = index {
239
272
if i != idx { return nil }
@@ -294,6 +327,15 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
294
327
}
295
328
}
296
329
330
+ public func popIndexedElements( ) -> SmallProjectionPath {
331
+ var p = self
332
+ while true {
333
+ let ( k, _, numBits) = p. top
334
+ if !k. isIndexedElement { return p }
335
+ p = p. pop ( numBits: numBits)
336
+ }
337
+ }
338
+
297
339
/// Pops the last class projection and all following value fields from the tail of the path.
298
340
/// For example:
299
341
/// `s0.e2.3.c4.s1` -> `s0.e2.3`
@@ -344,7 +386,9 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
344
386
let ( kind, _, subPath) = pop ( )
345
387
if !kind. isClassField { return false }
346
388
return subPath. matches ( pattern: subPattern)
347
- case . structField, . tupleField, . enumCase, . classField, . tailElements:
389
+ case . anyIndexedElement:
390
+ return popIndexedElements ( ) . matches ( pattern: subPattern)
391
+ case . structField, . tupleField, . enumCase, . classField, . tailElements, . indexedElement, . existential:
348
392
let ( kind, index, subPath) = pop ( )
349
393
if kind != patternKind || index != patternIdx { return false }
350
394
return subPath. matches ( pattern: subPattern)
@@ -373,6 +417,15 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
373
417
}
374
418
return subPath. push ( lhsKind, index: lhsIdx)
375
419
}
420
+ if lhsKind. isIndexedElement || rhsKind. isIndexedElement {
421
+ let subPath = popIndexedElements ( ) . merge ( with: rhs. popIndexedElements ( ) )
422
+ let subPathTopKind = subPath. top. kind
423
+ assert ( !subPathTopKind. isIndexedElement)
424
+ if subPathTopKind == . anything || subPathTopKind == . anyValueFields {
425
+ return subPath
426
+ }
427
+ return subPath. push ( . anyIndexedElement)
428
+ }
376
429
if lhsKind. isValueField || rhsKind. isValueField {
377
430
let subPath = popAllValueFields ( ) . merge ( with: rhs. popAllValueFields ( ) )
378
431
assert ( !subPath. top. kind. isValueField)
@@ -407,6 +460,9 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
407
460
if lhsKind == . anything || rhsKind == . anything {
408
461
return true
409
462
}
463
+ if lhsKind == . anyIndexedElement || rhsKind == . anyIndexedElement {
464
+ return popIndexedElements ( ) . mayOverlap ( with: rhs. popIndexedElements ( ) )
465
+ }
410
466
if lhsKind == . anyValueFields || rhsKind == . anyValueFields {
411
467
return popAllValueFields ( ) . mayOverlap ( with: rhs. popAllValueFields ( ) )
412
468
}
@@ -417,6 +473,29 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
417
473
}
418
474
return false
419
475
}
476
+
477
+ /// Return true if this path is a sub-path of `rhs` or is equivalent to `rhs`.
478
+ ///
479
+ /// For example:
480
+ /// `s0` is a sub-path of `s0.s1`
481
+ /// `s0` is not a sub-path of `s1`
482
+ /// `s0.s1` is a sub-path of `s0.s1`
483
+ /// `i*.s1` is not a sub-path of `i*.s1` because the actual field is unknown on both sides
484
+ public func isSubPath( of rhs: SmallProjectionPath ) -> Bool {
485
+ let ( lhsKind, lhsIdx, lhsBits) = top
486
+ switch lhsKind {
487
+ case . root:
488
+ return true
489
+ case . classField, . tailElements, . structField, . tupleField, . enumCase, . existential, . indexedElement:
490
+ let ( rhsKind, rhsIdx, rhsBits) = rhs. top
491
+ if lhsKind == rhsKind && lhsIdx == rhsIdx {
492
+ return pop ( numBits: lhsBits) . isSubPath ( of: rhs. pop ( numBits: rhsBits) )
493
+ }
494
+ return false
495
+ case . anything, . anyValueFields, . anyClassField, . anyIndexedElement:
496
+ return false
497
+ }
498
+ }
420
499
}
421
500
422
501
//===----------------------------------------------------------------------===//
@@ -490,8 +569,12 @@ extension StringParser {
490
569
entries. append ( ( . anyClassField, 0 ) )
491
570
} else if consume ( " v** " ) {
492
571
entries. append ( ( . anyValueFields, 0 ) )
572
+ } else if consume ( " i* " ) {
573
+ entries. append ( ( . anyIndexedElement, 0 ) )
493
574
} else if consume ( " ct " ) {
494
575
entries. append ( ( . tailElements, 0 ) )
576
+ } else if consume ( " x " ) {
577
+ entries. append ( ( . existential, 0 ) )
495
578
} else if consume ( " c " ) {
496
579
guard let idx = consumeInt ( withWhiteSpace: false ) else {
497
580
try throwError ( " expected class field index " )
@@ -507,6 +590,11 @@ extension StringParser {
507
590
try throwError ( " expected struct field index " )
508
591
}
509
592
entries. append ( ( . structField, idx) )
593
+ } else if consume ( " i " ) {
594
+ guard let idx = consumeInt ( withWhiteSpace: false ) else {
595
+ try throwError ( " expected index " )
596
+ }
597
+ entries. append ( ( . indexedElement, idx) )
510
598
} else if let tupleElemIdx = consumeInt ( ) {
511
599
entries. append ( ( . tupleField, tupleElemIdx) )
512
600
} else if !consume( " . " ) {
@@ -546,6 +634,7 @@ extension SmallProjectionPath {
546
634
merging ( )
547
635
matching ( )
548
636
overlapping ( )
637
+ subPathTesting ( )
549
638
predicates ( )
550
639
path2path ( )
551
640
@@ -561,6 +650,11 @@ extension SmallProjectionPath {
561
650
assert ( k4 == . enumCase && i4 == 876 )
562
651
let p5 = SmallProjectionPath ( . anything)
563
652
assert ( p5. pop ( ) . path. isEmpty)
653
+ let p6 = SmallProjectionPath ( . indexedElement, index: 1 ) . push ( . indexedElement, index: 2 )
654
+ let ( k6, i6, p7) = p6. pop ( )
655
+ assert ( k6 == . anyIndexedElement && i6 == 0 && p7. isEmpty)
656
+ let p8 = SmallProjectionPath ( . indexedElement, index: 0 )
657
+ assert ( p8. isEmpty)
564
658
}
565
659
566
660
func parsing( ) {
@@ -575,7 +669,10 @@ extension SmallProjectionPath {
575
669
. push ( . enumCase, index: 6 )
576
670
. push ( . anyClassField)
577
671
. push ( . tupleField, index: 2 ) )
578
-
672
+ testParse ( " i3.x.i* " , expect: SmallProjectionPath ( . anyIndexedElement)
673
+ . push ( . existential)
674
+ . push ( . indexedElement, index: 3 ) )
675
+
579
676
do {
580
677
var parser = StringParser ( " c*.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.s123.s3.** " )
581
678
_ = try parser. parseProjectionPathFromSIL ( )
@@ -606,6 +703,10 @@ extension SmallProjectionPath {
606
703
testMerge ( " s1.s1.c2 " , " s1.c2 " , expect: " s1.v**.c2 " )
607
704
testMerge ( " s1.s0 " , " s2.s0 " , expect: " v** " )
608
705
testMerge ( " ct " , " c2 " , expect: " c* " )
706
+ testMerge ( " i1 " , " i2 " , expect: " i* " )
707
+ testMerge ( " i* " , " i2 " , expect: " i* " )
708
+ testMerge ( " s0.i*.e3 " , " s0.e3 " , expect: " s0.i*.e3 " )
709
+ testMerge ( " i* " , " v** " , expect: " v** " )
609
710
610
711
testMerge ( " ct.s0.e0.v**.c0 " , " ct.s0.e0.v**.c0 " , expect: " ct.s0.e0.v**.c0 " )
611
712
testMerge ( " ct.s0.s0.c0 " , " ct.s0.e0.s0.c0 " , expect: " ct.s0.v**.c0 " )
@@ -635,18 +736,22 @@ extension SmallProjectionPath {
635
736
testMatch ( " c* " , " c1 " , expect: false )
636
737
testMatch ( " c* " , " ct " , expect: false )
637
738
testMatch ( " v** " , " s0 " , expect: false )
739
+ testMatch ( " i1 " , " i1 " , expect: true )
740
+ testMatch ( " i1 " , " i* " , expect: true )
741
+ testMatch ( " i* " , " i1 " , expect: false )
638
742
639
743
testMatch ( " s0.s1 " , " s0.s1 " , expect: true )
640
744
testMatch ( " s0.s2 " , " s0.s1 " , expect: false )
641
745
testMatch ( " s0 " , " s0.v** " , expect: true )
642
746
testMatch ( " s0.s1 " , " s0.v** " , expect: true )
643
747
testMatch ( " s0.1.e2 " , " s0.v** " , expect: true )
644
- testMatch ( " s0.v**.e2 " , " v** " , expect: true )
748
+ testMatch ( " s0.v**.x. e2 " , " v** " , expect: true )
645
749
testMatch ( " s0.v** " , " s0.s1 " , expect: false )
646
750
testMatch ( " s0.s1.c* " , " s0.v** " , expect: false )
647
751
testMatch ( " s0.v** " , " s0.** " , expect: true )
648
752
testMatch ( " s1.v** " , " s0.** " , expect: false )
649
753
testMatch ( " s0.** " , " s0.v** " , expect: false )
754
+ testMatch ( " s0.s1 " , " s0.i*.s1 " , expect: true )
650
755
}
651
756
652
757
func testMatch( _ lhsStr: String , _ rhsStr: String , expect: Bool ) {
@@ -670,13 +775,17 @@ extension SmallProjectionPath {
670
775
testOverlap ( " s0.c*.s2 " , " s0.c1.c2.s2 " , expect: false )
671
776
testOverlap ( " s0.c*.s2 " , " s0.s2 " , expect: false )
672
777
673
- testOverlap ( " s0.v**.s2 " , " s0.s3 " , expect: true )
778
+ testOverlap ( " s0.v**.s2 " , " s0.s3.x " , expect: true )
674
779
testOverlap ( " s0.v**.s2.c2 " , " s0.s3.c1 " , expect: false )
675
780
testOverlap ( " s0.v**.s2 " , " s1.s3 " , expect: false )
676
781
testOverlap ( " s0.v**.s2 " , " s0.v**.s3 " , expect: true )
677
782
678
783
testOverlap ( " s0.** " , " s0.s3.c1 " , expect: true )
679
784
testOverlap ( " ** " , " s0.s3.c1 " , expect: true )
785
+
786
+ testOverlap ( " i1 " , " i* " , expect: true )
787
+ testOverlap ( " i1 " , " v** " , expect: true )
788
+ testOverlap ( " s0.i*.s1 " , " s0.s1 " , expect: true )
680
789
}
681
790
682
791
func testOverlap( _ lhsStr: String , _ rhsStr: String , expect: Bool ) {
@@ -690,6 +799,27 @@ extension SmallProjectionPath {
690
799
assert ( reversedResult == expect)
691
800
}
692
801
802
+ func subPathTesting( ) {
803
+ testSubPath ( " s0 " , " s0.s1 " , expect: true )
804
+ testSubPath ( " s0 " , " s1 " , expect: false )
805
+ testSubPath ( " s0.s1 " , " s0.s1 " , expect: true )
806
+ testSubPath ( " i*.s1 " , " i*.s1 " , expect: false )
807
+ testSubPath ( " ct.s1.0.i3.x " , " ct.s1.0.i3.x " , expect: true )
808
+ testSubPath ( " c0.s1.0.i3 " , " c0.s1.0.i3.x " , expect: true )
809
+ testSubPath ( " s1.0.i3.x " , " s1.0.i3 " , expect: false )
810
+ testSubPath ( " v**.s1 " , " v**.s1 " , expect: false )
811
+ testSubPath ( " i* " , " i* " , expect: false )
812
+ }
813
+
814
+ func testSubPath( _ lhsStr: String , _ rhsStr: String , expect: Bool ) {
815
+ var lhsParser = StringParser ( lhsStr)
816
+ let lhs = try ! lhsParser. parseProjectionPathFromSIL ( )
817
+ var rhsParser = StringParser ( rhsStr)
818
+ let rhs = try ! rhsParser. parseProjectionPathFromSIL ( )
819
+ let result = lhs. isSubPath ( of: rhs)
820
+ assert ( result == expect)
821
+ }
822
+
693
823
func predicates( ) {
694
824
testPredicate ( " v** " , \. hasClassProjection, expect: false )
695
825
testPredicate ( " v**.c0.s1.v** " , \. hasClassProjection, expect: true )
@@ -737,6 +867,10 @@ extension SmallProjectionPath {
737
867
testPath2Path ( " c0.s3 " , { $0. popIfMatches ( . anyClassField) } , expect: nil )
738
868
testPath2Path ( " ** " , { $0. popIfMatches ( . anyClassField) } , expect: " ** " )
739
869
testPath2Path ( " c*.e3 " , { $0. popIfMatches ( . anyClassField) } , expect: " e3 " )
870
+
871
+ testPath2Path ( " i*.e3.s0 " , { $0. popIfMatches ( . enumCase, index: 3 ) } , expect: " s0 " )
872
+ testPath2Path ( " i1.e3.s0 " , { $0. popIfMatches ( . enumCase, index: 3 ) } , expect: nil )
873
+ testPath2Path ( " i*.e3.s0 " , { $0. popIfMatches ( . indexedElement, index: 0 ) } , expect: " i*.e3.s0 " )
740
874
}
741
875
742
876
func testPath2Path( _ pathStr: String , _ transform: ( SmallProjectionPath ) -> SmallProjectionPath ? , expect: String ? ) {
0 commit comments