about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/eval/src/compiler.rs8
-rw-r--r--tvix/eval/src/opcode.rs3
-rw-r--r--tvix/eval/src/vm.rs15
3 files changed, 25 insertions, 1 deletions
diff --git a/tvix/eval/src/compiler.rs b/tvix/eval/src/compiler.rs
index 5ad8215b10d3..5da844448860 100644
--- a/tvix/eval/src/compiler.rs
+++ b/tvix/eval/src/compiler.rs
@@ -32,6 +32,12 @@ impl Compiler {
                 self.compile_string(op)
             }
 
+            // The interpolation node is just a wrapper around the
+            // inner value of a fragment, it only requires unwrapping.
+            rnix::SyntaxKind::NODE_STRING_INTERPOL => {
+                self.compile(node.first_child().expect("TODO (should not be possible)"))
+            }
+
             rnix::SyntaxKind::NODE_BIN_OP => {
                 let op = rnix::types::BinOp::cast(node).expect("TODO (should not be possible)");
                 self.compile_binop(op)
@@ -112,7 +118,7 @@ impl Compiler {
         }
 
         if count != 1 {
-            todo!("assemble string interpolation instruction")
+            self.chunk.add_op(OpCode::OpInterpolate(count));
         }
 
         Ok(())
diff --git a/tvix/eval/src/opcode.rs b/tvix/eval/src/opcode.rs
index adfa2433fcc3..0af8f23fc79d 100644
--- a/tvix/eval/src/opcode.rs
+++ b/tvix/eval/src/opcode.rs
@@ -35,4 +35,7 @@ pub enum OpCode {
 
     // Lists
     OpList(usize),
+
+    // Strings
+    OpInterpolate(usize),
 }
diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs
index d328900a34c5..e4597807cea4 100644
--- a/tvix/eval/src/vm.rs
+++ b/tvix/eval/src/vm.rs
@@ -118,6 +118,7 @@ impl VM {
                 OpCode::OpFalse => self.push(Value::Bool(false)),
                 OpCode::OpAttrs(count) => self.run_attrset(count)?,
                 OpCode::OpList(count) => self.run_list(count)?,
+                OpCode::OpInterpolate(count) => self.run_interpolate(count)?,
             }
 
             if self.ip == self.chunk.code.len() {
@@ -154,6 +155,20 @@ impl VM {
         self.push(Value::List(NixList(list)));
         Ok(())
     }
+
+    // Interpolate string fragments by popping the specified number of
+    // fragments of the stack, evaluating them to strings, and pushing
+    // the concatenated result string back on the stack.
+    fn run_interpolate(&mut self, count: usize) -> EvalResult<()> {
+        let mut out = String::new();
+
+        for _ in 0..count {
+            out.push_str(&self.pop().as_string()?.0);
+        }
+
+        self.push(Value::String(NixString(out)));
+        Ok(())
+    }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq)]