about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/eval/src/compiler.rs2
-rw-r--r--tvix/eval/src/opcode.rs1
-rw-r--r--tvix/eval/src/value/mod.rs10
-rw-r--r--tvix/eval/src/vm.rs18
4 files changed, 30 insertions, 1 deletions
diff --git a/tvix/eval/src/compiler.rs b/tvix/eval/src/compiler.rs
index 5da8444488..5b6f748dc7 100644
--- a/tvix/eval/src/compiler.rs
+++ b/tvix/eval/src/compiler.rs
@@ -224,7 +224,7 @@ impl Compiler {
             // otherwise we need to emit an instruction to construct
             // the attribute path.
             if key_count > 1 {
-                todo!("emit OpAttrPath(n) instruction")
+                self.chunk.add_op(OpCode::OpAttrPath(2));
             }
 
             // The value is just compiled as normal so that its
diff --git a/tvix/eval/src/opcode.rs b/tvix/eval/src/opcode.rs
index 0af8f23fc7..622a02ac85 100644
--- a/tvix/eval/src/opcode.rs
+++ b/tvix/eval/src/opcode.rs
@@ -32,6 +32,7 @@ pub enum OpCode {
 
     // Attribute sets
     OpAttrs(usize),
+    OpAttrPath(usize),
 
     // Lists
     OpList(usize),
diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs
index d00fc56e2a..8a95d00f41 100644
--- a/tvix/eval/src/value/mod.rs
+++ b/tvix/eval/src/value/mod.rs
@@ -21,6 +21,10 @@ pub enum Value {
     String(NixString),
     Attrs(Rc<NixAttrs>),
     List(NixList),
+
+    // Internal values that, while they technically exist at runtime,
+    // are never returned to or created directly by users.
+    AttrPath(Vec<NixString>),
 }
 
 impl Value {
@@ -41,6 +45,9 @@ impl Value {
             Value::String(_) => "string",
             Value::Attrs(_) => "set",
             Value::List(_) => "list",
+
+            // Internal types
+            Value::AttrPath(_) => "internal",
         }
     }
 
@@ -76,6 +83,9 @@ impl Display for Value {
             Value::String(s) => s.fmt(f),
             Value::Attrs(attrs) => attrs.fmt(f),
             Value::List(list) => list.fmt(f),
+
+            // internal types
+            Value::AttrPath(_) => f.write_str("internal"),
         }
     }
 }
diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs
index d6d3de154c..58ef67cb55 100644
--- a/tvix/eval/src/vm.rs
+++ b/tvix/eval/src/vm.rs
@@ -121,6 +121,7 @@ impl VM {
                 OpCode::OpTrue => self.push(Value::Bool(true)),
                 OpCode::OpFalse => self.push(Value::Bool(false)),
                 OpCode::OpAttrs(count) => self.run_attrset(count)?,
+                OpCode::OpAttrPath(count) => self.run_attr_path(count)?,
                 OpCode::OpList(count) => self.run_list(count)?,
                 OpCode::OpInterpolate(count) => self.run_interpolate(count)?,
             }
@@ -218,6 +219,23 @@ impl VM {
         Ok(())
     }
 
+    // Construct runtime representation of an attr path (essentially
+    // just a list of strings).
+    //
+    // The difference to the list construction operation is that this
+    // forces all elements into strings, as attribute set keys are
+    // required to be strict in Nix.
+    fn run_attr_path(&mut self, count: usize) -> EvalResult<()> {
+        let mut path = vec![NixString(String::new()); count];
+
+        for idx in 0..count {
+            path[count - idx - 1] = self.pop().as_string()?
+        }
+
+        self.push(Value::AttrPath(path));
+        Ok(())
+    }
+
     // Construct runtime representation of a list. Because the list
     // items are on the stack in reverse order, the vector is created
     // initialised and elements are directly assigned to their