scontainers
Version:
A container/collection/iterator library for JavaScript, comfortable to use, performant and versatile.
326 lines (232 loc) • 10.4 kB
JavaScript
"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
};