compose_eval/expression/
binary.rs1use crate::vm::Machine;
2use crate::{Eval, Evaluated, ValueEvaluatedExtensions};
3use compose_library::diag::{bail, At, SourceResult};
4use compose_library::{ops, Value};
5use compose_syntax::ast;
6use compose_syntax::ast::{AstNode, BinOp};
7
8impl Eval for ast::Binary<'_> {
9 fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
10 let op = match self.op() {
11 BinOp::Add => |l, r, _heap| ops::add(l, r),
12 BinOp::Sub => |l, r, _heap| ops::sub(l, r),
13 BinOp::Mul => |l, r, _heap| ops::mul(l, r),
14 BinOp::Lt => |l, r, _heap| ops::lt(l, r),
15 BinOp::Gt => |l, r, _heap| ops::gt(l, r),
16 BinOp::Gte => |l, r, _heap| ops::gte(l, r),
17 BinOp::Eq => |l, r, heap| ops::eq(l, r, heap),
18 BinOp::Neq => |l, r, heap| ops::neq(l, r, heap),
19 BinOp::Mod => |l, r, _heap| ops::mod_(l, r),
20 BinOp::And => |l, r, _heap| ops::logical_and(l, r),
21 BinOp::Or => |l, r, _heap| ops::logical_or(l, r),
22 other => bail!(
23 self.span(),
24 "unsupported binary operator `{}`",
25 other.descriptive_name()
26 ),
27 };
28
29 let l = self.lhs();
30 let lhs = l.eval(vm)?;
31
32 if self.op().short_circuits(&lhs.value) {
34 return Ok(lhs.value.mutable());
35 }
36
37 let r = self.rhs();
38 let rhs = r.eval(vm)?;
39
40 Ok(op(&lhs.value, &rhs.value, &vm.heap).at(self.span())?.mutable())
41 }
42}
43
44trait ShortCircuits {
45 fn short_circuits(&self, val: &Value) -> bool;
46}
47
48impl ShortCircuits for BinOp {
49 fn short_circuits(&self, val: &Value) -> bool {
50 #[allow(clippy::match_like_matches_macro)]
51 match (self, val) {
52 (BinOp::And, Value::Bool(false)) => true,
53 (BinOp::Or, Value::Bool(true)) => true,
54 _ => false,
55 }
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use crate::test::assert_eval;
62 use compose_library::Value;
63
64 #[test]
65 fn test_addition() {
66 assert_eq!(assert_eval("2 + 4"), Value::Int(6));
67 assert_eq!(assert_eval("2 + 4 + 6"), Value::Int(12));
68 assert_eq!(assert_eval("0 + 9884 + 2171"), Value::Int(12055));
69 }
70
71 #[test]
72 fn test_multiplication() {
73 assert_eq!(assert_eval("2 * 4"), Value::Int(8));
74 assert_eq!(assert_eval("2 * 4 * 6"), Value::Int(48));
75 assert_eq!(assert_eval("0 * 9884 * 2171"), Value::Int(0));
76 }
77
78 #[test]
79 fn test_mixed() {
80 assert_eq!(assert_eval("2 + 4 * 6"), Value::Int(26));
81 assert_eq!(assert_eval("2 * 4 + 6"), Value::Int(14));
82 }
83
84 #[test]
85 fn test_assignment() {
86 assert_eq!(assert_eval("let x; x = 6; x"), Value::Int(6));
87 }
88}