From 4b920912b8ea5ad963b76359c27902f6ece2ceec Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 11 Aug 2022 14:12:58 +0300 Subject: feat(tvix/compiler): implement `&&` operator This logical operator is implemented in terms of jumping operations and thus requires slightly different treatment than other binary operators. Change-Id: Ib3d768b70dd7e16014c9b47d770aa74eec60ae92 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6150 Tested-by: BuildkiteCI Reviewed-by: grfn Reviewed-by: sterni --- tvix/eval/src/compiler.rs | 48 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) (limited to 'tvix/eval/src/compiler.rs') diff --git a/tvix/eval/src/compiler.rs b/tvix/eval/src/compiler.rs index e38b06b456..f03ebee530 100644 --- a/tvix/eval/src/compiler.rs +++ b/tvix/eval/src/compiler.rs @@ -19,7 +19,7 @@ use crate::opcode::{CodeIdx, OpCode}; use crate::value::Value; use rnix; -use rnix::types::{EntryHolder, TokenWrapper, TypedNode, Wrapper}; +use rnix::types::{BinOpKind, EntryHolder, TokenWrapper, TypedNode, Wrapper}; struct Compiler { chunk: Chunk, @@ -142,11 +142,21 @@ impl Compiler { } fn compile_binop(&mut self, op: rnix::types::BinOp) -> EvalResult<()> { + // Short-circuiting logical operators, which are under the + // same node type as NODE_BIN_OP, but need to be handled + // separately (i.e. before compiling the expressions used for + // standard binary operators). + match op.operator().unwrap() { + BinOpKind::And => return self.compile_and(op), + BinOpKind::Implication => todo!(), + BinOpKind::Or => todo!(), + + _ => {} + }; + self.compile(op.lhs().unwrap())?; self.compile(op.rhs().unwrap())?; - use rnix::types::BinOpKind; - match op.operator().unwrap() { BinOpKind::Add => self.chunk.add_op(OpCode::OpAdd), BinOpKind::Sub => self.chunk.add_op(OpCode::OpSub), @@ -160,16 +170,15 @@ impl Compiler { BinOpKind::MoreOrEq => self.chunk.add_op(OpCode::OpMoreOrEq), BinOpKind::Concat => self.chunk.add_op(OpCode::OpConcat), - BinOpKind::And => todo!(), - BinOpKind::Or => todo!(), - BinOpKind::Implication => todo!(), - BinOpKind::NotEqual => { self.chunk.add_op(OpCode::OpEqual); self.chunk.add_op(OpCode::OpInvert) } BinOpKind::IsSet => todo!("? operator"), + + // Handled by separate branch above. + BinOpKind::And | BinOpKind::Implication | BinOpKind::Or => unreachable!(), }; Ok(()) @@ -320,6 +329,31 @@ impl Compiler { Ok(()) } + fn compile_and(&mut self, node: rnix::types::BinOp) -> EvalResult<()> { + debug_assert!( + matches!(node.operator(), Some(BinOpKind::And)), + "compile_and called with wrong operator kind: {:?}", + node.operator(), + ); + + // Leave left-hand side value on the stack. + self.compile(node.lhs().unwrap())?; + + // If this value is false, jump over the right-hand side - the + // whole expression is false. + let end_idx = self.chunk.add_op(OpCode::OpJumpIfFalse(0)); + + // Otherwise, remove the previous value and leave the + // right-hand side on the stack. Its result is now the value + // of the whole expression. + self.chunk.add_op(OpCode::OpPop); + self.compile(node.rhs().unwrap())?; + + self.patch_jump(end_idx); + + Ok(()) + } + fn patch_jump(&mut self, idx: CodeIdx) { let offset = self.chunk.code.len() - 1 - idx.0; -- cgit 1.4.1