@@ -373,7 +373,37 @@ public struct SmallProjectionPath : CustomStringConvertible, CustomReflectable,
373
373
}
374
374
return Self ( . anything)
375
375
}
376
+
377
+ /// Returns true if this path may overlap with `rhs`.
378
+ ///
379
+ /// "Overlapping" means that both paths may project the same field.
380
+ /// For example:
381
+ /// `s0.s1` and `s0.s1` overlap (the paths are identical)
382
+ /// `s0.s1` and `s0.s2` don't overlap
383
+ /// `s0.s1` and `s0` overlap (the second path is a sub-path of the first one)
384
+ /// `s0.v**` and `s0.s1` overlap
385
+ public func mayOverlap( with rhs: SmallProjectionPath ) -> Bool {
386
+ if isEmpty || rhs. isEmpty {
387
+ return true
388
+ }
376
389
390
+ let ( lhsKind, lhsIdx, lhsBits) = top
391
+ let ( rhsKind, rhsIdx, rhsBits) = rhs. top
392
+
393
+ if lhsKind == . anything || rhsKind == . anything {
394
+ return true
395
+ }
396
+ if lhsKind == . anyValueFields || rhsKind == . anyValueFields {
397
+ return popAllValueFields ( ) . mayOverlap ( with: rhs. popAllValueFields ( ) )
398
+ }
399
+ if ( lhsKind == rhsKind && lhsIdx == rhsIdx) ||
400
+ ( lhsKind == . anyClassField && rhsKind. isClassField) ||
401
+ ( lhsKind. isClassField && rhsKind == . anyClassField) {
402
+ return pop ( numBits: lhsBits) . mayOverlap ( with: rhs. pop ( numBits: rhsBits) )
403
+ }
404
+ return false
405
+ }
406
+
377
407
public var customMirror : Mirror { Mirror ( self , children: [ ] ) }
378
408
}
379
409
@@ -505,6 +535,7 @@ extension SmallProjectionPath {
505
535
parsing ( )
506
536
merging ( )
507
537
matching ( )
538
+ overlapping ( )
508
539
predicates ( )
509
540
path2path ( )
510
541
@@ -617,6 +648,38 @@ extension SmallProjectionPath {
617
648
precondition ( result == expect)
618
649
}
619
650
651
+ func overlapping( ) {
652
+ testOverlap ( " s0.s1.s2 " , " s0.s1.s2 " , expect: true )
653
+ testOverlap ( " s0.s1.s2 " , " s0.s2.s2 " , expect: false )
654
+ testOverlap ( " s0.s1.s2 " , " s0.e1.s2 " , expect: false )
655
+ testOverlap ( " s0.s1.s2 " , " s0.s1 " , expect: true )
656
+ testOverlap ( " s0.s1.s2 " , " s1.s2 " , expect: false )
657
+
658
+ testOverlap ( " s0.c*.s2 " , " s0.ct.s2 " , expect: true )
659
+ testOverlap ( " s0.c*.s2 " , " s0.c1.s2 " , expect: true )
660
+ testOverlap ( " s0.c*.s2 " , " s0.c1.c2.s2 " , expect: false )
661
+ testOverlap ( " s0.c*.s2 " , " s0.s2 " , expect: false )
662
+
663
+ testOverlap ( " s0.v**.s2 " , " s0.s3 " , expect: true )
664
+ testOverlap ( " s0.v**.s2.c2 " , " s0.s3.c1 " , expect: false )
665
+ testOverlap ( " s0.v**.s2 " , " s1.s3 " , expect: false )
666
+ testOverlap ( " s0.v**.s2 " , " s0.v**.s3 " , expect: true )
667
+
668
+ testOverlap ( " s0.** " , " s0.s3.c1 " , expect: true )
669
+ testOverlap ( " ** " , " s0.s3.c1 " , expect: true )
670
+ }
671
+
672
+ func testOverlap( _ lhsStr: String , _ rhsStr: String , expect: Bool ) {
673
+ var lhsParser = StringParser ( lhsStr)
674
+ let lhs = try ! lhsParser. parseProjectionPathFromSIL ( )
675
+ var rhsParser = StringParser ( rhsStr)
676
+ let rhs = try ! rhsParser. parseProjectionPathFromSIL ( )
677
+ let result = lhs. mayOverlap ( with: rhs)
678
+ precondition ( result == expect)
679
+ let reversedResult = rhs. mayOverlap ( with: lhs)
680
+ precondition ( reversedResult == expect)
681
+ }
682
+
620
683
func predicates( ) {
621
684
testPredicate ( " v** " , \. hasNoClassProjection, expect: true )
622
685
testPredicate ( " c0 " , \. hasNoClassProjection, expect: false )
0 commit comments