diff options
Diffstat (limited to 'lib/mininix/builtins.nix')
-rw-r--r-- | lib/mininix/builtins.nix | 302 |
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 @@ | |||
1 | rec { | ||
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 | } | ||