Skip to content

Add support for quantification kinds to the DSL #150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 10, 2022

Conversation

natecook1000
Copy link
Member

This adds support for specifying eager / reluctant / possessive quantification from within the DSL, like the literal quantification operators with an appended ?.

For example, with the default eager capturing, the following simple regex matches the entire string and capture the "2":

let regex = Regex {
    oneOrMore(.word, .reluctant)
    CharacterClass.digit.capture()
}
"aaa1bbb2".match(regex).match // ("aaa1", "1")

This adds support for specifying eager / reluctant / possessive
quantification from within the DSL, like the literal quantification
operators with an appended `?`.

---

```swift
let regex = Regex {
    oneOrMore(.word, .reluctant)
    CharacterClass.digit.capture()
}
"aaa1bbb2".match(regex).match // ("aaa1", "1")
```
@natecook1000 natecook1000 requested a review from rxwei February 9, 2022 05:58
Comment on lines 356 to 357
_ component: Component,
_ kind: QuantificationKind = .eager
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should kind come before component? This would make it consistent with the builder API. For the builder API we are kinda out of options since we'd have to support a trailing closure.


So it's

oneOrMore("x", .eager)
oneOrMore(.eager) { "x" }

vs.

oneOrMore(.eager, "x")
oneOrMore(.eager) { "x" }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't decide about this. On the one hand, it's nice to have them in a consistent order. On the other, I think it reads better to have the thing you're matching come first, and only shift it to the end when it's in the trailing closure position. IIUC, this is how SwiftUI handles it pretty much across the board — for example, when creating a Button with just a label, the string is the first parameter, otherwise, the builder closure comes at the end.

Let's punt on this for now to see how many different calls like this we have.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. If there's a precedent in SwiftUI I agree we should follow that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if the fact we keep using the term "kind" clues us in a bit. An eager oneOrMore is a different kind of operation than a reluctant or possessive one, and both spelling it as passing a ternary enum or as 3 separate overloads could make sense. Under that interpretation, I think the kind should come first.

We have a default (.eager for regex default semantics), so I would expect the Button(label) analogy for declaring the construction of a button with a label to be more analogous to oneOrMore(pattern) when you just want the default.

@@ -62,6 +62,44 @@ extension CharacterClass: RegexProtocol {

// Note: Quantifiers are currently gyb'd.

/// Specifies how much to attempt to match when using a quantifier.
public struct QuantificationKind {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • "Kind" feels a bit too general. Kind could also refer to one-or-more/zero-or-more/zero-or-one. Is there a more specific word for this? Maybe QuantificationBehavior?
  • For the DSL, should quantifier kinds read like an adverb for fluency?
    oneOrMore("x", .eagerly)
    oneOrMore("x", .reluctantly)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed on both these counts — good call!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would these instead be properties returning Self if we're going that route?

Copy link
Contributor

@rxwei rxwei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

_ component: Component
public func \(kind.rawValue)<\(genericParams)>(
_ component: Component,
_ kind: QuantificationBehavior = .eagerly
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit

Suggested change
_ kind: QuantificationBehavior = .eagerly
_ behavior: QuantificationBehavior = .eagerly

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, missed these!

}

\(arity == 0 ? "@_disfavoredOverload" : "")
public func \(kind.rawValue)<\(genericParameters(withConstraints: true))>(
public func \(kind.rawValue)<\(genericParams)>(
_ kind: QuantificationBehavior = .eagerly,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_ kind: QuantificationBehavior = .eagerly,
_ behavior: QuantificationBehavior = .eagerly,

@natecook1000
Copy link
Member Author

@swift-ci Please test

@rxwei
Copy link
Contributor

rxwei commented Feb 10, 2022

Sorry for the merge conflicts!

Copy link
Member

@milseman milseman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -62,6 +62,44 @@ extension CharacterClass: RegexProtocol {

// Note: Quantifiers are currently gyb'd.

/// Specifies how much to attempt to match when using a quantifier.
public struct QuantificationBehavior {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on this being a "thing" in API vs a set of operations on quantifiable patterns? E.g. there could be a sub-protocol for quantifiables with a var eagerly: Self requirement.

(Not arguing one way or another)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes the most sense as a parameter to the quantification methods — making it an operation on a oneOrMore that uses a builder puts the adverb too far from what it describes. The .eagerly in this example doesn't read clearly as modifying oneOrMore to me:

let regex = Regex {
    oneOrMore {
        "a"
        optionally(.digit)
        .word
    }.eagerly
}

{
oneOrMore {
oneOrMore(.word, .reluctantly)
CharacterClass.digit.capture()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we drop CharacterClass?

At some point I had to add a RegexWithComponent protocol and some overloads to make the static vars discoverable, but @rxwei was able to remove that and keep the lookup. I don't recall the issues there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the compiler able to distinguish between .digit being a method of oneOrMore(...)'s return type and it being a static member call on its own? I haven't been able to get that kind of lookup to work for static protocol methods.

@natecook1000
Copy link
Member Author

@swift-ci Please test Linux platform

@natecook1000 natecook1000 merged commit 12b4b4a into swiftlang:main Feb 10, 2022
@natecook1000 natecook1000 deleted the dsl_quanitification_kind branch February 10, 2022 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants