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