about summary refs log tree commit diff
path: root/tvix/eval
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/eval')
-rw-r--r--tvix/eval/src/builtins/mod.rs8
-rw-r--r--tvix/eval/src/errors.rs10
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.nix4
-rw-r--r--tvix/eval/src/value/list.rs4
5 files changed, 27 insertions, 0 deletions
diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs
index 74215cd37c92..3598ac71f7db 100644
--- a/tvix/eval/src/builtins/mod.rs
+++ b/tvix/eval/src/builtins/mod.rs
@@ -75,6 +75,14 @@ fn pure_builtins() -> Vec<Builtin> {
             }
             Ok(Value::Integer(args[0].to_list()?.len() as i64))
         }),
+        Builtin::new("head", 1, |args, vm| {
+            force!(vm, &args[0], xs, {
+                match xs.to_list()?.get(0) {
+                    Some(x) => Ok(x.clone()),
+                    None => Err(ErrorKind::IndexOutOfBounds { index: 0 }),
+                }
+            })
+        }),
         Builtin::new("isAttrs", 1, |args, _| {
             Ok(Value::Bool(matches!(args[0], Value::Attrs(_))))
         }),
diff --git a/tvix/eval/src/errors.rs b/tvix/eval/src/errors.rs
index 086cc9d9052c..a84c931d0a4e 100644
--- a/tvix/eval/src/errors.rs
+++ b/tvix/eval/src/errors.rs
@@ -25,6 +25,11 @@ pub enum ErrorKind {
         name: String,
     },
 
+    // Attempted to index into a list beyond its boundaries.
+    IndexOutOfBounds {
+        index: usize,
+    },
+
     TypeError {
         expected: &'static str,
         actual: &'static str,
@@ -123,6 +128,10 @@ impl Error {
                 name
             ),
 
+            ErrorKind::IndexOutOfBounds { index } => {
+                format!("list index '{}' is out of bounds", index)
+            }
+
             ErrorKind::TypeError { expected, actual } => format!(
                 "expected value of type '{}', but found a '{}'",
                 expected, actual
@@ -208,6 +217,7 @@ to a missing value in the attribute set(s) included via `with`."#,
             ErrorKind::DuplicateAttrsKey { .. } => "E016",
             ErrorKind::ThunkForce(_) => "E017",
             ErrorKind::NotCoercibleToString { .. } => "E018",
+            ErrorKind::IndexOutOfBounds { .. } => "E019",
             ErrorKind::NotImplemented(_) => "E999",
         }
     }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.exp
new file mode 100644
index 000000000000..afe288459f2e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.exp
@@ -0,0 +1 @@
+[ "foo" 1 ]
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.nix
new file mode 100644
index 000000000000..1741a7aac4bb
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-head.nix
@@ -0,0 +1,4 @@
+[
+  (builtins.head [ "foo" ])
+  (builtins.head [ 1 2 3 ])
+]
diff --git a/tvix/eval/src/value/list.rs b/tvix/eval/src/value/list.rs
index da86fdda26ec..c00ddd4191ea 100644
--- a/tvix/eval/src/value/list.rs
+++ b/tvix/eval/src/value/list.rs
@@ -32,6 +32,10 @@ impl NixList {
         self.0.len()
     }
 
+    pub fn get(&self, i: usize) -> Option<&Value> {
+        self.0.get(i)
+    }
+
     pub fn construct(count: usize, stack_slice: Vec<Value>) -> Self {
         debug_assert!(
             count == stack_slice.len(),