Coverage Report

Created: 2026-03-14 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}