├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .npmrc ├── .prettierrc.json ├── LICENSE ├── README.md ├── assets ├── IBMPlexMono-Bold-SlashedZero.woff2 ├── IBMPlexMono-BoldItalic-SlashedZero.woff2 ├── IBMPlexMono-Italic-SlashedZero.woff2 ├── IBMPlexMono-Regular-SlashedZero.woff2 ├── IBMPlexSans-Bold-SlashedZero.woff2 ├── IBMPlexSans-BoldItalic-SlashedZero.woff2 ├── IBMPlexSans-Italic-SlashedZero.woff2 ├── IBMPlexSans-Regular-SlashedZero.woff2 ├── IBMPlexSerif-Bold-SlashedZero.woff2 ├── IBMPlexSerif-BoldItalic-SlashedZero.woff2 ├── IBMPlexSerif-Italic-SlashedZero.woff2 ├── IBMPlexSerif-Regular-SlashedZero.woff2 ├── ecma-header.png ├── ecmarkup.css ├── ecmarkup.js ├── expand.js └── print.css ├── index.html ├── package.json └── spec.emu /.gitattributes: -------------------------------------------------------------------------------- 1 | index.html -diff merge=ours 2 | spec.js -diff merge=ours 3 | spec.css -diff merge=ours 4 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Deploy spec 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: ljharb/actions/node/install@main 12 | - run: npm run build 13 | - name: commit changes 14 | uses: elstudio/actions-js-build/commit@v3 15 | with: 16 | commitMessage: "fixup: [spec] `npm run build`" 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # Only apps should have lockfiles 40 | yarn.lock 41 | package-lock.json 42 | npm-shrinkwrap.json 43 | pnpm-lock.yaml 44 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "proseWrap": "always" 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 ECMA TC39 and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ECMAScript Pattern Matching 2 | 3 | ## [Status](https://tc39.github.io/process-document/) 4 | 5 | **Stage**: 1 6 | 7 | **Spec Text**: 8 | 9 | **Authors**: Originally Kat Marchán (Microsoft, 10 | [@zkat\_\_](https://twitter.com/zkat__)); now, the below champions. 11 | 12 | **Champions**: (in alphabetical order) 13 | 14 | - Daniel Rosenwasser (Microsoft, 15 | [@drosenwasser](https://twitter.com/drosenwasser)) 16 | - Jack Works (Sujitech, [@Jack-Works](https://github.com/Jack-Works)) 17 | - Jordan Harband (HeroDevs, [@ljharb](https://twitter.com/ljharb)) 18 | - Mark Cohen ([@mpcsh\_](https://twitter.com/mpcsh_)) 19 | - Ross Kirsling (Sony, [@rkirsling](https://twitter.com/rkirsling)) 20 | - Tab Atkins-Bittner (Google, [@tabatkins](https://twitter.com/tabatkins)) 21 | 22 | # Introduction 23 | 24 | ## Problem 25 | 26 | There are many ways to match *values* in the language, 27 | but there are no ways to match *patterns* 28 | beyond regular expressions for strings. 29 | However, wanting to take different actions 30 | based on patterns in a given value 31 | is a very common desire: 32 | do X if the value has a `foo` property, 33 | do Y if it contains three or more items, 34 | etc. 35 | 36 | ### Current Approaches 37 | 38 | `switch` has the desired *structure* -- 39 | a value is given, 40 | and several possible match criteria are offered, 41 | each with a different associated action. 42 | But it's severely lacking in practice: 43 | it may not appear in expression position; 44 | an explicit `break` is required in each `case` to avoid accidental fallthrough; 45 | scoping is ambiguous 46 | (block-scoped variables inside one `case` are available in the scope of the others, 47 | unless curly braces are used); 48 | the only comparison it can do is `===`; etc. 49 | 50 | `if/else` has the necessary *power* 51 | (you can perform any comparison you like), 52 | but it's overly verbose even for common cases, 53 | requiring the author to explicitly list paths into the value's structure multiple times, 54 | once per test performed. 55 | It's also statement-only 56 | (unless the author opts for the harder-to-understand ternary syntax) 57 | and requires the value under test to be stored in a (possibly temporary) variable. 58 | 59 | ## Priorities for a solution 60 | 61 | This section details this proposal’s priorities. Note that not every champion 62 | may agree with each priority. 63 | 64 | ### _Pattern_ matching 65 | 66 | The pattern matching construct is a full conditional logic construct that can do 67 | more than just pattern matching. As such, there have been (and there will be 68 | more) trade-offs that need to be made. In those cases, we should prioritize the 69 | ergonomics of structural pattern matching over other capabilities of this 70 | construct. 71 | 72 | ### Subsumption of `switch` 73 | 74 | This feature must be easily searchable, so that tutorials and documentation are 75 | easy to locate, and so that the feature is easy to learn and recognize. As such, 76 | there must be no syntactic overlap with the `switch` statement. 77 | 78 | This proposal seeks to preserve the good parts of `switch`, and eliminate any 79 | reasons to reach for it. 80 | 81 | ### Be better than `switch` 82 | 83 | `switch` contains a plethora of footguns such as accidental case fallthrough and 84 | ambiguous scoping. This proposal should eliminate those footguns, while also 85 | introducing new capabilities that `switch` currently can not provide. 86 | 87 | ### Expression semantics 88 | 89 | The pattern matching construct should be usable as an expression: 90 | 91 | - `return match { ... }` 92 | - `let foo = match { ... }` 93 | - `() => match { ... }` 94 | - etc. 95 | 96 | The value of the whole expression is the value of whatever [clause](#clause) is 97 | matched. 98 | 99 | ### Exhaustiveness and ordering 100 | 101 | If the developer wants to ignore certain possible cases, they should specify 102 | that explicitly. A development-time error is less costly than a production-time 103 | error from something further down the stack. 104 | 105 | If the developer wants two cases to share logic (what we know as "fall-through" 106 | from `switch`), they should specify it explicitly. Implicit fall-through 107 | inevitably silently accepts buggy code. 108 | 109 | [Clauses](#clause) should always be checked in the order they’re written, i.e. 110 | from top to bottom. 111 | 112 | ### User extensibility 113 | 114 | Userland objects should be able to encapsulate their own matching semantics, 115 | without unnecessarily privileging builtins. This includes regular expressions 116 | (as opposed to the literal pattern syntax), numeric ranges, etc. 117 | 118 | ## Prior Art 119 | 120 | This proposal adds a pattern matching expression to the language, based in part 121 | on the existing 122 | [Destructuring Binding Patterns](https://tc39.github.io/ecma262/#sec-destructuring-binding-patterns). 123 | 124 | This proposal was approved for Stage 1 in the May 2018 TC39 meeting, and slides 125 | for that presentation are available 126 | [here](https://docs.google.com/presentation/d/1WPyAO4pHRsfwGoiIZupz_-tzAdv8mirw-aZfbxbAVcQ). 127 | Its current form was presented to TC39 in the April 2021 meeting 128 | ([slides](https://hackmd.io/@mpcsh/HkZ712ig_#/)). 129 | 130 | This proposal draws from, and partially overlaps with, corresponding features in 131 | [CoffeeScript](https://coffeescript.org/#switch), 132 | [Rust](https://doc.rust-lang.org/1.6.0/book/patterns.html), 133 | [Python](https://www.python.org/dev/peps/pep-0622/), 134 | [F#](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching), 135 | [Scala](http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html), 136 | [Elixir/Erlang](https://elixir-lang.org/getting-started/pattern-matching.html), 137 | and [C++](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1371r2.pdf). 138 | 139 | ### Userland matching 140 | 141 | A list of community libraries that provide similar matching functionality: 142 | 143 | - [Optionals](https://github.com/OliverBrotchie/optionals) — Rust-like error handling, options and exhaustive pattern matching for TypeScript and Deno 144 | - [ts-pattern](https://github.com/gvergnaud/ts-pattern) — Exhaustive Pattern Matching library for TypeScript, with smart type inference. 145 | - [babel-plugin-proposal-pattern-matching](https://github.com/iptop/babel-plugin-proposal-pattern-matching) — Minimal grammar, high performance JavaScript pattern matching implementation. 146 | - [match-iz](https://github.com/shuckster/match-iz) — A tiny functional pattern-matching library inspired by the TC39 proposal. 147 | - [patcom](https://github.com/concept-not-found/patcom) — Feature parity with TC39 proposal without any new syntax 148 | 149 | # Specification 150 | 151 | This proposal introduces three new concepts to Javascript: 152 | 153 | * the "matcher pattern", 154 | a new DSL closely related to destructuring patterns, 155 | which allows recursively testing the structure and contents of a value 156 | in multiple ways at once, 157 | and extracting some of that structure into local bindings at the same time 158 | * the `match(){}` expression, 159 | a general replacement for the `switch` statement 160 | that uses matcher patterns 161 | to resolve to one of several values, 162 | * the `is` boolean operator, 163 | which allows for one-off testing of a value against a matcher pattern, 164 | potentially also introducing bindings from that test into the local environment. 165 | 166 | 167 | # Matcher Patterns 168 | 169 | Matcher patterns are a new DSL, 170 | closely inspired by destructuring patterns, 171 | for recursively testing the structure and contents of a value 172 | while simultaneously extracting some parts of that value 173 | as local bindings for use by other code. 174 | 175 | Matcher patterns can be divided into three general varieties: 176 | * Value patterns, which test that the subject matches some criteria, like "is the string `"foo"`" or "matches the variable `bar`". 177 | * Structure patterns, which test the subject matches some structural criteria like "has the property `foo`" or "is at least length 3", and also let you recursively apply additional matchers to parts of that structure. 178 | * Combinator patterns, which let you match several patterns in parallel on the same subject, with simple boolean `and`/`or` logic. 179 | 180 | ## Value Matchers 181 | 182 | There are several types of value patterns, performing different types of tests. 183 | 184 | ### Primitive Patterns 185 | 186 | All primitive values can be used directly as matcher patterns, 187 | representing a test that the subject matches the specified value, 188 | using [`SameValue`](https://tc39.es/ecma262/#sec-samevalue) semantics 189 | (except when otherwise noted). 190 | 191 | For example, `1` tests that the subject is `SameValue` to `1`, 192 | `"foo"` tests that it's `SameValue` to `"foo"`, 193 | etc. 194 | 195 | Specifically, 196 | boolean literals, 197 | numeric literals, 198 | string literals, 199 | untagged template literals, 200 | and the null literal 201 | can all be used. 202 | 203 | * Numeric literals can additionally be prefixed with a `+` or `-` unary operator: 204 | `+` is a no-op (but see the note about `0`, below), 205 | but `-` negates the value, 206 | as you'd expect. 207 | * Within the interpolation expressions of template literals, 208 | see [Bindings](#bindings) for details on what bindings are visible. 209 | 210 | The one exception to `SameValue` matching semantics 211 | is that the pattern `0` is matched using `SameValueZero` semantics. 212 | `+0` and `-0` are matched with `SameValue`, as normal. 213 | (This has the effect that an "unsigned" zero pattern 214 | will match both positive and negative zero values, 215 | while the "signed" zero patterns 216 | will only match the appropriately signed zero values.) 217 | 218 | (Additional notes for `SameValue` semantics: 219 | it works "as expected" for NaN values, 220 | correctly matching NaN values against NaN patterns; 221 | it does not do any implicit type coercion, 222 | so a `1` value will not match a `"1"` pattern.) 223 | 224 | Note: No coercion takes place in these matches: 225 | if you match against a string literal, for example, 226 | your subject must already be a string 227 | or else it'll automatically fail the match, 228 | even if it would stringify to a matching string. 229 | 230 | #### Examples 231 | 232 | ```js 233 | ``` 234 | 235 | 236 | ### Variable Patterns 237 | 238 | A variable pattern is a "ident expression": `foo`, `foo.bar`, `foo[bar]` etc., 239 | excluding those that are already primitives like `null`, 240 | and optionally prefixed by a `+` or `-` unary operator. 241 | 242 | A variable pattern resolves the identifier against the visible bindings 243 | (see [Bindings](#bindings) for details), 244 | and if it has a `+` or `-` prefix, 245 | converts it to a number (via `toValue`) 246 | and possibly negates it. 247 | If the result is an object with a `Symbol.customMatcher` property, 248 | or is a function, 249 | then it represents a custom matcher test. 250 | See [custom matchers](#custom-matchers) for details. 251 | Otherwise, it represents a test that the subject is `SameValue` with the result, 252 | similar to a [Primitive Pattern](#primitive-patterns). 253 | 254 | Note: This implies that, for example, 255 | a variable holding an array will only match that exact array, 256 | via object equivalence; 257 | it is not equivalent to an [array pattern](#array-patterns) 258 | doing a structural match. 259 | 260 | Note that several values which are often *thought of* as literals, 261 | like `Infinity` or `undefined`, 262 | are in fact bindings. 263 | Since Primitive Patterns and Variable Patterns are treated largely identically, 264 | the distinction can fortunately remain academic here. 265 | 266 | Note: Just like with Primitive Patterns, 267 | no coercion is performed on the subjects 268 | (or on the pattern value, 269 | except for the effect of `+`/`-`, 270 | which is explicitly asking for a numeric coercion), 271 | so the type has to already match. 272 | 273 | 274 | #### Examples 275 | 276 | ```js 277 | ``` 278 | 279 | 280 | ### Custom Matchers 281 | 282 | If the object that the variable pattern resolves to 283 | has a `Symbol.customMatcher` property in its prototype chain, 284 | then it is a "custom matcher". 285 | 286 | To determine whether the pattern matches or not, 287 | the custom matcher function is invoked 288 | with the subject as its first argument, 289 | and an object with the key `"matchType"` set to `"boolean"` 290 | as its second argument. 291 | 292 | If it returns a truthy value 293 | (one which becomes `true` when `!!` is used on it) 294 | the match is successful; 295 | otherwise, 296 | the match fails. 297 | If it throws, 298 | it passes the error up. 299 | 300 | Note: [Extractor patterns](#extractor-patterns) use the identical machinery, 301 | but allow further matching against the returned value, 302 | rather than being limited to just returning true/false. 303 | 304 | #### Examples 305 | 306 | ```js 307 | ``` 308 | 309 | 310 | #### Built-in Custom Matchers 311 | 312 | Several JS objects have custom matchers installed on them by default. 313 | 314 | All of the classes for primitive types 315 | (Boolean, String, Number, BigInt, Symbol) 316 | expose a built-in Symbol.customMatcher static method, 317 | matching if and only if the subject is 318 | a primitive (or a boxed object) corresponding to that type 319 | The return value of a successful match 320 | (for the purpose of [extractor patterns](#extractor-patterns)) 321 | is an iterator containing the (possibly auto-unboxed) primitive value. 322 | 323 | ```js 324 | class Boolean { 325 | static [Symbol.customMatcher](subject) { 326 | return typeof subject == "boolean"; 327 | } 328 | } 329 | /* et cetera for the other primitives */ 330 | ``` 331 | 332 | `Function.prototype` has a custom matcher 333 | that checks if the function has an `[[IsClassConstructor]]` slot 334 | (meaning it's the `constructor()` function from a `class` block); 335 | if so, it tests whether the subject is an object of that class 336 | (using brand-checking to verify, similar to `Array.isArray()`); 337 | if not, it invokes the function as a predicate, 338 | passing it the subject, 339 | and returns the return value: 340 | 341 | ```js 342 | /* roughly */ 343 | Function.prototype[Symbol.customMatcher] = function(subject) { 344 | if(isClassConstructor(this)) { 345 | return hasCorrectBrand(this, subject); 346 | } else { 347 | return this(subject); 348 | } 349 | } 350 | ``` 351 | 352 | This way, predicate functions can be used directly as matchers, 353 | like `x is upperAlpha`, 354 | and classes can also be used directly as matchers 355 | to test if objects are of that class, 356 | like `x is Option.Some`. 357 | 358 | `RegExp.prototype` has a custom matcher 359 | that executes the regexp on the subject, 360 | and matches if the match was successful: 361 | 362 | ```js 363 | RegExp.prototype[Symbol.customMatcher] = function(subject, {matchType}) { 364 | const result = this.exec(subject); 365 | if(matchType == "boolean") return result; 366 | if(matchType == "extractor") return [result, ...result.slice(1)]; 367 | } 368 | ``` 369 | 370 | 371 | #### Examples 372 | 373 | ```js 374 | ``` 375 | 376 | 377 | ### Binding Patterns 378 | 379 | A `let`, `const`, or `var` keyword followed by a valid variable name 380 | (identical to binding statements anywhere else). 381 | Binding patterns always match, 382 | and additionally introduce a binding, 383 | binding the subject to the given name 384 | with the given binding semantics. 385 | 386 | 387 | #### Binding Behavior Details 388 | 389 | As with normal binding statements, 390 | the bindings introduced by binding patterns 391 | are established in the nearest block scope 392 | (for `let`/`const`) 393 | or the nearest function scope (for `var`). 394 | 395 | Issue: Or in the obvious scope, when used in a for/while header or a function arglist. 396 | Don't know the right term for this off the top of my head. 397 | 398 | Bindings are established according to their *presence* in a pattern; 399 | whether or not the binding pattern itself is ever executed is irrelevant. 400 | (For example, `[1, 2] is ["foo", let foo]` 401 | will still establish a `foo` binding in the block scope, 402 | despite the first pattern failing to match 403 | and thus skipping the binding pattern.) 404 | 405 | Standard TDZ rules apply before the binding pattern is actually executed. 406 | (For example, `when [x, let x]` is an early `ReferenceError`, 407 | since the `x` binding has not yet been initialized 408 | when the first pattern is run 409 | and attempts to dereference `x`.) 410 | 411 | Unlike standard binding rules, 412 | within the scope of an entire top-level pattern, 413 | a given name can appear in multiple binding patterns, 414 | as long as all instances use the same binding type keyword. 415 | It is a runtime `ReferenceError` 416 | if more than one of these binding patterns actually execute, however 417 | (with one exception - see [`or` patterns](#or-patterns)). 418 | (This behavior has precedent: 419 | it was previously the case that named capture groups 420 | had to be completely unique within a regexp. 421 | Now they're allowed to be repeated 422 | as long as they're in different branches of an alternative, 423 | like `/foo(?.*)|(?.*)foo/`.) 424 | 425 | 426 | #### Examples 427 | 428 | ```js 429 | (x or [let y]) and (z or {key: let y}) 430 | ``` 431 | 432 | Valid at parse-time: both binding patterns name `y` 433 | with `let` semantics. 434 | This establishes a `y` binding in the nearest block scope. 435 | 436 | If x *or* z matches, but not both, 437 | then `y` gets bound appropriately. 438 | If neither matches, `y` remains uninitialized 439 | (so it's a runtime ReferenceError to use it). 440 | If both match, a runtime ReferenceError is thrown 441 | while executing the second `let y` pattern, 442 | as its binding has already been initialized. 443 | 444 | ```js 445 | (x or [let y]) and (z or {key: const y}) 446 | ``` 447 | Early ReferenceError, as `y` is being bound twice 448 | with differing semantics. 449 | 450 | ```js 451 | x and let y and z and if(y == "foo") 452 | ``` 453 | Valid at parse-time, establishes a `y` binding in block scope. 454 | 455 | If x doesn't match, 456 | `y` remains uninitialized, 457 | but the guard pattern is also skipped, 458 | so no runtime error (yet). 459 | If z doesn't match, 460 | `y` is initialized to the match subject, 461 | but the `if()` test never runs. 462 | 463 | ```js 464 | [let x and String] or {length: let x} 465 | ``` 466 | Valid at parse-time, establishes an `x` binding. 467 | 468 | [`or` pattern](#or-patterns) semantics allow overriding an already-initialized binding, 469 | if that binding came from an earlier failed sub-pattern, 470 | to avoid forcing authors to awkwardly arrange their binding patterns 471 | after the fallible tests. 472 | 473 | So in this example, if passed an object like `[5]`, 474 | it will pass the initial length check, 475 | execute the `let x` pattern and bind it to `5`, 476 | then fail the `String` pattern, 477 | as the subject is a `Number`. 478 | It will then continue to the next `or` sub-pattern, 479 | and successfully bind `x` to 1, 480 | as the existing binding was initialized in a failed sub-pattern. 481 | 482 | 483 | 484 | ## Structure Patterns 485 | 486 | Structure patterns let you test the structure of the subject 487 | (its properties, its length, etc) 488 | and then recurse into that structure with additional matcher patterns. 489 | 490 | ### Array Patterns 491 | 492 | A comma-separated list of zero or more patterns, surrounded by square brackets. 493 | It represents a test that: 494 | 495 | 1. The subject is iterable. 496 | 2. The subject contains exactly as many iteration items 497 | as the length of the array pattern. 498 | 3. Each item matches the associated sub-pattern. 499 | 500 | For example, `["foo", {bar}]` will match 501 | when the subject is an iterable with exactly two items, 502 | the first item is the string `"foo"`, 503 | and the second item has a `bar` property. 504 | 505 | The final item in the array pattern can optionally be a "rest pattern": 506 | either a literal `...`, 507 | or a `...` followed by another pattern. 508 | In either case, the presence of a rest pattern relaxes the length test 509 | (2 in the list above) 510 | to merely check that the subject has *at least* as many items 511 | as the array pattern, 512 | ignoring the rest pattern. 513 | That is, `[a, b, ...]` will only match a subject 514 | who contains 2 or more items. 515 | 516 | If the `...` is followed by a pattern, 517 | like `[a, b, ...let c]`, 518 | then the iterator is fully exhausted, 519 | all the leftover items are collected into an `Array`, 520 | and that array is then applied as the subject of the rest pattern's test. 521 | 522 | Note: The above implies that `[a, b]` will pull three items from the subject: 523 | two to match against the sub-patterns, 524 | and a third to verify that the subject doesn't *have* a third item. 525 | `[a, b, ...]` will pull only two items from the subject, 526 | to match against the sub-patterns. 527 | `[a, b, ...c]` will exhaust the subject's iterator, 528 | verifying it has at least two items 529 | (to match against the sub-patterns) 530 | and then pulling the rest to match against the rest pattern. 531 | 532 | Array pattern execution order is as follows: 533 | 534 | 1. Obtain an iterator from the subject. Return failure if this fails. 535 | 2. For each expected item up to the number of sub-patterns (ignoring the rest pattern, if present): 536 | 1. Pull one item from the iterator. Return failure if this fails. 537 | 2. Execute the corresponding pattern. Return failure if this doesn't match. 538 | 3. If there is no rest pattern, pull one more item from the iterator, verifying that it's a `{done: true}` result. If so, return success; if not, return failure. 539 | 4. If there is a `...` rest pattern, return success. 540 | 5. If there is a `...` rest pattern, pull the remaining items of the iterator into a fresh `Array`, then match the pattern against that. If it matches, return success; otherwise return failure. 541 | 542 | Issue: Or should we pull all the necessary values from the iterator first, 543 | *then* do all the matchers? 544 | 545 | #### Examples 546 | 547 | ```js 548 | match (res) { 549 | when isEmpty: ...; 550 | when {data: [let page] }: ...; 551 | when {data: [let frontPage, ...let pages] }: ...; 552 | default: ...; 553 | } 554 | ``` 555 | 556 | [**Array patterns**](#array-patterns) implicitly check the length of the subject. 557 | 558 | The first arm is a [variable pattern](#variable-patterns), 559 | invoking the default `Function.prototype` custom matcher 560 | which calls `isEmpty(res)` 561 | and matches if that returns `true`. 562 | 563 | The second arm is an [object pattern](#object-patterns) 564 | which contains an [array pattern](#array-patterns), 565 | which matches if `data` has exactly one element, 566 | and binds that element to `page` for the RHS. 567 | 568 | The third arm matches if `data` has **at least one** element, 569 | binding that first element to `frontPage`, 570 | and binding an array of any remaining elements to `pages` 571 | using a rest pattern. 572 | 573 | 574 | #### Array Pattern Caching 575 | 576 | To allow for idiomatic uses of generators 577 | and other "single-shot" iterators 578 | to be reasonably matched against several array patterns, 579 | the iterators and their results are cached over the scope of the match construct. 580 | 581 | Specifically, whenever a subject is matched against an array pattern, 582 | the subject is used as the key in a cache, 583 | whose value is the iterator obtained from the subject, 584 | and all items pulled from the subject by an array pattern. 585 | 586 | Whenever something would be matched against an array pattern, 587 | the cache is first checked, 588 | and the already-pulled items stored in the cache are used for the pattern, 589 | with new items pulled from the iterator only if necessary. 590 | 591 | For example: 592 | 593 | ```js 594 | function* integers(to) { 595 | for(var i = 1; i <= to; i++) yield i; 596 | } 597 | 598 | const fiveIntegers = integers(5); 599 | match (fiveIntegers) { 600 | when [let a]: 601 | console.log(`found one int: ${a}`); 602 | // Matching a generator against an array pattern. 603 | // Obtain the iterator (which is just the generator itself), 604 | // then pull two items: 605 | // one to match against the `a` pattern (which succeeds), 606 | // the second to verify the iterator only has one item 607 | // (which fails). 608 | when [let a, let b]: 609 | console.log(`found two ints: ${a} and ${b}`); 610 | // Matching against an array pattern again. 611 | // The generator object has already been cached, 612 | // so we fetch the cached results. 613 | // We need three items in total; 614 | // two to check against the patterns, 615 | // and the third to verify the iterator has only two items. 616 | // Two are already in the cache, 617 | // so we’ll just pull one more (and fail the pattern). 618 | default: console.log("more than two ints"); 619 | } 620 | console.log([...fiveIntegers]); 621 | // logs [4, 5] 622 | // The match construct pulled three elements from the generator, 623 | // so there’s two leftover afterwards. 624 | ``` 625 | 626 | When execution of the match construct finishes, all cached iterators are closed. 627 | 628 | 629 | ### Object Patterns 630 | 631 | A comma-separated list of zero or more "object pattern clauses", wrapped in curly braces. 632 | Each "object pattern clause" is either ``, `let/var/const ` or `: `, 633 | where `` is an identifier or a computed-key expression like `[Symbol.foo]`. 634 | It represents a test that the subject: 635 | 636 | 1. Has every specified property in its prototype chain. 637 | 2. If the key has an associated sub-pattern, 638 | then the value of that property matches the sub-pattern. 639 | 640 | A `` object pattern clause 641 | is exactly equivalent to `: void`. 642 | A `let/var/const ` object pattern clause 643 | is exactly equivalent to `: let/var/const `. 644 | 645 | That is, `when {foo, let bar, baz: "qux"}` 646 | is equivalent to `when {foo: void, bar: let bar, baz: "qux"}`: 647 | it tests that the subject has `foo`, `bar`, and `baz` properties, 648 | introduces a `bar` binding for the value of the `bar` property, 649 | and verifies that the value of the `baz` property is the string `"qux"`. 650 | 651 | Additionally, object patterns can contain a "rest pattern": 652 | a `...` followed by a pattern. 653 | Unlike array patterns, a lone `...` is not valid in an object pattern 654 | (since there's no strict check to relax). 655 | If the rest pattern exists, 656 | then all *enumerable own properties* 657 | that aren't already matched by object pattern clauses 658 | are collected into a fresh object, 659 | which is then matched against the rest pattern. 660 | (This matches the behavior of object destructuring.) 661 | 662 | Issue: Do we want a `key?: pattern` pattern clause as well? 663 | Makes it an optional test - 664 | *if* the subject has this property, 665 | verify that it matches the pattern. 666 | If the pattern is skipped because the property doesn't exist, 667 | treat any bindings coming from the pattern 668 | the same as ones coming from skipped `or` patterns. 669 | 670 | Issue: Ban `__proto__`? Do something funky? 671 | 672 | Object pattern execution order is as follows: 673 | 674 | 1. For each non-rest object pattern clause `key: sub-pattern`, in source order: 675 | 1. Check that the subject has the property `key` (using `in`, or `HasProperty()`, semantics). If it doesn't, return failure. 676 | 2. Get the value of the `key` property, and match it against `sub-pattern`. If that fails to match, return failure. 677 | 2. If there's a rest pattern clause, 678 | collect all enumerable own properties of the subject 679 | that weren't tested in the previous step, 680 | and put them into a fresh `Object`. 681 | Match that against the rest pattern. 682 | If that fails, return failure. 683 | 3. Return success. 684 | 685 | #### Examples 686 | 687 | ```js 688 | ``` 689 | 690 | #### Object Pattern Caching 691 | 692 | Similar to [array pattern caching](#array-pattern-caching), 693 | object patterns cache their results over the scope of the match construct, 694 | so that multiple clauses don’t observably retrieve the same property multiple times. 695 | 696 | (Unlike array pattern caching, 697 | which is necessary for this proposal to work with iterators, 698 | object pattern caching is a nice-to-have. 699 | It does guard against some weirdness like non-idempotent getters 700 | (including, notably, getters that return iterators), 701 | and helps make idempotent-but-expensive getters usable in pattern matching 702 | without contortions, 703 | but mostly it’s just for conceptual consistency.) 704 | 705 | Whenever a subject is matched against an object pattern, 706 | for each property name in the object pattern, 707 | a `(, )` tuple is used as the key in a cache, 708 | whose value is the value of the property. 709 | 710 | Whenever something would be matched against an object pattern, 711 | the cache is first checked, 712 | and if the subject and that property name are already in the cache, 713 | the value is retrieved from cache instead of by a fresh Get against the subject. 714 | 715 | For example: 716 | 717 | ```js 718 | const randomItem = { 719 | get numOrString() { return Math.random() < .5 ? 1 : "1"; } 720 | }; 721 | 722 | match (randomItem) { 723 | when {numOrString: Number}: 724 | console.log("Only matches half the time."); 725 | // Whether the pattern matches or not, 726 | // we cache the (randomItem, "numOrString") pair 727 | // with the result. 728 | when {numOrString: String}: 729 | console.log("Guaranteed to match the other half of the time."); 730 | // Since (randomItem, "numOrString") has already been cached, 731 | // we reuse the result here; 732 | // if it was a string for the first clause, 733 | // it’s the same string here. 734 | } 735 | ``` 736 | 737 | Issue: This potentially introduces a lot more caching, 738 | and the major use-case is just making sure that iterator caching 739 | works both at the top-level and when nested in an object. 740 | Expensive or non-idempotent getters benefit, 741 | but that's a much less important benefit. 742 | This caching *is* potentially droppable, 743 | but it will mean that we only cache iterables at the top level. 744 | 745 | ### Extractor Patterns 746 | 747 | A dotted-ident followed by a parenthesized "argument list" 748 | containing the same syntax as an [array matcher](#array-matcher). 749 | Represents a combination of a [custom matcher pattern](#custom-matcher-pattern) 750 | and an [array pattern](#array-patterns): 751 | 752 | 1. The dotted-ident is resolved against the visible bindings. 753 | If that results in an object with a `Symbol.customMatcher` property, 754 | and the value of that property is a function, 755 | then continue; 756 | otherwise, this throws an XXX error. 757 | 758 | 2. The custom matcher function is invoked with the subject as its first argument, 759 | and an object with the key `"matchType"` set to `"extractor"` 760 | as its second argument. 761 | Let result be the return value. 762 | 763 | 3. Match result against the [arglist pattern](#arglist-patterns). 764 | 765 | Note: While [custom matchers](#custom-matchers) only require the return value be *truthy* or *falsey*, 766 | extractor patterns are stricter about types: 767 | the value must be *exactly* `true` or `false`, 768 | or an `Array`, 769 | or an iterable. 770 | 771 | #### Arglist Patterns 772 | 773 | An arglist pattern is a sub-pattern of an Extractor Pattern, 774 | and is mostly identical to an [Array Pattern](#array-patterns). 775 | It has identical syntax, 776 | except it's bounded by parentheses (`()`) 777 | rather than square brackets (`[]`). 778 | It behaves slightly differently with a few subjects, as well: 779 | 780 | * a `false` subject always fails to match 781 | * a `true` subject matches as if it were an empty Array 782 | * an `Array` subject is matched per-index, 783 | rather than invoking the iterator protocol. 784 | 785 | If the subject is an `Array`, 786 | then it's matched as follows: 787 | 788 | 1. If the arglist pattern doesn't end in a rest pattern, 789 | then the subject's `length` property must exactly equal 790 | the length of the pattern, 791 | or it fails to match. 792 | 793 | 2. If the arglist pattern *does* end in a rest pattern, 794 | then the subject's `length` property must be equal or greater 795 | than the length of the pattern - 1, 796 | or it fails to match. 797 | 798 | 3. For each non-rest sub-pattern of the arglist pattern, 799 | the corresponding integer-valued property of the subject is fetched, 800 | and matched against the corresponding sub-pattern. 801 | 802 | 4. If the final sub-pattern is a `...`, 803 | collect the remaining integer-valued properties of the subject, 804 | up to but not including its `length` value, 805 | into a fresh Array, 806 | and match against that pattern. 807 | 808 | 5. If any of the matches failed, 809 | the entire arglist pattern fails to match. 810 | Otherwise, it succeeds. 811 | 812 | Other than the above exceptions, 813 | arglist patterns are matched 814 | exactly the same as array patterns. 815 | 816 | Issue: Do we cache arglists the same way we cache array patterns? 817 | 818 | Note: The `Array` behavior here 819 | is for performance, based on implementor feedback. 820 | Invoking the iterator protocol is expensive, 821 | and we don't want to discourage use of custom matchers 822 | when the *by far* expected usage pattern 823 | is to just return an `Array`, 824 | rather than some more complex iterable. 825 | We're (currently) still sticking with iterator protocol for array matchers, 826 | to match destructuring, 827 | but could potentially change that. 828 | 829 | 830 | 831 | #### Examples 832 | 833 | ```js 834 | class Option { 835 | constructor() { throw new TypeError(); } 836 | static Some = class extends Option { 837 | constructor(value) { this.value = value; } 838 | map(cb) { return new Option.Some(cb(this.value)); } 839 | // etc 840 | static [Symbol.customMatcher](subject) { 841 | if (subject instanceof Option.Some) { return [subject.value]; } 842 | return false; 843 | } 844 | }; 845 | 846 | static None = class extends Option { 847 | constructor() { } 848 | map(cb) { return this; } 849 | // Use the default custom matcher, 850 | // which just checks that the subject matches the class. 851 | }; 852 | } 853 | 854 | let val = Option.Some(5); 855 | match(val) { 856 | when Option.Some(String and let a): console.log(`Got a string "${a}".`); 857 | when Option.Some(Number and let a): console.log(`Got a number ${a}.`); 858 | when Option.Some(...): console.log(`Got something unexpected.`); 859 | // Or `Option.Some`, either works. 860 | // `Option.Some()` will never match, as the return value 861 | // is a 1-item array, which doesn't match `[]` 862 | when Option.None(): console.log(`Operation failed.`); 863 | // or `Option.None`, either works 864 | default: console.log(`Didn't get an Option at all.`) 865 | } 866 | ``` 867 | 868 | Issue: We don't have an easy way to get access to the "built-in" custom matcher, 869 | so the above falls back to doing an instanceof test 870 | (rather than the technically more correct branding test 871 | that the built-in one does). 872 | To work "properly" I'd have to define the class without a custom matcher, 873 | then pull off the custom matcher, 874 | save it to a local variable, 875 | and define a new custom matcher that invokes the original one 876 | and returns the `[subject.value]` on success. 877 | That's a silly amount of work for correctness. 878 | 879 | 880 | ## Combinator Patterns 881 | 882 | Sometimes you need to match multiple patterns on a single value, 883 | or pass a value that matches any of several patterns, 884 | or just negate a pattern. 885 | All of these can be achieved with combinator patterns. 886 | 887 | ### And Patterns 888 | 889 | Two or more patterns, each separated by the keyword `and`. 890 | This represents a test 891 | that the subject passes *all* of the sub-patterns. 892 | Any pattern can be 893 | (and in some cases must be, see [combining combinators](#combining-combinator-patterns)) 894 | wrapped in parentheses. 895 | 896 | Short-circuiting applies; if any sub-pattern fails to match the subject, 897 | matching stops immediately. 898 | 899 | `and` pattern execution order is as follows: 900 | 901 | 1. For each sub-pattern, in source order, match the subject against the sub-pattern. If that fails to match, return failure. 902 | 2. Return success. 903 | 904 | 905 | ### Or Patterns 906 | 907 | Two or more patterns, each separated by the keyword `or`. 908 | This represents a test 909 | that the subject passes *at least one* of the sub-patterns. 910 | Any pattern can be 911 | (and in some cases must be, see [combining combinators](#combining-combinator-patterns)) 912 | wrapped in parentheses. 913 | 914 | Short-circuiting applies; if any sub-pattern successfully matches the subject, 915 | matching stops immediately. 916 | 917 | `or` pattern execution order is as follows: 918 | 919 | 1. For each sub-pattern, in source order, match the subject against the sub-pattern. If that successfully matches, return success. 920 | 2. Return failure. 921 | 922 | Note: As defined in [Binding Behavior Details](#binding-behavior-details), 923 | a [binding pattern](#binding-patterns) in a failed sub-pattern 924 | can be overridden by a binding pattern in a later sub-pattern 925 | without error. 926 | That is, `[let foo] or {length: let foo}` is valid 927 | both at parse-time and run-time, 928 | even tho the `foo` binding is potentially initialized twice 929 | (given a subject like `[1, 2]`). 930 | 931 | 932 | ### Not Patterns 933 | 934 | A pattern preceded by the keyword `not`. 935 | This represents a test that the subject *does not* match the sub-pattern. 936 | The pattern can be 937 | (and in some cases must be, see [combining combinators](#combining-combinator-patterns)) 938 | wrapped in parentheses. 939 | 940 | 941 | ### Combining Combinator Patterns 942 | 943 | Combinator patterns cannot be combined at the same "level"; 944 | there is no precedence relationship between them. 945 | Instead, parentheses must be used to explicitly provide an ordering. 946 | 947 | That is, `foo and bar or baz` is a syntax error; 948 | it must be written `(foo and bar) or baz` 949 | or `foo and (bar or baz)`. 950 | 951 | Similarly, `not foo and bar` is a syntax error; 952 | it must be written `(not foo) and bar` 953 | or `not (foo and bar)`. 954 | 955 | 956 | ## Guard Patterns 957 | 958 | A guard pattern has the syntax `if()`, 959 | and represents a test that the expression is truthy. 960 | This is an arbitrary JS expression, 961 | *not* a pattern. 962 | 963 | 964 | 965 | # `match` expression 966 | 967 | `match` expressions are a new type of expression 968 | that makes use of [patterns](#patterns) 969 | to select one of several expressions to resolve to. 970 | 971 | A match expression looks like: 972 | 973 | ```js 974 | match() { 975 | when : ; 976 | when : ; 977 | ... 978 | default: ; 979 | } 980 | ``` 981 | 982 | That is, the `match` head contains a ``, 983 | which is an arbitrary JS expression 984 | that evaluates to a "subject". 985 | 986 | The `match` block contains zero or more "match arms", 987 | consisting of: 988 | * the keyword `when` 989 | * a [pattern](#patterns) 990 | * a literal colon 991 | * an arbitrary JS expression 992 | * a semicolon (yes, required) 993 | 994 | After the match arms, 995 | it can optionally contain default a "default arm", 996 | consisting of: 997 | * the keyword `default` 998 | * a literal colon 999 | * an arbitrary JS expression 1000 | * a semicolon 1001 | 1002 | After obtaining the subject, 1003 | each match arm is tested in turn, 1004 | matching the subject against the arm's pattern. 1005 | If the match is successful, 1006 | the arm's expression is evaluated, 1007 | and the `match` expression resolves to that result. 1008 | 1009 | If all match arms fail to match, 1010 | and there is a default arm, 1011 | the default arm's expression is evaluated, 1012 | and the `match` expression resolves to that result. 1013 | If there is no default arm, 1014 | the `match` expression throws a `TypeError`. 1015 | 1016 | ## Bindings 1017 | 1018 | The `` is part of the nearest block scope. 1019 | 1020 | Each match arm and the default arm 1021 | are independent nested block scopes, 1022 | covering both the pattern and the expression of the arm. 1023 | (That is, different arms can't see each other's bindings, 1024 | and the bindings don't escape the `match` expression. 1025 | Within each arm, they shadow the outer scope's bindings.) 1026 | 1027 | ## Examples 1028 | 1029 | ```jsx 1030 | match (res) { 1031 | when { status: 200, let body, ...let rest }: handleData(body, rest); 1032 | when { const status, destination: let url } and if (300 <= status && status < 400): 1033 | handleRedirect(url); 1034 | when { status: 500 } and if (!this.hasRetried): do { 1035 | retry(req); 1036 | this.hasRetried = true; 1037 | }; 1038 | default: throwSomething(); 1039 | } 1040 | ``` 1041 | 1042 | This example tests a "response" object against several patterns, 1043 | branching based on the `.status` property, 1044 | and extracting different parts from the response in each branch 1045 | to process in various handler functions. 1046 | 1047 | ----- 1048 | 1049 | ```js 1050 | match (command) { 1051 | when ['go', let dir and ('north' or 'east' or 'south' or 'west')]: go(dir); 1052 | when ['take', /[a-z]+ ball/ and {let weight}: takeBall(weight); 1053 | default: lookAround() 1054 | } 1055 | ``` 1056 | 1057 | This sample is a contrived parser for a text-based adventure game. 1058 | 1059 | The first match arm matches if the command is an array with exactly two items. 1060 | The first must be exactly the string `'go'`, 1061 | and the second must be one of the given cardinal directions. 1062 | Note the use of the [**and pattern**](#and-patterns) 1063 | to bind the second item in the array to `dir` 1064 | using a [**binding pattern**](#binding-patterns) 1065 | before verifying (using the [or pattern](#or-patterns)) 1066 | that it’s one of the given directions. 1067 | 1068 | The second match arm is slightly more complex. 1069 | First, a [regex pattern](#regex-patterns) is used 1070 | to verify that the object stringifies to `"something ball"`, 1071 | then an [object patterns](#object-patterns) 1072 | verifies that it has a `.weight` property 1073 | and binds it to `weight`, 1074 | so that the weight is available to the arm's expression. 1075 | 1076 | ## Statement vs Expression 1077 | 1078 | For maximum expressivity, 1079 | the `match` expression is an expression, not a statement. 1080 | This allows for easy use in expression contexts 1081 | like `return match(val){...}`. 1082 | 1083 | It can, of course, be used in statement context, 1084 | as in the first example above. 1085 | However, the match arms still contain expressions only. 1086 | 1087 | It is *expected* that do-expressions will allow 1088 | for match arms to execute statements 1089 | (again, as in the first example above). 1090 | If that proposal does not end up advancing, 1091 | a future iteration of this proposal will include some way 1092 | to have a match arm contain statements. 1093 | (Probably just by inlining do-expr's functionality.) 1094 | 1095 | # `is` operator 1096 | 1097 | The `is` operator is a new boolean operator, 1098 | of the form ` is `. 1099 | It returns a boolean result, 1100 | indicating whether the subject matched the pattern or not. 1101 | 1102 | ## Bindings 1103 | 1104 | Bindings established in the pattern of an `is` 1105 | are visible in the nearest block scope, 1106 | as defined in [Binding Patterns](#binding-patterns). 1107 | 1108 | This includes when used in the head of an `if()` statement: 1109 | 1110 | ```js 1111 | function foo(x) { 1112 | if(x is [let head, ...let rest]) { 1113 | console.log(head, rest); 1114 | } else { 1115 | // `head` and `rest` are defined here, 1116 | // but will throw a ReferenceError if dereferenced, 1117 | // since if the pattern failed 1118 | // the binding patterns must not have been executed. 1119 | } 1120 | } 1121 | 1122 | function bar(x) { 1123 | if(x is not {let necessaryProperty}) { 1124 | // Pattern succeeded, because `x.necessaryProperty` 1125 | // doesn't exist. 1126 | return; 1127 | } 1128 | // Here the pattern failed because `x.necessaryProperty` 1129 | // *does* exist, so the binding pattern was executed, 1130 | // and the `necessaryProperty` binding is visible here. 1131 | console.log(necessaryProperty); 1132 | } 1133 | ``` 1134 | 1135 | When used in the head of a `for()`, 1136 | the usual binding scopes apply: 1137 | the bindings are scoped to the `for()` head+block, 1138 | and in the case of `for-of`, 1139 | are copied to the inner per-iteration binding scopes. 1140 | 1141 | `while` and `do-while` do not currently have any special scoping rules 1142 | for things in their heads. 1143 | We propose that they adopt the same rules as `for-of` blocks: 1144 | the head is in a new scope surrounding the rule, 1145 | and its bindings are copied to a per-iteration scope 1146 | surrounding the `{}` block. 1147 | For do-while, 1148 | the bindings are TDZ on the first iteration, 1149 | before the head is executed. 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | 1156 | # Motivating examples 1157 | 1158 | Below are selected situations where we expect pattern matching will be widely 1159 | used. As such, we want to optimize the ergonomics of such cases to the best of 1160 | our ability. 1161 | 1162 | ------ 1163 | 1164 | Validating JSON structure. 1165 | 1166 | Here's the simple destructuring version of the code, 1167 | which does zero checks on the data ahead of time, 1168 | just pulls it apart and hopes everything is correct: 1169 | 1170 | ```js 1171 | var json = { 1172 | 'user': ['Lily', 13] 1173 | }; 1174 | var {user: [name, age]} = json; 1175 | print(`User ${name} is ${age} years old.`); 1176 | ``` 1177 | 1178 | Destructuring with checks that everything is correct and of the expected shape: 1179 | 1180 | ```js 1181 | if ( json.user !== undefined ) { 1182 | var user = json.user; 1183 | if (Array.isArray(user) && 1184 | user.length == 2 && 1185 | typeof user[0] == "string" && 1186 | typeof user[1] == "number") { 1187 | var [name, age] = user; 1188 | print(`User ${name} is ${age} years old.`); 1189 | } 1190 | } 1191 | ``` 1192 | 1193 | Exactly the same checks, but using pattern-matching: 1194 | 1195 | ```js 1196 | if( json is {user: [String and let name, Number and let age]} ) { 1197 | print(`User ${name} is ${age} years old.`); 1198 | } 1199 | ``` 1200 | 1201 | ------ 1202 | 1203 | Matching `fetch()` responses: 1204 | 1205 | ```jsx 1206 | const res = await fetch(jsonService) 1207 | match (res) { 1208 | when { status: 200, headers: { 'Content-Length': let s } }: 1209 | console.log(`size is ${s}`); 1210 | when { status: 404 }: 1211 | console.log('JSON not found'); 1212 | when { let status } and if (status >= 400): do { 1213 | throw new RequestError(res); 1214 | } 1215 | }; 1216 | ``` 1217 | 1218 | --- 1219 | 1220 | More concise, more functional handling of Redux reducers (compare with 1221 | [this same example in the Redux documentation](https://redux.js.org/basics/reducers#splitting-reducers)): 1222 | 1223 | ```jsx 1224 | function todosReducer(state = initialState, action) { 1225 | return match (action) { 1226 | when { type: 'set-visibility-filter', payload: let visFilter }: 1227 | { ...state, visFilter }; 1228 | when { type: 'add-todo', payload: let text }: 1229 | { ...state, todos: [...state.todos, { text, completed: false }] }; 1230 | when { type: 'toggle-todo', payload: let index }: do { 1231 | const newTodos = state.todos.map((todo, i) => { 1232 | return i !== index ? todo : { 1233 | ...todo, 1234 | completed: !todo.completed 1235 | }; 1236 | }); 1237 | 1238 | ({ 1239 | ...state, 1240 | todos: newTodos, 1241 | }); 1242 | } 1243 | default: state // ignore unknown actions 1244 | } 1245 | } 1246 | ``` 1247 | 1248 | --- 1249 | 1250 | Concise conditional logic in JSX (via 1251 | [Divjot Singh](https://twitter.com/bogas04/status/977499729557839873)): 1252 | 1253 | ```jsx 1254 | 1255 | {props => match (props) { 1256 | when {loading}: ; 1257 | when {let error}: do { 1258 | console.err("something bad happened"); 1259 | 1260 | }; 1261 | when {let data}: ; 1262 | }} 1263 | 1264 | ``` 1265 | 1266 | 1267 | # Possible future enhancements 1268 | 1269 | ## Void Patterns 1270 | 1271 | The keyword `void` is a pattern 1272 | that always matches, 1273 | and does nothing else. 1274 | It's useful in structure patterns, 1275 | when you want to test for the existence of a property 1276 | without caring what its value is. 1277 | 1278 | This is the most likely proposal to move back into the main proposal; 1279 | it's pulled out solely because we want to make sure 1280 | that it stays consistent 1281 | with [Void Bindings](https://github.com/tc39/proposal-discard-binding). 1282 | 1283 | ## `async match` 1284 | 1285 | If the `match` construct appears inside a context where `await` is allowed, 1286 | `await` can already be used inside it, just like inside `do` expressions. 1287 | However, just like `async do` expressions, there’s uses of being able to use 1288 | `await` and produce a Promise, even when not already inside an `async function`. 1289 | 1290 | ```js 1291 | async match (await subject) { 1292 | when { let a }: await a; 1293 | when { let b }: b.then(() => 42); 1294 | default: await somethingThatRejects(); 1295 | } // produces a Promise 1296 | ``` 1297 | 1298 | ## Relational Patterns 1299 | 1300 | Currently there are patterns for expressing various types of equality, 1301 | and kinda an instanceof (for custom matchers against a class). 1302 | We could express more types of operator-based checks, 1303 | like: 1304 | 1305 | ```js 1306 | match(val) { 1307 | when < 10: console.log("small"); 1308 | when >= 10 and < 20: console.log("mid"); 1309 | default: "large"; 1310 | } 1311 | ``` 1312 | 1313 | Generally, all the binary boolean operators could be used, 1314 | with the subject as the implicit LHS of the operator. 1315 | 1316 | (This would slightly tie our hands on future syntax expansions for patterns, 1317 | but it's unlikely we'd ever *want* to reuse existing operators 1318 | in a way that's different from how they work in expression contexts.) 1319 | 1320 | ## Default Values 1321 | 1322 | Destructuring can supply a default value with `= ` which is used when a 1323 | key isn’t present. Is this useful for pattern matching? 1324 | 1325 | Optional keys seem reasonable; right now they’d require duplicating the pattern 1326 | like `({a, b} or {a})` (`b` will be bound to undefined in the RHS if not present). 1327 | 1328 | Do we need/want full defaulting? Does it complicate the syntax to much to have 1329 | arbitrary JS expressions there, without anything like wrapper characters to 1330 | distinguish it from surrounding patterns? 1331 | 1332 | This would bring us into closer alignment with destructuring, which is nice. 1333 | 1334 | 1335 | ## Destructuring enhancements 1336 | 1337 | Both destructuring and pattern matching should remain in sync, so enhancements 1338 | to one would need to work for the other. 1339 | 1340 | ## Integration with `catch` 1341 | 1342 | Allow a `catch` statement to conditionally catch an exception, saving a level of 1343 | indentation: 1344 | 1345 | ```js 1346 | try { 1347 | throw new TypeError('a'); 1348 | } catch match (e) { 1349 | when RangeError: ...; 1350 | when /^abc$/: ...; 1351 | // unmatched, default to rethrowing e 1352 | } 1353 | ``` 1354 | 1355 | Or possibly just allow an `is` check in the catch head: 1356 | 1357 | ```js 1358 | try { 1359 | throw new TypeError('a'); 1360 | } catch (e is RangeError) { 1361 | ... 1362 | } catch (e is /^abc$/) { 1363 | ... 1364 | } 1365 | ``` 1366 | 1367 | (In both cases, the name used for the subject automatically creates a binding, 1368 | same as `catch (e)` does today.) 1369 | 1370 | ## Chaining guards 1371 | 1372 | Some reasonable use-cases require repetition of patterns today, like: 1373 | 1374 | ```js 1375 | match (res) { 1376 | when { status: 200 or 201, let pages, let data } and if (pages > 1): 1377 | handlePagedData(pages, data); 1378 | when { status: 200 or 201, let pages, let data } and if (pages === 1): 1379 | handleSinglePage(data); 1380 | default: handleError(res); 1381 | } 1382 | ``` 1383 | 1384 | We might want to allow match constructs to be chained, where the child match 1385 | construct sees the bindings introduced in their parent clause, and which will 1386 | cause the entire parent clause to fail if none of the sub-classes match. 1387 | 1388 | The above would then be written as: 1389 | 1390 | ```js 1391 | match (res) { 1392 | when { status: 200 or 201, let data } match { 1393 | when { pages: 1 }: handleSinglePage(data); 1394 | when { pages: >= 2 and let pages }: handlePagedData(pages, data); 1395 | }; 1396 | default: handleError(res); 1397 | // runs if the status indicated an error, 1398 | // or if the data didn't match one of the above cases, 1399 | // notably if pages == 0 1400 | } 1401 | ``` 1402 | 1403 | Note the lack of a `` in the child (just `match {...}`), to 1404 | signify that it’s chaining from the `when` rather than just being part an 1405 | independent match construct in the RHS (which would, instead, throw if none of 1406 | the clauses match): 1407 | 1408 | ```js 1409 | match (res) { 1410 | when { status: 200 or 201, let data }: match(res) { 1411 | when { pages: 1}: handleSinglePage(data); 1412 | when { pages: >= 2 and let pages}: handlePagedData(pages, data); 1413 | // just an RHS, so if pages == 0, 1414 | // the inner construct fails to match anything 1415 | // and throws a TypeError 1416 | }; 1417 | default: handleError(res); 1418 | } 1419 | ``` 1420 | 1421 | The presence or absence of the separator colon also distinguishes these cases, 1422 | of course. 1423 | 1424 | 1425 | 1431 | -------------------------------------------------------------------------------- /assets/IBMPlexMono-Bold-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-pattern-matching/7a554cd7c771c21a3c9523a0f92cabe2d9687ae9/assets/IBMPlexMono-Bold-SlashedZero.woff2 -------------------------------------------------------------------------------- /assets/IBMPlexMono-BoldItalic-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-pattern-matching/7a554cd7c771c21a3c9523a0f92cabe2d9687ae9/assets/IBMPlexMono-BoldItalic-SlashedZero.woff2 -------------------------------------------------------------------------------- /assets/IBMPlexMono-Italic-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-pattern-matching/7a554cd7c771c21a3c9523a0f92cabe2d9687ae9/assets/IBMPlexMono-Italic-SlashedZero.woff2 -------------------------------------------------------------------------------- /assets/IBMPlexMono-Regular-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-pattern-matching/7a554cd7c771c21a3c9523a0f92cabe2d9687ae9/assets/IBMPlexMono-Regular-SlashedZero.woff2 -------------------------------------------------------------------------------- /assets/IBMPlexSans-Bold-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-pattern-matching/7a554cd7c771c21a3c9523a0f92cabe2d9687ae9/assets/IBMPlexSans-Bold-SlashedZero.woff2 -------------------------------------------------------------------------------- /assets/IBMPlexSans-BoldItalic-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-pattern-matching/7a554cd7c771c21a3c9523a0f92cabe2d9687ae9/assets/IBMPlexSans-BoldItalic-SlashedZero.woff2 -------------------------------------------------------------------------------- /assets/IBMPlexSans-Italic-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-pattern-matching/7a554cd7c771c21a3c9523a0f92cabe2d9687ae9/assets/IBMPlexSans-Italic-SlashedZero.woff2 -------------------------------------------------------------------------------- /assets/IBMPlexSans-Regular-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-pattern-matching/7a554cd7c771c21a3c9523a0f92cabe2d9687ae9/assets/IBMPlexSans-Regular-SlashedZero.woff2 -------------------------------------------------------------------------------- /assets/IBMPlexSerif-Bold-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-pattern-matching/7a554cd7c771c21a3c9523a0f92cabe2d9687ae9/assets/IBMPlexSerif-Bold-SlashedZero.woff2 -------------------------------------------------------------------------------- /assets/IBMPlexSerif-BoldItalic-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-pattern-matching/7a554cd7c771c21a3c9523a0f92cabe2d9687ae9/assets/IBMPlexSerif-BoldItalic-SlashedZero.woff2 -------------------------------------------------------------------------------- /assets/IBMPlexSerif-Italic-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-pattern-matching/7a554cd7c771c21a3c9523a0f92cabe2d9687ae9/assets/IBMPlexSerif-Italic-SlashedZero.woff2 -------------------------------------------------------------------------------- /assets/IBMPlexSerif-Regular-SlashedZero.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-pattern-matching/7a554cd7c771c21a3c9523a0f92cabe2d9687ae9/assets/IBMPlexSerif-Regular-SlashedZero.woff2 -------------------------------------------------------------------------------- /assets/ecma-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-pattern-matching/7a554cd7c771c21a3c9523a0f92cabe2d9687ae9/assets/ecma-header.png -------------------------------------------------------------------------------- /assets/ecmarkup.css: -------------------------------------------------------------------------------- 1 | /* IBM Plex fonts (Latin subset) have been downloaded from Google Fonts and modified to add support back in for the `zero` substitution (slashed zeroes) */ 2 | 3 | /* https://fonts.googleapis.com/css2?family=IBM%20Plex%20Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap */ 4 | @font-face { 5 | font-family: 'IBM Plex Serif'; 6 | font-style: normal; 7 | font-weight: 400; 8 | font-display: swap; 9 | src: local(IBM Plex Serif Regular), local(IBMPlexSerif-Regular), url(./IBMPlexSerif-Regular-SlashedZero.woff2) format('woff2'); 10 | } 11 | @font-face { 12 | font-family: 'IBM Plex Serif'; 13 | font-style: normal; 14 | font-weight: 700; 15 | font-display: swap; 16 | src: local(IBM Plex Serif Bold), local(IBMPlexSerif-Bold), url(./IBMPlexSerif-Bold-SlashedZero.woff2) format('woff2'); 17 | } 18 | @font-face { 19 | font-family: 'IBM Plex Serif'; 20 | font-style: italic; 21 | font-weight: 400; 22 | font-display: swap; 23 | src: local(IBM Plex Serif Italic), local(IBMPlexSerif-Italic), url(./IBMPlexSerif-Italic-SlashedZero.woff2) format('woff2'); 24 | } 25 | @font-face { 26 | font-family: 'IBM Plex Serif'; 27 | font-style: italic; 28 | font-weight: 700; 29 | font-display: swap; 30 | src: local(IBM Plex Serif Bold Italic), local(IBMPlexSerif-BoldItalic), url(./IBMPlexSerif-BoldItalic-SlashedZero.woff2) format('woff2'); 31 | } 32 | 33 | /* https://fonts.googleapis.com/css2?family=IBM%20Plex%20Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap */ 34 | @font-face { 35 | font-family: 'IBM Plex Sans'; 36 | font-style: normal; 37 | font-weight: 400; 38 | font-display: swap; 39 | src: local(IBM Plex Sans Regular), local(IBMPlexSans-Regular), url(./IBMPlexSans-Regular-SlashedZero.woff2) format('woff2'); 40 | } 41 | @font-face { 42 | font-family: 'IBM Plex Sans'; 43 | font-style: normal; 44 | font-weight: 700; 45 | font-display: swap; 46 | src: local(IBM Plex Sans Bold), local(IBMPlexSans-Bold), url(./IBMPlexSans-Bold-SlashedZero.woff2) format('woff2'); 47 | } 48 | @font-face { 49 | font-family: 'IBM Plex Sans'; 50 | font-style: italic; 51 | font-weight: 400; 52 | font-display: swap; 53 | src: local(IBM Plex Sans Italic), local(IBMPlexSans-Italic), url(./IBMPlexSans-Italic-SlashedZero.woff2) format('woff2'); 54 | } 55 | @font-face { 56 | font-family: 'IBM Plex Sans'; 57 | font-style: italic; 58 | font-weight: 700; 59 | font-display: swap; 60 | src: local(IBM Plex Sans Bold Italic), local(IBMPlexSans-BoldItalic), url(./IBMPlexSans-BoldItalic-SlashedZero.woff2) format('woff2'); 61 | } 62 | 63 | /* https://fonts.googleapis.com/css2?family=IBM%20Plex%20Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap */ 64 | @font-face { 65 | font-family: 'IBM Plex Mono'; 66 | font-style: normal; 67 | font-weight: 400; 68 | font-display: swap; 69 | src: local(IBM Plex Mono Regular), local(IBMPlexMono-Regular), url(./IBMPlexMono-Regular-SlashedZero.woff2) format('woff2'); 70 | } 71 | @font-face { 72 | font-family: 'IBM Plex Mono'; 73 | font-style: normal; 74 | font-weight: 700; 75 | font-display: swap; 76 | src: local(IBM Plex Mono Bold), local(IBMPlexMono-Bold), url(./IBMPlexMono-Bold-SlashedZero.woff2) format('woff2'); 77 | } 78 | @font-face { 79 | font-family: 'IBM Plex Mono'; 80 | font-style: italic; 81 | font-weight: 400; 82 | font-display: swap; 83 | src: local(IBM Plex Mono Italic), local(IBMPlexMono-Italic), url(./IBMPlexMono-Italic-SlashedZero.woff2) format('woff2'); 84 | } 85 | @font-face { 86 | font-family: 'IBM Plex Mono'; 87 | font-style: italic; 88 | font-weight: 700; 89 | font-display: swap; 90 | src: local(IBM Plex Mono Bold Italic), local(IBMPlexMono-BoldItalic), url(./IBMPlexMono-BoldItalic-SlashedZero.woff2) format('woff2'); 91 | } 92 | 93 | html { 94 | background-color: #fff; 95 | } 96 | 97 | body { 98 | display: flex; 99 | word-wrap: break-word; 100 | font-size: 18px; 101 | line-height: 1.5; 102 | font-family: 103 | IBM Plex Serif, 104 | serif; 105 | font-variant-numeric: slashed-zero; 106 | padding: 0; 107 | margin: 0; 108 | color: #111; 109 | } 110 | 111 | #spec-container { 112 | padding: 0 20px; 113 | max-width: 80rem; 114 | margin: 0 auto; 115 | flex-grow: 1; 116 | flex-basis: 66%; 117 | box-sizing: border-box; 118 | overflow: hidden; 119 | padding-bottom: 1em; 120 | } 121 | 122 | body.oldtoc { 123 | margin: 0 auto; 124 | } 125 | 126 | span[aria-hidden='true'] { 127 | font-size: 0; 128 | white-space: pre; 129 | } 130 | 131 | a { 132 | text-decoration: none; 133 | color: #206ca7; 134 | } 135 | 136 | a:visited { 137 | color: #206ca7; 138 | } 139 | 140 | a:hover { 141 | text-decoration: underline; 142 | color: #239dee; 143 | } 144 | 145 | a.e-user-code, 146 | span.e-user-code { 147 | white-space: nowrap; 148 | } 149 | 150 | a.e-user-code::before, 151 | span.e-user-code::before { 152 | display: none; 153 | color: brown; 154 | background-color: white; 155 | border: 2pt solid brown; 156 | padding: 0.3ex; 157 | margin: 0 0.25em 0 0; 158 | line-height: normal; 159 | vertical-align: middle; 160 | text-transform: uppercase; 161 | font-family: 162 | IBM Plex Sans, 163 | sans-serif; 164 | font-weight: 900; 165 | font-size: x-small; 166 | } 167 | 168 | a.e-user-code:hover::before, 169 | span.e-user-code:hover::before { 170 | background-color: brown; 171 | color: white; 172 | } 173 | 174 | html.show-ao-annotations a.e-user-code::before, 175 | html.show-ao-annotations span.e-user-code::before { 176 | display: inline-block; 177 | } 178 | 179 | a.e-user-code::before, 180 | span.e-user-code::before { 181 | content: 'UC'; 182 | } 183 | 184 | code { 185 | font-weight: bold; 186 | font-family: 187 | Comic Code, 188 | IBM Plex Mono, 189 | monospace; 190 | white-space: pre; 191 | } 192 | 193 | pre code { 194 | font-weight: inherit; 195 | } 196 | 197 | pre code.hljs { 198 | background-color: #fff; 199 | margin: 0; 200 | padding: 0; 201 | } 202 | 203 | ol.toc { 204 | list-style: none; 205 | padding-left: 0; 206 | } 207 | 208 | ol.toc ol.toc { 209 | padding-left: 2ex; 210 | list-style: none; 211 | } 212 | 213 | var { 214 | border-radius: 5px; 215 | color: #218379; 216 | transition: all 0.25s ease-out; 217 | cursor: pointer; 218 | padding: 0 4px; 219 | margin: 0 -4px; 220 | mix-blend-mode: multiply; 221 | } 222 | var.field { 223 | font: inherit; 224 | color: inherit; 225 | } 226 | /* suppress line break opportunities between `.` and `[[FieldName]]` */ 227 | var.field::before { 228 | content: '\2060' 229 | } 230 | 231 | var.referenced { 232 | color: inherit; 233 | } 234 | 235 | var.referenced0 { 236 | background-color: #ffff6c; 237 | } 238 | var.referenced1 { 239 | background-color: #ffa4a8; 240 | } 241 | var.referenced2 { 242 | background-color: #96e885; 243 | } 244 | var.referenced3 { 245 | background-color: #3eeed2; 246 | } 247 | var.referenced4 { 248 | background-color: #eacfb6; 249 | } 250 | var.referenced5 { 251 | background-color: #82ddff; 252 | } 253 | var.referenced6 { 254 | background-color: #ffbcf2; 255 | } 256 | 257 | emu-const { 258 | font-family: 259 | IBM Plex Sans, 260 | sans-serif; 261 | font-variant: small-caps; 262 | text-transform: uppercase; 263 | } 264 | 265 | emu-val { 266 | font-weight: bold; 267 | } 268 | 269 | /* depth 1 */ 270 | emu-alg ol, 271 | /* depth 4 */ 272 | emu-alg ol ol ol ol, 273 | emu-alg ol.nested-thrice, 274 | emu-alg ol.nested-twice ol, 275 | emu-alg ol.nested-once ol ol { 276 | list-style-type: decimal; 277 | } 278 | 279 | /* depth 2 */ 280 | emu-alg ol ol, 281 | emu-alg ol.nested-once, 282 | /* depth 5 */ 283 | emu-alg ol ol ol ol ol, 284 | emu-alg ol.nested-four-times, 285 | emu-alg ol.nested-thrice ol, 286 | emu-alg ol.nested-twice ol ol, 287 | emu-alg ol.nested-once ol ol ol { 288 | list-style-type: lower-alpha; 289 | } 290 | 291 | /* depth 3 */ 292 | emu-alg ol ol ol, 293 | emu-alg ol.nested-twice, 294 | emu-alg ol.nested-once ol, 295 | /* depth 6 */ 296 | emu-alg ol ol ol ol ol ol, 297 | emu-alg ol.nested-lots, 298 | emu-alg ol.nested-four-times ol, 299 | emu-alg ol.nested-thrice ol ol, 300 | emu-alg ol.nested-twice ol ol ol, 301 | emu-alg ol.nested-once ol ol ol ol, 302 | /* depth 7+ */ 303 | emu-alg ol.nested-lots ol { 304 | list-style-type: lower-roman; 305 | } 306 | 307 | emu-eqn { 308 | display: block; 309 | margin-left: 4em; 310 | } 311 | 312 | emu-eqn.inline { 313 | display: inline; 314 | margin: 0; 315 | } 316 | 317 | emu-eqn div:first-child { 318 | margin-left: -2em; 319 | } 320 | 321 | emu-note { 322 | margin: 1em 0; 323 | display: flex; 324 | gap: 1em; 325 | flex-direction: row; 326 | color: inherit; 327 | border-left: 5px solid #52e052; 328 | background: #e9fbe9; 329 | padding: 10px 10px 10px 0; 330 | overflow-x: auto; 331 | } 332 | 333 | emu-note > span.note { 334 | white-space: nowrap; 335 | flex-grow: 0; 336 | flex-shrink: 1; 337 | text-transform: uppercase; 338 | padding-left: 5px; 339 | } 340 | 341 | emu-note[type='editor'] { 342 | border-left-color: #faa; 343 | } 344 | 345 | emu-note > div.note-contents { 346 | flex-grow: 1; 347 | flex-shrink: 1; 348 | overflow: auto; 349 | } 350 | 351 | emu-note > div.note-contents > p:first-of-type { 352 | margin-top: 0; 353 | } 354 | 355 | emu-note > div.note-contents > p:last-of-type { 356 | margin-bottom: 0; 357 | } 358 | 359 | emu-table:not(.code) td code { 360 | white-space: normal; 361 | } 362 | 363 | emu-figure { 364 | display: block; 365 | } 366 | 367 | emu-example { 368 | display: block; 369 | margin: 1em 3em; 370 | } 371 | 372 | emu-example figure figcaption { 373 | margin-top: 0.5em; 374 | text-align: left; 375 | } 376 | 377 | emu-figure figure, 378 | emu-example figure, 379 | emu-table figure { 380 | display: flex; 381 | flex-direction: column; 382 | align-items: center; 383 | } 384 | 385 | emu-production { 386 | display: block; 387 | } 388 | 389 | emu-grammar[type='example'] emu-production, 390 | emu-grammar[type='definition'] emu-production { 391 | margin-top: 1em; 392 | margin-bottom: 1em; 393 | margin-left: 5ex; 394 | } 395 | 396 | emu-grammar.inline, 397 | emu-production.inline, 398 | emu-grammar.inline emu-production emu-rhs, 399 | emu-production.inline emu-rhs, 400 | emu-grammar[collapsed] emu-production emu-rhs { 401 | display: inline; 402 | padding-left: 1ex; 403 | margin-left: 0; 404 | } 405 | 406 | emu-production[collapsed] emu-rhs { 407 | display: inline; 408 | padding-left: 0.5ex; 409 | margin-left: 0; 410 | } 411 | 412 | emu-grammar[collapsed] emu-production, 413 | emu-production[collapsed] { 414 | margin: 0; 415 | } 416 | 417 | emu-constraints { 418 | font-size: 0.75em; 419 | margin-right: 0.5ex; 420 | } 421 | 422 | emu-gann { 423 | margin-right: 0.5ex; 424 | } 425 | 426 | emu-gann emu-t:last-child, 427 | emu-gann emu-gprose:last-child, 428 | emu-gann emu-nt:last-child { 429 | margin-right: 0; 430 | } 431 | 432 | emu-geq { 433 | margin-left: 0.5ex; 434 | font-weight: bold; 435 | } 436 | 437 | emu-oneof { 438 | font-weight: bold; 439 | margin-left: 0.5ex; 440 | } 441 | 442 | emu-nt { 443 | display: inline-block; 444 | font-style: italic; 445 | white-space: nowrap; 446 | text-indent: 0; 447 | } 448 | 449 | emu-nt a, 450 | emu-nt a:visited { 451 | color: #333; 452 | } 453 | 454 | emu-rhs emu-nt { 455 | margin-right: 0.5ex; 456 | } 457 | 458 | emu-t { 459 | display: inline-block; 460 | font-family: 461 | IBM Plex Mono, 462 | monospace; 463 | font-weight: bold; 464 | white-space: nowrap; 465 | text-indent: 0; 466 | } 467 | 468 | emu-production emu-t { 469 | margin-right: 0.5ex; 470 | } 471 | 472 | emu-rhs { 473 | display: block; 474 | padding-left: 75px; 475 | text-indent: -25px; 476 | } 477 | 478 | emu-production:not([collapsed]) emu-rhs { 479 | border: 0.2ex dashed transparent; 480 | } 481 | 482 | emu-production:not([collapsed]) emu-rhs:hover { 483 | border-color: #888; 484 | background-color: #f0f0f0; 485 | } 486 | 487 | emu-mods { 488 | font-size: 0.85em; 489 | vertical-align: sub; 490 | font-style: normal; 491 | font-weight: normal; 492 | } 493 | 494 | emu-params, 495 | emu-opt { 496 | margin-right: 1ex; 497 | font-family: 498 | IBM Plex Mono, 499 | monospace; 500 | } 501 | 502 | emu-params, 503 | emu-constraints { 504 | color: #2aa198; 505 | } 506 | 507 | emu-opt { 508 | color: #b58900; 509 | } 510 | 511 | emu-gprose { 512 | font-size: 0.9em; 513 | font-family: 514 | IBM Plex Sans, 515 | sans-serif; 516 | } 517 | 518 | emu-production emu-gprose { 519 | margin-right: 1ex; 520 | } 521 | 522 | h1.shortname { 523 | color: #f60; 524 | font-size: 1.5em; 525 | margin: 0; 526 | } 527 | 528 | h1.version { 529 | color: #f60; 530 | font-size: 1.5em; 531 | } 532 | 533 | h1.title { 534 | color: #f60; 535 | } 536 | 537 | h1, 538 | h2, 539 | h3, 540 | h4, 541 | h5, 542 | h6 { 543 | position: relative; 544 | } 545 | 546 | h1 .secnum { 547 | text-decoration: none; 548 | margin-right: 5px; 549 | } 550 | 551 | h1 span.title { 552 | order: 2; 553 | } 554 | 555 | h1 { 556 | font-size: 2.67em; 557 | margin-bottom: 0; 558 | line-height: 1em; 559 | } 560 | h2 { 561 | font-size: 2em; 562 | } 563 | h3 { 564 | font-size: 1.56em; 565 | } 566 | h4 { 567 | font-size: 1.25em; 568 | } 569 | h5 { 570 | font-size: 1.11em; 571 | } 572 | h6 { 573 | font-size: 1em; 574 | } 575 | 576 | pre code.hljs { 577 | background: transparent; 578 | } 579 | 580 | emu-clause[id], 581 | emu-annex[id], 582 | emu-intro[id] { 583 | scroll-margin-top: 2ex; 584 | } 585 | 586 | emu-intro h1, 587 | emu-clause h1, 588 | emu-annex h1 { 589 | font-size: 2em; 590 | } 591 | emu-intro h2, 592 | emu-clause h2, 593 | emu-annex h2 { 594 | font-size: 1.56em; 595 | } 596 | emu-intro h3, 597 | emu-clause h3, 598 | emu-annex h3 { 599 | font-size: 1.25em; 600 | } 601 | emu-intro h4, 602 | emu-clause h4, 603 | emu-annex h4 { 604 | font-size: 1.11em; 605 | } 606 | emu-intro h5, 607 | emu-clause h5, 608 | emu-annex h5 { 609 | font-size: 1em; 610 | } 611 | emu-intro h6, 612 | emu-clause h6, 613 | emu-annex h6 { 614 | font-size: 0.9em; 615 | } 616 | emu-intro emu-intro h1, 617 | emu-clause emu-clause h1, 618 | emu-annex emu-annex h1 { 619 | font-size: 1.56em; 620 | } 621 | emu-intro emu-intro h2, 622 | emu-clause emu-clause h2, 623 | emu-annex emu-annex h2 { 624 | font-size: 1.25em; 625 | } 626 | emu-intro emu-intro h3, 627 | emu-clause emu-clause h3, 628 | emu-annex emu-annex h3 { 629 | font-size: 1.11em; 630 | } 631 | emu-intro emu-intro h4, 632 | emu-clause emu-clause h4, 633 | emu-annex emu-annex h4 { 634 | font-size: 1em; 635 | } 636 | emu-intro emu-intro h5, 637 | emu-clause emu-clause h5, 638 | emu-annex emu-annex h5 { 639 | font-size: 0.9em; 640 | } 641 | emu-intro emu-intro emu-intro h1, 642 | emu-clause emu-clause emu-clause h1, 643 | emu-annex emu-annex emu-annex h1 { 644 | font-size: 1.25em; 645 | } 646 | emu-intro emu-intro emu-intro h2, 647 | emu-clause emu-clause emu-clause h2, 648 | emu-annex emu-annex emu-annex h2 { 649 | font-size: 1.11em; 650 | } 651 | emu-intro emu-intro emu-intro h3, 652 | emu-clause emu-clause emu-clause h3, 653 | emu-annex emu-annex emu-annex h3 { 654 | font-size: 1em; 655 | } 656 | emu-intro emu-intro emu-intro h4, 657 | emu-clause emu-clause emu-clause h4, 658 | emu-annex emu-annex emu-annex h4 { 659 | font-size: 0.9em; 660 | } 661 | emu-intro emu-intro emu-intro emu-intro h1, 662 | emu-clause emu-clause emu-clause emu-clause h1, 663 | emu-annex emu-annex emu-annex emu-annex h1 { 664 | font-size: 1.11em; 665 | } 666 | emu-intro emu-intro emu-intro emu-intro h2, 667 | emu-clause emu-clause emu-clause emu-clause h2, 668 | emu-annex emu-annex emu-annex emu-annex h2 { 669 | font-size: 1em; 670 | } 671 | emu-intro emu-intro emu-intro emu-intro h3, 672 | emu-clause emu-clause emu-clause emu-clause h3, 673 | emu-annex emu-annex emu-annex emu-annex h3 { 674 | font-size: 0.9em; 675 | } 676 | emu-intro emu-intro emu-intro emu-intro emu-intro h1, 677 | emu-clause emu-clause emu-clause emu-clause emu-clause h1, 678 | emu-annex emu-annex emu-annex emu-annex emu-annex h1 { 679 | font-size: 1em; 680 | } 681 | emu-intro emu-intro emu-intro emu-intro emu-intro h2, 682 | emu-clause emu-clause emu-clause emu-clause emu-clause h2, 683 | emu-annex emu-annex emu-annex emu-annex emu-annex h2 { 684 | font-size: 0.9em; 685 | } 686 | emu-intro emu-intro emu-intro emu-intro emu-intro emu-intro h1, 687 | emu-clause emu-clause emu-clause emu-clause emu-clause emu-clause h1, 688 | emu-annex emu-annex emu-annex emu-annex emu-annex emu-annex h1 { 689 | font-size: 0.9em; 690 | } 691 | 692 | emu-clause, 693 | emu-intro, 694 | emu-annex { 695 | display: block; 696 | } 697 | 698 | /* these values are twice the font-size for the

