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
|
||||||
logs
|
logs
|
||||||
*.log
|
*.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\"",
|
"description": "Proof-of-concept tiny package like mathjs with mutable \"typed functions\" and module-based dependency tracking and \"tree-shaking\"",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha"
|
"test": "npx mocha"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -15,5 +15,9 @@
|
|||||||
"algebra"
|
"algebra"
|
||||||
],
|
],
|
||||||
"author": "Glen Whitney",
|
"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