typst_eval/
code.rs

1use ecow::{EcoVec, eco_vec};
2use typst_library::diag::{At, SourceResult, bail, error, warning};
3use typst_library::engine::Engine;
4use typst_library::foundations::{
5    Array, Capturer, Closure, ClosureNode, Content, ContextElem, Dict, Func,
6    NativeElement, Selector, Str, Value, ops,
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
22/// Evaluate a stream of expressions.
23fn 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::SetRule(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::ShowRule(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).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::ListItem(v) => v.eval(vm).map(Value::Content),
98            Self::EnumItem(v) => v.eval(vm).map(Value::Content),
99            Self::TermItem(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::CodeBlock(v) => v.eval(vm),
120            Self::ContentBlock(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::LetBinding(v) => v.eval(vm),
130            Self::DestructAssignment(v) => v.eval(vm),
131            Self::SetRule(_) => bail!(forbidden("set")),
132            Self::ShowRule(_) => bail!(forbidden("show")),
133            Self::Contextual(v) => v.eval(vm).map(Value::Content),
134            Self::Conditional(v) => v.eval(vm),
135            Self::WhileLoop(v) => v.eval(vm),
136            Self::ForLoop(v) => v.eval(vm),
137            Self::ModuleImport(v) => v.eval(vm),
138            Self::ModuleInclude(v) => v.eval(vm).map(Value::Content),
139            Self::LoopBreak(v) => v.eval(vm),
140            Self::LoopContinue(v) => v.eval(vm),
141            Self::FuncReturn(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::default();
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        // Check whether this is a get rule field access.
327        if let Value::Func(func) = &value
328            && let Some(element) = func.element()
329            && let Some(id) = element.field_id(&field)
330            && let styles = vm.context.styles().at(field.span())
331            && let Ok(value) = element
332                .field_from_styles(id, styles.as_ref().map(|&s| s).unwrap_or_default())
333        {
334            // Only validate the context once we know that this is indeed
335            // a field from the style chain.
336            let _ = styles?;
337            return Ok(value);
338        }
339
340        Err(err)
341    }
342}
343
344impl Eval for ast::Contextual<'_> {
345    type Output = Content;
346
347    fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
348        let body = self.body();
349
350        // Collect captured variables.
351        let captured = {
352            let mut visitor = CapturesVisitor::new(Some(&vm.scopes), Capturer::Context);
353            visitor.visit(body.to_untyped());
354            visitor.finish()
355        };
356
357        // Define the closure.
358        let closure = Closure {
359            node: ClosureNode::Context(self.body().to_untyped().clone()),
360            defaults: vec![],
361            captured,
362            num_pos_params: 0,
363        };
364
365        let func = Func::from(closure).spanned(body.span());
366        Ok(ContextElem::new(func).pack().spanned(body.span()))
367    }
368}
369
370/// Emits a warning when we discard content while returning unconditionally.
371fn warn_for_discarded_content(engine: &mut Engine, event: &FlowEvent, joined: &Value) {
372    let FlowEvent::Return(span, Some(_), false) = event else { return };
373    let Value::Content(tree) = &joined else { return };
374
375    let selector = singleton!(
376        Selector,
377        Selector::Or(eco_vec![State::select_any(), Counter::select_any()])
378    );
379
380    let mut warning = warning!(
381        *span,
382        "this return unconditionally discards the content before it";
383        hint: "try omitting the `return` to automatically join all values"
384    );
385
386    if tree.query_first_naive(selector).is_some() {
387        warning.hint("state/counter updates are content that must end up in the document to have an effect");
388    }
389
390    engine.sink.warn(warning);
391}