Files
nix-js/nix-js/runtime-ts/src/builtins/list.ts

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)));
};