1pub(crate) mod ops;
4
5mod access;
6mod binding;
7mod call;
8mod code;
9mod flow;
10mod import;
11mod markup;
12mod math;
13mod methods;
14mod rules;
15mod vm;
16
17pub use self::call::{CapturesVisitor, eval_closure};
18pub use self::flow::FlowEvent;
19pub use self::import::import;
20pub use self::vm::Vm;
21
22use self::access::*;
23use self::binding::*;
24use self::methods::*;
25
26use comemo::{Track, Tracked, TrackedMut};
27use typst_library::World;
28use typst_library::diag::{SourceResult, bail};
29use typst_library::engine::{Engine, Route, Sink, Traced};
30use typst_library::foundations::{Context, Module, NativeElement, Scope, Scopes, Value};
31use typst_library::introspection::Introspector;
32use typst_library::math::EquationElem;
33use typst_library::routines::Routines;
34use typst_syntax::{Source, Span, SyntaxMode, ast, parse, parse_code, parse_math};
35
36#[comemo::memoize]
38#[typst_macros::time(name = "eval", span = source.root().span())]
39pub fn eval(
40 routines: &Routines,
41 world: Tracked<dyn World + '_>,
42 traced: Tracked<Traced>,
43 sink: TrackedMut<Sink>,
44 route: Tracked<Route>,
45 source: &Source,
46) -> SourceResult<Module> {
47 let id = source.id();
49 if route.contains(id) {
50 panic!("Tried to cyclicly evaluate {:?}", id.vpath());
51 }
52
53 let introspector = Introspector::default();
55 let engine = Engine {
56 routines,
57 world,
58 introspector: introspector.track(),
59 traced,
60 sink,
61 route: Route::extend(route).with_id(id),
62 };
63
64 let context = Context::none();
66 let scopes = Scopes::new(Some(world.library()));
67 let root = source.root();
68 let mut vm = Vm::new(engine, context.track(), scopes, root.span());
69
70 let errors = root.errors();
72 if !errors.is_empty() && vm.inspected.is_none() {
73 return Err(errors.into_iter().map(Into::into).collect());
74 }
75
76 let markup = root.cast::<ast::Markup>().unwrap();
78 let output = markup.eval(&mut vm)?;
79
80 if let Some(flow) = vm.flow {
82 bail!(flow.forbidden());
83 }
84
85 let name = id
87 .vpath()
88 .as_rootless_path()
89 .file_stem()
90 .unwrap_or_default()
91 .to_string_lossy();
92
93 Ok(Module::new(name, vm.scopes.top).with_content(output).with_file_id(id))
94}
95
96#[comemo::memoize]
100pub fn eval_string(
101 routines: &Routines,
102 world: Tracked<dyn World + '_>,
103 sink: TrackedMut<Sink>,
104 string: &str,
105 span: Span,
106 mode: SyntaxMode,
107 scope: Scope,
108) -> SourceResult<Value> {
109 let mut root = match mode {
110 SyntaxMode::Code => parse_code(string),
111 SyntaxMode::Markup => parse(string),
112 SyntaxMode::Math => parse_math(string),
113 };
114
115 root.synthesize(span);
116
117 let errors = root.errors();
119 if !errors.is_empty() {
120 return Err(errors.into_iter().map(Into::into).collect());
121 }
122
123 let introspector = Introspector::default();
125 let traced = Traced::default();
126 let engine = Engine {
127 routines,
128 world,
129 introspector: introspector.track(),
130 traced: traced.track(),
131 sink,
132 route: Route::default(),
133 };
134
135 let context = Context::none();
137 let scopes = Scopes::new(Some(world.library()));
138 let mut vm = Vm::new(engine, context.track(), scopes, root.span());
139 vm.scopes.scopes.push(scope);
140
141 let output = match mode {
143 SyntaxMode::Code => root.cast::<ast::Code>().unwrap().eval(&mut vm)?,
144 SyntaxMode::Markup => {
145 Value::Content(root.cast::<ast::Markup>().unwrap().eval(&mut vm)?)
146 }
147 SyntaxMode::Math => Value::Content(
148 EquationElem::new(root.cast::<ast::Math>().unwrap().eval(&mut vm)?)
149 .with_block(false)
150 .pack()
151 .spanned(span),
152 ),
153 };
154
155 if let Some(flow) = vm.flow {
157 bail!(flow.forbidden());
158 }
159
160 Ok(output)
161}
162
163pub trait Eval {
165 type Output;
167
168 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output>;
170}