Skip to content

Commit bb35181

Browse files
authored
Merge pull request #328 from rintaro/integration-main-06f40f6
[Integration] main (06f40f6) -> swift/main
2 parents 3a92092 + 4d198ed commit bb35181

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2268
-1215
lines changed

Documentation/Evolution/CharacterClasses.md

Lines changed: 0 additions & 503 deletions
This file was deleted.

Documentation/Evolution/ProposalOverview.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ Covers the result builder approach and basic API.
1919

2020
## Run-time Regex Construction
2121

22-
- [Pitch](https://github.com/apple/swift-experimental-string-processing/blob/main/Documentation/Evolution/RegexSyntaxRunTimeConstruction.md)
22+
- [Pitch](https://github.com/apple/swift-experimental-string-processing/blob/main/Documentation/Evolution/RegexSyntaxRunTimeConstruction.md), [Thread](https://forums.swift.org/t/pitch-2-regex-syntax-and-run-time-construction/56624)
2323
- (old) Pitch thread: [Regex Syntax](https://forums.swift.org/t/pitch-regex-syntax/55711)
2424
+ Brief: Syntactic superset of PCRE2, Oniguruma, ICU, UTS\#18, etc.
2525

2626
Covers the "interior" syntax, extended syntaxes, run-time construction of a regex from a string, and details of `AnyRegexOutput`.
2727

2828
## Regex Literals
2929

30-
- [Draft](https://github.com/apple/swift-experimental-string-processing/pull/187)
30+
- [Draft](https://github.com/apple/swift-experimental-string-processing/pull/187), [Thread](https://forums.swift.org/t/pitch-2-regex-literals/56736)
3131
- (Old) original pitch:
3232
+ [Thread](https://forums.swift.org/t/pitch-regular-expression-literals/52820)
3333
+ [Update](https://forums.swift.org/t/pitch-regular-expression-literals/52820/90)
@@ -39,17 +39,17 @@ Covers the "interior" syntax, extended syntaxes, run-time construction of a rege
3939

4040
Proposes a slew of Regex-powered algorithms.
4141

42-
Introduces `CustomMatchingRegexComponent`, which is a monadic-parser style interface for external parsers to be used as components of a regex.
42+
Introduces `CustomPrefixMatchRegexComponent`, which is a monadic-parser style interface for external parsers to be used as components of a regex.
4343

4444
## Unicode for String Processing
4545

46-
- Draft: TBD
46+
- [Draft](https://github.com/apple/swift-experimental-string-processing/blob/main/Documentation/Evolution/UnicodeForStringProcessing.md)
4747
- (Old) [Character class definitions](https://forums.swift.org/t/pitch-character-classes-for-string-processing/52920)
4848

4949
Covers three topics:
5050

51-
- Proposes literal and DSL API for library-defined character classes, Unicode scripts and properties, and custom character classes.
52-
- Proposes literal and DSL API for options that affect matching behavior.
51+
- Proposes regex syntax and `RegexBuilder` API for options that affect matching behavior.
52+
- Proposes regex syntax and `RegexBuilder` API for library-defined character classes, Unicode properties, and custom character classes.
5353
- Defines how Unicode scalar-based classes are extended to grapheme clusters in the different semantic and other matching modes.
5454

5555

Documentation/Evolution/RegexLiterals.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ In *[Regex Type and Overview][regex-type]* we introduced the `Regex` type, which
1212

1313
```swift
1414
let pattern = #"(\w+)\s\s+(\S+)\s\s+((?:(?!\s\s).)*)\s\s+(.*)"#
15-
let regex = try! Regex(compiling: pattern)
15+
let regex = try! Regex(pattern)
1616
// regex: Regex<AnyRegexOutput>
1717
```
1818

@@ -366,7 +366,7 @@ However we decided against this because:
366366

367367
### No custom literal
368368

369-
Instead of adding a custom regex literal, we could require users to explicitly write `try! Regex(compiling: "[abc]+")`. This would be similar to `NSRegularExpression`, and loses all the benefits of parsing the literal at compile time. This would mean:
369+
Instead of adding a custom regex literal, we could require users to explicitly write `try! Regex("[abc]+")`. This would be similar to `NSRegularExpression`, and loses all the benefits of parsing the literal at compile time. This would mean:
370370

371371
- No source tooling support (e.g syntax highlighting, refactoring actions) would be available.
372372
- Parse errors would be diagnosed at run time rather than at compile time.

Documentation/Evolution/RegexSyntaxRunTimeConstruction.md

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11

22
# Regex Syntax and Run-time Construction
33

4-
- Authors: [Hamish Knight](https://github.com/hamishknight), [Michael Ilseman](https://github.com/milseman)
4+
* Proposal: [SE-NNNN](NNNN-filename.md)
5+
* Authors: [Hamish Knight](https://github.com/hamishknight), [Michael Ilseman](https://github.com/milseman)
6+
* Review Manager: [Ben Cohen](https://github.com/airspeedswift)
7+
* Status: **Awaiting review**
8+
* Implementation: https://github.com/apple/swift-experimental-string-processing
9+
* Available in nightly toolchain snapshots with `import _StringProcessing`
510

611
## Introduction
712

@@ -50,11 +55,11 @@ We propose run-time construction of `Regex` from a best-in-class treatment of fa
5055

5156
```swift
5257
let pattern = #"(\w+)\s\s+(\S+)\s\s+((?:(?!\s\s).)*)\s\s+(.*)"#
53-
let regex = try! Regex(compiling: pattern)
58+
let regex = try! Regex(pattern)
5459
// regex: Regex<AnyRegexOutput>
5560

5661
let regex: Regex<(Substring, Substring, Substring, Substring, Substring)> =
57-
try! Regex(compiling: pattern)
62+
try! Regex(pattern)
5863
```
5964

6065
### Syntax
@@ -81,11 +86,11 @@ We propose initializers to declare and compile a regex from syntax. Upon failure
8186
```swift
8287
extension Regex {
8388
/// Parse and compile `pattern`, resulting in a strongly-typed capture list.
84-
public init(compiling pattern: String, as: Output.Type = Output.self) throws
89+
public init(_ pattern: String, as: Output.Type = Output.self) throws
8590
}
8691
extension Regex where Output == AnyRegexOutput {
8792
/// Parse and compile `pattern`, resulting in an existentially-typed capture list.
88-
public init(compiling pattern: String) throws
93+
public init(_ pattern: String) throws
8994
}
9095
```
9196

@@ -156,6 +161,20 @@ extension Regex.Match where Output == AnyRegexOutput {
156161
}
157162
```
158163

164+
We propose adding API to query and access captures by name in an existentially typed regex match:
165+
166+
```swift
167+
extension Regex.Match where Output == AnyRegexOutput {
168+
/// If a named-capture with `name` is present, returns its value. Otherwise `nil`.
169+
public subscript(_ name: String) -> AnyRegexOutput.Element? { get }
170+
}
171+
172+
extension AnyRegexOutput {
173+
/// If a named-capture with `name` is present, returns its value. Otherwise `nil`.
174+
public subscript(_ name: String) -> AnyRegexOutput.Element? { get }
175+
}
176+
```
177+
159178
The rest of this proposal will be a detailed and exhaustive definition of our proposed regex syntax.
160179

161180
<details><summary>Grammar Notation</summary>
@@ -392,7 +411,7 @@ For non-Unicode properties, only a value is required. These include:
392411
- The special PCRE2 properties `Xan`, `Xps`, `Xsp`, `Xuc`, `Xwd`.
393412
- The special Java properties `javaLowerCase`, `javaUpperCase`, `javaWhitespace`, `javaMirrored`.
394413

395-
Note that the internal `PropertyContents` syntax is shared by both the `\p{...}` and POSIX-style `[:...:]` syntax, allowing e.g `[:script=Latin:]` as well as `\p{alnum}`.
414+
Note that the internal `PropertyContents` syntax is shared by both the `\p{...}` and POSIX-style `[:...:]` syntax, allowing e.g `[:script=Latin:]` as well as `\p{alnum}`. Both spellings may be used inside and outside of a custom character class.
396415

397416
#### `\K`
398417

@@ -534,6 +553,7 @@ These operators have a lower precedence than the implicit union of members, e.g
534553

535554
To avoid ambiguity between .NET's subtraction syntax and range syntax, .NET specifies that a subtraction will only be parsed if the right-hand-side is a nested custom character class. We propose following this behavior.
536555

556+
Note that a custom character class may begin with the `:` character, and only becomes a POSIX character property if a closing `:]` is present. For example, `[:a]` is the character class of `:` and `a`.
537557

538558
### Matching options
539559

@@ -863,7 +883,23 @@ PCRE supports `\N` meaning "not a newline", however there are engines that treat
863883

864884
### Extended character property syntax
865885

866-
ICU unifies the character property syntax `\p{...}` with the syntax for POSIX character classes `[:...:]`, such that they follow the same internal grammar, which allows referencing any Unicode character property in addition to the POSIX properties. We propose supporting this, though it is a purely additive feature, and therefore should not conflict with regex engines that implement a more limited POSIX syntax.
886+
ICU unifies the character property syntax `\p{...}` with the syntax for POSIX character classes `[:...:]`. This has two effects:
887+
888+
- They share the same internal grammar, which allows the use of any Unicode character properties in addition to the POSIX properties.
889+
- The POSIX syntax may be used outside of custom character classes, unlike in PCRE and Oniguruma.
890+
891+
We propose following both of these rules. The former is purely additive, and therefore should not conflict with regex engines that implement a more limited POSIX syntax. The latter does conflict with other engines, but we feel it is much more likely that a user would expect e.g `[:space:]` to be a character property rather than the character class `[:aceps]`. We do however feel that a warning might be warranted in order to avoid confusion.
892+
893+
### POSIX character property disambiguation
894+
895+
PCRE, Oniguruma and ICU allow `[:` to be part of a custom character class if a closing `:]` is not present. For example, `[:a]` is the character class of `:` and `a`. However they each have different rules for detecting the closing `:]`:
896+
897+
- PCRE will scan ahead until it hits either `:]`, `]`, or `[:`.
898+
- Oniguruma will scan ahead until it hits either `:]`, `]`, or the length exceeds 20 characters.
899+
- ICU will scan ahead until it hits a known escape sequence (e.g `\a`, `\e`, `\Q`, ...), or `:]`. Note this excludes character class escapes e.g `\d`. It also excludes `]`, meaning that even `[:a][:]` is parsed as a POSIX character property.
900+
901+
We propose unifying these behaviors by scanning ahead until we hit either `[`, `]`, `:]`, or `\`. Additionally, we will stop on encountering `}` or a second occurrence of `=`. These fall out the fact that they would be invalid contents of the alternative `\p{...}` syntax.
902+
867903

868904
### Script properties
869905

Documentation/Evolution/RegexTypeOverview.md

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Regex Type and Overview
22

3-
- Authors: [Michael Ilseman](https://github.com/milseman)
3+
* Proposal: [SE-0350](0350-regex-type-overview.md)
4+
* Authors: [Michael Ilseman](https://github.com/milseman)
5+
* Review Manager: [Ben Cohen](https://github.com/airspeedswift)
6+
* Status: **Active Review (4 - 28 April 2022)**
7+
* Implementation: https://github.com/apple/swift-experimental-string-processing
8+
* Available in nightly toolchain snapshots with `import _StringProcessing`
49

510
## Introduction
611

@@ -134,11 +139,11 @@ Regexes can be created at run time from a string containing familiar regex synta
134139

135140
```swift
136141
let pattern = #"(\w+)\s\s+(\S+)\s\s+((?:(?!\s\s).)*)\s\s+(.*)"#
137-
let regex = try! Regex(compiling: pattern)
142+
let regex = try! Regex(pattern)
138143
// regex: Regex<AnyRegexOutput>
139144

140145
let regex: Regex<(Substring, Substring, Substring, Substring, Substring)> =
141-
try! Regex(compiling: pattern)
146+
try! Regex(pattern)
142147
```
143148

144149
*Note*: The syntax accepted and further details on run-time compilation, including `AnyRegexOutput` and extended syntaxes, are discussed in [Run-time Regex Construction][pitches].
@@ -207,7 +212,7 @@ func processEntry(_ line: String) -> Transaction? {
207212
// amount: Substring
208213
// )>
209214

210-
guard let match = regex.matchWhole(line),
215+
guard let match = regex.wholeMatch(line),
211216
let kind = Transaction.Kind(match.kind),
212217
let date = try? Date(String(match.date), strategy: dateParser),
213218
let amount = try? Decimal(String(match.amount), format: decimalParser)
@@ -226,7 +231,7 @@ The result builder allows for inline failable value construction, which particip
226231

227232
Swift regexes describe an unambiguous algorithm, where choice is ordered and effects can be reliably observed. For example, a `print()` statement inside the `TryCapture`'s transform function will run whenever the overall algorithm naturally dictates an attempt should be made. Optimizations can only elide such calls if they can prove it is behavior-preserving (e.g. "pure").
228233

229-
`CustomMatchingRegexComponent`, discussed in [String Processing Algorithms][pitches], allows industrial-strength parsers to be used a regex components. This allows us to drop the overly-permissive pre-parsing step:
234+
`CustomPrefixMatchRegexComponent`, discussed in [String Processing Algorithms][pitches], allows industrial-strength parsers to be used a regex components. This allows us to drop the overly-permissive pre-parsing step:
230235

231236
```swift
232237
func processEntry(_ line: String) -> Transaction? {
@@ -300,7 +305,7 @@ Regex targets [UTS\#18 Level 2](https://www.unicode.org/reports/tr18/#Extended_U
300305
```swift
301306
/// A regex represents a string processing algorithm.
302307
///
303-
/// let regex = try Regex(compiling: "a(.*)b")
308+
/// let regex = try Regex("a(.*)b")
304309
/// let match = "cbaxb".firstMatch(of: regex)
305310
/// print(match.0) // "axb"
306311
/// print(match.1) // "x"
@@ -384,21 +389,25 @@ extension Regex.Match {
384389
// Run-time compilation interfaces
385390
extension Regex {
386391
/// Parse and compile `pattern`, resulting in a strongly-typed capture list.
387-
public init(compiling pattern: String, as: Output.Type = Output.self) throws
392+
public init(_ pattern: String, as: Output.Type = Output.self) throws
388393
}
389394
extension Regex where Output == AnyRegexOutput {
390395
/// Parse and compile `pattern`, resulting in an existentially-typed capture list.
391-
public init(compiling pattern: String) throws
396+
public init(_ pattern: String) throws
392397
}
393398
```
394399

400+
### Cancellation
401+
402+
Regex is somewhat different from existing standard library operations in that regex processing can be a long-running task.
403+
For this reason regex algorithms may check if the parent task has been cancelled and end execution.
404+
395405
### On severability and related proposals
396406

397407
The proposal split presented is meant to aid focused discussion, while acknowledging that each is interconnected. The boundaries between them are not completely cut-and-dry and could be refined as they enter proposal phase.
398408

399409
Accepting this proposal in no way implies that all related proposals must be accepted. They are severable and each should stand on their own merit.
400410

401-
402411
## Source compatibility
403412

404413
Everything in this proposal is additive. Regex delimiters may have their own source compatibility impact, which is discussed in that proposal.
@@ -422,7 +431,7 @@ Regular expressions have a deservedly mixed reputation, owing to their historica
422431

423432
* "Regular expressions are bad because you should use a real parser"
424433
- In other systems, you're either in or you're out, leading to a gravitational pull to stay in when... you should get out
425-
- Our remedy is interoperability with real parsers via `CustomMatchingRegexComponent`
434+
- Our remedy is interoperability with real parsers via `CustomPrefixMatchRegexComponent`
426435
- Literals with refactoring actions provide an incremental off-ramp from regex syntax to result builders and real parsers
427436
* "Regular expressions are bad because ugly unmaintainable syntax"
428437
- We propose literals with source tools support, allowing for better syntax highlighting and analysis
@@ -488,6 +497,16 @@ The generic parameter `Output` is proposed to contain both the whole match (the
488497

489498
The biggest issue with this alternative design is that the numbering of `Captures` elements misaligns with the numbering of captures in textual regexes, where backreference `\0` refers to the entire match and captures start at `\1`. This design would sacrifice familarity and have the pitfall of introducing off-by-one errors.
490499

500+
### Encoding `Regex`es into the type system
501+
502+
During the initial review period the following comment was made:
503+
504+
> I think the goal should be that, at least for regex literals (and hopefully for the DSL to some extent), one day we might not even need a bytecode or interpreter. I think the ideal case is if each literal was its own function or type that gets generated and optimised as if you wrote it in Swift.
505+
506+
This is an approach that has been tried a few times in a few different languages (including by a few members of the Swift Standard Library and Core teams), and while it can produce attractive microbenchmarks, it has almost always proved to be a bad idea at the macro scale. In particular, even if we set aside witness tables and other associated swift generics overhead, optimizing a fixed pipeline for each pattern you want to match causes significant codesize expansion when there are multiple patterns in use, as compared to a more flexible byte code interpreter. A bytecode interpreter makes better use of instruction caches and memory, and can also benefit from micro architectural resources that are shared across different patterns. There is a tradeoff w.r.t. branch prediction resources, where separately compiled patterns may have more decisive branch history data, but a shared bytecode engine has much more data to use; this tradeoff tends to fall on the side of a bytecode engine, but it does not always do so.
507+
508+
It should also be noted that nothing prevents AOT or JIT compiling of the bytecode if we believe it will be advantageous, but compiling or interpreting arbitrary Swift code at runtime is rather more unattractive, since both the type system and language are undecidable. Even absent this rationale, we would probably not encode regex programs directly into the type system simply because it is unnecessarily complex.
509+
491510
### Future work: static optimization and compilation
492511

493512
Swift's support for static compilation is still developing, and future work here is leveraging that to compile regex when profitable. Many regex describe simple [DFAs](https://en.wikipedia.org/wiki/Deterministic_finite_automaton) and can be statically compiled into very efficient programs. Full static compilation needs to be balanced with code size concerns, as a matching-specific bytecode is typically far smaller than a corresponding program (especially since the bytecode interpreter is shared).
@@ -497,7 +516,7 @@ Regex are compiled into an intermediary representation and fairly simple analysi
497516

498517
### Future work: parser combinators
499518

500-
What we propose here is an incremental step towards better parsing support in Swift using parser-combinator style libraries. The underlying execution engine supports recursive function calls and mechanisms for library extensibility. `CustomMatchingRegexComponent`'s protocol requirement is effectively a [monadic parser](https://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf), meaning `Regex` provides a regex-flavored combinator-like system.
519+
What we propose here is an incremental step towards better parsing support in Swift using parser-combinator style libraries. The underlying execution engine supports recursive function calls and mechanisms for library extensibility. `CustomPrefixMatchRegexComponent`'s protocol requirement is effectively a [monadic parser](https://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf), meaning `Regex` provides a regex-flavored combinator-like system.
501520

502521
An issues with traditional parser combinator libraries are the compilation barriers between call-site and definition, resulting in excessive and overly-cautious backtracking traffic. These can be eliminated through better [compilation techniques](https://core.ac.uk/download/pdf/148008325.pdf). As mentioned above, Swift's support for custom static compilation is still under development.
503522

@@ -546,7 +565,7 @@ Regexes are often used for tokenization and tokens can be represented with Swift
546565
547566
### Future work: baked-in localized processing
548567
549-
- `CustomMatchingRegexComponent` gives an entry point for localized processors
568+
- `CustomPrefixMatchRegexComponent` gives an entry point for localized processors
550569
- Future work includes (sub?)protocols to communicate localization intent
551570
552571
-->

0 commit comments

Comments
 (0)