diff options
author | Vincent Ambo <mail@tazj.in> | 2022-08-11T11·12+0300 |
---|---|---|
committer | tazjin <tazjin@tvl.su> | 2022-08-25T16·00+0000 |
commit | 4b920912b8ea5ad963b76359c27902f6ece2ceec (patch) | |
tree | 2cff2672e3dd53a4669c291d1c54c41f87416f46 | |
parent | f3f8262637a522e783e6114c6882e31e7a68da17 (diff) |
feat(tvix/compiler): implement `&&` operator r/4485
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 <grfn@gws.fyi> Reviewed-by: sterni <sternenseemann@systemli.org>
Diffstat (limited to '')
-rw-r--r-- | tvix/eval/src/compiler.rs | 48 |
1 files changed, 41 insertions, 7 deletions
diff --git a/tvix/eval/src/compiler.rs b/tvix/eval/src/compiler.rs index e38b06b456c5..f03ebee530b8 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; |