Skip to content

Commit 7ed23cc

Browse files
committed
Reapply changes on top of main
1 parent f6c6eb1 commit 7ed23cc

21 files changed

+1323
-160
lines changed

Sources/Exercises/Participants/HandWrittenParticipant.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ private func graphemeBreakPropertyData(
6060
}
6161

6262
// For testing our framework
63-
if forceFailure, lower == Unicode.Scalar(0x07FD) {
63+
if forceFailure/*, lower == Unicode.Scalar(0x07FD)*/ {
6464
return nil
6565
}
6666

Sources/RegexBuilder/Anchor.swift

+61
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,64 @@ public struct NegativeLookahead<Output>: _BuiltinRegexComponent {
226226
self.init(_RegexFactory().negativeLookaheadNonCapturing(component()))
227227
}
228228
}
229+
230+
/// A regex component that allows a match to continue only if its contents
231+
/// match at the given location.
232+
///
233+
/// A lookbehind is a zero-length assertion that its included regex matches at
234+
/// a particular position. Lookbehinds do not advance the overall matching
235+
/// position in the input string — once a lookbehind succeeds, matching continues
236+
/// in the regex from the same position.
237+
@available(SwiftStdlib 5.7, *) // TODO: How should this be gated?
238+
public struct Lookbehind<Output>: _BuiltinRegexComponent {
239+
public var regex: Regex<Output>
240+
241+
init(_ regex: Regex<Output>) {
242+
self.regex = regex
243+
}
244+
245+
/// Creates a lookbehind from the given regex component.
246+
public init<R: RegexComponent>(
247+
_ component: R
248+
) where R.RegexOutput == Output {
249+
self.init(_RegexFactory().lookbehindNonCapturing(component))
250+
}
251+
252+
/// Creates a lookbehind from the regex generated by the given builder closure.
253+
public init<R: RegexComponent>(
254+
@RegexComponentBuilder _ component: () -> R
255+
) where R.RegexOutput == Output {
256+
self.init(_RegexFactory().lookbehindNonCapturing(component()))
257+
}
258+
}
259+
260+
/// A regex component that allows a match to continue only if its contents
261+
/// do not match at the given location.
262+
///
263+
/// A negative lookbehind is a zero-length assertion that its included regex
264+
/// does not match at a particular position. Lookbehinds do not advance the
265+
/// overall matching position in the input string — once a lookbehind succeeds,
266+
/// matching continues in the regex from the same position.
267+
@available(SwiftStdlib 5.7, *) // TODO: How should this be gated?
268+
public struct NegativeLookbehind<Output>: _BuiltinRegexComponent {
269+
public var regex: Regex<Output>
270+
271+
init(_ regex: Regex<Output>) {
272+
self.regex = regex
273+
}
274+
275+
/// Creates a negative lookbehind from the given regex component.
276+
public init<R: RegexComponent>(
277+
_ component: R
278+
) where R.RegexOutput == Output {
279+
self.init(_RegexFactory().negativeLookbehindNonCapturing(component))
280+
}
281+
282+
/// Creates a negative lookbehind from the regex generated by the given builder
283+
/// closure.
284+
public init<R: RegexComponent>(
285+
@RegexComponentBuilder _ component: () -> R
286+
) where R.RegexOutput == Output {
287+
self.init(_RegexFactory().negativeLookbehindNonCapturing(component()))
288+
}
289+
}

Sources/_RegexParser/Regex/AST/MatchingOptions.swift

+3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ extension AST {
4747

4848
// NSRegularExpression compatibility special-case
4949
case nsreCompatibleDot // no AST representation
50+
51+
// Lookbehind support
52+
case reverse // no AST representation
5053
}
5154

5255
public var kind: Kind

Sources/_RegexParser/Regex/Parse/Parse.swift

+13
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,19 @@ extension Parser {
523523
mutating func parseCustomCharacterClass(
524524
_ start: Source.Located<CustomCC.Start>
525525
) -> CustomCC {
526+
// Excessively nested recursion is a common DOS attack, so limit
527+
// our recursion.
528+
context.parseDepth += 1
529+
defer { context.parseDepth -= 1 }
530+
guard context.parseDepth < context.maxParseDepth else {
531+
self.errorAtCurrentPosition(.nestingTooDeep)
532+
533+
// This is not generally recoverable and further errors will be
534+
// incorrect
535+
diags.suppressFurtherDiagnostics = true
536+
return .init(start, [], start.location)
537+
}
538+
526539
let alreadyInCCC = context.isInCustomCharacterClass
527540
context.isInCustomCharacterClass = true
528541
defer { context.isInCustomCharacterClass = alreadyInCCC }

Sources/_RegexParser/Regex/Parse/Sema.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ extension RegexValidator {
143143
case .caseInsensitive, .possessiveByDefault, .reluctantByDefault,
144144
.singleLine, .multiline, .namedCapturesOnly, .extended, .extraExtended,
145145
.asciiOnlyDigit, .asciiOnlyWord, .asciiOnlySpace, .asciiOnlyPOSIXProps,
146-
.nsreCompatibleDot:
146+
.nsreCompatibleDot, .reverse:
147147
break
148148
}
149149
}
@@ -370,7 +370,7 @@ extension RegexValidator {
370370
}
371371
switch kind.value {
372372
case .capture, .namedCapture, .nonCapture, .lookahead, .negativeLookahead,
373-
.atomicNonCapturing:
373+
.atomicNonCapturing, .lookbehind, .negativeLookbehind:
374374
break
375375

376376
case .balancedCapture:
@@ -384,8 +384,8 @@ extension RegexValidator {
384384
case .nonAtomicLookahead:
385385
error(.unsupported("non-atomic lookahead"), at: kind.location)
386386

387-
case .lookbehind, .negativeLookbehind, .nonAtomicLookbehind:
388-
error(.unsupported("lookbehind"), at: kind.location)
387+
case .nonAtomicLookbehind:
388+
error(.unsupported("non-atomic lookbehind"), at: kind.location)
389389

390390
case .scriptRun, .atomicScriptRun:
391391
error(.unsupported("script run"), at: kind.location)

0 commit comments

Comments
 (0)