Skip to content
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

Normative: allow duplicate named capture groups #2721

Merged
merged 1 commit into from
Nov 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Normative: allow duplicate named capture groups (#2721)
  • Loading branch information
bakkot authored and ljharb committed Nov 8, 2024
commit 1cc4d4b72e27015e81b48331288f9ea247440cc3
55 changes: 42 additions & 13 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -35986,7 +35986,7 @@ <h1>Static Semantics: Early Errors</h1>
It is a Syntax Error if CountLeftCapturingParensWithin(|Pattern|) ≥ 2<sup>32</sup> - 1.
</li>
<li>
It is a Syntax Error if |Pattern| contains two or more |GroupSpecifier|s for which the CapturingGroupName of |GroupSpecifier| is the same.
It is a Syntax Error if |Pattern| contains two distinct |GroupSpecifier|s _x_ and _y_ such that the CapturingGroupName of _x_ is the CapturingGroupName of _y_ and such that MightBothParticipate(_x_, _y_) is *true*.
</li>
</ul>
<emu-grammar>QuantifierPrefix :: `{` DecimalDigits `,` DecimalDigits `}`</emu-grammar>
Expand Down Expand Up @@ -36132,6 +36132,22 @@ <h1>
</emu-alg>
</emu-clause>

<emu-clause id="sec-mightbothparticipate" type="abstract operation">
<h1>
Static Semantics: MightBothParticipate (
_x_: a Parse Node,
_y_: a Parse Node,
): a Boolean
</h1>
<dl class="header">
</dl>
<emu-alg>
1. Assert: _x_ and _y_ have the same enclosing |Pattern|.
1. If the enclosing |Pattern| contains a <emu-grammar>Disjunction :: Alternative `|` Disjunction</emu-grammar> Parse Node such that either _x_ is contained within the |Alternative| and _y_ is contained within the derived |Disjunction|, or _x_ is contained within the derived |Disjunction| and _y_ is contained within the |Alternative|, return *false*.
1. Return *true*.
</emu-alg>
</emu-clause>

<emu-clause id="sec-patterns-static-semantics-capturing-group-number" type="sdo">
<h1>Static Semantics: CapturingGroupNumber ( ): a positive integer</h1>
<dl class="header">
Expand Down Expand Up @@ -37189,7 +37205,7 @@ <h1>
<emu-alg>
1. Let _n_ be the CapturingGroupNumber of |DecimalEscape|.
1. Assert: _n_ ≤ _rer_.[[CapturingGroupsCount]].
1. Return BackreferenceMatcher(_rer_, _n_, _direction_).
1. Return BackreferenceMatcher(_rer_, « _n_ », _direction_).
</emu-alg>
<emu-note>
<p>An escape sequence of the form `\\` followed by a non-zero decimal number _n_ matches the result of the _n_<sup>th</sup> set of capturing parentheses (<emu-xref href="#sec-pattern-notation"></emu-xref>). It is an error if the regular expression has fewer than _n_ capturing parentheses. If the regular expression has _n_ or more capturing parentheses but the _n_<sup>th</sup> one is *undefined* because it has not captured anything, then the backreference always succeeds.</p>
Expand Down Expand Up @@ -37225,10 +37241,11 @@ <h1>
<emu-grammar>AtomEscape :: `k` GroupName</emu-grammar>
<emu-alg>
1. Let _matchingGroupSpecifiers_ be GroupSpecifiersThatMatch(|GroupName|).
1. Assert: _matchingGroupSpecifiers_ contains a single |GroupSpecifier|.
1. Let _groupSpecifier_ be the sole element of _matchingGroupSpecifiers_.
1. Let _parenIndex_ be CountLeftCapturingParensBefore(_groupSpecifier_).
1. Return BackreferenceMatcher(_rer_, _parenIndex_, _direction_).
1. Let _parenIndices_ be a new empty List.
1. For each |GroupSpecifier| _groupSpecifier_ of _matchingGroupSpecifiers_, do
1. Let _parenIndex_ be CountLeftCapturingParensBefore(_groupSpecifier_).
1. Append _parenIndex_ to _parenIndices_.
1. Return BackreferenceMatcher(_rer_, _parenIndices_, _direction_).
</emu-alg>

<emu-clause id="sec-runtime-semantics-charactersetmatcher-abstract-operation" type="abstract operation">
Expand Down Expand Up @@ -37271,20 +37288,23 @@ <h1>
<h1>
BackreferenceMatcher (
_rer_: a RegExp Record,
_n_: a positive integer,
_ns_: a List of positive integers,
_direction_: ~forward~ or ~backward~,
): a Matcher
</h1>
<dl class="header">
</dl>
<emu-alg>
1. Assert: _n_ ≥ 1.
1. Return a new Matcher with parameters (_x_, _c_) that captures _rer_, _n_, and _direction_ and performs the following steps when called:
1. Return a new Matcher with parameters (_x_, _c_) that captures _rer_, _ns_, and _direction_ and performs the following steps when called:
1. Assert: _x_ is a MatchState.
1. Assert: _c_ is a MatcherContinuation.
1. Let _Input_ be _x_.[[Input]].
1. Let _cap_ be _x_.[[Captures]].
1. Let _r_ be _cap_[_n_].
1. Let _r_ be *undefined*.
1. For each integer _n_ of _ns_, do
1. If _cap_[_n_] is not *undefined*, then
1. Assert: _r_ is *undefined*.
1. Set _r_ to _cap_[_n_].
1. If _r_ is *undefined*, return _c_(_x_).
1. Let _e_ be _x_.[[EndIndex]].
1. Let _rs_ be _r_.[[StartIndex]].
Expand Down Expand Up @@ -38523,6 +38543,7 @@ <h1>
1. Let _groups_ be *undefined*.
1. Let _hasGroups_ be *false*.
1. Perform ! CreateDataPropertyOrThrow(_A_, *"groups"*, _groups_).
1. Let _matchedGroupNames_ be a new empty List.
1. For each integer _i_ such that 1 ≤ _i_ ≤ _n_, in ascending order, do
1. Let _captureI_ be _i_<sup>th</sup> element of _r_.[[Captures]].
1. If _captureI_ is *undefined*, then
Expand All @@ -38540,8 +38561,14 @@ <h1>
1. Perform ! CreateDataPropertyOrThrow(_A_, ! ToString(𝔽(_i_)), _capturedValue_).
1. If the _i_<sup>th</sup> capture of _R_ was defined with a |GroupName|, then
1. Let _s_ be the CapturingGroupName of that |GroupName|.
1. Perform ! CreateDataPropertyOrThrow(_groups_, _s_, _capturedValue_).
1. Append _s_ to _groupNames_.
1. If _matchedGroupNames_ contains _s_, then
1. Assert: _capturedValue_ is *undefined*.
1. Append *undefined* to _groupNames_.
1. Else,
1. If _capturedValue_ is not *undefined*, append _s_ to _matchedGroupNames_.
1. NOTE: If there are multiple groups named _s_, _groups_ may already have an _s_ property at this point. However, because _groups_ is an ordinary object whose properties are all writable data properties, the call to CreateDataPropertyOrThrow is nevertheless guaranteed to succeed.
1. Perform ! CreateDataPropertyOrThrow(_groups_, _s_, _capturedValue_).
1. Append _s_ to _groupNames_.
1. Else,
1. Append *undefined* to _groupNames_.
1. If _hasIndices_ is *true*, then
Expand Down Expand Up @@ -38684,7 +38711,9 @@ <h1>
1. Perform ! CreateDataPropertyOrThrow(_A_, ! ToString(𝔽(_i_)), _matchIndexPair_).
1. If _i_ > 0 and _groupNames_[_i_ - 1] is not *undefined*, then
1. Assert: _groups_ is not *undefined*.
1. Perform ! CreateDataPropertyOrThrow(_groups_, _groupNames_[_i_ - 1], _matchIndexPair_).
1. Let _s_ be _groupNames_[_i_ - 1].
1. NOTE: If there are multiple groups named _s_, _groups_ may already have an _s_ property at this point. However, because _groups_ is an ordinary object whose properties are all writable data properties, the call to CreateDataPropertyOrThrow is nevertheless guaranteed to succeed.
1. Perform ! CreateDataPropertyOrThrow(_groups_, _s_, _matchIndexPair_).
1. Return _A_.
</emu-alg>
</emu-clause>
Expand Down
Loading