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 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 .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(_) => {} 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 assert_eval_with_vm(&mut vm, &world, "let a = 3");
168 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 assert_eval_with_vm(&mut vm, &world, "let a");
179 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 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 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 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}