Skip to content

Commit 6f7e386

Browse files
hamishknightmilseman
authored andcommitted
Allow numbered \k backreferences
Allow e.g `\k<1>`, which is supported by Oniguruma and .NET.
1 parent ba702f0 commit 6f7e386

File tree

2 files changed

+20
-7
lines changed

2 files changed

+20
-7
lines changed

Sources/_MatchingEngine/Regex/Parse/LexicalAnalysis.swift

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,11 +1176,22 @@ extension Source {
11761176
}
11771177

11781178
if src.tryEat("k") {
1179-
// Perl/.NET-style backreferences.
1180-
if let openChar = src.tryEat(anyOf: "<", "'", "{") {
1179+
// Perl/.NET/Oniguruma-style backreferences.
1180+
if let openChar = src.tryEat(anyOf: "<", "'") {
11811181
let closing = String(Source.getClosingDelimiter(for: openChar))
1182+
1183+
// Perl only accept named references here, but Oniguruma and .NET
1184+
// also accepts numbered references. This shouldn't be an ambiguity
1185+
// as named references may not begin with a digit, '-', or '+'.
1186+
let ref = try src.expectNamedOrNumberedReference(
1187+
endingWith: closing)
1188+
1189+
return .backreference(ref)
1190+
}
1191+
// Perl/.NET also allow a named references with the '{' delimiter.
1192+
if src.tryEat("{") {
11821193
return .backreference(
1183-
try src.expectNamedReference(endingWith: closing))
1194+
try src.expectNamedReference(endingWith: "}"))
11841195
}
11851196
return nil
11861197
}
@@ -1225,7 +1236,6 @@ extension Source {
12251236
try recordLoc { src in
12261237
try src.tryEating { src in
12271238
guard src.tryEat(sequence: "(?") else { return nil }
1228-
let _start = src.currentPosition
12291239

12301240
// Note the below should be covered by canLexGroupLikeReference.
12311241

@@ -1243,8 +1253,7 @@ extension Source {
12431253
}
12441254

12451255
// Whole-pattern recursion, which is equivalent to (?0).
1246-
if src.tryEat("R") {
1247-
let loc = Location(_start ..< src.currentPosition)
1256+
if let loc = src.tryEatWithLoc("R") {
12481257
try src.expect(")")
12491258
return .subpattern(.init(.recurseWholePattern, innerLoc: loc))
12501259
}

Tests/RegexTests/ParseTests.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,10 @@ extension RegexTests {
846846
parseTest(#"\g{52}"#, backreference(.absolute(52)))
847847
parseTest(#"\g{-01}"#, backreference(.relative(-1)))
848848
parseTest(#"\g{+30}"#, backreference(.relative(30)))
849+
parseTest(#"\k<+4>"#, backreference(.relative(4)))
850+
parseTest(#"\k<2>"#, backreference(.absolute(2)))
851+
parseTest(#"\k'-3'"#, backreference(.relative(-3)))
852+
parseTest(#"\k'1'"#, backreference(.absolute(1)))
849853

850854
parseTest(#"\k{a0}"#, backreference(.named("a0")))
851855
parseTest(#"\k<bc>"#, backreference(.named("bc")))
@@ -1335,8 +1339,8 @@ extension RegexTests {
13351339
diagnosticTest(#"\k'#'"#, .groupNameMustBeAlphaNumeric)
13361340
diagnosticTest(#"(?&#)"#, .groupNameMustBeAlphaNumeric)
13371341

1338-
diagnosticTest(#"\k'1'"#, .groupNameCannotStartWithNumber)
13391342
diagnosticTest(#"(?P>1)"#, .groupNameCannotStartWithNumber)
1343+
diagnosticTest(#"\k{1}"#, .groupNameCannotStartWithNumber)
13401344

13411345
// MARK: Conditionals
13421346

0 commit comments

Comments
 (0)