1use comemo::Tracked;
2use typst_library::World;
3use typst_library::diag::warning;
4use typst_library::engine::Engine;
5use typst_library::foundations::{Binding, Context, IntoValue, Scopes, Value};
6use typst_syntax::Span;
7use typst_syntax::ast::{self, AstNode};
8
9use crate::FlowEvent;
10
11pub struct Vm<'a> {
16 pub engine: Engine<'a>,
18 pub flow: Option<FlowEvent>,
20 pub scopes: Scopes<'a>,
22 pub inspected: Option<Span>,
24 pub context: Tracked<'a, Context<'a>>,
26}
27
28impl<'a> Vm<'a> {
29 pub fn new(
31 engine: Engine<'a>,
32 context: Tracked<'a, Context<'a>>,
33 scopes: Scopes<'a>,
34 target: Span,
35 ) -> Self {
36 let inspected = target.id().and_then(|id| engine.traced.get(id));
37 Self { engine, context, flow: None, scopes, inspected }
38 }
39
40 pub fn world(&self) -> Tracked<'a, dyn World + 'a> {
42 self.engine.world
43 }
44
45 pub fn define(&mut self, var: ast::Ident, value: impl IntoValue) {
49 self.bind(var, Binding::new(value, var.span()));
50 }
51
52 pub fn bind(&mut self, var: ast::Ident, binding: Binding) {
57 if self.inspected == Some(var.span()) {
58 self.trace(binding.read().clone());
59 }
60
61 if var.get() == "is" {
63 self.engine.sink.warn(warning!(
64 var.span(),
65 "`is` will likely become a keyword in future versions and will \
66 not be allowed as an identifier";
67 hint: "rename this variable to avoid future errors";
68 hint: "try `is_` instead"
69 ));
70 }
71
72 self.scopes.top.bind(var.get().clone(), binding);
73 }
74
75 #[cold]
77 pub fn trace(&mut self, value: Value) {
78 self.engine
79 .sink
80 .value(value.clone(), self.context.styles().ok().map(|s| s.to_map()));
81 }
82}