typst_library/foundations/func.rs
1#[doc(inline)]
2pub use typst_macros::func;
3use typst_syntax::ast::AstNode;
4
5use std::fmt::{self, Debug, Formatter};
6use std::sync::{Arc, LazyLock};
7
8use comemo::{Tracked, TrackedMut};
9use ecow::{EcoString, eco_format};
10use either::Either;
11use typst_syntax::{Span, Spanned, SyntaxNode, ast};
12use typst_utils::{DefSite, LazyHash, Static, singleton};
13
14use crate::diag::{At, SourceResult, StrResult, WarningSink, bail};
15use crate::engine::Engine;
16use crate::foundations::{
17 Args, Bytes, CastInfo, Content, Context, Element, IntoArgs, PluginFunc, Repr, Scope,
18 Selector, Type, Value, cast, scope, ty,
19};
20
21/// A mapping from argument values to a return value.
22///
23/// You can call a function by writing a comma-separated list of function
24/// _arguments_ enclosed in parentheses directly after the function name.
25/// Additionally, you can pass any number of trailing content block arguments to
26/// a function _after_ the normal argument list. If the normal argument list
27/// would become empty, it can be omitted. Typst supports positional and named
28/// arguments. The former are identified by position and type, while the latter
29/// are written as `name: value`.
30///
31/// Within math mode, function calls have special behaviour. See the
32/// @math[math documentation] for more details.
33///
34/// = Example <example>
35/// ```example
36/// // Call a function.
37/// #list([A], [B])
38///
39/// // Named arguments and trailing
40/// // content blocks.
41/// #enum(start: 2)[A][B]
42///
43/// // Version without parentheses.
44/// #list[A][B]
45/// ```
46///
47/// Functions are a fundamental building block of Typst. Typst provides
48/// functions for a variety of typesetting tasks. Moreover, the markup you write
49/// is backed by functions and all styling happens through functions. This
50/// reference lists all available functions and how you can use them. Please
51/// also refer to the documentation about @reference:styling:set-rules[set] and
52/// @reference:styling:show-rules[show] rules to learn about additional ways you
53/// can work with functions in Typst.
54///
55/// = Element functions <element-functions>
56/// Some functions are associated with _elements_ like @heading[headings] or
57/// @table[tables]. When called, these create an element of their respective
58/// kind. In contrast to normal functions, they can further be used in
59/// @reference:styling:set-rules[set rules],
60/// @reference:styling:show-rules[show rules], and @selector[selectors].
61///
62/// = Function scopes <function-scopes>
63/// Functions can hold related definitions in their own scope, similar to a
64/// @reference:scripting:modules[module]. Examples of this are @assert.eq or
65/// @list.item. However, this feature is currently only available for built-in
66/// functions.
67///
68/// = Defining functions <defining-functions>
69/// You can define your own function with a
70/// @reference:scripting:bindings[let binding] that has a parameter list after
71/// the binding's name. The parameter list can contain mandatory positional
72/// parameters, named parameters with default values and
73/// @arguments[argument sinks].
74///
75/// The right-hand side of a function binding is the function body, which can be
76/// a block or any other expression. It defines the function's return value and
77/// can depend on the parameters. If the function body is a
78/// @reference:scripting:blocks[code block], the return value is the result of
79/// joining the values of each expression in the block.
80///
81/// Within a function body, the `return` keyword can be used to exit early and
82/// optionally specify a return value. If no explicit return value is given, the
83/// body evaluates to the result of joining all expressions preceding the
84/// `return`.
85///
86/// Functions that don't return any meaningful value return @none instead. The
87/// return type of such functions is not explicitly specified in the
88/// documentation. (An example of this is @array.push).
89///
90/// ```example
91/// #let alert(body, fill: red) = {
92/// set text(white)
93/// set align(center)
94/// rect(
95/// fill: fill,
96/// inset: 8pt,
97/// radius: 4pt,
98/// [*Warning:\ #body*],
99/// )
100/// }
101///
102/// #alert[
103/// Danger is imminent!
104/// ]
105///
106/// #alert(fill: blue)[
107/// KEEP OFF TRACKS
108/// ]
109/// ```
110///
111/// = Importing functions <importing-functions>
112/// Functions can be imported from one file
113/// (@reference:scripting:modules[`module`]) into another using `{import}`. For
114/// example, assume that we have defined the `alert` function from the previous
115/// example in a file called `foo.typ`. We can import it into another file by
116/// writing `{import "foo.typ": alert}`.
117///
118/// = #short-or-long[Unnamed][Unnamed functions] <unnamed>
119/// You can also create an unnamed function without creating a binding by
120/// specifying a parameter list followed by `=>` and the function body. If your
121/// function has just one parameter, the parentheses around the parameter list
122/// are optional. Unnamed functions are mainly useful for show rules, but also
123/// as arguments to other functions, like @array.position.
124///
125/// ```example
126/// #show "once?": it => [#it #it]
127/// once?
128/// ```
129///
130/// = Note on function purity <note-on-function-purity>
131/// In Typst, all functions are _pure._ This means that for the same arguments,
132/// they always return the same result. They cannot "remember" things to produce
133/// another value when they are called a second time.
134///
135/// The only exception are built-in methods like
136/// @array.push[`array.push(value)`]. These can modify the values they are
137/// called on.
138#[ty(scope, cast, name = "function")]
139#[derive(Clone, Hash)]
140pub struct Func {
141 /// The internal representation.
142 inner: FuncInner,
143 /// The span with which errors are reported when this function is called.
144 span: Span,
145}
146
147/// The different kinds of function representations.
148#[derive(Clone, PartialEq, Hash)]
149enum FuncInner {
150 /// A native Rust function.
151 Native(Static<NativeFuncData>),
152 /// A function for an element.
153 Element(Element),
154 /// A user-defined closure.
155 Closure(Arc<LazyHash<Closure>>),
156 /// A plugin WebAssembly function.
157 Plugin(Arc<PluginFunc>),
158 /// A nested function with pre-applied arguments.
159 With(Arc<(Func, Args)>),
160}
161
162impl Func {
163 /// The function's name (e.g. `min`).
164 ///
165 /// Returns `None` if this is an anonymous closure.
166 pub fn name(&self) -> Option<&str> {
167 match &self.inner {
168 FuncInner::Native(native) => Some(native.name),
169 FuncInner::Element(elem) => Some(elem.name()),
170 FuncInner::Closure(closure) => closure.name(),
171 FuncInner::Plugin(func) => Some(func.name()),
172 FuncInner::With(with) => with.0.name(),
173 }
174 }
175
176 /// The function's title case name, for use in documentation (e.g. `Minimum`).
177 ///
178 /// Returns `None` if this is a closure.
179 pub fn title(&self) -> Option<&'static str> {
180 match &self.inner {
181 FuncInner::Native(native) => Some(native.title),
182 FuncInner::Element(elem) => Some(elem.title()),
183 FuncInner::Closure(_) => None,
184 FuncInner::Plugin(_) => None,
185 FuncInner::With(with) => with.0.title(),
186 }
187 }
188
189 /// Documentation for the function (as Markdown).
190 pub fn docs(&self) -> Option<&'static str> {
191 match &self.inner {
192 FuncInner::Native(native) => Some(native.docs),
193 FuncInner::Element(elem) => Some(elem.docs()),
194 FuncInner::Closure(_) => None,
195 FuncInner::Plugin(_) => None,
196 FuncInner::With(with) => with.0.docs(),
197 }
198 }
199
200 /// Whether the function is known to be contextual.
201 pub fn contextual(&self) -> Option<bool> {
202 match &self.inner {
203 FuncInner::Native(native) => Some(native.contextual),
204 _ => None,
205 }
206 }
207
208 /// Get details about this function's parameters if available.
209 pub fn params(&self) -> impl Iterator<Item = ParamInfo> {
210 match &self.inner {
211 FuncInner::Native(native) => {
212 Either::Left(native.0.params.iter().map(ParamInfo::Native))
213 }
214 FuncInner::Element(elem) => {
215 Either::Left(elem.params().iter().map(ParamInfo::Native))
216 }
217 FuncInner::Closure(closure) => {
218 Either::Right(Either::Left(closure.params().map(ParamInfo::Closure)))
219 }
220 FuncInner::Plugin(_) => {
221 Either::Right(Either::Right([ParamInfo::Plugin].into_iter()))
222 }
223 // TODO: We could take into account the known arguments.
224 FuncInner::With(with) => with.0.params(),
225 }
226 }
227
228 /// Get the parameter info for a parameter with the given name if it exist.
229 pub fn param(&self, name: &str) -> Option<ParamInfo> {
230 self.params().find(|param| param.name() == Some(name))
231 }
232
233 /// Get details about the function's return type.
234 pub fn returns(&self) -> Option<&'static CastInfo> {
235 match &self.inner {
236 FuncInner::Native(native) => Some(&native.0.returns),
237 FuncInner::Element(_) => {
238 Some(singleton!(CastInfo, CastInfo::Type(Type::of::<Content>())))
239 }
240 FuncInner::Closure(_) => None,
241 FuncInner::Plugin(_) => None,
242 FuncInner::With(with) => with.0.returns(),
243 }
244 }
245
246 /// Search keywords for the function.
247 pub fn keywords(&self) -> &'static [&'static str] {
248 match &self.inner {
249 FuncInner::Native(native) => native.keywords,
250 FuncInner::Element(elem) => elem.keywords(),
251 FuncInner::Closure(_) => &[],
252 FuncInner::Plugin(_) => &[],
253 FuncInner::With(with) => with.0.keywords(),
254 }
255 }
256
257 /// Where the function is defined in the Rust source code (only `Some(_)` if
258 /// it is native, and even then it can be `None` if the function is
259 /// generated, like the typed HTML API).
260 pub fn def_site(&self) -> Option<DefSite> {
261 match &self.inner {
262 FuncInner::Native(native) => native.def_site,
263 FuncInner::Element(elem) => Some(elem.def_site()),
264 _ => None,
265 }
266 }
267
268 /// The function's associated scope of sub-definition.
269 pub fn scope(&self) -> Option<&'static Scope> {
270 match &self.inner {
271 FuncInner::Native(native) => Some(&native.0.scope),
272 FuncInner::Element(elem) => Some(elem.scope()),
273 FuncInner::Closure(_) => None,
274 FuncInner::Plugin(_) => None,
275 FuncInner::With(with) => with.0.scope(),
276 }
277 }
278
279 /// Get a field from this function's scope, if possible.
280 pub fn field(
281 &self,
282 field: &str,
283 sink: impl WarningSink,
284 ) -> StrResult<&'static Value> {
285 let scope =
286 self.scope().ok_or("cannot access fields on user-defined functions")?;
287 match scope.get(field) {
288 Some(binding) => Ok(binding.read_checked(sink)),
289 None => match self.name() {
290 Some(name) => bail!("function `{name}` does not contain field `{field}`"),
291 None => bail!("function does not contain field `{field}`"),
292 },
293 }
294 }
295
296 /// Extract the element function, if it is one.
297 pub fn to_element(&self) -> Option<Element> {
298 match self.inner {
299 FuncInner::Element(func) => Some(func),
300 _ => None,
301 }
302 }
303
304 /// Extract the plugin function, if it is one.
305 pub fn to_plugin(&self) -> Option<&PluginFunc> {
306 match &self.inner {
307 FuncInner::Plugin(func) => Some(func),
308 _ => None,
309 }
310 }
311
312 /// Call the function with the given context and arguments.
313 pub fn call<A: IntoArgs>(
314 &self,
315 engine: &mut Engine,
316 context: Tracked<Context>,
317 args: A,
318 ) -> SourceResult<Value> {
319 self.call_impl(engine, context, args.into_args(self.span))
320 }
321
322 /// Non-generic implementation of `call`.
323 #[typst_macros::time(name = "func call", span = self.span())]
324 fn call_impl(
325 &self,
326 engine: &mut Engine,
327 context: Tracked<Context>,
328 mut args: Args,
329 ) -> SourceResult<Value> {
330 match &self.inner {
331 FuncInner::Native(native) => {
332 let value = (native.function.0)(engine, context, &mut args)?;
333 args.finish()?;
334 Ok(value)
335 }
336 FuncInner::Element(func) => {
337 let value = func.construct(engine, &mut args)?;
338 args.finish()?;
339 Ok(Value::Content(value))
340 }
341 FuncInner::Closure(closure) => (engine.library.routines.eval_closure)(
342 self,
343 closure,
344 engine.world,
345 engine.library,
346 engine.introspector.into_raw(),
347 engine.traced,
348 TrackedMut::reborrow_mut(&mut engine.sink),
349 engine.route.track(),
350 context,
351 args,
352 ),
353 FuncInner::Plugin(func) => {
354 let inputs = args.all::<Bytes>()?;
355 let output = func.call(inputs).at(args.span)?;
356 args.finish()?;
357 Ok(Value::Bytes(output))
358 }
359 FuncInner::With(with) => {
360 args.items = with.1.items.iter().cloned().chain(args.items).collect();
361 with.0.call(engine, context, args)
362 }
363 }
364 }
365
366 /// The function's span.
367 pub fn span(&self) -> Span {
368 self.span
369 }
370
371 /// Attach a span to this function if it doesn't already have one.
372 pub fn spanned(mut self, span: Span) -> Self {
373 if self.span.is_detached() {
374 self.span = span;
375 }
376 self
377 }
378}
379
380#[scope]
381impl Func {
382 /// Returns a new function that has the given arguments pre-applied.
383 #[func]
384 pub fn with(
385 self,
386 args: &mut Args,
387 /// The arguments to apply to the function.
388 #[external]
389 #[variadic]
390 arguments: Vec<Value>,
391 ) -> Func {
392 let span = self.span;
393 Self {
394 inner: FuncInner::With(Arc::new((self, args.take()))),
395 span,
396 }
397 }
398
399 /// Returns a selector that filters for elements belonging to this function
400 /// whose fields have the values of the given arguments.
401 ///
402 /// ```example
403 /// #show heading.where(level: 2): set text(blue)
404 /// = Section
405 /// == Subsection
406 /// === Sub-subsection
407 /// ```
408 #[func]
409 pub fn where_(
410 self,
411 args: &mut Args,
412 /// The fields to filter for.
413 #[variadic]
414 #[external]
415 fields: Vec<Value>,
416 ) -> StrResult<Selector> {
417 let fields = args.to_named();
418 args.items.retain(|arg| arg.name.is_none());
419
420 let element = self
421 .to_element()
422 .ok_or("`where()` can only be called on element functions")?;
423
424 let fields = fields
425 .into_iter()
426 .map(|(key, value)| {
427 element.field_id(&key).map(|id| (id, value)).ok_or_else(|| {
428 eco_format!(
429 "element `{}` does not have field `{}`",
430 element.name(),
431 key
432 )
433 })
434 })
435 .collect::<StrResult<smallvec::SmallVec<_>>>()?;
436
437 Ok(element.where_(fields))
438 }
439}
440
441impl Debug for Func {
442 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
443 write!(f, "Func({})", self.name().unwrap_or(".."))
444 }
445}
446
447impl Repr for Func {
448 fn repr(&self) -> EcoString {
449 const DEFAULT: &str = "(..) => ..";
450 match &self.inner {
451 FuncInner::Native(native) => native.name.into(),
452 FuncInner::Element(elem) => elem.name().into(),
453 FuncInner::Closure(closure) => closure.name().unwrap_or(DEFAULT).into(),
454 FuncInner::Plugin(func) => func.name().clone(),
455 FuncInner::With(_) => DEFAULT.into(),
456 }
457 }
458}
459
460impl PartialEq for Func {
461 fn eq(&self, other: &Self) -> bool {
462 self.inner == other.inner
463 }
464}
465
466impl PartialEq<&'static NativeFuncData> for Func {
467 fn eq(&self, other: &&'static NativeFuncData) -> bool {
468 match &self.inner {
469 FuncInner::Native(native) => *native == Static(*other),
470 _ => false,
471 }
472 }
473}
474
475impl PartialEq<Element> for Func {
476 fn eq(&self, other: &Element) -> bool {
477 match &self.inner {
478 FuncInner::Element(elem) => elem == other,
479 _ => false,
480 }
481 }
482}
483
484impl From<FuncInner> for Func {
485 fn from(inner: FuncInner) -> Self {
486 Self { inner, span: Span::detached() }
487 }
488}
489
490impl From<&'static NativeFuncData> for Func {
491 fn from(data: &'static NativeFuncData) -> Self {
492 FuncInner::Native(Static(data)).into()
493 }
494}
495
496impl From<Element> for Func {
497 fn from(func: Element) -> Self {
498 FuncInner::Element(func).into()
499 }
500}
501
502impl From<Closure> for Func {
503 fn from(closure: Closure) -> Self {
504 FuncInner::Closure(Arc::new(LazyHash::new(closure))).into()
505 }
506}
507
508impl From<PluginFunc> for Func {
509 fn from(func: PluginFunc) -> Self {
510 FuncInner::Plugin(Arc::new(func)).into()
511 }
512}
513
514/// Details about a function parameter.
515#[derive(Debug, Clone)]
516pub enum ParamInfo {
517 /// Details about a parameter of a native, Rust-defined function.
518 Native(&'static NativeParamInfo),
519 /// Details about a user-defined function.
520 Closure(Spanned<ClosureParamInfo>),
521 /// A plugin's sole variadic bytes parameter.
522 Plugin,
523}
524
525impl ParamInfo {
526 /// Attempts to cast to a native parameter info.
527 pub fn to_native(&self) -> Option<&'static NativeParamInfo> {
528 match self {
529 Self::Native(native) => Some(native),
530 _ => None,
531 }
532 }
533
534 /// The parameter's name, if any.
535 pub fn name(&self) -> Option<&str> {
536 match self {
537 Self::Native(info) => Some(info.name),
538 Self::Closure(info) => match &info.v {
539 ClosureParamInfo::Pos { name } => name.as_deref(),
540 ClosureParamInfo::Sink { name } => name.as_deref(),
541 ClosureParamInfo::Named { name, .. } => Some(name),
542 },
543 Self::Plugin => None,
544 }
545 }
546
547 /// The parameter's default value, if any.
548 pub fn default(&self) -> Option<Value> {
549 match self {
550 Self::Native(info) => info.default.map(|f| f()),
551 Self::Closure(info) => match &info.v {
552 ClosureParamInfo::Named { default, .. } => Some(default.clone()),
553 _ => None,
554 },
555 Self::Plugin => None,
556 }
557 }
558
559 /// Whether the parameter is positional (includes variadic).
560 pub fn positional(&self) -> bool {
561 match self {
562 Self::Native(info) => info.positional,
563 Self::Closure(info) => matches!(
564 &info.v,
565 ClosureParamInfo::Pos { .. } | ClosureParamInfo::Sink { .. }
566 ),
567 Self::Plugin => true,
568 }
569 }
570
571 /// Whether the parameter is named.
572 pub fn named(&self) -> bool {
573 match self {
574 Self::Native(info) => info.named,
575 Self::Closure(info) => matches!(&info.v, ClosureParamInfo::Named { .. }),
576 Self::Plugin => false,
577 }
578 }
579
580 /// Whether the parameter is variadic.
581 pub fn variadic(&self) -> bool {
582 match self {
583 Self::Native(info) => info.variadic,
584 Self::Closure(info) => matches!(&info.v, ClosureParamInfo::Sink { .. }),
585 Self::Plugin => true,
586 }
587 }
588
589 /// Whether the parameter is required.
590 pub fn required(&self) -> bool {
591 match self {
592 Self::Native(info) => info.required,
593 Self::Closure(info) => matches!(&info.v, ClosureParamInfo::Pos { .. }),
594 Self::Plugin => false,
595 }
596 }
597
598 /// Whether the parameter is settable.
599 pub fn settable(&self) -> bool {
600 match self {
601 Self::Native(info) => info.settable,
602 Self::Closure(_) => false,
603 Self::Plugin => false,
604 }
605 }
606}
607
608/// A Typst function that is defined by a native Rust type that shadows a
609/// native Rust function.
610pub trait NativeFunc {
611 /// Get the function for the native Rust type.
612 fn func() -> Func {
613 Func::from(Self::data())
614 }
615
616 /// Get the function data for the native Rust function.
617 fn data() -> &'static NativeFuncData;
618}
619
620/// Defines a native function.
621#[derive(Debug)]
622pub struct NativeFuncData {
623 /// The implementation of the function.
624 pub function: NativeFuncPtr,
625 /// The function's normal name (e.g. `align`), as exposed to Typst.
626 pub name: &'static str,
627 /// The function's title case name (e.g. `Align`).
628 pub title: &'static str,
629 /// The documentation for this function as a string.
630 pub docs: &'static str,
631 /// Where the function is defined in the source code.
632 pub def_site: Option<DefSite>,
633 /// A list of alternate search terms for this function.
634 pub keywords: &'static [&'static str],
635 /// Whether this function makes use of context.
636 pub contextual: bool,
637 /// Definitions in the scope of the function.
638 pub scope: DynLazyLock<Scope>,
639 /// A list of parameter information for each parameter.
640 pub params: DynLazyLock<Vec<NativeParamInfo>>,
641 /// Information about the return value of this function.
642 pub returns: DynLazyLock<CastInfo>,
643}
644
645cast! {
646 &'static NativeFuncData,
647 self => Func::from(self).into_value(),
648}
649
650/// A pointer to a native function's implementation.
651pub struct NativeFuncPtr(pub &'static NativeFuncSignature);
652
653/// The signature of a native function's implementation.
654type NativeFuncSignature =
655 dyn Fn(&mut Engine, Tracked<Context>, &mut Args) -> SourceResult<Value> + Send + Sync;
656
657impl Debug for NativeFuncPtr {
658 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
659 f.pad("NativeFuncPtr(..)")
660 }
661}
662
663/// A `LazyLock` that uses a static closure for initialization instead of only
664/// working with function pointers.
665///
666/// Can be created from a normal function or closure by prepending with a `&`,
667/// e.g. `LazyLock::new(&|| "hello")`. Can be created from a dynamic closure
668/// by allocating and then leaking it. This is equivalent to having it
669/// statically allocated, but allows for it to be generated at runtime.
670type DynLazyLock<T> = LazyLock<T, &'static (dyn Fn() -> T + Send + Sync)>;
671
672/// Describes a function parameter.
673#[derive(Debug, Clone)]
674pub struct NativeParamInfo {
675 /// The parameter's name.
676 pub name: &'static str,
677 /// Documentation for the parameter.
678 pub docs: &'static str,
679 /// Where the parameter is defined in the source code.
680 pub def_site: Option<DefSite>,
681 /// Describe what values this parameter accepts.
682 pub input: CastInfo,
683 /// Creates an instance of the parameter's default value.
684 pub default: Option<fn() -> Value>,
685 /// Is the parameter positional?
686 pub positional: bool,
687 /// Is the parameter named?
688 ///
689 /// Can be true even if `positional` is true if the parameter can be given
690 /// in both variants.
691 pub named: bool,
692 /// Can the parameter be given any number of times?
693 pub variadic: bool,
694 /// Is the parameter required?
695 pub required: bool,
696 /// Is the parameter settable with a set rule?
697 pub settable: bool,
698}
699
700/// Distinguishes between variants of closures.
701#[derive(Debug, Hash)]
702pub enum ClosureNode {
703 /// A regular closure. Must always be castable to a `ast::Closure`.
704 Closure(SyntaxNode),
705 /// Synthetic closure used for `context` expressions. Can be any `ast::Expr`
706 /// and has no parameters.
707 Context(SyntaxNode),
708}
709
710/// A user-defined closure.
711#[derive(Debug, Hash)]
712pub struct Closure {
713 /// The closure's syntax node.
714 pub node: ClosureNode,
715 /// Default values of named parameters.
716 pub defaults: Vec<Value>,
717 /// Captured values from outer scopes.
718 pub captured: Scope,
719 /// The number of positional parameters in the closure.
720 pub num_pos_params: usize,
721}
722
723impl Closure {
724 /// The name of the closure.
725 pub fn name(&self) -> Option<&str> {
726 match self.node {
727 ClosureNode::Closure(ref node) => {
728 node.cast::<ast::Closure>()?.name().map(|ident| ident.as_str())
729 }
730 _ => None,
731 }
732 }
733
734 /// Returns details about this closure's parameters.
735 pub fn params(&self) -> impl Iterator<Item = Spanned<ClosureParamInfo>> {
736 let params = match self.node {
737 ClosureNode::Closure(ref node) => Some(
738 node.cast::<ast::Closure>()
739 .expect("node to be an `ast::Closure`")
740 .params()
741 .children(),
742 ),
743 ClosureNode::Context(_) => None,
744 };
745
746 let mut defaults = self.defaults.iter();
747 params.into_iter().flatten().map(move |param| {
748 let info = match param {
749 ast::Param::Pos(pattern) => ClosureParamInfo::Pos {
750 name: match pattern {
751 ast::Pattern::Normal(ast::Expr::Ident(ident)) => {
752 Some(ident.get().clone())
753 }
754 _ => None,
755 },
756 },
757 ast::Param::Spread(spread) => ClosureParamInfo::Sink {
758 name: spread.sink_ident().map(|ident| ident.get().clone()),
759 },
760 ast::Param::Named(named) => ClosureParamInfo::Named {
761 name: named.name().get().clone(),
762 default: defaults.next().unwrap().clone(),
763 },
764 };
765 Spanned::new(info, param.span())
766 })
767 }
768}
769
770cast! {
771 Closure,
772 self => Value::Func(self.into()),
773}
774
775/// Details about a closure parameter.
776#[derive(Debug, Clone)]
777pub enum ClosureParamInfo {
778 /// A positional parameter. It might have a name, but it could also be a
779 /// pattern.
780 Pos { name: Option<EcoString> },
781 /// A sink parameter. Might have a name, but could also just be a discarding
782 /// sink.
783 Sink { name: Option<EcoString> },
784 /// A named parameter with its name and default value.
785 Named { name: EcoString, default: Value },
786}