about summary refs log tree commit diff
path: root/tvix
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-08-14T22·13+0300
committertazjin <tazjin@tvl.su>2022-08-31T22·26+0000
commit59f50dc81a6afe8d2cd77d96ffb1d92f40c971cc (patch)
tree452ca647895b8bd13804b0c10a98101d19ccf5f0 /tvix
parent911fb96eca84a3059ecc11af9c3ac6fdacc28166 (diff)
feat(tvix/eval): Implement OpResolveWith instruction r/4556
Change-Id: I4d2a69f28a6b6199b3ff48ef81135e7da9fe1c3b
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6222
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
Reviewed-by: grfn <grfn@gws.fyi>
Diffstat (limited to 'tvix')
-rw-r--r--tvix/eval/src/errors.rs3
-rw-r--r--tvix/eval/src/opcode.rs1
-rw-r--r--tvix/eval/src/vm.rs21
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")]