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