compose_eval/
statement.rs

1use 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        // trace_log!("eval statement: {:#?}", self);
13        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}