146 lines
3.8 KiB
TypeScript
146 lines
3.8 KiB
TypeScript
import { op } from "../operators";
|
|
import { force } from "../thunk";
|
|
import { forceBool, forceFunction, forceInt, forceList } from "../type-assert";
|
|
import type { NixAttrs, NixList, NixValue } from "../types";
|
|
|
|
export const map =
|
|
(f: NixValue) =>
|
|
(list: NixValue): NixList => {
|
|
const forcedList = forceList(list);
|
|
if (forcedList.length) {
|
|
const func = forceFunction(f);
|
|
return forcedList.map(func);
|
|
}
|
|
return [];
|
|
};
|
|
|
|
export const filter =
|
|
(f: NixValue) =>
|
|
(list: NixValue): NixList => {
|
|
const forcedList = forceList(list);
|
|
if (forcedList.length) {
|
|
const func = forceFunction(f);
|
|
return forcedList.filter((e) => forceBool(func(e)));
|
|
}
|
|
return [];
|
|
};
|
|
|
|
export const length = (e: NixValue): bigint => {
|
|
const forced = force(e);
|
|
if (typeof forced === "string") return BigInt(forced.length);
|
|
return BigInt(forceList(forced).length);
|
|
};
|
|
|
|
export const head = (list: NixValue): NixValue => forceList(list)[0];
|
|
|
|
export const tail = (list: NixValue): NixList => forceList(list).slice(1);
|
|
|
|
export const elem =
|
|
(x: NixValue) =>
|
|
(xs: NixValue): boolean =>
|
|
forceList(xs).find((e) => op.eq(x, e)) !== undefined;
|
|
|
|
export const elemAt =
|
|
(xs: NixValue) =>
|
|
(n: NixValue): NixValue => {
|
|
const list = forceList(xs);
|
|
const idx = Number(forceInt(n));
|
|
|
|
if (idx < 0 || idx >= list.length) {
|
|
throw new RangeError(`Index ${idx} out of bounds for list of length ${list.length}`);
|
|
}
|
|
|
|
return list[idx];
|
|
};
|
|
|
|
export const concatLists = (lists: NixValue): NixList => {
|
|
return forceList(lists).reduce((acc: NixList, cur: NixValue) => {
|
|
return acc.concat(forceList(cur));
|
|
}, []);
|
|
};
|
|
|
|
export const concatMap =
|
|
(f: NixValue) =>
|
|
(lists: NixValue): NixList => {
|
|
const fn = forceFunction(f);
|
|
return forceList(lists).reduce((acc: NixList, cur: NixValue) => {
|
|
return acc.concat(force(fn(cur)) as NixList);
|
|
}, []);
|
|
};
|
|
|
|
export const foldlPrime =
|
|
(opFn: NixValue) =>
|
|
(nul: NixValue) =>
|
|
(list: NixValue): NixValue => {
|
|
const forcedOp = forceFunction(opFn);
|
|
return forceList(list).reduce((acc: NixValue, cur: NixValue) => {
|
|
return forceFunction(forcedOp(acc))(cur);
|
|
}, nul);
|
|
};
|
|
|
|
export const sort =
|
|
(cmp: NixValue) =>
|
|
(list: NixValue): NixList => {
|
|
const forcedList = [...forceList(list)];
|
|
const forcedCmp = forceFunction(cmp);
|
|
return forcedList.sort((a, b) => {
|
|
if (force(forceFunction(forcedCmp(a))(b))) return -1;
|
|
if (force(forceFunction(forcedCmp(b))(a))) return 1;
|
|
return 0;
|
|
});
|
|
};
|
|
|
|
export const partition =
|
|
(pred: NixValue) =>
|
|
(list: NixValue): NixAttrs => {
|
|
const forcedList = forceList(list);
|
|
const forcedPred = forceFunction(pred);
|
|
const right: NixList = [];
|
|
const wrong: NixList = [];
|
|
for (const elem of forcedList) {
|
|
if (force(forcedPred(elem))) {
|
|
right.push(elem);
|
|
} else {
|
|
wrong.push(elem);
|
|
}
|
|
}
|
|
return new Map<string, NixValue>([
|
|
["right", right],
|
|
["wrong", wrong],
|
|
]);
|
|
};
|
|
|
|
export const genList =
|
|
(f: NixValue) =>
|
|
(len: NixValue): NixList => {
|
|
const func = forceFunction(f);
|
|
const length = forceInt(len);
|
|
|
|
if (length < 0) {
|
|
throw new TypeError(`genList length must be non-negative integer, got ${length}`);
|
|
}
|
|
|
|
return [...Array(Number(length)).keys()].map((i) => func(BigInt(i)));
|
|
};
|
|
|
|
export const all =
|
|
(pred: NixValue) =>
|
|
(list: NixValue): boolean => {
|
|
const forcedList = forceList(list);
|
|
if (forcedList.length) {
|
|
const f = forceFunction(pred);
|
|
return forcedList.every((e) => forceBool(f(e)));
|
|
}
|
|
return true;
|
|
};
|
|
|
|
export const any =
|
|
(pred: NixValue) =>
|
|
(list: NixValue): boolean => {
|
|
// CppNix forces `pred` eagerly
|
|
const f = forceFunction(pred);
|
|
const forcedList = forceList(list);
|
|
// `false` when no element
|
|
return forcedList.some((e) => forceBool(f(e)));
|
|
};
|