From 8dd5ab09e400149e433f4594cae3d5fa19cd833f Mon Sep 17 00:00:00 2001 From: Kevin Gibbons Date: Sat, 2 Apr 2022 20:32:25 -0700 Subject: [PATCH] Normative: allow duplicate named capture groups --- spec.html | 55 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/spec.html b/spec.html index b272c6b8ab..08c347c119 100644 --- a/spec.html +++ b/spec.html @@ -35715,7 +35715,7 @@

Static Semantics: Early Errors

It is a Syntax Error if CountLeftCapturingParensWithin(|Pattern|) ≥ 232 - 1.
  • - It is a Syntax Error if |Pattern| contains two or more |GroupSpecifier|s for which CapturingGroupName of |GroupSpecifier| is the same. + It is a Syntax Error if |Pattern| contains two distinct |GroupSpecifier|s _x_ and _y_ for which CapturingGroupName(_x_) is the same as CapturingGroupName(_y_) and such that CanBothParticipate(_x_, _y_) is *true*.
  • QuantifierPrefix :: `{` DecimalDigits `,` DecimalDigits `}` @@ -35861,6 +35861,22 @@

    + +

    + Static Semantics: CanBothParticipate ( + _x_: a Parse Node, + _y_: a Parse Node, + ): a Boolean +

    +
    +
    + + 1. Assert: _x_ and _y_ have the same enclosing |Pattern|. + 1. If the enclosing |Pattern| contains a Disjunction :: Alternative `|` Disjunction 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*. + +
    +

    Static Semantics: CapturingGroupNumber ( ): a positive integer

    @@ -36914,7 +36930,7 @@

    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_).

    An escape sequence of the form `\\` followed by a non-zero decimal number _n_ matches the result of the _n_th set of capturing parentheses (). 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_th one is *undefined* because it has not captured anything, then the backreference always succeeds.

    @@ -36950,10 +36966,11 @@

    AtomEscape :: `k` GroupName 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_). @@ -36996,20 +37013,23 @@

    BackreferenceMatcher ( _rer_: a RegExp Record, - _n_: a positive integer, + _ns_: a List of positive integers, _direction_: ~forward~ or ~backward~, ): a Matcher

    - 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]]. @@ -38248,6 +38268,7 @@

    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_th element of _r_.[[Captures]]. 1. If _captureI_ is *undefined*, then @@ -38265,8 +38286,14 @@

    1. Perform ! CreateDataPropertyOrThrow(_A_, ! ToString(𝔽(_i_)), _capturedValue_). 1. If the _i_th 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 @@ -38407,7 +38434,9 @@

    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_.