about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-08-11T14·23+0300
committertazjin <tazjin@tvl.su>2022-08-26T15·06+0000
commitdd8f5f1dc8e314b1908d90620f69efed2b284072 (patch)
treec056a2489c8fd95dd15dc44fb8c5b944945be768
parentcf3e3b784bc322c7e2f032b0c803e42524bede38 (diff)
feat(tvix/eval): implement nested '?' operator r/4496
Implements the nested presence check operator for attribuet sets by
traversing the chain of lookups through instructions that push/pop
sequentially deeper attribute sets onto the stack.

Note that this commit introduces a bug in case of nested attributes
not being found, which is fixed in a later commit.

Change-Id: Ic8b4c8648736f6cb048e3aa52592e4d075bf0544
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6163
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
Reviewed-by: grfn <grfn@gws.fyi>
-rw-r--r--tvix/eval/src/compiler.rs38
1 files changed, 25 insertions, 13 deletions
diff --git a/tvix/eval/src/compiler.rs b/tvix/eval/src/compiler.rs
index 1d56e227f4..b9e850a36b 100644
--- a/tvix/eval/src/compiler.rs
+++ b/tvix/eval/src/compiler.rs
@@ -448,22 +448,34 @@ impl Compiler {
         //
         // Otherwise, the right hand side is the (only) key expression
         // itself and can be compiled directly.
-        let rhs = node.rhs().unwrap();
+        let mut next = node.rhs().unwrap();
+        let mut fragments = vec![];
+
+        loop {
+            if matches!(next.kind(), rnix::SyntaxKind::NODE_SELECT) {
+                // Keep nesting deeper until we encounter something
+                // different than `NODE_SELECT` on the left side. This is
+                // required because `rnix` parses nested keys as select
+                // expressions, instead of as a key expression.
+                //
+                // The parsed tree will nest something like `a.b.c.d.e.f`
+                // as (((((a, b), c), d), e), f).
+                fragments.push(next.last_child().unwrap());
+                next = next.first_child().unwrap();
+            } else {
+                self.compile_with_literal_ident(next)?;
+
+                for fragment in fragments.into_iter().rev() {
+                    println!("fragment: {}", fragment);
+                    self.chunk.add_op(OpCode::OpAttrsSelect);
+                    self.compile_with_literal_ident(fragment)?;
+                }
 
-        if matches!(rhs.kind(), rnix::SyntaxKind::NODE_SELECT) {
-            // Keep nesting deeper until we encounter something
-            // different than `NODE_SELECT` on the left side. This is
-            // required because `rnix` parses nested keys as select
-            // expressions, instead of as a key expression.
-            //
-            // The parsed tree will nest something like `a.b.c.d.e.f`
-            // as (((((a, b), c), d), e), f).
-            todo!("nested '?' check")
-        } else {
-            self.compile_with_literal_ident(rhs)?;
+                self.chunk.add_op(OpCode::OpAttrsIsSet);
+                break;
+            }
         }
 
-        self.chunk.add_op(OpCode::OpAttrsIsSet);
         Ok(())
     }