about summary refs log tree commit diff
path: root/tvix/eval/src/value/builtin.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/eval/src/value/builtin.rs')
-rw-r--r--tvix/eval/src/value/builtin.rs66
1 files changed, 66 insertions, 0 deletions
diff --git a/tvix/eval/src/value/builtin.rs b/tvix/eval/src/value/builtin.rs
new file mode 100644
index 0000000000..8d7e821361
--- /dev/null
+++ b/tvix/eval/src/value/builtin.rs
@@ -0,0 +1,66 @@
+//! 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 {
+    /// 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.
+        return 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>>")
+        }
+    }
+}