Stage 1 Draft / September 7, 2024

Pattern Matching

Welcome

TODOs

Introduction

This specification consists of the following parts:

Trivia built-in matchers are folded. Click to not show the trivia sections.

Layering

The pattern-matching champion group designed this proposal with a layering approach. It does not mean the proposal is an MVP. The champion group wishes to ship the proposal as a whole when possible, but we can drop some features if there is strong pushback from the committee.

This approach allows the champion group to consider how all features combine and also how the proposal should behave if any of the features are missing.

A feature will have a note if

  • it is a convenient feature instead of a necessary feature.
  • not all champion group members represent the hope to include it.

4 Overview

4.5 Organization of This Specification

Clause 30 describes the pattern-matching feature.

6 ECMAScript Data Types and Values

6.1 ECMAScript Language Types

6.1.5 The Symbol Type

6.1.5.1 Well-Known Symbols

Table 1: Well-known Symbols
Specification Name [[Description]] Value and Purpose
%Symbol.customMatcher% "Symbol.customMatcher" A method that performs custom pattern matching semantics. Called by the semantics of the pattern-matching features.

6.1.7 Object Internal Methods and Internal Slots

All objects have an internal slot named [[ConstructedBy]], which is a List of ECMAScript language values. This List represents the origin of the object. Initially, it is an empty List.

7 Abstract Operations

7.3 Operations on Objects

7.3.34 InitializeInstanceElements ( O, constructor )

The abstract operation InitializeInstanceElements takes arguments O (an Object) and constructor (an ECMAScript function object) and returns either a normal completion containing unused or a throw completion. It performs the following steps when called:

  1. Let methods be the value of constructor.[[PrivateMethods]].
  2. For each PrivateElement method of methods, do
    1. Perform ? PrivateMethodOrAccessorAdd(O, method).
  3. Let fields be the value of constructor.[[Fields]].
  4. For each element fieldRecord of fields, do
    1. Perform ? DefineField(O, fieldRecord).
  5. Append constructor to O.[[ConstructedBy]].
  6. Return unused.
Editor's Note
Rename this abstract operation to InitializeInstance.

8 Syntax-Directed Operations

8.2 Scope Analysis

8.2.1 Static Semantics: BoundNames

The syntax-directed operation BoundNames takes no arguments and returns a List of Strings.

Editor's Note
TODO: Scope Analysis.

8.2.2 Static Semantics: DeclarationPart

The syntax-directed operation DeclarationPart takes no arguments and returns a Parse Node.

Editor's Note
TODO: Scope Analysis.

8.2.3 Static Semantics: IsConstantDeclaration

The syntax-directed operation IsConstantDeclaration takes no arguments and returns a Boolean.

Editor's Note
TODO: Scope Analysis.

8.2.4 Static Semantics: LexicallyDeclaredNames

The syntax-directed operation LexicallyDeclaredNames takes no arguments and returns a List of Strings.

Editor's Note
TODO: Scope Analysis.

8.2.5 Static Semantics: LexicallyScopedDeclarations

The syntax-directed operation LexicallyScopedDeclarations takes no arguments and returns a List of Parse Nodes.

Editor's Note
TODO: Scope Analysis.

8.2.6 Static Semantics: VarDeclaredNames

The syntax-directed operation VarDeclaredNames takes no arguments and returns a List of Strings.

Editor's Note
TODO: Scope Analysis.

8.2.7 Static Semantics: VarScopedDeclarations

The syntax-directed operation VarScopedDeclarations takes no arguments and returns a List of Parse Nodes.

Editor's Note
TODO: Scope Analysis.

8.2.8 Static Semantics: TopLevelLexicallyDeclaredNames

The syntax-directed operation TopLevelLexicallyDeclaredNames takes no arguments and returns a List of Strings.

Editor's Note
TODO: Scope Analysis.

8.2.9 Static Semantics: TopLevelLexicallyScopedDeclarations

The syntax-directed operation TopLevelLexicallyScopedDeclarations takes no arguments and returns a List of Parse Nodes.

Editor's Note
TODO: Scope Analysis.

8.2.10 Static Semantics: TopLevelVarDeclaredNames

The syntax-directed operation TopLevelVarDeclaredNames takes no arguments and returns a List of Strings.

Editor's Note
TODO: Scope Analysis.

8.2.11 Static Semantics: TopLevelVarScopedDeclarations

The syntax-directed operation TopLevelVarScopedDeclarations takes no arguments and returns a List of Parse Nodes.

Editor's Note
TODO: Scope Analysis.

8.6 Miscellaneous

8.6.2 Runtime Semantics: BindingInitialization

The syntax-directed operation BindingInitialization takes arguments value (an ECMAScript language value) and environment (an Environment Record or undefined) and returns either a normal completion containing unused or an abrupt completion.

Editor's Note
TODO: Scope Analysis.

8.6.3 Runtime Semantics: IteratorBindingInitialization

The syntax-directed operation IteratorBindingInitialization takes arguments iteratorRecord (an Iterator Record) and environment (an Environment Record or undefined) and returns either a normal completion containing unused or an abrupt completion.

Editor's Note
TODO: Scope Analysis.

9 Executable Code and Execution Contexts

9.10 Processing Model of WeakRef and FinalizationRegistryweakly hold Targets

9.10.3 Execution

At any time, if a set of objects and/or symbols S is not live, an ECMAScript implementation may perform the following steps atomically:

  1. For each element value of S, do
    1. For each WeakRef ref such that ref.[[WeakRefTarget]] is value, do
      1. Set ref.[[WeakRefTarget]] to empty.
    2. For each FinalizationRegistry fg such that fg.[[Cells]] contains a Record cell such that cell.[[WeakRefTarget]] is value, do
      1. Set cell.[[WeakRefTarget]] to empty.
      2. Optionally, perform HostEnqueueFinalizationRegistryCleanupJob(fg).
    3. For each WeakMap map such that map.[[WeakMapData]] contains a Record r such that r.[[Key]] is value, do
      1. Set r.[[Key]] to empty.
      2. Set r.[[Value]] to empty.
    4. For each WeakSet set such that set.[[WeakSetData]] contains value, do
      1. Replace the element of set.[[WeakSetData]] whose value is value with an element whose value is empty.
    5. For each Object o such that o.[[ConstructedBy]] contains value, do
      1. Remove value from o.[[ConstructedBy]].

12 ECMAScript Language: Lexical Grammar

12.10 Automatic Semicolon Insertion

12.10.1 Rules of Automatic Semicolon Insertion

Note

The following are the additions of the restricted productions in the grammar:

RelationalExpression[In, Yield, Await] : RelationalExpression [no LineTerminator here] is MatchPattern MatchExpression[Yield, Await] : CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await] [no LineTerminator here] { MatchExpressionClauses[?Yield, ?Await] ; } MatchHead : match [no LineTerminator here] ( Expression )

12.10.3 Interesting Cases of Automatic Semicolon Insertion

12.10.3.2 Cases of Automatic Semicolon Insertion and “[no LineTerminator here]”

12.10.3.2.1 List of Grammar Productions with Optional Operands and “[no LineTerminator here]”

13 ECMAScript Language: Expressions

13.2 Primary Expression

Syntax

PrimaryExpression[Yield, Await] : RegularExpressionLiteral MatchExpression[?Yield, ?Await]

13.2.10 Match Expression

See The match Expression for PrimaryExpression : MatchExpression

13.10 Relational Operators

Syntax

RelationalExpression[In, Yield, Await] : RelationalExpression[?In, ?Yield, ?Await] [no LineTerminator here] is MatchPattern[?Yield, ?Await] Note
const isOk = response is { ok: true, status: > 200 and < 400 };
Editor's Note
This feature can be replaced by The match Expression. The code example above can be written as:
const isOk = match (response) {
  { ok: true, status: > 200 and < 400 }: true,
  default: false
};
Editor's Note

13.10.1 Runtime Semantics: Evaluation

RelationalExpression : RelationalExpression is MatchPattern
  1. Let lref be ? Evaluation of RelationalExpression.
  2. Let lval be ? GetValue(lref).
  3. Let cacheGroup be CreateMatchCache().
  4. Let matchCompletion be ? MatchPatternMatches of MatchPattern with argument lval and cacheGroup.
  5. If matchCompletion is a normal completion, then
    1. If matchCompletion.[[Value]] is not-matched, set matchCompletion to NormalCompletion(false).
    2. Else, set matchCompletion to NormalCompletion(true).
  6. Let result be Completion(FinishMatch(matchCompletion, cacheGroup)).
  7. Assert: result is a normal completion or an abrupt completion.
  8. Return result.

14 ECMAScript Language: Statements and Declarations

Syntax

14.7 Iteration Statements

14.7.1 The for-in, for-of, and for-await-of Statements

Editor's Note
It is possible to add pattern-matching to the for iteration statements. It might look like this:
for (const response of responses) {
  if (item is { ok: true, let body }) {
  }
}
// can be written as
for (const response is { ok: true, let body } of responses) {
}
// or
for (const response of responses matches { ok: true, let body }) {
}

14.14 The try Statement

Editor's Note
It is possible to add pattern-matching to the try statement. It might look like this:
try { }
catch (error) {
  if (error is { message: /JSON/ }) { return null; }
  throw error;
}
// can be written as
try { }
catch (error is { message: /JSON/ }) { return null; }
// unmatched error will be re-thrown.

15 ECMAScript Language: Functions and Classes

15.7 Class Definitions

15.7.14 Runtime Semantics: ClassDefinitionEvaluation

The syntax-directed operation ClassDefinitionEvaluation takes arguments classBinding (a String or undefined) and className (a property key or a Private Name) and returns either a normal completion containing a function object or an abrupt completion.

Editor's Note

15.10 Tail Position Calls

15.10.2 Static Semantics: HasCallInTailPosition

The syntax-directed operation HasCallInTailPosition takes argument call (a CallExpression Parse Node, a MemberExpression Parse Node, or an OptionalChain Parse Node) and returns a Boolean. It is defined piecewise over the following productions:

RelationalExpression : RelationalExpression is MatchPattern
  1. Return false.
PrimaryExpression : MatchExpression
  1. Return HasCallInTailPosition of MatchExpression with argument call.
MatchExpression : CoverCallExpressionAndAsyncArrowHead { MatchExpressionClauses ; }
  1. Return HasCallInTailPosition of MatchExpressionClauses with argument call.
MatchExpressionClauses : MatchExpressionClause
  1. Return HasCallInTailPosition of MatchExpressionClause with argument call.
MatchExpressionClauses : MatchExpressionClauses ; MatchExpressionClause
  1. Let result be HasCallInTailPosition of MatchExpressionClauses with argument call.
  2. If result is true, return true.
  3. Return HasCallInTailPosition of MatchExpressionClause with argument call.
MatchExpressionClauses : MatchExpressionClauses ; default : Expression
  1. Let result be HasCallInTailPosition of MatchExpressionClauses with argument call.
  2. If result is true, return true.
  3. Return HasCallInTailPosition of Expression with argument call.
