UNPKG

scontainers

Version:

A container/collection/iterator library for JavaScript, comfortable to use, performant and versatile.

526 lines (406 loc) 14.4 kB
"use strict"; function _implSymbol(target, sym, value) { Object.defineProperty(target, sym, { value, configurable: true }); return target[sym]; } function _getSymbol(targetSymName, ...traitSets) { let symbol; traitSets.forEach(traitSet => { const sym = traitSet[targetSymName]; if (typeof sym === 'symbol') { if (!!symbol && symbol !== sym) { throw new Error(`Symbol ${targetSymName} offered by multiple trait sets.`); } symbol = sym; } }); if (!symbol) { throw new Error(`No trait set is providing symbol ${targetSymName}.`); } return symbol; } function _testTraitSet(traitSet) { if (!traitSet || typeof traitSet === 'boolean' || typeof traitSet === 'number' || typeof traitSet === 'string') { throw new Error(`${traitSet} cannot be used as a trait set.`); } } const { deriveProtocols } = require('./derived_traits.js'); const { assert, traits, options, ReorderedIterator, KVN, KVIt, Done } = require('../utils.js'); _testTraitSet(traits.utils); _testTraitSet(traits.descriptors); _testTraitSet(traits.scontainers); const _InnerCollection = _getSymbol("InnerCollection", traits.utils, traits.descriptors, traits.scontainers); const _extractKeys = _getSymbol("extractKeys", traits.utils, traits.descriptors, traits.scontainers); const _keyToN = _getSymbol("keyToN", traits.utils, traits.descriptors, traits.scontainers); const _implScontainer = _getSymbol("implScontainer", traits.utils, traits.descriptors, traits.scontainers); const _addTraitFactories = _getSymbol("addTraitFactories", traits.utils, traits.descriptors, traits.scontainers); const _len = _getSymbol("len", traits.utils, traits.descriptors, traits.scontainers); const _nToKey = _getSymbol("nToKey", traits.utils, traits.descriptors, traits.scontainers); const _hasKey = _getSymbol("hasKey", traits.utils, traits.descriptors, traits.scontainers); const _innerCollectionKey = _getSymbol("innerCollectionKey", traits.utils, traits.descriptors, traits.scontainers); const _mappingOnly = _getSymbol("mappingOnly", traits.utils, traits.descriptors, traits.scontainers); const _nthKVN = _getSymbol("nthKVN", traits.utils, traits.descriptors, traits.scontainers); const _getKVN = _getSymbol("getKVN", traits.utils, traits.descriptors, traits.scontainers); const _nth = _getSymbol("nth", traits.utils, traits.descriptors, traits.scontainers); const _transformStream = _getSymbol("transformStream", traits.utils, traits.descriptors, traits.scontainers); const _kvIterator = _getSymbol("kvIterator", traits.utils, traits.descriptors, traits.scontainers); const _kvReorderedIterator = _getSymbol("kvReorderedIterator", traits.utils, traits.descriptors, traits.scontainers); const _setNth = _getSymbol("setNth", traits.utils, traits.descriptors, traits.scontainers); const _has = _getSymbol("has", traits.utils, traits.descriptors, traits.scontainers); const _values = _getSymbol("values", traits.utils, traits.descriptors, traits.scontainers); const _iterator = _getSymbol("iterator", traits.utils, traits.descriptors, traits.scontainers); const _implCoreTraits = _getSymbol("implCoreTraits", traits.utils, traits.descriptors, traits.scontainers); _implSymbol(Object.prototype, _implCoreTraits, function (compilerConfiguration) { if (!options.derivation) { return; } if (this[_InnerCollection]) { deriveProtocolsForTransformation.call(this, compilerConfiguration); } else { deriveProtocolsForRootType.call(this, compilerConfiguration); } // deriving all the core protocols again, in case something depends on the newly added iterators... deriveCoreProtocols.call(this); // deriving non-core protocols deriveProtocols.call(this); }); function deriveProtocolsForRootType(configuration = {}) { assert(this, `deriveProtocolsForRootType() must be called on an object`); const check = (cond, err) => { assert(cond, `${this.name}.compileProtocolsForTransformation(): ${err}`); }; // taking the non-protocol data from `configuration` (e.g. `nStage` and `stage`) let { nthUnchecked, getUnchecked } = configuration[_extractKeys](Object.keys({ nthUnchecked: null, getUnchecked: null })); // deriving the missing non-protocol functions we can derive { if (nthUnchecked) { check(!getUnchecked, `either supply \`nthUnchecked\` or \`getUnchecked\``); getUnchecked = function (key) { const n = this[_keyToN](key); return nthUnchecked.call(this, n); }; } } // everything in `compilerConfiguration` should be protocol generator factories: assigning them to `this` this.prototype[_implScontainer](configuration); // deriving the other core protocol generator factories we can derive from the non-protocol data { const proto = this.prototype; traits.scontainers[_addTraitFactories](this.prototype, { inplace() { return function inplace() { return this; }; }, hasKey() { if (nthUnchecked) { return function (key) { const n = this[_keyToN](key); return Number.isInteger(n) && n >= 0 && n < this[_len](); }; } }, nthKVN() { if (nthUnchecked) { return function (n) { return new KVN(this[_nToKey](n), nthUnchecked.call(this, n), n); }; } }, getKVN() { if (getUnchecked) { if (proto[_keyToN]) { return function (key) { if (this[_hasKey](key)) { return new KVN(key, getUnchecked.call(this, key), this[_keyToN](key)); } }; } else { return function (key) { if (this[_hasKey](key)) { return new KVN(key, getUnchecked.call(this, key)); } }; } } }, nth() { if (nthUnchecked) { return function (n) { assert(Number.isInteger(n), `${this.constructor.name}.nth(${n}): ${n} not valid`); if (n >= 0 && n < this[_len]()) { return nthUnchecked.call(this, n); } }; } }, get() { if (getUnchecked && proto[_hasKey]) { return function (key) { if (!this[_hasKey](key)) { return; } return getUnchecked.call(this, key); }; } }, kvIterator() { if (nthUnchecked) { return function kvIterator() { return { collection: this, i: 0, next() { const coll = this.collection; if (this.i < coll[_len]()) { const n = this.i++; return new KVN(coll[_nToKey](n), nthUnchecked.call(coll, n), n); } } }; }; } } }); } } function deriveProtocolsForTransformation(configuration = {}) { assert(this, `deriveProtocolsForTransformation() must be called on an object`); const check = (cond, err) => { assert(cond, `${this.name}.compileProtocolsForTransformation(): ${err}`); }; const Collection = this; const ParentCollection = this[_InnerCollection]; check(ParentCollection, `need to specify the ParentType`); const parentProto = ParentCollection.prototype; const innerCollectionKey = this[_innerCollectionKey]; // TODO: `stage` (and its specializations) could be improved... // most collections don't do anything *before* `stage` returns from recursion. // it would be more efficient (and easier to write) if we had a `stageEnd` that doesn't recurse, like the old `propagator.next`. // taking the non-protocol data from `configuration` (e.g. `nStage` and `stage`) let { stage, nStage, kStage, indexToParentIndex, nToParentN, keyToParentKey } = configuration[_extractKeys](Object.keys({ stage: null, nStage: null, kStage: null, indexToParentIndex: null, nToParentN: null, keyToParentKey: null })); // deriving the missing non-protocol functions we can derive { check(!!stage === !!indexToParentIndex && !!nStage === !!nToParentN && !!kStage === !!keyToParentKey, `\`*Stage\` needs to match \`*ToParent*\``); if (stage) { check(!nStage && !kStage, `either supply \`stage\` or \`nStage\` or \`kStage\``); nStage = stage; kStage = stage; } if (indexToParentIndex) { check(!nToParentN && !keyToParentKey, `either supply \`indexToParentIndex\` or \`nToParentN\` or \`keyToParentKey\``); nToParentN = indexToParentIndex; keyToParentKey = indexToParentIndex; } } // all the remaining stuff in `compilerConfiguration` should be protocol generator factories: assigning them to `this` traits.scontainers[_addTraitFactories](this.prototype, configuration); // deriving the other core protocol factories we can derive from the non-protocol data traits.scontainers[_addTraitFactories](this.prototype, { len() { if (Collection[_mappingOnly] && parentProto[_len]) { return function () { return this[innerCollectionKey][_len](); }; } }, nToKey() { if (nToParentN && parentProto[_nToKey]) { return function (n) { const parentN = nToParentN.call(this, n); return this[innerCollectionKey][_nToKey](parentN); }; } }, keyToN() { if (keyToParentKey && parentProto[_keyToN]) { return function (key) { const innerKey = keyToParentKey.call(this, key); return this[innerCollectionKey][_keyToN](innerKey); }; } }, nthKVN() { if (nStage && parentProto[_nthKVN]) { return function (n) { const parentN = nToParentN.call(this, n); const parentKVN = this[innerCollectionKey][_nthKVN](parentN); parentKVN.n = n; if (parentKVN) { return nStage.call(this, parentKVN); } }; } }, getKVN() { if (kStage && parentProto[_getKVN]) { return function (key) { const innerKey = keyToParentKey.call(this, key); const parentKVN = this[innerCollectionKey][_getKVN](innerKey); if (parentKVN) { return kStage.call(this, parentKVN); } }; } }, hasKey() { if (kStage && parentProto[_nth]) { return function (key) { return kStage.call(this, c => { this[innerCollectionKey][_nth](c, c.key); }); }; } if (Collection[_mappingOnly] && parentProto[_hasKey]) { return function (key) { return this[innerCollectionKey][_hasKey](keyToParentKey(key)); }; } } }); // deriving all the remaining core protocols deriveCoreProtocols.call(this); // if there are no iterators, we can derived them starting from the parent collection... // TODO NOTE FIXME: BROKEN // 1. we have no way to say when to start and when to stop for `Slice` // get rid of the above `this::deriveCoreProtocols()` to see whether it's fixed traits.scontainers[_addTraitFactories](this.prototype, { kvIterator() { if (Collection[_transformStream] && parentProto[_kvIterator] && kStage) { return function () { const self = this; const parentCollection = this[innerCollectionKey]; const it = parentCollection[_kvIterator](); return { next() { const next = it.next(); if (!next) { return; } const kvn = kStage.call(self, next); if (!kvn) { return this.next(); } return new KVN(kvn.key, kvn.value, kvn.n); } }; }; } }, kvReorderedIterator() { if (parentProto[_kvReorderedIterator] && kStage) { return function () { const innerRIt = this[innerCollectionKey][_kvReorderedIterator](); return new ReorderedIterator.MapReorderedIterator(innerRIt, kStage.bind(this)); }; } } }); } function deriveCoreProtocols() { assert(this, `deriveCoreProtocols() must be called on an object`); const Collection = this; const proto = Collection.prototype; traits.scontainers[_addTraitFactories](proto, { nth() { if (proto[_nthKVN]) { return function (n) { const kvn = this[_nthKVN](n); if (kvn) { return kvn.value; } }; } }, get() { if (proto[_getKVN]) { return function (key) { const kvn = this[_getKVN](key); if (kvn) { return kvn.value; } }; } }, set() { if (proto[_setNth]) { return function (key, value) { const n = this[_keyToN](key); this[_setNth](n, value); }; } }, kvIterator() { if (proto[_nthKVN]) { return function kvIterator() { return { collection: this, i: 0, next() { const coll = this.collection; if (this.i < coll[_len]()) { const n = this.i++; const kvn = coll[_nthKVN](n); assert(kvn, `${Collection.name}.nth(${n}) not there?! :F`); return kvn; } } }; }; } }, kvReorderedIterator() { if (proto[_kvIterator]) { return function kvReorderedIterator() { return new ReorderedIterator.FromIterator(this[_kvIterator]()); }; } }, iterator() { if (proto[_nth] || proto[_has]) { return function () { return this[_values]()[_iterator](); }; } if (proto[_kvIterator]) { return function iterator() { return { it: this[_kvIterator](), next() { const next = this.it.next(); if (!next) { return new Done(); } const { key, value } = next; return new KVIt(key, value); } }; }; } } }); }