Skip to content

Commit b14a85b

Browse files
committed
Change quantifiers in the DSL from structure types to top-level functions. This allows for a lot more flexibility with overloading a quantifier based on the input Match type. The immediate benefit of this is getting rid of void and nested void types (see example below), and as a result eliminate the need for void-filtering within concatenation. A more important benefit is being able to get rid of nominal tuples and switch back to Swift tuples, as Swift tuples enable strongly typed named captures and eliminates the complexity that comes with nominal tuples.
----- Before: ```swift let r0 = OneOrMore(.digit) // => `.Match == Tuple2<Substring, [()]>` let r1 = Optionally(.digit) // => `.Match == Tuple2<Substring, ()?>` let r2 = OneOrMore(Repeat(Optionally(.digit))) // => `.Match == Tuple2<Substring, [[()?]]>` "123".match(r2) // => `RegexMatch<Tuple2<Substring, [[()?]]>>?` ``` After: ```swift let r0 = oneOrMore(.digit) // => `.Match == Substring` let r1 = optionally(.digit) // => `.Match == Substring` let r2 = oneOrMore(many(optionally(.digit))) // => `.Match == Substring` "123".match(r2) // => `RegexMatch<Substring>` ``` ----- Before: ```swift /(?<number>\d+)/ // => `Regex<Tuple2<Substring, Substring>>` ``` After: ```swift /(?<number>\d+)/ // => `Regex<(Substring, number: Substring)>` ```
1 parent c9c9028 commit b14a85b

File tree

8 files changed

+1201
-152
lines changed

8 files changed

+1201
-152
lines changed

