This is kind of a hack too, but you can see how the regex builders work in the Swift evolution proposal:
Regex {
regex0
regex1
regex2
regex3
}
becomes
Regex {
let e0 = RegexComponentBuilder.buildExpression(regex0)
let e1 = RegexComponentBuilder.buildExpression(regex1)
let e2 = RegexComponentBuilder.buildExpression(regex2)
let e3 = RegexComponentBuilder.buildExpression(regex3)
let r0 = RegexComponentBuilder.buildPartialBlock(first: e0)
let r1 = RegexComponentBuilder.buildPartialBlock(accumulated: r0, next: e1)
let r2 = RegexComponentBuilder.buildPartialBlock(accumulated: r1, next: e2)
let r3 = RegexComponentBuilder.buildPartialBlock(accumulated: r2, next: e3)
return r3
}
Rather than RegexComponentBuilder
, we can use AlternationBuilder
here to make a ChoiceOf
. You can see that the way that buildExpression
and buildPartialBlock
are called are like a map
and reduce
.
let regex = Regex {
let exps = Fruit.allCases.map { AlternationBuilder.buildExpression($0.rawValue) }
// assuming exps is not empty
exps.dropFirst().reduce(AlternationBuilder.buildPartialBlock(first: exps[0])) { acc, next in
AlternationBuilder.buildPartialBlock(accumulated: acc, next: next)
}
}
We can put this into an extension:
extension ChoiceOf where RegexOutput == Substring {
init<S: Sequence<String>>(_ components: S) {
let exps = components.map { AlternationBuilder.buildExpression($0) }
guard !exps.isEmpty else {
fatalError("Empty choice!")
}
self = exps.dropFirst().reduce(AlternationBuilder.buildPartialBlock(first: exps[0])) { acc, next in
AlternationBuilder.buildPartialBlock(accumulated: acc, next: next)
}
}
}
Notably, this does not work when the array is empty, i.e. when there is no choice to be made. You cannot just return: Choice { }
. That violates one of the constraints of that initialiser. And indeed, Choice { }
doesn't make sense anyway.
I think this is also why this isn't supported out of the box - the compiler cannot determine whether Fruits.allCases
, or whatever other array you give it, is empty or not.