Skip to content

Commit 2c424b9

Browse files
committed
Move reference name lexing onto expectQuoted
This lets us throw an error if the name is empty.
1 parent e9ccfdc commit 2c424b9

File tree

4 files changed

+24
-15
lines changed

4 files changed

+24
-15
lines changed

Sources/_MatchingEngine/Regex/Parse/Diagnostics.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ enum ParseError: Error, Hashable {
3030

3131
case expectedASCII(Character)
3232

33+
case expectedNonEmptyContents
34+
3335
case expectedCustomCharacterClassMembers
3436
case invalidCharacterClassRangeOperand
3537

@@ -61,6 +63,8 @@ extension ParseError: CustomStringConvertible {
6163
return "unexpected end of input"
6264
case let .misc(s):
6365
return s
66+
case .expectedNonEmptyContents:
67+
return "expected non-empty contents"
6468
case let .expectedASCII(c):
6569
return "expected ASCII for '\(c)'"
6670
case .expectedCustomCharacterClassMembers:

Sources/_MatchingEngine/Regex/Parse/LexicalAnalysis.swift

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ extension Source {
435435
return false
436436
}.value
437437
guard !result.isEmpty else {
438-
throw ParseError.misc("Expected non-empty contents")
438+
throw ParseError.expectedNonEmptyContents
439439
}
440440
return result
441441
}
@@ -840,20 +840,25 @@ extension Source {
840840
}
841841
}
842842

843+
/// Eat a named reference up to a given closing delimiter.
844+
private mutating func expectNamedReference(
845+
endingWith end: String
846+
) throws -> Reference {
847+
.named(try expectQuoted(endingWith: end).value)
848+
}
849+
843850
/// Try to lex a numbered reference, or otherwise a named reference.
844851
///
845852
/// NameOrNumberRef -> NumberRef | <String>
846853
///
847854
private mutating func expectNamedOrNumberedReference(
848855
endingWith ending: String
849-
) throws -> Located<Reference> {
850-
try recordLoc { src in
851-
if let numbered = try src.lexNumberedReference() {
852-
try src.expect(sequence: ending)
853-
return numbered.value
854-
}
855-
return .named(try src.lexUntil(eating: ending).value)
856+
) throws -> Reference {
857+
if let numbered = try lexNumberedReference() {
858+
try expect(sequence: ending)
859+
return numbered.value
856860
}
861+
return try expectNamedReference(endingWith: ending)
857862
}
858863

859864
private static func getClosingDelimiter(
@@ -886,15 +891,14 @@ extension Source {
886891
if src.tryEat("g") {
887892
// PCRE-style backreferences.
888893
if src.tryEat("{") {
889-
let ref = try src.expectNamedOrNumberedReference(
890-
endingWith: "}").value
894+
let ref = try src.expectNamedOrNumberedReference(endingWith: "}")
891895
return .backreference(ref)
892896
}
893897

894898
// Oniguruma-style subpatterns.
895899
if let openChar = src.tryEat(anyOf: "<", "'") {
896900
let ref = try src.expectNamedOrNumberedReference(
897-
endingWith: String(Source.getClosingDelimiter(for: openChar))).value
901+
endingWith: String(Source.getClosingDelimiter(for: openChar)))
898902
return .subpattern(ref)
899903
}
900904

@@ -911,9 +915,9 @@ extension Source {
911915
if src.tryEat("k") {
912916
// Perl/.NET-style backreferences.
913917
if let openChar = src.tryEat(anyOf: "<", "'", "{") {
914-
let closingChar = Source.getClosingDelimiter(for: openChar)
915-
return .backreference(.named(
916-
try src.lexUntil(eating: closingChar).value))
918+
let closing = String(Source.getClosingDelimiter(for: openChar))
919+
return .backreference(
920+
try src.expectNamedReference(endingWith: closing))
917921
}
918922
// Fallback to a literal character. We need to return here as we've
919923
// already eaten the 'k'.

Tests/RegexTests/LexTests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ extension RegexTests {
150150
diagnose(#""ab\""#, expecting: .expected("\""), .experimental) { _ = try $0.lexQuote() }
151151
diagnose(#""ab\"#, expecting: .unexpectedEndOfInput, .experimental) { _ = try $0.lexQuote() }
152152

153+
diagnose(#"\k''"#, expecting: .expectedNonEmptyContents) { _ = try $0.lexBasicAtom() }
154+
153155
// TODO: want to dummy print out source ranges, etc, test that.
154156
}
155157

Tests/RegexTests/ParseTests.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,6 @@ extension RegexTests {
799799

800800
parseTest(#"\k{a0}"#, backreference(.named("a0")))
801801
parseTest(#"\k<bc>"#, backreference(.named("bc")))
802-
parseTest(#"\k''"#, backreference(.named("")))
803802
parseTest(#"\g{abc}"#, backreference(.named("abc")))
804803

805804
parseTest(#"\g<1>"#, subpattern(.absolute(1)))

0 commit comments

Comments
 (0)