diff options
-rw-r--r-- | tvix/eval/src/errors.rs | 3 | ||||
-rw-r--r-- | tvix/eval/src/opcode.rs | 1 | ||||
-rw-r--r-- | tvix/eval/src/vm.rs | 21 |
3 files changed, 24 insertions, 1 deletions
diff --git a/tvix/eval/src/errors.rs b/tvix/eval/src/errors.rs index e262d9def417..a5ba5bf40059 100644 --- a/tvix/eval/src/errors.rs +++ b/tvix/eval/src/errors.rs @@ -28,6 +28,9 @@ pub enum Error { // Unknown variable in statically known scope. UnknownStaticVariable(rnix::types::Ident), + + // Unknown variable in dynamic scope (with, rec, ...). + UnknownDynamicVariable(String), } impl Display for Error { diff --git a/tvix/eval/src/opcode.rs b/tvix/eval/src/opcode.rs index 7d2de537d522..7dcfea93aaf7 100644 --- a/tvix/eval/src/opcode.rs +++ b/tvix/eval/src/opcode.rs @@ -55,6 +55,7 @@ pub enum OpCode { // `with`-handling OpPushWith(usize), OpPopWith, + OpResolveWith, // Lists OpList(usize), diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs index 8a6959114fff..09e3aa247555 100644 --- a/tvix/eval/src/vm.rs +++ b/tvix/eval/src/vm.rs @@ -98,7 +98,7 @@ impl VM { #[cfg(feature = "disassembler")] let mut tracer = Tracer::new(); - loop { + 'dispatch: loop { let op = self.inc_ip(); match op { OpCode::OpConstant(idx) => { @@ -285,6 +285,25 @@ impl VM { OpCode::OpPopWith => { self.with_stack.pop(); } + + OpCode::OpResolveWith => { + let ident = self.pop().to_string()?; + + // Attempt to resolve the variable, starting at + // the back of the with_stack. + 'with: for idx in self.with_stack.iter().rev() { + let with = self.stack[*idx].as_attrs()?; + match with.select(ident.as_str()) { + None => continue 'with, + Some(val) => { + self.push(val.clone()); + continue 'dispatch; + } + } + } + + return Err(Error::UnknownDynamicVariable(ident.to_string())); + } } #[cfg(feature = "disassembler")] |