1use comemo::{Tracked, TrackedMut};
2use ecow::{EcoString, EcoVec, eco_format};
3use typst_library::World;
4use typst_library::diag::{
5 At, HintedStrResult, HintedString, SourceDiagnostic, SourceResult, Trace, Tracepoint,
6 bail, error,
7};
8use typst_library::engine::{Engine, Sink, Traced};
9use typst_library::foundations::{
10 Arg, Args, Binding, Capturer, Closure, ClosureNode, Content, Context, Func,
11 NativeElement, Scope, Scopes, SymbolElem, Value,
12};
13use typst_library::introspection::Introspector;
14use typst_library::math::LrElem;
15use typst_library::routines::Routines;
16use typst_syntax::ast::{self, AstNode, Ident};
17use typst_syntax::{Span, Spanned, SyntaxNode};
18use typst_utils::LazyHash;
19
20use crate::{Access, Eval, FlowEvent, Route, Vm, call_method_mut, is_mutating_method};
21
22impl Eval for ast::FuncCall<'_> {
23 type Output = Value;
24
25 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
26 let span = self.span();
27 let callee = self.callee();
28 let callee_span = callee.span();
29 let args = self.args();
30
31 vm.engine.route.check_call_depth().at(span)?;
32
33 let (callee_value, args_value) = if let ast::Expr::FieldAccess(access) = callee {
35 let target = access.target();
36 let field = access.field();
37 match eval_field_call(target, field, args, span, vm)? {
38 FieldCall::Normal(callee, args) => {
39 if vm.inspected == Some(callee_span) {
40 vm.trace(callee.clone());
41 }
42 (callee, args)
43 }
44 FieldCall::Resolved(value) => return Ok(value),
45 }
46 } else {
47 (callee.eval(vm)?, args.eval(vm)?.spanned(span))
49 };
50
51 let func_result = callee_value.clone().cast::<Func>();
52
53 if func_result.is_err() && in_math(callee) {
54 return wrap_args_in_math(
55 callee_value,
56 callee_span,
57 args_value,
58 args.trailing_comma(),
59 );
60 }
61
62 let func = func_result
63 .map_err(|err| hint_if_shadowed_std(vm, &self.callee(), err))
64 .at(callee_span)?;
65
66 let point = || Tracepoint::Call(func.name().map(Into::into));
67 let f = || {
68 func.call(&mut vm.engine, vm.context, args_value).trace(
69 vm.world(),
70 point,
71 span,
72 )
73 };
74
75 #[cfg(target_arch = "wasm32")]
77 return f();
78
79 #[cfg(not(target_arch = "wasm32"))]
80 stacker::maybe_grow(32 * 1024, 2 * 1024 * 1024, f)
81 }
82}
83
84impl Eval for ast::Args<'_> {
85 type Output = Args;
86
87 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
88 let mut items = EcoVec::with_capacity(self.items().count());
89
90 for arg in self.items() {
91 let span = arg.span();
92 match arg {
93 ast::Arg::Pos(expr) => {
94 items.push(Arg {
95 span,
96 name: None,
97 value: Spanned::new(expr.eval(vm)?, expr.span()),
98 });
99 }
100 ast::Arg::Named(named) => {
101 let expr = named.expr();
102 items.push(Arg {
103 span,
104 name: Some(named.name().get().clone().into()),
105 value: Spanned::new(expr.eval(vm)?, expr.span()),
106 });
107 }
108 ast::Arg::Spread(spread) => match spread.expr().eval(vm)? {
109 Value::None => {}
110 Value::Array(array) => {
111 items.extend(array.into_iter().map(|value| Arg {
112 span,
113 name: None,
114 value: Spanned::new(value, span),
115 }));
116 }
117 Value::Dict(dict) => {
118 items.extend(dict.into_iter().map(|(key, value)| Arg {
119 span,
120 name: Some(key),
121 value: Spanned::new(value, span),
122 }));
123 }
124 Value::Args(args) => items.extend(args.items),
125 v => bail!(spread.span(), "cannot spread {}", v.ty()),
126 },
127 }
128 }
129
130 Ok(Args { span: Span::detached(), items })
133 }
134}
135
136impl Eval for ast::Closure<'_> {
137 type Output = Value;
138
139 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
140 let mut defaults = Vec::new();
142 for param in self.params().children() {
143 if let ast::Param::Named(named) = param {
144 defaults.push(named.expr().eval(vm)?);
145 }
146 }
147
148 let captured = {
150 let mut visitor = CapturesVisitor::new(Some(&vm.scopes), Capturer::Function);
151 visitor.visit(self.to_untyped());
152 visitor.finish()
153 };
154
155 let closure = Closure {
157 node: ClosureNode::Closure(self.to_untyped().clone()),
158 defaults,
159 captured,
160 num_pos_params: self
161 .params()
162 .children()
163 .filter(|p| matches!(p, ast::Param::Pos(_)))
164 .count(),
165 };
166
167 Ok(Value::Func(Func::from(closure).spanned(self.params().span())))
168 }
169}
170
171#[comemo::memoize]
173#[allow(clippy::too_many_arguments)]
174pub fn eval_closure(
175 func: &Func,
176 closure: &LazyHash<Closure>,
177 routines: &Routines,
178 world: Tracked<dyn World + '_>,
179 introspector: Tracked<Introspector>,
180 traced: Tracked<Traced>,
181 sink: TrackedMut<Sink>,
182 route: Tracked<Route>,
183 context: Tracked<Context>,
184 mut args: Args,
185) -> SourceResult<Value> {
186 let (name, params, body) = match closure.node {
187 ClosureNode::Closure(ref node) => {
188 let closure =
189 node.cast::<ast::Closure>().expect("node to be an `ast::Closure`");
190 (closure.name(), closure.params(), closure.body())
191 }
192 ClosureNode::Context(ref node) => {
193 (None, ast::Params::default(), node.cast().unwrap())
194 }
195 };
196
197 let mut scopes = Scopes::new(None);
200 scopes.top = closure.captured.clone();
201
202 let engine = Engine {
204 routines,
205 world,
206 introspector,
207 traced,
208 sink,
209 route: Route::extend(route),
210 };
211
212 let mut vm = Vm::new(engine, context, scopes, body.span());
214
215 if let Some(name) = name {
217 vm.define(name, func.clone());
218 }
219
220 let num_pos_args = args.to_pos().len();
221 let sink_size = num_pos_args.checked_sub(closure.num_pos_params);
222
223 let mut sink = None;
224 let mut sink_pos_values = None;
225 let mut defaults = closure.defaults.iter();
226 for p in params.children() {
227 match p {
228 ast::Param::Pos(pattern) => match pattern {
229 ast::Pattern::Normal(ast::Expr::Ident(ident)) => {
230 vm.define(ident, args.expect::<Value>(&ident)?)
231 }
232 pattern => {
233 crate::destructure(
234 &mut vm,
235 pattern,
236 args.expect::<Value>("pattern parameter")?,
237 )?;
238 }
239 },
240 ast::Param::Spread(spread) => {
241 sink = Some(spread.sink_ident());
242 if let Some(sink_size) = sink_size {
243 sink_pos_values = Some(args.consume(sink_size)?);
244 }
245 }
246 ast::Param::Named(named) => {
247 let name = named.name();
248 let default = defaults.next().unwrap();
249 let value =
250 args.named::<Value>(&name)?.unwrap_or_else(|| default.clone());
251 vm.define(name, value);
252 }
253 }
254 }
255
256 if let Some(sink) = sink {
257 let mut remaining_args = args.take();
259 if let Some(sink_name) = sink {
260 if let Some(sink_pos_values) = sink_pos_values {
261 remaining_args.items.extend(sink_pos_values);
262 }
263 vm.define(sink_name, remaining_args);
264 }
265 }
266
267 args.finish()?;
269
270 let output = body.eval(&mut vm)?;
272 match vm.flow {
273 Some(FlowEvent::Return(_, Some(explicit), _)) => return Ok(explicit),
274 Some(FlowEvent::Return(_, None, _)) => {}
275 Some(flow) => bail!(flow.forbidden()),
276 None => {}
277 }
278
279 Ok(output)
280}
281
282enum FieldCall {
286 Normal(Value, Args),
287 Resolved(Value),
288}
289
290fn eval_field_call(
304 target_expr: ast::Expr,
305 field: Ident,
306 args: ast::Args,
307 span: Span,
308 vm: &mut Vm,
309) -> SourceResult<FieldCall> {
310 let (target, mut args) = if is_mutating_method(&field) {
312 let args = args.eval(vm)?.spanned(span);
316 match target_expr.access(vm)? {
322 target @ (Value::Array(_) | Value::Dict(_)) => {
324 let value = call_method_mut(target, &field, args, span);
325 let point = || Tracepoint::Call(Some(field.get().clone()));
326 return Ok(FieldCall::Resolved(value.trace(vm.world(), point, span)?));
327 }
328 target => (target.clone(), args),
329 }
330 } else {
331 let target = target_expr.eval(vm)?;
332 let args = args.eval(vm)?.spanned(span);
333 (target, args)
334 };
335
336 let field_span = field.span();
337 let sink = (&mut vm.engine, field_span);
338 if let Some(callee) = target.ty().scope().get(&field) {
339 args.insert(0, target_expr.span(), target);
340 Ok(FieldCall::Normal(callee.read_checked(sink).clone(), args))
341 } else if let Value::Content(content) = &target {
342 if let Some(callee) = content.elem().scope().get(&field) {
343 args.insert(0, target_expr.span(), target);
344 Ok(FieldCall::Normal(callee.read_checked(sink).clone(), args))
345 } else {
346 bail!(missing_field_call_error(target, field))
347 }
348 } else if matches!(
349 target,
350 Value::Symbol(_) | Value::Func(_) | Value::Type(_) | Value::Module(_)
351 ) {
352 let value = target.field(&field, sink).at(field_span)?;
355 Ok(FieldCall::Normal(value, args))
356 } else {
357 bail!(missing_field_call_error(target, field))
359 }
360}
361
362fn missing_field_call_error(target: Value, field: Ident) -> SourceDiagnostic {
364 let mut error = match &target {
365 Value::Content(content) => error!(
366 field.span(),
367 "element {} has no method `{}`",
368 content.elem().name(),
369 field.as_str(),
370 ),
371 _ => error!(
372 field.span(),
373 "type {} has no method `{}`",
374 target.ty(),
375 field.as_str()
376 ),
377 };
378
379 match target {
380 Value::Dict(ref dict) if matches!(dict.get(&field), Ok(Value::Func(_))) => {
381 error.hint(eco_format!(
382 "to call the function stored in the dictionary, surround \
383 the field access with parentheses, e.g. `(dict.{})(..)`",
384 field.as_str(),
385 ));
386 }
387 _ if target.field(&field, ()).is_ok() => {
388 error.hint(eco_format!(
389 "did you mean to access the field `{}`?",
390 field.as_str(),
391 ));
392 }
393 _ => {}
394 }
395
396 error
397}
398
399fn in_math(expr: ast::Expr) -> bool {
401 match expr {
402 ast::Expr::MathIdent(_) => true,
403 ast::Expr::FieldAccess(access) => in_math(access.target()),
404 _ => false,
405 }
406}
407
408fn wrap_args_in_math(
410 callee: Value,
411 callee_span: Span,
412 mut args: Args,
413 trailing_comma: bool,
414) -> SourceResult<Value> {
415 let mut body = Content::empty();
416 for (i, arg) in args.all::<Content>()?.into_iter().enumerate() {
417 if i > 0 {
418 body += SymbolElem::packed(',');
419 }
420 body += arg;
421 }
422 if trailing_comma {
423 body += SymbolElem::packed(',');
424 }
425
426 let formatted = callee.display().spanned(callee_span)
427 + LrElem::new(SymbolElem::packed('(') + body + SymbolElem::packed(')'))
428 .pack()
429 .spanned(args.span);
430
431 args.finish()?;
432 Ok(Value::Content(formatted))
433}
434
435fn hint_if_shadowed_std(
437 vm: &mut Vm,
438 callee: &ast::Expr,
439 mut err: HintedString,
440) -> HintedString {
441 if let ast::Expr::Ident(ident) = callee {
442 let ident = ident.get();
443 if vm.scopes.check_std_shadowed(ident) {
444 err.hint(eco_format!(
445 "use `std.{ident}` to access the shadowed standard library function",
446 ));
447 }
448 }
449 err
450}
451
452pub struct CapturesVisitor<'a> {
454 external: Option<&'a Scopes<'a>>,
455 internal: Scopes<'a>,
456 captures: Scope,
457 capturer: Capturer,
458}
459
460impl<'a> CapturesVisitor<'a> {
461 pub fn new(external: Option<&'a Scopes<'a>>, capturer: Capturer) -> Self {
463 Self {
464 external,
465 internal: Scopes::new(None),
466 captures: Scope::new(),
467 capturer,
468 }
469 }
470
471 pub fn finish(self) -> Scope {
473 self.captures
474 }
475
476 pub fn visit(&mut self, node: &SyntaxNode) {
478 match node.cast() {
479 Some(ast::Expr::Ident(ident)) => self.capture(ident.get(), Scopes::get),
484 Some(ast::Expr::MathIdent(ident)) => {
485 self.capture(ident.get(), Scopes::get_in_math)
486 }
487
488 Some(ast::Expr::CodeBlock(_) | ast::Expr::ContentBlock(_)) => {
490 self.internal.enter();
491 for child in node.children() {
492 self.visit(child);
493 }
494 self.internal.exit();
495 }
496
497 Some(ast::Expr::FieldAccess(access)) => {
499 self.visit(access.target().to_untyped());
500 }
501
502 Some(ast::Expr::Closure(expr)) => {
506 for param in expr.params().children() {
507 if let ast::Param::Named(named) = param {
508 self.visit(named.expr().to_untyped());
509 }
510 }
511
512 self.internal.enter();
513 if let Some(name) = expr.name() {
514 self.bind(name);
515 }
516
517 for param in expr.params().children() {
518 match param {
519 ast::Param::Pos(pattern) => {
520 for ident in pattern.bindings() {
521 self.bind(ident);
522 }
523 }
524 ast::Param::Named(named) => self.bind(named.name()),
525 ast::Param::Spread(spread) => {
526 if let Some(ident) = spread.sink_ident() {
527 self.bind(ident);
528 }
529 }
530 }
531 }
532
533 self.visit(expr.body().to_untyped());
534 self.internal.exit();
535 }
536
537 Some(ast::Expr::LetBinding(expr)) => {
540 if let Some(init) = expr.init() {
541 self.visit(init.to_untyped());
542 }
543
544 for ident in expr.kind().bindings() {
545 self.bind(ident);
546 }
547 }
548
549 Some(ast::Expr::ForLoop(expr)) => {
553 self.visit(expr.iterable().to_untyped());
554 self.internal.enter();
555
556 let pattern = expr.pattern();
557 for ident in pattern.bindings() {
558 self.bind(ident);
559 }
560
561 self.visit(expr.body().to_untyped());
562 self.internal.exit();
563 }
564
565 Some(ast::Expr::ModuleImport(expr)) => {
568 self.visit(expr.source().to_untyped());
569 if let Some(ast::Imports::Items(items)) = expr.imports() {
570 for item in items.iter() {
571 self.bind(item.bound_name());
572 }
573 }
574 }
575
576 _ => {
577 if let Some(named) = node.cast::<ast::Named>() {
579 self.visit(named.expr().to_untyped());
580 return;
581 }
582
583 for child in node.children() {
585 self.visit(child);
586 }
587 }
588 }
589 }
590
591 fn bind(&mut self, ident: ast::Ident) {
593 self.internal
596 .top
597 .bind(ident.get().clone(), Binding::detached(Value::None));
598 }
599
600 fn capture(
602 &mut self,
603 ident: &EcoString,
604 getter: impl FnOnce(&'a Scopes<'a>, &str) -> HintedStrResult<&'a Binding>,
605 ) {
606 if self.internal.get(ident).is_ok() {
607 return;
608 }
609
610 let binding = match self.external {
611 Some(external) => match getter(external, ident) {
612 Ok(binding) => binding.capture(self.capturer),
613 Err(_) => return,
614 },
615 None => Binding::detached(Value::None),
618 };
619
620 self.captures.bind(ident.clone(), binding);
621 }
622}
623
624#[cfg(test)]
625mod tests {
626 use typst_syntax::parse;
627
628 use super::*;
629
630 #[track_caller]
631 fn test(scopes: &Scopes, text: &str, result: &[&str]) {
632 let mut visitor = CapturesVisitor::new(Some(scopes), Capturer::Function);
633 let root = parse(text);
634 visitor.visit(&root);
635
636 let captures = visitor.finish();
637 let mut names: Vec<_> = captures.iter().map(|(k, ..)| k).collect();
638 names.sort();
639
640 assert_eq!(names, result);
641 }
642
643 #[test]
644 fn test_captures() {
645 let mut scopes = Scopes::new(None);
646 scopes.top.define("f", 0);
647 scopes.top.define("x", 0);
648 scopes.top.define("y", 0);
649 scopes.top.define("z", 0);
650 let s = &scopes;
651
652 test(s, "#let x = x", &["x"]);
654 test(s, "#let x; #(x + y)", &["y"]);
655 test(s, "#let f(x, y) = x + y", &[]);
656 test(s, "#let f(x, y) = f", &[]);
657 test(s, "#let f = (x, y) => f", &["f"]);
658
659 test(s, "#((x, y) => x + z)", &["z"]);
661 test(s, "#((x: y, z) => x + z)", &["y"]);
662 test(s, "#((..x) => x + y)", &["y"]);
663 test(s, "#((x, y: x + z) => x + y)", &["x", "z"]);
664 test(s, "#{x => x; x}", &["x"]);
665
666 test(s, "#show y: x => x", &["y"]);
668 test(s, "#show y: x => x + z", &["y", "z"]);
669 test(s, "#show x: x => x", &["x"]);
670
671 test(s, "#for x in y { x + z }", &["y", "z"]);
673 test(s, "#for (x, y) in y { x + y }", &["y"]);
674 test(s, "#for x in y {} #x", &["x", "y"]);
675
676 test(s, "#import z: x, y", &["z"]);
678 test(s, "#import x + y: x, y, z", &["x", "y"]);
679
680 test(s, "#{ let x = 1; { let y = 2; y }; x + y }", &["y"]);
682 test(s, "#[#let x = 1]#x", &["x"]);
683
684 test(s, "#x.y.f(z)", &["x", "z"]);
686
687 test(s, "#f(x: 1)", &["f"]);
689 test(s, "#(x: 1)", &[]);
690 test(s, "#(x = 1)", &["x"]);
691 test(s, "#(x += y)", &["x", "y"]);
692 test(s, "#{ (x, z) = (y, 1) }", &["x", "y", "z"]);
693 test(s, "#(x.at(y) = 5)", &["x", "y"]);
694 }
695
696 #[test]
697 fn test_captures_in_math() {
698 let mut scopes = Scopes::new(None);
699 scopes.top.define("f", 0);
700 scopes.top.define("x", 0);
701 scopes.top.define("y", 0);
702 scopes.top.define("z", 0);
703 scopes.top.define("foo", 0);
705 scopes.top.define("bar", 0);
706 scopes.top.define("x-bar", 0);
707 scopes.top.define("x_bar", 0);
708 let s = &scopes;
709
710 test(s, "$ x f(z) $", &[]); test(s, "$ #x #f(z) $", &["f", "x", "z"]);
713 test(s, "$ foo f(bar) $", &["bar", "foo"]);
714 test(s, "$ #foo[#$bar$] $", &["bar", "foo"]);
715 test(s, "$ #let foo = x; foo $", &["x"]);
716
717 test(s, "$ x-y x_y foo-x x_bar $", &["bar", "foo"]);
719 test(s, "$ #x-bar #x_bar $", &["x-bar", "x_bar"]);
720
721 test(s, "$ foo(bar: y) $", &["foo"]);
723 test(s, "$ foo(x-y: 1, bar-z: 2) $", &["foo"]);
724
725 test(s, "$ foo.bar $", &["foo"]);
727 test(s, "$ foo.x $", &["foo"]);
728 test(s, "$ x.foo $", &["foo"]);
729 test(s, "$ foo . bar $", &["bar", "foo"]);
730 test(s, "$ foo.x.y.bar(z) $", &["foo"]);
731 test(s, "$ foo.x-bar $", &["bar", "foo"]);
732 test(s, "$ foo.x_bar $", &["bar", "foo"]);
733 test(s, "$ #x_bar.x-bar $", &["x_bar"]);
734 }
735}