diff --git a/src/core/PocomathInstance.mjs b/src/core/PocomathInstance.mjs index dd4a6cc..4d32896 100644 --- a/src/core/PocomathInstance.mjs +++ b/src/core/PocomathInstance.mjs @@ -6,7 +6,7 @@ export default class PocomathInstance { * in that if a new top-level PocomathInstance method is added, its name * must be added to this list. */ - static reserved = new Set(['install']) + static reserved = new Set(['install', 'importDependencies']) constructor(name) { this.name = name @@ -52,6 +52,43 @@ export default class PocomathInstance { for (const key in ops) this._installOp(key, ops[key]) } + /** + * Import (and install) all dependencies of previously installed functions, + * for the specified types. + * + * @param {string[]} types A list of type names + */ + async importDependencies(types) { + const doneSet = new Set(['self']) // nothing to do for self dependencies + while (true) { + const requiredSet = new Set() + /* Grab all of the known deps */ + for (const func in this._imps) { + if (func === 'Types') continue + for (const definition of Object.values(this._imps[func])) { + for (const dependency of definition[0]) { + const depName = dependency.split('(',1)[0] + if (doneSet.has(depName)) continue + requiredSet.add(depName) + } + } + } + if (requiredSet.size === 0) break + for (const name of requiredSet) { + for (const type of types) { + try { + const modName = `../${type}/${name}.mjs` + const mod = await import(modName) + this.install(mod) + } catch (err) { + // No such module, but that's OK + } + } + doneSet.add(name) + } + } + } + /* Used internally by install, see the documentation there */ _installOp(name, implementations) { if (name.charAt(0) === '_') { diff --git a/test/custom.mjs b/test/custom.mjs index 72ba2d0..008218e 100644 --- a/test/custom.mjs +++ b/test/custom.mjs @@ -6,7 +6,9 @@ import * as numberAdd from '../src/number/add.mjs' import * as complex from '../src/complex/all.mjs' import * as complexAdd from '../src/complex/add.mjs' import * as complexNegate from '../src/complex/negate.mjs' +import * as complexComplex from '../src/complex/complex.mjs' import * as concreteSubtract from '../src/generic/subtract.concrete.mjs' +import * as genericSubtract from '../src/generic/subtract.mjs' import extendToComplex from '../src/complex/extendToComplex.mjs' const bw = new PocomathInstance('backwards') @@ -62,4 +64,18 @@ describe('A custom instance', () => { ok.install(concreteSubtract) assert.strictEqual(ok.subtract(7, 5), 2) }) + + it("can load generics and then import their dependences", async function () { + const chase = new PocomathInstance('Chase Dependencies') + chase.install(genericSubtract) + chase.install(complexComplex) // for convenience to build complex numbers + await chase.importDependencies(['bigint', 'complex']) + /* Now we have an instance that supports subtraction for Gaussian + integers. + */ + assert.deepStrictEqual( + chase.subtract(chase.complex(3n, 2n), chase.complex(2n, 5n)), + math.complex(1n, -3n)) + }) + })