Skip to content

Commit ed842cb

Browse files
authored
Merge pull request #535 from hamishknight/one-more
2 parents b7bfd16 + 08e12b9 commit ed842cb

File tree

4 files changed

+164
-43
lines changed

4 files changed

+164
-43
lines changed

Sources/RegexBuilder/Variadics.swift

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,7 @@ extension Repeat {
784784
_ component: Component,
785785
count: Int
786786
) where RegexOutput == Substring {
787-
assert(count > 0, "Must specify a positive count")
787+
precondition(count >= 0, "Must specify a positive count")
788788
let factory = makeFactory()
789789
self.init(factory.exactly(count, component))
790790
}
@@ -795,7 +795,7 @@ extension Repeat {
795795
count: Int,
796796
@RegexComponentBuilder _ component: () -> Component
797797
) where RegexOutput == Substring {
798-
assert(count > 0, "Must specify a positive count")
798+
precondition(count >= 0, "Must specify a positive count")
799799
let factory = makeFactory()
800800
self.init(factory.exactly(count, component()))
801801
}
@@ -913,7 +913,7 @@ extension Repeat {
913913
_ component: Component,
914914
count: Int
915915
) where RegexOutput == (Substring, C1?), Component.RegexOutput == (W, C1) {
916-
assert(count > 0, "Must specify a positive count")
916+
precondition(count >= 0, "Must specify a positive count")
917917
let factory = makeFactory()
918918
self.init(factory.exactly(count, component))
919919
}
@@ -923,7 +923,7 @@ extension Repeat {
923923
count: Int,
924924
@RegexComponentBuilder _ component: () -> Component
925925
) where RegexOutput == (Substring, C1?), Component.RegexOutput == (W, C1) {
926-
assert(count > 0, "Must specify a positive count")
926+
precondition(count >= 0, "Must specify a positive count")
927927
let factory = makeFactory()
928928
self.init(factory.exactly(count, component()))
929929
}
@@ -1039,7 +1039,7 @@ extension Repeat {
10391039
_ component: Component,
10401040
count: Int
10411041
) where RegexOutput == (Substring, C1?, C2?), Component.RegexOutput == (W, C1, C2) {
1042-
assert(count > 0, "Must specify a positive count")
1042+
precondition(count >= 0, "Must specify a positive count")
10431043
let factory = makeFactory()
10441044
self.init(factory.exactly(count, component))
10451045
}
@@ -1049,7 +1049,7 @@ extension Repeat {
10491049
count: Int,
10501050
@RegexComponentBuilder _ component: () -> Component
10511051
) where RegexOutput == (Substring, C1?, C2?), Component.RegexOutput == (W, C1, C2) {
1052-
assert(count > 0, "Must specify a positive count")
1052+
precondition(count >= 0, "Must specify a positive count")
10531053
let factory = makeFactory()
10541054
self.init(factory.exactly(count, component()))
10551055
}
@@ -1165,7 +1165,7 @@ extension Repeat {
11651165
_ component: Component,
11661166
count: Int
11671167
) where RegexOutput == (Substring, C1?, C2?, C3?), Component.RegexOutput == (W, C1, C2, C3) {
1168-
assert(count > 0, "Must specify a positive count")
1168+
precondition(count >= 0, "Must specify a positive count")
11691169
let factory = makeFactory()
11701170
self.init(factory.exactly(count, component))
11711171
}
@@ -1175,7 +1175,7 @@ extension Repeat {
11751175
count: Int,
11761176
@RegexComponentBuilder _ component: () -> Component
11771177
) where RegexOutput == (Substring, C1?, C2?, C3?), Component.RegexOutput == (W, C1, C2, C3) {
1178-
assert(count > 0, "Must specify a positive count")
1178+
precondition(count >= 0, "Must specify a positive count")
11791179
let factory = makeFactory()
11801180
self.init(factory.exactly(count, component()))
11811181
}
@@ -1291,7 +1291,7 @@ extension Repeat {
12911291
_ component: Component,
12921292
count: Int
12931293
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?), Component.RegexOutput == (W, C1, C2, C3, C4) {
1294-
assert(count > 0, "Must specify a positive count")
1294+
precondition(count >= 0, "Must specify a positive count")
12951295
let factory = makeFactory()
12961296
self.init(factory.exactly(count, component))
12971297
}
@@ -1301,7 +1301,7 @@ extension Repeat {
13011301
count: Int,
13021302
@RegexComponentBuilder _ component: () -> Component
13031303
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?), Component.RegexOutput == (W, C1, C2, C3, C4) {
1304-
assert(count > 0, "Must specify a positive count")
1304+
precondition(count >= 0, "Must specify a positive count")
13051305
let factory = makeFactory()
13061306
self.init(factory.exactly(count, component()))
13071307
}
@@ -1417,7 +1417,7 @@ extension Repeat {
14171417
_ component: Component,
14181418
count: Int
14191419
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?, C5?), Component.RegexOutput == (W, C1, C2, C3, C4, C5) {
1420-
assert(count > 0, "Must specify a positive count")
1420+
precondition(count >= 0, "Must specify a positive count")
14211421
let factory = makeFactory()
14221422
self.init(factory.exactly(count, component))
14231423
}
@@ -1427,7 +1427,7 @@ extension Repeat {
14271427
count: Int,
14281428
@RegexComponentBuilder _ component: () -> Component
14291429
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?, C5?), Component.RegexOutput == (W, C1, C2, C3, C4, C5) {
1430-
assert(count > 0, "Must specify a positive count")
1430+
precondition(count >= 0, "Must specify a positive count")
14311431
let factory = makeFactory()
14321432
self.init(factory.exactly(count, component()))
14331433
}
@@ -1543,7 +1543,7 @@ extension Repeat {
15431543
_ component: Component,
15441544
count: Int
15451545
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?, C5?, C6?), Component.RegexOutput == (W, C1, C2, C3, C4, C5, C6) {
1546-
assert(count > 0, "Must specify a positive count")
1546+
precondition(count >= 0, "Must specify a positive count")
15471547
let factory = makeFactory()
15481548
self.init(factory.exactly(count, component))
15491549
}
@@ -1553,7 +1553,7 @@ extension Repeat {
15531553
count: Int,
15541554
@RegexComponentBuilder _ component: () -> Component
15551555
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?, C5?, C6?), Component.RegexOutput == (W, C1, C2, C3, C4, C5, C6) {
1556-
assert(count > 0, "Must specify a positive count")
1556+
precondition(count >= 0, "Must specify a positive count")
15571557
let factory = makeFactory()
15581558
self.init(factory.exactly(count, component()))
15591559
}
@@ -1669,7 +1669,7 @@ extension Repeat {
16691669
_ component: Component,
16701670
count: Int
16711671
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?, C5?, C6?, C7?), Component.RegexOutput == (W, C1, C2, C3, C4, C5, C6, C7) {
1672-
assert(count > 0, "Must specify a positive count")
1672+
precondition(count >= 0, "Must specify a positive count")
16731673
let factory = makeFactory()
16741674
self.init(factory.exactly(count, component))
16751675
}
@@ -1679,7 +1679,7 @@ extension Repeat {
16791679
count: Int,
16801680
@RegexComponentBuilder _ component: () -> Component
16811681
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?, C5?, C6?, C7?), Component.RegexOutput == (W, C1, C2, C3, C4, C5, C6, C7) {
1682-
assert(count > 0, "Must specify a positive count")
1682+
precondition(count >= 0, "Must specify a positive count")
16831683
let factory = makeFactory()
16841684
self.init(factory.exactly(count, component()))
16851685
}
@@ -1795,7 +1795,7 @@ extension Repeat {
17951795
_ component: Component,
17961796
count: Int
17971797
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?, C5?, C6?, C7?, C8?), Component.RegexOutput == (W, C1, C2, C3, C4, C5, C6, C7, C8) {
1798-
assert(count > 0, "Must specify a positive count")
1798+
precondition(count >= 0, "Must specify a positive count")
17991799
let factory = makeFactory()
18001800
self.init(factory.exactly(count, component))
18011801
}
@@ -1805,7 +1805,7 @@ extension Repeat {
18051805
count: Int,
18061806
@RegexComponentBuilder _ component: () -> Component
18071807
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?, C5?, C6?, C7?, C8?), Component.RegexOutput == (W, C1, C2, C3, C4, C5, C6, C7, C8) {
1808-
assert(count > 0, "Must specify a positive count")
1808+
precondition(count >= 0, "Must specify a positive count")
18091809
let factory = makeFactory()
18101810
self.init(factory.exactly(count, component()))
18111811
}
@@ -1921,7 +1921,7 @@ extension Repeat {
19211921
_ component: Component,
19221922
count: Int
19231923
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?, C5?, C6?, C7?, C8?, C9?), Component.RegexOutput == (W, C1, C2, C3, C4, C5, C6, C7, C8, C9) {
1924-
assert(count > 0, "Must specify a positive count")
1924+
precondition(count >= 0, "Must specify a positive count")
19251925
let factory = makeFactory()
19261926
self.init(factory.exactly(count, component))
19271927
}
@@ -1931,7 +1931,7 @@ extension Repeat {
19311931
count: Int,
19321932
@RegexComponentBuilder _ component: () -> Component
19331933
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?, C5?, C6?, C7?, C8?, C9?), Component.RegexOutput == (W, C1, C2, C3, C4, C5, C6, C7, C8, C9) {
1934-
assert(count > 0, "Must specify a positive count")
1934+
precondition(count >= 0, "Must specify a positive count")
19351935
let factory = makeFactory()
19361936
self.init(factory.exactly(count, component()))
19371937
}
@@ -2047,7 +2047,7 @@ extension Repeat {
20472047
_ component: Component,
20482048
count: Int
20492049
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?, C5?, C6?, C7?, C8?, C9?, C10?), Component.RegexOutput == (W, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10) {
2050-
assert(count > 0, "Must specify a positive count")
2050+
precondition(count >= 0, "Must specify a positive count")
20512051
let factory = makeFactory()
20522052
self.init(factory.exactly(count, component))
20532053
}
@@ -2057,7 +2057,7 @@ extension Repeat {
20572057
count: Int,
20582058
@RegexComponentBuilder _ component: () -> Component
20592059
) where RegexOutput == (Substring, C1?, C2?, C3?, C4?, C5?, C6?, C7?, C8?, C9?, C10?), Component.RegexOutput == (W, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10) {
2060-
assert(count > 0, "Must specify a positive count")
2060+
precondition(count >= 0, "Must specify a positive count")
20612061
let factory = makeFactory()
20622062
self.init(factory.exactly(count, component()))
20632063
}

