typst_eval/
code.rs

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
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::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        // Check whether this is a get rule field access.
327        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                // Only validate the context once we know that this is indeed
338                // a field from the style chain.
339                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        // Collect captured variables.
355        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        // Define the closure.
362        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
374/// Emits a warning when we discard content while returning unconditionally.
375fn 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}