Sources/Exercises/Participants/RegexParticipant.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,16 @@ private func graphemeBreakPropertyData(
8080
forLine line: String
8181
) -> GraphemeBreakEntry? {
8282
line.match {
83-
OneOrMore(.hexDigit).tryCapture(Unicode.Scalar.init(hex:))
84-
Optionally {
83+
oneOrMore(.hexDigit).tryCapture(Unicode.Scalar.init(hex:))
84+
optionally {
8585
".."
86-
OneOrMore(.hexDigit).tryCapture(Unicode.Scalar.init(hex:))
86+
oneOrMore(.hexDigit).tryCapture(Unicode.Scalar.init(hex:))
8787
}
88-
OneOrMore(.whitespace)
88+
oneOrMore(.whitespace)
8989
";"
90-
OneOrMore(.whitespace)
91-
OneOrMore(.word).tryCapture(Unicode.GraphemeBreakProperty.init)
92-
Repeat(.any)
90+
oneOrMore(.whitespace)
91+
oneOrMore(.word).tryCapture(Unicode.GraphemeBreakProperty.init)
92+
many(.any)
9393
}.map {
9494
let (_, lower, upper, property) = $0.match.tuple
9595
return GraphemeBreakEntry(lower...(upper ?? lower), property)

Sources/VariadicsGenerator/VariadicsGenerator.swift

Lines changed: 122 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ struct StandardErrorStream: TextOutputStream {
8989
var standardError = StandardErrorStream()
9090

9191
typealias Counter = Int64
92-
let patternProtocolName = "RegexProtocol"
92+
let regexProtocolName = "RegexProtocol"
9393
let concatenationStructTypeBaseName = "Concatenate"
9494
let capturingGroupTypeBaseName = "CapturingGroup"
9595
let matchAssociatedTypeName = "Match"
@@ -132,9 +132,10 @@ struct VariadicsGenerator: ParsableCommand {
132132
emitTupleStruct(arity: arity)
133133
}
134134

135+
print("Generating concatenation overloads...", to: &standardError)
135136
for (leftArity, rightArity) in Permutations(totalArity: maxArity) {
136137
print(
137-
"Left arity: \(leftArity) Right arity: \(rightArity)",
138+
" Left arity: \(leftArity) Right arity: \(rightArity)",
138139
to: &standardError)
139140
emitConcatenation(leftArity: leftArity, rightArity: rightArity)
140141
}
@@ -144,7 +145,20 @@ struct VariadicsGenerator: ParsableCommand {
144145
}
145146

146147
output("\n\n")
147-
output("// END AUTO-GENERATED CONTENT")
148+
149+
print("Generating quantifiers...", to: &standardError)
150+
for arity in 0..<maxArity {
151+
print(" Arity \(arity): ", terminator: "", to: &standardError)
152+
for kind in QuantifierKind.allCases {
153+
print("\(kind.rawValue) ", terminator: "", to: &standardError)
154+
emitQuantifier(kind: kind, arity: arity)
155+
}
156+
print(to: &standardError)
157+
}
158+
159+
output("\n\n")
160+
161+
output("// END AUTO-GENERATED CONTENT\n")
148162

149163
print("Done!", to: &standardError)
150164
}
@@ -231,7 +245,7 @@ struct VariadicsGenerator: ParsableCommand {
231245
}
232246
output(", ")
233247
if withConstraints {
234-
output("R0: \(patternProtocolName), R1: \(patternProtocolName)")
248+
output("R0: \(regexProtocolName), R1: \(regexProtocolName)")
235249
} else {
236250
output("R0, R1")
237251
}
@@ -268,7 +282,7 @@ struct VariadicsGenerator: ParsableCommand {
268282
let typeName = "\(concatenationStructTypeBaseName)_\(leftArity)_\(rightArity)"
269283
output("public struct \(typeName)<\n ")
270284
emitGenericParameters(withConstraints: true)
271-
output("\n>: \(patternProtocolName)")
285+
output("\n>: \(regexProtocolName)")
272286
output(" where ")
273287
output("R0.Match == ")
274288
if leftArity == 0 {
@@ -343,7 +357,7 @@ struct VariadicsGenerator: ParsableCommand {
343357
", C\($0)"
344358
}
345359
output("""
346-
, R0: \(patternProtocolName), R1: \(patternProtocolName)>(
360+
, R0: \(regexProtocolName), R1: \(regexProtocolName)>(
347361
combining next: R1, into combined: R0
348362
) -> Regex<
349363
""")
@@ -374,4 +388,106 @@ struct VariadicsGenerator: ParsableCommand {
374388
375389
""")
376390
}
391+
392+
enum QuantifierKind: String, CaseIterable {
393+
case zeroOrOne = "optionally"
394+
case zeroOrMore = "many"
395+
case oneOrMore = "oneOrMore"
396+
397+
var typeName: String {
398+
switch self {
399+
case .zeroOrOne: return "_ZeroOrOne"
400+
case .zeroOrMore: return "_ZeroOrMore"
401+
case .oneOrMore: return "_OneOrMore"
402+
}
403+
}
404+
405+
var operatorName: String {
406+
switch self {
407+
case .zeroOrOne: return ".?"
408+
case .zeroOrMore: return ".+"
409+
case .oneOrMore: return ".*"
410+
}
411+
}
412+
413+
var astQuantifierAmount: String {
414+
switch self {
415+
case .zeroOrOne: return "zeroOrOne"
416+
case .zeroOrMore: return "zeroOrMore"
417+
case .oneOrMore: return "oneOrMore"
418+
}
419+
}
420+
}
421+
422+
func emitQuantifier(kind: QuantifierKind, arity: Int) {
423+
assert(arity >= 0)
424+
func genericParameters(withConstraints: Bool) -> String {
425+
var result = ""
426+
if arity > 0 {
427+
result += "W"
428+
result += (0..<arity).map { ", C\($0)" }.joined()
429+
result += ", "
430+
}
431+
result += "Component"
432+
if withConstraints {
433+
result += ": \(regexProtocolName)"
434+
}
435+
return result
436+
}
437+
let captures = (0..<arity).map { "C\($0)" }.joined(separator: ", ")
438+
let capturesTupled = arity == 1 ? captures : "Tuple\(arity)<\(captures)>"
439+
let componentConstraint: String = arity == 0 ? "" :
440+
"where Component.Match == Tuple\(arity+1)<W, \(captures)>"
441+
let quantifiedCaptures: String = {
442+
switch kind {
443+
case .zeroOrOne:
444+
return "\(capturesTupled)?"
445+
case .zeroOrMore, .oneOrMore:
446+
return "[\(capturesTupled)]"
447+
}
448+
}()
449+
let matchType = arity == 0 ? baseMatchTypeName : "Tuple2<\(baseMatchTypeName), \(quantifiedCaptures)>"
450+
output("""
451+
public struct \(kind.typeName)_\(arity)<\(genericParameters(withConstraints: true))>: \(regexProtocolName) \(componentConstraint) {
452+
public typealias \(matchAssociatedTypeName) = \(matchType)
453+
public let regex: Regex<\(matchAssociatedTypeName)>
454+
public init(component: Component) {
455+
self.regex = .init(ast: \(kind.astQuantifierAmount)(.eager, component.regex.ast.root))
456+
}
457+
}
458+
459+
\(arity == 0 ? "@_disfavoredOverload" : "")
460+
public func \(kind.rawValue)<\(genericParameters(withConstraints: true))>(
461+
_ component: Component
462+
) -> \(kind.typeName)_\(arity)<\(genericParameters(withConstraints: false))> {
463+
.init(component: component)
464+
}
465+
466+
\(arity == 0 ? "@_disfavoredOverload" : "")
467+
public func \(kind.rawValue)<\(genericParameters(withConstraints: true))>(
468+
@RegexBuilder _ component: () -> Component
469+
) -> \(kind.typeName)_\(arity)<\(genericParameters(withConstraints: false))> {
470+
\(kind.rawValue)(component())
471+
}
472+
473+
\(arity == 0 ? "@_disfavoredOverload" : "")
474+
public postfix func \(kind.operatorName)<\(genericParameters(withConstraints: true))>(
475+
_ component: Component
476+
) -> \(kind.typeName)_\(arity)<\(genericParameters(withConstraints: false))> {
477+
\(kind.rawValue)(component)
478+
}
479+
480+
\(kind == .zeroOrOne ?
481+
"""
482+
extension RegexBuilder {
483+
public static func buildLimitedAvailability<\(genericParameters(withConstraints: true))>(
484+
_ component: Component
485+
) -> \(kind.typeName)_\(arity)<\(genericParameters(withConstraints: false))> {
486+
\(kind.rawValue)(component)
487+
}
488+
}
489+
""" : "")
490+
491+
""")
492+
}
377493
}

Sources/_StringProcessing/RegexDSL/Builder.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,4 @@ public enum RegexBuilder {
3333
public static func buildEither<R: RegexProtocol>(second component: R) -> R {
3434
component
3535
}
36-
37-
public static func buildLimitedAvailability<R: RegexProtocol>(
38-
_ component: R
39-
) -> Optionally<R> {
40-
.init(component)
41-
}
4236
}

0 commit comments

Comments
 (0)