compose_eval/vm/
mod.rs

1mod stack;
2
3//noinspection RsUnusedImport - false positive, actually used
4use crate::expression::eval_lambda;
5use crate::vm::stack::{StackFrames, TrackMarker};
6use compose_library::diag::{At, SourceDiagnostic, SourceResult, error};
7use compose_library::{
8    Args, Binding, BindingKind, Engine, Func, FuncKind, Heap, IntoValue, Routines, Scopes, Sink,
9    SyntaxContext, Trace, UntypedRef, Value, VariableAccessError, Visibility, Vm, World,
10};
11use compose_syntax::ast::AstNode;
12use compose_syntax::{Span, ast};
13use ecow::EcoString;
14pub use stack::Tracked;
15pub use stack::TrackedContainer;
16use std::fmt::Debug;
17use std::ops::{Deref, DerefMut};
18
19pub struct Machine<'a> {
20    pub frames: StackFrames<'a>,
21    pub flow: Option<FlowEvent>,
22    pub engine: Engine<'a>,
23    pub context: EvalContext,
24    pub heap: Heap,
25}
26
27impl<'a> Vm<'a> for Machine<'a> {
28    fn heap(&self) -> &Heap {
29        &self.heap
30    }
31
32    fn heap_mut(&mut self) -> &mut Heap {
33        &mut self.heap
34    }
35
36    fn engine(&self) -> &Engine<'a> {
37        &self.engine
38    }
39
40    fn engine_mut(&mut self) -> &mut Engine<'a> {
41        &mut self.engine
42    }
43
44    fn call_func(&mut self, func: &Func, args: Args) -> SourceResult<Value> {
45        match &func.kind {
46            FuncKind::Native(native) => native.call(self, args),
47            FuncKind::Closure(closure) => eval_lambda(closure, self, args),
48        }
49    }
50}
51
52impl<'a> Machine<'a> {
53    pub(crate) fn sink_mut(&mut self) -> &mut Sink {
54        &mut self.engine.sink
55    }
56
57    pub(crate) fn in_scope<T>(&mut self, f: impl FnOnce(&mut Machine<'a>) -> T) -> T {
58        self.frames.top.scopes.enter();
59        let result = f(self);
60        self.frames.top.scopes.exit();
61        result
62    }
63
64    pub fn world(&self) -> &dyn World {
65        self.engine.world
66    }
67
68    pub fn syntax_ctx<'w>(&self) -> SyntaxContext<'w>
69    where
70        'a: 'w,
71    {
72        SyntaxContext {
73            world: self.engine.world,
74        }
75    }
76}
77
78impl Debug for Machine<'_> {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        f.debug_struct("Vm")
81            .field("frames", &self.frames)
82            .field("flow", &self.flow)
83            .field("sink", &self.engine)
84            .field("heap", &self.heap)
85            .finish()
86    }
87}
88
89#[derive(Debug, Clone)]
90pub enum FlowEvent {
91    Continue(Span),
92    Break(Span, Option<Value>),
93    Return(Span, Option<Value>),
94}
95
96impl Trace for FlowEvent {
97    fn visit_refs(&self, f: &mut dyn FnMut(UntypedRef)) {
98        match self {
99            FlowEvent::Continue(_) => {}
100            FlowEvent::Break(_, Some(value)) => value.visit_refs(f),
101            FlowEvent::Break(_, None) => {}
102            FlowEvent::Return(_, Some(value)) => value.visit_refs(f),
103            FlowEvent::Return(_, None) => {}
104        }
105    }
106}
107
108impl FlowEvent {
109    pub(crate) fn forbidden(&self) -> SourceDiagnostic {
110        match *self {
111            Self::Break(span, _) => {
112                error!(span, "cannot break outside of a loop")
113            }
114            Self::Return(span, _) => {
115                error!(span, "cannot return outside of a function")
116            }
117            Self::Continue(span) => {
118                error!(span, "cannot continue outside of a loop")
119            }
120        }
121    }
122}
123
124impl<'a> Machine<'a> {
125    pub fn new(world: &'a dyn World) -> Self {
126        Self {
127            frames: StackFrames::new(Some(world.library())),
128            flow: None,
129            engine: Engine {
130                routines: routines(),
131                sink: Sink::default(),
132                world,
133            },
134            context: Default::default(),
135            heap: Heap::new(),
136        }
137    }
138
139    /// Enter a new stack frame to evaluate `f`. This frame does not have access
140    /// to any defined variables in other frames, but does share the same heap.
141    /// This means references passed into this frame can share data.
142    pub fn with_frame<T>(&mut self, f: impl FnOnce(&mut Machine) -> T) -> T {
143        self.frames.enter();
144        let result = f(self);
145        self.frames.exit();
146        result
147    }
148
149    pub fn maybe_gc(&mut self) {
150        let roots = VmRoots {
151            frames: &self.frames,
152            flow: &self.flow,
153        };
154        self.heap.maybe_gc(&roots);
155    }
156}
157
158#[derive(Debug)]
159pub struct VmRoots<'a> {
160    pub frames: &'a StackFrames<'a>,
161    pub flow: &'a Option<FlowEvent>,
162}
163
164impl Trace for VmRoots<'_> {
165    fn visit_refs(&self, f: &mut dyn FnMut(UntypedRef)) {
166        self.frames.visit_refs(f);
167        if let Some(flow) = self.flow {
168            flow.visit_refs(f);
169        }
170    }
171}
172
173impl<'a> Machine<'a> {
174    pub fn track_tmp_root(&mut self, value: &impl Trace) {
175        self.frames.top.track(value);
176    }
177
178    // TODO: remove
179    pub fn debug_tracked(&self, from: &str) {
180        let mut tracked = Vec::new();
181        self.frames
182            .visit_refs(&mut |k| tracked.push((k, self.heap.get_untyped(k))));
183
184        dbg!(from, &tracked);
185    }
186
187    pub fn temp_root_marker(&mut self) -> TrackMarker {
188        self.frames.top.marker()
189    }
190
191    pub fn pop_temp_roots(&mut self, marker: TrackMarker) {
192        self.frames.top.forget(marker);
193    }
194
195    /// Automatically forgets any tracked values during `f` after f is finished
196    pub fn temp_root_scope(
197        &mut self,
198        f: impl FnOnce(&mut Machine<'a>) -> SourceResult<Value>,
199    ) -> SourceResult<Value> {
200        let marker = self.temp_root_marker();
201        let result = f(self);
202        self.pop_temp_roots(marker);
203        result
204    }
205
206    pub fn define(
207        &mut self,
208        var: ast::Ident,
209        value: impl IntoValue,
210        binding_kind: BindingKind,
211        visibility: Visibility,
212    ) -> SourceResult<&mut Binding> {
213        self.try_bind(
214            var.get().clone(),
215            Binding::new(value, var.span())
216                .with_kind(binding_kind)
217                .with_visibility(visibility),
218        )
219    }
220
221    pub fn try_bind(&mut self, name: EcoString, binding: Binding) -> SourceResult<&mut Binding> {
222        let span = binding.span();
223        self.frames.top.scopes.top.try_bind(name, binding).at(span)
224    }
225
226    pub fn bind(&mut self, name: EcoString, binding: Binding) -> &mut Binding {
227        self.frames.top.scopes.top.bind(name, binding)
228    }
229
230    fn scopes(&self) -> &Scopes<'a> {
231        &self.frames.top.scopes
232    }
233    fn scopes_mut(&mut self) -> &mut Scopes<'a> {
234        &mut self.frames.top.scopes
235    }
236
237    pub fn get(&self, name: &str) -> Result<&Binding, VariableAccessError> {
238        self.scopes().get(name)
239    }
240
241    pub fn get_mut(&mut self, name: &str) -> Result<&mut Binding, VariableAccessError> {
242        self.scopes_mut().get_mut(name)
243    }
244}
245
246pub struct TempRootGuard<'a, 'b> {
247    pub marker: TrackMarker,
248    pub vm: &'b mut Machine<'a>,
249}
250
251impl<'a, 'b> Drop for TempRootGuard<'a, 'b> {
252    fn drop(&mut self) {
253        self.vm.pop_temp_roots(self.marker);
254    }
255}
256
257impl<'a> Machine<'a> {
258    pub fn temp_root_guard<'b>(&'b mut self) -> TempRootGuard<'a, 'b> {
259        let marker = self.temp_root_marker();
260        TempRootGuard { marker, vm: self }
261    }
262}
263
264impl<'a, 'b> Deref for TempRootGuard<'a, 'b> {
265    type Target = Machine<'a>;
266
267    fn deref(&self) -> &Self::Target {
268        self.vm
269    }
270}
271
272impl<'a, 'b> DerefMut for TempRootGuard<'a, 'b> {
273    fn deref_mut(&mut self) -> &mut Self::Target {
274        self.vm
275    }
276}
277
278pub fn routines() -> Routines {
279    Routines {}
280}
281
282#[derive(Debug, Clone, Default)]
283pub struct EvalContext {
284    pub closure_capture: ErrorMode,
285}
286
287/// Whether to defer an error or immediately emit it
288#[derive(Debug, Clone, Copy, Default)]
289pub enum ErrorMode {
290    #[default]
291    Immediate,
292    Deferred,
293}
294
295impl ErrorMode {
296    pub fn should_defer(&self) -> bool {
297        matches!(self, Self::Deferred)
298    }
299}
300
301impl Machine<'_> {
302    /// Run f with closure capture errors deferred.
303    ///
304    /// This makes the caller responsible for either handling or emitting the unresolved errors.
305    pub fn with_closure_capture_errors_mode<T>(
306        &mut self,
307        mode: ErrorMode,
308        f: impl FnOnce(&mut Machine) -> SourceResult<T>,
309    ) -> SourceResult<T> {
310        let old = self.context.closure_capture;
311        self.context.closure_capture = mode;
312        let result = f(self);
313        self.context.closure_capture = old;
314        result
315    }
316}