Sources/VariadicsGenerator/VariadicsGenerator.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ struct VariadicsGenerator: ParsableCommand {
505505
_ component: Component,
506506
count: Int
507507
) \(params.whereClauseForInit) {
508-
assert(count > 0, "Must specify a positive count")
508+
precondition(count >= 0, "Must specify a positive count")
509509
let factory = makeFactory()
510510
self.init(factory.exactly(count, component))
511511
}
@@ -516,7 +516,7 @@ struct VariadicsGenerator: ParsableCommand {
516516
count: Int,
517517
@\(concatBuilderName) _ component: () -> Component
518518
) \(params.whereClauseForInit) {
519-
assert(count > 0, "Must specify a positive count")
519+
precondition(count >= 0, "Must specify a positive count")
520520
let factory = makeFactory()
521521
self.init(factory.exactly(count, component()))
522522
}

Sources/_StringProcessing/Regex/DSLTree.swift

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -853,25 +853,38 @@ extension DSLTree.Node {
853853
_ node: DSLTree.Node
854854
) -> DSLTree.Node {
855855
// TODO: Throw these as errors
856-
assert(range.lowerBound >= 0, "Cannot specify a negative lower bound")
857-
assert(!range.isEmpty, "Cannot specify an empty range")
858-
859-
let kind: DSLTree.QuantificationKind = behavior.map { .explicit($0.dslTreeKind) } ?? .default
860-
861-
switch (range.lowerBound, range.upperBound) {
862-
case (0, Int.max): // 0...
863-
return .quantification(.zeroOrMore, kind, node)
864-
case (1, Int.max): // 1...
865-
return .quantification(.oneOrMore, kind, node)
866-
case _ where range.count == 1: // ..<1 or ...0 or any range with count == 1
856+
precondition(range.lowerBound >= 0, "Cannot specify a negative lower bound")
857+
precondition(!range.isEmpty, "Cannot specify an empty range")
858+
859+
let kind: DSLTree.QuantificationKind = behavior
860+
.map { .explicit($0.dslTreeKind) } ?? .default
861+
862+
// The upper bound needs adjusting down as
863+
// `.quantification` expects a closed range.
864+
let lower = range.lowerBound
865+
let upperInclusive = range.upperBound - 1
866+
867+
// Unbounded cases
868+
if range.upperBound == Int.max {
869+
switch lower {
870+
case 0: // 0...
871+
return .quantification(.zeroOrMore, kind, node)
872+
case 1: // 1...
873+
return .quantification(.oneOrMore, kind, node)
874+
default: // n...
875+
return .quantification(.nOrMore(lower), kind, node)
876+
}
877+
}
878+
if range.count == 1 {
879+
// ..<1 or ...0 or any range with count == 1
867880
// Note: `behavior` is ignored in this case
868-
return .quantification(.exactly(range.lowerBound), .default, node)
869-
case (0, _): // 0..<n or 0...n or ..<n or ...n
870-
return .quantification(.upToN(range.upperBound), kind, node)
871-
case (_, Int.max): // n...
872-
return .quantification(.nOrMore(range.lowerBound), kind, node)
873-
default: // any other range
874-
return .quantification(.range(range.lowerBound, range.upperBound), kind, node)
881+
return .quantification(.exactly(lower), .default, node)
882+
}
883+
switch lower {
884+
case 0: // 0..<n or 0...n or ..<n or ...n
885+
return .quantification(.upToN(upperInclusive), kind, node)
886+
default:
887+
return .quantification(.range(lower, upperInclusive), kind, node)
875888
}
876889
}
877890
}