titles for clauses */ 699 | emu-intro, 700 | emu-clause, 701 | emu-annex { 702 | margin-top: 4em; 703 | } 704 | emu-intro emu-intro, 705 | emu-clause emu-clause, 706 | emu-annex emu-annex { 707 | margin-top: 3.12em; 708 | } 709 | emu-intro emu-intro emu-intro, 710 | emu-clause emu-clause emu-clause, 711 | emu-annex emu-annex emu-annex { 712 | margin-top: 2.5em; 713 | } 714 | emu-intro emu-intro emu-intro emu-intro, 715 | emu-clause emu-clause emu-clause emu-clause, 716 | emu-annex emu-annex emu-annex emu-annex { 717 | margin-top: 2.22em; 718 | } 719 | emu-intro emu-intro emu-intro emu-intro emu-intro, 720 | emu-clause emu-clause emu-clause emu-clause emu-clause, 721 | emu-annex emu-annex emu-annex emu-annex emu-annex { 722 | margin-top: 2em; 723 | } 724 | emu-intro emu-intro emu-intro emu-intro emu-intro emu-intro, 725 | emu-clause emu-clause emu-clause emu-clause emu-clause emu-clause, 726 | emu-annex emu-annex emu-annex emu-annex emu-annex emu-annex { 727 | margin-top: 1.8em; 728 | } 729 | 730 | #spec-container > emu-intro:first-of-type, 731 | #spec-container > emu-clause:first-of-type, 732 | #spec-container > emu-annex:first-of-type { 733 | margin-top: 0; 734 | } 735 | 736 | /* Figures and tables */ 737 | figure { 738 | display: block; 739 | overflow-x: auto; 740 | margin: 1.5em 0; 741 | } 742 | figure object { 743 | display: block; 744 | margin: 0 auto; 745 | } 746 | figure table.real-table { 747 | margin: 0 auto; 748 | } 749 | figure figcaption { 750 | display: block; 751 | color: #555555; 752 | font-weight: bold; 753 | text-align: center; 754 | } 755 | 756 | emu-table table { 757 | margin: 0 auto; 758 | } 759 | 760 | emu-table table, 761 | table.real-table { 762 | border-collapse: collapse; 763 | } 764 | 765 | emu-table td, 766 | emu-table th, 767 | table.real-table td, 768 | table.real-table th { 769 | border: 1px solid black; 770 | padding: 0.4em; 771 | vertical-align: baseline; 772 | } 773 | emu-table th, 774 | emu-table thead td, 775 | table.real-table th { 776 | background-color: #eeeeee; 777 | } 778 | 779 | emu-table td { 780 | background: #fff; 781 | } 782 | 783 | /* Note: the left content edges of table.lightweight-table >tbody >tr >td 784 | and div.display line up. */ 785 | table.lightweight-table { 786 | border-collapse: collapse; 787 | margin: 0 0 0 1.5em; 788 | } 789 | table.lightweight-table td, 790 | table.lightweight-table th { 791 | border: none; 792 | padding: 0 0.5em; 793 | vertical-align: baseline; 794 | } 795 | 796 | /* for non-clause-like link targets, apply a fading highlight 797 | and a persistent focus-associated highlight */ 798 | @keyframes highlight-target-bg { 799 | 0% { 800 | background-color: rgba(249, 241, 172, 1); 801 | } 802 | 100% { 803 | background-color: rgba(249, 241, 172, 0); 804 | } 805 | } 806 | #spec-container :target:not(emu-annex, emu-clause, emu-intro, emu-note, body) { 807 | animation: highlight-target-bg 2.5s ease-out; 808 | } 809 | #spec-container :target:focus-within:not(:has(:not(a))) { 810 | animation: none; 811 | background-color: rgba(249, 241, 172, 1); 812 | } 813 | 814 | /* diff styles */ 815 | ins { 816 | background-color: #e0f8e0; 817 | text-decoration: none; 818 | border-bottom: 1px solid #396; 819 | } 820 | 821 | del { 822 | background-color: #fee; 823 | } 824 | 825 | ins.block, 826 | del.block, 827 | emu-production > ins, 828 | emu-production > del, 829 | emu-grammar > ins, 830 | emu-grammar > del { 831 | display: block; 832 | } 833 | emu-rhs > ins, 834 | emu-rhs > del { 835 | display: inline; 836 | } 837 | 838 | tr.ins > td > ins { 839 | border-bottom: none; 840 | } 841 | 842 | tr.ins > td { 843 | background-color: #e0f8e0; 844 | } 845 | 846 | tr.del > td { 847 | background-color: #fee; 848 | } 849 | 850 | /* Menu Styles */ 851 | #menu-toggle { 852 | font-size: 2em; 853 | 854 | position: fixed; 855 | top: 0; 856 | left: 0; 857 | width: 1.5em; 858 | height: 1.5em; 859 | z-index: 3; 860 | visibility: hidden; 861 | color: #1567a2; 862 | background-color: #fff; 863 | 864 | line-height: 1.5em; 865 | text-align: center; 866 | -webkit-touch-callout: none; 867 | -webkit-user-select: none; 868 | -khtml-user-select: none; 869 | -moz-user-select: none; 870 | -ms-user-select: none; 871 | user-select: none; 872 | 873 | cursor: pointer; 874 | } 875 | 876 | #menu { 877 | display: flex; 878 | flex-direction: column; 879 | width: 33%; 880 | height: 100vh; 881 | max-width: 500px; 882 | box-sizing: border-box; 883 | background-color: #ddd; 884 | overflow: hidden; 885 | transition: opacity 0.1s linear; 886 | padding: 0 5px; 887 | position: fixed; 888 | left: 0; 889 | top: 0; 890 | border-right: 2px solid #bbb; 891 | 892 | z-index: 2; 893 | } 894 | 895 | .menu-spacer { 896 | flex-basis: 33%; 897 | max-width: 500px; 898 | flex-grow: 0; 899 | flex-shrink: 0; 900 | } 901 | 902 | #menu a { 903 | color: #1567a2; 904 | } 905 | 906 | #menu.active { 907 | display: flex; 908 | opacity: 1; 909 | z-index: 2; 910 | } 911 | 912 | #menu-pins { 913 | flex-grow: 1; 914 | display: none; 915 | } 916 | 917 | #menu-pins.active { 918 | display: block; 919 | } 920 | 921 | #menu-pins .unpin-all { 922 | border: none; 923 | background: #ccc; 924 | border-radius: 4px; 925 | height: 18px; 926 | font-size: 12px; 927 | margin: 0 5px 0 10px; 928 | font-family: IBM Plex Sans; 929 | } 930 | #menu-pins .unpin-all:hover { 931 | background: #ddd; 932 | } 933 | 934 | #menu-pins-list { 935 | margin: 0; 936 | padding: 0; 937 | counter-reset: pins-counter; 938 | } 939 | 940 | #menu-pins-list > li { 941 | display: flex; 942 | align-items: stretch; 943 | gap: 4px; 944 | margin: 3px 1px; 945 | border-radius: 4px; 946 | } 947 | 948 | #menu-pins-list > li > a { 949 | flex-grow: 1; 950 | align-self: center; 951 | } 952 | 953 | #menu-pins-list > li:before, 954 | #menu-pins-list > li > .unpin { 955 | flex-shrink: 0; 956 | flex-grow: 0; 957 | text-align: center; 958 | padding: 1px 3px; 959 | border-radius: 4px; 960 | background: none; 961 | border: none; 962 | } 963 | #menu-pins-list > li:before, 964 | #menu-pins-list > li > .unpin:hover { 965 | background: #ccc; 966 | } 967 | 968 | #menu-pins-list > li > .unpin, 969 | #menu-pins .unpin-all { 970 | cursor: pointer; 971 | } 972 | #menu-pins-list > li > .unpin:hover, 973 | #menu-pins .unpin-all:hover { 974 | color: #bb1212; 975 | } 976 | 977 | #menu-pins-list > li:before { 978 | content: counter(pins-counter); 979 | counter-increment: pins-counter; 980 | font-size: 16px; 981 | } 982 | 983 | #menu-toc > ol { 984 | padding: 0; 985 | flex-grow: 1; 986 | } 987 | 988 | #menu-toc > ol li { 989 | padding: 0; 990 | } 991 | 992 | #menu-toc > ol, 993 | #menu-toc > ol ol { 994 | list-style-type: none; 995 | margin: 0; 996 | padding: 0; 997 | } 998 | 999 | #menu-toc > ol ol { 1000 | padding-left: 0.75em; 1001 | } 1002 | 1003 | #menu-toc li { 1004 | text-overflow: ellipsis; 1005 | overflow: hidden; 1006 | white-space: nowrap; 1007 | } 1008 | 1009 | #menu-toc .item-toggle { 1010 | display: inline-block; 1011 | transform: rotate(0deg); 1012 | transition: transform 0.1s ease; 1013 | background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMC42MDUiIGhlaWdodD0iMTUuNTU1Ij48cGF0aCBmaWxsPSIjODg5IiBkPSJtMi44MjggMTUuNTU1IDcuNzc3LTcuNzc5TDIuODI4IDAgMCAyLjgyOGw0Ljk0OSA0Ljk0OEwwIDEyLjcyN2wyLjgyOCAyLjgyOHoiLz48L3N2Zz4); 1014 | background-repeat: no-repeat; 1015 | background-position: center; 1016 | background-size: auto 50%; 1017 | text-align: center; 1018 | width: 1em; 1019 | 1020 | color: transparent; 1021 | 1022 | -webkit-touch-callout: none; 1023 | -webkit-user-select: none; 1024 | -khtml-user-select: none; 1025 | -moz-user-select: none; 1026 | -ms-user-select: none; 1027 | user-select: none; 1028 | 1029 | cursor: pointer; 1030 | } 1031 | 1032 | #menu-toc .item-toggle-none { 1033 | display: inline-block; 1034 | width: 1em; 1035 | } 1036 | 1037 | #menu-toc li.active > .item-toggle, 1038 | #menu-toc li.revealed > .item-toggle { 1039 | transform: rotate(90deg); 1040 | } 1041 | 1042 | #menu-toc li > ol { 1043 | display: none; 1044 | } 1045 | 1046 | #menu-toc li.active > ol { 1047 | display: block; 1048 | } 1049 | 1050 | #menu-toc li.revealed > a { 1051 | background-color: #ccc; 1052 | font-weight: bold; 1053 | } 1054 | 1055 | #menu-toc li.revealed-leaf > a { 1056 | color: #206ca7; 1057 | } 1058 | 1059 | #menu-toc li.revealed > ol { 1060 | display: block; 1061 | } 1062 | 1063 | #menu-toc li > a { 1064 | padding: 2px 5px; 1065 | } 1066 | 1067 | #menu > * { 1068 | margin-bottom: 5px; 1069 | } 1070 | 1071 | .menu-pane-header { 1072 | padding: 2px 8px; 1073 | background-color: #bbb; 1074 | font-weight: bold; 1075 | letter-spacing: 1px; 1076 | flex-grow: 0; 1077 | flex-shrink: 0; 1078 | font-size: 80%; 1079 | user-select: none; 1080 | } 1081 | 1082 | .menu-pane-header emu-opt, 1083 | .menu-pane-header emu-t, 1084 | .menu-pane-header emu-nt { 1085 | margin-right: 0px; 1086 | display: inline; 1087 | color: inherit; 1088 | } 1089 | 1090 | .menu-pane-header emu-rhs { 1091 | display: inline; 1092 | padding-left: 0px; 1093 | text-indent: 0px; 1094 | } 1095 | 1096 | .menu-pane-header emu-geq { 1097 | margin-left: 0px; 1098 | } 1099 | 1100 | a.menu-pane-header-production { 1101 | color: inherit; 1102 | } 1103 | 1104 | .menu-pane-header-production { 1105 | text-transform: none; 1106 | letter-spacing: 1.5px; 1107 | padding-left: 0.5em; 1108 | } 1109 | 1110 | #menu-toc { 1111 | display: flex; 1112 | flex-direction: column; 1113 | width: 100%; 1114 | overflow: hidden; 1115 | flex-grow: 1; 1116 | } 1117 | 1118 | #menu-toc ol.toc { 1119 | overflow-x: hidden; 1120 | overflow-y: auto; 1121 | } 1122 | 1123 | #menu-search { 1124 | position: relative; 1125 | flex-grow: 0; 1126 | flex-shrink: 0; 1127 | width: 100%; 1128 | 1129 | display: flex; 1130 | flex-direction: column; 1131 | 1132 | max-height: 300px; 1133 | } 1134 | 1135 | #menu-trace-list { 1136 | display: none; 1137 | } 1138 | 1139 | #menu-search-box { 1140 | box-sizing: border-box; 1141 | display: block; 1142 | width: 100%; 1143 | margin: 5px 0 0 0; 1144 | font-size: 1em; 1145 | padding: 2px; 1146 | background-color: #bbb; 1147 | border: 1px solid #999; 1148 | } 1149 | 1150 | #menu-search-results { 1151 | overflow-x: hidden; 1152 | overflow-y: auto; 1153 | } 1154 | 1155 | li.menu-search-result-clause:before { 1156 | content: 'clause'; 1157 | width: 40px; 1158 | display: inline-block; 1159 | text-align: right; 1160 | padding-right: 1ex; 1161 | color: #666; 1162 | font-size: 75%; 1163 | } 1164 | li.menu-search-result-op:before { 1165 | content: 'op'; 1166 | width: 40px; 1167 | display: inline-block; 1168 | text-align: right; 1169 | padding-right: 1ex; 1170 | color: #666; 1171 | font-size: 75%; 1172 | } 1173 | 1174 | li.menu-search-result-prod:before { 1175 | content: 'prod'; 1176 | width: 40px; 1177 | display: inline-block; 1178 | text-align: right; 1179 | padding-right: 1ex; 1180 | color: #666; 1181 | font-size: 75%; 1182 | } 1183 | 1184 | li.menu-search-result-term:before { 1185 | content: 'term'; 1186 | width: 40px; 1187 | display: inline-block; 1188 | text-align: right; 1189 | padding-right: 1ex; 1190 | color: #666; 1191 | font-size: 75%; 1192 | } 1193 | 1194 | #menu-search-results ul { 1195 | padding: 0 5px; 1196 | margin: 0; 1197 | } 1198 | 1199 | #menu-search-results li { 1200 | white-space: nowrap; 1201 | text-overflow: ellipsis; 1202 | } 1203 | 1204 | #menu-trace-list { 1205 | counter-reset: item; 1206 | margin: 0 0 0 20px; 1207 | padding: 0; 1208 | } 1209 | #menu-trace-list li { 1210 | display: block; 1211 | white-space: nowrap; 1212 | } 1213 | 1214 | #menu-trace-list li .secnum:after { 1215 | content: ' '; 1216 | } 1217 | #menu-trace-list li:before { 1218 | content: counter(item) ' '; 1219 | background-color: #222; 1220 | counter-increment: item; 1221 | color: #999; 1222 | width: 20px; 1223 | height: 20px; 1224 | line-height: 20px; 1225 | display: inline-block; 1226 | text-align: center; 1227 | margin: 2px 4px 2px 0; 1228 | } 1229 | 1230 | @media (max-width: 1000px) { 1231 | body { 1232 | margin: 0; 1233 | display: block; 1234 | } 1235 | 1236 | #menu { 1237 | display: none; 1238 | padding-top: 3em; 1239 | width: 450px; 1240 | } 1241 | 1242 | #menu.active { 1243 | position: fixed; 1244 | height: 100%; 1245 | left: 0; 1246 | top: 0; 1247 | right: 300px; 1248 | } 1249 | 1250 | #menu-toggle { 1251 | visibility: visible; 1252 | } 1253 | 1254 | #spec-container { 1255 | padding: 0 5px; 1256 | } 1257 | 1258 | #references-pane-spacer { 1259 | display: none; 1260 | } 1261 | } 1262 | 1263 | @media only screen and (max-width: 800px) { 1264 | #menu { 1265 | width: 100%; 1266 | } 1267 | 1268 | h1 .secnum:empty { 1269 | margin: 0; 1270 | padding: 0; 1271 | } 1272 | } 1273 | 1274 | /* Toolbox */ 1275 | .toolbox-container { 1276 | position: absolute; 1277 | display: none; 1278 | padding-bottom: 7px; 1279 | } 1280 | 1281 | .toolbox-container.active { 1282 | display: inline-block; 1283 | } 1284 | 1285 | .toolbox { 1286 | position: relative; 1287 | background: #ddd; 1288 | border: 1px solid #aaa; 1289 | color: #eee; 1290 | padding: 5px 7px; 1291 | border-radius: 3px; 1292 | } 1293 | 1294 | .toolbox a { 1295 | text-decoration: none; 1296 | padding: 0 3px; 1297 | white-space: nowrap; 1298 | } 1299 | 1300 | .toolbox a:hover { 1301 | text-decoration: underline; 1302 | } 1303 | 1304 | .toolbox:after, 1305 | .toolbox:before { 1306 | top: 100%; 1307 | left: 15px; 1308 | border: solid transparent; 1309 | content: ' '; 1310 | height: 0; 1311 | width: 0; 1312 | position: absolute; 1313 | pointer-events: none; 1314 | } 1315 | 1316 | .toolbox:after { 1317 | border-color: rgba(0, 0, 0, 0); 1318 | border-top-color: #ddd; 1319 | border-width: 10px; 1320 | margin-left: -10px; 1321 | } 1322 | .toolbox:before { 1323 | border-color: rgba(204, 204, 204, 0); 1324 | border-top-color: #aaa; 1325 | border-width: 12px; 1326 | margin-left: -12px; 1327 | } 1328 | 1329 | #references-pane-container { 1330 | position: fixed; 1331 | bottom: 0; 1332 | left: 0; 1333 | right: 0; 1334 | display: none; 1335 | background-color: #ddd; 1336 | z-index: 1; 1337 | } 1338 | 1339 | #references-pane-table-container { 1340 | overflow-x: hidden; 1341 | overflow-y: auto; 1342 | min-height: 35px; 1343 | max-height: 85vh; 1344 | } 1345 | 1346 | #references-pane { 1347 | flex-grow: 1; 1348 | overflow: hidden; 1349 | display: flex; 1350 | flex-direction: column; 1351 | } 1352 | 1353 | #references-pane > .menu-pane-header { 1354 | cursor: row-resize; 1355 | } 1356 | 1357 | #references-pane-container.active { 1358 | display: flex; 1359 | } 1360 | 1361 | #references-pane-close:after { 1362 | content: '\2716'; 1363 | float: right; 1364 | cursor: pointer; 1365 | } 1366 | 1367 | #references-pane table tbody { 1368 | vertical-align: baseline; 1369 | } 1370 | 1371 | #references-pane table tr td:first-child { 1372 | text-align: right; 1373 | padding-right: 5px; 1374 | } 1375 | 1376 | [normative-optional], 1377 | [deprecated], 1378 | [legacy] { 1379 | border-left: 5px solid #ff6600; 1380 | padding: 0.5em; 1381 | background: #ffeedd; 1382 | } 1383 | 1384 | .attributes-tag { 1385 | text-transform: uppercase; 1386 | color: #884400; 1387 | } 1388 | 1389 | .attributes-tag a { 1390 | color: #884400; 1391 | } 1392 | 1393 | /* Shortcuts help dialog */ 1394 | 1395 | #shortcuts-help { 1396 | position: fixed; 1397 | left: 5%; 1398 | margin: 0 auto; 1399 | right: 5%; 1400 | z-index: 10; 1401 | top: 10%; 1402 | top: calc(5vw + 5vh); 1403 | padding: 30px 90px; 1404 | max-width: 500px; 1405 | outline: solid 10000px rgba(255, 255, 255, 0.6); 1406 | border-radius: 5px; 1407 | border-width: 1px 1px 0 1px; 1408 | background-color: #ddd; 1409 | display: none; 1410 | } 1411 | 1412 | #shortcuts-help.active { 1413 | display: block; 1414 | } 1415 | 1416 | #shortcuts-help ul { 1417 | padding: 0; 1418 | } 1419 | 1420 | #shortcuts-help li { 1421 | display: flex; 1422 | justify-content: space-between; 1423 | } 1424 | 1425 | #shortcuts-help code { 1426 | padding: 3px 10px; 1427 | border-radius: 3px; 1428 | border-width: 1px 1px 0 1px; 1429 | border-color: #bbb; 1430 | background-color: #eee; 1431 | box-shadow: inset 0 -1px 0 #ccc; 1432 | } 1433 | -------------------------------------------------------------------------------- /assets/ecmarkup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | let sdoBox = { 3 | init() { 4 | this.$alternativeId = null; 5 | this.$outer = document.createElement('div'); 6 | this.$outer.classList.add('toolbox-container'); 7 | this.$container = document.createElement('div'); 8 | this.$container.classList.add('toolbox'); 9 | this.$displayLink = document.createElement('a'); 10 | this.$displayLink.setAttribute('href', '#'); 11 | this.$displayLink.textContent = 'Syntax-Directed Operations'; 12 | this.$displayLink.addEventListener('click', e => { 13 | e.preventDefault(); 14 | e.stopPropagation(); 15 | referencePane.showSDOs(sdoMap[this.$alternativeId] || {}, this.$alternativeId); 16 | }); 17 | this.$container.appendChild(this.$displayLink); 18 | this.$outer.appendChild(this.$container); 19 | document.body.appendChild(this.$outer); 20 | }, 21 | 22 | activate(el) { 23 | clearTimeout(this.deactiveTimeout); 24 | Toolbox.deactivate(); 25 | this.$alternativeId = el.id; 26 | let numSdos = Object.keys(sdoMap[this.$alternativeId] || {}).length; 27 | this.$displayLink.textContent = 'Syntax-Directed Operations (' + numSdos + ')'; 28 | this.$outer.classList.add('active'); 29 | let top = el.offsetTop - this.$outer.offsetHeight; 30 | let left = el.offsetLeft + 50 - 10; // 50px = padding-left(=75px) + text-indent(=-25px) 31 | this.$outer.setAttribute('style', 'left: ' + left + 'px; top: ' + top + 'px'); 32 | if (top < document.body.scrollTop) { 33 | this.$container.scrollIntoView(); 34 | } 35 | }, 36 | 37 | deactivate() { 38 | clearTimeout(this.deactiveTimeout); 39 | this.$outer.classList.remove('active'); 40 | }, 41 | }; 42 | 43 | document.addEventListener('DOMContentLoaded', () => { 44 | if (typeof sdoMap == 'undefined') { 45 | console.error('could not find sdo map'); 46 | return; 47 | } 48 | sdoBox.init(); 49 | 50 | let insideTooltip = false; 51 | sdoBox.$outer.addEventListener('pointerenter', () => { 52 | insideTooltip = true; 53 | }); 54 | sdoBox.$outer.addEventListener('pointerleave', () => { 55 | insideTooltip = false; 56 | sdoBox.deactivate(); 57 | }); 58 | 59 | sdoBox.deactiveTimeout = null; 60 | [].forEach.call(document.querySelectorAll('emu-grammar[type=definition] emu-rhs'), node => { 61 | node.addEventListener('pointerenter', function () { 62 | sdoBox.activate(this); 63 | }); 64 | 65 | node.addEventListener('pointerleave', () => { 66 | sdoBox.deactiveTimeout = setTimeout(() => { 67 | if (!insideTooltip) { 68 | sdoBox.deactivate(); 69 | } 70 | }, 500); 71 | }); 72 | }); 73 | 74 | document.addEventListener( 75 | 'keydown', 76 | debounce(e => { 77 | if (e.code === 'Escape') { 78 | sdoBox.deactivate(); 79 | } 80 | }), 81 | ); 82 | }); 83 | 84 | 'use strict'; 85 | function Search(menu) { 86 | this.menu = menu; 87 | this.$search = document.getElementById('menu-search'); 88 | this.$searchBox = document.getElementById('menu-search-box'); 89 | this.$searchResults = document.getElementById('menu-search-results'); 90 | 91 | this.loadBiblio(); 92 | 93 | document.addEventListener('keydown', this.documentKeydown.bind(this)); 94 | 95 | this.$searchBox.addEventListener( 96 | 'keydown', 97 | debounce(this.searchBoxKeydown.bind(this), { stopPropagation: true }), 98 | ); 99 | this.$searchBox.addEventListener( 100 | 'keyup', 101 | debounce(this.searchBoxKeyup.bind(this), { stopPropagation: true }), 102 | ); 103 | 104 | // Perform an initial search if the box is not empty. 105 | if (this.$searchBox.value) { 106 | this.search(this.$searchBox.value); 107 | } 108 | } 109 | 110 | Search.prototype.loadBiblio = function () { 111 | if (typeof biblio === 'undefined') { 112 | console.error('could not find biblio'); 113 | this.biblio = { refToClause: {}, entries: [] }; 114 | } else { 115 | this.biblio = biblio; 116 | this.biblio.clauses = this.biblio.entries.filter(e => e.type === 'clause'); 117 | this.biblio.byId = this.biblio.entries.reduce((map, entry) => { 118 | map[entry.id] = entry; 119 | return map; 120 | }, {}); 121 | let refParentClause = Object.create(null); 122 | this.biblio.refParentClause = refParentClause; 123 | let refsByClause = this.biblio.refsByClause; 124 | Object.keys(refsByClause).forEach(clause => { 125 | refsByClause[clause].forEach(ref => { 126 | refParentClause[ref] = clause; 127 | }); 128 | }); 129 | } 130 | }; 131 | 132 | Search.prototype.documentKeydown = function (e) { 133 | if (e.key === '/') { 134 | e.preventDefault(); 135 | e.stopPropagation(); 136 | this.triggerSearch(); 137 | } 138 | }; 139 | 140 | Search.prototype.searchBoxKeydown = function (e) { 141 | e.stopPropagation(); 142 | e.preventDefault(); 143 | if (e.keyCode === 191 && e.target.value.length === 0) { 144 | e.preventDefault(); 145 | } else if (e.keyCode === 13) { 146 | e.preventDefault(); 147 | this.selectResult(); 148 | } 149 | }; 150 | 151 | Search.prototype.searchBoxKeyup = function (e) { 152 | if (e.keyCode === 13 || e.keyCode === 9) { 153 | return; 154 | } 155 | 156 | this.search(e.target.value); 157 | }; 158 | 159 | Search.prototype.triggerSearch = function () { 160 | if (this.menu.isVisible()) { 161 | this._closeAfterSearch = false; 162 | } else { 163 | this._closeAfterSearch = true; 164 | this.menu.show(); 165 | } 166 | 167 | this.$searchBox.focus(); 168 | this.$searchBox.select(); 169 | }; 170 | // bit 12 - Set if the result starts with searchString 171 | // bits 8-11: 8 - number of chunks multiplied by 2 if cases match, otherwise 1. 172 | // bits 1-7: 127 - length of the entry 173 | // General scheme: prefer case sensitive matches with fewer chunks, and otherwise 174 | // prefer shorter matches. 175 | function relevance(result) { 176 | let relevance = 0; 177 | 178 | relevance = Math.max(0, 8 - result.match.chunks) << 7; 179 | 180 | if (result.match.caseMatch) { 181 | relevance *= 2; 182 | } 183 | 184 | if (result.match.prefix) { 185 | relevance += 2048; 186 | } 187 | 188 | relevance += Math.max(0, 255 - result.key.length); 189 | 190 | return relevance; 191 | } 192 | 193 | Search.prototype.search = function (searchString) { 194 | if (searchString === '') { 195 | this.displayResults([]); 196 | this.hideSearch(); 197 | return; 198 | } else { 199 | this.showSearch(); 200 | } 201 | 202 | if (searchString.length === 1) { 203 | this.displayResults([]); 204 | return; 205 | } 206 | 207 | let results; 208 | 209 | if (/^[\d.]*$/.test(searchString)) { 210 | results = this.biblio.clauses 211 | .filter(clause => clause.number.substring(0, searchString.length) === searchString) 212 | .map(clause => ({ key: getKey(clause), entry: clause })); 213 | } else { 214 | results = []; 215 | 216 | for (let i = 0; i < this.biblio.entries.length; i++) { 217 | let entry = this.biblio.entries[i]; 218 | let key = getKey(entry); 219 | if (!key) { 220 | // biblio entries without a key aren't searchable 221 | continue; 222 | } 223 | 224 | let match = fuzzysearch(searchString, key); 225 | if (match) { 226 | results.push({ key, entry, match }); 227 | } 228 | } 229 | 230 | results.forEach(result => { 231 | result.relevance = relevance(result, searchString); 232 | }); 233 | 234 | results = results.sort((a, b) => b.relevance - a.relevance); 235 | } 236 | 237 | if (results.length > 50) { 238 | results = results.slice(0, 50); 239 | } 240 | 241 | this.displayResults(results); 242 | }; 243 | Search.prototype.hideSearch = function () { 244 | this.$search.classList.remove('active'); 245 | }; 246 | 247 | Search.prototype.showSearch = function () { 248 | this.$search.classList.add('active'); 249 | }; 250 | 251 | Search.prototype.selectResult = function () { 252 | let $first = this.$searchResults.querySelector('li:first-child a'); 253 | 254 | if ($first) { 255 | document.location = $first.getAttribute('href'); 256 | } 257 | 258 | this.$searchBox.value = ''; 259 | this.$searchBox.blur(); 260 | this.displayResults([]); 261 | this.hideSearch(); 262 | 263 | if (this._closeAfterSearch) { 264 | this.menu.hide(); 265 | } 266 | }; 267 | 268 | Search.prototype.displayResults = function (results) { 269 | if (results.length > 0) { 270 | this.$searchResults.classList.remove('no-results'); 271 | 272 | let html = '
    '; 273 | 274 | results.forEach(result => { 275 | let key = result.key; 276 | let entry = result.entry; 277 | let id = entry.id; 278 | let cssClass = ''; 279 | let text = ''; 280 | 281 | if (entry.type === 'clause') { 282 | let number = entry.number ? entry.number + ' ' : ''; 283 | text = number + key; 284 | cssClass = 'clause'; 285 | id = entry.id; 286 | } else if (entry.type === 'production') { 287 | text = key; 288 | cssClass = 'prod'; 289 | id = entry.id; 290 | } else if (entry.type === 'op') { 291 | text = key; 292 | cssClass = 'op'; 293 | id = entry.id || entry.refId; 294 | } else if (entry.type === 'term') { 295 | text = key; 296 | cssClass = 'term'; 297 | id = entry.id || entry.refId; 298 | } 299 | 300 | if (text) { 301 | html += ``; 302 | } 303 | }); 304 | 305 | html += '
