aboutsummaryrefslogtreecommitdiffstats
path: root/lib/mininix/builtins.nix
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mininix/builtins.nix')
-rw-r--r--lib/mininix/builtins.nix302
1 files changed, 302 insertions, 0 deletions
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 @@
1rec {
2 inherit true false null functionArgs typeOf seq deepSeq bitAnd bitOr bitXor floor ceil; # from the minimal prelude
3
4 abort = _: null null; # we ignore the provided message
5 throw = abort; # same here
6
7 head = xs: (__mn_list_match xs).head;
8 tail = xs: (__mn_list_match xs).tail;
9
10 __mn_matchAttr = f: as: f (__mn_attr_match as);
11 __mn_matchList = f: xs: f (__mn_list_match xs);
12 __mn_matchString = f: s: f (__mn_string_match s);
13
14 __mn_foldr = op: nul:
15 __mn_matchList ({ head, tail, empty }:
16 if empty then nul else op head (__mn_foldr op nul tail));
17
18 # foldl' should really be strict. But if we do that (using seq), the
19 # complexity of this function suddenly morphs from linear to
20 # exponential, which is way worse than not actually being strict.
21 foldl' = op: nul:
22 __mn_matchList
23 ({ head, tail, empty }:
24 if empty then nul else
25 let v = op nul head; in
26 seq v (foldl' op v tail));
27 map = f:
28 __mn_matchList ({ head, tail, empty }:
29 if empty then [ ] else [ (f head) ] ++ map f tail);
30 elem = x: any (y: x == y);
31 elemAt = xs: n: assert n >= 0;
32 let go = xs: n: if n == 0 then head xs else go (tail xs) (n - 1);
33 in go xs n;
34 length = __mn_matchList ({ head, tail, empty }:
35 if empty then 0 else 1 + length tail);
36 sort = __mn_mergesort;
37 any = f: __mn_matchList ({ head, tail, empty }: !empty && (f head || any f tail));
38 all = f: __mn_matchList ({ head, tail, empty }: !empty -> (f head && all f tail));
39 concatLists =
40 __mn_matchList ({ head, tail, empty }:
41 if empty then [ ] else head ++ concatLists tail);
42 concatMap = f: xss: concatLists (map f xss);
43 concatStringsSep = sep:
44 __mn_matchList ({ head, tail, empty }:
45 if empty then "" else if tail == [ ] then head
46 else head + sep + concatStringsSep sep tail);
47 filter = f:
48 __mn_matchList ({ head, tail, empty }:
49 if empty then [ ] else (if f head then [ head ] else [ ]) ++ filter f tail);
50 groupBy = f: xs:
51 let update = x: acc: acc // { ${f x} = [ x ] ++ (acc.${f x} or [ ]); };
52 in __mn_foldr update { } xs;
53 partition = f: groupBy (x: if f x then "right" else "wrong");
54
55 hasAttr = x: as: as ? ${x};
56 getAttr = x: as: as.${x};
57 attrNames = __mn_matchAttr ({ key, tail, empty, ... }:
58 if empty then [ ] else [ key ] ++ attrNames tail);
59 attrValues = __mn_matchAttr ({ head, tail, empty, ... }:
60 if empty then [ ] else [ head ] ++ attrValues tail);
61 mapAttrs = f: __mn_matchAttr ({ key, head, tail, empty }:
62 if empty then { } else
63 mapAttrs f tail // { ${key} = f key head; });
64 removeAttrs = __mn_foldr (x: as': __mn_attr_delete as' x);
65 zipAttrsWith = f: ass: mapAttrs f (__mn_zipAttrs ass);
66 catAttrs = x:
67 __mn_matchList ({ head, tail, empty }:
68 if empty then [ ]
69 else (if head ? ${x} then [ head.${x} ] else [ ]) ++ catAttrs x tail);
70 listToAttrs =
71 __mn_foldr (attr: as': as' // { ${attr.name} = attr.value; }) { };
72 intersectAttrs = e1: e2:
73 __mn_matchAttr
74 ({ key, head, tail, empty }:
75 if empty then { } else
76 (if e2 ? ${key} then { ${key} = e2.${key}; } else { }) //
77 intersectAttrs tail (__mn_attr_delete e2 key))
78 e1;
79
80 lessThan = x: y: x < y; # documentation is misleading, not only for numbers
81 add = x: y: x + y;
82 mul = x: y: x * y;
83 div = x: y: x / y;
84 sub = x: y: x - y;
85 genList = gen: n:
86 let
87 aux = off: if off >= n then [ ] else
88 [ (gen off) ] ++ aux (off + 1);
89 in
90 aux 0;
91
92 __mn_genericClosure = { operator, seen, startSet }:
93 __mn_matchList
94 ({ head, tail, empty }:
95 if empty then [ ] else
96 if seen head.key
97 then __mn_genericClosure { inherit operator seen; startSet = tail; }
98 else [ head ] ++ __mn_genericClosure {
99 inherit operator;
100 seen = k: k == head.key || seen k;
101 startSet = tail ++ operator head;
102 })
103 startSet;
104 genericClosure = { operator, startSet }:
105 __mn_genericClosure { inherit operator startSet; seen = _: false; };
106
107 isAttrs = e: typeOf e == "set";
108 isBool = e: typeOf e == "bool";
109 isFloat = e: typeOf e == "float";
110 isFunction = e: typeOf e == "lambda";
111 isInt = e: typeOf e == "int";
112 isList = e: typeOf e == "list";
113 isNull = e: typeOf e == "null";
114 isString = e: typeOf e == "string";
115
116 toString = e:
117 if isAttrs e then
118 if e ? __toString then e.__toString e else e.outPath
119 else if isBool e then
120 if e then "1" else ""
121 else if isFloat e then
122 __mn_float_toString e
123 else if isInt e then
124 __mn_int_toString e
125 else if isList e then
126 concatStringsSep " " (map toString e)
127 else if isNull e then
128 ""
129 else if isString e then
130 e
131 else abort null;
132
133 stringLength =
134 __mn_matchString ({ head, tail, empty }:
135 if empty then 0 else 1 + stringLength tail);
136
137 substring = start: assert start >= 0; len:
138 __mn_matchString ({ head, tail, empty }:
139 if empty || len == 0 then "" else
140 if start > 0
141 then substring (start - 1) len tail
142 else head + substring 0 (len - 1) tail);
143
144 replaceStrings = from: to: s:
145 __mn_matchList
146 ({ head, tail, empty }:
147 let from = if empty then [ ] else [ head ] ++ tail; in
148 __mn_matchList
149 ({ head, tail, empty }:
150 let to = if empty then [ ] else [ head ] ++ tail; in
151 assert length from == length to;
152 __mn_strings_replace from to s)
153 to)
154 from;
155
156 __mn_strings_replace = subsFrom: subsTo: s:
157 let go = __mn_strings_replace subsFrom subsTo; in
158 __mn_strings_replace_aux go subsFrom subsTo s;
159
160 __mn_strings_replace_aux = go: subsFrom: subsTo: s:
161 __mn_matchList
162 ({ head, tail, empty }:
163 if empty
164 then
165 __mn_matchString
166 ({ head, tail, empty }: if empty then "" else head + go tail)
167 s
168 else
169 let subFrom = head; subsFrom' = tail; in
170 __mn_matchList
171 ({ head, tail, ... }:
172 let subTo = head; subsTo' = tail; in
173 if subFrom == ""
174 then
175 # We can only ask ourselves why, but it is so -- in Nix:
176 # replaceStrings ["" "a"] ["X" "_"] "asdf" ~> "XaXsXdXfX"
177 # and so we emulate this 'behavior'
178 subTo + __mn_matchString
179 ({ head, tail, empty }:
180 if empty then "" else head + go tail)
181 s
182 else
183 ({ ok, rest }:
184 if ok
185 then subTo + go rest
186 else __mn_strings_replace_aux go subsFrom' subsTo' s)
187 (__mn_string_chopPrefix subFrom s))
188 subsTo)
189 subsFrom;
190
191 __mn_string_chopPrefix = prefix: s:
192 __mn_matchString
193 ({ head, tail, empty }:
194 if empty then { ok = true; rest = s; } else
195 let prefix = head; prefix' = tail; in __mn_matchString
196 ({ head, tail, empty }:
197 if empty || prefix != head then { ok = false; rest = null; } else
198 __mn_string_chopPrefix prefix' tail)
199 s)
200 prefix;
201
202 __mn_string_drop = n: s:
203 if n <= 0 then s else
204 __mn_matchString
205 ({ tail, empty, ... }:
206 if empty then "" else
207 __mn_string_drop (n - 1) tail)
208 s;
209
210 __mn_float_toString = x:
211 let
212 sign = x < 0;
213 abs = __mn_abs x;
214 int = floor abs;
215 dec = __mn_nearestEven ((abs - int) * 1000000);
216 in
217 (if sign then "-" else "") +
218 __mn_int_toString int + "." + __mn_int_toString dec;
219 __mn_int_toString = x: (if x < 0 then "-" else "") +
220 (
221 let d10 = __mn_quotRem (__mn_abs x) 10; in
222 (if d10.quot != 0 then toString d10.quot else "") +
223 (if d10.rem == 0 then "0" else
224 if d10.rem == 1 then "1" else
225 if d10.rem == 2 then "2" else
226 if d10.rem == 3 then "3" else
227 if d10.rem == 4 then "4" else
228 if d10.rem == 5 then "5" else
229 if d10.rem == 6 then "6" else
230 if d10.rem == 7 then "7" else
231 if d10.rem == 8 then "8" else
232 if d10.rem == 9 then "9" else
233 abort null)
234 );
235
236 __mn_quotRem = x: y:
237 let quot = x / y; in
238 { inherit quot; rem = x - quot * y; };
239 __mn_abs = x: if x < 0 then -x else x;
240
241 __mn_attr_insertNew = as: x: e:
242 if x == null then { } else
243 assert !(as ? ${x}); as // __mn_singleton x e;
244 __mn_attr_has_aux = d: e:
245 if typeOf d != "set" then false else __mn_attr_has_prim d e;
246 __mn_attr_has = e:
247 __mn_matchList ({ head, tail, empty }:
248 if empty then true else
249 if __mn_attr_has_aux e head then __mn_attr_has e.${head} tail
250 else false);
251 __mn_attr_select = e:
252 __mn_matchList ({ head, tail, empty }:
253 if empty then e
254 else __mn_attr_select e.${head} tail);
255 __mn_attr_selectOr = e: as: d:
256 if __mn_attr_has e as
257 then __mn_attr_select e as
258 else d;
259 __mn_assert = e1: e2:
260 if e1 then e2 else abort null;
261
262 __mn_consAttrs = as: acc:
263 __mn_foldr
264 (x: acc: acc // {
265 ${x} = [ as.${x} ] ++ (acc.${x} or [ ]);
266 })
267 acc
268 (attrNames as);
269 __mn_zipAttrs = __mn_foldr __mn_consAttrs { };
270
271 # Old merge sort algorithm, taken from GHC.Internal.Data.OldList.
272 __mn_mergesort = cmp: xs: __mn_mergesort' cmp (__mn_singletons xs);
273 __mn_singletons = map (x: [ x ]);
274 __mn_mergesort' = cmp: xs:
275 __mn_matchList
276 ({ head, tail, empty }:
277 if empty then [ ] else if tail == [ ] then head else
278 __mn_mergesort' cmp (__mn_mergePairs cmp xs))
279 xs;
280 __mn_mergePairs = cmp:
281 __mn_matchList ({ head, tail, empty }: if empty then [ ] else
282 let xs' = head; in __mn_matchList
283 ({ head, tail, empty }:
284 if empty then [ xs' ] else
285 let ys' = head; xss' = tail; in
286 [ (__mn_merge cmp xs' ys') ] ++ __mn_mergePairs cmp xss')
287 tail);
288 __mn_merge = cmp: xs: ys:
289 __mn_matchList
290 ({ head, tail, empty }:
291 if empty then ys else
292 let x = head; xs' = tail; in
293 __mn_matchList
294 ({ head, tail, empty }:
295 if empty then xs else
296 let y = head; ys' = tail; in
297 if cmp y x
298 then [ y ] ++ __mn_merge cmp xs ys' # y < x, i.e., x > y
299 else [ x ] ++ __mn_merge cmp xs' ys)
300 ys)
301 xs;
302}