UNPKG

scontainers

Version:

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

326 lines (232 loc) 10.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 { assert, traits, semantics, KVN } = require('../utils.js'); const Compiler = require('./compiler.js'); _testTraitSet(traits.utils); _testTraitSet(traits.descriptors); _testTraitSet(traits.semantics); function deriveProtocolsFromGenerators() { assert(this, `deriveProtocolsFromGenerators() must be called on an object`); const Type = this; // implementing core protocols from generators { _testTraitSet(traits.generators); const _return = _getSymbol("return", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _addTraitFactories = _getSymbol("addTraitFactories", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _nthKVN = _getSymbol("nthKVN", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _new = _getSymbol("new", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _getKVN = _getSymbol("getKVN", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _statement = _getSymbol("statement", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _member = _getSymbol("member", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _call = _getSymbol("call", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _if = _getSymbol("if", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _lt = _getSymbol("lt", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _len = _getSymbol("len", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _hasKey = _getSymbol("hasKey", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _comment = _getSymbol("comment", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _standardIteration = _getSymbol("standardIteration", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _defaultGet = _getSymbol("defaultGet", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _assign = _getSymbol("assign", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _increment = _getSymbol("increment", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _loop = _getSymbol("loop", traits.utils, traits.descriptors, traits.semantics, traits.generators); const _declare = _getSymbol("declare", traits.utils, traits.descriptors, traits.semantics, traits.generators); const proto = this.prototype; const deriveProtocols = function (protoObj) { const factories = {}; for (let key in protoObj) { const factory = protoObj[key](); if (!factory) { continue; } factories[key] = { factory() { var _context; const compiler = new Compiler(Type, key); // compiler.body._Straits.statement( compiler.debug() ); const result = (_context = compiler.frame, factory).call(_context); if (result) { compiler.body[_return](result); } return compiler.compile(); } }; } return traits.scontainers[_addTraitFactories](proto, factories); }; // deriving core protocols from protocol generators deriveProtocols({ nthKVN() { if (Type[_nthKVN]) { return function () { const KVNVar = this.registerConstant(KVN, `KVN`); const n = this.registerParameter(`n`); const kvn = this[_nthKVN](n); return KVNVar[_new](kvn.key, kvn.value, kvn.n); }; } }, getKVN() { if (Type[_getKVN]) { return function () { const KVNVar = this.registerConstant(KVN, `KVN`); const n = this.registerParameter(`n`); const kvn = this[_getKVN](n); return KVNVar[_new](kvn.key, kvn.value, kvn.n); }; } }, nth() { if (Type[_nthKVN]) { return function () { const n = this.registerParameter(`n`); this.body[_statement](this.assert(semantics.id(`Number`)[_member](`isInteger`)[_call](n)))[_if](semantics.or(n[_lt](this[_len]())), semantics.return(this[_nthKVN](n).value)); }; } }, get() { if (Type[_getKVN] && Type[_hasKey]) { return function () { const key = this.registerParameter(`key`); this.body[_comment](`${Type.name} GET KVN`); const kvn = this[_getKVN](key); /* this.body ._Straits.comment( semantics.lit(`HAS KEY`) ) ._Straits.if( this._Straits.hasKey(key), this.body = semantics.block() ) */ this.body[_return](kvn.value); }; } }, len() { if (Type[_len]) { return function () { return this[_len](); }; } }, kvIterator() { if (Type[_nthKVN] && this[_standardIteration]) { return function () { const fns = this.registerConstants({ KVN }); const Iterator = this.createUniqueVariable(`Iterator`); const thisI = semantics.this()[_member](this.createUniqueVariable(`i`)); const thisLen = semantics.this()[_member](this.createUniqueVariable(`len`)); let typeArgMapFn = this.typeArgMapFn; let body = this.body; const reset = () => { this.typeArgMapFn = typeArgMapFn; this.body = body; }; { // will map all the type variables that are used to the variable member of `this` // `this.wrapped.mapFn` becomes: // key:{name:mapFn, variable:this.wrapped.mapFn, fn:...}, value:this.mapFn const argVarMap = new Map(); const rebase = function (variable) { const rebaseRec = variable => { if (variable.type === 'ThisExpression') { return this; } return rebaseRec(variable.object)[_member](variable.property, variable.computed); }; return rebaseRec(variable); }; // computing `Iterator.prototype.next` first { this.typeArgMapFn = function (arg) { return argVarMap[_defaultGet](arg, () => semantics.this()[_member](this.createUniqueVariable(arg.name))); }; // as that's the one doing the actual computations // after it's compiled, we'll know which type arguments are needed this.ast.body.unshift(semantics.statement(Iterator[_member]('prototype')[_member]('next')[_assign](semantics.function(null, [], semantics.block()[_if](thisI[_lt](thisLen), this.body = semantics.block()))))); const kvn = this[_nthKVN](thisI); this.body[_return](fns.KVN[_new](kvn.key, kvn.value, thisI[_increment](false))); reset(); } // defining `Iterator` { const collection = this.createUniqueVariable(`collection`); this.typeArgMapFn = function (arg) { return rebase.call(collection, arg.variable); }; this.ast.body.unshift(semantics.declareFunction(Iterator, [collection], this.body = semantics.block()[_statement](thisI[_assign](0)))); // assigning `thisLen` this.body[_statement](thisLen[_assign](this[_len]())); // assignign all the other arg vars argVarMap.forEach((thisVar, collectionVar) => { this.body[_statement](thisVar[_assign](rebase.call(collection, collectionVar.variable))); }); reset(); } /* this.ast IteratorConstructor, Iterator.member('prototype').member('next').assign( nextFunction ) ); */ } // the main function only instantiates a new `Iterator` this.body[_return](Iterator[_new](semantics.this())); }; } } }); // deriving derived protocols deriveProtocols({ forEach() { if (Type[_loop]) { return function () { const forEachFn = this.registerParameter(`forEachFn`); const kvn = this[_loop](); this.body[_statement](forEachFn[_call](kvn.value, kvn.key, kvn.n)); }; } }, reduce() { if (Type[_loop]) { return function () { const reduceFn = this.registerParameter(`reduceFn`); const initialValue = this.registerParameter(`initialValue`); const state = this.createUniqueVariable(`state`); const outsideLoop = this.body; this.body[_declare](state, initialValue, `var`); const kvn = this[_loop](); this.body[_statement](state[_assign](reduceFn[_call](state, kvn.value, kvn.key, kvn.n))); this.body = outsideLoop; this.body[_return](state); }; } } }); } } module.exports = { deriveProtocolsFromGenerators };