more prelude functions
This commit is contained in:
parent
51b64da106
commit
3c06cac36c
|
|
@ -43,6 +43,27 @@ export const globalFunctions = {
|
|||
}
|
||||
},
|
||||
|
||||
// type predicates
|
||||
'string?': (v: any) => toValue(v).type === 'string',
|
||||
'number?': (v: any) => toValue(v).type === 'number',
|
||||
'boolean?': (v: any) => toValue(v).type === 'boolean',
|
||||
'array?': (v: any) => toValue(v).type === 'array',
|
||||
'dict?': (v: any) => toValue(v).type === 'dict',
|
||||
'function?': (v: any) => {
|
||||
const t = toValue(v).type
|
||||
return t === 'function' || t === 'native'
|
||||
},
|
||||
'null?': (v: any) => toValue(v).type === 'null',
|
||||
'some?': (v: any) => toValue(v).type !== 'null',
|
||||
|
||||
// boolean/logic
|
||||
not: (v: any) => !v,
|
||||
|
||||
// utilities
|
||||
inc: (n: number) => n + 1,
|
||||
dec: (n: number) => n - 1,
|
||||
identity: (v: any) => v,
|
||||
|
||||
// strings
|
||||
str: {
|
||||
join: (arr: string[], sep: string = ',') => arr.join(sep),
|
||||
|
|
@ -65,6 +86,10 @@ export const globalFunctions = {
|
|||
'pad-end': (str: string, length: number, pad: string = ' ') => str.padEnd(length, pad),
|
||||
lines: (str: string) => str.split('\n'),
|
||||
chars: (str: string) => str.split(''),
|
||||
'index-of': (str: string, search: string) => str.indexOf(search),
|
||||
'last-index-of': (str: string, search: string) => str.lastIndexOf(search),
|
||||
match: (str: string, regex: RegExp) => str.match(regex),
|
||||
'test?': (str: string, regex: RegExp) => regex.test(str),
|
||||
},
|
||||
|
||||
// list
|
||||
|
|
@ -96,6 +121,18 @@ export const globalFunctions = {
|
|||
// predicates
|
||||
'empty?': (list: any[]) => list.length === 0,
|
||||
'contains?': (list: any[], item: any) => list.includes(item),
|
||||
'any?': async (list: any[], cb: Function) => {
|
||||
for (const value of list) {
|
||||
if (await cb(value)) return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
'all?': async (list: any[], cb: Function) => {
|
||||
for (const value of list) {
|
||||
if (!await cb(value)) return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
// sequence operations
|
||||
reverse: (list: any[]) => list.slice().reverse(),
|
||||
sort: (list: any[], cb?: (a: any, b: any) => number) => list.slice().sort(cb),
|
||||
|
|
@ -112,6 +149,34 @@ export const globalFunctions = {
|
|||
append: (list: any[], item: any) => [...list, item],
|
||||
prepend: (list: any[], item: any) => [item, ...list],
|
||||
'index-of': (list: any[], item: any) => list.indexOf(item),
|
||||
// utilities
|
||||
sum: (list: any[]) => list.reduce((acc, x) => acc + x, 0),
|
||||
count: async (list: any[], cb: Function) => {
|
||||
let count = 0
|
||||
for (const value of list) {
|
||||
if (await cb(value)) count++
|
||||
}
|
||||
return count
|
||||
},
|
||||
partition: async (list: any[], cb: Function) => {
|
||||
const truthy: any[] = []
|
||||
const falsy: any[] = []
|
||||
for (const value of list) {
|
||||
if (await cb(value)) truthy.push(value)
|
||||
else falsy.push(value)
|
||||
}
|
||||
return [truthy, falsy]
|
||||
},
|
||||
compact: (list: any[]) => list.filter(x => x != null),
|
||||
'group-by': async (list: any[], cb: Function) => {
|
||||
const groups: Record<string, any[]> = {}
|
||||
for (const value of list) {
|
||||
const key = String(await cb(value))
|
||||
if (!groups[key]) groups[key] = []
|
||||
groups[key].push(value)
|
||||
}
|
||||
return groups
|
||||
},
|
||||
},
|
||||
|
||||
// collections
|
||||
|
|
@ -148,6 +213,21 @@ export const globalFunctions = {
|
|||
get: (dict: Record<string, any>, key: string, defaultValue: any = null) => dict[key] ?? defaultValue,
|
||||
merge: (...dicts: Record<string, any>[]) => Object.assign({}, ...dicts),
|
||||
'empty?': (dict: Record<string, any>) => Object.keys(dict).length === 0,
|
||||
map: async (dict: Record<string, any>, cb: Function) => {
|
||||
const result: Record<string, any> = {}
|
||||
for (const [key, value] of Object.entries(dict)) {
|
||||
result[key] = await cb(value, key)
|
||||
}
|
||||
return result
|
||||
},
|
||||
filter: async (dict: Record<string, any>, cb: Function) => {
|
||||
const result: Record<string, any> = {}
|
||||
for (const [key, value] of Object.entries(dict)) {
|
||||
if (await cb(value, key)) result[key] = value
|
||||
}
|
||||
return result
|
||||
},
|
||||
'from-entries': (entries: [string, any][]) => Object.fromEntries(entries),
|
||||
},
|
||||
|
||||
// math
|
||||
|
|
@ -161,6 +241,9 @@ export const globalFunctions = {
|
|||
pow: (base: number, exp: number) => Math.pow(base, exp),
|
||||
sqrt: (n: number) => Math.sqrt(n),
|
||||
random: () => Math.random(),
|
||||
clamp: (n: number, min: number, max: number) => Math.min(Math.max(n, min), max),
|
||||
sign: (n: number) => Math.sign(n),
|
||||
trunc: (n: number) => Math.trunc(n),
|
||||
// predicates
|
||||
'even?': (n: number) => n % 2 === 0,
|
||||
'odd?': (n: number) => n % 2 !== 0,
|
||||
|
|
|
|||
|
|
@ -87,6 +87,78 @@ describe('string operations', () => {
|
|||
test('chars splits into characters', async () => {
|
||||
await expect(`str.chars 'abc'`).toEvaluateTo(['a', 'b', 'c'], globalFunctions)
|
||||
})
|
||||
|
||||
test('index-of finds substring position', async () => {
|
||||
await expect(`str.index-of 'hello world' 'world'`).toEvaluateTo(6, globalFunctions)
|
||||
await expect(`str.index-of 'hello' 'bye'`).toEvaluateTo(-1, globalFunctions)
|
||||
})
|
||||
|
||||
test('last-index-of finds last occurrence', async () => {
|
||||
await expect(`str.last-index-of 'hello hello' 'hello'`).toEvaluateTo(6, globalFunctions)
|
||||
})
|
||||
})
|
||||
|
||||
describe('type predicates', () => {
|
||||
test('string? checks for string type', async () => {
|
||||
await expect(`string? 'hello'`).toEvaluateTo(true, globalFunctions)
|
||||
await expect(`string? 42`).toEvaluateTo(false, globalFunctions)
|
||||
})
|
||||
|
||||
test('number? checks for number type', async () => {
|
||||
await expect(`number? 42`).toEvaluateTo(true, globalFunctions)
|
||||
await expect(`number? 'hello'`).toEvaluateTo(false, globalFunctions)
|
||||
})
|
||||
|
||||
test('boolean? checks for boolean type', async () => {
|
||||
await expect(`boolean? true`).toEvaluateTo(true, globalFunctions)
|
||||
await expect(`boolean? 42`).toEvaluateTo(false, globalFunctions)
|
||||
})
|
||||
|
||||
test('array? checks for array type', async () => {
|
||||
await expect(`array? [1 2 3]`).toEvaluateTo(true, globalFunctions)
|
||||
await expect(`array? 42`).toEvaluateTo(false, globalFunctions)
|
||||
})
|
||||
|
||||
test('dict? checks for dict type', async () => {
|
||||
await expect(`dict? [a=1]`).toEvaluateTo(true, globalFunctions)
|
||||
await expect(`dict? []`).toEvaluateTo(false, globalFunctions)
|
||||
})
|
||||
|
||||
test('null? checks for null type', async () => {
|
||||
await expect(`null? null`).toEvaluateTo(true, globalFunctions)
|
||||
await expect(`null? 42`).toEvaluateTo(false, globalFunctions)
|
||||
})
|
||||
|
||||
test('some? checks for non-null', async () => {
|
||||
await expect(`some? 42`).toEvaluateTo(true, globalFunctions)
|
||||
await expect(`some? null`).toEvaluateTo(false, globalFunctions)
|
||||
})
|
||||
})
|
||||
|
||||
describe('boolean logic', () => {
|
||||
test('not negates value', async () => {
|
||||
await expect(`not true`).toEvaluateTo(false, globalFunctions)
|
||||
await expect(`not false`).toEvaluateTo(true, globalFunctions)
|
||||
await expect(`not 42`).toEvaluateTo(false, globalFunctions)
|
||||
await expect(`not null`).toEvaluateTo(true, globalFunctions)
|
||||
})
|
||||
})
|
||||
|
||||
describe('utilities', () => {
|
||||
test('inc increments by 1', async () => {
|
||||
await expect(`inc 5`).toEvaluateTo(6, globalFunctions)
|
||||
await expect(`inc -1`).toEvaluateTo(0, globalFunctions)
|
||||
})
|
||||
|
||||
test('dec decrements by 1', async () => {
|
||||
await expect(`dec 5`).toEvaluateTo(4, globalFunctions)
|
||||
await expect(`dec 0`).toEvaluateTo(-1, globalFunctions)
|
||||
})
|
||||
|
||||
test('identity returns value as-is', async () => {
|
||||
await expect(`identity 42`).toEvaluateTo(42, globalFunctions)
|
||||
await expect(`identity 'hello'`).toEvaluateTo('hello', globalFunctions)
|
||||
})
|
||||
})
|
||||
|
||||
describe('introspection', () => {
|
||||
|
|
@ -262,6 +334,64 @@ describe('collections', () => {
|
|||
await expect(`list.index-of [1 2 3] 2`).toEvaluateTo(1, globalFunctions)
|
||||
await expect(`list.index-of [1 2 3] 5`).toEvaluateTo(-1, globalFunctions)
|
||||
})
|
||||
|
||||
test('list.any? checks if any element matches', async () => {
|
||||
await expect(`
|
||||
gt-three = do x: x > 3 end
|
||||
list.any? [1 2 4 5] gt-three
|
||||
`).toEvaluateTo(true, globalFunctions)
|
||||
await expect(`
|
||||
gt-ten = do x: x > 10 end
|
||||
list.any? [1 2 3] gt-ten
|
||||
`).toEvaluateTo(false, globalFunctions)
|
||||
})
|
||||
|
||||
test('list.all? checks if all elements match', async () => {
|
||||
await expect(`
|
||||
positive = do x: x > 0 end
|
||||
list.all? [1 2 3] positive
|
||||
`).toEvaluateTo(true, globalFunctions)
|
||||
await expect(`
|
||||
positive = do x: x > 0 end
|
||||
list.all? [1 -2 3] positive
|
||||
`).toEvaluateTo(false, globalFunctions)
|
||||
})
|
||||
|
||||
test('list.sum adds all numbers', async () => {
|
||||
await expect(`list.sum [1 2 3 4]`).toEvaluateTo(10, globalFunctions)
|
||||
await expect(`list.sum []`).toEvaluateTo(0, globalFunctions)
|
||||
})
|
||||
|
||||
test('list.count counts matching elements', async () => {
|
||||
await expect(`
|
||||
gt-two = do x: x > 2 end
|
||||
list.count [1 2 3 4 5] gt-two
|
||||
`).toEvaluateTo(3, globalFunctions)
|
||||
})
|
||||
|
||||
test('list.partition splits array by predicate', async () => {
|
||||
await expect(`
|
||||
gt-two = do x: x > 2 end
|
||||
list.partition [1 2 3 4 5] gt-two
|
||||
`).toEvaluateTo([[3, 4, 5], [1, 2]], globalFunctions)
|
||||
})
|
||||
|
||||
test('list.compact removes null values', async () => {
|
||||
await expect(`list.compact [1 null 2 null 3]`).toEvaluateTo([1, 2, 3], globalFunctions)
|
||||
})
|
||||
|
||||
test('list.group-by groups by key function', async () => {
|
||||
await expect(`
|
||||
get-type = do x:
|
||||
if (string? x):
|
||||
'str'
|
||||
else:
|
||||
'num'
|
||||
end
|
||||
end
|
||||
list.group-by ['a' 1 'b' 2] get-type
|
||||
`).toEvaluateTo({ str: ['a', 'b'], num: [1, 2] }, globalFunctions)
|
||||
})
|
||||
})
|
||||
|
||||
describe('enumerables', () => {
|
||||
|
|
@ -343,6 +473,24 @@ describe('dict operations', () => {
|
|||
test('dict.merge combines dicts', async () => {
|
||||
await expect(`dict.merge [a=1] [b=2]`).toEvaluateTo({ a: 1, b: 2 }, globalFunctions)
|
||||
})
|
||||
|
||||
test('dict.map transforms values', async () => {
|
||||
await expect(`
|
||||
double = do v k: v * 2 end
|
||||
dict.map [a=1 b=2] double
|
||||
`).toEvaluateTo({ a: 2, b: 4 }, globalFunctions)
|
||||
})
|
||||
|
||||
test('dict.filter keeps matching entries', async () => {
|
||||
await expect(`
|
||||
gt-one = do v k: v > 1 end
|
||||
dict.filter [a=1 b=2 c=3] gt-one
|
||||
`).toEvaluateTo({ b: 2, c: 3 }, globalFunctions)
|
||||
})
|
||||
|
||||
test('dict.from-entries creates dict from array', async () => {
|
||||
await expect(`dict.from-entries [['a' 1] ['b' 2]]`).toEvaluateTo({ a: 1, b: 2 }, globalFunctions)
|
||||
})
|
||||
})
|
||||
|
||||
describe('math operations', () => {
|
||||
|
|
@ -405,6 +553,23 @@ describe('math operations', () => {
|
|||
await expect(`math.zero? 0`).toEvaluateTo(true, globalFunctions)
|
||||
await expect(`math.zero? 5`).toEvaluateTo(false, globalFunctions)
|
||||
})
|
||||
|
||||
test('math.clamp restricts value to range', async () => {
|
||||
await expect(`math.clamp 5 0 10`).toEvaluateTo(5, globalFunctions)
|
||||
await expect(`math.clamp -5 0 10`).toEvaluateTo(0, globalFunctions)
|
||||
await expect(`math.clamp 15 0 10`).toEvaluateTo(10, globalFunctions)
|
||||
})
|
||||
|
||||
test('math.sign returns sign of number', async () => {
|
||||
await expect(`math.sign 5`).toEvaluateTo(1, globalFunctions)
|
||||
await expect(`math.sign -5`).toEvaluateTo(-1, globalFunctions)
|
||||
await expect(`math.sign 0`).toEvaluateTo(0, globalFunctions)
|
||||
})
|
||||
|
||||
test('math.trunc truncates decimal', async () => {
|
||||
await expect(`math.trunc 3.7`).toEvaluateTo(3, globalFunctions)
|
||||
await expect(`math.trunc -3.7`).toEvaluateTo(-3, globalFunctions)
|
||||
})
|
||||
})
|
||||
|
||||
// describe('echo', () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user