MatchExpressionClauses : default : Expression
  1. Return HasCallInTailPosition of Expression with argument call.
MatchExpressionClause : MatchPattern : Expression
  1. Return HasCallInTailPosition of Expression with argument call.

20 Fundamental Objects

20.1 Object Objects

20.1.2 Properties of the Object Constructor

20.1.2.24 Object [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. If subject is not an Object, return false.
  3. Return true.

20.2 Function Objects

20.2.2 Properties of the Function Constructor

20.2.2.2 Function [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. Return IsCallable(subject).

20.2.3 Properties of the Function Prototype Object

20.2.3.7 Function.prototype [ %Symbol.customMatcher% ] ( subject, hint, receiver )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint).
  2. Let func be the this value.
  3. If IsCallable(func) is false, throw a TypeError exception.
  4. If subject.[[ConstructedBy]] contains func, return true.
  5. If func does not have a [[IsClassConstructor]] internal slot or func.[[IsClassConstructor]] is false, return ? Call(func, receiver, « subject, hint »).
  6. Return false.
Note
// For non-class functions.
[] is Array.isArray; // true, by Array.isArray(expr)

// For objects created by `new`, it uses private-field-like semantics.
class MyError extends Error {}
const myError = new MyError();
myError is MyError; // true
myError is Error; // true
Object.create(MyError.prototype) is MyError; // false

// Also works for normal functions
function ES5StyleClass() {}
new ES5StyleClass() is ES5StyleClass; // true
Object.create(ES5StyleClass.prototype) is ES5StyleClass; // false
Editor's Note

This does not work with ES5 style class inherit.

function MyError() {
  Error.call(this);
}
MyError.prototype = Object.create(Error.prototype);
var error = new MyError();
error is MyError; // true
error is Error; // false
Editor's Note

Not everyone in the champion group agrees with private-field-like brand check semantics.

There are performance concerns, "hackable" concerns, and interaction with %Symbol.hasInstance% concerns.

Another approach is to use the instanceof semantics.

20.3 Boolean Objects

20.3.2 Properties of the Boolean Constructor

20.3.2.2 Boolean [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint).
  2. If subject is not a Boolean and does not have a [[BooleanData]] internal slot, return false.
  3. If hint is "boolean", return true.
  4. If subject is a Boolean, return CreateArrayFromListsubject »).
  5. Return CreateArrayFromListsubject.[[BooleanData]] »).
Editor's Note
Another approach is to ignore boxed primitives and only match primitive values.

20.4 Symbol Objects

20.4.2 Properties of the Symbol Constructor

20.4.2.17 Symbol.customMatcher

The initial value of Symbol.customMatcher is the well-known symbol %Symbol.customMatcher% (Table 1).

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.

20.4.2.18 Symbol [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint).
  2. If subject is not a Symbol and does not have a [[SymbolData]] internal slot, return false.
  3. If hint is "boolean", return true.
  4. If subject is a Symbol, return CreateArrayFromListsubject »).
  5. Return CreateArrayFromListsubject.[[SymbolData]] »).
Editor's Note
Another approach is to ignore boxed primitives and only match primitive values.

20.5 Error Objects

20.5.1 The Error Constructor

20.5.1.1 Error ( message [ , options ] )

This function performs the following steps when called:

  1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
  2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%Error.prototype%", « [[ErrorData]] »).
  3. Set O.[[ErrorData]] to "Error".
  4. If message is not undefined, then
    1. Let msg be ? ToString(message).
    2. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
  5. Perform ? InstallErrorCause(O, options).
  6. Return O.

20.5.2 Properties of the Error Constructor

20.5.2.2 Error [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. If subject does not have a [[ErrorData]] internal slot, return false.
  3. Return true.
Editor's Note
It is possible to provide extractor semantics for Error matchers.
if (expr is Error(let message, { let cause })) {}

20.5.4 Properties of Error Instances

Error instances are ordinary objects that inherit properties from the Error prototype object and have an [[ErrorData]] internal slot whose value is undefined a String. The only specified uses of [[ErrorData]] is to identify Error, AggregateError, and NativeError instances as Error objects within Object.prototype.toString and their %Symbol.customMatcher% methods.

Editor's Note
Rename this internal slot to [[ErrorKind]].

20.5.6 NativeError Object Structure

20.5.6.1 The NativeError Constructors

20.5.6.1.1 NativeError ( message [ , options ] )

Each NativeError function performs the following steps when called:

  1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
  2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »).
  3. Set O.[[ErrorData]] to NativeError.
  4. If message is not undefined, then
    1. Let msg be ? ToString(message).
    2. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
  5. Perform ? InstallErrorCause(O, options).
  6. Return O.

20.5.6.2 Properties of the NativeError Constructors

20.5.6.2.2 NativeError [ %Symbol.customMatcher% ] ( subject, hint )

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. If subject does not have a [[ErrorData]] internal slot or subject.[[ErrorData]] is not NativeError, return false.
  3. Return true.

20.5.6.4 Properties of NativeError Instances

NativeError instances are ordinary objects that inherit properties from their NativeError prototype object and have an [[ErrorData]] internal slot whose value is undefined a String. The only specified use of [[ErrorData]] is by Object.prototype.toString (20.1.3.6) and their %Symbol.customMatcher% methods to identify Error, AggregateError, or NativeError instances.

20.5.7 AggregateError Objects

20.5.7.1 The AggregateError Constructor

20.5.7.1.1 AggregateError ( errors, message [ , options ] )

This function performs the following steps when called:

  1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
  2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%AggregateError.prototype%", « [[ErrorData]] »).
  3. Set O.[[ErrorData]] to "AggregateError".
  4. If message is not undefined, then
    1. Let msg be ? ToString(message).
    2. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
  5. Perform ? InstallErrorCause(O, options).
  6. Let errorsList be ? IteratorToList(? GetIterator(errors, sync)).
  7. Perform ! DefinePropertyOrThrow(O, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errorsList) }).
  8. Return O.

20.5.7.2 Properties of the AggregateError Constructor

20.5.7.2.2 AggregateError [ %Symbol.customMatcher% ] ( subject, hint )

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. If subject does not have a [[ErrorData]] internal slot or subject.[[ErrorData]] is not "AggregateError", return false.
  3. Return true.

20.5.7.4 Properties of AggregateError Instances

AggregateError instances are ordinary objects that inherit properties from their AggregateError prototype object and have an [[ErrorData]] internal slot whose value is undefined a String. The only specified use of [[ErrorData]] is by Object.prototype.toString (20.1.3.6) and their %Symbol.customMatcher% methods to identify Error, AggregateError, or NativeError instances.

21 Numbers and Dates

21.1 Number Objects

21.1.2 Properties of the Number Constructor

21.1.2.16 Number [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint).
  2. If subject is not a Number and does not have a [[NumberData]] internal slot, return false.
  3. If hint is "boolean", return true.
  4. If subject is a Number, return CreateArrayFromListsubject »).
  5. Return CreateArrayFromListsubject.[[NumberData]] »).
Editor's Note
Another approach is to ignore boxed primitives and only match primitive values.

21.2 BigInt Objects

21.2.2 Properties of the BigInt Constructor

21.2.2.4 BigInt [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint).
  2. If subject is not a BigInt and does not have a [[BigIntData]] internal slot, return false.
  3. If hint is "boolean", return true.
  4. If subject is a BigInt, return CreateArrayFromListsubject »).
  5. Return CreateArrayFromListsubject.[[BigIntData]] »).
Editor's Note
Another approach is to ignore boxed primitives and only match primitive values.

21.4 Date Objects

21.4.3 Properties of the Date Constructor

21.4.3.5 Date [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. If subject does not have a [[DateValue]] internal slot, return false.
  3. Return true.

22 Text Processing

22.1 String Objects

22.1.2 Properties of the String Constructor

22.1.2.5 String [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint).
  2. If subject is not a String and does not have a [[StringData]] internal slot, return false.
  3. If hint is "boolean", return true.
  4. If subject is a String, return CreateArrayFromListsubject »).
  5. Return CreateArrayFromListsubject.[[StringData]] »).
Editor's Note
Another approach is to ignore boxed primitives and only match primitive values.

22.2 RegExp (Regular Expression) Objects

22.2.5 Properties of the RegExp Constructor

22.2.5.3 RegExp [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. If ? IsRegExp(subject) is false, return false.
  3. Return true.
Note
IsRegExp returns true for objects that have a truthy %Symbol.match% property. Do we want this?

22.2.6 Properties of the RegExp Prototype Object

22.2.6.20 RegExp.prototype [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint).
  2. Let regexp be this value.
  3. If hint is "boolean", return ? Call(? Get(regexp, "test"), regexp, « subject »).
  4. Let isRegExp be ? IsRegExp(regexp).
  5. If isRegExp is true, then
    1. Let flags be ? Get(regexp, "flags").
    2. Perform ? RequireObjectCoercible(flags).
    3. If ? ToString(flags) contains g, then
      1. Let iterator be ? Call(? Get(regexp, %Symbol.matchAll%), regexp, « subject »).
      2. Let array be ? Call(%Array.from%, %Array%, « iterator »).
      3. If ! LengthOfArrayLike(array) is 0, return false.
      4. Return array.
  6. Let result be ? Call(? Get(regexp, %Symbol.match%), regexp, « subject »).
  7. If result is null, return false.
  8. Return CreateArrayFromListresult »).
Note
let regex = /(?<id>\d+)-?/g
'012-345' is regex(["012-", "012"], { groups: { id: "345" } });
// true, match with %Symbol.matchAll%

let regex2 = /(?<id>\d+)-?/
'012-345' is regex({ groups: { id: "012" } });
// true, match with %Symbol.match%
Editor's Note
The "flags" property in this algorithm is accessed twice, this is not ideal. Another access is in RegExp.prototype [ %Symbol.matchAll% ] ( string ) or RegExp.prototype [ %Symbol.match% ] ( string ) depends on if the regexp has the g flag.
Editor's Note
When matching with a RegExp that "flags" contains "g", both %RegExpStringIteratorPrototype%.next ( ) and %ArrayIteratorPrototype%.next ( ) are called, this is not ideal.

23 Indexed Collections

23.1 Array Objects

23.1.2 Properties of the Array Constructor

23.1.2.6 Array [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint).
  2. If ? IsArray(subject) is false, return false.
  3. If hint is "boolean", return true.
  4. Return subject.
Note
if (expr is Array(1, 2, 3, 4)) {}

23.2 TypedArray Objects

23.2.6 Properties of the TypedArray Constructors

23.2.6.3 TypedArray [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint).
  2. If subject does not have a [[TypedArrayName]] internal slot or subject.[[TypedArrayName]] is not TypedArray, return false.
  3. If hint is "boolean", return true.
  4. Return subject.
