feat: Initial core of picomath
Implements a totally simplistic "poortf" mutable typed function and a picomath instance generator, as well as the very beginnings of a number type and one generic function and a default full picomath instance. Also provides some tests which serve as usage examples.
This commit is contained in:
parent
36cc91ca95
commit
536656bfe8
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,6 @@
|
||||
# Editor backups
|
||||
*~
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
9
generic/subtract.js
Normal file
9
generic/subtract.js
Normal file
@ -0,0 +1,9 @@
|
||||
export default function create(pmath) {
|
||||
const add = pmath('add')
|
||||
const negate = pmath('negate')
|
||||
if (!pmath.subtract) { // avoid double definition at cost of extensibility
|
||||
pmath('subtract', [args => args.length === 2,
|
||||
(x, y) => add(x, negate(y))])
|
||||
}
|
||||
return pmath.subtract
|
||||
}
|
6
number/add.js
Normal file
6
number/add.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { allNumbers } from './number.js'
|
||||
|
||||
export default function create(pmath) {
|
||||
return pmath('add', [allNumbers,
|
||||
(...addends) => addends.reduce((x,y) => x+y, 0)])
|
||||
}
|
12
number/all.js
Normal file
12
number/all.js
Normal file
@ -0,0 +1,12 @@
|
||||
import createNumber from './number.js'
|
||||
import createAdd from './add.js'
|
||||
import createNegate from './negate.js'
|
||||
import createSubtract from '../generic/subtract.js'
|
||||
|
||||
export default function create(pmath) {
|
||||
createNumber(pmath)
|
||||
createAdd(pmath)
|
||||
createNegate(pmath)
|
||||
createSubtract(pmath)
|
||||
// not sure if there's anything reasonable to return here
|
||||
}
|
7
number/negate.js
Normal file
7
number/negate.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { oneNumber } from './number.js'
|
||||
|
||||
export default function create(pmath) {
|
||||
return pmath('negate', [oneNumber, n => -n])
|
||||
}
|
||||
|
||||
|
14
number/number.js
Normal file
14
number/number.js
Normal file
@ -0,0 +1,14 @@
|
||||
export function allNumbers(args) {
|
||||
for (let i = 0; i < args.length; ++i) {
|
||||
if (typeof args[i] !== 'number') return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export function oneNumber(args) {
|
||||
return args.length === 1 && typeof args[0] === 'number'
|
||||
}
|
||||
|
||||
export default function create(pmath) {
|
||||
return pmath('number', [() => true, x => Number(x)])
|
||||
}
|
1446
package-lock.json
generated
Normal file
1446
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@
|
||||
"description": "Proof-of-concept tiny package like mathjs with mutable \"typed functions\" and module-based dependency tracking and \"tree-shaking\"",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
"test": "npx mocha"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -15,5 +15,9 @@
|
||||
"algebra"
|
||||
],
|
||||
"author": "Glen Whitney",
|
||||
"license": "Apache-2.0"
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"mocha": "^9.2.2"
|
||||
}
|
||||
}
|
||||
|
8
picomath.js
Normal file
8
picomath.js
Normal file
@ -0,0 +1,8 @@
|
||||
/* Core of picomath: generates an instance */
|
||||
import picomathInstance from './picomathInstance.js'
|
||||
import createNumbers from './number/all.js'
|
||||
|
||||
const math = picomathInstance('math')
|
||||
createNumbers(math)
|
||||
|
||||
export default math
|
20
picomathInstance.js
Normal file
20
picomathInstance.js
Normal file
@ -0,0 +1,20 @@
|
||||
/* Core of picomath: generates an instance */
|
||||
import poortf from './poortf.js'
|
||||
|
||||
export default function picomathInstance (instName) {
|
||||
function fn (name, imps) {
|
||||
if (name in fn) {
|
||||
fn[name].addImps(imps)
|
||||
} else {
|
||||
fn[name] = poortf(name, imps)
|
||||
}
|
||||
return fn[name]
|
||||
}
|
||||
|
||||
Object.defineProperty(fn, 'name', {value: instName})
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
|
||||
|
27
poortf.js
Normal file
27
poortf.js
Normal file
@ -0,0 +1,27 @@
|
||||
/* Totally minimal "typed functions" */
|
||||
|
||||
const addImps = (dest, imps) => {
|
||||
if (imps) {
|
||||
if (!Array.isArray(imps[0])) imps = [imps]
|
||||
for (const imp of imps) dest.push(imp)
|
||||
}
|
||||
}
|
||||
|
||||
export default function poortf (name, imps) {
|
||||
/* This is the (function) object we will return */
|
||||
function fn () {
|
||||
for (const imp of fn.imps) {
|
||||
if (imp[0](arguments)) return imp[1].apply(null, arguments)
|
||||
}
|
||||
throw new TypeError(
|
||||
`TF ${fn.name}: No match for ${arguments[0]}, ${arguments[1]}, ...`)
|
||||
}
|
||||
|
||||
/* Now dress it up for use */
|
||||
Object.defineProperty(fn, 'name', {value: name})
|
||||
fn.imps = []
|
||||
addImps(fn.imps, imps)
|
||||
fn.addImps = newI => addImps(fn.imps, newI)
|
||||
|
||||
return fn
|
||||
}
|
15
test/_picomath.js
Normal file
15
test/_picomath.js
Normal file
@ -0,0 +1,15 @@
|
||||
import assert from 'assert'
|
||||
import math from '../picomath.js'
|
||||
|
||||
describe('The default full picomath instance "math"', () => {
|
||||
it('performs basic arithmetic operations', () => {
|
||||
assert.strictEqual(math.subtract(16, math.add(3,4,2)), 7)
|
||||
assert.strictEqual(math.negate(math.number('8')), -8)
|
||||
})
|
||||
|
||||
it('can be extended', () => {
|
||||
math('add', [args => typeof args[0] === 'string',
|
||||
(...addends) => addends.reduce((x,y) => x+y, '')])
|
||||
assert.strictEqual(math.add('Kilroy',' is here'), 'Kilroy is here')
|
||||
})
|
||||
})
|
13
test/_picomathInstance.js
Normal file
13
test/_picomathInstance.js
Normal file
@ -0,0 +1,13 @@
|
||||
import assert from 'assert'
|
||||
import picomathInstance from '../picomathInstance.js'
|
||||
|
||||
describe('picomath core', () => {
|
||||
it('creates an instance that can define TFs', () => {
|
||||
const pmath = picomathInstance('pmath')
|
||||
pmath('add', [() => true, (a,b) => a+b])
|
||||
assert.strictEqual(pmath.add(2,2), 4)
|
||||
assert.strictEqual(pmath.add('Kilroy', 17), 'Kilroy17')
|
||||
assert.strictEqual(pmath.add(1), NaN)
|
||||
// I guess + never throws!
|
||||
})
|
||||
})
|
41
test/_poortf.js
Normal file
41
test/_poortf.js
Normal file
@ -0,0 +1,41 @@
|
||||
import assert from 'assert'
|
||||
import poortf from '../poortf.js'
|
||||
|
||||
describe('poortf', () => {
|
||||
const slate = poortf('slate')
|
||||
it('creates an empty tf', () => {
|
||||
assert.throws(() => slate('empty'), TypeError)
|
||||
assert.throws(() => slate('empty'), /slate.*empty/)
|
||||
})
|
||||
|
||||
const add = poortf('add', [
|
||||
[args => Array.from(args).every(a => typeof a === 'number'),
|
||||
(a, b) => a+b],
|
||||
[args => Array.from(args).every(a => typeof a === 'boolean'),
|
||||
(p, q) => p || q]
|
||||
])
|
||||
|
||||
it('creates a tf with initial behaviors', () => {
|
||||
assert.strictEqual(add(2,2), 4)
|
||||
assert.strictEqual(add(true, false), true)
|
||||
assert.throws(() => add('kilroy'), TypeError)
|
||||
})
|
||||
|
||||
it('extends an empty tf', () => {
|
||||
slate.addImps([
|
||||
[args => typeof args[0] === 'string', s => s + ' wuz here'],
|
||||
[args => typeof args[0] === 'number', () => 'I am not a number']
|
||||
])
|
||||
assert.strictEqual(slate('Kilroy', 'was here'), 'Kilroy wuz here')
|
||||
assert.strictEqual(slate(2, 'was here'), 'I am not a number')
|
||||
assert.throws(() => slate(['Ha!']), TypeError)
|
||||
})
|
||||
|
||||
it('extends a tf with other behaviors', () => {
|
||||
add.addImps([args => typeof args[0] === 'string', (s,x) => s + x]),
|
||||
assert.strictEqual(add('Kilroy', 23), 'Kilroy23')
|
||||
assert.throws(() => add(['Ha!'], 'gotcha'), TypeError)
|
||||
})
|
||||
})
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user