feat: First operational instance with just add
This commit is contained in:
parent
cb016474ec
commit
c5384e0ee7
90
PocomathInstance.mjs
Normal file
90
PocomathInstance.mjs
Normal file
@ -0,0 +1,90 @@
|
||||
/* Core of pocomath: create an instance */
|
||||
import typed from 'typed-function'
|
||||
|
||||
export default class PocomathInstance {
|
||||
constructor(name) {
|
||||
this.name = name
|
||||
this._imps = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* (Partially) define one or more operations of the instance:
|
||||
*
|
||||
* @param {Object<string, Object<Signature, [string[], function]>>} ops
|
||||
* The only parameter ops gives the semantics of the operations to install.
|
||||
* The keys are operation names. The value for a key is an object
|
||||
* mapping (typed-function) signature strings to pairs of dependency
|
||||
* lists and implementation functions.
|
||||
*
|
||||
* A dependency list is a list of strings. Each string can either be the
|
||||
* name of a function that the corresponding implementation has to call,
|
||||
* or a specification of a particular signature of a function that it has
|
||||
* to call, in the form 'FN(SIGNATURE)'. Note the function name can be
|
||||
* the special value 'self' to indicate a recursive call to the given
|
||||
* operation (either with or without a particular signature.
|
||||
*
|
||||
* There are two cases for the implementation function. If the dependency
|
||||
* list is empty, it should be a function taking the arguments specified
|
||||
* by the signature and returning the value. Otherwise, it should be
|
||||
* a function taking an object with the dependency lists as keys and the
|
||||
* requested functions as values, to a function taking the arguments
|
||||
* specified by the signature and returning the value
|
||||
*/
|
||||
install(ops) {
|
||||
for (const key in ops) this._installOp(key, ops[key])
|
||||
}
|
||||
|
||||
/* Used internally by install, see the documentation there */
|
||||
_installOp(name, implementations) {
|
||||
// new implementations, so set the op up to lazily recreate itself
|
||||
this._invalidate(name)
|
||||
const opImps = this._imps[name]
|
||||
for (const signature in implementations) {
|
||||
if (signature in opImps) {
|
||||
if (implemenatations[signature] === opImps[signature]) continue
|
||||
throw new SyntaxError(
|
||||
`Conflicting definitions of ${signature} for ${name}`)
|
||||
} else {
|
||||
opImps[signature] = implementations[signature]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset an operation to require creation of typed-function,
|
||||
* and if it has no implementations so far, set them up.
|
||||
*/
|
||||
_invalidate(name) {
|
||||
const self = this
|
||||
this[name] = function () {
|
||||
return self._bundle(name).apply(self, arguments)
|
||||
}
|
||||
if (!(name in this._imps)) {
|
||||
this._imps[name] = {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a typed-function from the signatures for the given name and
|
||||
* assign it to the property with that name, returning it as well
|
||||
*/
|
||||
_bundle(name) {
|
||||
const imps = this._imps[name]
|
||||
if (!imps || Object.keys(imps).length === 0) {
|
||||
throw new SyntaxError(`No implementations for ${name}`)
|
||||
}
|
||||
const tf_imps = {}
|
||||
// TODO: handle nonempty dependencies
|
||||
for (const signature in imps) {
|
||||
const [deps, imp] = imps[signature]
|
||||
if (deps.length === 0) {
|
||||
tf_imps[signature] = imp
|
||||
} else {
|
||||
throw new Error('Unimplemented')
|
||||
}
|
||||
}
|
||||
const tf = typed(name, tf_imps)
|
||||
this[name] = tf
|
||||
return tf
|
||||
}
|
||||
}
|
3
number/add.mjs
Normal file
3
number/add.mjs
Normal file
@ -0,0 +1,3 @@
|
||||
export const add = {
|
||||
'...number': [[], addends => addends.reduce((x,y) => x+y, 0)],
|
||||
}
|
@ -21,4 +21,7 @@
|
||||
devDependencies: {
|
||||
mocha: '^10.0.0',
|
||||
},
|
||||
dependencies: {
|
||||
'typed-function': '^3.0.0',
|
||||
},
|
||||
}
|
||||
|
@ -2,6 +2,10 @@ lockfileVersion: 5.4
|
||||
|
||||
specifiers:
|
||||
mocha: ^10.0.0
|
||||
typed-function: ^3.0.0
|
||||
|
||||
dependencies:
|
||||
typed-function: 3.0.0
|
||||
|
||||
devDependencies:
|
||||
mocha: 10.0.0
|
||||
@ -465,6 +469,11 @@ packages:
|
||||
is-number: 7.0.0
|
||||
dev: true
|
||||
|
||||
/typed-function/3.0.0:
|
||||
resolution: {integrity: sha512-mKJKkt2xYxJUuMD7jyfgUxfn5KCsCxkEKBVjep5yYellJJ5aEDO2QUAmIGdvcZmfQnIrplkzELIaG+5b1475qg==}
|
||||
engines: {node: '>= 14'}
|
||||
dev: false
|
||||
|
||||
/workerpool/6.2.1:
|
||||
resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==}
|
||||
dev: true
|
||||
|
8
pocomath.mjs
Normal file
8
pocomath.mjs
Normal file
@ -0,0 +1,8 @@
|
||||
/* Core of pocomath: generates the default instance */
|
||||
import PocomathInstance from './PocomathInstance.mjs'
|
||||
import * as numberAdd from './number/add.mjs'
|
||||
|
||||
const math = new PocomathInstance('math')
|
||||
math.install(numberAdd)
|
||||
|
||||
export default math
|
10
test/_pocomath.mjs
Normal file
10
test/_pocomath.mjs
Normal file
@ -0,0 +1,10 @@
|
||||
import assert from 'assert'
|
||||
import math from '../pocomath.mjs'
|
||||
|
||||
describe('The default full pocomath instance "math"', () => {
|
||||
it('can add numbers', () => {
|
||||
assert.strictEqual(math.add(3, 4), 7)
|
||||
assert.strictEqual(math.add(1.5, 2.5, 3.5), 7.5)
|
||||
assert.strictEqual(math.add(Infinity), Infinity)
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user