Note
const isPNG = binary is Uint8Array(
  0x89, 0x50, 0x4E, 0x47,
  0x0D, 0x0A, 0x1A, 0x0A, ...
); // the ... is necessary otherwise it will only match a length-8 binary.

24 Keyed Collections

24.1 Map Objects

24.1.2 Properties of the Map Constructor

24.1.2.3 Map [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint).
  2. If subject does not have a [[MapData]] internal slot, return false.
  3. If hint is "boolean", return true.
  4. Return CreateArrayFromListsubject »).
Note
expr is Map([[1, 2], [3, 4]]);
// matches new Map([[1, 2], [3, 4]])
// but not new Map([[3, 4], [1, 2]])
Editor's Note
A Map can be iterated, but is it meaningful to do so in pattern matching?

24.2 Set Objects

24.2.2 Properties of the Set Constructor

24.2.2.3 Set [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint).
  2. If subject does not have a [[SetData]] internal slot, return false.
  3. If hint is "boolean", return true.
  4. Return CreateArrayFromListsubject »).
Note
expr is Set([1, 2, 3]);
// matches new Set([1, 2, 3])
// but not new Set([3, 2, 1])
Editor's Note
A Set can be iterated, but is it meaningful to do so in pattern matching?

24.3 WeakMap Objects

24.3.2 Properties of the WeakMap Constructor

