From 175eb975059e5a36444453d7b63839c5339cd003 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 9 Aug 2022 18:49:05 +0300 Subject: feat(tvix/eval): construct internal attribute path representation This is required for constructing nested attribute sets at runtime. There'll be quite a lot of optimisation potential with this solution eventually, if it should turn out to be a bottleneck. This introduces a conceptual change, in that the `Value` enum is now an enum representing "all runtime values" instead of "all Nix language types". This makes sense in general, as this type will also contain Chunk representations etc. which are not exposed to users. Change-Id: Ic5f72b2a0965b146c6a451efad34c6a81ca1aad8 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6103 Reviewed-by: grfn Tested-by: BuildkiteCI --- tvix/eval/src/compiler.rs | 2 +- tvix/eval/src/opcode.rs | 1 + tvix/eval/src/value/mod.rs | 10 ++++++++++ tvix/eval/src/vm.rs | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/tvix/eval/src/compiler.rs b/tvix/eval/src/compiler.rs index 5da8444488..5b6f748dc7 100644 --- a/tvix/eval/src/compiler.rs +++ b/tvix/eval/src/compiler.rs @@ -224,7 +224,7 @@ impl Compiler { // otherwise we need to emit an instruction to construct // the attribute path. if key_count > 1 { - todo!("emit OpAttrPath(n) instruction") + self.chunk.add_op(OpCode::OpAttrPath(2)); } // The value is just compiled as normal so that its diff --git a/tvix/eval/src/opcode.rs b/tvix/eval/src/opcode.rs index 0af8f23fc7..622a02ac85 100644 --- a/tvix/eval/src/opcode.rs +++ b/tvix/eval/src/opcode.rs @@ -32,6 +32,7 @@ pub enum OpCode { // Attribute sets OpAttrs(usize), + OpAttrPath(usize), // Lists OpList(usize), diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs index d00fc56e2a..8a95d00f41 100644 --- a/tvix/eval/src/value/mod.rs +++ b/tvix/eval/src/value/mod.rs @@ -21,6 +21,10 @@ pub enum Value { String(NixString), Attrs(Rc), List(NixList), + + // Internal values that, while they technically exist at runtime, + // are never returned to or created directly by users. + AttrPath(Vec), } impl Value { @@ -41,6 +45,9 @@ impl Value { Value::String(_) => "string", Value::Attrs(_) => "set", Value::List(_) => "list", + + // Internal types + Value::AttrPath(_) => "internal", } } @@ -76,6 +83,9 @@ impl Display for Value { Value::String(s) => s.fmt(f), Value::Attrs(attrs) => attrs.fmt(f), Value::List(list) => list.fmt(f), + + // internal types + Value::AttrPath(_) => f.write_str("internal"), } } } diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs index d6d3de154c..58ef67cb55 100644 --- a/tvix/eval/src/vm.rs +++ b/tvix/eval/src/vm.rs @@ -121,6 +121,7 @@ impl VM { OpCode::OpTrue => self.push(Value::Bool(true)), OpCode::OpFalse => self.push(Value::Bool(false)), OpCode::OpAttrs(count) => self.run_attrset(count)?, + OpCode::OpAttrPath(count) => self.run_attr_path(count)?, OpCode::OpList(count) => self.run_list(count)?, OpCode::OpInterpolate(count) => self.run_interpolate(count)?, } @@ -218,6 +219,23 @@ impl VM { Ok(()) } + // Construct runtime representation of an attr path (essentially + // just a list of strings). + // + // The difference to the list construction operation is that this + // forces all elements into strings, as attribute set keys are + // required to be strict in Nix. + fn run_attr_path(&mut self, count: usize) -> EvalResult<()> { + let mut path = vec![NixString(String::new()); count]; + + for idx in 0..count { + path[count - idx - 1] = self.pop().as_string()? + } + + self.push(Value::AttrPath(path)); + Ok(()) + } + // Construct runtime representation of a list. Because the list // items are on the stack in reverse order, the vector is created // initialised and elements are directly assigned to their -- cgit 1.4.1