compose_eval/
statement.rs1use crate::vm::FlowEvent;
2use crate::{eval, Eval, EvalConfig, Evaluated, Machine};
3use compose_library::diag::{bail, error, SourceResult, Trace, TracePoint, Warned};
4use compose_library::{Binding, IntoValue, Module};
5use compose_syntax::ast::{AstNode, BreakStatement};
6use compose_syntax::{ast, FileId};
7use ecow::{eco_vec, EcoString};
8use std::path::PathBuf;
9
10impl Eval for ast::Statement<'_> {
11 fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
12 let guard = vm.temp_root_guard();
14 let result = match self {
15 ast::Statement::Expr(e) => e.eval(guard.vm),
16 ast::Statement::Let(l) => l.eval(guard.vm),
17 ast::Statement::Assign(a) => a.eval(guard.vm),
18 ast::Statement::Break(b) => b.eval(guard.vm),
19 ast::Statement::Return(r) => r.eval(guard.vm),
20 ast::Statement::Continue(c) => c.eval(guard.vm),
21 ast::Statement::ModuleImport(i) => i.eval(guard.vm),
22 };
23
24 guard.vm.maybe_gc();
25 result
26 }
27}
28
29impl Eval for BreakStatement<'_> {
30 fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
31 match self.value() {
32 None => {
33 vm.flow = Some(FlowEvent::Break(self.span(), None));
34 }
35 Some(expr) => {
36 let value = expr.eval(vm)?.value;
37 vm.flow = Some(FlowEvent::Break(self.span(), Some(value)))
38 }
39 }
40
41 Ok(Evaluated::unit())
42 }
43}
44
45impl Eval for ast::ReturnStatement<'_> {
46 fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
47 match self.value() {
48 None => {
49 vm.flow = Some(FlowEvent::Return(self.span(), None));
50 }
51 Some(expr) => {
52 let value = expr.eval(vm)?.value;
53 vm.flow = Some(FlowEvent::Return(self.span(), Some(value)))
54 }
55 }
56
57 Ok(Evaluated::unit())
58 }
59}
60
61impl Eval for ast::Continue<'_> {
62 fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
63 vm.flow = Some(FlowEvent::Continue(self.span()));
64 Ok(Evaluated::unit())
65 }
66}
67
68impl Eval for ast::ModuleImport<'_> {
69 fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated> {
70 let source = self.source();
71
72 let path = match self.span().resolve_path(source.as_str()) {
73 Some(p) => p,
74 None => bail!(
75 self.source_span(),
76 "could not resolve module path from a file without path information"
77 ),
78 };
79
80 let name = match self.alias() {
81 None => {
82 let as_path = PathBuf::from(source.as_str());
83 let stem = match as_path.file_stem() {
84 Some(stem) => stem,
85 None => bail!(self.source_span(), "could not resolve module name from path")
86 };
87 EcoString::from(stem.to_string_lossy().as_ref())
88 }
89 Some(name) => name.get().to_owned()
90 };
91
92 let id = FileId::new(path);
93
94 let source = vm.engine.world.source(id).map_err(|e| {
95 eco_vec!(error!(
96 self.source_span(),
97 "could not load module source: {}", e
98 ))
99 })?;
100
101 let module = vm.with_frame(|vm| {
102 let Warned { value, warnings } = eval(&source, vm, &EvalConfig::default());
103 value?;
104 vm.sink_mut().warnings.extend(warnings);
105
106
107 let module = Module::new(name.clone(), vm.frames.top.scopes.top.clone());
108 SourceResult::Ok(module.into_value())
109 }).trace(|| TracePoint::Import, self.source_span())?;
110
111 vm.try_bind(name, Binding::new(module, self.span()))?;
112
113 Ok(Evaluated::unit())
114 }
115}