24.3.2.2 WeakMap [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. If subject does not have a [[WeakMapData]] internal slot, return false.
  3. Return true.

24.4 WeakSet Objects

24.4.2 Properties of the WeakSet Constructor

24.4.2.2 WeakSet [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. If subject does not have a [[WeakSetData]] internal slot, return false.
  3. Return true.

25 Structured Data

25.1 ArrayBuffer Objects

25.1.5 Properties of the ArrayBuffer Constructor

25.1.5.4 ArrayBuffer [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. If subject has a [[ArrayBufferData]] internal slot, return true.
  3. Return false.

25.2 SharedArrayBuffer Objects

25.2.4 Properties of the SharedArrayBuffer Constructor

25.2.4.3 SharedArrayBuffer [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. If subject does not have a [[ArrayBufferData]] internal slot, return false.
  3. Return IsSharedArrayBuffer(subject).

25.3 DataView Objects

25.3.3 Properties of the DataView Constructor

25.3.3.2 DataView [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. If subject does not have a [[DataView]] internal slot, return false.
  3. Return true.

26 Managing Memory

26.1 WeakRef Objects

26.1.2 Properties of the WeakRef Constructor

26.1.2.2 WeakRef [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint).
  2. If subject does not have a [[WeakRefTarget]] internal slot, return false.
  3. If hint is "boolean", return true.
  4. Return CreateArrayFromListWeakRefDeref(subject) »).
Note
if (expr is WeakRef(let object)) {}

26.2 FinalizationRegistry Objects

26.2.2 Properties of the FinalizationRegistry Constructor

26.2.2.2 FinalizationRegistry [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. If subject does not have a [[CleanupCallback]] internal slot, return false.
  3. Return true.

27 Control Abstraction Objects

27.2 Promise Objects

27.2.4 Properties of the Promise Constructor

27.2.4.9 Promise [ %Symbol.customMatcher% ] ( subject, hint )

This function performs the following steps when called:

  1. Perform ? ValidateCustomMatcherHint(hint, boolean).
  2. Return IsPromise(subject).

28 Reflection

28.2 Proxy Objects

28.2.1 The Proxy Constructor

28.2.1.2 Proxy [ %Symbol.customMatcher% ] ( )

This function performs the following steps when called:

  1. Throw a TypeError exception.
Editor's Note

All built-in constructors have a %Symbol.customMatcher% method. This one is for design consistency.

This is not a consensus in the champion group.

30 Pattern Matching

30.1 Match Patterns

Syntax

MatchPattern[Yield, Await] : ( MatchPattern[?Yield, ?Await] ) PrimitivePattern VariableDeclarationPattern[?Yield, ?Await] MemberExpressionPattern[?Yield, ?Await] ObjectPattern[?Yield, ?Await] ArrayPattern[?Yield, ?Await] UnaryAlgebraicPattern[?Yield, ?Await] RelationalPattern[?Yield, ?Await] IfPattern[?Yield, ?Await] CombinedMatchPattern[?Yield, ?Await] Editor's Note
MatchPattern : void

This production will be added by discard bindings proposal.

Some of the committe members prefer _ as the discard binding identifier.

if (expr is [let x, void, void]) {}
// can be written as:
if (expr is [let x,,,]) {}
// or
if (expr is [let x, let _y, let _z,]) {}
PrimitivePattern : Literal NoSubstitutionTemplate Note 1
if (value is null) {};
if (value is true) {};
if (value is 1_000_000) {};
if (value is "string") {};
if (value is `line 1
line 2`) {};
Editor's Note

Preserve the interpolation syntax for (1) concerns about arbitrary expression interpolation and (2) intuitive string matching.

For example, match "com.example:method()" by

if (qualifiedName is `${isPackageName and let pkg}:${isIdentifier and let method}()`) {}
VariableDeclarationPattern[Yield, Await] : VarOrLetOrConst BindingIdentifier[?Yield, ?Await] VarOrLetOrConst : var LetOrConst Note 2
if (expr is { kind: let kind }) {}
return match (expr) {
  [let left, let op, let right]: evaluate(op, left, right);
};
MemberExpressionPattern[Yield, Await] : PatternMatchingMemberExpression[?Yield, ?Await] PatternMatchingMemberExpression[?Yield, ?Await] ( MatchList[?Yield, ?Await] ) PatternMatchingMemberExpression[Yield, Await] : this MetaProperty IdentifierReference[?Yield, ?Await] super . IdentifierName PatternMatchingMemberExpression[?Yield, ?Await] . IdentifierName PatternMatchingMemberExpression[?Yield, ?Await] . PrivateIdentifier PatternMatchingMemberExpression[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ] Note 3
if (expr is { x: undefined, y: undefined }) {}
if (expr is { angle: Math.PI, let length }) {}
if (expr is { ok: true, value: this.#lastValue }) {}
if (expr === import.meta?.hot?.data && expr is { let previousData }) {}
if (expr is WeakRef(let object)) {}
Editor's Note
This is the minimal requirement. All other productions can be removed. PatternMatchingMemberExpression : IdentifierReference PatternMatchingMemberExpression . IdentifierName
if (value is { version: 2, data: this.#data }) {};
// can be written as:
const data = this.#data;
if (expr is { version: 2, data: data }) {}

It is similar to the DecoratorMemberExpression.

Should we allow MemberExpressionPattern[expr] to match with computed property names?

Editor's Note
It is possible to add the following productions: PatternMatchingMemberExpression : PatternMatchingMemberExpression ?. IdentifierName PatternMatchingMemberExpression ?. PrivateIdentifier

It does not clear if this should match undefined or fail the match if the PatternMatchingMemberExpression is undefined.

ObjectPattern[Yield, Await] : { } { MatchRestProperty[?Yield, ?Await] } { MatchPropertyList[?Yield, ?Await] ,opt } { MatchPropertyList[?Yield, ?Await] , MatchRestProperty[?Yield, ?Await] } ArrayPattern[Yield, Await] : [ MatchList[?Yield, ?Await]opt ] MatchList[Yield, Await] : Elisionopt MatchRestElement[?Yield, ?Await]opt MatchElementList[?Yield, ?Await] MatchElementList[?Yield, ?Await] , Elisionopt MatchRestElement[?Yield, ?Await]opt MatchRestProperty[Yield, Await] : ... MatchPattern[?Yield, ?Await] MatchPropertyList[Yield, Await] : MatchProperty[?Yield, ?Await] MatchPropertyList[?Yield, ?Await] , MatchProperty[?Yield, ?Await] MatchElementList[Yield, Await] : MatchElisionElement[?Yield, ?Await] MatchElementList[?Yield, ?Await] , MatchElisionElement[?Yield, ?Await] MatchElisionElement[Yield, Await] : Elisionopt MatchElement[?Yield, ?Await] MatchProperty[Yield, Await] : PropertyName[?Yield, ?Await] ?opt PropertyName[?Yield, ?Await] ?opt : MatchPattern[?Yield, ?Await] VarOrLetOrConst BindingIdentifier[?Yield, ?Await] ?opt VarOrLetOrConst BindingIdentifier[?Yield, ?Await] ?opt : MatchPattern[?Yield, ?Await] MatchElement[Yield, Await] : MatchPattern[?Yield, ?Await] ?opt MatchRestElement[Yield, Await] : ... ... MatchPattern[?Yield, ?Await] Note 4

Object matchers

if (expr is { version: 2, type: String, value?: void }) {}
// test if subject[Symbol.iterator] is present.
if (expr is { [Symbol.iterator]?: Function }) {}
if (expr is { 0: String }) {}

Array matchers

if (expr is ["request", let method, ...let params]) {}
// match an iterable that first 2 values match a and b and no more values.
if (expr is [a, b]) {}
// match an iterable that first 2 values match a and b.
if (expr is [a, b, ...]) {}
Editor's Note
It is possible to add PrivateIdentifier to MatchProperty.
class MyClass {
  #field;
  test(data) {
    if (data is { value: { #field: let field } }) {}
    // can be written as
    if (data is { let value: MyClass }) {
      let field = value.#field;
    }
  }
}
Editor's Note
It is possible to add Initializer to MatchProperty and MatchElement.
if (expr is { type: "report", let key }) { reportKey(key); }
else { reportKey(Symbol.for("missing")) };
// can be written as
if (expr is { type: "report", let key = Symbol.for("missing") }) { reportKey(key); }
Editor's Note

The ? in the production MatchProperty or MatchElement can be removed.

The feature is not a consensus in the champion group yet.

if (expr is { let x, let y? }) {}
// can be written as
if (expr is { let x }) {}
else if (expr is { let x, let y }) {}

if (expr is [x, y?]) {}
// can be written as
if (expr is [x]) {}
else if (expr is [x, y]) {}
Editor's Note
MatchProperty : VarOrLetOrConst BindingIdentifier ?opt : MatchPattern VarOrLetOrConst BindingIdentifier ?opt

These productions can be removed.

This feature is not a consensus in the champion group yet.

if (expr is { version: 2, let y: String }) {}
// can be written as
if (expr is { version: 2, y: String and let y }) {}
Editor's Note
MatchProperty : PropertyName ?opt

Computed properties can be removed from this production, but it will be harder to match computed properties.

if (expr is { let length, [Symbol.iterator]: Function }) {}
// can be written as
function isIterable(value) { return typeof value[Symbol.iterator] === "function"; }
if (expr is { let length } and isIterable) {}
UnaryAlgebraicPattern[Yield, Await] : PatternMatchingUnaryAlgebraicExpression[?Yield, ?Await] PatternMatchingUnaryAlgebraicExpression[Yield, Await] : + PatternMatchingMemberExpression[?Yield, ?Await] - PatternMatchingMemberExpression[?Yield, ?Await] + NumericLiteral - NumericLiteral Note 5
if (expr is -0) {} // not matching `+0` and vice versa
if (expr is { x: -Infinity, y: -Infinity }) {}
if (expr is { angle: -Math.PI, let length }) {}
Editor's Note

Only literal +0 or -0 will be matched with SameValue, +x and -x where x is 0 will be matched with SameValueZero.

Editor's Note
This feature can be removed, but it will be harder to handle with +0 and -0. The code example above can be written as:
const negPi = -Math.PI;
if (Object.is(expr, -0)) {} // not matching `+0` and vice versa
if (expr is { x: Number.NEGATIVE_INFINITY, y: Number.NEGATIVE_INFINITY }) {}
if (expr is { angle: negPi, let length }) {}
RelationalPattern[Yield, Await] : < PatternMatchingRelationalExpression[?Yield, ?Await] > PatternMatchingRelationalExpression[?Yield, ?Await] <= PatternMatchingRelationalExpression[?Yield, ?Await] >= PatternMatchingRelationalExpression[?Yield, ?Await] instanceof PatternMatchingMemberExpression[?Yield, ?Await] in PatternMatchingMemberExpression[?Yield, ?Await] == PatternMatchingRelationalExpression[?Yield, ?Await] != PatternMatchingRelationalExpression[?Yield, ?Await] === PatternMatchingRelationalExpression[?Yield, ?Await] !== PatternMatchingRelationalExpression[?Yield, ?Await] PatternMatchingRelationalExpression[Yield, Await] : Literal NoSubstitutionTemplate PatternMatchingMemberExpression[?Yield, ?Await] PatternMatchingUnaryAlgebraicExpression[?Yield, ?Await] Note 6
if (expr is > -10 and < 10) {}
if (expr is { value: instanceof Error and { let message } }) {}
if (expr is [=== Array, === Object]) {}
Editor's Note

It is possible to add the following production.

RelationalPattern[Yield, Await] : typeof == PatternMatchingStringLikeExpression[?Yield, ?Await] typeof != PatternMatchingStringLikeExpression[?Yield, ?Await] typeof === PatternMatchingStringLikeExpression[?Yield, ?Await] typeof !== PatternMatchingStringLikeExpression[?Yield, ?Await] PatternMatchingStringLikeExpression[Yield, Await] : StringLiteral NoSubstitutionTemplate PatternMatchingMemberExpression[?Yield, ?Await]

The recommended way to match a String is to use the String [ %Symbol.customMatcher% ] ( subject, hint ), but it can be replaced by a custom implementation. This production adds the ability to do the unforgeable typeof test.

if (expr is { version: 2 or 3, value: String }) {}
// can be written as this if built-ins might be replaced:
const isString = {
  [Symbol.customMatcher](val) {
    return typeof val === "string";
  }
};
if (expr is { version: 2 or 3, value: isString }) {}
Editor's Note

It is possible to add the following production.

RelationalPattern[Yield, Await] : has PatternMatchingStringLikeExpression[?Yield, ?Await] has PrivateIdentifier
function hasPrivateField(val) { return #field in val; }
if (expr is { version: 2 or 3, data: hasPrivateField }) {}
// can be written as:
if (expr is { version: 2 or 3, data: has #field }) {}

This feature can be a proposal on its own, to be the in operator reversed.

if (expr has #field) { }
if (expr hasOwn "prototype") { }
Editor's Note

The instanceof production can be removed, but it will be harder to match by instanceof semantics.

This feature is not a consensus in the champion group.

The class matchers match by private-field semantics, the instanceof match is an escape hatch to match with the instanceof semantics.

if (expr is { elements: [instanceof Map, instanceof Map] }) {}
// can be written as:
function isInstanceOfMap(val) { return val instanceof Map; }
if (expr is { elements: [isInstanceOfMap, isInstanceOfMap] }) {}
Editor's Note

The ==, !=, ===, and !== production can be removed, but it will be harder to match by === semantics when the PatternMatchingRelationalExpression is a custom matcher.

This feature is not a consensus in the champion group.

if (expr is { type: "create", initializers: [createX] }) {}

The code above will try to call createX as a custom matcher. This feature is an opt-out of the custom matcher, to always do the === check.

if (expr is { type: "create", initializers: [=== createX] }) {}
// can be written as:
function isCreateX(val) { return val === createX; }
if (expr is { type: "create", initializers: [isCreateX] }) {}
Editor's Note

The in production can be removed, but it will be harder to match by in semantics.

This feature is not a consensus in the champion group.

if (expr is { let key: in object } and inCache) {}
// can be written as:
function isInObj(key) { return key in object; }
if (expr is { let key: isInObj } and inCache) {}
Editor's Note

The >, <, >= and <= production can be removed, but it will be harder to match numbers.

This feature is not a consensus in the champion group.

if (expr is [> -Math.PI and < Math.PI, > 0]) {}
// can be written as:
if (expr[0] > -Math.PI && expr[0] < Math.PI && expr[1] > 0) {}
IfPattern[Yield, Await] : if ( Expression[+In, ?Yield, ?Await] ) Note 7
return match (expr) {
  { let x, let y, let z } and if (norm(x, y, z) < maxLength): [x, y];
  { let x, let y } and if (norm(x, y) < maxLength): [x, y];
  Number and < maxLength let length: [length];
}
Editor's Note
This feature can be removed, but this is designed as an escape hatch. No alternative is available for this feature.
CombinedMatchPattern[Yield, Await] : MatchPattern[?Yield, ?Await] and MatchPattern[?Yield, ?Await] MatchPattern[?Yield, ?Await] or MatchPattern[?Yield, ?Await] not MatchPattern[?Yield, ?Await] Note 8
if (expr is { version: 2 or 3 }) {}
if (expr is { version: Number and not 1 }) {}

30.1.1 Static Semantics: Early Errors

PrimitivePattern : NoSubstitutionTemplate VariableDeclarationPattern : VarOrLetOrConst BindingIdentifier MatchProperty : PropertyName ?opt PropertyName ?opt : MatchPattern Note 1
if (expr is { __proto__: null, property?: void }) {} // Syntax Error
if (expr is { "__proto__": null, property?: void }) {} // Syntax Error
if (expr is { ["__proto__"]: null, property?: void }) {} // no Syntax Error
MatchRestElement : ... MatchPattern MatchRestElement : ... MatchPattern Note 2
if (expr is { x: 0, y: 0, ...rest }) {} // Syntax Error
if (expr is { x: 0, y: 0, ...let rest }) {} // no Syntax Error, bind rest properties to _rest_
if (expr is { x: 0, y: 0, ...(isEmpty) }) {} // no Syntax Error, call isEmpty with the rest object
MatchProperty : PropertyName[?Yield, ?Await] ?opt PropertyName[?Yield, ?Await] ?opt : MatchPattern[?Yield, ?Await] Note 3
if (expr is { x, y, z }) {} // Syntax Error
if (expr is { x: void, y: void, z: void }) {} // no Syntax Error
if (expr is { let x, let y, let z }) {} // no Syntax Error

if (expr is { if }) {} // no Syntax Error
MatchList : MatchElementList , Elisionopt MatchRestElementopt MatchElementList : MatchElementList , MatchElisionElement Note 4
value is [1, 2?, 3]; // Syntax Error
value is [1, 2?, 3?]; // no Syntax Error

value is [1, 2?, , ]; // Syntax Error (Elision)
value is [1, 2?, void?, ]; // no Syntax Error

value is [1, 2?, ...]; // no Syntax Error
CombinedMatchPattern : MatchPattern and MatchPattern CombinedMatchPattern : MatchPattern or MatchPattern CombinedMatchPattern : not MatchPattern Note 5
value is a and b and c; // no Syntax Error
value is a or b or c; // no Syntax Error

value is a and b or c; // Syntax Error
value is (a and b) or c; // no Syntax Error
value is a and (b or c); // no Syntax Error

value is not not a; // Syntax Error
value is not (not a); // no Syntax Error

value is not a or b; // Syntax Error
value is not (a or b); // no Syntax Error

30.1.2 Static Semantics: IsOptionalPattern

The syntax-directed operation IsOptionalPattern takes no arguments and returns a Boolean. It is defined piecewise over the following productions:

MatchElementList : MatchElisionElement
  1. Return IsOptionalPattern of MatchElisionElement.
MatchElementList : MatchElementList , MatchElisionElement
  1. If IsOptionalPattern of MatchElementList is true, return true.
  2. Return IsOptionalPattern of MatchElisionElement.
MatchElisionElement : Elisionopt MatchElement
  1. If Elision is present, return false.
  2. Return IsOptionalPattern of MatchElement.
MatchElement : MatchPattern ?opt
  1. If ? is present, return true.
  2. Return false.

30.1.3 Runtime Semantics: MatchPatternMatches

The syntax-directed operation MatchPatternMatches takes arguments subject (an ECMAScript language value) and cacheGroup (a %Map%) and returns either a normal completion containing a Boolean or an abrupt completion. It is defined piecewise over the following productions:

MatchPattern : ( MatchPattern )
  1. Return ? MatchPatternMatches of MatchPattern with arguments subject and cacheGroup.
MatchPattern : PrimitivePattern
  1. Return PrimitivePatternMatches of PrimitivePattern with argument subject.
MatchPattern : VariableDeclarationPattern
  1. Return VariableDeclarationPatternMatches of VariableDeclarationPattern with argument subject.
MatchPattern : MemberExpressionPattern
  1. Return ? MemberExpressionPatternMatches of MemberExpressionPattern with arguments subject and cacheGroup.
MatchPattern : ObjectPattern
  1. Return ? ObjectPatternMatches of ObjectPattern with arguments subject and cacheGroup.
MatchPattern : ArrayPattern
  1. Return ? ArrayPatternMatches of ArrayPattern with arguments subject and cacheGroup.
MatchPattern : UnaryAlgebraicPattern
  1. Return ? UnaryAlgebraicPatternMatches of UnaryAlgebraicPattern with argument subject.
MatchPattern : RelationalPattern
  1. Return ? RelationalPatternMatches of RelationalPattern with arguments subject.
MatchPattern : IfPattern
  1. Return ? IfPatternMatches of IfPattern.
MatchPattern : CombinedMatchPattern
  1. Return ? CombinedMatchPatternMatches of CombinedMatchPattern with arguments subject and cacheGroup.

30.1.4 Runtime Semantics: PrimitivePatternMatches

The syntax-directed operation PrimitivePatternMatches takes argument subject (an ECMAScript language value) and returns a Boolean. It is defined piecewise over the following productions:

PrimitivePattern : Literal
  1. Return SameValueZero(subject, ! Evaluation of Literal).
PrimitivePattern : NoSubstitutionTemplate
  1. Return SameValueNonNumber(subject, ! Evaluation of NoSubstitutionTemplate).

30.1.5 Runtime Semantics: VariableDeclarationPatternMatches

The syntax-directed operation VariableDeclarationPatternMatches takes argument subject (an ECMAScript language value) and returns a Boolean. It is defined piecewise over the following productions:

VariableDeclarationPattern : VarOrLetOrConst BindingIdentifier
  1. TODO: This section is not written in the spec language yet.
  2. Let name be StringValue of BindingIdentifier.
  3. Initialize variable name with subject with the semantics of var, let or const based on VarOrLetOrConst.
  4. Return true.

30.1.6 Runtime Semantics: MemberExpressionPatternMatches

The syntax-directed operation MemberExpressionPatternMatches takes arguments subject (an ECMAScript language value) and cacheGroup (a %Map%) and returns either a normal completion containing a Boolean or an abrupt completion. It is defined piecewise over the following productions:

MemberExpressionPattern : PatternMatchingMemberExpression
  1. Let expr be the LeftHandSideExpression that is covered by PatternMatchingMemberExpression.
  2. Let ref be ? Evaluation of expr.
  3. Let value be ? GetValue(ref).
  4. If ref is a Reference Record and IsPropertyReference(ref) is true, let receiver be GetThisValue(ref).
  5. Else, let receiver be null.
  6. Let result be ? InvokeCustomMatcher(value, subject, cacheGroup, boolean, receiver).
  7. Assert: result is a Boolean.
  8. Return result.
MemberExpressionPattern : PatternMatchingMemberExpression ( MatchListopt )
  1. Let expr be the LeftHandSideExpression that is covered by PatternMatchingMemberExpression.
  2. Let ref be ? Evaluation of expr.
  3. Let value be ? GetValue(ref).
  4. If ref is a Reference Record and IsPropertyReference(ref) is true, let receiver be GetThisValue(ref).
  5. Else, let receiver be null.
  6. Let listMatchResult be ? InvokeCustomMatcher(value, subject, cacheGroup, list, receiver).
  7. If listMatchResult is false, return false.
  8. Assert: listMatchResult is an Iterator Record.
  9. If MatchList is not present, then
    1. Return ? FinishListMatch(listMatchResult, cacheGroup, 0).
  10. Return ? ListPatternMatches of MatchList with arguments listMatchResult and cacheGroup.

30.1.7 Runtime Semantics: ObjectPatternMatches

The syntax-directed operation ObjectPatternMatches takes arguments subject (an ECMAScript language value) and cacheGroup (a %Map%) and returns either a normal completion containing a Boolean or an abrupt completion. It is defined piecewise over the following productions:

ObjectPattern : { }
  1. If subject is an Object, return true.
  2. Return false.
ObjectPattern : { MatchRestProperty }
  1. If subject is not an Object, return false.
  2. Let matches be ? ObjectPatternInnerMatches of MatchRestProperty with arguments subject, cacheGroup and a new empty List.
  3. If matches is not-matched, return false.
  4. Return true.
ObjectPattern : { MatchPropertyList ,opt }
  1. If subject is not an Object, return false.
  2. Let matches be ? ObjectPatternInnerMatches of MatchPropertyList with arguments subject, cacheGroup and a new empty List.
  3. If matches is not-matched, return false.
  4. Return true.
ObjectPattern : { MatchPropertyList , MatchRestProperty }
  1. If subject is not an Object, return false.
  2. Let matches be ? ObjectPatternInnerMatches of MatchPropertyList with arguments subject, cacheGroup and a new empty List.
  3. If matches is not-matched, return false.
  4. If MatchRestProperty is not present, return true.
  5. Let restMatches be ? ObjectPatternInnerMatches of MatchRestProperty with arguments subject, cacheGroup and matches.
  6. If restMatches is not-matched, return false.
  7. Return true.

30.1.8 Runtime Semantics: ObjectPatternInnerMatches

The syntax-directed operation ObjectPatternInnerMatches takes arguments subject (an Object), cacheGroup (a %Map%), and excludedNames (a List of String) and returns either a normal completion containing either a List of String or not-matched, or an abrupt completion. It is defined piecewise over the following productions:

MatchRestProperty : ... MatchPattern
  1. Let restObj be OrdinaryObjectCreate(%Object.prototype%).
  2. Perform ? CopyDataProperties(restObj, subject, excludedNames).
  3. Let matches be ? MatchPatternMatches of MatchPattern with arguments restObj and cacheGroup.
  4. If matches is false, return not-matched.
  5. Return « ».
MatchPropertyList : MatchProperty
  1. Return ? ObjectPatternInnerMatches of MatchProperty with arguments subject, cacheGroup and excludedNames.
MatchPropertyList : MatchPropertyList , MatchProperty
  1. Let matches be ? ObjectPatternInnerMatches of MatchProperty with arguments subject, cacheGroup and excludedNames.
  2. If matches is not-matched, return not-matched.
  3. Let propMatches be ? ObjectPatternInnerMatches of MatchPropertyList with arguments subject, cacheGroup and matches.
  4. If propMatches is not-matched, return not-matched.
  5. Assert: matches and propMatches are Lists of String.
  6. Return the list-concatenation of matches and propMatches.
MatchProperty : PropertyName ?opt
  1. Let propertyName be ? Evaluation of PropertyName.
  2. If ? HasPropertyCached(subject, cacheGroup, propertyName) is false, then
    1. If ? is present, return « ».
    2. Return not-matched.
  3. Return « propertyName ».
MatchProperty : PropertyName ?opt : MatchPattern
  1. Let propertyName be ? Evaluation of PropertyName.
  2. If ? HasPropertyCached(subject, cacheGroup, propertyName) is false, then
    1. If ? is present, return « ».
    2. Return not-matched.
  3. Let value be ? GetCached(subject, cacheGroup, propertyName).
  4. Let matches be ? MatchPatternMatches of MatchPattern with arguments value and cacheGroup.
  5. If matches is false, return not-matched.
  6. Return « propertyName ».
MatchProperty : VarOrLetOrConst BindingIdentifier ?opt
  1. TODO: This section is not written in the spec language yet.
  2. Let propertyName be StringValue of BindingIdentifier.
  3. If ? HasPropertyCached(subject, cacheGroup, propertyName) is false, then
    1. If ? is present, return « ».
    2. Return not-matched.
  4. Let value be ? GetCached(subject, cacheGroup, propertyName).
  5. Initialize variable propertyName with value with the semantics of var, let or const based on VarOrLetOrConst.
  6. Return « propertyName ».
MatchProperty : VarOrLetOrConst BindingIdentifier ?opt : MatchPattern
  1. TODO: This section is not written in the spec language yet.
  2. Let propertyName be StringValue of BindingIdentifier.
  3. If ? HasPropertyCached(subject, cacheGroup, propertyName) is false, then
    1. If ? is present, return « ».
    2. Return not-matched.
  4. Let value be ? GetCached(subject, cacheGroup, propertyName).
  5. Let matches be ? MatchPatternMatches of MatchPattern with arguments value and cacheGroup.
  6. If matches is false, return not-matched.
  7. Initialize variable propertyName with value with the semantics of var, let or const based on VarOrLetOrConst.
  8. Return « propertyName ».

30.1.9 Runtime Semantics: ArrayPatternMatches

The syntax-directed operation ArrayPatternMatches takes arguments subject (an ECMAScript language value) and cacheGroup (a %Map%) and returns either a normal completion containing a Boolean or an abrupt completion. It is defined piecewise over the following productions:

ArrayPattern : [ MatchListopt ]
  1. If IsCallable(? GetCached(subject, cacheGroup, %Symbol.iterator%)) is false, return false.
  2. Let iteratorRecord be ? GetIteratorCached(subject, cacheGroup).
  3. If MatchList is not present, then
    1. Return ? FinishListMatch(subject, cacheGroup, 0).
  4. Return ? ListPatternMatches of MatchList with arguments iteratorRecord and cacheGroup.

30.1.10 Runtime Semantics: ListPatternMatches

The syntax-directed operation ListPatternMatches takes arguments iterator (an Iterator Record) and cacheGroup (a %Map%) and returns either a normal completion containing a Boolean or an abrupt completion. It is defined piecewise over the following productions:

MatchList : Elisionopt MatchRestElementopt
  1. Let visitedCount be 0.
  2. If Elision is present, then
    1. If ? GetIteratorNthValueCached(iterator, cacheGroup, 0) is not-matched, return false.
    2. Set visitedCount to 1.
  3. If MatchRestElement is present, then
    1. Let matches be ? ListPatternInnerMatches of MatchRestElement with arguments iterator, cacheGroup and visitedCount.
    2. Return ? FinishListMatch(iterator, cacheGroup, matches).
  4. Return ? FinishListMatch(iterator, cacheGroup, visitedCount).
MatchList : MatchElementList
  1. Let matches be ? ListPatternInnerMatches of MatchElementList with arguments iterator, cacheGroup and 0.
  2. Return ? FinishListMatch(iterator, cacheGroup, matches).
MatchList : MatchElementList , Elisionopt MatchRestElementopt
  1. Let visitedCount be ? ListPatternInnerMatches of MatchElementList with arguments iterator, cacheGroup and 0.
  2. If visitedCount is not-matched, return false.
  3. Assert: visitedCount is a non-negative integer.
  4. If Elision is present, then
    1. If ? GetIteratorNthValueCached(iterator, cacheGroup, visitedCount) is not-matched, return false.
    2. Set visitedCount to visitedCount + 1.
  5. If MatchRestElement is present, then
    1. Let matches be ? ListPatternInnerMatches of MatchRestElement with arguments iterator, cacheGroup and visitedCount.
    2. Return ? FinishListMatch(iterator, cacheGroup, matches).
  6. Return ? FinishListMatch(iterator, cacheGroup, visitedCount).
Note
It is possible to use array-like object or IsArray semantics rather than iterator protocol if there is performance issue reported from the engine.

30.1.11 Runtime Semantics: ListPatternInnerMatches

The syntax-directed operation ListPatternInnerMatches takes arguments iterator (an Iterator Record), cacheGroup (a %Map%), and startIndex (a non-negative integer) and returns either a normal completion containing either a non-negative integer, unlimited or not-matched, or an abrupt completion. It is defined piecewise over the following productions:

MatchElementList : MatchElisionElement
  1. Return ? ListPatternInnerMatches of MatchElisionElement with arguments iterator, cacheGroup and startIndex.
MatchElementList : MatchElementList , MatchElisionElement
  1. Let visitedCount be ? ListPatternInnerMatches of MatchElementList with arguments iterator, cacheGroup and startIndex.
  2. If visitedCount is not-matched, return not-matched.
  3. Assert: visitedCount is a non-negative integer.
  4. Let matches be ? ListPatternInnerMatches of MatchElisionElement with arguments iterator, cacheGroup and startIndex + visitedCount.
  5. If matches is not-matched, return not-matched.
  6. Assert: matches is a non-negative integer.
  7. Return visitedCount + matches.
MatchElisionElement : Elisionopt MatchElement
  1. Let visitedCount be 0.
  2. If Elision is present, then
    1. If ? GetIteratorNthValueCached(iterator, cacheGroup, startIndex) is not-matched, return not-matched.
    2. Set visitedCount to visitedCount + 1.
  3. Let matches be ? ListPatternInnerMatches of MatchElement with arguments iterator, cacheGroup and startIndex + visitedCount.
  4. If matches is not-matched, return not-matched.
  5. Assert: matches is a non-negative integer.
  6. Return visitedCount + matches.
MatchElement : MatchPattern ?opt
  1. Let value be ? GetIteratorNthValueCached(iterator, cacheGroup, startIndex).
  2. If value is not-matched, then
    1. If ? is present, return 1.
    2. Return not-matched.
  3. Let matches be ? MatchPatternMatches of MatchPattern with arguments value and cacheGroup.
  4. If matches is false, return not-matched.
  5. Return 1.
MatchRestElement : ...
  1. Return unlimited.
MatchRestElement : ... MatchPattern
  1. Let elements be a new empty List.
  2. Let i be startIndex.
  3. Let next be unused.
  4. Repeat, while next is not not-matched,
    1. Set next to ? GetIteratorNthValueCached(iterator, cacheGroup, i).
    2. If next is not not-matched, append next to elements.
    3. Set i to i + 1.
  5. Let rest be CreateArrayFromList(elements).
  6. Let matches be ? MatchPatternMatches of MatchPattern with arguments rest and cacheGroup.
  7. If matches is false, return not-matched.
  8. Return unlimited.

30.1.12 Runtime Semantics: UnaryAlgebraicPatternMatches

The syntax-directed operation UnaryAlgebraicPatternMatches takes argument subject (an ECMAScript language value) and returns either a normal completion containing a Boolean or an abrupt completion. It is defined piecewise over the following productions:

UnaryAlgebraicPattern : PatternMatchingUnaryAlgebraicExpression
  1. Let expr be the Expression that is covered by PatternMatchingUnaryAlgebraicExpression.
  2. Let ref be ? Evaluation of expr.
  3. Let value be ? GetValue(ref).
  4. If PatternMatchingUnaryAlgebraicExpression is + NumericLiteral or - NumericLiteral, return SameValue(subject, value).
  5. Return SameValueZero(subject, value).
Editor's Note

Only literal +0 or -0 will be matched with SameValue, +x and -x where x is 0 will be matched with SameValueZero.

30.1.13 Runtime Semantics: RelationalPatternMatches

The syntax-directed operation RelationalPatternMatches takes argument subject (an ECMAScript language value) and returns either a normal completion containing a Boolean or an abrupt completion. It is defined piecewise over the following productions:

RelationalPattern : < PatternMatchingRelationalExpression
  1. If subjectis not a String, a Number or a BigInt, return false.
  2. Let expr be the Expression that is covered by PatternMatchingRelationalExpression.
  3. Let ref be ? Evaluation of expr.
  4. Let value be ? GetValue(ref).
  5. Let r be ? IsLessThan(subject, value, true).
  6. If r is undefined, return false. Otherwise, return r.
RelationalPattern : > PatternMatchingRelationalExpression
  1. If subjectis not a String, a Number or a BigInt, return false.
  2. Let expr be the Expression that is covered by PatternMatchingRelationalExpression.
  3. Let ref be ? Evaluation of expr.
  4. Let value be ? GetValue(ref).
  5. Let r be ? IsLessThan(value, subject, false).
  6. If r is undefined, return false. Otherwise, return r.
RelationalPattern : <= PatternMatchingRelationalExpression
  1. If subjectis not a String, a Number or a BigInt, return false.
  2. Let expr be the Expression that is covered by PatternMatchingRelationalExpression.
  3. Let ref be ? Evaluation of expr.
  4. Let value be ? GetValue(ref).
  5. Let r be ? IsLessThan(subject, value, true).
  6. If r is either true or undefined, return false. Otherwise, return true.
RelationalPattern : >= PatternMatchingRelationalExpression
  1. If subjectis not a String, a Number or a BigInt, return false.
  2. Let expr be the Expression that is covered by PatternMatchingRelationalExpression.
  3. Let ref be ? Evaluation of expr.
  4. Let value be ? GetValue(ref).
  5. Let r be ? IsLessThan(value, subject, false).
  6. If r is either true or undefined, return false. Otherwise, return true.
RelationalPattern : instanceof PatternMatchingMemberExpression
  1. Let expr be the Expression that is covered by PatternMatchingMemberExpression.
  2. Let ref be ? Evaluation of expr.
  3. Let value be ? GetValue(ref).
  4. Return ? InstanceofOperator(subject, value).
RelationalPattern : in PatternMatchingMemberExpression
  1. If subject is not a property key, return false.
  2. Let expr be the Expression that is covered by PatternMatchingMemberExpression.
  3. Let ref be ? Evaluation of expr.
  4. Let value be ? GetValue(ref).
  5. If value is not an Object, return false.
  6. Return ? HasProperty(value, subject).
RelationalPattern : == PatternMatchingRelationalExpression
  1. Let expr be the Expression that is covered by PatternMatchingRelationalExpression.
  2. Let ref be ? Evaluation of expr.
  3. Let value be ? GetValue(ref).
  4. Return ? IsLooselyEqual(subject, value).
RelationalPattern : != PatternMatchingRelationalExpression
  1. Let expr be the Expression that is covered by PatternMatchingRelationalExpression.
  2. Let ref be ? Evaluation of expr.
  3. Let value be ? GetValue(ref).
  4. Let r be ? IsLooselyEqual(subject, value).
  5. If r is true, return false. Otherwise, return true.
RelationalPattern : === PatternMatchingRelationalExpression
  1. Let expr be the Expression that is covered by PatternMatchingRelationalExpression.
  2. Let ref be ? Evaluation of expr.
  3. Let value be ? GetValue(ref).
  4. Return IsStrictlyEqual(subject, value).
RelationalPattern : !== PatternMatchingRelationalExpression
  1. Let expr be the Expression that is covered by PatternMatchingRelationalExpression.
  2. Let ref be ? Evaluation of expr.
  3. Let value be ? GetValue(ref).
  4. Let r be IsStrictlyEqual(subject, value).
  5. If r is true, return false. Otherwise, return true.

30.1.14 Runtime Semantics: IfPatternMatches

The syntax-directed operation IfPatternMatches takes no arguments and returns either a normal completion containing a Boolean or an abrupt completion. It is defined piecewise over the following productions:

IfPattern : if ( Expression )
  1. Let result be ? Evaluation of Expression.
  2. Return ToBoolean(result).

30.1.15 Runtime Semantics: CombinedMatchPatternMatches

The syntax-directed operation CombinedMatchPatternMatches takes arguments subject (an ECMAScript language value) and cacheGroup (a %Map%) and returns either a normal completion containing a Boolean or an abrupt completion. It is defined piecewise over the following productions:

CombinedMatchPattern : MatchPattern and MatchPattern
  1. Let leftMatches be ? MatchPatternMatches of the first MatchPattern with arguments subject and cacheGroup.
  2. If leftMatches is false, return false.
  3. Return ? MatchPatternMatches of the second MatchPattern with arguments subject and cacheGroup.
CombinedMatchPattern : MatchPattern or MatchPattern
  1. Let leftMatches be ? MatchPatternMatches of the first MatchPattern with arguments subject and cacheGroup.
  2. If leftMatches is true, return true.
  3. Return ? MatchPatternMatches of the second MatchPattern with arguments subject and cacheGroup.
CombinedMatchPattern : not MatchPattern
  1. Let matches be ? MatchPatternMatches of MatchPattern with arguments subject and cacheGroup.
  2. If matches is true, return false.
  3. Return true.

30.2 The match Expression

Syntax

MatchExpression[Yield, Await] : CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await] [no LineTerminator here] { MatchExpressionClauses[?Yield, ?Await] ; } MatchExpressionClauses[Yield, Await] : MatchExpressionClause[?Yield, ?Await] MatchExpressionClauses[?Yield, ?Await] ; MatchExpressionClause[?Yield, ?Await] MatchExpressionClauses[?Yield, ?Await] ; default : Expression[+In, ?Yield, ?Await] default : Expression[+In, ?Yield, ?Await] MatchExpressionClause[Yield, Await] : MatchPattern[?Yield, ?Await] : Expression[+In, ?Yield, ?Await]

Supplemental Syntax

When processing an instance of the production
MatchExpression : CoverCallExpressionAndAsyncArrowHead { MatchExpressionClauses ; }
the interpretation of CoverCallExpressionAndAsyncArrowHead is refined using the following grammar:

MatchHead : match [no LineTerminator here] ( Expression ) Note
const result = match (value) {
  { op: "add", let lhs, let rhs }: lhs + rhs,
  { op: "sub", let lhs, let rhs }: lhs - rhs,
  { op: "mul", let lhs, let rhs }: lhs * rhs,
  { op: "div", let lhs, let rhs }: lhs / rhs,
}
Editor's Note

If the do expression proposal will never happen, we will need to add a statement version of MatchExpression.

match (value) {
  { let x, let y, let z }: do {
    const w = average(x, y, z);
    return w * w;
  },
  // ...
}
Editor's Note
This feature can be replaced by the is expression. The code example above can be written as:
const result =
  value is { op: "add", var lhs, var rhs } ? lhs + rhs :
  value is { op: "sub", var lhs, var rhs } ? lhs - rhs :
  value is { op: "mul", var lhs, var rhs } ? lhs * rhs :
  value is { op: "div", var lhs, var rhs } ? lhs / rhs :
  (() => { throw new TypeError() })
Editor's Note
It is syntactically impossible to have a match expression that
  • has no clause.
  • has multiple default clauses.
  • the default clause is not the final clause.
Editor's Note
Rename CoverCallExpressionAndAsyncArrowHead to CoverCallExpressionAndAsyncArrowHeadAndMatchHead.

30.2.1 Static Semantics: Early Errors

MatchExpression : CoverCallExpressionAndAsyncArrowHead { MatchExpressionClauses ; }

30.2.2 Runtime Semantics: Evaluation

MatchExpression : CoverCallExpressionAndAsyncArrowHead { MatchExpressionClauses ; }
  1. Let head be the MatchHead that is covered by CoverCallExpressionAndAsyncArrowHead.
  2. Let subjectParseNode be the Expression of head.
  3. Let subjectRef be the ? Evaluation of subjectParseNode.
  4. Let subject be ? GetValue(subjectRef).
  5. Let cacheGroup be CreateMatchCache().
  6. Let matchCompletion be Completion(MatchExpressionClausesEvaluation of MatchExpressionClauses with arguments subject and cacheGroup).
  7. Let result be Completion(FinishMatch(matchCompletion, cacheGroup)).
  8. Assert: result is a normal completion or an abrupt completion.
  9. Return result.

30.2.3 Runtime Semantics: MatchExpressionClausesEvaluation

The syntax-directed operation MatchExpressionClausesEvaluation takes arguments subject (an ECMAScript language value) and cacheGroup (a %Map%) and returns either a normal completion containing either an ECMAScript language value or not-matched, or an abrupt completion. It is defined piecewise over the following productions:

MatchExpressionClauses : MatchExpressionClause
  1. Return ? MatchExpressionClauseEvaluation of MatchExpressionClause with arguments subject and cacheGroup.
MatchExpressionClauses : MatchExpressionClauses ; MatchExpressionClause
  1. Let result be ? MatchExpressionClausesEvaluation of MatchExpressionClauses with arguments subject and cacheGroup.
  2. If result is an ECMAScript language value, return result.
  3. Assert: result is not-matched.
  4. Return ? MatchExpressionClauseEvaluation of MatchExpressionClause with arguments subject and cacheGroup.
MatchExpressionClauses : MatchExpressionClauses ; default : Expression
  1. Let result be ? MatchExpressionClausesEvaluation of MatchExpressionClauses with arguments subject and cacheGroup.
  2. If result is an ECMAScript language value, return result.
  3. Assert: result is not-matched.
  4. Return ? Evaluation of Expression.
MatchExpressionClauses : default : Expression
  1. Return ? Evaluation of Expression.

30.2.4 Runtime Semantics: MatchExpressionClauseEvaluation

The syntax-directed operation MatchExpressionClauseEvaluation takes arguments subject (an ECMAScript language value) and cacheGroup (a %Map%) and returns either a normal completion containing either an ECMAScript language value or not-matched, or an abrupt completion. It is defined piecewise over the following productions:

MatchExpressionClause : MatchPattern : Expression
  1. Let matches be ? MatchPatternMatches of MatchPattern with arguments subject and cacheGroup.
  2. If matches is true, return ? Evaluation of Expression.
  3. Return not-matched.

30.3 Abstract Operations for Pattern Matching

30.3.1 InvokeCustomMatcher ( matcher, subject, cacheGroup, kind, receiver )

The abstract operation InvokeCustomMatcher takes arguments matcher (an ECMAScript language value), subject (an ECMAScript language value), cacheGroup (a %Map%), kind (boolean or list), and receiver (an ECMAScript language value) and returns either a normal completion containing either a Boolean or an Iterator Record, or an abrupt completion. It performs the following steps when called:

  1. If matcher is not an Object, then
    1. If kind is boolean, return SameValueZero(matcher, subject).
    2. Throw a TypeError exception.
  2. Let f be ? Get(matcher, %Symbol.customMatcher%).
  3. If f is undefined, then
    1. If kind is boolean, return SameValueNonNumber(matcher, subject).
    2. Throw a TypeError exception.
  4. If kind is boolean, let hint be "boolean".
  5. Else, let hint be "list".
  6. Let result be ? Call(f, matcher, « subject, hint, receiver »).
  7. If result is false, return false.
  8. If kind is boolean, return ToBoolean(result).
  9. If result is not an Object, throw a TypeError exception.
  10. Perform ? GetIteratorCached(result, cacheGroup).
  11. Return result.
Editor's Note
The hint parameter is not a consensus in the champion group yet. This design is to solve runtime performance concerns about custom matchers.
Editor's Note
The receiver parameter is not a consensus in the champion group yet. This design is to keep the this value when calling the custom matchers. Not everyone in the champion group agrees we need to keep the this value.
const zero = new BigNumber(0);
match (expr) {
  zero.equal: console.log('zero point matched.');
  BigNumber: console.log(expr.toString() + ' left.');
}
Editor's Note
The ToBoolean in step 8 cause a strange behaviour around [[IsHTMLDDA]].
function f() { return document.all; }
if (null is f) {}
// not match, by ToBoolean
if (null is f(let html, ...)) {}
// match, because document.all is an object and has [Symbol.iterator].

30.3.2 ValidateCustomMatcherHint ( hint [ , kind ] )

The abstract operation ValidateCustomMatcherHint takes argument hint (an ECMAScript language value) and optional argument kind (boolean or list) and returns either a normal completion containing unused or a throw completion. It performs the following steps when called:

  1. If hint is not "boolean" or "list", throw a TypeError exception.
  2. If kind is not present, return unused.
  3. If hint is "boolean" and kind is list, throw a TypeError exception.
  4. If hint is "list" and kind is boolean, throw a TypeError exception.
  5. Return unused.
Editor's Note
The following sections describe how pattern matching matches the subject with a cache. Those sections use %Map%, %Set% and %Array% internally. This is for the convenience. Implementations don't have to actually create those data structures behind the scenes. It is a spec bug if operations on cache objects trigger any user code.

30.3.3 CreateMatchCache ( )

The abstract operation CreateMatchCache takes no arguments and returns a %Map%. It performs the following steps when called:

  1. Let map be ! Construct(%Map%).
  2. Let iteratorsToClose be ! Construct(%Set%).
  3. Perform ! CreateDataPropertyOrThrow(map, "IteratorsToClose", iteratorsToClose).
  4. Return map.

30.3.4 GetMatchCache ( subject, cacheGroup )

The abstract operation GetMatchCache takes arguments subject (an ECMAScript language value) and cacheGroup (a %Map%) and returns an ECMAScript language value. It performs the following steps when called:

  1. Assert: cacheGroup is created by CreateMatchCache and used internally for pattern-matching.
  2. If ! Call(%Map.prototype.has%, cacheGroup, « subject ») is true, then
    1. Return ! Call(%Map.prototype.get%, cacheGroup, « subject »).
  3. Let cache be OrdinaryObjectCreate(null).
  4. Perform ! CreateDataPropertyOrThrow(cache, "Has", ! Construct(%Map%)).
  5. Perform ! CreateDataPropertyOrThrow(cache, "Get", ! Construct(%Map%)).
  6. Perform ! CreateDataPropertyOrThrow(cache, "Iterator", undefined).
  7. Perform ! CreateDataPropertyOrThrow(cache, "IteratedValues", ! ArrayCreate(0, null)).
  8. Perform ! Call(%Map.prototype.set%, cacheGroup, « subject, cache »).
  9. Return cache.

30.3.5 HasPropertyCached ( subject, cacheGroup, propertyName )

The abstract operation HasPropertyCached takes arguments subject (an ECMAScript language value), cacheGroup (a %Map%), and propertyName (a property key) and returns either a normal completion containing a Boolean or an abrupt completion. It performs the following steps when called:

  1. Assert: cacheGroup is created by CreateMatchCache and used internally for pattern-matching.
  2. Let cache be ! Get(GetMatchCache(subject, cacheGroup), "Has").
  3. Let hasCache be ! Call(%Map.prototype.has%, cache, « propertyName »).
  4. If hasCache is true, return ! Call(%Map.prototype.get%, cache, « propertyName »).
  5. Let result be ? HasProperty(subject, propertyName).
  6. Perform ! Call(%Map.prototype.set%, cache, « propertyName, result »).
  7. Return result.

30.3.6 GetCached ( subject, cacheGroup, propertyName )

The abstract operation GetCached takes arguments subject (an ECMAScript language value), cacheGroup (a %Map%), and propertyName (a property key) and returns either a normal completion containing an ECMAScript language value or an abrupt completion. It performs the following steps when called:

  1. Assert: cacheGroup is created by CreateMatchCache and used internally for pattern-matching.
  2. Let cache be ! Get(GetMatchCache(subject, cacheGroup), "Get").
  3. Let hasCache be ! Call(%Map.prototype.has%, cache, « propertyName »).
  4. If hasCache is true, return ! Call(%Map.prototype.get%, cache, « propertyName »).
  5. Let result be ? Get(subject, propertyName).
  6. Perform ! Call(%Map.prototype.set%, cache, « propertyName, result »).
  7. Return result.

30.3.7 GetIteratorCached ( subject, cacheGroup )

The abstract operation GetIteratorCached takes arguments subject (an ECMAScript language value) and cacheGroup (a %Map%) and returns either a normal completion containing an Iterator Record or an abrupt completion. It performs the following steps when called:

  1. Assert: cacheGroup is created by CreateMatchCache and used internally for pattern-matching.
  2. Let cache be GetMatchCache(subject, cacheGroup).
  3. Let iteratorRecordObject be ! Get(cache, "Iterator").
  4. If iteratorRecordObject is not undefined, return iteratorRecordObject.[[CachedIterator]].
  5. Let f be ? GetCached(subject, cacheGroup, %Symbol.iterator%).
  6. Let iteratorRecord be ? GetIteratorFromMethod(subject, f).
  7. Set iteratorRecordObject to OrdinaryObjectCreate(null, « [[CachedIterator]] »).
  8. Set iteratorRecordObject.[[CachedIterator]] to iteratorRecord.
  9. Perform ! Set(cache, "Iterator", iteratorRecordObject, true).
  10. Perform ! Call(%Set.prototype.add%, ! Get(cacheGroup, "IteratorsToClose"), « iteratorRecordObject »).
  11. Return iteratorRecordObject.[[CachedIterator]].
Editor's Note
The [[CachedIterator]] field is only used in GetIteratorCached and CloseCachedIterators. Iterator Records are not ECMAScript language values and cannot be Set on an object, therefore an object is created to wrap the Iterator Record.

30.3.8 IteratorStepCached ( iterator, cacheGroup )

The abstract operation IteratorStepCached takes arguments iterator (an Iterator Record) and cacheGroup (a %Map%) and returns either a normal completion containing either an ECMAScript language value or not-matched, or an abrupt completion. It performs the following steps when called:

  1. Assert: cacheGroup is created by CreateMatchCache and used internally for pattern-matching.
  2. If iterator.[[Done]] is true, return not-matched.
  3. Let cache be GetMatchCache(iterator, cacheGroup).
  4. Let iteratedValues be ! Get(cache, "IteratedValues").
  5. Let iteratorResult be Completion(IteratorStep(iterator)).
  6. If iteratorResult is an abrupt completion, set iterator.[[Done]] to true.
  7. ReturnIfAbrupt(iteratorResult).
  8. If iteratorResult is false, then
    1. Set iterator.[[Done]] to true.
    2. Return not-matched.
  9. Let value be Completion(IteratorValue(iteratorResult)).
  10. If value is an abrupt completion, set iterator.[[Done]] to true.
  11. ReturnIfAbrupt(value).
  12. Perform ! Call(%Array.prototype.push%, iteratedValues, « value »).
  13. Return value.

30.3.9 GetIteratorNthValueCached ( iterator, cacheGroup, n )

The abstract operation GetIteratorNthValueCached takes arguments iterator (an Iterator Record), cacheGroup (a %Map%), and n (a non-negative integer) and returns either a normal completion containing either an ECMAScript language value or not-matched, or an abrupt completion. It performs the following steps when called:

  1. NOTE: 0th is the first value.
  2. Assert: cacheGroup is created by CreateMatchCache and used internally for pattern-matching.
  3. Let iteratedValues be ! Get(GetMatchCache(iterator, cacheGroup), "IteratedValues").
  4. Let nthItem be ! iteratedValues.[[GetOwnProperty]](n).
  5. If nthItem is not undefined, return nthItem.[[Value]].
  6. Assert: ! LengthOfArrayLike(iteratedValues) is n. For example, iteratedValues has 0 values, n should be 0 to get the first value from the iterator.
  7. Return ? IteratorStepCached(iterator, cacheGroup).
Editor's Note
This abstract operation gets the nth (start from 0) value of the iterator, and runs IteratorStepCached if it is not cached in the cacheGroup. For all n > 1, this abstract operation expects these invariants:
  • The evaluation of MatchList always evaluates the n-1th value before evaluating the nth.
  • If the nth value is exists ([[GetOwnProperty]](n) returns a Property Descriptor), the n-1th value also exists.
  • If the nth value does not exist and the iterator.[[Done]] is true, there is no further call with a bigger n parameter with the same cacheGroup and iterator.

30.3.10 FinishListMatch ( iterator, cacheGroup, expectedLength )

The abstract operation FinishListMatch takes arguments iterator (an Iterator Record), cacheGroup (a %Map%), and expectedLength (either a non-negative integer, not-matched or unlimited) and returns either a normal completion containing a Boolean or an abrupt completion. It performs the following steps when called:

  1. If expectedLength is not-matched, return false.
  2. If expectedLength is unlimited, return true.
  3. Let cache be GetMatchCache(iterator, cacheGroup).
  4. Let cachedLength be ! LengthOfArrayLike(! Get(cache, "IteratedValues")).
  5. If cachedLength > expectedLength, return false.
  6. Assert: cachedLength is expectedLength.
  7. If iterator.[[Done]] is true, return true.
  8. Let iteratorResult be ? GetIteratorNthValueCached(iterator, cacheGroup, expectedLength).
  9. If iteratorResult is not-matched, return true.
  10. Return false.
Editor's Note

This abstract operation is called the final step of the evaluation of MatchList.

For example, when matching with [], the expectedLength is 0. This abstract operation checks if cachedLength is 0. If the iterator is done, the match succeeds. If the iterator is not done, it will try to get the 0th value from the iterator. If there is a 0th value, the match fails.

Another example is when matching with [1, 2, 3], the expectedLength is 3. This abstract operation checks if cachedLength is 3. If the iterator is done, the match succeeds. If the iterator is not done, it will try to get the 3th (remember we start from the 0th) value from the iterator. If there is a 3rd (which means the cachedLength is now 4) value, the match fails.

30.3.11 FinishMatch ( matchCompletion, cacheGroup )

The abstract operation FinishMatch takes arguments matchCompletion (a Completion Record) and cacheGroup (a %Map%) and returns a Completion Record. It performs the following steps when called:

  1. Assert: cacheGroup is created by CreateMatchCache and used internally for pattern-matching.
  2. If matchCompletion is a normal completion and matchCompletion.[[Value]] is not-matched, then
    1. Set matchCompletion to ThrowCompletion(a newly created TypeError).
  3. Let iterators be a new empty List.
  4. For each element value of ! Get(cacheGroup, "IteratorsToClose").[[SetData]], do
    1. Assert: value.[[Value]] is not empty.
    2. Let iteratorRecordObject be ! Get(value.[[Value]], "IteratorsToClose").
    3. Append iteratorRecordObject.[[CachedIterator]] to iterators.
  5. Let errors be a new empty List.
  6. If matchCompletion is an abrupt completion, append matchCompletion.[[Value]] to errors.
  7. For each element iterator of iterators, do
    1. If iterator.[[Done]] is false, then
      1. Let closeResult be Completion(IteratorClose(iterator, NormalCompletion(undefined))).
      2. If closeResult is an abrupt completion, append closeResult.[[Value]] to errors.
  8. If errors is not empty, then
    1. If length of errors is 1, return ThrowCompletion(errors[0]).
    2. Let error be a newly created AggregateError object.
    3. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errors) }).
    4. Return ThrowCompletion(error).
  9. Return matchCompletion.
Editor's Note

If any error occurs when closing iterators, this abstract operation ignores the matchCompletion and returns an abrupt completion. This might be problematic when the matchCompletion is a Return Completion, Continue Completion, or a Break Completion.

for (const iterator of iterators) {
  try {
    match(iterator) {
      [String, ...] and [...let list]: callback(list);
      [...]: continue;
    }
  } catch {
  }
}

In the code example above, if the second branch matches (and returns a continue completion), but the iterator throws an error when closing, the continue will be ignored, and the catch block will be evaluated.

A Grammar Summary

A.1 Expressions

PrimaryExpression[Yield, Await] : RegularExpressionLiteral MatchExpression[?Yield, ?Await] RelationalExpression[In, Yield, Await] : RelationalExpression[?In, ?Yield, ?Await] [no LineTerminator here] is MatchPattern[?Yield, ?Await] MatchExpression[Yield, Await] : CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await] [no LineTerminator here] { MatchExpressionClauses[?Yield, ?Await] ; } MatchExpressionClauses[Yield, Await] : MatchExpressionClause[?Yield, ?Await] MatchExpressionClauses[?Yield, ?Await] ; MatchExpressionClause[?Yield, ?Await] MatchExpressionClauses[?Yield, ?Await] ; default : Expression[+In, ?Yield, ?Await] default : Expression[+In, ?Yield, ?Await] MatchExpressionClause[Yield, Await] : MatchPattern[?Yield, ?Await] : Expression[+In, ?Yield, ?Await]

A.9 Patterns

MatchPattern[Yield, Await] : ( MatchPattern[?Yield, ?Await] ) PrimitivePattern VariableDeclarationPattern[?Yield, ?Await] MemberExpressionPattern[?Yield, ?Await] ObjectPattern[?Yield, ?Await] ArrayPattern[?Yield, ?Await] UnaryAlgebraicPattern[?Yield, ?Await] RelationalPattern[?Yield, ?Await] IfPattern[?Yield, ?Await] CombinedMatchPattern[?Yield, ?Await] PrimitivePattern : Literal NoSubstitutionTemplate VariableDeclarationPattern[Yield, Await] : VarOrLetOrConst BindingIdentifier[?Yield, ?Await] VarOrLetOrConst : var LetOrConst MemberExpressionPattern[Yield, Await] : PatternMatchingMemberExpression[?Yield, ?Await] PatternMatchingMemberExpression[?Yield, ?Await] ( MatchList[?Yield, ?Await] ) ObjectPattern[Yield, Await] : { } { MatchRestProperty[?Yield, ?Await] } { MatchPropertyList[?Yield, ?Await] ,opt } { MatchPropertyList[?Yield, ?Await] , MatchRestProperty[?Yield, ?Await] } ArrayPattern[Yield, Await] : [ MatchList[?Yield, ?Await]opt ] UnaryAlgebraicPattern[Yield, Await] : PatternMatchingUnaryAlgebraicExpression[?Yield, ?Await] RelationalPattern[Yield, Await] : < PatternMatchingRelationalExpression[?Yield, ?Await] > PatternMatchingRelationalExpression[?Yield, ?Await] <= PatternMatchingRelationalExpression[?Yield, ?Await] >= PatternMatchingRelationalExpression[?Yield, ?Await] instanceof PatternMatchingMemberExpression[?Yield, ?Await] in PatternMatchingMemberExpression[?Yield, ?Await] == PatternMatchingRelationalExpression[?Yield, ?Await] != PatternMatchingRelationalExpression[?Yield, ?Await] === PatternMatchingRelationalExpression[?Yield, ?Await] !== PatternMatchingRelationalExpression[?Yield, ?Await] IfPattern[Yield, Await] : if ( Expression[+In, ?Yield, ?Await] ) CombinedMatchPattern[Yield, Await] : MatchPattern[?Yield, ?Await] and MatchPattern[?Yield, ?Await] MatchPattern[?Yield, ?Await] or MatchPattern[?Yield, ?Await] not MatchPattern[?Yield, ?Await]

Object in pattern-matching:

ObjectPattern[Yield, Await] : { } { MatchRestProperty[?Yield, ?Await] } { MatchPropertyList[?Yield, ?Await] ,opt } { MatchPropertyList[?Yield, ?Await] , MatchRestProperty[?Yield, ?Await] } MatchRestProperty[Yield, Await] : ... MatchPattern[?Yield, ?Await] MatchPropertyList[Yield, Await] : MatchProperty[?Yield, ?Await] MatchPropertyList[?Yield, ?Await] , MatchProperty[?Yield, ?Await] MatchProperty[Yield, Await] : PropertyName[?Yield, ?Await] ?opt PropertyName[?Yield, ?Await] ?opt : MatchPattern[?Yield, ?Await] VarOrLetOrConst BindingIdentifier[?Yield, ?Await] ?opt VarOrLetOrConst BindingIdentifier[?Yield, ?Await] ?opt : MatchPattern[?Yield, ?Await]

Array in pattern-matching:

ArrayPattern[Yield, Await] : [ MatchList[?Yield, ?Await]opt ] MatchList[Yield, Await] : Elisionopt MatchRestElement[?Yield, ?Await]opt MatchElementList[?Yield, ?Await] MatchElementList[?Yield, ?Await] , Elisionopt MatchRestElement[?Yield, ?Await]opt MatchRestElement[Yield, Await] : ... ... MatchPattern[?Yield, ?Await] MatchElementList[Yield, Await] : MatchElisionElement[?Yield, ?Await] MatchElementList[?Yield, ?Await] , MatchElisionElement[?Yield, ?Await] MatchElisionElement[Yield, Await] : Elisionopt MatchElement[?Yield, ?Await] MatchElement[Yield, Await] : MatchPattern[?Yield, ?Await] ?opt

Limited Expressions which is evaluated as an Expression in pattern-matching:

PatternMatchingMemberExpression[Yield, Await] : this MetaProperty IdentifierReference[?Yield, ?Await] super . IdentifierName PatternMatchingMemberExpression[?Yield, ?Await] . IdentifierName PatternMatchingMemberExpression[?Yield, ?Await] . PrivateIdentifier PatternMatchingMemberExpression[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ] PatternMatchingUnaryAlgebraicExpression[Yield, Await] : + PatternMatchingMemberExpression[?Yield, ?Await] - PatternMatchingMemberExpression[?Yield, ?Await] + NumericLiteral - NumericLiteral PatternMatchingRelationalExpression[Yield, Await] : Literal NoSubstitutionTemplate PatternMatchingMemberExpression[?Yield, ?Await] PatternMatchingUnaryAlgebraicExpression[?Yield, ?Await]

B Copyright & Software License

Copyright Notice

© 2024 Daniel Rosenwasser,Jack Works,Jordan Harband,Mark Cohen,Ross Kirsling,Tab Atkins

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.