Tests/RegexBuilderTests/RegexDSLTests.swift

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,9 +450,13 @@ class RegexDSLTests: XCTestCase {
450450

451451
try _testDSLCaptures(
452452
("aaabbbcccdddeeefff", "aaabbbcccdddeeefff"),
453+
("aaabbbcccccdddeeefff", "aaabbbcccccdddeeefff"),
454+
("aaabbbcccddddeeefff", "aaabbbcccddddeeefff"),
455+
("aaabbbccccccdddeeefff", nil),
453456
("aaaabbbcccdddeeefff", nil),
454457
("aaacccdddeeefff", nil),
455458
("aaabbbcccccccdddeeefff", nil),
459+
("aaabbbcccdddddeeefff", nil),
456460
("aaabbbcccddddddeeefff", nil),
457461
("aaabbbcccdddefff", nil),
458462
("aaabbbcccdddeee", "aaabbbcccdddeee"),
@@ -465,7 +469,111 @@ class RegexDSLTests: XCTestCase {
465469
Repeat(2...) { "e" }
466470
Repeat(0...) { "f" }
467471
}
468-
472+
473+
try _testDSLCaptures(
474+
("", nil),
475+
("a", nil),
476+
("aa", "aa"),
477+
("aaa", "aaa"),
478+
matchType: Substring.self, ==)
479+
{
480+
Repeat(2...) { "a" }
481+
}
482+
483+
try _testDSLCaptures(
484+
("", ""),
485+
("a", "a"),
486+
("aa", "aa"),
487+
("aaa", nil),
488+
matchType: Substring.self, ==)
489+
{
490+
Repeat(...2) { "a" }
491+
}
492+
493+
try _testDSLCaptures(
494+
("", ""),
495+
("a", "a"),
496+
("aa", nil),
497+
("aaa", nil),
498+
matchType: Substring.self, ==)
499+
{
500+
Repeat(..<2) { "a" }
501+
}
502+
503+
try _testDSLCaptures(
504+
("", ""),
505+
("a", nil),
506+
("aa", nil),
507+
matchType: Substring.self, ==)
508+
{
509+
Repeat(...0) { "a" }
510+
}
511+
512+
try _testDSLCaptures(
513+
("", ""),
514+
("a", nil),
515+
("aa", nil),
516+
matchType: Substring.self, ==)
517+
{
518+
Repeat(0 ... 0) { "a" }
519+
}
520+
521+
try _testDSLCaptures(
522+
("", ""),
523+
("a", nil),
524+
("aa", nil),
525+
matchType: Substring.self, ==)
526+
{
527+
Repeat(count: 0) { "a" }
528+
}
529+
530+
try _testDSLCaptures(
531+
("", ""),
532+
("a", "a"),
533+
("aa", nil),
534+
matchType: Substring.self, ==)
535+
{
536+
Repeat(0 ... 1) { "a" }
537+
}
538+
539+
try _testDSLCaptures(
540+
("", nil),
541+
("a", "a"),
542+
("aa", "aa"),
543+
("aaa", nil),
544+
matchType: Substring.self, ==)
545+
{
546+
Repeat(1 ... 2) { "a" }
547+
}
548+
549+
try _testDSLCaptures(
550+
("", ""),
551+
("a", nil),
552+
("aa", nil),
553+
matchType: Substring.self, ==)
554+
{
555+
Repeat(0 ..< 1) { "a" }
556+
}
557+
558+
try _testDSLCaptures(
559+
("", ""),
560+
("a", "a"),
561+
("aa", nil),
562+
matchType: Substring.self, ==)
563+
{
564+
Repeat(0 ..< 2) { "a" }
565+
}
566+
567+
try _testDSLCaptures(
568+
("", nil),
569+
("a", "a"),
570+
("aa", "aa"),
571+
("aaa", nil),
572+
matchType: Substring.self, ==)
573+
{
574+
Repeat(1 ..< 3) { "a" }
575+
}
576+
469577
let octoDecimalRegex: Regex<(Substring, Int?)> = Regex {
470578
let charClass = CharacterClass(.digit, "a"..."h")//.ignoringCase()
471579
Capture {

0 commit comments

Comments
 (0)