1mod stack;
2
3use 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 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 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 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#[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 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}