compose_eval/expression/
bindings.rs

1use crate::vm::{ErrorMode, Machine};
2use crate::{Eval, Evaluated};
3use compose_library::diag::{At, SourceResult, Spanned};
4use compose_library::{BindingKind, Value, Visibility, diag};
5use compose_syntax::ast;
6use compose_syntax::ast::{AstNode, Expr, Pattern};
7use ecow::eco_vec;
8
9impl<'a> Eval for ast::Ident<'a> {
10    fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
11        let span = self.span();
12        let binding = vm.frames.top.scopes.get(&self).at(span)?;
13
14        let mutable = binding.is_mutable();
15
16        Ok(Evaluated::new(
17            binding.read_checked(span, &mut vm.engine.sink).clone(),
18            mutable,
19        )
20        .with_origin(binding.span()))
21    }
22}
23
24impl<'a> Eval for ast::LetBinding<'a> {
25    fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
26        let has_init = self.has_initial_value();
27        let init = self.initial_value();
28
29        let binding_kind = match (self.is_mut(), has_init) {
30            (true, true) => BindingKind::Mutable,
31            (false, true) => BindingKind::Immutable { first_assign: None },
32            (true, false) => BindingKind::UninitializedMutable,
33            (false, false) => BindingKind::Uninitialized,
34        };
35
36        let visibility = if self.is_public() {
37            Visibility::Public
38        } else {
39            Visibility::Private
40        };
41
42        let value = match init {
43            Some(expr) => {
44                vm.with_closure_capture_errors_mode(ErrorMode::Deferred, |vm| expr.eval(vm))?
45            }
46            None => Evaluated::unit(),
47        };
48
49        // handle control flow
50        if vm.flow.is_some() {
51            return Ok(Evaluated::unit());
52        }
53
54        destructure_pattern(vm, self.pattern(), value.value, binding_kind, visibility)?;
55
56        Ok(Evaluated::unit())
57    }
58}
59
60pub fn destructure_pattern(
61    vm: &mut Machine,
62    pattern: Pattern,
63    value: Value,
64    binding_kind: BindingKind,
65    visibility: Visibility,
66) -> SourceResult<()> {
67    destructure_impl(vm, pattern, value, &mut |vm, expr, value| match expr {
68        Expr::Ident(ident) => {
69            let name = ident.get().clone();
70            let spanned = value
71                .named(Spanned::new(name, ident.span()))
72                // Now that the names have been added, make sure any deferred errors are resolved
73                .resolved()?;
74
75            vm.define(ident, spanned, binding_kind, visibility)?;
76
77            Ok(())
78        }
79        _ => Err(eco_vec![diag::SourceDiagnostic::error(
80            expr.span(),
81            "cannot destructure pattern",
82        )]),
83    })
84}
85
86fn destructure_impl(
87    vm: &mut Machine,
88    pattern: Pattern,
89    value: Value,
90    bind: &mut impl Fn(&mut Machine, Expr, Value) -> SourceResult<()>,
91) -> SourceResult<()> {
92    match pattern {
93        Pattern::Single(expr) => bind(vm, expr, value)?,
94        Pattern::PlaceHolder(_) => {} // A placeholder means we discard the value, no need to bind
95        Pattern::Destructuring(_) => {
96            unimplemented!("destructuring")
97        }
98    };
99
100    Ok(())
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106    use crate::test::{TestWorld, assert_eval, assert_eval_with_vm, eval_code_with_vm};
107    use compose_error_codes::{E0004_MUTATE_IMMUTABLE_VARIABLE, W0001_USED_UNINITIALIZED_VARIABLE};
108    use compose_library::{BindingKind, UnitValue};
109
110    #[test]
111    fn test_let_binding() {
112        let world = TestWorld::new();
113        let mut vm = Machine::new(&world);
114        eval_code_with_vm(&mut vm, &world, "let a = 3")
115            .value
116            .expect("failed to evaluate");
117
118        let binding = vm.get("a").unwrap();
119
120        assert_eq!(binding.read(), &Value::Int(3));
121        assert_eq!(
122            binding.kind(),
123            BindingKind::Immutable { first_assign: None }
124        );
125    }
126
127    #[test]
128    fn test_let_mut_binding() {
129        let world = TestWorld::new();
130        let mut vm = Machine::new(&world);
131        assert_eval_with_vm(&mut vm, &world, "let mut a = 3");
132
133        let binding = vm.get("a").unwrap();
134
135        assert_eq!(binding.read(), &Value::Int(3));
136        assert_eq!(binding.kind(), BindingKind::Mutable);
137    }
138
139    #[test]
140    fn test_let_binding_without_value() {
141        let world = TestWorld::new();
142        let mut vm = Machine::new(&world);
143        assert_eval_with_vm(&mut vm, &world, "let a");
144
145        let binding = vm.get("a").unwrap();
146
147        assert_eq!(binding.read(), &Value::unit());
148        assert_eq!(binding.kind(), BindingKind::Uninitialized);
149    }
150
151    #[test]
152    fn test_let_mut_binding_without_value() {
153        let world = TestWorld::new();
154        let mut vm = Machine::new(&world);
155        assert_eval_with_vm(&mut vm, &world, "let mut a");
156
157        let binding = vm.get("a").unwrap();
158        assert_eq!(binding.read(), &Value::unit());
159        assert_eq!(binding.kind(), BindingKind::UninitializedMutable);
160    }
161
162    #[test]
163    fn test_read_ident() {
164        let world = TestWorld::new();
165        let mut vm = Machine::new(&world);
166        // define the variable
167        assert_eval_with_vm(&mut vm, &world, "let a = 3");
168        // read the variable
169        let result = assert_eval_with_vm(&mut vm, &world, "a");
170        assert_eq!(result, Value::Int(3));
171    }
172
173    #[test]
174    fn test_read_uninitialised_variable() {
175        let world = TestWorld::new();
176        let mut vm = Machine::new(&world);
177        // set up the variable
178        assert_eval_with_vm(&mut vm, &world, "let a");
179        // reading emits warning
180        let result = eval_code_with_vm(&mut vm, &world, "a")
181            .assert_warnings(&[W0001_USED_UNINITIALIZED_VARIABLE])
182            .assert_no_errors()
183            .get_value();
184
185        assert_eq!(result, Value::unit());
186    }
187
188    #[test]
189    fn integration() {
190        let result = assert_eval(
191            r#"
192            let a = 3;
193            let b = 4;
194            let c = a + b;
195            c * 2;
196        "#,
197        );
198
199        assert_eq!(result, Value::Int(14));
200    }
201
202    #[test]
203    fn assign_mut() {
204        let world = TestWorld::new();
205        let mut vm = Machine::new(&world);
206        assert_eval_with_vm(&mut vm, &world, "let mut a = 3");
207
208        let binding = vm.get("a").unwrap();
209        assert_eq!(binding.kind(), BindingKind::Mutable);
210
211        assert_eval_with_vm(&mut vm, &world, "a = 4");
212        let result = assert_eval_with_vm(&mut vm, &world, "a");
213        assert_eq!(result, Value::Int(4));
214
215        // should still be mutable
216        let binding = vm.get("a").unwrap();
217        assert_eq!(binding.kind(), BindingKind::Mutable);
218    }
219
220    #[test]
221    fn assign_mut_uninitialised() {
222        let world = TestWorld::new();
223        let mut vm = Machine::new(&world);
224        assert_eval_with_vm(&mut vm, &world, "let mut a");
225
226        let binding = vm.get("a").unwrap();
227        assert_eq!(binding.kind(), BindingKind::UninitializedMutable);
228        assert_eq!(binding.read(), &Value::Unit(UnitValue));
229
230        assert_eval_with_vm(&mut vm, &world, "a = 4");
231        let result = assert_eval_with_vm(&mut vm, &world, "a");
232        assert_eq!(result, Value::Int(4));
233
234        // should now be mutable
235        let binding = vm.get("a").unwrap();
236        assert_eq!(binding.kind(), BindingKind::Mutable);
237    }
238
239    #[test]
240    fn assign_uninitialised() {
241        let world = TestWorld::new();
242        let mut vm = Machine::new(&world);
243        assert_eval_with_vm(&mut vm, &world, "let a");
244
245        let binding = vm.get("a").unwrap();
246        assert_eq!(binding.kind(), BindingKind::Uninitialized);
247        assert_eq!(binding.read(), &Value::unit());
248
249        assert_eval_with_vm(&mut vm, &world, "a = 4");
250        let result = assert_eval_with_vm(&mut vm, &world, "a");
251        assert_eq!(result, Value::Int(4));
252
253        // should now be immutable
254        let binding = vm.get("a").unwrap();
255        assert!(matches!(binding.kind(), BindingKind::Immutable { .. }));
256    }
257
258    #[test]
259    fn assign_immut_error() {
260        let world = TestWorld::new();
261        let mut vm = Machine::new(&world);
262        assert_eval_with_vm(&mut vm, &world, "let a = 3");
263        let binding = vm.get("a").unwrap();
264        assert_eq!(
265            binding.kind(),
266            BindingKind::Immutable { first_assign: None }
267        );
268        assert_eq!(binding.read(), &Value::Int(3));
269
270        eval_code_with_vm(&mut vm, &world, "a = 4")
271            .assert_errors(&[E0004_MUTATE_IMMUTABLE_VARIABLE]);
272    }
273}