1use crate::vm::{ErrorMode, Machine};
2use crate::{Eval, Evaluated};
3use compose_error_codes::E0301_ARRAY_DESTRUCTURING_WRONG_NUMBER_OF_ELEMENTS;
4use compose_library::diag::{bail, error, At, SourceDiagnostic, SourceResult, Spanned};
5use compose_library::{diag, ArrayValue, BindingKind, IntoValue, Value, Visibility, Vm};
6use compose_syntax::ast;
7use compose_syntax::ast::{AstNode, DestructuringItem, Expr, Pattern};
8use ecow::{eco_format, eco_vec};
9
10impl<'a> Eval for ast::Ident<'a> {
11 fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
12 let span = self.span();
13 let binding = vm.frames.top.scopes.get(&self).at(span)?;
14
15 let mutable = binding.is_mutable();
16
17 Ok(Evaluated::new(
18 binding.read_checked(span, &mut vm.engine.sink).clone(),
19 mutable,
20 )
21 .with_origin(binding.span()))
22 }
23}
24
25impl<'a> Eval for ast::LetBinding<'a> {
26 fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
27 let has_init = self.has_initial_value();
28 let init = self.initial_value();
29
30 let binding_kind = match (self.is_mut(), has_init) {
31 (true, true) => BindingKind::Mutable,
32 (false, true) => BindingKind::Immutable { first_assign: None },
33 (true, false) => BindingKind::UninitializedMutable,
34 (false, false) => BindingKind::Uninitialized,
35 };
36
37 let visibility = if self.is_public() {
38 Visibility::Public
39 } else {
40 Visibility::Private
41 };
42
43 let value = match init {
44 Some(expr) => {
45 vm.with_closure_capture_errors_mode(ErrorMode::Deferred, |vm| expr.eval(vm))?
46 }
47 None => Evaluated::unit(),
48 };
49
50 if vm.flow.is_some() {
52 return Ok(Evaluated::unit());
53 }
54
55 destructure_pattern(vm, self.pattern(), value.value, binding_kind, visibility)?;
56
57 Ok(Evaluated::unit())
58 }
59}
60
61pub fn destructure_pattern(
62 vm: &mut Machine,
63 pattern: Pattern,
64 value: Value,
65 binding_kind: BindingKind,
66 visibility: Visibility,
67) -> SourceResult<()> {
68 destructure_impl(vm, pattern, value, &mut |vm, expr, value| match expr {
69 Expr::Ident(ident) => {
70 let name = ident.get().clone();
71 let spanned = value
72 .named(Spanned::new(name, ident.span()))
73 .resolved()?;
75
76 vm.define(ident, spanned, binding_kind, visibility)?;
77
78 Ok(())
79 }
80 _ => Err(eco_vec![diag::SourceDiagnostic::error(
81 expr.span(),
82 "cannot destructure pattern",
83 )]),
84 })
85}
86
87fn destructure_impl(
88 vm: &mut Machine,
89 pattern: Pattern,
90 value: Value,
91 bind: &mut impl Fn(&mut Machine, Expr, Value) -> SourceResult<()>,
92) -> SourceResult<()> {
93 match pattern {
94 Pattern::Single(expr) => bind(vm, expr, value)?,
95 Pattern::PlaceHolder(_) => {} Pattern::Destructuring(destruct) => match value {
97 Value::Array(value) => destructure_array(vm, destruct, value, bind)?,
98 Value::Map(value) => destructure_map(vm, destruct, value, bind)?,
99 _ => bail!(pattern.span(), "cannot destructure {}", value.ty()),
100 }
101 };
102
103 Ok(())
104}
105
106fn destructure_array(vm: &mut Machine, destruct: ast::Destructuring, value: ArrayValue, bind: &mut impl Fn(&mut Machine, Expr, Value) -> SourceResult<()>) -> SourceResult<()> {
107 let arr = value.heap_ref().get_unwrap(&vm.heap).clone();
108
109 let len = arr.len();
110 let mut index = 0;
111
112 for p in destruct.items() {
113 match p {
114 DestructuringItem::Pattern(pat) => {
115 let Some(v) = arr.get(index) else {
116 bail!(wrong_number_of_elements(destruct, len))
117 };
118
119 destructure_impl(vm, pat, v.clone(), bind)?;
120 index += 1;
121 }
122 DestructuringItem::Named(named) => {
123 bail!(named.span(), "cannot destructure a named pattern from an array")
124 }
125 DestructuringItem::Spread(spread) => {
126 let sink_size = (1 + len).checked_sub(destruct.items().count());
128
129 let sunk_items = sink_size.and_then(|n| arr.get(index..index + n));
131
132 let (Some(sink_size), Some(sunk_items)) = (sink_size, sunk_items) else {
133 bail!(wrong_number_of_elements(destruct, len))
134 };
135
136 if let Some(expr) = spread.sink_expr() {
137 let sunk_arr = ArrayValue::from(vm.heap_mut(), sunk_items.to_vec()).into_value();
138 bind(vm, expr, sunk_arr)?;
139 }
140 index += sink_size;
141 }
142 }
143 }
144
145 if index != len {
147 bail!(wrong_number_of_elements(destruct, len))
148 }
149
150 Ok(())
151}
152
153#[allow(unused)]
154fn destructure_map(vm: &mut Machine, destruct: ast::Destructuring, value: compose_library::MapValue, bind: &mut impl Fn(&mut Machine, Expr, Value) -> SourceResult<()>) -> SourceResult<()> {
155
156 unimplemented!("destructuring maps")
157}
158
159
160#[cold]
163fn wrong_number_of_elements(
164 destruct: ast::Destructuring,
165 len: usize,
166) -> SourceDiagnostic {
167 let mut count = 0;
168 let mut spread = false;
169
170 for p in destruct.items() {
171 match p {
172 DestructuringItem::Pattern(_) => count += 1,
173 DestructuringItem::Spread(_) => spread = true,
174 DestructuringItem::Named(_) => {}
175 }
176 }
177
178 let quantifier = if len > count { "too many" } else { "not enough" };
179 let expected = match (spread, count) {
180 (true, 1) => "at least 1 element".into(),
181 (true, c) => eco_format!("at least {c} elements"),
182 (false, 0) => "an empty array".into(),
183 (false, 1) => "a single element".into(),
184 (false, c) => eco_format!("{c} elements",),
185 };
186
187 let mut err = error!(
188 destruct.span(), "{quantifier} elements to destructure";
189 hint: "the provided array has a length of {len}, \
190 but the pattern expects {expected}";
191 code: &E0301_ARRAY_DESTRUCTURING_WRONG_NUMBER_OF_ELEMENTS;
192 );
193
194 if len > count {
195 err.hint("use `..` to ignore the remaining elements, or `..rest` to bind them");
196 }
197
198 err
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204 use crate::test::{assert_eval, assert_eval_with_vm, eval_code_with_vm, TestWorld};
205 use compose_error_codes::{E0004_MUTATE_IMMUTABLE_VARIABLE, W0001_USED_UNINITIALIZED_VARIABLE};
206 use compose_library::{BindingKind, UnitValue};
207
208 #[test]
209 fn test_let_binding() {
210 let world = TestWorld::new();
211 let mut vm = Machine::new(&world);
212 eval_code_with_vm(&mut vm, &world, "let a = 3")
213 .value
214 .expect("failed to evaluate");
215
216 let binding = vm.get("a").unwrap();
217
218 assert_eq!(binding.read(), &Value::Int(3));
219 assert_eq!(
220 binding.kind(),
221 BindingKind::Immutable { first_assign: None }
222 );
223 }
224
225 #[test]
226 fn test_let_mut_binding() {
227 let world = TestWorld::new();
228 let mut vm = Machine::new(&world);
229 assert_eval_with_vm(&mut vm, &world, "let mut a = 3");
230
231 let binding = vm.get("a").unwrap();
232
233 assert_eq!(binding.read(), &Value::Int(3));
234 assert_eq!(binding.kind(), BindingKind::Mutable);
235 }
236
237 #[test]
238 fn test_let_binding_without_value() {
239 let world = TestWorld::new();
240 let mut vm = Machine::new(&world);
241 assert_eval_with_vm(&mut vm, &world, "let a");
242
243 let binding = vm.get("a").unwrap();
244
245 assert_eq!(binding.read(), &Value::unit());
246 assert_eq!(binding.kind(), BindingKind::Uninitialized);
247 }
248
249 #[test]
250 fn test_let_mut_binding_without_value() {
251 let world = TestWorld::new();
252 let mut vm = Machine::new(&world);
253 assert_eval_with_vm(&mut vm, &world, "let mut a");
254
255 let binding = vm.get("a").unwrap();
256 assert_eq!(binding.read(), &Value::unit());
257 assert_eq!(binding.kind(), BindingKind::UninitializedMutable);
258 }
259
260 #[test]
261 fn test_read_ident() {
262 let world = TestWorld::new();
263 let mut vm = Machine::new(&world);
264 assert_eval_with_vm(&mut vm, &world, "let a = 3");
266 let result = assert_eval_with_vm(&mut vm, &world, "a");
268 assert_eq!(result, Value::Int(3));
269 }
270
271 #[test]
272 fn test_read_uninitialised_variable() {
273 let world = TestWorld::new();
274 let mut vm = Machine::new(&world);
275 assert_eval_with_vm(&mut vm, &world, "let a");
277 let result = eval_code_with_vm(&mut vm, &world, "a")
279 .assert_warnings(&[W0001_USED_UNINITIALIZED_VARIABLE])
280 .assert_no_errors()
281 .get_value();
282
283 assert_eq!(result, Value::unit());
284 }
285
286 #[test]
287 fn integration() {
288 let result = assert_eval(
289 r#"
290 let a = 3;
291 let b = 4;
292 let c = a + b;
293 c * 2;
294 "#,
295 );
296
297 assert_eq!(result, Value::Int(14));
298 }
299
300 #[test]
301 fn assign_mut() {
302 let world = TestWorld::new();
303 let mut vm = Machine::new(&world);
304 assert_eval_with_vm(&mut vm, &world, "let mut a = 3");
305
306 let binding = vm.get("a").unwrap();
307 assert_eq!(binding.kind(), BindingKind::Mutable);
308
309 assert_eval_with_vm(&mut vm, &world, "a = 4");
310 let result = assert_eval_with_vm(&mut vm, &world, "a");
311 assert_eq!(result, Value::Int(4));
312
313 let binding = vm.get("a").unwrap();
315 assert_eq!(binding.kind(), BindingKind::Mutable);
316 }
317
318 #[test]
319 fn assign_mut_uninitialised() {
320 let world = TestWorld::new();
321 let mut vm = Machine::new(&world);
322 assert_eval_with_vm(&mut vm, &world, "let mut a");
323
324 let binding = vm.get("a").unwrap();
325 assert_eq!(binding.kind(), BindingKind::UninitializedMutable);
326 assert_eq!(binding.read(), &Value::Unit(UnitValue));
327
328 assert_eval_with_vm(&mut vm, &world, "a = 4");
329 let result = assert_eval_with_vm(&mut vm, &world, "a");
330 assert_eq!(result, Value::Int(4));
331
332 let binding = vm.get("a").unwrap();
334 assert_eq!(binding.kind(), BindingKind::Mutable);
335 }
336
337 #[test]
338 fn assign_uninitialised() {
339 let world = TestWorld::new();
340 let mut vm = Machine::new(&world);
341 assert_eval_with_vm(&mut vm, &world, "let a");
342
343 let binding = vm.get("a").unwrap();
344 assert_eq!(binding.kind(), BindingKind::Uninitialized);
345 assert_eq!(binding.read(), &Value::unit());
346
347 assert_eval_with_vm(&mut vm, &world, "a = 4");
348 let result = assert_eval_with_vm(&mut vm, &world, "a");
349 assert_eq!(result, Value::Int(4));
350
351 let binding = vm.get("a").unwrap();
353 assert!(matches!(binding.kind(), BindingKind::Immutable { .. }));
354 }
355
356 #[test]
357 fn assign_immut_error() {
358 let world = TestWorld::new();
359 let mut vm = Machine::new(&world);
360 assert_eval_with_vm(&mut vm, &world, "let a = 3");
361 let binding = vm.get("a").unwrap();
362 assert_eq!(
363 binding.kind(),
364 BindingKind::Immutable { first_assign: None }
365 );
366 assert_eq!(binding.read(), &Value::Int(3));
367
368 eval_code_with_vm(&mut vm, &world, "a = 4")
369 .assert_errors(&[E0004_MUTATE_IMMUTABLE_VARIABLE]);
370 }
371}