about summary refs log tree commit diff
path: root/tvix/eval/src/value/builtin.rs
blob: e876c235557ed458ed8aff3d05d80ab82dfe4bf6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//! This module implements the runtime representation of a Nix
//! builtin.
//!
//! Builtins are directly backed by Rust code operating on Nix values.

use crate::errors::EvalResult;

use super::Value;

use std::fmt::{Debug, Display};

pub type BuiltinFn = fn(arg: Vec<Value>) -> EvalResult<Value>;

/// Represents a single built-in function which directly executes Rust
/// code that operates on a Nix value.
///
/// Builtins are the only functions in Nix that have varying arities
/// (for example, `hasAttr` has an arity of 2, but `isAttrs` an arity
/// of 1). To facilitate this generically, builtins expect to be
/// called with a vector of Nix values corresponding to their
/// arguments in order.
///
/// Partially applied builtins act similar to closures in that they
/// "capture" the partially applied arguments, and are treated
/// specially when printing their representation etc.
#[derive(Clone)]
pub struct Builtin {
    name: &'static str,
    arity: usize,
    func: BuiltinFn,

    // Partially applied function arguments.
    partials: Vec<Value>,
}

impl Builtin {
    pub fn new(name: &'static str, arity: usize, func: BuiltinFn) -> Self {
        Builtin {
            name,
            arity,
            func,
            partials: vec![],
        }
    }

    pub fn name(&self) -> &'static str {
        self.name
    }

    /// Apply an additional argument to the builtin, which will either
    /// lead to execution of the function or to returning a partial
    /// builtin.
    pub fn apply(mut self, arg: Value) -> EvalResult<Value> {
        self.partials.push(arg);

        if self.partials.len() == self.arity {
            return (self.func)(self.partials);
        }

        // Function is not yet ready to be called.
        Ok(Value::Builtin(self))
    }
}

impl Debug for Builtin {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "builtin[{}]", self.name)
    }
}

impl Display for Builtin {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if !self.partials.is_empty() {
            f.write_str("<<primop-app>>")
        } else {
            f.write_str("<<primop>>")
        }
    }
}