1use ecow::{eco_vec, EcoVec};
2use typst_library::diag::{bail, error, warning, At, SourceResult};
3use typst_library::engine::Engine;
4use typst_library::foundations::{
5 ops, Array, Capturer, Closure, Content, ContextElem, Dict, Func, NativeElement,
6 Selector, Str, Value,
7};
8use typst_library::introspection::{Counter, State};
9use typst_syntax::ast::{self, AstNode};
10use typst_utils::singleton;
11
12use crate::{CapturesVisitor, Eval, FlowEvent, Vm};
13
14impl Eval for ast::Code<'_> {
15 type Output = Value;
16
17 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
18 eval_code(vm, &mut self.exprs())
19 }
20}
21
22fn eval_code<'a>(
24 vm: &mut Vm,
25 exprs: &mut impl Iterator<Item = ast::Expr<'a>>,
26) -> SourceResult<Value> {
27 let flow = vm.flow.take();
28 let mut output = Value::None;
29
30 while let Some(expr) = exprs.next() {
31 let span = expr.span();
32 let value = match expr {
33 ast::Expr::Set(set) => {
34 let styles = set.eval(vm)?;
35 if vm.flow.is_some() {
36 break;
37 }
38
39 let tail = eval_code(vm, exprs)?.display();
40 Value::Content(tail.styled_with_map(styles))
41 }
42 ast::Expr::Show(show) => {
43 let recipe = show.eval(vm)?;
44 if vm.flow.is_some() {
45 break;
46 }
47
48 let tail = eval_code(vm, exprs)?.display();
49 Value::Content(tail.styled_with_recipe(
50 &mut vm.engine,
51 vm.context,
52 recipe,
53 )?)
54 }
55 _ => expr.eval(vm)?,
56 };
57
58 output = ops::join(output, value, &mut (&mut vm.engine, span)).at(span)?;
59
60 if let Some(event) = &vm.flow {
61 warn_for_discarded_content(&mut vm.engine, event, &output);
62 break;
63 }
64 }
65
66 if flow.is_some() {
67 vm.flow = flow;
68 }
69
70 Ok(output)
71}
72
73impl Eval for ast::Expr<'_> {
74 type Output = Value;
75
76 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
77 let span = self.span();
78 let forbidden = |name| {
79 error!(span, "{} is only allowed directly in code and content blocks", name)
80 };
81
82 let v = match self {
83 Self::Text(v) => v.eval(vm).map(Value::Content),
84 Self::Space(v) => v.eval(vm).map(Value::Content),
85 Self::Linebreak(v) => v.eval(vm).map(Value::Content),
86 Self::Parbreak(v) => v.eval(vm).map(Value::Content),
87 Self::Escape(v) => v.eval(vm),
88 Self::Shorthand(v) => v.eval(vm),
89 Self::SmartQuote(v) => v.eval(vm).map(Value::Content),
90 Self::Strong(v) => v.eval(vm).map(Value::Content),
91 Self::Emph(v) => v.eval(vm).map(Value::Content),
92 Self::Raw(v) => v.eval(vm).map(Value::Content),
93 Self::Link(v) => v.eval(vm).map(Value::Content),
94 Self::Label(v) => v.eval(vm),
95 Self::Ref(v) => v.eval(vm).map(Value::Content),
96 Self::Heading(v) => v.eval(vm).map(Value::Content),
97 Self::List(v) => v.eval(vm).map(Value::Content),
98 Self::Enum(v) => v.eval(vm).map(Value::Content),
99 Self::Term(v) => v.eval(vm).map(Value::Content),
100 Self::Equation(v) => v.eval(vm).map(Value::Content),
101 Self::Math(v) => v.eval(vm).map(Value::Content),
102 Self::MathText(v) => v.eval(vm).map(Value::Content),
103 Self::MathIdent(v) => v.eval(vm),
104 Self::MathShorthand(v) => v.eval(vm),
105 Self::MathAlignPoint(v) => v.eval(vm).map(Value::Content),
106 Self::MathDelimited(v) => v.eval(vm).map(Value::Content),
107 Self::MathAttach(v) => v.eval(vm).map(Value::Content),
108 Self::MathPrimes(v) => v.eval(vm).map(Value::Content),
109 Self::MathFrac(v) => v.eval(vm).map(Value::Content),
110 Self::MathRoot(v) => v.eval(vm).map(Value::Content),
111 Self::Ident(v) => v.eval(vm),
112 Self::None(v) => v.eval(vm),
113 Self::Auto(v) => v.eval(vm),
114 Self::Bool(v) => v.eval(vm),
115 Self::Int(v) => v.eval(vm),
116 Self::Float(v) => v.eval(vm),
117 Self::Numeric(v) => v.eval(vm),
118 Self::Str(v) => v.eval(vm),
119 Self::Code(v) => v.eval(vm),
120 Self::Content(v) => v.eval(vm).map(Value::Content),
121 Self::Array(v) => v.eval(vm).map(Value::Array),
122 Self::Dict(v) => v.eval(vm).map(Value::Dict),
123 Self::Parenthesized(v) => v.eval(vm),
124 Self::FieldAccess(v) => v.eval(vm),
125 Self::FuncCall(v) => v.eval(vm),
126 Self::Closure(v) => v.eval(vm),
127 Self::Unary(v) => v.eval(vm),
128 Self::Binary(v) => v.eval(vm),
129 Self::Let(v) => v.eval(vm),
130 Self::DestructAssign(v) => v.eval(vm),
131 Self::Set(_) => bail!(forbidden("set")),
132 Self::Show(_) => bail!(forbidden("show")),
133 Self::Contextual(v) => v.eval(vm).map(Value::Content),
134 Self::Conditional(v) => v.eval(vm),
135 Self::While(v) => v.eval(vm),
136 Self::For(v) => v.eval(vm),
137 Self::Import(v) => v.eval(vm),
138 Self::Include(v) => v.eval(vm).map(Value::Content),
139 Self::Break(v) => v.eval(vm),
140 Self::Continue(v) => v.eval(vm),
141 Self::Return(v) => v.eval(vm),
142 }?
143 .spanned(span);
144
145 if vm.inspected == Some(span) {
146 vm.trace(v.clone());
147 }
148
149 Ok(v)
150 }
151}
152
153impl Eval for ast::Ident<'_> {
154 type Output = Value;
155
156 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
157 let span = self.span();
158 Ok(vm
159 .scopes
160 .get(&self)
161 .at(span)?
162 .read_checked((&mut vm.engine, span))
163 .clone())
164 }
165}
166
167impl Eval for ast::None<'_> {
168 type Output = Value;
169
170 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
171 Ok(Value::None)
172 }
173}
174
175impl Eval for ast::Auto<'_> {
176 type Output = Value;
177
178 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
179 Ok(Value::Auto)
180 }
181}
182
183impl Eval for ast::Bool<'_> {
184 type Output = Value;
185
186 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
187 Ok(Value::Bool(self.get()))
188 }
189}
190
191impl Eval for ast::Int<'_> {
192 type Output = Value;
193
194 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
195 Ok(Value::Int(self.get()))
196 }
197}
198
199impl Eval for ast::Float<'_> {
200 type Output = Value;
201
202 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
203 Ok(Value::Float(self.get()))
204 }
205}
206
207impl Eval for ast::Numeric<'_> {
208 type Output = Value;
209
210 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
211 Ok(Value::numeric(self.get()))
212 }
213}
214
215impl Eval for ast::Str<'_> {
216 type Output = Value;
217
218 fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
219 Ok(Value::Str(self.get().into()))
220 }
221}
222
223impl Eval for ast::Array<'_> {
224 type Output = Array;
225
226 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
227 let items = self.items();
228
229 let mut vec = EcoVec::with_capacity(items.size_hint().0);
230 for item in items {
231 match item {
232 ast::ArrayItem::Pos(expr) => vec.push(expr.eval(vm)?),
233 ast::ArrayItem::Spread(spread) => match spread.expr().eval(vm)? {
234 Value::None => {}
235 Value::Array(array) => vec.extend(array.into_iter()),
236 v => bail!(spread.span(), "cannot spread {} into array", v.ty()),
237 },
238 }
239 }
240
241 Ok(vec.into())
242 }
243}
244
245impl Eval for ast::Dict<'_> {
246 type Output = Dict;
247
248 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
249 let mut map = indexmap::IndexMap::new();
250 let mut invalid_keys = eco_vec![];
251
252 for item in self.items() {
253 match item {
254 ast::DictItem::Named(named) => {
255 map.insert(named.name().get().clone().into(), named.expr().eval(vm)?);
256 }
257 ast::DictItem::Keyed(keyed) => {
258 let raw_key = keyed.key();
259 let key = raw_key.eval(vm)?;
260 let key =
261 key.cast::<Str>().at(raw_key.span()).unwrap_or_else(|errors| {
262 invalid_keys.extend(errors);
263 Str::default()
264 });
265 map.insert(key, keyed.expr().eval(vm)?);
266 }
267 ast::DictItem::Spread(spread) => match spread.expr().eval(vm)? {
268 Value::None => {}
269 Value::Dict(dict) => map.extend(dict.into_iter()),
270 v => bail!(spread.span(), "cannot spread {} into dictionary", v.ty()),
271 },
272 }
273 }
274
275 if !invalid_keys.is_empty() {
276 return Err(invalid_keys);
277 }
278
279 Ok(map.into())
280 }
281}
282
283impl Eval for ast::CodeBlock<'_> {
284 type Output = Value;
285
286 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
287 vm.scopes.enter();
288 let output = self.body().eval(vm)?;
289 vm.scopes.exit();
290 Ok(output)
291 }
292}
293
294impl Eval for ast::ContentBlock<'_> {
295 type Output = Content;
296
297 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
298 vm.scopes.enter();
299 let content = self.body().eval(vm)?;
300 vm.scopes.exit();
301 Ok(content)
302 }
303}
304
305impl Eval for ast::Parenthesized<'_> {
306 type Output = Value;
307
308 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
309 self.expr().eval(vm)
310 }
311}
312
313impl Eval for ast::FieldAccess<'_> {
314 type Output = Value;
315
316 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
317 let value = self.target().eval(vm)?;
318 let field = self.field();
319 let field_span = field.span();
320
321 let err = match value.field(&field, (&mut vm.engine, field_span)).at(field_span) {
322 Ok(value) => return Ok(value),
323 Err(err) => err,
324 };
325
326 if_chain::if_chain! {
328 if let Value::Func(func) = &value;
329 if let Some(element) = func.element();
330 if let Some(id) = element.field_id(&field);
331 let styles = vm.context.styles().at(field.span());
332 if let Ok(value) = element.field_from_styles(
333 id,
334 styles.as_ref().map(|&s| s).unwrap_or_default(),
335 );
336 then {
337 let _ = styles?;
340 return Ok(value);
341 }
342 }
343
344 Err(err)
345 }
346}
347
348impl Eval for ast::Contextual<'_> {
349 type Output = Content;
350
351 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
352 let body = self.body();
353
354 let captured = {
356 let mut visitor = CapturesVisitor::new(Some(&vm.scopes), Capturer::Context);
357 visitor.visit(body.to_untyped());
358 visitor.finish()
359 };
360
361 let closure = Closure {
363 node: self.body().to_untyped().clone(),
364 defaults: vec![],
365 captured,
366 num_pos_params: 0,
367 };
368
369 let func = Func::from(closure).spanned(body.span());
370 Ok(ContextElem::new(func).pack().spanned(body.span()))
371 }
372}
373
374fn warn_for_discarded_content(engine: &mut Engine, event: &FlowEvent, joined: &Value) {
376 let FlowEvent::Return(span, Some(_), false) = event else { return };
377 let Value::Content(tree) = &joined else { return };
378
379 let selector = singleton!(
380 Selector,
381 Selector::Or(eco_vec![State::select_any(), Counter::select_any()])
382 );
383
384 let mut warning = warning!(
385 *span,
386 "this return unconditionally discards the content before it";
387 hint: "try omitting the `return` to automatically join all values"
388 );
389
390 if tree.query_first(selector).is_some() {
391 warning.hint("state/counter updates are content that must end up in the document to have an effect");
392 }
393
394 engine.sink.warn(warning);
395}