'; 306 | 307 | this.$searchResults.innerHTML = html; 308 | } else { 309 | this.$searchResults.innerHTML = ''; 310 | this.$searchResults.classList.add('no-results'); 311 | } 312 | }; 313 | 314 | function getKey(item) { 315 | if (item.key) { 316 | return item.key; 317 | } 318 | switch (item.type) { 319 | case 'clause': 320 | return item.title || item.titleHTML; 321 | case 'production': 322 | return item.name; 323 | case 'op': 324 | return item.aoid; 325 | case 'term': 326 | return item.term; 327 | case 'table': 328 | case 'figure': 329 | case 'example': 330 | case 'note': 331 | return item.caption; 332 | case 'step': 333 | return item.id; 334 | default: 335 | throw new Error("Can't get key for " + item.type); 336 | } 337 | } 338 | 339 | function Menu() { 340 | this.$toggle = document.getElementById('menu-toggle'); 341 | this.$menu = document.getElementById('menu'); 342 | this.$toc = document.querySelector('menu-toc > ol'); 343 | this.$pins = document.querySelector('#menu-pins'); 344 | this.$pinList = document.getElementById('menu-pins-list'); 345 | this.$toc = document.querySelector('#menu-toc > ol'); 346 | this.$specContainer = document.getElementById('spec-container'); 347 | this.search = new Search(this); 348 | 349 | this._pinnedIds = {}; 350 | this.loadPinEntries(); 351 | 352 | // unpin all button 353 | document 354 | .querySelector('#menu-pins .unpin-all') 355 | .addEventListener('click', this.unpinAll.bind(this)); 356 | 357 | // individual unpinning buttons 358 | this.$pinList.addEventListener('click', this.pinListClick.bind(this)); 359 | 360 | // toggle menu 361 | this.$toggle.addEventListener('click', this.toggle.bind(this)); 362 | 363 | // keydown events for pinned clauses 364 | document.addEventListener('keydown', this.documentKeydown.bind(this)); 365 | 366 | // toc expansion 367 | let tocItems = this.$menu.querySelectorAll('#menu-toc li'); 368 | for (let i = 0; i < tocItems.length; i++) { 369 | let $item = tocItems[i]; 370 | $item.addEventListener('click', event => { 371 | $item.classList.toggle('active'); 372 | event.stopPropagation(); 373 | }); 374 | } 375 | 376 | // close toc on toc item selection 377 | let tocLinks = this.$menu.querySelectorAll('#menu-toc li > a'); 378 | for (let i = 0; i < tocLinks.length; i++) { 379 | let $link = tocLinks[i]; 380 | $link.addEventListener('click', event => { 381 | this.toggle(); 382 | event.stopPropagation(); 383 | }); 384 | } 385 | 386 | // update active clause on scroll 387 | window.addEventListener('scroll', debounce(this.updateActiveClause.bind(this))); 388 | this.updateActiveClause(); 389 | 390 | // prevent menu scrolling from scrolling the body 391 | this.$toc.addEventListener('wheel', e => { 392 | let target = e.currentTarget; 393 | let offTop = e.deltaY < 0 && target.scrollTop === 0; 394 | if (offTop) { 395 | e.preventDefault(); 396 | } 397 | let offBottom = e.deltaY > 0 && target.offsetHeight + target.scrollTop >= target.scrollHeight; 398 | 399 | if (offBottom) { 400 | e.preventDefault(); 401 | } 402 | }); 403 | } 404 | 405 | Menu.prototype.documentKeydown = function (e) { 406 | e.stopPropagation(); 407 | if (e.keyCode === 80) { 408 | this.togglePinEntry(); 409 | } else if (e.keyCode >= 48 && e.keyCode < 58) { 410 | this.selectPin((e.keyCode - 9) % 10); 411 | } 412 | }; 413 | 414 | Menu.prototype.updateActiveClause = function () { 415 | this.setActiveClause(findActiveClause(this.$specContainer)); 416 | }; 417 | 418 | Menu.prototype.setActiveClause = function (clause) { 419 | this.$activeClause = clause; 420 | this.revealInToc(this.$activeClause); 421 | }; 422 | 423 | Menu.prototype.revealInToc = function (path) { 424 | let current = this.$toc.querySelectorAll('li.revealed'); 425 | for (let i = 0; i < current.length; i++) { 426 | current[i].classList.remove('revealed'); 427 | current[i].classList.remove('revealed-leaf'); 428 | } 429 | 430 | current = this.$toc; 431 | let index = 0; 432 | outer: while (index < path.length) { 433 | let children = current.children; 434 | for (let i = 0; i < children.length; i++) { 435 | if ('#' + path[index].id === children[i].children[1].hash) { 436 | children[i].classList.add('revealed'); 437 | if (index === path.length - 1) { 438 | children[i].classList.add('revealed-leaf'); 439 | let rect = children[i].getBoundingClientRect(); 440 | // this.$toc.getBoundingClientRect().top; 441 | let tocRect = this.$toc.getBoundingClientRect(); 442 | if (rect.top + 10 > tocRect.bottom) { 443 | this.$toc.scrollTop = 444 | this.$toc.scrollTop + (rect.top - tocRect.bottom) + (rect.bottom - rect.top); 445 | } else if (rect.top < tocRect.top) { 446 | this.$toc.scrollTop = this.$toc.scrollTop - (tocRect.top - rect.top); 447 | } 448 | } 449 | current = children[i].querySelector('ol'); 450 | index++; 451 | continue outer; 452 | } 453 | } 454 | console.log('could not find location in table of contents', path); 455 | break; 456 | } 457 | }; 458 | 459 | function findActiveClause(root, path) { 460 | path = path || []; 461 | 462 | let visibleClauses = getVisibleClauses(root, path); 463 | let midpoint = Math.floor(window.innerHeight / 2); 464 | 465 | for (let [$clause, path] of visibleClauses) { 466 | let { top: clauseTop, bottom: clauseBottom } = $clause.getBoundingClientRect(); 467 | let isFullyVisibleAboveTheFold = 468 | clauseTop > 0 && clauseTop < midpoint && clauseBottom < window.innerHeight; 469 | if (isFullyVisibleAboveTheFold) { 470 | return path; 471 | } 472 | } 473 | 474 | visibleClauses.sort(([, pathA], [, pathB]) => pathB.length - pathA.length); 475 | for (let [$clause, path] of visibleClauses) { 476 | let { top: clauseTop, bottom: clauseBottom } = $clause.getBoundingClientRect(); 477 | let $header = $clause.querySelector('h1'); 478 | let clauseStyles = getComputedStyle($clause); 479 | let marginTop = Math.max( 480 | 0, 481 | parseInt(clauseStyles['margin-top']), 482 | parseInt(getComputedStyle($header)['margin-top']), 483 | ); 484 | let marginBottom = Math.max(0, parseInt(clauseStyles['margin-bottom'])); 485 | let crossesMidpoint = 486 | clauseTop - marginTop <= midpoint && clauseBottom + marginBottom >= midpoint; 487 | if (crossesMidpoint) { 488 | return path; 489 | } 490 | } 491 | 492 | return path; 493 | } 494 | 495 | function getVisibleClauses(root, path) { 496 | let childClauses = getChildClauses(root); 497 | path = path || []; 498 | 499 | let result = []; 500 | 501 | let seenVisibleClause = false; 502 | for (let $clause of childClauses) { 503 | let { top: clauseTop, bottom: clauseBottom } = $clause.getBoundingClientRect(); 504 | let isPartiallyVisible = 505 | (clauseTop > 0 && clauseTop < window.innerHeight) || 506 | (clauseBottom > 0 && clauseBottom < window.innerHeight) || 507 | (clauseTop < 0 && clauseBottom > window.innerHeight); 508 | 509 | if (isPartiallyVisible) { 510 | seenVisibleClause = true; 511 | let innerPath = path.concat($clause); 512 | result.push([$clause, innerPath]); 513 | result.push(...getVisibleClauses($clause, innerPath)); 514 | } else if (seenVisibleClause) { 515 | break; 516 | } 517 | } 518 | 519 | return result; 520 | } 521 | 522 | function* getChildClauses(root) { 523 | for (let el of root.children) { 524 | switch (el.nodeName) { 525 | // descend into 526 | case 'EMU-IMPORT': 527 | yield* getChildClauses(el); 528 | break; 529 | 530 | // accept , , and 531 | case 'EMU-CLAUSE': 532 | case 'EMU-INTRO': 533 | case 'EMU-ANNEX': 534 | yield el; 535 | } 536 | } 537 | } 538 | 539 | Menu.prototype.toggle = function () { 540 | this.$menu.classList.toggle('active'); 541 | }; 542 | 543 | Menu.prototype.show = function () { 544 | this.$menu.classList.add('active'); 545 | }; 546 | 547 | Menu.prototype.hide = function () { 548 | this.$menu.classList.remove('active'); 549 | }; 550 | 551 | Menu.prototype.isVisible = function () { 552 | return this.$menu.classList.contains('active'); 553 | }; 554 | 555 | Menu.prototype.showPins = function () { 556 | this.$pins.classList.add('active'); 557 | }; 558 | 559 | Menu.prototype.hidePins = function () { 560 | this.$pins.classList.remove('active'); 561 | }; 562 | 563 | Menu.prototype.addPinEntry = function (id) { 564 | let entry = this.search.biblio.byId[id]; 565 | if (!entry) { 566 | // id was deleted after pin (or something) so remove it 567 | delete this._pinnedIds[id]; 568 | this.persistPinEntries(); 569 | return; 570 | } 571 | 572 | let text; 573 | if (entry.type === 'clause') { 574 | let prefix; 575 | if (entry.number) { 576 | prefix = entry.number + ' '; 577 | } else { 578 | prefix = ''; 579 | } 580 | text = `${prefix}${entry.titleHTML}`; 581 | } else { 582 | text = getKey(entry); 583 | } 584 | 585 | let link = `${text}`; 586 | this.$pinList.innerHTML += `
  • ${link}
  • `; 587 | 588 | if (Object.keys(this._pinnedIds).length === 0) { 589 | this.showPins(); 590 | } 591 | this._pinnedIds[id] = true; 592 | this.persistPinEntries(); 593 | }; 594 | 595 | Menu.prototype.removePinEntry = function (id) { 596 | let item = this.$pinList.querySelector(`li[data-section-id="${id}"]`); 597 | this.$pinList.removeChild(item); 598 | delete this._pinnedIds[id]; 599 | if (Object.keys(this._pinnedIds).length === 0) { 600 | this.hidePins(); 601 | } 602 | 603 | this.persistPinEntries(); 604 | }; 605 | 606 | Menu.prototype.unpinAll = function () { 607 | for (let id of Object.keys(this._pinnedIds)) { 608 | this.removePinEntry(id); 609 | } 610 | }; 611 | 612 | Menu.prototype.pinListClick = function (event) { 613 | if (event?.target?.classList.contains('unpin')) { 614 | let id = event.target.parentNode.dataset.sectionId; 615 | if (id) { 616 | this.removePinEntry(id); 617 | } 618 | } 619 | }; 620 | 621 | Menu.prototype.persistPinEntries = function () { 622 | try { 623 | if (!window.localStorage) return; 624 | } catch (e) { 625 | return; 626 | } 627 | 628 | localStorage.pinEntries = JSON.stringify(Object.keys(this._pinnedIds)); 629 | }; 630 | 631 | Menu.prototype.loadPinEntries = function () { 632 | try { 633 | if (!window.localStorage) return; 634 | } catch (e) { 635 | return; 636 | } 637 | 638 | let pinsString = window.localStorage.pinEntries; 639 | if (!pinsString) return; 640 | let pins = JSON.parse(pinsString); 641 | for (let i = 0; i < pins.length; i++) { 642 | this.addPinEntry(pins[i]); 643 | } 644 | }; 645 | 646 | Menu.prototype.togglePinEntry = function (id) { 647 | if (!id) { 648 | id = this.$activeClause[this.$activeClause.length - 1].id; 649 | } 650 | 651 | if (this._pinnedIds[id]) { 652 | this.removePinEntry(id); 653 | } else { 654 | this.addPinEntry(id); 655 | } 656 | }; 657 | 658 | Menu.prototype.selectPin = function (num) { 659 | if (num >= this.$pinList.children.length) return; 660 | document.location = this.$pinList.children[num].children[0].href; 661 | }; 662 | 663 | let menu; 664 | 665 | document.addEventListener('DOMContentLoaded', init); 666 | 667 | function debounce(fn, opts) { 668 | opts = opts || {}; 669 | let timeout; 670 | return function (e) { 671 | if (opts.stopPropagation) { 672 | e.stopPropagation(); 673 | } 674 | let args = arguments; 675 | if (timeout) { 676 | clearTimeout(timeout); 677 | } 678 | timeout = setTimeout(() => { 679 | timeout = null; 680 | fn.apply(this, args); 681 | }, 150); 682 | }; 683 | } 684 | 685 | let CLAUSE_NODES = ['EMU-CLAUSE', 'EMU-INTRO', 'EMU-ANNEX']; 686 | function findContainer($elem) { 687 | let parentClause = $elem.parentNode; 688 | while (parentClause && CLAUSE_NODES.indexOf(parentClause.nodeName) === -1) { 689 | parentClause = parentClause.parentNode; 690 | } 691 | return parentClause; 692 | } 693 | 694 | function findLocalReferences(parentClause, name) { 695 | let vars = parentClause.querySelectorAll('var'); 696 | let references = []; 697 | 698 | for (let i = 0; i < vars.length; i++) { 699 | let $var = vars[i]; 700 | 701 | if ($var.innerHTML === name) { 702 | references.push($var); 703 | } 704 | } 705 | 706 | return references; 707 | } 708 | 709 | let REFERENCED_CLASSES = Array.from({ length: 7 }, (x, i) => `referenced${i}`); 710 | function chooseHighlightIndex(parentClause) { 711 | let counts = REFERENCED_CLASSES.map($class => parentClause.getElementsByClassName($class).length); 712 | // Find the earliest index with the lowest count. 713 | let minCount = Infinity; 714 | let index = null; 715 | for (let i = 0; i < counts.length; i++) { 716 | if (counts[i] < minCount) { 717 | minCount = counts[i]; 718 | index = i; 719 | } 720 | } 721 | return index; 722 | } 723 | 724 | function toggleFindLocalReferences($elem) { 725 | let parentClause = findContainer($elem); 726 | let references = findLocalReferences(parentClause, $elem.innerHTML); 727 | if ($elem.classList.contains('referenced')) { 728 | references.forEach($reference => { 729 | $reference.classList.remove('referenced', ...REFERENCED_CLASSES); 730 | }); 731 | } else { 732 | let index = chooseHighlightIndex(parentClause); 733 | references.forEach($reference => { 734 | $reference.classList.add('referenced', `referenced${index}`); 735 | }); 736 | } 737 | } 738 | 739 | function installFindLocalReferences() { 740 | document.addEventListener('click', e => { 741 | if (e.target.nodeName === 'VAR') { 742 | toggleFindLocalReferences(e.target); 743 | } 744 | }); 745 | } 746 | 747 | document.addEventListener('DOMContentLoaded', installFindLocalReferences); 748 | 749 | // The following license applies to the fuzzysearch function 750 | // The MIT License (MIT) 751 | // Copyright © 2015 Nicolas Bevacqua 752 | // Copyright © 2016 Brian Terlson 753 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 754 | // this software and associated documentation files (the "Software"), to deal in 755 | // the Software without restriction, including without limitation the rights to 756 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 757 | // the Software, and to permit persons to whom the Software is furnished to do so, 758 | // subject to the following conditions: 759 | 760 | // The above copyright notice and this permission notice shall be included in all 761 | // copies or substantial portions of the Software. 762 | 763 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 764 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 765 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 766 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 767 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 768 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 769 | function fuzzysearch(searchString, haystack, caseInsensitive) { 770 | let tlen = haystack.length; 771 | let qlen = searchString.length; 772 | let chunks = 1; 773 | let finding = false; 774 | 775 | if (qlen > tlen) { 776 | return false; 777 | } 778 | 779 | if (qlen === tlen) { 780 | if (searchString === haystack) { 781 | return { caseMatch: true, chunks: 1, prefix: true }; 782 | } else if (searchString.toLowerCase() === haystack.toLowerCase()) { 783 | return { caseMatch: false, chunks: 1, prefix: true }; 784 | } else { 785 | return false; 786 | } 787 | } 788 | 789 | let j = 0; 790 | outer: for (let i = 0; i < qlen; i++) { 791 | let nch = searchString[i]; 792 | while (j < tlen) { 793 | let targetChar = haystack[j++]; 794 | if (targetChar === nch) { 795 | finding = true; 796 | continue outer; 797 | } 798 | if (finding) { 799 | chunks++; 800 | finding = false; 801 | } 802 | } 803 | 804 | if (caseInsensitive) { 805 | return false; 806 | } 807 | 808 | return fuzzysearch(searchString.toLowerCase(), haystack.toLowerCase(), true); 809 | } 810 | 811 | return { caseMatch: !caseInsensitive, chunks, prefix: j <= qlen }; 812 | } 813 | 814 | let referencePane = { 815 | init() { 816 | this.$container = document.createElement('div'); 817 | this.$container.setAttribute('id', 'references-pane-container'); 818 | 819 | let $spacer = document.createElement('div'); 820 | $spacer.setAttribute('id', 'references-pane-spacer'); 821 | $spacer.classList.add('menu-spacer'); 822 | 823 | this.$pane = document.createElement('div'); 824 | this.$pane.setAttribute('id', 'references-pane'); 825 | 826 | this.$container.appendChild($spacer); 827 | this.$container.appendChild(this.$pane); 828 | 829 | this.$header = document.createElement('div'); 830 | this.$header.classList.add('menu-pane-header'); 831 | this.$headerText = document.createElement('span'); 832 | this.$header.appendChild(this.$headerText); 833 | this.$headerRefId = document.createElement('a'); 834 | this.$header.appendChild(this.$headerRefId); 835 | this.$header.addEventListener('pointerdown', e => { 836 | this.dragStart(e); 837 | }); 838 | 839 | this.$closeButton = document.createElement('span'); 840 | this.$closeButton.setAttribute('id', 'references-pane-close'); 841 | this.$closeButton.addEventListener('click', () => { 842 | this.deactivate(); 843 | }); 844 | this.$header.appendChild(this.$closeButton); 845 | 846 | this.$pane.appendChild(this.$header); 847 | this.$tableContainer = document.createElement('div'); 848 | this.$tableContainer.setAttribute('id', 'references-pane-table-container'); 849 | 850 | this.$table = document.createElement('table'); 851 | this.$table.setAttribute('id', 'references-pane-table'); 852 | 853 | this.$tableBody = this.$table.createTBody(); 854 | 855 | this.$tableContainer.appendChild(this.$table); 856 | this.$pane.appendChild(this.$tableContainer); 857 | 858 | if (menu != null) { 859 | menu.$specContainer.appendChild(this.$container); 860 | } 861 | }, 862 | 863 | activate() { 864 | this.$container.classList.add('active'); 865 | }, 866 | 867 | deactivate() { 868 | this.$container.classList.remove('active'); 869 | this.state = null; 870 | }, 871 | 872 | showReferencesFor(entry) { 873 | this.activate(); 874 | this.state = { type: 'ref', id: entry.id }; 875 | this.$headerText.textContent = 'References to '; 876 | let newBody = document.createElement('tbody'); 877 | let previousId; 878 | let previousCell; 879 | let dupCount = 0; 880 | this.$headerRefId.innerHTML = getKey(entry); 881 | this.$headerRefId.setAttribute('href', makeLinkToId(entry.id)); 882 | this.$headerRefId.style.display = 'inline'; 883 | (entry.referencingIds || []) 884 | .map(id => { 885 | let cid = menu.search.biblio.refParentClause[id]; 886 | let clause = menu.search.biblio.byId[cid]; 887 | if (clause == null) { 888 | throw new Error('could not find clause for id ' + cid); 889 | } 890 | return { id, clause }; 891 | }) 892 | .sort((a, b) => sortByClauseNumber(a.clause, b.clause)) 893 | .forEach(record => { 894 | if (previousId === record.clause.id) { 895 | previousCell.innerHTML += ` (${dupCount + 2})`; 896 | dupCount++; 897 | } else { 898 | let row = newBody.insertRow(); 899 | let cell = row.insertCell(); 900 | cell.innerHTML = record.clause.number; 901 | cell = row.insertCell(); 902 | cell.innerHTML = `${record.clause.titleHTML}`; 903 | previousCell = cell; 904 | previousId = record.clause.id; 905 | dupCount = 0; 906 | } 907 | }, this); 908 | this.$table.removeChild(this.$tableBody); 909 | this.$tableBody = newBody; 910 | this.$table.appendChild(this.$tableBody); 911 | this.autoSize(); 912 | }, 913 | 914 | showSDOs(sdos, alternativeId) { 915 | let rhs = document.getElementById(alternativeId); 916 | let parentName = rhs.parentNode.getAttribute('name'); 917 | let colons = rhs.parentNode.querySelector('emu-geq'); 918 | rhs = rhs.cloneNode(true); 919 | rhs.querySelectorAll('emu-params,emu-constraints').forEach(e => { 920 | e.remove(); 921 | }); 922 | rhs.querySelectorAll('[id]').forEach(e => { 923 | e.removeAttribute('id'); 924 | }); 925 | rhs.querySelectorAll('a').forEach(e => { 926 | e.parentNode.replaceChild(document.createTextNode(e.textContent), e); 927 | }); 928 | 929 | this.$headerText.innerHTML = `Syntax-Directed Operations for
    ${parentName} ${colons.outerHTML} `; 930 | this.$headerText.querySelector('a').append(rhs); 931 | this.showSDOsBody(sdos, alternativeId); 932 | }, 933 | 934 | showSDOsBody(sdos, alternativeId) { 935 | this.activate(); 936 | this.state = { type: 'sdo', id: alternativeId, html: this.$headerText.innerHTML }; 937 | this.$headerRefId.style.display = 'none'; 938 | let newBody = document.createElement('tbody'); 939 | Object.keys(sdos).forEach(sdoName => { 940 | let pair = sdos[sdoName]; 941 | let clause = pair.clause; 942 | let ids = pair.ids; 943 | let first = ids[0]; 944 | let row = newBody.insertRow(); 945 | let cell = row.insertCell(); 946 | cell.innerHTML = clause; 947 | cell = row.insertCell(); 948 | let html = '' + sdoName + ''; 949 | for (let i = 1; i < ids.length; ++i) { 950 | html += ' (' + (i + 1) + ')'; 951 | } 952 | cell.innerHTML = html; 953 | }); 954 | this.$table.removeChild(this.$tableBody); 955 | this.$tableBody = newBody; 956 | this.$table.appendChild(this.$tableBody); 957 | this.autoSize(); 958 | }, 959 | 960 | autoSize() { 961 | this.$tableContainer.style.height = 962 | Math.min(250, this.$table.getBoundingClientRect().height) + 'px'; 963 | }, 964 | 965 | dragStart(pointerDownEvent) { 966 | let startingMousePos = pointerDownEvent.clientY; 967 | let startingHeight = this.$tableContainer.getBoundingClientRect().height; 968 | let moveListener = pointerMoveEvent => { 969 | if (pointerMoveEvent.buttons === 0) { 970 | removeListeners(); 971 | return; 972 | } 973 | let desiredHeight = startingHeight - (pointerMoveEvent.clientY - startingMousePos); 974 | this.$tableContainer.style.height = Math.max(0, desiredHeight) + 'px'; 975 | }; 976 | let listenerOptions = { capture: true, passive: true }; 977 | let removeListeners = () => { 978 | document.removeEventListener('pointermove', moveListener, listenerOptions); 979 | this.$header.removeEventListener('pointerup', removeListeners, listenerOptions); 980 | this.$header.removeEventListener('pointercancel', removeListeners, listenerOptions); 981 | }; 982 | document.addEventListener('pointermove', moveListener, listenerOptions); 983 | this.$header.addEventListener('pointerup', removeListeners, listenerOptions); 984 | this.$header.addEventListener('pointercancel', removeListeners, listenerOptions); 985 | }, 986 | }; 987 | 988 | let Toolbox = { 989 | init() { 990 | this.$outer = document.createElement('div'); 991 | this.$outer.classList.add('toolbox-container'); 992 | this.$container = document.createElement('div'); 993 | this.$container.classList.add('toolbox'); 994 | this.$outer.appendChild(this.$container); 995 | this.$permalink = document.createElement('a'); 996 | this.$permalink.textContent = 'Permalink'; 997 | this.$pinLink = document.createElement('a'); 998 | this.$pinLink.textContent = 'Pin'; 999 | this.$pinLink.setAttribute('href', '#'); 1000 | this.$pinLink.addEventListener('click', e => { 1001 | e.preventDefault(); 1002 | e.stopPropagation(); 1003 | menu.togglePinEntry(this.entry.id); 1004 | this.$pinLink.textContent = menu._pinnedIds[this.entry.id] ? 'Unpin' : 'Pin'; 1005 | }); 1006 | 1007 | this.$refsLink = document.createElement('a'); 1008 | this.$refsLink.setAttribute('href', '#'); 1009 | this.$refsLink.addEventListener('click', e => { 1010 | e.preventDefault(); 1011 | e.stopPropagation(); 1012 | referencePane.showReferencesFor(this.entry); 1013 | }); 1014 | this.$container.appendChild(this.$permalink); 1015 | this.$container.appendChild(document.createTextNode(' ')); 1016 | this.$container.appendChild(this.$pinLink); 1017 | this.$container.appendChild(document.createTextNode(' ')); 1018 | this.$container.appendChild(this.$refsLink); 1019 | document.body.appendChild(this.$outer); 1020 | }, 1021 | 1022 | activate(el, entry, target) { 1023 | if (el === this._activeEl) return; 1024 | sdoBox.deactivate(); 1025 | this.active = true; 1026 | this.entry = entry; 1027 | this.$pinLink.textContent = menu._pinnedIds[entry.id] ? 'Unpin' : 'Pin'; 1028 | this.$outer.classList.add('active'); 1029 | this.top = el.offsetTop - this.$outer.offsetHeight; 1030 | this.left = el.offsetLeft - 10; 1031 | this.$outer.setAttribute('style', 'left: ' + this.left + 'px; top: ' + this.top + 'px'); 1032 | this.updatePermalink(); 1033 | this.updateReferences(); 1034 | this._activeEl = el; 1035 | if (this.top < document.body.scrollTop && el === target) { 1036 | // don't scroll unless it's a small thing (< 200px) 1037 | this.$outer.scrollIntoView(); 1038 | } 1039 | }, 1040 | 1041 | updatePermalink() { 1042 | this.$permalink.setAttribute('href', makeLinkToId(this.entry.id)); 1043 | }, 1044 | 1045 | updateReferences() { 1046 | this.$refsLink.textContent = `References (${(this.entry.referencingIds || []).length})`; 1047 | }, 1048 | 1049 | activateIfMouseOver(e) { 1050 | let ref = this.findReferenceUnder(e.target); 1051 | if (ref && (!this.active || e.pageY > this._activeEl.offsetTop)) { 1052 | let entry = menu.search.biblio.byId[ref.id]; 1053 | this.activate(ref.element, entry, e.target); 1054 | } else if ( 1055 | this.active && 1056 | (e.pageY < this.top || e.pageY > this._activeEl.offsetTop + this._activeEl.offsetHeight) 1057 | ) { 1058 | this.deactivate(); 1059 | } 1060 | }, 1061 | 1062 | findReferenceUnder(el) { 1063 | while (el) { 1064 | let parent = el.parentNode; 1065 | if (el.nodeName === 'EMU-RHS' || el.nodeName === 'EMU-PRODUCTION') { 1066 | return null; 1067 | } 1068 | if ( 1069 | el.nodeName === 'H1' && 1070 | parent.nodeName.match(/EMU-CLAUSE|EMU-ANNEX|EMU-INTRO/) && 1071 | parent.id 1072 | ) { 1073 | return { element: el, id: parent.id }; 1074 | } else if (el.nodeName === 'EMU-NT') { 1075 | if ( 1076 | parent.nodeName === 'EMU-PRODUCTION' && 1077 | parent.id && 1078 | parent.id[0] !== '_' && 1079 | parent.firstElementChild === el 1080 | ) { 1081 | // return the LHS non-terminal element 1082 | return { element: el, id: parent.id }; 1083 | } 1084 | return null; 1085 | } else if ( 1086 | el.nodeName.match(/EMU-(?!CLAUSE|XREF|ANNEX|INTRO)|DFN/) && 1087 | el.id && 1088 | el.id[0] !== '_' 1089 | ) { 1090 | if ( 1091 | el.nodeName === 'EMU-FIGURE' || 1092 | el.nodeName === 'EMU-TABLE' || 1093 | el.nodeName === 'EMU-EXAMPLE' 1094 | ) { 1095 | // return the figcaption element 1096 | return { element: el.children[0].children[0], id: el.id }; 1097 | } else { 1098 | return { element: el, id: el.id }; 1099 | } 1100 | } 1101 | el = parent; 1102 | } 1103 | }, 1104 | 1105 | deactivate() { 1106 | this.$outer.classList.remove('active'); 1107 | this._activeEl = null; 1108 | this.active = false; 1109 | }, 1110 | }; 1111 | 1112 | function sortByClauseNumber(clause1, clause2) { 1113 | let c1c = clause1.number.split('.'); 1114 | let c2c = clause2.number.split('.'); 1115 | 1116 | for (let i = 0; i < c1c.length; i++) { 1117 | if (i >= c2c.length) { 1118 | return 1; 1119 | } 1120 | 1121 | let c1 = c1c[i]; 1122 | let c2 = c2c[i]; 1123 | let c1cn = Number(c1); 1124 | let c2cn = Number(c2); 1125 | 1126 | if (Number.isNaN(c1cn) && Number.isNaN(c2cn)) { 1127 | if (c1 > c2) { 1128 | return 1; 1129 | } else if (c1 < c2) { 1130 | return -1; 1131 | } 1132 | } else if (!Number.isNaN(c1cn) && Number.isNaN(c2cn)) { 1133 | return -1; 1134 | } else if (Number.isNaN(c1cn) && !Number.isNaN(c2cn)) { 1135 | return 1; 1136 | } else if (c1cn > c2cn) { 1137 | return 1; 1138 | } else if (c1cn < c2cn) { 1139 | return -1; 1140 | } 1141 | } 1142 | 1143 | if (c1c.length === c2c.length) { 1144 | return 0; 1145 | } 1146 | return -1; 1147 | } 1148 | 1149 | function makeLinkToId(id) { 1150 | let hash = '#' + id; 1151 | if (typeof idToSection === 'undefined' || !idToSection[id]) { 1152 | return hash; 1153 | } 1154 | let targetSec = idToSection[id]; 1155 | return (targetSec === 'index' ? './' : targetSec + '.html') + hash; 1156 | } 1157 | 1158 | function doShortcut(e) { 1159 | if (!(e.target instanceof HTMLElement)) { 1160 | return; 1161 | } 1162 | let target = e.target; 1163 | let name = target.nodeName.toLowerCase(); 1164 | if (name === 'textarea' || name === 'input' || name === 'select' || target.isContentEditable) { 1165 | return; 1166 | } 1167 | if (e.altKey || e.ctrlKey || e.metaKey) { 1168 | return; 1169 | } 1170 | if (e.key === 'm' && usesMultipage) { 1171 | let pathParts = location.pathname.split('/'); 1172 | let hash = location.hash; 1173 | if (pathParts[pathParts.length - 2] === 'multipage') { 1174 | if (hash === '') { 1175 | let sectionName = pathParts[pathParts.length - 1]; 1176 | if (sectionName.endsWith('.html')) { 1177 | sectionName = sectionName.slice(0, -5); 1178 | } 1179 | if (idToSection['sec-' + sectionName] !== undefined) { 1180 | hash = '#sec-' + sectionName; 1181 | } 1182 | } 1183 | location = pathParts.slice(0, -2).join('/') + '/' + hash; 1184 | } else { 1185 | location = 'multipage/' + hash; 1186 | } 1187 | } else if (e.key === 'u') { 1188 | document.documentElement.classList.toggle('show-ao-annotations'); 1189 | } else if (e.key === '?') { 1190 | document.getElementById('shortcuts-help').classList.toggle('active'); 1191 | } 1192 | } 1193 | 1194 | function init() { 1195 | if (document.getElementById('menu') == null) { 1196 | return; 1197 | } 1198 | menu = new Menu(); 1199 | let $container = document.getElementById('spec-container'); 1200 | $container.addEventListener( 1201 | 'mouseover', 1202 | debounce(e => { 1203 | Toolbox.activateIfMouseOver(e); 1204 | }), 1205 | ); 1206 | document.addEventListener( 1207 | 'keydown', 1208 | debounce(e => { 1209 | if (e.code === 'Escape') { 1210 | if (Toolbox.active) { 1211 | Toolbox.deactivate(); 1212 | } 1213 | document.getElementById('shortcuts-help').classList.remove('active'); 1214 | } 1215 | }), 1216 | ); 1217 | } 1218 | 1219 | document.addEventListener('keypress', doShortcut); 1220 | 1221 | document.addEventListener('DOMContentLoaded', () => { 1222 | Toolbox.init(); 1223 | referencePane.init(); 1224 | }); 1225 | 1226 | // preserve state during navigation 1227 | 1228 | function getTocPath(li) { 1229 | let path = []; 1230 | let pointer = li; 1231 | while (true) { 1232 | let parent = pointer.parentElement; 1233 | if (parent == null) { 1234 | return null; 1235 | } 1236 | let index = [].indexOf.call(parent.children, pointer); 1237 | if (index == -1) { 1238 | return null; 1239 | } 1240 | path.unshift(index); 1241 | pointer = parent.parentElement; 1242 | if (pointer == null) { 1243 | return null; 1244 | } 1245 | if (pointer.id === 'menu-toc') { 1246 | break; 1247 | } 1248 | if (pointer.tagName !== 'LI') { 1249 | return null; 1250 | } 1251 | } 1252 | return path; 1253 | } 1254 | 1255 | function activateTocPath(path) { 1256 | try { 1257 | let pointer = document.getElementById('menu-toc'); 1258 | for (let index of path) { 1259 | pointer = pointer.querySelector('ol').children[index]; 1260 | } 1261 | pointer.classList.add('active'); 1262 | } catch (e) { 1263 | // pass 1264 | } 1265 | } 1266 | 1267 | function getActiveTocPaths() { 1268 | return [...menu.$menu.querySelectorAll('.active')].map(getTocPath).filter(p => p != null); 1269 | } 1270 | 1271 | function initTOCExpansion(visibleItemLimit) { 1272 | // Initialize to a reasonable amount of TOC expansion: 1273 | // * Expand any full-breadth nesting level up to visibleItemLimit. 1274 | // * Expand any *single-item* level while under visibleItemLimit (even if that pushes over it). 1275 | 1276 | // Limit to initialization by bailing out if any parent item is already expanded. 1277 | const tocItems = Array.from(document.querySelectorAll('#menu-toc li')); 1278 | if (tocItems.some(li => li.classList.contains('active') && li.querySelector('li'))) { 1279 | return; 1280 | } 1281 | 1282 | const selfAndSiblings = maybe => Array.from(maybe?.parentNode.children ?? []); 1283 | let currentLevelItems = selfAndSiblings(tocItems[0]); 1284 | let availableCount = visibleItemLimit - currentLevelItems.length; 1285 | while (availableCount > 0 && currentLevelItems.length) { 1286 | const nextLevelItems = currentLevelItems.flatMap(li => selfAndSiblings(li.querySelector('li'))); 1287 | availableCount -= nextLevelItems.length; 1288 | if (availableCount > 0 || currentLevelItems.length === 1) { 1289 | // Expand parent items of the next level down (i.e., current-level items with children). 1290 | for (const ol of new Set(nextLevelItems.map(li => li.parentNode))) { 1291 | ol.closest('li').classList.add('active'); 1292 | } 1293 | } 1294 | currentLevelItems = nextLevelItems; 1295 | } 1296 | } 1297 | 1298 | function initState() { 1299 | if (typeof menu === 'undefined' || window.navigating) { 1300 | return; 1301 | } 1302 | const storage = typeof sessionStorage !== 'undefined' ? sessionStorage : Object.create(null); 1303 | if (storage.referencePaneState != null) { 1304 | let state = JSON.parse(storage.referencePaneState); 1305 | if (state != null) { 1306 | if (state.type === 'ref') { 1307 | let entry = menu.search.biblio.byId[state.id]; 1308 | if (entry != null) { 1309 | referencePane.showReferencesFor(entry); 1310 | } 1311 | } else if (state.type === 'sdo') { 1312 | let sdos = sdoMap[state.id]; 1313 | if (sdos != null) { 1314 | referencePane.$headerText.innerHTML = state.html; 1315 | referencePane.showSDOsBody(sdos, state.id); 1316 | } 1317 | } 1318 | delete storage.referencePaneState; 1319 | } 1320 | } 1321 | 1322 | if (storage.activeTocPaths != null) { 1323 | document.querySelectorAll('#menu-toc li.active').forEach(li => li.classList.remove('active')); 1324 | let active = JSON.parse(storage.activeTocPaths); 1325 | active.forEach(activateTocPath); 1326 | delete storage.activeTocPaths; 1327 | } else { 1328 | initTOCExpansion(20); 1329 | } 1330 | 1331 | if (storage.searchValue != null) { 1332 | let value = JSON.parse(storage.searchValue); 1333 | menu.search.$searchBox.value = value; 1334 | menu.search.search(value); 1335 | delete storage.searchValue; 1336 | } 1337 | 1338 | if (storage.tocScroll != null) { 1339 | let tocScroll = JSON.parse(storage.tocScroll); 1340 | menu.$toc.scrollTop = tocScroll; 1341 | delete storage.tocScroll; 1342 | } 1343 | } 1344 | 1345 | document.addEventListener('DOMContentLoaded', initState); 1346 | 1347 | window.addEventListener('pageshow', initState); 1348 | 1349 | window.addEventListener('beforeunload', () => { 1350 | if (!window.sessionStorage || typeof menu === 'undefined') { 1351 | return; 1352 | } 1353 | sessionStorage.referencePaneState = JSON.stringify(referencePane.state || null); 1354 | sessionStorage.activeTocPaths = JSON.stringify(getActiveTocPaths()); 1355 | sessionStorage.searchValue = JSON.stringify(menu.search.$searchBox.value); 1356 | sessionStorage.tocScroll = JSON.stringify(menu.$toc.scrollTop); 1357 | }); 1358 | 1359 | 'use strict'; 1360 | 1361 | // Manually prefix algorithm step list items with hidden counter representations 1362 | // corresponding with their markers so they get selected and copied with content. 1363 | // We read list-style-type to avoid divergence with the style sheet, but 1364 | // for efficiency assume that all lists at the same nesting depth use the same 1365 | // style (except for those associated with replacement steps). 1366 | // We also precompute some initial items for each supported style type. 1367 | // https://w3c.github.io/csswg-drafts/css-counter-styles/ 1368 | 1369 | const lowerLetters = Array.from({ length: 26 }, (_, i) => 1370 | String.fromCharCode('a'.charCodeAt(0) + i), 1371 | ); 1372 | // Implement the lower-alpha 'alphabetic' algorithm, 1373 | // adjusting for indexing from 0 rather than 1. 1374 | // https://w3c.github.io/csswg-drafts/css-counter-styles/#simple-alphabetic 1375 | // https://w3c.github.io/csswg-drafts/css-counter-styles/#alphabetic-system 1376 | const lowerAlphaTextForIndex = i => { 1377 | let S = ''; 1378 | for (const N = lowerLetters.length; i >= 0; i--) { 1379 | S = lowerLetters[i % N] + S; 1380 | i = Math.floor(i / N); 1381 | } 1382 | return S; 1383 | }; 1384 | 1385 | const weightedLowerRomanSymbols = Object.entries({ 1386 | m: 1000, 1387 | cm: 900, 1388 | d: 500, 1389 | cd: 400, 1390 | c: 100, 1391 | xc: 90, 1392 | l: 50, 1393 | xl: 40, 1394 | x: 10, 1395 | ix: 9, 1396 | v: 5, 1397 | iv: 4, 1398 | i: 1, 1399 | }); 1400 | // Implement the lower-roman 'additive' algorithm, 1401 | // adjusting for indexing from 0 rather than 1. 1402 | // https://w3c.github.io/csswg-drafts/css-counter-styles/#simple-numeric 1403 | // https://w3c.github.io/csswg-drafts/css-counter-styles/#additive-system 1404 | const lowerRomanTextForIndex = i => { 1405 | let value = i + 1; 1406 | let S = ''; 1407 | for (const [symbol, weight] of weightedLowerRomanSymbols) { 1408 | if (!value) break; 1409 | if (weight > value) continue; 1410 | const reps = Math.floor(value / weight); 1411 | S += symbol.repeat(reps); 1412 | value -= weight * reps; 1413 | } 1414 | return S; 1415 | }; 1416 | 1417 | // Memoize pure index-to-text functions with an exposed cache for fast retrieval. 1418 | const makeCounter = (pureGetTextForIndex, precomputeCount = 30) => { 1419 | const cache = Array.from({ length: precomputeCount }, (_, i) => pureGetTextForIndex(i)); 1420 | const getTextForIndex = i => { 1421 | if (i >= cache.length) cache[i] = pureGetTextForIndex(i); 1422 | return cache[i]; 1423 | }; 1424 | return { getTextForIndex, cache }; 1425 | }; 1426 | 1427 | const counterByStyle = { 1428 | __proto__: null, 1429 | decimal: makeCounter(i => String(i + 1)), 1430 | 'lower-alpha': makeCounter(lowerAlphaTextForIndex), 1431 | 'upper-alpha': makeCounter(i => lowerAlphaTextForIndex(i).toUpperCase()), 1432 | 'lower-roman': makeCounter(lowerRomanTextForIndex), 1433 | 'upper-roman': makeCounter(i => lowerRomanTextForIndex(i).toUpperCase()), 1434 | }; 1435 | const fallbackCounter = makeCounter(() => '?'); 1436 | const counterByDepth = []; 1437 | 1438 | function addStepNumberText( 1439 | ol, 1440 | depth = 0, 1441 | indent = '', 1442 | special = [...ol.classList].some(c => c.startsWith('nested-')), 1443 | ) { 1444 | let counter = !special && counterByDepth[depth]; 1445 | if (!counter) { 1446 | const counterStyle = getComputedStyle(ol)['list-style-type']; 1447 | counter = counterByStyle[counterStyle]; 1448 | if (!counter) { 1449 | console.warn('unsupported list-style-type', { 1450 | ol, 1451 | counterStyle, 1452 | id: ol.closest('[id]')?.getAttribute('id'), 1453 | }); 1454 | counterByStyle[counterStyle] = fallbackCounter; 1455 | counter = fallbackCounter; 1456 | } 1457 | if (!special) { 1458 | counterByDepth[depth] = counter; 1459 | } 1460 | } 1461 | const { cache, getTextForIndex } = counter; 1462 | let i = (Number(ol.getAttribute('start')) || 1) - 1; 1463 | for (const li of ol.children) { 1464 | const marker = document.createElement('span'); 1465 | const markerText = i < cache.length ? cache[i] : getTextForIndex(i); 1466 | const extraIndent = ' '.repeat(markerText.length + 2); 1467 | marker.textContent = `${indent}${markerText}. `; 1468 | marker.setAttribute('aria-hidden', 'true'); 1469 | marker.setAttribute('class', 'list-marker'); 1470 | const attributesContainer = li.querySelector('.attributes-tag'); 1471 | if (attributesContainer == null) { 1472 | li.prepend(marker); 1473 | } else { 1474 | attributesContainer.insertAdjacentElement('afterend', marker); 1475 | } 1476 | for (const sublist of li.querySelectorAll(':scope > ol')) { 1477 | addStepNumberText(sublist, depth + 1, indent + extraIndent, special); 1478 | } 1479 | i++; 1480 | } 1481 | } 1482 | 1483 | document.addEventListener('DOMContentLoaded', () => { 1484 | document.querySelectorAll('emu-alg > ol').forEach(ol => { 1485 | addStepNumberText(ol); 1486 | }); 1487 | }); 1488 | 1489 | // Omit indendation when copying a single algorithm step. 1490 | document.addEventListener('copy', evt => { 1491 | // Construct a DOM from the selection. 1492 | const doc = document.implementation.createHTMLDocument(''); 1493 | const domRoot = doc.createElement('div'); 1494 | const html = evt.clipboardData.getData('text/html'); 1495 | if (html) { 1496 | domRoot.innerHTML = html; 1497 | } else { 1498 | const selection = getSelection(); 1499 | const singleRange = selection?.rangeCount === 1 && selection.getRangeAt(0); 1500 | const container = singleRange?.commonAncestorContainer; 1501 | if (!container?.querySelector?.('.list-marker')) { 1502 | return; 1503 | } 1504 | domRoot.append(singleRange.cloneContents()); 1505 | } 1506 | 1507 | // Preserve the indentation if there is no hidden list marker, or if selection 1508 | // of more than one step is indicated by either multiple such markers or by 1509 | // visible text before the first one. 1510 | const listMarkers = domRoot.querySelectorAll('.list-marker'); 1511 | if (listMarkers.length !== 1) { 1512 | return; 1513 | } 1514 | const treeWalker = document.createTreeWalker(domRoot, undefined, { 1515 | acceptNode(node) { 1516 | return node.nodeType === Node.TEXT_NODE || node === listMarkers[0] 1517 | ? NodeFilter.FILTER_ACCEPT 1518 | : NodeFilter.FILTER_SKIP; 1519 | }, 1520 | }); 1521 | while (treeWalker.nextNode()) { 1522 | const node = treeWalker.currentNode; 1523 | if (node.nodeType === Node.ELEMENT_NODE) break; 1524 | if (/\S/u.test(node.data)) return; 1525 | } 1526 | 1527 | // Strip leading indentation from the plain text representation. 1528 | evt.clipboardData.setData('text/plain', domRoot.textContent.trimStart()); 1529 | if (!html) { 1530 | evt.clipboardData.setData('text/html', domRoot.innerHTML); 1531 | } 1532 | evt.preventDefault(); 1533 | }); 1534 | 1535 | 'use strict'; 1536 | 1537 | // Update superscripts to not suffer misinterpretation when copied and pasted as plain text. 1538 | // For example, 1539 | // * Replace `103` with 1540 | // `103` 1541 | // so it gets pasted as `10**3` rather than `103`. 1542 | // * Replace `10-x` with 1543 | // `10-x` 1544 | // so it gets pasted as `10**-x` rather than `10-x`. 1545 | // * Replace `2a + 1` with 1546 | // `2**(a + 1)` 1547 | // so it gets pasted as `2**(a + 1)` rather than `2a + 1`. 1548 | 1549 | function makeExponentPlainTextSafe(sup) { 1550 | // Change a only if it appears to be an exponent: 1551 | // * text-only and contains only mathematical content (not e.g. `1st`) 1552 | // * contains only s and internal links (e.g. 1553 | // `2(_y_)`) 1554 | const isText = [...sup.childNodes].every(node => node.nodeType === 3); 1555 | const text = sup.textContent; 1556 | if (isText) { 1557 | if (!/^[0-9. 𝔽ℝℤ()=*×/÷±+\u2212-]+$/u.test(text)) { 1558 | return; 1559 | } 1560 | } else { 1561 | if (sup.querySelector('*:not(var, emu-xref, :scope emu-xref a)')) { 1562 | return; 1563 | } 1564 | } 1565 | 1566 | let prefix = '**'; 1567 | let suffix = ''; 1568 | 1569 | // Add wrapping parentheses unless they are already present 1570 | // or this is a simple (possibly signed) integer or single-variable exponent. 1571 | const skipParens = 1572 | /^[±+\u2212-]?(?:[0-9]+|\p{ID_Start}\p{ID_Continue}*)$/u.test(text) || 1573 | // Split on parentheses and remember them; the resulting parts must 1574 | // start and end empty (i.e., with open/close parentheses) 1575 | // and increase depth to 1 only at the first parenthesis 1576 | // to e.g. wrap `(a+1)*(b+1)` but not `((a+1)*(b+1))`. 1577 | text 1578 | .trim() 1579 | .split(/([()])/g) 1580 | .reduce((depth, s, i, parts) => { 1581 | if (s === '(') { 1582 | return depth > 0 || i === 1 ? depth + 1 : NaN; 1583 | } else if (s === ')') { 1584 | return depth > 0 ? depth - 1 : NaN; 1585 | } else if (s === '' || (i > 0 && i < parts.length - 1)) { 1586 | return depth; 1587 | } 1588 | return NaN; 1589 | }, 0) === 0; 1590 | if (!skipParens) { 1591 | prefix += '('; 1592 | suffix += ')'; 1593 | } 1594 | 1595 | sup.insertAdjacentHTML('beforebegin', ``); 1596 | if (suffix) { 1597 | sup.insertAdjacentHTML('afterend', ``); 1598 | } 1599 | } 1600 | 1601 | document.addEventListener('DOMContentLoaded', () => { 1602 | document.querySelectorAll('sup:not(.text)').forEach(sup => { 1603 | makeExponentPlainTextSafe(sup); 1604 | }); 1605 | }); 1606 | 1607 | let sdoMap = JSON.parse(`{"prod-EdmoEhta":{"HasCallInTailPosition":{"clause":"15.10.2","ids":["prod-9oXzuZL6"]}},"prod-XSmG8KFu":{"HasCallInTailPosition":{"clause":"15.10.2","ids":["prod-UB1LtDnR"]}},"prod-jdvmig2z":{"HasCallInTailPosition":{"clause":"15.10.2","ids":["prod-BHK6aZq8"]}},"prod-zNwY0NcB":{"HasCallInTailPosition":{"clause":"15.10.2","ids":["prod-YG9CRILW"]},"MatchExpressionClausesEvaluation":{"clause":"30.2.3","ids":["prod-Rom5m7Ja"]}},"prod-ZOtaLsb5":{"HasCallInTailPosition":{"clause":"15.10.2","ids":["prod-O1ji-jbG"]},"MatchExpressionClausesEvaluation":{"clause":"30.2.3","ids":["prod-ndBX1W1V"]}},"prod-inqwxZRh":{"HasCallInTailPosition":{"clause":"15.10.2","ids":["prod-ac1fz2Ib"]},"MatchExpressionClausesEvaluation":{"clause":"30.2.3","ids":["prod--Nw1hDs1"]}},"prod-iGULIx-t":{"HasCallInTailPosition":{"clause":"15.10.2","ids":["prod-4UejTJZU"]},"MatchExpressionClausesEvaluation":{"clause":"30.2.3","ids":["prod-QDysejGw"]}},"prod-POLnbn8y":{"HasCallInTailPosition":{"clause":"15.10.2","ids":["prod-FYuYdt05"]},"MatchExpressionClauseEvaluation":{"clause":"30.2.4","ids":["prod-dgN-SNm3"]}},"prod-gb-onOzg":{"IsOptionalPattern":{"clause":"30.1.2","ids":["prod-yCjtcQ2s"]},"ListPatternInnerMatches":{"clause":"30.1.11","ids":["prod-xREui9vq"]}},"prod--q9atpfX":{"IsOptionalPattern":{"clause":"30.1.2","ids":["prod-uWaCy0rP"]},"ListPatternInnerMatches":{"clause":"30.1.11","ids":["prod-OcHtMyto"]}},"prod-SwXPxa2u":{"IsOptionalPattern":{"clause":"30.1.2","ids":["prod-NV8BPc1p"]},"ListPatternInnerMatches":{"clause":"30.1.11","ids":["prod-3oXeLOQS"]}},"prod-lKjcdi_o":{"IsOptionalPattern":{"clause":"30.1.2","ids":["prod-Eavr6r2E"]},"ListPatternInnerMatches":{"clause":"30.1.11","ids":["prod-wWtr3ozj"]}},"prod-KUcUUgsn":{"MatchPatternMatches":{"clause":"30.1.3","ids":["prod-U80C8biY"]}},"prod-NnfvhTBM":{"MatchPatternMatches":{"clause":"30.1.3","ids":["prod-6fPS70C2"]}},"prod--5M7srSm":{"MatchPatternMatches":{"clause":"30.1.3","ids":["prod-8y3jNblH"]}},"prod-dtu4srcz":{"MatchPatternMatches":{"clause":"30.1.3","ids":["prod-vBMoWeeV"]}},"prod-ZdrTiyJE":{"MatchPatternMatches":{"clause":"30.1.3","ids":["prod-mpb2HCVB"]}},"prod-l2SLZUMT":{"MatchPatternMatches":{"clause":"30.1.3","ids":["prod-QXRatsJR"]}},"prod-en0pW04U":{"MatchPatternMatches":{"clause":"30.1.3","ids":["prod-MW-Egi0C"]}},"prod-LetWbF4Q":{"MatchPatternMatches":{"clause":"30.1.3","ids":["prod-E8S1P1H4"]}},"prod-GC1hibKU":{"MatchPatternMatches":{"clause":"30.1.3","ids":["prod-rvZDZ_Jw"]}},"prod-LAj8DpMp":{"MatchPatternMatches":{"clause":"30.1.3","ids":["prod-EquXXU0V"]}},"prod-lZG2aTTb":{"PrimitivePatternMatches":{"clause":"30.1.4","ids":["prod-mlLTmHeP"]}},"prod-rqU7_eLx":{"PrimitivePatternMatches":{"clause":"30.1.4","ids":["prod-eFLzsKUd"]}},"prod-u_-PVDpb":{"VariableDeclarationPatternMatches":{"clause":"30.1.5","ids":["prod-Otz3gD-Y"]}},"prod-G4gXeSPg":{"MemberExpressionPatternMatches":{"clause":"30.1.6","ids":["prod--tiCo4Kg"]}},"prod-QmuUz1ZR":{"MemberExpressionPatternMatches":{"clause":"30.1.6","ids":["prod-UC7vB794"]}},"prod-nriCsL_r":{"ObjectPatternMatches":{"clause":"30.1.7","ids":["prod-ijKqIu5y"]}},"prod-EWVgVnxN":{"ObjectPatternMatches":{"clause":"30.1.7","ids":["prod-rsN4rFAw"]}},"prod-UnQjidH-":{"ObjectPatternMatches":{"clause":"30.1.7","ids":["prod-t1nKdCY9"]}},"prod-xpU3Tqeh":{"ObjectPatternMatches":{"clause":"30.1.7","ids":["prod-yj96DPDb"]}},"prod-NpqrpNeK":{"ObjectPatternInnerMatches":{"clause":"30.1.8","ids":["prod-CVegFgkL"]}},"prod-kRoJE3bP":{"ObjectPatternInnerMatches":{"clause":"30.1.8","ids":["prod-T4vISnOq"]}},"prod-_cQam8pS":{"ObjectPatternInnerMatches":{"clause":"30.1.8","ids":["prod-qAB6O-_U"]}},"prod-VxHnNNub":{"ObjectPatternInnerMatches":{"clause":"30.1.8","ids":["prod-wn8ysC7M"]}},"prod-GMqEmzsz":{"ObjectPatternInnerMatches":{"clause":"30.1.8","ids":["prod-K_6p11YI"]}},"prod-GO0bgB3_":{"ObjectPatternInnerMatches":{"clause":"30.1.8","ids":["prod-67mQe-Kl"]}},"prod-xrsY-24G":{"ObjectPatternInnerMatches":{"clause":"30.1.8","ids":["prod-dFlCv79J"]}},"prod-mfwF-eVh":{"ArrayPatternMatches":{"clause":"30.1.9","ids":["prod-PyMOIQ3Y"]}},"prod-xnwBdNr0":{"ListPatternMatches":{"clause":"30.1.10","ids":["prod-jo48_6Fj"]}},"prod-5aJhmP6v":{"ListPatternMatches":{"clause":"30.1.10","ids":["prod-ZH2p23SX"]}},"prod-Qinb_JH_":{"ListPatternMatches":{"clause":"30.1.10","ids":["prod-TB4qCm-Y"]}},"prod-fOlW2Nsd":{"ListPatternInnerMatches":{"clause":"30.1.11","ids":["prod-iIwhAXB2"]}},"prod-w_IWHbxt":{"ListPatternInnerMatches":{"clause":"30.1.11","ids":["prod-QkDn-2nd"]}},"prod-blOttJBG":{"UnaryAlgebraicPatternMatches":{"clause":"30.1.12","ids":["prod-HOehzcwY"]}},"prod-Ee4jkDah":{"RelationalPatternMatches":{"clause":"30.1.13","ids":["prod-HIh36vwy"]}},"prod-Lt1ufiOJ":{"RelationalPatternMatches":{"clause":"30.1.13","ids":["prod-7OFxsw1H"]}},"prod-MbwVHJm5":{"RelationalPatternMatches":{"clause":"30.1.13","ids":["prod-06obvqDG"]}},"prod-LIsJEGG_":{"RelationalPatternMatches":{"clause":"30.1.13","ids":["prod-RHBOL0Pm"]}},"prod-sQz7HIlw":{"RelationalPatternMatches":{"clause":"30.1.13","ids":["prod-yy5yPHTl"]}},"prod-wD-UQ48A":{"RelationalPatternMatches":{"clause":"30.1.13","ids":["prod-4m0pop5D"]}},"prod-LdC39dQb":{"RelationalPatternMatches":{"clause":"30.1.13","ids":["prod--dRddGUV"]}},"prod-WzM35PsW":{"RelationalPatternMatches":{"clause":"30.1.13","ids":["prod-w4_WjGgR"]}},"prod-ofL39yLB":{"RelationalPatternMatches":{"clause":"30.1.13","ids":["prod-7Ze364jc"]}},"prod-atFGrhH0":{"RelationalPatternMatches":{"clause":"30.1.13","ids":["prod-jvHPj17C"]}},"prod-F0nSIfMB":{"IfPatternMatches":{"clause":"30.1.14","ids":["prod-OcKu_40M"]}},"prod-a_2cW9fV":{"CombinedMatchPatternMatches":{"clause":"30.1.15","ids":["prod-TGrK8XcZ"]}},"prod-BMX8uZDG":{"CombinedMatchPatternMatches":{"clause":"30.1.15","ids":["prod-hYdo8hkU"]}},"prod-4IhLPseH":{"CombinedMatchPatternMatches":{"clause":"30.1.15","ids":["prod-_QtiVeJ1"]}}}`); 1608 | let biblio = JSON.parse(`{"refsByClause":{"sec-nav":["_ref_0","_ref_1","_ref_2","_ref_3","_ref_4","_ref_5","_ref_6","_ref_7","_ref_8","_ref_9","_ref_10","_ref_11","_ref_12","_ref_19"],"sec-organization-of-this-specification":["_ref_13"],"sec-primary-expression-match-expression":["_ref_14","_ref_199"],"sec-relational-operators":["_ref_15","_ref_200","_ref_201"],"sec-match-patterns":["_ref_16","_ref_221","_ref_222","_ref_223","_ref_224","_ref_225","_ref_226","_ref_227","_ref_228","_ref_229","_ref_230","_ref_231","_ref_232","_ref_233","_ref_234","_ref_235","_ref_236","_ref_237","_ref_238","_ref_239","_ref_240","_ref_241","_ref_242","_ref_243","_ref_244","_ref_245","_ref_246","_ref_247","_ref_248","_ref_249","_ref_250","_ref_251","_ref_252","_ref_253","_ref_254","_ref_255","_ref_256","_ref_257","_ref_258","_ref_259","_ref_260","_ref_261","_ref_262","_ref_263","_ref_264","_ref_265","_ref_266","_ref_267","_ref_268","_ref_269","_ref_270","_ref_271","_ref_272","_ref_273","_ref_274","_ref_275","_ref_276","_ref_277","_ref_278","_ref_279","_ref_280","_ref_281","_ref_282","_ref_283","_ref_284","_ref_285","_ref_286","_ref_287","_ref_288","_ref_289","_ref_290","_ref_291","_ref_292","_ref_293","_ref_294","_ref_295","_ref_296","_ref_297","_ref_298","_ref_299"],"sec-match-expression":["_ref_17","_ref_447","_ref_448","_ref_449","_ref_450","_ref_451","_ref_452","_ref_453","_ref_454"],"sec-invoke-custom-matcher":["_ref_18","_ref_161","_ref_162","_ref_163","_ref_164","_ref_165"],"sec-object-internal-methods-and-internal-slots":["_ref_20"],"sec-runtime-semantics-bindinginitialization":["_ref_21"],"sec-relational-operators-runtime-semantics-evaluation":["_ref_22","_ref_23","_ref_24","_ref_202","_ref_203","_ref_204","_ref_205"],"sec-static-semantics-hascallintailposition":["_ref_25","_ref_26","_ref_27","_ref_28","_ref_29","_ref_30","_ref_31","_ref_32","_ref_33","_ref_206","_ref_207","_ref_208","_ref_209","_ref_210","_ref_211","_ref_212","_ref_213","_ref_214","_ref_215","_ref_216","_ref_217","_ref_218","_ref_219","_ref_220"],"sec-object-%symbol.custommatcher%":["_ref_34"],"sec-function-%symbol.custommatcher%":["_ref_35"],"sec-function.prototype-%symbol.custommatcher%":["_ref_36"],"sec-boolean-%symbol.custommatcher%":["_ref_37"],"sec-symbol.custommatcher":["_ref_38"],"sec-symbol-%symbol.custommatcher%":["_ref_39","_ref_40","_ref_41"],"sec-error-%symbol.custommatcher%":["_ref_42"],"sec-properties-of-error-instances":["_ref_43"],"sec-nativeerror-%symbol.custommatcher%":["_ref_44"],"sec-properties-of-nativeerror-instances":["_ref_45"],"sec-aggregate-error-%symbol.custommatcher%":["_ref_46"],"sec-properties-of-aggregate-error-instances":["_ref_47"],"sec-number-%symbol.custommatcher%":["_ref_48"],"sec-bigint-%symbol.custommatcher%":["_ref_49"],"sec-date-%symbol.custommatcher%":["_ref_50"],"sec-string-%symbol.custommatcher%":["_ref_51"],"sec-regexp-%symbol.custommatcher%":["_ref_52","_ref_53"],"sec-regexp.prototype-%symbol.custommatcher%":["_ref_54","_ref_55","_ref_56"],"sec-array-%symbol.custommatcher%":["_ref_57"],"sec-_typedarray_-%symbol.custommatcher%":["_ref_58"],"sec-map-%symbol.custommatcher%":["_ref_59"],"sec-set-%symbol.custommatcher%":["_ref_60"],"sec-weakmap-%symbol.custommatcher%":["_ref_61"],"sec-weakset-%symbol.custommatcher%":["_ref_62"],"sec-arraybuffer-%symbol.custommatcher%":["_ref_63"],"sec-sharedarraybuffer-%symbol.custommatcher%":["_ref_64"],"sec-dataview-%symbol.custommatcher%":["_ref_65"],"sec-weakref-%symbol.custommatcher%":["_ref_66"],"sec-finalizationregistry-%symbol.custommatcher%":["_ref_67"],"sec-promise-%symbol.custommatcher%":["_ref_68"],"sec-proxy-constructor":["_ref_69"],"sec-match-patterns-static-semantics-early-errors":["_ref_70","_ref_71","_ref_72","_ref_300","_ref_301","_ref_302","_ref_303","_ref_304","_ref_305","_ref_306","_ref_307","_ref_308","_ref_309","_ref_310","_ref_311","_ref_312","_ref_313","_ref_314","_ref_315","_ref_316","_ref_317","_ref_318","_ref_319","_ref_320","_ref_321","_ref_322","_ref_323","_ref_324","_ref_325"],"sec-is-optional-pattern":["_ref_73","_ref_74","_ref_75","_ref_76","_ref_326","_ref_327","_ref_328","_ref_329","_ref_330","_ref_331","_ref_332","_ref_333","_ref_334"],"sec-match-pattern-matches":["_ref_77","_ref_78","_ref_79","_ref_80","_ref_81","_ref_82","_ref_83","_ref_84","_ref_85","_ref_86","_ref_87","_ref_335","_ref_336","_ref_337","_ref_338","_ref_339","_ref_340","_ref_341","_ref_342","_ref_343","_ref_344","_ref_345","_ref_346","_ref_347","_ref_348","_ref_349","_ref_350","_ref_351","_ref_352","_ref_353","_ref_354"],"sec-primitive-pattern-matches":["_ref_88"],"sec-variable-declaration-pattern-matches":["_ref_89","_ref_355","_ref_356"],"sec-member-expression-pattern-matches":["_ref_90","_ref_91","_ref_92","_ref_93","_ref_94","_ref_357","_ref_358","_ref_359","_ref_360","_ref_361","_ref_362","_ref_363"],"sec-object-pattern-matches":["_ref_95","_ref_96","_ref_97","_ref_98","_ref_99","_ref_364","_ref_365","_ref_366","_ref_367","_ref_368","_ref_369","_ref_370","_ref_371","_ref_372"],"sec-object-pattern-inner-matches":["_ref_100","_ref_101","_ref_102","_ref_103","_ref_104","_ref_105","_ref_106","_ref_107","_ref_108","_ref_109","_ref_110","_ref_111","_ref_112","_ref_373","_ref_374","_ref_375","_ref_376","_ref_377","_ref_378","_ref_379","_ref_380","_ref_381","_ref_382","_ref_383","_ref_384","_ref_385","_ref_386","_ref_387","_ref_388"],"sec-array-pattern-matches":["_ref_113","_ref_114","_ref_115","_ref_116","_ref_117","_ref_118","_ref_389","_ref_390","_ref_391"],"sec-list-pattern-matches":["_ref_119","_ref_120","_ref_121","_ref_122","_ref_123","_ref_124","_ref_125","_ref_126","_ref_127","_ref_128","_ref_129","_ref_392","_ref_393","_ref_394","_ref_395","_ref_396","_ref_397","_ref_398","_ref_399","_ref_400","_ref_401"],"sec-list-pattern-inner-matches":["_ref_130","_ref_131","_ref_132","_ref_133","_ref_134","_ref_135","_ref_136","_ref_137","_ref_138","_ref_402","_ref_403","_ref_404","_ref_405","_ref_406","_ref_407","_ref_408","_ref_409","_ref_410","_ref_411","_ref_412","_ref_413"],"sec-unary-algebraic-pattern-matches":["_ref_139","_ref_414","_ref_415","_ref_416"],"sec-relational-pattern-matches":["_ref_140","_ref_417","_ref_418","_ref_419","_ref_420","_ref_421","_ref_422","_ref_423","_ref_424","_ref_425","_ref_426","_ref_427","_ref_428","_ref_429","_ref_430","_ref_431","_ref_432","_ref_433","_ref_434","_ref_435","_ref_436"],"sec-combined-match-pattern-matches":["_ref_141","_ref_142","_ref_143","_ref_144","_ref_145","_ref_146","_ref_437","_ref_438","_ref_439","_ref_440","_ref_441","_ref_442","_ref_443","_ref_444","_ref_445","_ref_446"],"sec-match-expression-runtime-semantics-evaluation":["_ref_147","_ref_148","_ref_149","_ref_457","_ref_458","_ref_459"],"sec-match-expression-clauses-runtime-semantics-evaluation":["_ref_150","_ref_151","_ref_152","_ref_153","_ref_154","_ref_155","_ref_156","_ref_157","_ref_460","_ref_461","_ref_462","_ref_463","_ref_464","_ref_465","_ref_466","_ref_467"],"sec-match-expression-clause-runtime-semantics-evaluation":["_ref_158","_ref_159","_ref_160","_ref_468","_ref_469"],"sec-validatecustommatcherhint":["_ref_166"],"sec-get-match-cache":["_ref_167","_ref_168","_ref_169"],"sec-has-property-cached":["_ref_170","_ref_171","_ref_172"],"sec-get-cached":["_ref_173","_ref_174","_ref_175","_ref_176"],"sec-get-iterator-cached":["_ref_177","_ref_178","_ref_179","_ref_180","_ref_181","_ref_182"],"sec-iterator-step-cached":["_ref_183","_ref_184","_ref_185"],"sec-get-iterator-nth-value-cached":["_ref_186","_ref_187","_ref_188","_ref_189","_ref_190","_ref_470"],"sec-finish-list-match":["_ref_191","_ref_192","_ref_471"],"sec-finish-match":["_ref_193"],"sec-rules-of-automatic-semicolon-insertion":["_ref_194","_ref_195","_ref_196"],"sec-no-lineterminator-here-automatic-semicolon-insertion-list":["_ref_197"],"sec-primary-expression":["_ref_198"],"sec-match-expression-static-semantics-early-errors":["_ref_455","_ref_456"]},"entries":[{"type":"clause","id":"sec-todos","titleHTML":"TODOs","number":""},{"type":"clause","id":"sec-nav","titleHTML":"Introduction","number":""},{"type":"clause","id":"sec-notes-layering","titleHTML":"Layering","number":""},{"type":"clause","id":"welcome","titleHTML":"Welcome","number":""},{"type":"clause","id":"sec-organization-of-this-specification","titleHTML":"Organization of This Specification","number":"4.5"},{"type":"clause","id":"sec-overview","titleHTML":"Overview","number":"4"},{"type":"table","id":"table-1","number":1,"caption":"Table 1: Well-known Symbols"},{"type":"term","term":"%Symbol.customMatcher%","refId":"sec-well-known-symbols"},{"type":"clause","id":"sec-well-known-symbols","titleHTML":"Well-Known Symbols","number":"6.1.5.1","referencingIds":["_ref_19","_ref_38","_ref_43","_ref_45","_ref_47","_ref_53","_ref_55","_ref_56","_ref_69","_ref_115","_ref_164","_ref_181"]},{"type":"clause","id":"sec-ecmascript-language-types-symbol-type","titleHTML":"The Symbol Type","number":"6.1.5","referencingIds":["_ref_40","_ref_41"]},{"type":"clause","id":"sec-object-internal-methods-and-internal-slots","titleHTML":"Object Internal Methods and Internal Slots","number":"6.1.7"},{"type":"clause","id":"sec-ecmascript-language-types","titleHTML":"ECMAScript Language Types","number":"6.1","referencingIds":["_ref_20","_ref_21","_ref_77","_ref_88","_ref_89","_ref_90","_ref_95","_ref_113","_ref_139","_ref_140","_ref_141","_ref_150","_ref_151","_ref_154","_ref_157","_ref_158","_ref_159","_ref_161","_ref_162","_ref_163","_ref_166","_ref_167","_ref_168","_ref_170","_ref_173","_ref_174","_ref_177","_ref_182","_ref_183","_ref_186"]},{"type":"op","aoid":"Type","refId":"sec-ecmascript-data-types-and-values"},{"type":"clause","id":"sec-ecmascript-data-types-and-values","titleHTML":"ECMAScript Data Types and Values","number":"6"},{"type":"term","term":"InitializeInstance","refId":"sec-initializeinstance"},{"type":"op","aoid":"InitializeInstanceElements","refId":"sec-initializeinstance"},{"type":"clause","id":"sec-initializeinstance","title":"InitializeInstanceElements ( O, constructor )","titleHTML":"InitializeInstanceElements ( O, constructor )","number":"7.3.34","referencingIds":["_ref_8"]},{"type":"clause","id":"sec-operations-on-objects","titleHTML":"Operations on Objects","number":"7.3"},{"type":"clause","id":"sec-abstract-operations","titleHTML":"Abstract Operations","number":"7"},{"type":"op","aoid":"BoundNames","refId":"sec-static-semantics-boundnames"},{"type":"clause","id":"sec-static-semantics-boundnames","titleHTML":"Static Semantics: BoundNames","number":"8.2.1"},{"type":"op","aoid":"DeclarationPart","refId":"sec-static-semantics-declarationpart"},{"type":"clause","id":"sec-static-semantics-declarationpart","titleHTML":"Static Semantics: DeclarationPart","number":"8.2.2"},{"type":"op","aoid":"IsConstantDeclaration","refId":"sec-static-semantics-isconstantdeclaration"},{"type":"clause","id":"sec-static-semantics-isconstantdeclaration","titleHTML":"Static Semantics: IsConstantDeclaration","number":"8.2.3"},{"type":"op","aoid":"LexicallyDeclaredNames","refId":"sec-static-semantics-lexicallydeclarednames"},{"type":"clause","id":"sec-static-semantics-lexicallydeclarednames","titleHTML":"Static Semantics: LexicallyDeclaredNames","number":"8.2.4"},{"type":"op","aoid":"LexicallyScopedDeclarations","refId":"sec-static-semantics-lexicallyscopeddeclarations"},{"type":"clause","id":"sec-static-semantics-lexicallyscopeddeclarations","titleHTML":"Static Semantics: LexicallyScopedDeclarations","number":"8.2.5"},{"type":"op","aoid":"VarDeclaredNames","refId":"sec-static-semantics-vardeclarednames"},{"type":"clause","id":"sec-static-semantics-vardeclarednames","titleHTML":"Static Semantics: VarDeclaredNames","number":"8.2.6"},{"type":"op","aoid":"VarScopedDeclarations","refId":"sec-static-semantics-varscopeddeclarations"},{"type":"clause","id":"sec-static-semantics-varscopeddeclarations","titleHTML":"Static Semantics: VarScopedDeclarations","number":"8.2.7"},{"type":"op","aoid":"TopLevelLexicallyDeclaredNames","refId":"sec-static-semantics-toplevellexicallydeclarednames"},{"type":"clause","id":"sec-static-semantics-toplevellexicallydeclarednames","titleHTML":"Static Semantics: TopLevelLexicallyDeclaredNames","number":"8.2.8"},{"type":"op","aoid":"TopLevelLexicallyScopedDeclarations","refId":"sec-static-semantics-toplevellexicallyscopeddeclarations"},{"type":"clause","id":"sec-static-semantics-toplevellexicallyscopeddeclarations","titleHTML":"Static Semantics: TopLevelLexicallyScopedDeclarations","number":"8.2.9"},{"type":"op","aoid":"TopLevelVarDeclaredNames","refId":"sec-static-semantics-toplevelvardeclarednames"},{"type":"clause","id":"sec-static-semantics-toplevelvardeclarednames","titleHTML":"Static Semantics: TopLevelVarDeclaredNames","number":"8.2.10"},{"type":"op","aoid":"TopLevelVarScopedDeclarations","refId":"sec-static-semantics-toplevelvarscopeddeclarations"},{"type":"clause","id":"sec-static-semantics-toplevelvarscopeddeclarations","titleHTML":"Static Semantics: TopLevelVarScopedDeclarations","number":"8.2.11"},{"type":"clause","id":"sec-syntax-directed-operations-scope-analysis","titleHTML":"Scope Analysis","number":"8.2"},{"type":"op","aoid":"BindingInitialization","refId":"sec-runtime-semantics-bindinginitialization"},{"type":"clause","id":"sec-runtime-semantics-bindinginitialization","titleHTML":"Runtime Semantics: BindingInitialization","number":"8.6.2"},{"type":"op","aoid":"IteratorBindingInitialization","refId":"sec-runtime-semantics-iteratorbindinginitialization"},{"type":"clause","id":"sec-runtime-semantics-iteratorbindinginitialization","titleHTML":"Runtime Semantics: IteratorBindingInitialization","number":"8.6.3"},{"type":"clause","id":"sec-syntax-directed-operations-miscellaneous","titleHTML":"Miscellaneous","number":"8.6"},{"type":"clause","id":"sec-syntax-directed-operations","titleHTML":"Syntax-Directed Operations","number":"8","referencingIds":["_ref_7"]},{"type":"clause","id":"sec-weakly-hold-execution","titleHTML":"Execution","number":"9.10.3"},{"type":"clause","id":"sec-weakly-hold-targets-processing-model","title":"Processing Model of WeakRef and FinalizationRegistryweakly hold Targets","titleHTML":"Processing Model of WeakRef and FinalizationRegistryweakly hold Targets","number":"9.10","referencingIds":["_ref_9"]},{"type":"clause","id":"sec-executable-code-and-execution-contexts","titleHTML":"Executable Code and Execution Contexts","number":"9"},{"type":"clause","id":"sec-rules-of-automatic-semicolon-insertion","titleHTML":"Rules of Automatic Semicolon Insertion","number":"12.10.1"},{"type":"clause","id":"sec-no-lineterminator-here-automatic-semicolon-insertion-list","title":"List of Grammar Productions with Optional Operands and “[no LineTerminator here]”","titleHTML":"List of Grammar Productions with Optional Operands and “[no LineTerminator here]”","number":"12.10.3.2.1"},{"type":"clause","id":"sec-asi-cases-with-no-lineterminator-here","title":"Cases of Automatic Semicolon Insertion and “[no LineTerminator here]”","titleHTML":"Cases of Automatic Semicolon Insertion and “[no LineTerminator here]”","number":"12.10.3.2"},{"type":"clause","id":"sec-interesting-cases-of-automatic-semicolon-insertion","titleHTML":"Interesting Cases of Automatic Semicolon Insertion","number":"12.10.3"},{"type":"clause","id":"sec-automatic-semicolon-insertion","titleHTML":"Automatic Semicolon Insertion","number":"12.10"},{"type":"clause","id":"sec-ecmascript-language-lexical-grammar","titleHTML":"ECMAScript Language: Lexical Grammar","number":"12"},{"type":"production","id":"prod-PrimaryExpression","name":"PrimaryExpression"},{"type":"clause","id":"sec-primary-expression-match-expression","titleHTML":"Match Expression","number":"13.2.10"},{"type":"clause","id":"sec-primary-expression","titleHTML":"Primary Expression","number":"13.2"},{"type":"production","id":"prod-RelationalExpression","name":"RelationalExpression","referencingIds":["_ref_194","_ref_200","_ref_202","_ref_204","_ref_206"]},{"type":"clause","id":"sec-relational-operators-runtime-semantics-evaluation","titleHTML":"Runtime Semantics: Evaluation","number":"13.10.1"},{"type":"clause","id":"sec-relational-operators","titleHTML":"Relational Operators","number":"13.10","referencingIds":["_ref_3","_ref_17"]},{"type":"clause","id":"sec-ecmascript-language-expressions","titleHTML":"ECMAScript Language: Expressions","number":"13"},{"type":"clause","id":"sec-for-in-and-for-of-statements","title":"The for-in, for-of, and for-await-of Statements","titleHTML":"The for-in, for-of, and for-await-of Statements","number":"14.7.1","referencingIds":["_ref_11"]},{"type":"clause","id":"sec-iteration-statements","titleHTML":"Iteration Statements","number":"14.7"},{"type":"clause","id":"sec-try-statement","title":"The try Statement","titleHTML":"The try Statement","number":"14.14","referencingIds":["_ref_12"]},{"type":"clause","id":"sec-ecmascript-language-statements-and-declarations","titleHTML":"ECMAScript Language: Statements and Declarations","number":"14"},{"type":"op","aoid":"ClassDefinitionEvaluation","refId":"sec-runtime-semantics-classdefinitionevaluation"},{"type":"clause","id":"sec-runtime-semantics-classdefinitionevaluation","titleHTML":"Runtime Semantics: ClassDefinitionEvaluation","number":"15.7.14","referencingIds":["_ref_10"]},{"type":"clause","id":"sec-class-definitions","titleHTML":"Class Definitions","number":"15.7"},{"type":"op","aoid":"HasCallInTailPosition","refId":"sec-static-semantics-hascallintailposition"},{"type":"clause","id":"sec-static-semantics-hascallintailposition","titleHTML":"Static Semantics: HasCallInTailPosition","number":"15.10.2","referencingIds":["_ref_25","_ref_26","_ref_27","_ref_28","_ref_29","_ref_30","_ref_31","_ref_32","_ref_33"]},{"type":"clause","id":"sec-tail-position-calls","titleHTML":"Tail Position Calls","number":"15.10"},{"type":"clause","id":"sec-ecmascript-language-functions-and-classes","titleHTML":"ECMAScript Language: Functions and Classes","number":"15"},{"type":"clause","id":"sec-object-%symbol.custommatcher%","title":"Object [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"Object [ %Symbol.customMatcher% ] ( subject, hint )","number":"20.1.2.24"},{"type":"clause","id":"sec-properties-of-the-object-constructor","titleHTML":"Properties of the Object Constructor","number":"20.1.2"},{"type":"clause","id":"sec-object-objects","titleHTML":"Object Objects","number":"20.1"},{"type":"clause","id":"sec-function-%symbol.custommatcher%","title":"Function [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"Function [ %Symbol.customMatcher% ] ( subject, hint )","number":"20.2.2.2"},{"type":"clause","id":"sec-properties-of-the-function-constructor","titleHTML":"Properties of the Function Constructor","number":"20.2.2"},{"type":"clause","id":"sec-function.prototype-%symbol.custommatcher%","title":"Function.prototype [ %Symbol.customMatcher% ] ( subject, hint, receiver )","titleHTML":"Function.prototype [ %Symbol.customMatcher% ] ( subject, hint, receiver )","number":"20.2.3.7","referencingIds":["_ref_5"]},{"type":"clause","id":"sec-properties-of-the-function-prototype-object","titleHTML":"Properties of the Function Prototype Object","number":"20.2.3"},{"type":"clause","id":"sec-function-objects","titleHTML":"Function Objects","number":"20.2"},{"type":"clause","id":"sec-boolean-%symbol.custommatcher%","title":"Boolean [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"Boolean [ %Symbol.customMatcher% ] ( subject, hint )","number":"20.3.2.2"},{"type":"clause","id":"sec-properties-of-the-boolean-constructor","titleHTML":"Properties of the Boolean Constructor","number":"20.3.2"},{"type":"clause","id":"sec-boolean-objects","titleHTML":"Boolean Objects","number":"20.3"},{"type":"clause","id":"sec-symbol.custommatcher","titleHTML":"Symbol.customMatcher","number":"20.4.2.17"},{"type":"clause","id":"sec-symbol-%symbol.custommatcher%","title":"Symbol [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"Symbol [ %Symbol.customMatcher% ] ( subject, hint )","number":"20.4.2.18"},{"type":"clause","id":"sec-properties-of-the-symbol-constructor","titleHTML":"Properties of the Symbol Constructor","number":"20.4.2"},{"type":"clause","id":"sec-symbol-objects","titleHTML":"Symbol Objects","number":"20.4"},{"type":"clause","id":"sec-error-message","title":"Error ( message [ , options ] )","titleHTML":"Error ( message [ , options ] )","number":"20.5.1.1"},{"type":"clause","id":"sec-error-constructor","titleHTML":"The Error Constructor","number":"20.5.1"},{"type":"clause","id":"sec-error-%symbol.custommatcher%","title":"Error [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"Error [ %Symbol.customMatcher% ] ( subject, hint )","number":"20.5.2.2"},{"type":"clause","id":"sec-properties-of-the-error-constructors","titleHTML":"Properties of the Error Constructor","number":"20.5.2"},{"type":"clause","id":"sec-properties-of-error-instances","titleHTML":"Properties of Error Instances","number":"20.5.4"},{"type":"clause","id":"sec-nativeerror","title":"NativeError ( message [ , options ] )","titleHTML":"NativeError ( message [ , options ] )","number":"20.5.6.1.1"},{"type":"clause","id":"sec-nativeerror-constructors","title":"The NativeError Constructors","titleHTML":"The NativeError Constructors","number":"20.5.6.1"},{"type":"clause","id":"sec-nativeerror-%symbol.custommatcher%","title":"NativeError [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"NativeError [ %Symbol.customMatcher% ] ( subject, hint )","number":"20.5.6.2.2"},{"type":"clause","id":"sec-properties-of-the-nativeerror-constructors","title":"Properties of the NativeError Constructors","titleHTML":"Properties of the NativeError Constructors","number":"20.5.6.2"},{"type":"clause","id":"sec-properties-of-nativeerror-instances","title":"Properties of NativeError Instances","titleHTML":"Properties of NativeError Instances","number":"20.5.6.4"},{"type":"clause","id":"sec-nativeerror-object-structure","title":"NativeError Object Structure","titleHTML":"NativeError Object Structure","number":"20.5.6"},{"type":"clause","id":"sec-aggregate-error","title":"AggregateError ( errors, message [ , options ] )","titleHTML":"AggregateError ( errors, message [ , options ] )","number":"20.5.7.1.1"},{"type":"clause","id":"sec-aggregate-error-constructor","titleHTML":"The AggregateError Constructor","number":"20.5.7.1"},{"type":"clause","id":"sec-aggregate-error-%symbol.custommatcher%","title":"AggregateError [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"AggregateError [ %Symbol.customMatcher% ] ( subject, hint )","number":"20.5.7.2.2"},{"type":"clause","id":"sec-properties-of-the-aggregate-error-constructors","titleHTML":"Properties of the AggregateError Constructor","number":"20.5.7.2"},{"type":"clause","id":"sec-properties-of-aggregate-error-instances","titleHTML":"Properties of AggregateError Instances","number":"20.5.7.4"},{"type":"clause","id":"sec-aggregate-error-objects","titleHTML":"AggregateError Objects","number":"20.5.7"},{"type":"clause","id":"sec-error-objects","titleHTML":"Error Objects","number":"20.5"},{"type":"clause","id":"sec-fundamental-objects","titleHTML":"Fundamental Objects","number":"20"},{"type":"clause","id":"sec-number-%symbol.custommatcher%","title":"Number [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"Number [ %Symbol.customMatcher% ] ( subject, hint )","number":"21.1.2.16"},{"type":"clause","id":"sec-properties-of-the-number-constructor","titleHTML":"Properties of the Number Constructor","number":"21.1.2"},{"type":"clause","id":"sec-number-objects","titleHTML":"Number Objects","number":"21.1"},{"type":"clause","id":"sec-bigint-%symbol.custommatcher%","title":"BigInt [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"BigInt [ %Symbol.customMatcher% ] ( subject, hint )","number":"21.2.2.4"},{"type":"clause","id":"sec-properties-of-the-bigint-constructor","titleHTML":"Properties of the BigInt Constructor","number":"21.2.2"},{"type":"clause","id":"sec-bigint-objects","titleHTML":"BigInt Objects","number":"21.2"},{"type":"clause","id":"sec-date-%symbol.custommatcher%","title":"Date [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"Date [ %Symbol.customMatcher% ] ( subject, hint )","number":"21.4.3.5"},{"type":"clause","id":"sec-properties-of-the-date-constructor","titleHTML":"Properties of the Date Constructor","number":"21.4.3"},{"type":"clause","id":"sec-date-objects","titleHTML":"Date Objects","number":"21.4"},{"type":"clause","id":"sec-numbers-and-dates","titleHTML":"Numbers and Dates","number":"21"},{"type":"clause","id":"sec-string-%symbol.custommatcher%","title":"String [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"String [ %Symbol.customMatcher% ] ( subject, hint )","number":"22.1.2.5","referencingIds":["_ref_16"]},{"type":"clause","id":"sec-properties-of-the-string-constructor","titleHTML":"Properties of the String Constructor","number":"22.1.2"},{"type":"clause","id":"sec-string-objects","titleHTML":"String Objects","number":"22.1"},{"type":"clause","id":"sec-regexp-%symbol.custommatcher%","title":"RegExp [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"RegExp [ %Symbol.customMatcher% ] ( subject, hint )","number":"22.2.5.3"},{"type":"clause","id":"sec-properties-of-the-regexp-constructor","titleHTML":"Properties of the RegExp Constructor","number":"22.2.5"},{"type":"clause","id":"sec-regexp.prototype-%symbol.custommatcher%","title":"RegExp.prototype [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"RegExp.prototype [ %Symbol.customMatcher% ] ( subject, hint )","number":"22.2.6.20","referencingIds":["_ref_6"]},{"type":"clause","id":"sec-properties-of-the-regexp-prototype-object","titleHTML":"Properties of the RegExp Prototype Object","number":"22.2.6"},{"type":"clause","id":"sec-regexp-regular-expression-objects","titleHTML":"RegExp (Regular Expression) Objects","number":"22.2"},{"type":"clause","id":"sec-text-processing","titleHTML":"Text Processing","number":"22"},{"type":"clause","id":"sec-array-%symbol.custommatcher%","title":"Array [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"Array [ %Symbol.customMatcher% ] ( subject, hint )","number":"23.1.2.6"},{"type":"clause","id":"sec-properties-of-the-array-constructor","titleHTML":"Properties of the Array Constructor","number":"23.1.2"},{"type":"clause","id":"sec-array-objects","titleHTML":"Array Objects","number":"23.1"},{"type":"clause","id":"sec-_typedarray_-%symbol.custommatcher%","title":"TypedArray [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"TypedArray [ %Symbol.customMatcher% ] ( subject, hint )","number":"23.2.6.3"},{"type":"clause","id":"sec-properties-of-the-typedarray-constructors","title":"Properties of the TypedArray Constructors","titleHTML":"Properties of the TypedArray Constructors","number":"23.2.6"},{"type":"clause","id":"sec-typedarray-objects","titleHTML":"TypedArray Objects","number":"23.2"},{"type":"clause","id":"sec-indexed-collections","titleHTML":"Indexed Collections","number":"23"},{"type":"clause","id":"sec-map-%symbol.custommatcher%","title":"Map [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"Map [ %Symbol.customMatcher% ] ( subject, hint )","number":"24.1.2.3"},{"type":"clause","id":"sec-properties-of-the-map-constructor","titleHTML":"Properties of the Map Constructor","number":"24.1.2"},{"type":"clause","id":"sec-map-objects","titleHTML":"Map Objects","number":"24.1"},{"type":"clause","id":"sec-set-%symbol.custommatcher%","title":"Set [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"Set [ %Symbol.customMatcher% ] ( subject, hint )","number":"24.2.2.3"},{"type":"clause","id":"sec-properties-of-the-set-constructor","titleHTML":"Properties of the Set Constructor","number":"24.2.2"},{"type":"clause","id":"sec-set-objects","titleHTML":"Set Objects","number":"24.2"},{"type":"clause","id":"sec-weakmap-%symbol.custommatcher%","title":"WeakMap [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"WeakMap [ %Symbol.customMatcher% ] ( subject, hint )","number":"24.3.2.2"},{"type":"clause","id":"sec-properties-of-the-weakmap-constructor","titleHTML":"Properties of the WeakMap Constructor","number":"24.3.2"},{"type":"clause","id":"sec-weakmap-objects","titleHTML":"WeakMap Objects","number":"24.3"},{"type":"clause","id":"sec-weakset-%symbol.custommatcher%","title":"WeakSet [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"WeakSet [ %Symbol.customMatcher% ] ( subject, hint )","number":"24.4.2.2"},{"type":"clause","id":"sec-properties-of-the-weakset-constructor","titleHTML":"Properties of the WeakSet Constructor","number":"24.4.2"},{"type":"clause","id":"sec-weakset-objects","titleHTML":"WeakSet Objects","number":"24.4"},{"type":"clause","id":"sec-keyed-collections","titleHTML":"Keyed Collections","number":"24"},{"type":"clause","id":"sec-arraybuffer-%symbol.custommatcher%","title":"ArrayBuffer [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"ArrayBuffer [ %Symbol.customMatcher% ] ( subject, hint )","number":"25.1.5.4"},{"type":"clause","id":"sec-properties-of-the-arraybuffer-constructor","titleHTML":"Properties of the ArrayBuffer Constructor","number":"25.1.5"},{"type":"clause","id":"sec-arraybuffer-objects","titleHTML":"ArrayBuffer Objects","number":"25.1"},{"type":"clause","id":"sec-sharedarraybuffer-%symbol.custommatcher%","title":"SharedArrayBuffer [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"SharedArrayBuffer [ %Symbol.customMatcher% ] ( subject, hint )","number":"25.2.4.3"},{"type":"clause","id":"sec-properties-of-the-sharedarraybuffer-constructor","titleHTML":"Properties of the SharedArrayBuffer Constructor","number":"25.2.4"},{"type":"clause","id":"sec-sharedarraybuffer-objects","titleHTML":"SharedArrayBuffer Objects","number":"25.2"},{"type":"clause","id":"sec-dataview-%symbol.custommatcher%","title":"DataView [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"DataView [ %Symbol.customMatcher% ] ( subject, hint )","number":"25.3.3.2"},{"type":"clause","id":"sec-properties-of-the-dataview-constructor","titleHTML":"Properties of the DataView Constructor","number":"25.3.3"},{"type":"clause","id":"sec-dataview-objects","titleHTML":"DataView Objects","number":"25.3"},{"type":"clause","id":"sec-structured-data","titleHTML":"Structured Data","number":"25"},{"type":"clause","id":"sec-weakref-%symbol.custommatcher%","title":"WeakRef [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"WeakRef [ %Symbol.customMatcher% ] ( subject, hint )","number":"26.1.2.2"},{"type":"clause","id":"sec-properties-of-the-weak-ref-constructor","titleHTML":"Properties of the WeakRef Constructor","number":"26.1.2"},{"type":"clause","id":"sec-weak-ref-objects","titleHTML":"WeakRef Objects","number":"26.1"},{"type":"clause","id":"sec-finalizationregistry-%symbol.custommatcher%","title":"FinalizationRegistry [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"FinalizationRegistry [ %Symbol.customMatcher% ] ( subject, hint )","number":"26.2.2.2"},{"type":"clause","id":"sec-properties-of-the-finalization-registry-constructor","titleHTML":"Properties of the FinalizationRegistry Constructor","number":"26.2.2"},{"type":"clause","id":"sec-finalization-registry-objects","titleHTML":"FinalizationRegistry Objects","number":"26.2"},{"type":"clause","id":"sec-managing-memory","titleHTML":"Managing Memory","number":"26"},{"type":"clause","id":"sec-promise-%symbol.custommatcher%","title":"Promise [ %Symbol.customMatcher% ] ( subject, hint )","titleHTML":"Promise [ %Symbol.customMatcher% ] ( subject, hint )","number":"27.2.4.9"},{"type":"clause","id":"sec-properties-of-the-promise-constructor","titleHTML":"Properties of the Promise Constructor","number":"27.2.4"},{"type":"clause","id":"sec-promise-objects","titleHTML":"Promise Objects","number":"27.2"},{"type":"clause","id":"sec-control-abstraction-objects","titleHTML":"Control Abstraction Objects","number":"27"},{"type":"clause","id":"sec-proxy-%symbol.custommatcher%","titleHTML":"Proxy [ %Symbol.customMatcher% ] ( )","number":"28.2.1.2"},{"type":"clause","id":"sec-proxy-constructor","titleHTML":"The Proxy Constructor","number":"28.2.1"},{"type":"clause","id":"sec-proxy-objects","titleHTML":"Proxy Objects","number":"28.2"},{"type":"clause","id":"sec-reflection","titleHTML":"Reflection","number":"28"},{"type":"production","id":"prod-MatchPattern","name":"MatchPattern","referencingIds":["_ref_195","_ref_201","_ref_203","_ref_205","_ref_207","_ref_220","_ref_221","_ref_251","_ref_259","_ref_262","_ref_263","_ref_264","_ref_271","_ref_295","_ref_296","_ref_297","_ref_298","_ref_299","_ref_302","_ref_303","_ref_304","_ref_305","_ref_307","_ref_315","_ref_316","_ref_317","_ref_319","_ref_320","_ref_321","_ref_323","_ref_324","_ref_334","_ref_335","_ref_336","_ref_373","_ref_374","_ref_381","_ref_382","_ref_386","_ref_387","_ref_410","_ref_411","_ref_412","_ref_413","_ref_437","_ref_438","_ref_439","_ref_440","_ref_441","_ref_442","_ref_443","_ref_444","_ref_445","_ref_446","_ref_452","_ref_468","_ref_469"]},{"type":"production","id":"prod-PrimitivePattern","name":"PrimitivePattern","referencingIds":["_ref_222","_ref_337","_ref_338"]},{"type":"production","id":"prod-VariableDeclarationPattern","name":"VariableDeclarationPattern","referencingIds":["_ref_223","_ref_339","_ref_340"]},{"type":"production","id":"prod-VarOrLetOrConst","name":"VarOrLetOrConst","referencingIds":["_ref_231","_ref_260","_ref_261","_ref_270","_ref_272","_ref_300","_ref_301","_ref_355","_ref_356","_ref_383","_ref_384","_ref_385","_ref_388"]},{"type":"production","id":"prod-MemberExpressionPattern","name":"MemberExpressionPattern","referencingIds":["_ref_224","_ref_306","_ref_341","_ref_342"]},{"type":"production","id":"prod-PatternMatchingMemberExpression","name":"PatternMatchingMemberExpression","referencingIds":["_ref_232","_ref_233","_ref_235","_ref_236","_ref_237","_ref_238","_ref_239","_ref_240","_ref_241","_ref_274","_ref_275","_ref_280","_ref_281","_ref_286","_ref_292","_ref_357","_ref_358","_ref_359","_ref_361","_ref_425","_ref_426","_ref_427","_ref_428"]},{"type":"production","id":"prod-ObjectPattern","name":"ObjectPattern","referencingIds":["_ref_225","_ref_343","_ref_344"]},{"type":"production","id":"prod-ArrayPattern","name":"ArrayPattern","referencingIds":["_ref_226","_ref_345","_ref_346"]},{"type":"production","id":"prod-MatchList","name":"MatchList","referencingIds":["_ref_234","_ref_246","_ref_360","_ref_362","_ref_363","_ref_389","_ref_390","_ref_391","_ref_470","_ref_471"]},{"type":"production","id":"prod-MatchRestProperty","name":"MatchRestProperty","referencingIds":["_ref_242","_ref_245","_ref_364","_ref_365","_ref_369","_ref_371","_ref_372"]},{"type":"production","id":"prod-MatchPropertyList","name":"MatchPropertyList","referencingIds":["_ref_243","_ref_244","_ref_253","_ref_366","_ref_367","_ref_368","_ref_370","_ref_377","_ref_380"]},{"type":"production","id":"prod-MatchElementList","name":"MatchElementList","referencingIds":["_ref_248","_ref_249","_ref_256","_ref_308","_ref_310","_ref_311","_ref_313","_ref_328","_ref_330","_ref_395","_ref_396","_ref_397","_ref_399","_ref_404","_ref_406"]},{"type":"production","id":"prod-MatchElisionElement","name":"MatchElisionElement","referencingIds":["_ref_255","_ref_257","_ref_312","_ref_314","_ref_326","_ref_327","_ref_329","_ref_331","_ref_402","_ref_403","_ref_405","_ref_407"]},{"type":"production","id":"prod-MatchProperty","name":"MatchProperty","referencingIds":["_ref_252","_ref_254","_ref_265","_ref_266","_ref_268","_ref_375","_ref_376","_ref_378","_ref_379"]},{"type":"production","id":"prod-MatchElement","name":"MatchElement","referencingIds":["_ref_258","_ref_267","_ref_269","_ref_332","_ref_333","_ref_408","_ref_409"]},{"type":"production","id":"prod-MatchRestElement","name":"MatchRestElement","referencingIds":["_ref_247","_ref_250","_ref_309","_ref_392","_ref_393","_ref_394","_ref_398","_ref_400","_ref_401"]},{"type":"production","id":"prod-UnaryAlgebraicPattern","name":"UnaryAlgebraicPattern","referencingIds":["_ref_227","_ref_347","_ref_348"]},{"type":"production","id":"prod-PatternMatchingUnaryAlgebraicExpression","name":"PatternMatchingUnaryAlgebraicExpression","referencingIds":["_ref_273","_ref_287","_ref_414","_ref_415","_ref_416"]},{"type":"production","id":"prod-RelationalPattern","name":"RelationalPattern","referencingIds":["_ref_228","_ref_349","_ref_350"]},{"type":"production","id":"prod-PatternMatchingRelationalExpression","name":"PatternMatchingRelationalExpression","referencingIds":["_ref_276","_ref_277","_ref_278","_ref_279","_ref_282","_ref_283","_ref_284","_ref_285","_ref_294","_ref_417","_ref_418","_ref_419","_ref_420","_ref_421","_ref_422","_ref_423","_ref_424","_ref_429","_ref_430","_ref_431","_ref_432","_ref_433","_ref_434","_ref_435","_ref_436"]},{"type":"production","id":"prod-PatternMatchingStringLikeExpression","name":"PatternMatchingStringLikeExpression","referencingIds":["_ref_288","_ref_289","_ref_290","_ref_291","_ref_293"]},{"type":"production","id":"prod-IfPattern","name":"IfPattern","referencingIds":["_ref_229","_ref_351","_ref_352"]},{"type":"production","id":"prod-CombinedMatchPattern","name":"CombinedMatchPattern","referencingIds":["_ref_230","_ref_318","_ref_322","_ref_325","_ref_353","_ref_354"]},{"type":"clause","id":"sec-match-patterns-static-semantics-early-errors","titleHTML":"Static Semantics: Early Errors","number":"30.1.1"},{"type":"op","aoid":"IsOptionalPattern","refId":"sec-is-optional-pattern"},{"type":"clause","id":"sec-is-optional-pattern","titleHTML":"Static Semantics: IsOptionalPattern","number":"30.1.2","referencingIds":["_ref_70","_ref_71","_ref_72","_ref_73","_ref_74","_ref_75","_ref_76"]},{"type":"op","aoid":"MatchPatternMatches","refId":"sec-match-pattern-matches"},{"type":"clause","id":"sec-match-pattern-matches","titleHTML":"Runtime Semantics: MatchPatternMatches","number":"30.1.3","referencingIds":["_ref_23","_ref_78","_ref_100","_ref_107","_ref_112","_ref_136","_ref_138","_ref_142","_ref_143","_ref_144","_ref_145","_ref_146","_ref_160"]},{"type":"op","aoid":"PrimitivePatternMatches","refId":"sec-primitive-pattern-matches"},{"type":"clause","id":"sec-primitive-pattern-matches","titleHTML":"Runtime Semantics: PrimitivePatternMatches","number":"30.1.4","referencingIds":["_ref_79"]},{"type":"op","aoid":"VariableDeclarationPatternMatches","refId":"sec-variable-declaration-pattern-matches"},{"type":"clause","id":"sec-variable-declaration-pattern-matches","titleHTML":"Runtime Semantics: VariableDeclarationPatternMatches","number":"30.1.5","referencingIds":["_ref_80"]},{"type":"op","aoid":"MemberExpressionPatternMatches","refId":"sec-member-expression-pattern-matches"},{"type":"clause","id":"sec-member-expression-pattern-matches","titleHTML":"Runtime Semantics: MemberExpressionPatternMatches","number":"30.1.6","referencingIds":["_ref_81"]},{"type":"op","aoid":"ObjectPatternMatches","refId":"sec-object-pattern-matches"},{"type":"clause","id":"sec-object-pattern-matches","titleHTML":"Runtime Semantics: ObjectPatternMatches","number":"30.1.7","referencingIds":["_ref_82"]},{"type":"op","aoid":"ObjectPatternInnerMatches","refId":"sec-object-pattern-inner-matches"},{"type":"clause","id":"sec-object-pattern-inner-matches","titleHTML":"Runtime Semantics: ObjectPatternInnerMatches","number":"30.1.8","referencingIds":["_ref_96","_ref_97","_ref_98","_ref_99","_ref_101","_ref_102","_ref_103"]},{"type":"op","aoid":"ArrayPatternMatches","refId":"sec-array-pattern-matches"},{"type":"clause","id":"sec-array-pattern-matches","titleHTML":"Runtime Semantics: ArrayPatternMatches","number":"30.1.9","referencingIds":["_ref_83"]},{"type":"op","aoid":"ListPatternMatches","refId":"sec-list-pattern-matches"},{"type":"clause","id":"sec-list-pattern-matches","titleHTML":"Runtime Semantics: ListPatternMatches","number":"30.1.10","referencingIds":["_ref_94","_ref_118"]},{"type":"op","aoid":"ListPatternInnerMatches","refId":"sec-list-pattern-inner-matches"},{"type":"clause","id":"sec-list-pattern-inner-matches","titleHTML":"Runtime Semantics: ListPatternInnerMatches","number":"30.1.11","referencingIds":["_ref_120","_ref_123","_ref_125","_ref_127","_ref_130","_ref_131","_ref_132","_ref_134"]},{"type":"op","aoid":"UnaryAlgebraicPatternMatches","refId":"sec-unary-algebraic-pattern-matches"},{"type":"clause","id":"sec-unary-algebraic-pattern-matches","titleHTML":"Runtime Semantics: UnaryAlgebraicPatternMatches","number":"30.1.12","referencingIds":["_ref_84"]},{"type":"op","aoid":"RelationalPatternMatches","refId":"sec-relational-pattern-matches"},{"type":"clause","id":"sec-relational-pattern-matches","titleHTML":"Runtime Semantics: RelationalPatternMatches","number":"30.1.13","referencingIds":["_ref_85"]},{"type":"op","aoid":"IfPatternMatches","refId":"sec-if-pattern-matches"},{"type":"clause","id":"sec-if-pattern-matches","titleHTML":"Runtime Semantics: IfPatternMatches","number":"30.1.14","referencingIds":["_ref_86"]},{"type":"op","aoid":"CombinedMatchPatternMatches","refId":"sec-combined-match-pattern-matches"},{"type":"clause","id":"sec-combined-match-pattern-matches","titleHTML":"Runtime Semantics: CombinedMatchPatternMatches","number":"30.1.15","referencingIds":["_ref_87"]},{"type":"clause","id":"sec-match-patterns","titleHTML":"Match Patterns","number":"30.1"},{"type":"production","id":"prod-MatchExpression","name":"MatchExpression","referencingIds":["_ref_197","_ref_198","_ref_199","_ref_208","_ref_209","_ref_454"]},{"type":"production","id":"prod-MatchExpressionClauses","name":"MatchExpressionClauses","referencingIds":["_ref_196","_ref_210","_ref_211","_ref_214","_ref_216","_ref_218","_ref_219","_ref_447","_ref_449","_ref_451","_ref_453","_ref_455","_ref_457","_ref_459","_ref_462","_ref_464","_ref_466","_ref_467"]},{"type":"production","id":"prod-MatchExpressionClause","name":"MatchExpressionClause","referencingIds":["_ref_212","_ref_213","_ref_215","_ref_217","_ref_448","_ref_450","_ref_460","_ref_461","_ref_463","_ref_465"]},{"type":"production","id":"prod-MatchHead","name":"MatchHead","referencingIds":["_ref_456","_ref_458"]},{"type":"clause","id":"sec-match-expression-static-semantics-early-errors","titleHTML":"Static Semantics: Early Errors","number":"30.2.1"},{"type":"clause","id":"sec-match-expression-runtime-semantics-evaluation","titleHTML":"Runtime Semantics: Evaluation","number":"30.2.2"},{"type":"op","aoid":"MatchExpressionClausesEvaluation","refId":"sec-match-expression-clauses-runtime-semantics-evaluation"},{"type":"clause","id":"sec-match-expression-clauses-runtime-semantics-evaluation","titleHTML":"Runtime Semantics: MatchExpressionClausesEvaluation","number":"30.2.3","referencingIds":["_ref_148","_ref_153","_ref_156"]},{"type":"op","aoid":"MatchExpressionClauseEvaluation","refId":"sec-match-expression-clause-runtime-semantics-evaluation"},{"type":"clause","id":"sec-match-expression-clause-runtime-semantics-evaluation","titleHTML":"Runtime Semantics: MatchExpressionClauseEvaluation","number":"30.2.4","referencingIds":["_ref_152","_ref_155"]},{"type":"clause","id":"sec-match-expression","title":"The match Expression","titleHTML":"The match Expression","number":"30.2","referencingIds":["_ref_4","_ref_14","_ref_15"]},{"type":"step","id":"step-invoke-custom-matcher","referencingIds":["_ref_18"]},{"type":"op","aoid":"InvokeCustomMatcher","refId":"sec-invoke-custom-matcher"},{"type":"clause","id":"sec-invoke-custom-matcher","title":"InvokeCustomMatcher ( matcher, subject, cacheGroup, kind, receiver )","titleHTML":"InvokeCustomMatcher ( matcher, subject, cacheGroup, kind, receiver )","number":"30.3.1","referencingIds":["_ref_1","_ref_91","_ref_92"]},{"type":"op","aoid":"ValidateCustomMatcherHint","refId":"sec-validatecustommatcherhint"},{"type":"clause","id":"sec-validatecustommatcherhint","title":"ValidateCustomMatcherHint ( hint [ , kind ] )","titleHTML":"ValidateCustomMatcherHint ( hint [ , kind ] )","number":"30.3.2","referencingIds":["_ref_34","_ref_35","_ref_36","_ref_37","_ref_39","_ref_42","_ref_44","_ref_46","_ref_48","_ref_49","_ref_50","_ref_51","_ref_52","_ref_54","_ref_57","_ref_58","_ref_59","_ref_60","_ref_61","_ref_62","_ref_63","_ref_64","_ref_65","_ref_66","_ref_67","_ref_68"]},{"type":"op","aoid":"CreateMatchCache","refId":"sec-creatematchcache"},{"type":"clause","id":"sec-creatematchcache","titleHTML":"CreateMatchCache ( )","number":"30.3.3","referencingIds":["_ref_22","_ref_147","_ref_169","_ref_171","_ref_175","_ref_178","_ref_184","_ref_187","_ref_193"]},{"type":"op","aoid":"GetMatchCache","refId":"sec-get-match-cache"},{"type":"clause","id":"sec-get-match-cache","title":"GetMatchCache ( subject, cacheGroup )","titleHTML":"GetMatchCache ( subject, cacheGroup )","number":"30.3.4","referencingIds":["_ref_172","_ref_176","_ref_179","_ref_185","_ref_188","_ref_191"]},{"type":"op","aoid":"HasPropertyCached","refId":"sec-has-property-cached"},{"type":"clause","id":"sec-has-property-cached","title":"HasPropertyCached ( subject, cacheGroup, propertyName )","titleHTML":"HasPropertyCached ( subject, cacheGroup, propertyName )","number":"30.3.5","referencingIds":["_ref_104","_ref_105","_ref_108","_ref_110"]},{"type":"op","aoid":"GetCached","refId":"sec-get-cached"},{"type":"clause","id":"sec-get-cached","title":"GetCached ( subject, cacheGroup, propertyName )","titleHTML":"GetCached ( subject, cacheGroup, propertyName )","number":"30.3.6","referencingIds":["_ref_106","_ref_109","_ref_111","_ref_114","_ref_180"]},{"type":"op","aoid":"GetIteratorCached","refId":"sec-get-iterator-cached"},{"type":"clause","id":"sec-get-iterator-cached","title":"GetIteratorCached ( subject, cacheGroup )","titleHTML":"GetIteratorCached ( subject, cacheGroup )","number":"30.3.7","referencingIds":["_ref_116","_ref_165"]},{"type":"op","aoid":"IteratorStepCached","refId":"sec-iterator-step-cached"},{"type":"clause","id":"sec-iterator-step-cached","title":"IteratorStepCached ( iterator, cacheGroup )","titleHTML":"IteratorStepCached ( iterator, cacheGroup )","number":"30.3.8","referencingIds":["_ref_189","_ref_190"]},{"type":"op","aoid":"GetIteratorNthValueCached","refId":"sec-get-iterator-nth-value-cached"},{"type":"clause","id":"sec-get-iterator-nth-value-cached","title":"GetIteratorNthValueCached ( iterator, cacheGroup, n )","titleHTML":"GetIteratorNthValueCached ( iterator, cacheGroup, n )","number":"30.3.9","referencingIds":["_ref_119","_ref_126","_ref_133","_ref_135","_ref_137","_ref_192"]},{"type":"op","aoid":"FinishListMatch","refId":"sec-finish-list-match"},{"type":"clause","id":"sec-finish-list-match","title":"FinishListMatch ( iterator, cacheGroup, expectedLength )","titleHTML":"FinishListMatch ( iterator, cacheGroup, expectedLength )","number":"30.3.10","referencingIds":["_ref_93","_ref_117","_ref_121","_ref_122","_ref_124","_ref_128","_ref_129"]},{"type":"op","aoid":"FinishMatch","refId":"sec-finish-match"},{"type":"clause","id":"sec-finish-match","title":"FinishMatch ( matchCompletion, cacheGroup )","titleHTML":"FinishMatch ( matchCompletion, cacheGroup )","number":"30.3.11","referencingIds":["_ref_24","_ref_149"]},{"type":"note","id":"sec-pattern-match-cache-note","number":1,"referencingIds":["_ref_2"]},{"type":"clause","id":"sec-abstract-operations-for-pattern-matching","titleHTML":"Abstract Operations for Pattern Matching","number":"30.3"},{"type":"clause","id":"sec-pattern-matching","title":"Pattern Matching","titleHTML":"Pattern Matching","number":"30","referencingIds":["_ref_0","_ref_13"]},{"type":"clause","id":"sec-expressions","titleHTML":"Expressions","number":"A.1"},{"type":"clause","id":"sec-annex-match-patterns","titleHTML":"Patterns","number":"A.9"},{"type":"clause","id":"sec-grammar-summary","titleHTML":"Grammar Summary","number":"A"},{"type":"clause","id":"sec-copyright-and-software-license","title":"Copyright & Software License","titleHTML":"Copyright & Software License","number":"B"}]}`); 1609 | ;let usesMultipage = false -------------------------------------------------------------------------------- /assets/expand.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | __folded: true, 3 | get folded() { 4 | try { 5 | const v = localStorage.getItem("folded"); 6 | return v === "true" || v === null; 7 | } catch { 8 | return this.__folded; 9 | } 10 | }, 11 | set folded(val) { 12 | this.__folded = val; 13 | try { 14 | localStorage.setItem("folded", val); 15 | } catch {} 16 | document.body.classList.toggle("folded", val); 17 | }, 18 | }; 19 | state.folded = state.folded; 20 | 21 | const button = document.querySelector("#expand"); 22 | button.addEventListener("click", () => { 23 | state.folded = !state.folded; 24 | if (state.folded) { 25 | const old = scrollY; 26 | location.hash = ""; 27 | scrollTo(0, old); 28 | } 29 | }); 30 | 31 | function onHashChange(event) { 32 | const target = document.getElementById(location.hash.slice(1)); 33 | if (!target) return; 34 | if (!state.folded) return; 35 | for (const el of document.querySelectorAll(".fold")) { 36 | if (el.contains(target)) { 37 | state.folded = false; 38 | target.scrollIntoView(true); 39 | setExpand(false); 40 | } 41 | } 42 | } 43 | window.addEventListener("hashchange", onHashChange); 44 | window.addEventListener("DOMContentLoaded", onHashChange); 45 | -------------------------------------------------------------------------------- /assets/print.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, Helvetica, sans-serif; 3 | font-size: 10pt; 4 | background: #fff; 5 | color: #000; 6 | } 7 | 8 | .title { 9 | font-family: Verdana; 10 | } 11 | 12 | p { 13 | text-align: justify; 14 | text-rendering: optimizeLegibility; 15 | text-wrap: pretty; 16 | overflow-wrap: break-word; 17 | hyphens: auto; 18 | orphans: 2; 19 | widows: 2; 20 | } 21 | 22 | h1 { 23 | text-wrap: balance; 24 | line-height: 1.4; 25 | } 26 | 27 | emu-note p, 28 | emu-table td p { 29 | text-align: left; 30 | hyphens: manual; 31 | overflow: hidden; 32 | } 33 | 34 | emu-table td, 35 | emu-table th { 36 | overflow-wrap: break-word; 37 | } 38 | 39 | emu-table table { 40 | table-layout: auto; 41 | width: 100%; 42 | } 43 | 44 | emu-figure img { 45 | max-width: 100%; 46 | height: auto; 47 | } 48 | 49 | #spec-container { 50 | max-width: none; 51 | } 52 | 53 | #toc a, 54 | #toc var { 55 | color: #000; 56 | } 57 | 58 | #toc a[href] { 59 | background: #fff; 60 | padding-right: 0.5em; 61 | } 62 | #toc a[href]::after { 63 | content: /* leader(dotted) */ target-counter(attr(href), page); 64 | float: right; 65 | padding-left: 0.5em; 66 | background: #fff; 67 | } 68 | /* NOTE: hacks because Paged.js doesn't support leader() in content directives */ 69 | #toc ol { 70 | overflow-x: hidden; 71 | } 72 | #toc ol li:before { 73 | float: left; 74 | width: 0; 75 | white-space: nowrap; 76 | content: '. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ' 77 | '. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ' 78 | '. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .'; 79 | } 80 | 81 | /* skip the Introduction since it's before the first emu-clause (and therefore doesn't have a proper page number) */ 82 | #toc > ol > li:first-child { 83 | display: none; 84 | } 85 | 86 | #toc > ol > li { 87 | margin-top: 1ex; 88 | } 89 | 90 | #toc, 91 | #spec-container > emu-intro, 92 | #spec-container > emu-annex { 93 | break-before: recto; 94 | break-after: always; 95 | } 96 | 97 | /* according to Ecma guidelines, we're actually not supposed to break before every clause (only the first), but Paged.js fails if we do that */ 98 | /* so instead, just break before any of the clauses that have sub-clauses */ 99 | #spec-container > emu-clause:has(emu-clause:not([example])) { 100 | break-before: always; 101 | } 102 | 103 | #spec-container > emu-clause:first-of-type { 104 | break-before: recto; 105 | } 106 | 107 | emu-note, 108 | emu-note p, 109 | emu-table tr, 110 | emu-table th, 111 | emu-table td, 112 | emu-alg li, 113 | pre, 114 | h1, 115 | #metadata-block { 116 | break-inside: avoid; 117 | } 118 | 119 | emu-table thead, 120 | h1, 121 | figcaption, 122 | emu-alg > ol > li:first-child { 123 | break-after: avoid; 124 | } 125 | 126 | emu-grammar + emu-alg, 127 | figcaption + emu-table, 128 | figcaption + span[id] + emu-table, 129 | emu-alg > ol > li:last-child { 130 | break-before: avoid; 131 | } 132 | 133 | a[data-print-href]::after { 134 | content: ' <' attr(href) '>'; 135 | color: initial; 136 | } 137 | 138 | emu-table thead { 139 | display: table-header-group; 140 | } 141 | emu-table tfoot { 142 | display: table-footer-group; 143 | } 144 | 145 | @page { 146 | size: A4; 147 | } 148 | 149 | @page { 150 | @top-center { 151 | content: url(./ecma-header.png); 152 | } 153 | } 154 | @page :first { 155 | @top-center { 156 | content: none; 157 | } 158 | } 159 | 160 | :root { 161 | --page-number-style: decimal; 162 | } 163 | 164 | #toc { 165 | page: toc; 166 | } 167 | @page toc { 168 | --page-number-style: lower-roman; 169 | } 170 | emu-intro { 171 | page: intro; 172 | } 173 | @page intro { 174 | --page-number-style: lower-roman; 175 | } 176 | 177 | #toc { 178 | counter-reset: page 1; 179 | } 180 | #spec-container > emu-clause:first-of-type { 181 | counter-reset: page 1; 182 | } 183 | 184 | @page :left { 185 | @bottom-left { 186 | content: counter(page, var(--page-number-style)); 187 | } 188 | } 189 | @page :right { 190 | @bottom-right { 191 | content: counter(page, var(--page-number-style)); 192 | } 193 | } 194 | 195 | @page :first { 196 | @bottom-left { 197 | content: ''; 198 | } 199 | @bottom-right { 200 | content: ''; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "proposal-pattern-matching", 4 | "description": "A repository for ECMAScript pattern matching proposals.", 5 | "scripts": { 6 | "start": "npm run build -- --watch", 7 | "build": "SOURCE_DATE_EPOCH=$(git log -1 --pretty=\"format:%ct\" spec.emu) ecmarkup spec.emu index.html --load-biblio @tc39/ecma262-biblio --lint-spec --assets-dir assets --mark-effects" 8 | }, 9 | "homepage": "https://github.com/tc39/proposal-pattern-matching#readme", 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/tc39/proposal-pattern-matching.git" 13 | }, 14 | "license": "MIT", 15 | "devDependencies": { 16 | "@tc39/ecma262-biblio": "^2.1.2775", 17 | "ecmarkup": "^20.0.0" 18 | }, 19 | "packageManager": "pnpm@9.9.0+sha512.60c18acd138bff695d339be6ad13f7e936eea6745660d4cc4a776d5247c540d0edee1a563695c183a66eb917ef88f2b4feb1fc25f32a7adcadc7aaf3438e99c1" 20 | } 21 | --------------------------------------------------------------------------------