From a93933b487cf710400c9126d97450264138695df Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 9 Aug 2022 17:44:34 +0300 Subject: feat(tvix): implement string interpolation This adds a new instruction which assembles an interpolated string from a specified number of fragments, which are already going to be located on the stack in the right position. This will raise a type error if any of the fragments do not evaluate to a string. Change-Id: I5756248fa3e9fcc3d063c14db40b332f7e20a588 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6098 Tested-by: BuildkiteCI Reviewed-by: grfn --- tvix/eval/src/compiler.rs | 8 +++++++- tvix/eval/src/opcode.rs | 3 +++ tvix/eval/src/vm.rs | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) (limited to 'tvix') diff --git a/tvix/eval/src/compiler.rs b/tvix/eval/src/compiler.rs index 5ad8215b10..5da8444488 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 adfa2433fc..0af8f23fc7 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 d328900a34..e4597807ce 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)] -- cgit 1.4.1