| | |
| | | const UNDEFINED_MARKER = Symbol("undefined"); |
| | | |
| | | /** |
| | | * Public cell value exposed by `StackedMap`, where `undefined` is preserved as |
| | | * a valid stored result. |
| | | * @template T |
| | | * @typedef {T | undefined} Cell<T> |
| | | */ |
| | | |
| | | /** |
| | | * Internal cell value used to distinguish deleted entries and explicit |
| | | * `undefined` assignments while traversing stacked scopes. |
| | | * @template T |
| | | * @typedef {T | typeof TOMBSTONE | typeof UNDEFINED_MARKER} InternalCell<T> |
| | | */ |
| | | |
| | | /** |
| | | * Converts an internal key/value pair into the external representation returned |
| | | * by iteration helpers. |
| | | * @template K |
| | | * @template V |
| | | * @param {[K, InternalCell<V>]} pair the internal cell |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Layered map that supports child scopes while memoizing lookups from parent |
| | | * scopes into the current layer. |
| | | * @template K |
| | | * @template V |
| | | */ |
| | | class StackedMap { |
| | | /** |
| | | * Creates a new map layer on top of an optional parent stack. |
| | | * @param {Map<K, InternalCell<V>>[]=} parentStack an optional parent |
| | | */ |
| | | constructor(parentStack) { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Stores a value in the current layer, preserving explicit `undefined` |
| | | * values with an internal marker. |
| | | * @param {K} item the key of the element to add |
| | | * @param {V} value the value of the element to add |
| | | * @returns {void} |
| | |
| | | } |
| | | |
| | | /** |
| | | * Deletes a key from the current view, either by removing it outright in the |
| | | * root layer or by recording a tombstone in child layers. |
| | | * @param {K} item the item to delete |
| | | * @returns {void} |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Checks whether a key exists in the current scope chain, caching any parent |
| | | * lookup result in the current layer. |
| | | * @param {K} item the item to test |
| | | * @returns {boolean} true if the item exists in this set |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns the visible value for a key, caching parent hits and misses in the |
| | | * current layer. |
| | | * @param {K} item the key of the element to return |
| | | * @returns {Cell<V>} the value of the element |
| | | */ |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Collapses the stacked layers into a single concrete map. |
| | | */ |
| | | _compress() { |
| | | if (this.stack.length === 1) return; |
| | | this.map = new Map(); |
| | |
| | | this.stack = [this.map]; |
| | | } |
| | | |
| | | /** |
| | | * Returns the visible keys as an array after collapsing the stack. |
| | | * @returns {K[]} array of keys |
| | | */ |
| | | asArray() { |
| | | this._compress(); |
| | | return [...this.map.keys()]; |
| | | } |
| | | |
| | | /** |
| | | * Returns the visible keys as a `Set` after collapsing the stack. |
| | | * @returns {Set<K>} set of keys |
| | | */ |
| | | asSet() { |
| | | this._compress(); |
| | | return new Set(this.map.keys()); |
| | | } |
| | | |
| | | /** |
| | | * Returns visible key/value pairs using the external representation. |
| | | * @returns {[K, Cell<V>][]} array of key/value pairs |
| | | */ |
| | | asPairArray() { |
| | | this._compress(); |
| | | return Array.from(this.map.entries(), extractPair); |
| | | } |
| | | |
| | | /** |
| | | * Returns the visible contents as a plain `Map`. |
| | | * @returns {Map<K, Cell<V>>} materialized map |
| | | */ |
| | | asMap() { |
| | | return new Map(this.asPairArray()); |
| | | } |
| | | |
| | | /** |
| | | * Returns the number of visible keys after collapsing the stack. |
| | | * @returns {number} number of keys |
| | | */ |
| | | get size() { |
| | | this._compress(); |
| | | return this.map.size; |
| | | } |
| | | |
| | | /** |
| | | * Creates a child `StackedMap` that sees the current layers as its parent |
| | | * scope. |
| | | * @returns {StackedMap<K, V>} child map |
| | | */ |
| | | createChild() { |
| | | return new StackedMap(this.stack); |
| | | } |