/rust/registry/src/index.crates.io-1949cf8c6b5b557f/dhall-0.13.0/src/builtins.rs
Line | Count | Source |
1 | | use std::collections::{BTreeMap, HashMap}; |
2 | | use std::convert::TryInto; |
3 | | |
4 | | use crate::operations::{BinOp, OpKind}; |
5 | | use crate::semantics::{nze, Hir, HirKind, Nir, NirKind, NzEnv, VarEnv}; |
6 | | use crate::syntax::Const::Type; |
7 | | use crate::syntax::{ |
8 | | Const, Expr, ExprKind, InterpolatedText, InterpolatedTextContents, Label, |
9 | | NaiveDouble, NumKind, Span, UnspannedExpr, V, |
10 | | }; |
11 | | use crate::{Ctxt, Parsed}; |
12 | | |
13 | | /// Built-ins |
14 | | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
15 | | pub enum Builtin { |
16 | | Bool, |
17 | | Natural, |
18 | | Integer, |
19 | | Double, |
20 | | Text, |
21 | | List, |
22 | | Optional, |
23 | | OptionalNone, |
24 | | NaturalBuild, |
25 | | NaturalFold, |
26 | | NaturalIsZero, |
27 | | NaturalEven, |
28 | | NaturalOdd, |
29 | | NaturalToInteger, |
30 | | NaturalShow, |
31 | | NaturalSubtract, |
32 | | IntegerToDouble, |
33 | | IntegerShow, |
34 | | IntegerNegate, |
35 | | IntegerClamp, |
36 | | DoubleShow, |
37 | | ListBuild, |
38 | | ListFold, |
39 | | ListLength, |
40 | | ListHead, |
41 | | ListLast, |
42 | | ListIndexed, |
43 | | ListReverse, |
44 | | TextShow, |
45 | | TextReplace, |
46 | | } |
47 | | |
48 | | impl Builtin { |
49 | 0 | pub fn parse(s: &str) -> Option<Self> { |
50 | | use Builtin::*; |
51 | 0 | match s { |
52 | 0 | "Bool" => Some(Bool), |
53 | 0 | "Natural" => Some(Natural), |
54 | 0 | "Integer" => Some(Integer), |
55 | 0 | "Double" => Some(Double), |
56 | 0 | "Text" => Some(Text), |
57 | 0 | "List" => Some(List), |
58 | 0 | "Optional" => Some(Optional), |
59 | 0 | "None" => Some(OptionalNone), |
60 | 0 | "Natural/build" => Some(NaturalBuild), |
61 | 0 | "Natural/fold" => Some(NaturalFold), |
62 | 0 | "Natural/isZero" => Some(NaturalIsZero), |
63 | 0 | "Natural/even" => Some(NaturalEven), |
64 | 0 | "Natural/odd" => Some(NaturalOdd), |
65 | 0 | "Natural/toInteger" => Some(NaturalToInteger), |
66 | 0 | "Natural/show" => Some(NaturalShow), |
67 | 0 | "Natural/subtract" => Some(NaturalSubtract), |
68 | 0 | "Integer/toDouble" => Some(IntegerToDouble), |
69 | 0 | "Integer/show" => Some(IntegerShow), |
70 | 0 | "Integer/negate" => Some(IntegerNegate), |
71 | 0 | "Integer/clamp" => Some(IntegerClamp), |
72 | 0 | "Double/show" => Some(DoubleShow), |
73 | 0 | "List/build" => Some(ListBuild), |
74 | 0 | "List/fold" => Some(ListFold), |
75 | 0 | "List/length" => Some(ListLength), |
76 | 0 | "List/head" => Some(ListHead), |
77 | 0 | "List/last" => Some(ListLast), |
78 | 0 | "List/indexed" => Some(ListIndexed), |
79 | 0 | "List/reverse" => Some(ListReverse), |
80 | 0 | "Text/show" => Some(TextShow), |
81 | 0 | "Text/replace" => Some(TextReplace), |
82 | 0 | _ => None, |
83 | | } |
84 | 0 | } |
85 | | } |
86 | | |
87 | | /// A partially applied builtin. |
88 | | /// Invariant: the evaluation of the given args must not be able to progress further |
89 | | #[derive(Debug, Clone)] |
90 | | pub struct BuiltinClosure<'cx> { |
91 | | env: NzEnv<'cx>, |
92 | | b: Builtin, |
93 | | /// Arguments applied to the closure so far. |
94 | | args: Vec<Nir<'cx>>, |
95 | | } |
96 | | |
97 | | impl<'cx> BuiltinClosure<'cx> { |
98 | 0 | pub fn new(b: Builtin, env: NzEnv<'cx>) -> NirKind<'cx> { |
99 | 0 | apply_builtin(b, Vec::new(), env) |
100 | 0 | } |
101 | 0 | pub fn apply(&self, a: Nir<'cx>) -> NirKind<'cx> { |
102 | | use std::iter::once; |
103 | 0 | let args = self.args.iter().cloned().chain(once(a)).collect(); |
104 | 0 | apply_builtin(self.b, args, self.env.clone()) |
105 | 0 | } |
106 | 0 | pub fn to_hirkind(&self, venv: VarEnv) -> HirKind<'cx> { |
107 | 0 | HirKind::Expr(self.args.iter().fold( |
108 | 0 | ExprKind::Builtin(self.b), |
109 | 0 | |acc, v| { |
110 | 0 | ExprKind::Op(OpKind::App( |
111 | 0 | Hir::new(HirKind::Expr(acc), Span::Artificial), |
112 | 0 | v.to_hir(venv), |
113 | 0 | )) |
114 | 0 | }, |
115 | | )) |
116 | 0 | } |
117 | | } |
118 | | |
119 | 0 | pub fn rc(x: UnspannedExpr) -> Expr { |
120 | 0 | Expr::new(x, Span::Artificial) |
121 | 0 | } |
122 | | |
123 | | // Ad-hoc macro to help construct the types of builtins |
124 | | macro_rules! make_type { |
125 | | (Type) => { rc(ExprKind::Const(Const::Type)) }; |
126 | | (Bool) => { rc(ExprKind::Builtin(Builtin::Bool)) }; |
127 | | (Natural) => { rc(ExprKind::Builtin(Builtin::Natural)) }; |
128 | | (Integer) => { rc(ExprKind::Builtin(Builtin::Integer)) }; |
129 | | (Double) => { rc(ExprKind::Builtin(Builtin::Double)) }; |
130 | | (Text) => { rc(ExprKind::Builtin(Builtin::Text)) }; |
131 | | ($var:ident) => { |
132 | | rc(ExprKind::Var(V(stringify!($var).into(), 0))) |
133 | | }; |
134 | | (Optional $ty:ident) => { |
135 | | rc(ExprKind::Op(OpKind::App( |
136 | | rc(ExprKind::Builtin(Builtin::Optional)), |
137 | | make_type!($ty) |
138 | | ))) |
139 | | }; |
140 | | (List $($rest:tt)*) => { |
141 | | rc(ExprKind::Op(OpKind::App( |
142 | | rc(ExprKind::Builtin(Builtin::List)), |
143 | | make_type!($($rest)*) |
144 | | ))) |
145 | | }; |
146 | | ({ $($label:ident : $ty:ident),* }) => {{ |
147 | | let mut kts = BTreeMap::new(); |
148 | | $( |
149 | | kts.insert( |
150 | | Label::from(stringify!($label)), |
151 | | make_type!($ty), |
152 | | ); |
153 | | )* |
154 | | rc(ExprKind::RecordType(kts)) |
155 | | }}; |
156 | | ($ty:ident -> $($rest:tt)*) => { |
157 | | rc(ExprKind::Pi( |
158 | | "_".into(), |
159 | | make_type!($ty), |
160 | | make_type!($($rest)*) |
161 | | )) |
162 | | }; |
163 | | (($($arg:tt)*) -> $($rest:tt)*) => { |
164 | | rc(ExprKind::Pi( |
165 | | "_".into(), |
166 | | make_type!($($arg)*), |
167 | | make_type!($($rest)*) |
168 | | )) |
169 | | }; |
170 | | (forall ($var:ident : $($ty:tt)*) -> $($rest:tt)*) => { |
171 | | rc(ExprKind::Pi( |
172 | | stringify!($var).into(), |
173 | | make_type!($($ty)*), |
174 | | make_type!($($rest)*) |
175 | | )) |
176 | | }; |
177 | | } |
178 | | |
179 | 0 | pub fn type_of_builtin<'cx>(cx: Ctxt<'cx>, b: Builtin) -> Hir<'cx> { |
180 | | use Builtin::*; |
181 | 0 | let expr = match b { |
182 | 0 | Bool | Natural | Integer | Double | Text => make_type!(Type), |
183 | 0 | List | Optional => make_type!( |
184 | | Type -> Type |
185 | | ), |
186 | | |
187 | 0 | NaturalFold => make_type!( |
188 | | Natural -> |
189 | | forall (natural: Type) -> |
190 | | forall (succ: natural -> natural) -> |
191 | | forall (zero: natural) -> |
192 | | natural |
193 | | ), |
194 | 0 | NaturalBuild => make_type!( |
195 | | (forall (natural: Type) -> |
196 | | forall (succ: natural -> natural) -> |
197 | | forall (zero: natural) -> |
198 | | natural) -> |
199 | | Natural |
200 | | ), |
201 | 0 | NaturalIsZero | NaturalEven | NaturalOdd => make_type!( |
202 | | Natural -> Bool |
203 | | ), |
204 | 0 | NaturalToInteger => make_type!(Natural -> Integer), |
205 | 0 | NaturalShow => make_type!(Natural -> Text), |
206 | 0 | NaturalSubtract => make_type!(Natural -> Natural -> Natural), |
207 | | |
208 | 0 | IntegerToDouble => make_type!(Integer -> Double), |
209 | 0 | IntegerShow => make_type!(Integer -> Text), |
210 | 0 | IntegerNegate => make_type!(Integer -> Integer), |
211 | 0 | IntegerClamp => make_type!(Integer -> Natural), |
212 | | |
213 | 0 | DoubleShow => make_type!(Double -> Text), |
214 | 0 | TextShow => make_type!(Text -> Text), |
215 | 0 | TextReplace => make_type!( |
216 | | forall (needle: Text) -> |
217 | | forall (replacement: Text) -> |
218 | | forall (haystack: Text) -> |
219 | | Text |
220 | | ), |
221 | 0 | ListBuild => make_type!( |
222 | | forall (a: Type) -> |
223 | | (forall (list: Type) -> |
224 | | forall (cons: a -> list -> list) -> |
225 | | forall (nil: list) -> |
226 | | list) -> |
227 | | List a |
228 | | ), |
229 | 0 | ListFold => make_type!( |
230 | | forall (a: Type) -> |
231 | | (List a) -> |
232 | | forall (list: Type) -> |
233 | | forall (cons: a -> list -> list) -> |
234 | | forall (nil: list) -> |
235 | | list |
236 | | ), |
237 | 0 | ListLength => make_type!(forall (a: Type) -> (List a) -> Natural), |
238 | | ListHead | ListLast => { |
239 | 0 | make_type!(forall (a: Type) -> (List a) -> Optional a) |
240 | | } |
241 | 0 | ListIndexed => make_type!( |
242 | | forall (a: Type) -> |
243 | | (List a) -> |
244 | | List { index: Natural, value: a } |
245 | | ), |
246 | 0 | ListReverse => make_type!( |
247 | | forall (a: Type) -> (List a) -> List a |
248 | | ), |
249 | | |
250 | 0 | OptionalNone => make_type!( |
251 | | forall (A: Type) -> Optional A |
252 | | ), |
253 | | }; |
254 | 0 | Parsed::from_expr_without_imports(expr) |
255 | 0 | .resolve(cx) |
256 | 0 | .unwrap() |
257 | 0 | .0 |
258 | 0 | } |
259 | | |
260 | | // Ad-hoc macro to help construct closures |
261 | | macro_rules! make_closure { |
262 | | (var($var:ident)) => {{ |
263 | | rc(ExprKind::Var(V( |
264 | | Label::from(stringify!($var)).into(), |
265 | | 0 |
266 | | ))) |
267 | | }}; |
268 | | (λ($var:tt : $($ty:tt)*) -> $($body:tt)*) => {{ |
269 | | let var = Label::from(stringify!($var)); |
270 | | let ty = make_closure!($($ty)*); |
271 | | let body = make_closure!($($body)*); |
272 | | rc(ExprKind::Lam(var, ty, body)) |
273 | | }}; |
274 | | (Type) => { |
275 | | rc(ExprKind::Const(Type)) |
276 | | }; |
277 | | (Natural) => { |
278 | | rc(ExprKind::Builtin(Builtin::Natural)) |
279 | | }; |
280 | | (List $($ty:tt)*) => {{ |
281 | | let ty = make_closure!($($ty)*); |
282 | | rc(ExprKind::Op(OpKind::App( |
283 | | rc(ExprKind::Builtin(Builtin::List)), |
284 | | ty |
285 | | ))) |
286 | | }}; |
287 | | (Some($($v:tt)*)) => { |
288 | | rc(ExprKind::SomeLit( |
289 | | make_closure!($($v)*) |
290 | | )) |
291 | | }; |
292 | | (1 + $($v:tt)*) => { |
293 | | rc(ExprKind::Op(OpKind::BinOp( |
294 | | BinOp::NaturalPlus, |
295 | | make_closure!($($v)*), |
296 | | rc(ExprKind::Num(NumKind::Natural(1))) |
297 | | ))) |
298 | | }; |
299 | | ([ $($head:tt)* ] # $($tail:tt)*) => {{ |
300 | | let head = make_closure!($($head)*); |
301 | | let tail = make_closure!($($tail)*); |
302 | | rc(ExprKind::Op(OpKind::BinOp( |
303 | | BinOp::ListAppend, |
304 | | rc(ExprKind::NEListLit(vec![head])), |
305 | | tail, |
306 | | ))) |
307 | | }}; |
308 | | } |
309 | | |
310 | | #[allow(clippy::cognitive_complexity)] |
311 | 0 | fn apply_builtin<'cx>( |
312 | 0 | b: Builtin, |
313 | 0 | args: Vec<Nir<'cx>>, |
314 | 0 | env: NzEnv<'cx>, |
315 | 0 | ) -> NirKind<'cx> { |
316 | 0 | let cx = env.cx(); |
317 | | use NirKind::*; |
318 | | use NumKind::{Bool, Double, Integer, Natural}; |
319 | | |
320 | | // Small helper enum |
321 | | enum Ret<'cx> { |
322 | | NirKind(NirKind<'cx>), |
323 | | Nir(Nir<'cx>), |
324 | | DoneAsIs, |
325 | | } |
326 | 0 | let make_closure = |e| { |
327 | 0 | Parsed::from_expr_without_imports(e) |
328 | 0 | .resolve(cx) |
329 | 0 | .unwrap() |
330 | 0 | .typecheck(cx) |
331 | 0 | .unwrap() |
332 | 0 | .as_hir() |
333 | 0 | .eval(env.clone()) |
334 | 0 | }; |
335 | | |
336 | 0 | let ret = match (b, args.as_slice()) { |
337 | 0 | (Builtin::Bool, []) |
338 | 0 | | (Builtin::Natural, []) |
339 | 0 | | (Builtin::Integer, []) |
340 | 0 | | (Builtin::Double, []) |
341 | 0 | | (Builtin::Text, []) => Ret::NirKind(BuiltinType(b)), |
342 | 0 | (Builtin::Optional, [t]) => Ret::NirKind(OptionalType(t.clone())), |
343 | 0 | (Builtin::List, [t]) => Ret::NirKind(ListType(t.clone())), |
344 | | |
345 | 0 | (Builtin::OptionalNone, [t]) => { |
346 | 0 | Ret::NirKind(EmptyOptionalLit(t.clone())) |
347 | | } |
348 | 0 | (Builtin::NaturalIsZero, [n]) => match &*n.kind() { |
349 | 0 | Num(Natural(n)) => Ret::NirKind(Num(Bool(*n == 0))), |
350 | 0 | _ => Ret::DoneAsIs, |
351 | | }, |
352 | 0 | (Builtin::NaturalEven, [n]) => match &*n.kind() { |
353 | 0 | Num(Natural(n)) => Ret::NirKind(Num(Bool(*n % 2 == 0))), |
354 | 0 | _ => Ret::DoneAsIs, |
355 | | }, |
356 | 0 | (Builtin::NaturalOdd, [n]) => match &*n.kind() { |
357 | 0 | Num(Natural(n)) => Ret::NirKind(Num(Bool(*n % 2 != 0))), |
358 | 0 | _ => Ret::DoneAsIs, |
359 | | }, |
360 | 0 | (Builtin::NaturalToInteger, [n]) => match &*n.kind() { |
361 | 0 | Num(Natural(n)) => Ret::NirKind(Num(Integer(*n as i64))), |
362 | 0 | _ => Ret::DoneAsIs, |
363 | | }, |
364 | 0 | (Builtin::NaturalShow, [n]) => match &*n.kind() { |
365 | 0 | Num(Natural(n)) => Ret::Nir(Nir::from_text(n)), |
366 | 0 | _ => Ret::DoneAsIs, |
367 | | }, |
368 | 0 | (Builtin::NaturalSubtract, [a, b]) => match (&*a.kind(), &*b.kind()) { |
369 | 0 | (Num(Natural(a)), Num(Natural(b))) => { |
370 | 0 | Ret::NirKind(Num(Natural(if b > a { b - a } else { 0 }))) |
371 | | } |
372 | 0 | (Num(Natural(0)), _) => Ret::Nir(b.clone()), |
373 | 0 | (_, Num(Natural(0))) => Ret::NirKind(Num(Natural(0))), |
374 | 0 | _ if a == b => Ret::NirKind(Num(Natural(0))), |
375 | 0 | _ => Ret::DoneAsIs, |
376 | | }, |
377 | 0 | (Builtin::IntegerShow, [n]) => match &*n.kind() { |
378 | 0 | Num(Integer(n)) => { |
379 | 0 | let s = if *n < 0 { |
380 | 0 | n.to_string() |
381 | | } else { |
382 | 0 | format!("+{}", n) |
383 | | }; |
384 | 0 | Ret::Nir(Nir::from_text(s)) |
385 | | } |
386 | 0 | _ => Ret::DoneAsIs, |
387 | | }, |
388 | 0 | (Builtin::IntegerToDouble, [n]) => match &*n.kind() { |
389 | 0 | Num(Integer(n)) => { |
390 | 0 | Ret::NirKind(Num(Double(NaiveDouble::from(*n as f64)))) |
391 | | } |
392 | 0 | _ => Ret::DoneAsIs, |
393 | | }, |
394 | 0 | (Builtin::IntegerNegate, [n]) => match &*n.kind() { |
395 | 0 | Num(Integer(n)) => Ret::NirKind(Num(Integer(-n))), |
396 | 0 | _ => Ret::DoneAsIs, |
397 | | }, |
398 | 0 | (Builtin::IntegerClamp, [n]) => match &*n.kind() { |
399 | 0 | Num(Integer(n)) => { |
400 | 0 | Ret::NirKind(Num(Natural((*n).try_into().unwrap_or(0)))) |
401 | | } |
402 | 0 | _ => Ret::DoneAsIs, |
403 | | }, |
404 | 0 | (Builtin::DoubleShow, [n]) => match &*n.kind() { |
405 | 0 | Num(Double(n)) => Ret::Nir(Nir::from_text(n)), |
406 | 0 | _ => Ret::DoneAsIs, |
407 | | }, |
408 | 0 | (Builtin::TextShow, [v]) => match &*v.kind() { |
409 | 0 | TextLit(tlit) => { |
410 | 0 | if let Some(s) = tlit.as_text() { |
411 | | // Printing InterpolatedText takes care of all the escaping |
412 | 0 | let txt: InterpolatedText<Expr> = |
413 | 0 | std::iter::once(InterpolatedTextContents::Text(s)) |
414 | 0 | .collect(); |
415 | 0 | Ret::Nir(Nir::from_text(txt)) |
416 | | } else { |
417 | 0 | Ret::DoneAsIs |
418 | | } |
419 | | } |
420 | 0 | _ => Ret::DoneAsIs, |
421 | | }, |
422 | 0 | (Builtin::TextReplace, [needle, replacement, haystack]) => { |
423 | | // Helper to match a Nir as a text literal |
424 | 0 | fn nir_to_string(n: &Nir) -> Option<String> { |
425 | 0 | match &*n.kind() { |
426 | 0 | TextLit(n_lit) => n_lit.as_text(), |
427 | 0 | _ => None, |
428 | | } |
429 | 0 | } |
430 | | |
431 | | // The needle needs to be fully evaluated as Text otherwise no |
432 | | // progress can be made |
433 | 0 | match nir_to_string(needle) { |
434 | | // When the needle is empty the haystack is returned untouched |
435 | 0 | Some(n) if n.is_empty() => Ret::Nir(haystack.clone()), |
436 | 0 | Some(n) => { |
437 | | // The haystack needs to be fully evaluated as Text otherwise no |
438 | | // progress can be made |
439 | 0 | if let Some(h) = nir_to_string(haystack) { |
440 | | // Fast case when replacement is fully evaluated |
441 | 0 | if let Some(r) = nir_to_string(replacement) { |
442 | 0 | Ret::Nir(Nir::from_text(h.replace(&n, &r))) |
443 | | } else { |
444 | | use itertools::Itertools; |
445 | | |
446 | 0 | let parts = h.split(&n).map(|s| { |
447 | 0 | InterpolatedTextContents::Text(s.to_string()) |
448 | 0 | }); |
449 | 0 | let replacement = InterpolatedTextContents::Expr( |
450 | 0 | replacement.clone(), |
451 | 0 | ); |
452 | | |
453 | 0 | Ret::Nir(Nir::from_kind(NirKind::TextLit( |
454 | 0 | nze::nir::TextLit::new(Itertools::intersperse( |
455 | 0 | parts, |
456 | 0 | replacement, |
457 | 0 | )), |
458 | 0 | ))) |
459 | | } |
460 | | } else { |
461 | 0 | Ret::DoneAsIs |
462 | | } |
463 | | } |
464 | 0 | _ => Ret::DoneAsIs, |
465 | | } |
466 | | } |
467 | 0 | (Builtin::ListLength, [_, l]) => match &*l.kind() { |
468 | 0 | EmptyListLit(_) => Ret::NirKind(Num(Natural(0))), |
469 | 0 | NEListLit(xs) => Ret::NirKind(Num(Natural(xs.len() as u64))), |
470 | 0 | _ => Ret::DoneAsIs, |
471 | | }, |
472 | 0 | (Builtin::ListHead, [_, l]) => match &*l.kind() { |
473 | 0 | EmptyListLit(n) => Ret::NirKind(EmptyOptionalLit(n.clone())), |
474 | 0 | NEListLit(xs) => { |
475 | 0 | Ret::NirKind(NEOptionalLit(xs.iter().next().unwrap().clone())) |
476 | | } |
477 | 0 | _ => Ret::DoneAsIs, |
478 | | }, |
479 | 0 | (Builtin::ListLast, [_, l]) => match &*l.kind() { |
480 | 0 | EmptyListLit(n) => Ret::NirKind(EmptyOptionalLit(n.clone())), |
481 | 0 | NEListLit(xs) => Ret::NirKind(NEOptionalLit( |
482 | 0 | xs.iter().rev().next().unwrap().clone(), |
483 | 0 | )), |
484 | 0 | _ => Ret::DoneAsIs, |
485 | | }, |
486 | 0 | (Builtin::ListReverse, [_, l]) => match &*l.kind() { |
487 | 0 | EmptyListLit(n) => Ret::NirKind(EmptyListLit(n.clone())), |
488 | 0 | NEListLit(xs) => { |
489 | 0 | Ret::NirKind(NEListLit(xs.iter().rev().cloned().collect())) |
490 | | } |
491 | 0 | _ => Ret::DoneAsIs, |
492 | | }, |
493 | 0 | (Builtin::ListIndexed, [t, l]) => { |
494 | 0 | match l.kind() { |
495 | | EmptyListLit(_) | NEListLit(_) => { |
496 | | // Construct the returned record type: { index: Natural, value: t } |
497 | 0 | let mut kts = HashMap::new(); |
498 | 0 | kts.insert( |
499 | 0 | "index".into(), |
500 | 0 | Nir::from_builtin(cx, Builtin::Natural), |
501 | | ); |
502 | 0 | kts.insert("value".into(), t.clone()); |
503 | 0 | let t = Nir::from_kind(RecordType(kts)); |
504 | | |
505 | | // Construct the new list, with added indices |
506 | 0 | let list = match l.kind() { |
507 | 0 | EmptyListLit(_) => EmptyListLit(t), |
508 | 0 | NEListLit(xs) => NEListLit( |
509 | 0 | xs.iter() |
510 | 0 | .enumerate() |
511 | 0 | .map(|(i, e)| { |
512 | 0 | let mut kvs = HashMap::new(); |
513 | 0 | kvs.insert( |
514 | 0 | "index".into(), |
515 | 0 | Nir::from_kind(Num(Natural(i as u64))), |
516 | | ); |
517 | 0 | kvs.insert("value".into(), e.clone()); |
518 | 0 | Nir::from_kind(RecordLit(kvs)) |
519 | 0 | }) |
520 | 0 | .collect(), |
521 | | ), |
522 | 0 | _ => unreachable!(), |
523 | | }; |
524 | 0 | Ret::NirKind(list) |
525 | | } |
526 | 0 | _ => Ret::DoneAsIs, |
527 | | } |
528 | | } |
529 | 0 | (Builtin::ListBuild, [t, f]) => { |
530 | 0 | let list_t = Nir::from_builtin(cx, Builtin::List).app(t.clone()); |
531 | 0 | Ret::Nir( |
532 | 0 | f.app(list_t) |
533 | 0 | .app( |
534 | 0 | make_closure(make_closure!( |
535 | 0 | λ(T : Type) -> |
536 | 0 | λ(a : var(T)) -> |
537 | 0 | λ(as : List var(T)) -> |
538 | 0 | [ var(a) ] # var(as) |
539 | 0 | )) |
540 | 0 | .app(t.clone()), |
541 | 0 | ) |
542 | 0 | .app(EmptyListLit(t.clone()).into_nir()), |
543 | 0 | ) |
544 | | } |
545 | 0 | (Builtin::ListFold, [_, l, _, cons, nil]) => match &*l.kind() { |
546 | 0 | EmptyListLit(_) => Ret::Nir(nil.clone()), |
547 | 0 | NEListLit(xs) => { |
548 | 0 | let mut v = nil.clone(); |
549 | 0 | for x in xs.iter().cloned().rev() { |
550 | 0 | v = cons.app(x).app(v); |
551 | 0 | } |
552 | 0 | Ret::Nir(v) |
553 | | } |
554 | 0 | _ => Ret::DoneAsIs, |
555 | | }, |
556 | 0 | (Builtin::NaturalBuild, [f]) => Ret::Nir( |
557 | 0 | f.app(Nir::from_builtin(cx, Builtin::Natural)) |
558 | 0 | .app(make_closure(make_closure!( |
559 | 0 | λ(x : Natural) -> |
560 | 0 | 1 + var(x) |
561 | 0 | ))) |
562 | 0 | .app(Num(Natural(0)).into_nir()), |
563 | 0 | ), |
564 | | |
565 | 0 | (Builtin::NaturalFold, [n, t, succ, zero]) => match &*n.kind() { |
566 | 0 | Num(Natural(0)) => Ret::Nir(zero.clone()), |
567 | 0 | Num(Natural(n)) => { |
568 | 0 | let fold = Nir::from_builtin(cx, Builtin::NaturalFold) |
569 | 0 | .app(Num(Natural(n - 1)).into_nir()) |
570 | 0 | .app(t.clone()) |
571 | 0 | .app(succ.clone()) |
572 | 0 | .app(zero.clone()); |
573 | 0 | Ret::Nir(succ.app(fold)) |
574 | | } |
575 | 0 | _ => Ret::DoneAsIs, |
576 | | }, |
577 | 0 | _ => Ret::DoneAsIs, |
578 | | }; |
579 | 0 | match ret { |
580 | 0 | Ret::NirKind(v) => v, |
581 | 0 | Ret::Nir(v) => v.kind().clone(), |
582 | 0 | Ret::DoneAsIs => AppliedBuiltin(BuiltinClosure { b, args, env }), |
583 | | } |
584 | 0 | } |
585 | | |
586 | | impl<'cx> std::cmp::PartialEq for BuiltinClosure<'cx> { |
587 | 0 | fn eq(&self, other: &Self) -> bool { |
588 | 0 | self.b == other.b && self.args == other.args |
589 | 0 | } |
590 | | } |
591 | | impl<'cx> std::cmp::Eq for BuiltinClosure<'cx> {} |
592 | | |
593 | | impl std::fmt::Display for Builtin { |
594 | 0 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
595 | | use Builtin::*; |
596 | 0 | f.write_str(match *self { |
597 | 0 | Bool => "Bool", |
598 | 0 | Natural => "Natural", |
599 | 0 | Integer => "Integer", |
600 | 0 | Double => "Double", |
601 | 0 | Text => "Text", |
602 | 0 | List => "List", |
603 | 0 | Optional => "Optional", |
604 | 0 | OptionalNone => "None", |
605 | 0 | NaturalBuild => "Natural/build", |
606 | 0 | NaturalFold => "Natural/fold", |
607 | 0 | NaturalIsZero => "Natural/isZero", |
608 | 0 | NaturalEven => "Natural/even", |
609 | 0 | NaturalOdd => "Natural/odd", |
610 | 0 | NaturalToInteger => "Natural/toInteger", |
611 | 0 | NaturalShow => "Natural/show", |
612 | 0 | NaturalSubtract => "Natural/subtract", |
613 | 0 | IntegerToDouble => "Integer/toDouble", |
614 | 0 | IntegerNegate => "Integer/negate", |
615 | 0 | IntegerClamp => "Integer/clamp", |
616 | 0 | IntegerShow => "Integer/show", |
617 | 0 | DoubleShow => "Double/show", |
618 | 0 | ListBuild => "List/build", |
619 | 0 | ListFold => "List/fold", |
620 | 0 | ListLength => "List/length", |
621 | 0 | ListHead => "List/head", |
622 | 0 | ListLast => "List/last", |
623 | 0 | ListIndexed => "List/indexed", |
624 | 0 | ListReverse => "List/reverse", |
625 | 0 | TextShow => "Text/show", |
626 | 0 | TextReplace => "Text/replace", |
627 | | }) |
628 | 0 | } |
629 | | } |