compose_eval/expression/
control_flow.rs

1use crate::expression::bindings::destructure_pattern;
2use crate::vm::{FlowEvent, Tracked};
3use crate::{Eval, Evaluated, Machine};
4use compose_library::diag::{At, SourceResult};
5use compose_library::{BindingKind, IterValue, Value, ValueIterator, Visibility};
6use compose_syntax::ast;
7use compose_syntax::ast::AstNode;
8
9impl Eval for ast::Conditional<'_> {
10    fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
11        if eval_condition(self.condition(), vm)? {
12            return self.consequent().eval(vm);
13        }
14
15        for alternate in self.cond_alternates() {
16            if eval_condition(alternate.condition(), vm)? {
17                return alternate.consequent().eval(vm);
18            }
19        }
20
21        if let Some(cond_else) = self.cond_else() {
22            return cond_else.consequent().eval(vm);
23        }
24
25        Ok(Evaluated::unit())
26    }
27}
28
29impl Eval for ast::WhileLoop<'_> {
30    fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
31        let mut output = Value::unit();
32        let flow = vm.flow.take();
33        while eval_condition(self.condition(), vm)? {
34            output = self.body().eval(vm)?.value;
35
36            match &vm.flow {
37                None => {}
38                Some(FlowEvent::Break(_, value)) => {
39                    if let Some(value) = value {
40                        output = value.clone();
41                    }
42                    vm.flow = None;
43                    break;
44                }
45                Some(FlowEvent::Continue(_)) => vm.flow = None,
46                Some(FlowEvent::Return(..)) => break,
47            }
48        }
49
50        if let Some(flow) = flow {
51            vm.flow = Some(flow);
52        }
53
54        Ok(Evaluated::mutable(output))
55    }
56}
57
58impl Eval for ast::ForLoop<'_> {
59    //noinspection RsUnnecessaryQualifications - False positive
60    fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
61        let root_guard = vm.temp_root_guard();
62        let mut output = Value::unit();
63        let pattern = self.binding();
64        let iterable_expr = self.iterable();
65        let iterator = {
66            let value = iterable_expr
67                .eval(root_guard.vm)?
68                .track_tmp_root(root_guard.vm);
69            IterValue::try_from_value(value.value, value.mutable, root_guard.vm)
70                .at(iterable_expr.span())?
71                .track_tmp_root(root_guard.vm)
72        };
73
74        let body = self.body();
75
76        let flow = root_guard.vm.flow.take();
77
78        while let Some(v) = iterator.next(root_guard.vm)? {
79            root_guard.vm.in_scope(|vm| {
80                destructure_pattern(
81                    vm,
82                    pattern,
83                    v,
84                    BindingKind::Immutable { first_assign: None },
85                    Visibility::Private,
86                )?;
87
88                output = body.eval(vm)?.value;
89
90                SourceResult::Ok(())
91            })?;
92
93            match &root_guard.vm.flow {
94                None => {}
95                Some(FlowEvent::Break(_, value)) => {
96                    if let Some(value) = value {
97                        output = value.clone();
98                    }
99                    root_guard.vm.flow = None;
100                    break;
101                }
102                Some(FlowEvent::Continue(_)) => root_guard.vm.flow = None,
103                Some(FlowEvent::Return(..)) => break,
104            }
105        }
106
107        if let Some(flow) = flow {
108            root_guard.vm.flow = Some(flow);
109        }
110
111        Ok(Evaluated::mutable(output))
112    }
113}
114
115/// Evaluates the condition expression and ensures it's a boolean.
116#[inline]
117fn eval_condition(cond: ast::Condition<'_>, vm: &mut Machine) -> SourceResult<bool> {
118    let cond_expr = cond.expr();
119    let cond_value = cond_expr.eval(vm)?;
120    cond_value.value.cast::<bool>().at(cond_expr.span())
121}