forc
Version:
Clone of Clojure list comprehension
224 lines (195 loc) • 5.18 kB
JavaScript
import test from 'tape'
import forc from '../src/forc'
test('definition', (t) => {
t.plan(1)
t.equal(typeof forc, 'function', 'forc is a function')
})
test('empty expressions yield empty results', (t) => {
t.plan(1)
const actual = forc([], () => {
t.fail('should not be called')
})
t.deepEqual([...actual], [], 'empty result list')
})
test('bad arguments throw', (t) => {
t.plan(2)
t.throws(() => [...forc(null, () => {})], /first argument must be iterable/, 'first must be iterable')
t.throws(() => [...forc([], null)], /second argument must be a function/, 'second must be a function')
})
test('simple incrementing expression', (t) => {
t.plan(1)
const actual = forc([
'x', () => [10, 20, 30]
], ({x}) => x + 1)
t.deepEqual([...actual], [11, 21, 31], 'values were incremented')
})
test('shorthand incrementing expression (no function)', (t) => {
t.plan(1)
const actual = forc([
'x', [10, 20, 30]
], ({x}) => x + 1)
t.deepEqual([...actual], [11, 21, 31], 'values were incremented')
})
test('multiple expressions', (t) => {
t.plan(1)
const actual = forc([
'x', ['a', 'b', 'c'],
'y', [1, 2, 3]
], ({x, y}) => [x, y])
t.deepEqual([...actual], [['a', 1], ['a', 2], ['a', 3], ['b', 1], ['b', 2], ['b', 3], ['c', 1], ['c', 2], ['c', 3]], 'values were zipped up')
})
test('using previous expressions', (t) => {
t.plan(1)
const actual = forc([
'x', [1, 2, 3],
'y', ({x}) => [x + 1]
], ({x, y}) => [x, y])
t.deepEqual([...actual], [[1, 2], [2, 3], [3, 4]], 'values were zipped and incremented')
})
test('let', (t) => {
t.plan(1)
const actual = forc([
'x', [1, 2, 3],
':let', ['y', ({x}) => x + 1]
], ({x, y}) => [x, y])
t.deepEqual([...actual], [[1, 2], [2, 3], [3, 4]], 'values were zipped and added')
})
test('let and when', (t) => {
t.plan(1)
const actual = forc([
'x', [0, 1, 2, 3, 4, 5],
':let', ['y', ({x}) => x * 3],
':when', ({y}) => y % 2 === 0
], ({y}) => y)
t.deepEqual([...actual], [0, 6, 12], 'multiplied by 3 then even are returned')
})
test('when', (t) => {
t.plan(1)
const actual = forc([
'x', [0, 1, 2],
'y', [0, 1, 2],
':when', ({x, y}) => x !== y
], ({x, y}) => [x, y])
t.deepEqual([...actual], [[0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1]])
})
test('while', (t) => {
t.plan(1)
const actual = forc([
'x', [1, 2, 3],
'y', [3, 2, 1],
':while', ({x, y}) => x !== y
], ({x, y}) => [x, y])
t.deepEqual([...actual], [[1, 3], [1, 2]])
})
test('let', (t) => {
t.plan(1)
const actual = forc([
'x', [1, 2, 3, 4, 5],
':let', ['y', ({x}) => x * x,
'z', ({x}) => x * x * x]
], ({x, y, z}) => [x, y, z])
t.deepEqual([...actual], [[1, 1, 1], [2, 4, 8], [3, 9, 27], [4, 16, 64], [5, 25, 125]])
})
test('uneven let', (t) => {
t.plan(1)
t.throws(() => {
[...forc([
'x', [1],
':let', ['bad']
], ({x}) => x)]
}, /even number of forms/, 'needs an even number of binding forms')
})
test('empty let', (t) => {
t.plan(1)
const actual = forc([
'x', [1, 2, 3, 4, 5],
':let', []
], ({x}) => x + 1)
t.deepEqual([...actual], [2, 3, 4, 5, 6])
})
test('passing generators', (t) => {
function * genRange (n) {
for (let i = 0; i <= n; i++) {
yield i
}
}
t.plan(2)
t.deepEqual([...genRange(4)], [0, 1, 2, 3, 4], 'generator generated 0 -> 4')
const actual = forc([
'x', genRange(4)
], ({x}) => x + 1)
t.deepEqual([...actual], [1, 2, 3, 4, 5], 'forc passed the generated values through')
})
test('while after let', (t) => {
t.plan(1)
const actual = forc([
'x', [0, 1, 2, 3, 4, 5],
':let', ['y', ({x}) => x * 3],
':while', ({y}) => y < 10
], ({y}) => y)
t.deepEqual([...actual], [0, 3, 6, 9])
})
test('while, let and when in different orders', (t) => {
let actual
const expected = [0, 6]
t.plan(2)
actual = forc([
'x', [0, 1, 2, 3, 4, 5],
':let', ['y', ({x}) => x * 3],
':when', ({y}) => y % 2 === 0,
':while', ({y}) => y < 10
], ({y}) => y)
t.deepEqual([...actual], expected)
actual = forc([
'x', [0, 1, 2, 3, 4, 5],
':let', ['y', ({x}) => x * 3],
':while', ({y}) => y < 10,
':when', ({y}) => y % 2 === 0
], ({y}) => y)
t.deepEqual([...actual], expected)
})
test('REALLY long loops do not throw', (t) => {
function * numbers () {
let n = 0
while (true) {
yield n++
}
}
t.plan(1)
t.doesNotThrow(() => {
[...forc([
'x', numbers(),
':while', ({x}) => x < 100000
], ({x}) => [x])]
})
})
test('referencing a previous generator', (t) => {
function * numbers () {
yield 1
yield 2
yield 3
yield 4
yield 5
}
t.plan(1)
const actual = forc([
'x', numbers(),
'y', ({x}) => [x]
], ({x, y}) => x + y)
t.deepEqual([...actual], [2, 4, 6, 8, 10])
})
test('referencing a previous infinite generator', (t) => {
function * numbers () {
let n = 0
while (true) {
yield n++
}
}
t.plan(1)
const actual = forc([
'x', numbers(),
'y', ({x}) => [x],
':while', ({x}) => x < 5
], ({x, y}) => x + y)
t.deepEqual([...actual], [0, 2, 4, 6, 8])
})