Skip to content

Commit b2943e6

Browse files
authored
Merge pull request #41430 from hamishknight/choose-your-delimiter
Update regex literal delimiters
2 parents 5ec5ffc + 611fd33 commit b2943e6

File tree

8 files changed

+63
-44
lines changed

8 files changed

+63
-44
lines changed

lib/Parse/Lexer.cpp

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,8 +1959,6 @@ const char *Lexer::findEndOfCurlyQuoteStringLiteral(const char *Body,
19591959
}
19601960

19611961
bool Lexer::tryLexRegexLiteral(const char *TokStart) {
1962-
assert(*TokStart == '\'');
1963-
19641962
// We need to have experimental string processing enabled, and have the
19651963
// parsing logic for regex literals available.
19661964
if (!LangOpts.EnableExperimentalStringProcessing || !regexLiteralLexingFn)
@@ -1995,7 +1993,6 @@ bool Lexer::tryLexRegexLiteral(const char *TokStart) {
19951993

19961994
// Otherwise, we either had a successful lex, or something that was
19971995
// recoverable.
1998-
assert(ErrStr || CurPtr[-1] == '\'');
19991996
formToken(tok::regex_literal, TokStart);
20001997
return true;
20011998
}
@@ -2471,8 +2468,16 @@ void Lexer::lexImpl() {
24712468
case '\\': return formToken(tok::backslash, TokStart);
24722469

24732470
case '#':
2471+
// Try lex a raw string literal.
24742472
if (unsigned CustomDelimiterLen = advanceIfCustomDelimiter(CurPtr, Diags))
24752473
return lexStringLiteral(CustomDelimiterLen);
2474+
2475+
// If we have experimental string processing enabled, try lex a regex
2476+
// literal.
2477+
if (tryLexRegexLiteral(TokStart))
2478+
return;
2479+
2480+
// Otherwise try lex a magic pound literal.
24762481
return lexHash();
24772482

24782483
// Operator characters.
@@ -2525,13 +2530,20 @@ void Lexer::lexImpl() {
25252530
case '&': case '|': case '^': case '~': case '.':
25262531
return lexOperatorIdentifier();
25272532

2533+
case 'r':
2534+
// If we have experimental string processing enabled, try lex a regex
2535+
// literal.
2536+
if (tryLexRegexLiteral(TokStart))
2537+
return;
2538+
LLVM_FALLTHROUGH;
2539+
25282540
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
25292541
case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
25302542
case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
25312543
case 'V': case 'W': case 'X': case 'Y': case 'Z':
25322544
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
25332545
case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2534-
case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2546+
case 'o': case 'p': case 'q': /*r above*/ case 's': case 't': case 'u':
25352547
case 'v': case 'w': case 'x': case 'y': case 'z':
25362548
case '_':
25372549
return lexIdentifier();
@@ -2544,14 +2556,6 @@ void Lexer::lexImpl() {
25442556
return lexNumber();
25452557

25462558
case '\'':
2547-
// If we have experimental string processing enabled, and have the parsing
2548-
// logic for regex literals, try to lex a single quoted string as a regex
2549-
// literal.
2550-
if (tryLexRegexLiteral(TokStart))
2551-
return;
2552-
2553-
// Otherwise lex as a string literal and emit a diagnostic.
2554-
LLVM_FALLTHROUGH;
25552559
case '"':
25562560
return lexStringLiteral();
25572561

test/StringProcessing/Parse/regex.swift

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
// RUN: %target-typecheck-verify-swift -enable-experimental-string-processing
22
// REQUIRES: swift_in_compiler
33

4-
_ = '/abc/'
4+
_ = #/abc/#
5+
_ = #|abc|#
6+
_ = re'abc'
57

6-
_ = ('/[*/', '/+]/', '/.]/')
8+
func foo<T>(_ x: T...) {}
9+
foo(#/abc/#, #|abc|#, re'abc')
10+
11+
let arr = [#/abc/#, #|abc|#, re'abc']
12+
13+
_ = #/\w+/#.self
14+
_ = #|\w+|#.self
15+
_ = re'\w+'.self
16+
17+
_ = #/#/\/\#\\/#
18+
_ = #|#|\|\#\\|#
19+
_ = re're\r\e\'\\'
20+
21+
_ = (#/[*/#, #/+]/#, #/.]/#)
722
// expected-error@-1 {{cannot parse regular expression: quantifier '+' must appear after expression}}
823
// expected-error@-2 {{cannot parse regular expression: expected ']'}}
9-
10-
_ = '/\w+/'
11-
_ = '/\'\\/'

test/StringProcessing/Parse/regex_parse_end_of_buffer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33

44
// Note there is purposefully no trailing newline here.
55
// expected-error@+1 {{unterminated regex literal}}
6-
var unterminated = '/xy
6+
var unterminated = #/xy
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// RUN: %target-typecheck-verify-swift -enable-experimental-string-processing
22
// REQUIRES: swift_in_compiler
33

4-
let s = '/\\/''/ // expected-error {{unterminated regex literal}}
4+
let s = #/\\/''/ // expected-error {{unterminated regex literal}}
5+
_ = #|\| // expected-error {{unterminated regex literal}}
6+
_ = #// // expected-error {{unterminated regex literal}}
7+
_ = re'x // expected-error {{unterminated regex literal}}
58

69
// expected-error@+1 {{unterminated regex literal}}
7-
var unterminated = '/xy
10+
var unterminated = #/xy

test/StringProcessing/Runtime/regex_basic.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,19 @@ extension String {
2323
RegexBasicTests.test("Basic") {
2424
let input = "aabccd"
2525

26-
let match1 = input.expectMatch('/aabcc./')
26+
let match1 = input.expectMatch(#/aabcc./#)
2727
expectEqual("aabccd", input[match1.range])
2828
expectTrue("aabccd" == match1.match)
2929

30-
let match2 = input.expectMatch('/a*b.+./')
30+
let match2 = input.expectMatch(#/a*b.+./#)
3131
expectEqual("aabccd", input[match2.range])
3232
expectTrue("aabccd" == match2.match)
3333
}
3434

3535
RegexBasicTests.test("Modern") {
3636
let input = "aabccd"
3737

38-
let match1 = input.expectMatch('|a a bc c /*hello*/ .|')
38+
let match1 = input.expectMatch(#|a a bc c /*hello*/ .|#)
3939
expectEqual("aabccd", input[match1.range])
4040
expectTrue("aabccd" == match1.match)
4141
}
@@ -45,7 +45,7 @@ RegexBasicTests.test("Captures") {
4545
A6F0..A6F1 ; Extend # Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM \
4646
COMBINING MARK TUKWENTIS
4747
"""
48-
let regex = '/([0-9A-F]+)(?:\.\.([0-9A-F]+))?\s+;\s+(\w+).*/'
48+
let regex = #/([0-9A-F]+)(?:\.\.([0-9A-F]+))?\s+;\s+(\w+).*/#
4949
// Test inferred type.
5050
let _: Regex<(Substring, Substring, Substring?, Substring)>.Type
5151
= type(of: regex)

test/StringProcessing/SILGen/regex_literal_silgen.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// RUN: %target-swift-frontend -emit-silgen -enable-experimental-string-processing %s | %FileCheck %s
22
// REQUIRES: swift_in_compiler
33

4-
var s = '/abc/'
5-
// CHECK: [[REGEX_STR_LITERAL:%[0-9]+]] = string_literal utf8 "'/abc/'"
4+
var s = #/abc/#
5+
// CHECK: [[REGEX_STR_LITERAL:%[0-9]+]] = string_literal utf8 "#/abc/#"
66
// CHECK: [[STRING_INIT:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String
77
// CHECK: [[REGEX_STR:%[0-9]+]] = apply [[STRING_INIT]]([[REGEX_STR_LITERAL]]
88

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,48 @@
11
// RUN: %target-typecheck-verify-swift -enable-experimental-string-processing
22
// REQUIRES: swift_in_compiler
33

4-
let r0 = '/./'
4+
let r0 = #/./#
55
let _: Regex<Substring> = r0
66

77
func takesRegex<Match>(_: Regex<Match>) {}
8-
takesRegex('//') // okay
8+
takesRegex(#//#) // okay
99

10-
let r1 = '/.(.)/'
10+
let r1 = #/.(.)/#
1111
// Note: We test its type with a separate statement so that we know the type
1212
// checker inferred the regex's type independently without contextual types.
1313
let _: Regex<(Substring, Substring)>.Type = type(of: r1)
1414

1515
struct S {}
1616
// expected-error @+2 {{cannot assign value of type 'Regex<(Substring, Substring)>' to type 'Regex<S>'}}
1717
// expected-note @+1 {{arguments to generic parameter 'Match' ('(Substring, Substring)' and 'S') are expected to be equal}}
18-
let r2: Regex<S> = '/.(.)/'
18+
let r2: Regex<S> = #/.(.)/#
1919

20-
let r3 = '/(.)(.)/'
20+
let r3 = #/(.)(.)/#
2121
let _: Regex<(Substring, Substring, Substring)>.Type = type(of: r3)
2222

23-
let r4 = '/(?<label>.)(.)/'
23+
let r4 = #/(?<label>.)(.)/#
2424
let _: Regex<(Substring, label: Substring, Substring)>.Type = type(of: r4)
2525

26-
let r5 = '/(.(.(.)))/'
26+
let r5 = #/(.(.(.)))/#
2727
let _: Regex<(Substring, Substring, Substring, Substring)>.Type = type(of: r5)
2828

29-
let r6 = '/(?'we'.(?'are'.(?'regex'.)+)?)/'
30-
let _: Regex<(Substring, we: Substring, are: Substring?, regex: [Substring]?)>.Type = type(of: r6)
29+
let r6 = #/(?'we'.(?'are'.(?'regex'.)+)?)/#
30+
let _: Regex<(Substring, we: Substring, are: Substring?, regex: Substring?)>.Type = type(of: r6)
3131

32-
let r7 = '/(?:(?:(.(.(.)*)?))*?)?/'
32+
let r7 = #/(?:(?:(.(.(.)*)?))*?)?/#
3333
// ^ 1
3434
// ^ 2
3535
// ^ 3
36-
let _: Regex<(Substring, [Substring]?, [Substring?]?, [[Substring]?]?)>.Type = type(of: r7)
36+
let _: Regex<(Substring, Substring??, Substring???, Substring????)>.Type = type(of: r7)
3737

38-
let r8 = '/well(?<theres_no_single_element_tuple_what_can_we>do)/'
38+
let r8 = #/well(?<theres_no_single_element_tuple_what_can_we>do)/#
3939
let _: Regex<(Substring, theres_no_single_element_tuple_what_can_we: Substring)>.Type = type(of: r8)
4040

41-
let r9 = '/(a)|(b)|(c)|d/'
41+
let r9 = #/(a)|(b)|(c)|d/#
4242
let _: Regex<(Substring, Substring?, Substring?, Substring?)>.Type = type(of: r9)
4343

44-
let r10 = '/(a)|b/'
44+
let r10 = #/(a)|b/#
4545
let _: Regex<(Substring, Substring?)>.Type = type(of: r10)
4646

47-
let r11 = '/()()()()()()()()/'
47+
let r11 = #/()()()()()()()()/#
4848
let _: Regex<(Substring, Substring, Substring, Substring, Substring, Substring, Substring, Substring, Substring)>.Type = type(of: r11)

utils/update_checkout/update-checkout-config.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@
123123
"swift-cmark-gfm": "gfm",
124124
"swift-nio": "2.31.2",
125125
"swift-nio-ssl": "2.15.0",
126-
"swift-experimental-string-processing": "dev/6"
126+
"swift-experimental-string-processing": "dev/8"
127127
}
128128
},
129129
"rebranch": {
@@ -157,7 +157,7 @@
157157
"sourcekit-lsp": "main",
158158
"swift-format": "main",
159159
"swift-installer-scripts": "main",
160-
"swift-experimental-string-processing": "dev/6"
160+
"swift-experimental-string-processing": "dev/8"
161161
}
162162
},
163163
"release/5.6": {
@@ -308,7 +308,7 @@
308308
"sourcekit-lsp": "main",
309309
"swift-format": "main",
310310
"swift-installer-scripts": "main",
311-
"swift-experimental-string-processing": "dev/6"
311+
"swift-experimental-string-processing": "dev/8"
312312
}
313313
},
314314
"release/5.4": {

0 commit comments

Comments
 (0)