From ba61dfd69504ec6263a9dee9931d93adeb6f3142 Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Mon, 7 Jul 2025 21:52:08 +0200 Subject: Initialize repository --- lib/mininix/builtins.nix | 302 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 lib/mininix/builtins.nix (limited to 'lib/mininix/builtins.nix') diff --git a/lib/mininix/builtins.nix b/lib/mininix/builtins.nix new file mode 100644 index 0000000..9c7ed32 --- /dev/null +++ b/lib/mininix/builtins.nix @@ -0,0 +1,302 @@ +rec { + inherit true false null functionArgs typeOf seq deepSeq bitAnd bitOr bitXor floor ceil; # from the minimal prelude + + abort = _: null null; # we ignore the provided message + throw = abort; # same here + + head = xs: (__mn_list_match xs).head; + tail = xs: (__mn_list_match xs).tail; + + __mn_matchAttr = f: as: f (__mn_attr_match as); + __mn_matchList = f: xs: f (__mn_list_match xs); + __mn_matchString = f: s: f (__mn_string_match s); + + __mn_foldr = op: nul: + __mn_matchList ({ head, tail, empty }: + if empty then nul else op head (__mn_foldr op nul tail)); + + # foldl' should really be strict. But if we do that (using seq), the + # complexity of this function suddenly morphs from linear to + # exponential, which is way worse than not actually being strict. + foldl' = op: nul: + __mn_matchList + ({ head, tail, empty }: + if empty then nul else + let v = op nul head; in + seq v (foldl' op v tail)); + map = f: + __mn_matchList ({ head, tail, empty }: + if empty then [ ] else [ (f head) ] ++ map f tail); + elem = x: any (y: x == y); + elemAt = xs: n: assert n >= 0; + let go = xs: n: if n == 0 then head xs else go (tail xs) (n - 1); + in go xs n; + length = __mn_matchList ({ head, tail, empty }: + if empty then 0 else 1 + length tail); + sort = __mn_mergesort; + any = f: __mn_matchList ({ head, tail, empty }: !empty && (f head || any f tail)); + all = f: __mn_matchList ({ head, tail, empty }: !empty -> (f head && all f tail)); + concatLists = + __mn_matchList ({ head, tail, empty }: + if empty then [ ] else head ++ concatLists tail); + concatMap = f: xss: concatLists (map f xss); + concatStringsSep = sep: + __mn_matchList ({ head, tail, empty }: + if empty then "" else if tail == [ ] then head + else head + sep + concatStringsSep sep tail); + filter = f: + __mn_matchList ({ head, tail, empty }: + if empty then [ ] else (if f head then [ head ] else [ ]) ++ filter f tail); + groupBy = f: xs: + let update = x: acc: acc // { ${f x} = [ x ] ++ (acc.${f x} or [ ]); }; + in __mn_foldr update { } xs; + partition = f: groupBy (x: if f x then "right" else "wrong"); + + hasAttr = x: as: as ? ${x}; + getAttr = x: as: as.${x}; + attrNames = __mn_matchAttr ({ key, tail, empty, ... }: + if empty then [ ] else [ key ] ++ attrNames tail); + attrValues = __mn_matchAttr ({ head, tail, empty, ... }: + if empty then [ ] else [ head ] ++ attrValues tail); + mapAttrs = f: __mn_matchAttr ({ key, head, tail, empty }: + if empty then { } else + mapAttrs f tail // { ${key} = f key head; }); + removeAttrs = __mn_foldr (x: as': __mn_attr_delete as' x); + zipAttrsWith = f: ass: mapAttrs f (__mn_zipAttrs ass); + catAttrs = x: + __mn_matchList ({ head, tail, empty }: + if empty then [ ] + else (if head ? ${x} then [ head.${x} ] else [ ]) ++ catAttrs x tail); + listToAttrs = + __mn_foldr (attr: as': as' // { ${attr.name} = attr.value; }) { }; + intersectAttrs = e1: e2: + __mn_matchAttr + ({ key, head, tail, empty }: + if empty then { } else + (if e2 ? ${key} then { ${key} = e2.${key}; } else { }) // + intersectAttrs tail (__mn_attr_delete e2 key)) + e1; + + lessThan = x: y: x < y; # documentation is misleading, not only for numbers + add = x: y: x + y; + mul = x: y: x * y; + div = x: y: x / y; + sub = x: y: x - y; + genList = gen: n: + let + aux = off: if off >= n then [ ] else + [ (gen off) ] ++ aux (off + 1); + in + aux 0; + + __mn_genericClosure = { operator, seen, startSet }: + __mn_matchList + ({ head, tail, empty }: + if empty then [ ] else + if seen head.key + then __mn_genericClosure { inherit operator seen; startSet = tail; } + else [ head ] ++ __mn_genericClosure { + inherit operator; + seen = k: k == head.key || seen k; + startSet = tail ++ operator head; + }) + startSet; + genericClosure = { operator, startSet }: + __mn_genericClosure { inherit operator startSet; seen = _: false; }; + + isAttrs = e: typeOf e == "set"; + isBool = e: typeOf e == "bool"; + isFloat = e: typeOf e == "float"; + isFunction = e: typeOf e == "lambda"; + isInt = e: typeOf e == "int"; + isList = e: typeOf e == "list"; + isNull = e: typeOf e == "null"; + isString = e: typeOf e == "string"; + + toString = e: + if isAttrs e then + if e ? __toString then e.__toString e else e.outPath + else if isBool e then + if e then "1" else "" + else if isFloat e then + __mn_float_toString e + else if isInt e then + __mn_int_toString e + else if isList e then + concatStringsSep " " (map toString e) + else if isNull e then + "" + else if isString e then + e + else abort null; + + stringLength = + __mn_matchString ({ head, tail, empty }: + if empty then 0 else 1 + stringLength tail); + + substring = start: assert start >= 0; len: + __mn_matchString ({ head, tail, empty }: + if empty || len == 0 then "" else + if start > 0 + then substring (start - 1) len tail + else head + substring 0 (len - 1) tail); + + replaceStrings = from: to: s: + __mn_matchList + ({ head, tail, empty }: + let from = if empty then [ ] else [ head ] ++ tail; in + __mn_matchList + ({ head, tail, empty }: + let to = if empty then [ ] else [ head ] ++ tail; in + assert length from == length to; + __mn_strings_replace from to s) + to) + from; + + __mn_strings_replace = subsFrom: subsTo: s: + let go = __mn_strings_replace subsFrom subsTo; in + __mn_strings_replace_aux go subsFrom subsTo s; + + __mn_strings_replace_aux = go: subsFrom: subsTo: s: + __mn_matchList + ({ head, tail, empty }: + if empty + then + __mn_matchString + ({ head, tail, empty }: if empty then "" else head + go tail) + s + else + let subFrom = head; subsFrom' = tail; in + __mn_matchList + ({ head, tail, ... }: + let subTo = head; subsTo' = tail; in + if subFrom == "" + then + # We can only ask ourselves why, but it is so -- in Nix: + # replaceStrings ["" "a"] ["X" "_"] "asdf" ~> "XaXsXdXfX" + # and so we emulate this 'behavior' + subTo + __mn_matchString + ({ head, tail, empty }: + if empty then "" else head + go tail) + s + else + ({ ok, rest }: + if ok + then subTo + go rest + else __mn_strings_replace_aux go subsFrom' subsTo' s) + (__mn_string_chopPrefix subFrom s)) + subsTo) + subsFrom; + + __mn_string_chopPrefix = prefix: s: + __mn_matchString + ({ head, tail, empty }: + if empty then { ok = true; rest = s; } else + let prefix = head; prefix' = tail; in __mn_matchString + ({ head, tail, empty }: + if empty || prefix != head then { ok = false; rest = null; } else + __mn_string_chopPrefix prefix' tail) + s) + prefix; + + __mn_string_drop = n: s: + if n <= 0 then s else + __mn_matchString + ({ tail, empty, ... }: + if empty then "" else + __mn_string_drop (n - 1) tail) + s; + + __mn_float_toString = x: + let + sign = x < 0; + abs = __mn_abs x; + int = floor abs; + dec = __mn_nearestEven ((abs - int) * 1000000); + in + (if sign then "-" else "") + + __mn_int_toString int + "." + __mn_int_toString dec; + __mn_int_toString = x: (if x < 0 then "-" else "") + + ( + let d10 = __mn_quotRem (__mn_abs x) 10; in + (if d10.quot != 0 then toString d10.quot else "") + + (if d10.rem == 0 then "0" else + if d10.rem == 1 then "1" else + if d10.rem == 2 then "2" else + if d10.rem == 3 then "3" else + if d10.rem == 4 then "4" else + if d10.rem == 5 then "5" else + if d10.rem == 6 then "6" else + if d10.rem == 7 then "7" else + if d10.rem == 8 then "8" else + if d10.rem == 9 then "9" else + abort null) + ); + + __mn_quotRem = x: y: + let quot = x / y; in + { inherit quot; rem = x - quot * y; }; + __mn_abs = x: if x < 0 then -x else x; + + __mn_attr_insertNew = as: x: e: + if x == null then { } else + assert !(as ? ${x}); as // __mn_singleton x e; + __mn_attr_has_aux = d: e: + if typeOf d != "set" then false else __mn_attr_has_prim d e; + __mn_attr_has = e: + __mn_matchList ({ head, tail, empty }: + if empty then true else + if __mn_attr_has_aux e head then __mn_attr_has e.${head} tail + else false); + __mn_attr_select = e: + __mn_matchList ({ head, tail, empty }: + if empty then e + else __mn_attr_select e.${head} tail); + __mn_attr_selectOr = e: as: d: + if __mn_attr_has e as + then __mn_attr_select e as + else d; + __mn_assert = e1: e2: + if e1 then e2 else abort null; + + __mn_consAttrs = as: acc: + __mn_foldr + (x: acc: acc // { + ${x} = [ as.${x} ] ++ (acc.${x} or [ ]); + }) + acc + (attrNames as); + __mn_zipAttrs = __mn_foldr __mn_consAttrs { }; + + # Old merge sort algorithm, taken from GHC.Internal.Data.OldList. + __mn_mergesort = cmp: xs: __mn_mergesort' cmp (__mn_singletons xs); + __mn_singletons = map (x: [ x ]); + __mn_mergesort' = cmp: xs: + __mn_matchList + ({ head, tail, empty }: + if empty then [ ] else if tail == [ ] then head else + __mn_mergesort' cmp (__mn_mergePairs cmp xs)) + xs; + __mn_mergePairs = cmp: + __mn_matchList ({ head, tail, empty }: if empty then [ ] else + let xs' = head; in __mn_matchList + ({ head, tail, empty }: + if empty then [ xs' ] else + let ys' = head; xss' = tail; in + [ (__mn_merge cmp xs' ys') ] ++ __mn_mergePairs cmp xss') + tail); + __mn_merge = cmp: xs: ys: + __mn_matchList + ({ head, tail, empty }: + if empty then ys else + let x = head; xs' = tail; in + __mn_matchList + ({ head, tail, empty }: + if empty then xs else + let y = head; ys' = tail; in + if cmp y x + then [ y ] ++ __mn_merge cmp xs ys' # y < x, i.e., x > y + else [ x ] ++ __mn_merge cmp xs' ys) + ys) + xs; +} -- cgit v1.2.3