typst_syntax/
ast.rs

1//! A typed layer over the untyped syntax tree.
2//!
3//! The AST is rooted in the [`Markup`] node.
4
5use std::num::NonZeroUsize;
6use std::ops::Deref;
7use std::path::Path;
8use std::str::FromStr;
9
10use ecow::EcoString;
11use unscanny::Scanner;
12
13use crate::package::PackageSpec;
14use crate::{is_ident, is_newline, Span, SyntaxKind, SyntaxNode};
15
16/// A typed AST node.
17pub trait AstNode<'a>: Sized {
18    /// Convert a node into its typed variant.
19    fn from_untyped(node: &'a SyntaxNode) -> Option<Self>;
20
21    /// A reference to the underlying syntax node.
22    fn to_untyped(self) -> &'a SyntaxNode;
23
24    /// The source code location.
25    fn span(self) -> Span {
26        self.to_untyped().span()
27    }
28}
29
30macro_rules! node {
31    ($(#[$attr:meta])* $name:ident) => {
32        #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
33        #[repr(transparent)]
34        $(#[$attr])*
35        pub struct $name<'a>(&'a SyntaxNode);
36
37        impl<'a> AstNode<'a> for $name<'a> {
38            #[inline]
39            fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
40                if node.kind() == SyntaxKind::$name {
41                    Some(Self(node))
42                } else {
43                    Option::None
44                }
45            }
46
47            #[inline]
48            fn to_untyped(self) -> &'a SyntaxNode {
49                self.0
50            }
51        }
52
53        impl Default for $name<'_> {
54            #[inline]
55            fn default() -> Self {
56                static PLACEHOLDER: SyntaxNode
57                    = SyntaxNode::placeholder(SyntaxKind::$name);
58                Self(&PLACEHOLDER)
59            }
60        }
61    };
62}
63
64node! {
65    /// The syntactical root capable of representing a full parsed document.
66    Markup
67}
68
69impl<'a> Markup<'a> {
70    /// The expressions.
71    pub fn exprs(self) -> impl DoubleEndedIterator<Item = Expr<'a>> {
72        let mut was_stmt = false;
73        self.0
74            .children()
75            .filter(move |node| {
76                // Ignore newline directly after statements without semicolons.
77                let kind = node.kind();
78                let keep = !was_stmt || node.kind() != SyntaxKind::Space;
79                was_stmt = kind.is_stmt();
80                keep
81            })
82            .filter_map(Expr::cast_with_space)
83    }
84}
85
86/// An expression in markup, math or code.
87#[derive(Debug, Copy, Clone, Hash)]
88pub enum Expr<'a> {
89    /// Plain text without markup.
90    Text(Text<'a>),
91    /// Whitespace in markup or math. Has at most one newline in markup, as more
92    /// indicate a paragraph break.
93    Space(Space<'a>),
94    /// A forced line break: `\`.
95    Linebreak(Linebreak<'a>),
96    /// A paragraph break, indicated by one or multiple blank lines.
97    Parbreak(Parbreak<'a>),
98    /// An escape sequence: `\#`, `\u{1F5FA}`.
99    Escape(Escape<'a>),
100    /// A shorthand for a unicode codepoint. For example, `~` for non-breaking
101    /// space or `-?` for a soft hyphen.
102    Shorthand(Shorthand<'a>),
103    /// A smart quote: `'` or `"`.
104    SmartQuote(SmartQuote<'a>),
105    /// Strong content: `*Strong*`.
106    Strong(Strong<'a>),
107    /// Emphasized content: `_Emphasized_`.
108    Emph(Emph<'a>),
109    /// Raw text with optional syntax highlighting: `` `...` ``.
110    Raw(Raw<'a>),
111    /// A hyperlink: `https://typst.org`.
112    Link(Link<'a>),
113    /// A label: `<intro>`.
114    Label(Label<'a>),
115    /// A reference: `@target`, `@target[..]`.
116    Ref(Ref<'a>),
117    /// A section heading: `= Introduction`.
118    Heading(Heading<'a>),
119    /// An item in a bullet list: `- ...`.
120    List(ListItem<'a>),
121    /// An item in an enumeration (numbered list): `+ ...` or `1. ...`.
122    Enum(EnumItem<'a>),
123    /// An item in a term list: `/ Term: Details`.
124    Term(TermItem<'a>),
125    /// A mathematical equation: `$x$`, `$ x^2 $`.
126    Equation(Equation<'a>),
127    /// The contents of a mathematical equation: `x^2 + 1`.
128    Math(Math<'a>),
129    /// A lone text fragment in math: `x`, `25`, `3.1415`, `=`, `[`.
130    MathText(MathText<'a>),
131    /// An identifier in math: `pi`.
132    MathIdent(MathIdent<'a>),
133    /// A shorthand for a unicode codepoint in math: `a <= b`.
134    MathShorthand(MathShorthand<'a>),
135    /// An alignment point in math: `&`.
136    MathAlignPoint(MathAlignPoint<'a>),
137    /// Matched delimiters in math: `[x + y]`.
138    MathDelimited(MathDelimited<'a>),
139    /// A base with optional attachments in math: `a_1^2`.
140    MathAttach(MathAttach<'a>),
141    /// Grouped math primes
142    MathPrimes(MathPrimes<'a>),
143    /// A fraction in math: `x/2`.
144    MathFrac(MathFrac<'a>),
145    /// A root in math: `√x`, `∛x` or `∜x`.
146    MathRoot(MathRoot<'a>),
147    /// An identifier: `left`.
148    Ident(Ident<'a>),
149    /// The `none` literal.
150    None(None<'a>),
151    /// The `auto` literal.
152    Auto(Auto<'a>),
153    /// A boolean: `true`, `false`.
154    Bool(Bool<'a>),
155    /// An integer: `120`.
156    Int(Int<'a>),
157    /// A floating-point number: `1.2`, `10e-4`.
158    Float(Float<'a>),
159    /// A numeric value with a unit: `12pt`, `3cm`, `2em`, `90deg`, `50%`.
160    Numeric(Numeric<'a>),
161    /// A quoted string: `"..."`.
162    Str(Str<'a>),
163    /// A code block: `{ let x = 1; x + 2 }`.
164    Code(CodeBlock<'a>),
165    /// A content block: `[*Hi* there!]`.
166    Content(ContentBlock<'a>),
167    /// A grouped expression: `(1 + 2)`.
168    Parenthesized(Parenthesized<'a>),
169    /// An array: `(1, "hi", 12cm)`.
170    Array(Array<'a>),
171    /// A dictionary: `(thickness: 3pt, dash: "solid")`.
172    Dict(Dict<'a>),
173    /// A unary operation: `-x`.
174    Unary(Unary<'a>),
175    /// A binary operation: `a + b`.
176    Binary(Binary<'a>),
177    /// A field access: `properties.age`.
178    FieldAccess(FieldAccess<'a>),
179    /// An invocation of a function or method: `f(x, y)`.
180    FuncCall(FuncCall<'a>),
181    /// A closure: `(x, y) => z`.
182    Closure(Closure<'a>),
183    /// A let binding: `let x = 1`.
184    Let(LetBinding<'a>),
185    /// A destructuring assignment: `(x, y) = (1, 2)`.
186    DestructAssign(DestructAssignment<'a>),
187    /// A set rule: `set text(...)`.
188    Set(SetRule<'a>),
189    /// A show rule: `show heading: it => emph(it.body)`.
190    Show(ShowRule<'a>),
191    /// A contextual expression: `context text.lang`.
192    Contextual(Contextual<'a>),
193    /// An if-else conditional: `if x { y } else { z }`.
194    Conditional(Conditional<'a>),
195    /// A while loop: `while x { y }`.
196    While(WhileLoop<'a>),
197    /// A for loop: `for x in y { z }`.
198    For(ForLoop<'a>),
199    /// A module import: `import "utils.typ": a, b, c`.
200    Import(ModuleImport<'a>),
201    /// A module include: `include "chapter1.typ"`.
202    Include(ModuleInclude<'a>),
203    /// A break from a loop: `break`.
204    Break(LoopBreak<'a>),
205    /// A continue in a loop: `continue`.
206    Continue(LoopContinue<'a>),
207    /// A return from a function: `return`, `return x + 1`.
208    Return(FuncReturn<'a>),
209}
210
211impl<'a> Expr<'a> {
212    fn cast_with_space(node: &'a SyntaxNode) -> Option<Self> {
213        match node.kind() {
214            SyntaxKind::Space => node.cast().map(Self::Space),
215            _ => Self::from_untyped(node),
216        }
217    }
218}
219
220impl<'a> AstNode<'a> for Expr<'a> {
221    fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
222        match node.kind() {
223            SyntaxKind::Linebreak => node.cast().map(Self::Linebreak),
224            SyntaxKind::Parbreak => node.cast().map(Self::Parbreak),
225            SyntaxKind::Text => node.cast().map(Self::Text),
226            SyntaxKind::Escape => node.cast().map(Self::Escape),
227            SyntaxKind::Shorthand => node.cast().map(Self::Shorthand),
228            SyntaxKind::SmartQuote => node.cast().map(Self::SmartQuote),
229            SyntaxKind::Strong => node.cast().map(Self::Strong),
230            SyntaxKind::Emph => node.cast().map(Self::Emph),
231            SyntaxKind::Raw => node.cast().map(Self::Raw),
232            SyntaxKind::Link => node.cast().map(Self::Link),
233            SyntaxKind::Label => node.cast().map(Self::Label),
234            SyntaxKind::Ref => node.cast().map(Self::Ref),
235            SyntaxKind::Heading => node.cast().map(Self::Heading),
236            SyntaxKind::ListItem => node.cast().map(Self::List),
237            SyntaxKind::EnumItem => node.cast().map(Self::Enum),
238            SyntaxKind::TermItem => node.cast().map(Self::Term),
239            SyntaxKind::Equation => node.cast().map(Self::Equation),
240            SyntaxKind::Math => node.cast().map(Self::Math),
241            SyntaxKind::MathText => node.cast().map(Self::MathText),
242            SyntaxKind::MathIdent => node.cast().map(Self::MathIdent),
243            SyntaxKind::MathShorthand => node.cast().map(Self::MathShorthand),
244            SyntaxKind::MathAlignPoint => node.cast().map(Self::MathAlignPoint),
245            SyntaxKind::MathDelimited => node.cast().map(Self::MathDelimited),
246            SyntaxKind::MathAttach => node.cast().map(Self::MathAttach),
247            SyntaxKind::MathPrimes => node.cast().map(Self::MathPrimes),
248            SyntaxKind::MathFrac => node.cast().map(Self::MathFrac),
249            SyntaxKind::MathRoot => node.cast().map(Self::MathRoot),
250            SyntaxKind::Ident => node.cast().map(Self::Ident),
251            SyntaxKind::None => node.cast().map(Self::None),
252            SyntaxKind::Auto => node.cast().map(Self::Auto),
253            SyntaxKind::Bool => node.cast().map(Self::Bool),
254            SyntaxKind::Int => node.cast().map(Self::Int),
255            SyntaxKind::Float => node.cast().map(Self::Float),
256            SyntaxKind::Numeric => node.cast().map(Self::Numeric),
257            SyntaxKind::Str => node.cast().map(Self::Str),
258            SyntaxKind::CodeBlock => node.cast().map(Self::Code),
259            SyntaxKind::ContentBlock => node.cast().map(Self::Content),
260            SyntaxKind::Parenthesized => node.cast().map(Self::Parenthesized),
261            SyntaxKind::Array => node.cast().map(Self::Array),
262            SyntaxKind::Dict => node.cast().map(Self::Dict),
263            SyntaxKind::Unary => node.cast().map(Self::Unary),
264            SyntaxKind::Binary => node.cast().map(Self::Binary),
265            SyntaxKind::FieldAccess => node.cast().map(Self::FieldAccess),
266            SyntaxKind::FuncCall => node.cast().map(Self::FuncCall),
267            SyntaxKind::Closure => node.cast().map(Self::Closure),
268            SyntaxKind::LetBinding => node.cast().map(Self::Let),
269            SyntaxKind::DestructAssignment => node.cast().map(Self::DestructAssign),
270            SyntaxKind::SetRule => node.cast().map(Self::Set),
271            SyntaxKind::ShowRule => node.cast().map(Self::Show),
272            SyntaxKind::Contextual => node.cast().map(Self::Contextual),
273            SyntaxKind::Conditional => node.cast().map(Self::Conditional),
274            SyntaxKind::WhileLoop => node.cast().map(Self::While),
275            SyntaxKind::ForLoop => node.cast().map(Self::For),
276            SyntaxKind::ModuleImport => node.cast().map(Self::Import),
277            SyntaxKind::ModuleInclude => node.cast().map(Self::Include),
278            SyntaxKind::LoopBreak => node.cast().map(Self::Break),
279            SyntaxKind::LoopContinue => node.cast().map(Self::Continue),
280            SyntaxKind::FuncReturn => node.cast().map(Self::Return),
281            _ => Option::None,
282        }
283    }
284
285    fn to_untyped(self) -> &'a SyntaxNode {
286        match self {
287            Self::Text(v) => v.to_untyped(),
288            Self::Space(v) => v.to_untyped(),
289            Self::Linebreak(v) => v.to_untyped(),
290            Self::Parbreak(v) => v.to_untyped(),
291            Self::Escape(v) => v.to_untyped(),
292            Self::Shorthand(v) => v.to_untyped(),
293            Self::SmartQuote(v) => v.to_untyped(),
294            Self::Strong(v) => v.to_untyped(),
295            Self::Emph(v) => v.to_untyped(),
296            Self::Raw(v) => v.to_untyped(),
297            Self::Link(v) => v.to_untyped(),
298            Self::Label(v) => v.to_untyped(),
299            Self::Ref(v) => v.to_untyped(),
300            Self::Heading(v) => v.to_untyped(),
301            Self::List(v) => v.to_untyped(),
302            Self::Enum(v) => v.to_untyped(),
303            Self::Term(v) => v.to_untyped(),
304            Self::Equation(v) => v.to_untyped(),
305            Self::Math(v) => v.to_untyped(),
306            Self::MathText(v) => v.to_untyped(),
307            Self::MathIdent(v) => v.to_untyped(),
308            Self::MathShorthand(v) => v.to_untyped(),
309            Self::MathAlignPoint(v) => v.to_untyped(),
310            Self::MathDelimited(v) => v.to_untyped(),
311            Self::MathAttach(v) => v.to_untyped(),
312            Self::MathPrimes(v) => v.to_untyped(),
313            Self::MathFrac(v) => v.to_untyped(),
314            Self::MathRoot(v) => v.to_untyped(),
315            Self::Ident(v) => v.to_untyped(),
316            Self::None(v) => v.to_untyped(),
317            Self::Auto(v) => v.to_untyped(),
318            Self::Bool(v) => v.to_untyped(),
319            Self::Int(v) => v.to_untyped(),
320            Self::Float(v) => v.to_untyped(),
321            Self::Numeric(v) => v.to_untyped(),
322            Self::Str(v) => v.to_untyped(),
323            Self::Code(v) => v.to_untyped(),
324            Self::Content(v) => v.to_untyped(),
325            Self::Array(v) => v.to_untyped(),
326            Self::Dict(v) => v.to_untyped(),
327            Self::Parenthesized(v) => v.to_untyped(),
328            Self::Unary(v) => v.to_untyped(),
329            Self::Binary(v) => v.to_untyped(),
330            Self::FieldAccess(v) => v.to_untyped(),
331            Self::FuncCall(v) => v.to_untyped(),
332            Self::Closure(v) => v.to_untyped(),
333            Self::Let(v) => v.to_untyped(),
334            Self::DestructAssign(v) => v.to_untyped(),
335            Self::Set(v) => v.to_untyped(),
336            Self::Show(v) => v.to_untyped(),
337            Self::Contextual(v) => v.to_untyped(),
338            Self::Conditional(v) => v.to_untyped(),
339            Self::While(v) => v.to_untyped(),
340            Self::For(v) => v.to_untyped(),
341            Self::Import(v) => v.to_untyped(),
342            Self::Include(v) => v.to_untyped(),
343            Self::Break(v) => v.to_untyped(),
344            Self::Continue(v) => v.to_untyped(),
345            Self::Return(v) => v.to_untyped(),
346        }
347    }
348}
349
350impl Expr<'_> {
351    /// Can this expression be embedded into markup with a hash?
352    pub fn hash(self) -> bool {
353        matches!(
354            self,
355            Self::Ident(_)
356                | Self::None(_)
357                | Self::Auto(_)
358                | Self::Bool(_)
359                | Self::Int(_)
360                | Self::Float(_)
361                | Self::Numeric(_)
362                | Self::Str(_)
363                | Self::Code(_)
364                | Self::Content(_)
365                | Self::Array(_)
366                | Self::Dict(_)
367                | Self::Parenthesized(_)
368                | Self::FieldAccess(_)
369                | Self::FuncCall(_)
370                | Self::Let(_)
371                | Self::Set(_)
372                | Self::Show(_)
373                | Self::Contextual(_)
374                | Self::Conditional(_)
375                | Self::While(_)
376                | Self::For(_)
377                | Self::Import(_)
378                | Self::Include(_)
379                | Self::Break(_)
380                | Self::Continue(_)
381                | Self::Return(_)
382        )
383    }
384
385    /// Is this a literal?
386    pub fn is_literal(self) -> bool {
387        matches!(
388            self,
389            Self::None(_)
390                | Self::Auto(_)
391                | Self::Bool(_)
392                | Self::Int(_)
393                | Self::Float(_)
394                | Self::Numeric(_)
395                | Self::Str(_)
396        )
397    }
398}
399
400impl Default for Expr<'_> {
401    fn default() -> Self {
402        Expr::None(None::default())
403    }
404}
405
406node! {
407    /// Plain text without markup.
408    Text
409}
410
411impl<'a> Text<'a> {
412    /// Get the text.
413    pub fn get(self) -> &'a EcoString {
414        self.0.text()
415    }
416}
417
418node! {
419    /// Whitespace in markup or math. Has at most one newline in markup, as more
420    /// indicate a paragraph break.
421    Space
422}
423
424node! {
425    /// A forced line break: `\`.
426    Linebreak
427}
428
429node! {
430    /// A paragraph break, indicated by one or multiple blank lines.
431    Parbreak
432}
433
434node! {
435    /// An escape sequence: `\#`, `\u{1F5FA}`.
436    Escape
437}
438
439impl Escape<'_> {
440    /// Get the escaped character.
441    pub fn get(self) -> char {
442        let mut s = Scanner::new(self.0.text());
443        s.expect('\\');
444        if s.eat_if("u{") {
445            let hex = s.eat_while(char::is_ascii_hexdigit);
446            u32::from_str_radix(hex, 16)
447                .ok()
448                .and_then(std::char::from_u32)
449                .unwrap_or_default()
450        } else {
451            s.eat().unwrap_or_default()
452        }
453    }
454}
455
456node! {
457    /// A shorthand for a unicode codepoint. For example, `~` for a non-breaking
458    /// space or `-?` for a soft hyphen.
459    Shorthand
460}
461
462impl Shorthand<'_> {
463    /// A list of all shorthands in markup mode.
464    pub const LIST: &'static [(&'static str, char)] = &[
465        ("...", '…'),
466        ("~", '\u{00A0}'),
467        ("-", '\u{2212}'), // Only before a digit
468        ("--", '\u{2013}'),
469        ("---", '\u{2014}'),
470        ("-?", '\u{00AD}'),
471    ];
472
473    /// Get the shorthanded character.
474    pub fn get(self) -> char {
475        let text = self.0.text();
476        Self::LIST
477            .iter()
478            .find(|&&(s, _)| s == text)
479            .map_or_else(char::default, |&(_, c)| c)
480    }
481}
482
483node! {
484    /// A smart quote: `'` or `"`.
485    SmartQuote
486}
487
488impl SmartQuote<'_> {
489    /// Whether this is a double quote.
490    pub fn double(self) -> bool {
491        self.0.text() == "\""
492    }
493}
494
495node! {
496    /// Strong content: `*Strong*`.
497    Strong
498}
499
500impl<'a> Strong<'a> {
501    /// The contents of the strong node.
502    pub fn body(self) -> Markup<'a> {
503        self.0.cast_first_match().unwrap_or_default()
504    }
505}
506
507node! {
508    /// Emphasized content: `_Emphasized_`.
509    Emph
510}
511
512impl<'a> Emph<'a> {
513    /// The contents of the emphasis node.
514    pub fn body(self) -> Markup<'a> {
515        self.0.cast_first_match().unwrap_or_default()
516    }
517}
518
519node! {
520    /// Raw text with optional syntax highlighting: `` `...` ``.
521    Raw
522}
523
524impl<'a> Raw<'a> {
525    /// The lines in the raw block.
526    pub fn lines(self) -> impl DoubleEndedIterator<Item = Text<'a>> {
527        self.0.children().filter_map(SyntaxNode::cast)
528    }
529
530    /// An optional identifier specifying the language to syntax-highlight in.
531    pub fn lang(self) -> Option<RawLang<'a>> {
532        // Only blocky literals are supposed to contain a language.
533        let delim: RawDelim = self.0.cast_first_match()?;
534        if delim.0.len() < 3 {
535            return Option::None;
536        }
537
538        self.0.cast_first_match()
539    }
540
541    /// Whether the raw text should be displayed in a separate block.
542    pub fn block(self) -> bool {
543        self.0
544            .cast_first_match()
545            .is_some_and(|delim: RawDelim| delim.0.len() >= 3)
546            && self.0.children().any(|e| {
547                e.kind() == SyntaxKind::RawTrimmed && e.text().chars().any(is_newline)
548            })
549    }
550}
551
552node! {
553    /// A language tag at the start of raw element: ``typ ``.
554    RawLang
555}
556
557impl<'a> RawLang<'a> {
558    /// Get the language tag.
559    pub fn get(self) -> &'a EcoString {
560        self.0.text()
561    }
562}
563
564node! {
565    /// A raw delimiter in single or 3+ backticks: `` ` ``.
566    RawDelim
567}
568
569node! {
570    /// A hyperlink: `https://typst.org`.
571    Link
572}
573
574impl<'a> Link<'a> {
575    /// Get the URL.
576    pub fn get(self) -> &'a EcoString {
577        self.0.text()
578    }
579}
580
581node! {
582    /// A label: `<intro>`.
583    Label
584}
585
586impl<'a> Label<'a> {
587    /// Get the label's text.
588    pub fn get(self) -> &'a str {
589        self.0.text().trim_start_matches('<').trim_end_matches('>')
590    }
591}
592
593node! {
594    /// A reference: `@target`, `@target[..]`.
595    Ref
596}
597
598impl<'a> Ref<'a> {
599    /// Get the target.
600    pub fn target(self) -> &'a str {
601        self.0
602            .children()
603            .find(|node| node.kind() == SyntaxKind::RefMarker)
604            .map(|node| node.text().trim_start_matches('@'))
605            .unwrap_or_default()
606    }
607
608    /// Get the supplement.
609    pub fn supplement(self) -> Option<ContentBlock<'a>> {
610        self.0.cast_last_match()
611    }
612}
613
614node! {
615    /// A section heading: `= Introduction`.
616    Heading
617}
618
619impl<'a> Heading<'a> {
620    /// The contents of the heading.
621    pub fn body(self) -> Markup<'a> {
622        self.0.cast_first_match().unwrap_or_default()
623    }
624
625    /// The section depth (number of equals signs).
626    pub fn depth(self) -> NonZeroUsize {
627        self.0
628            .children()
629            .find(|node| node.kind() == SyntaxKind::HeadingMarker)
630            .and_then(|node| node.len().try_into().ok())
631            .unwrap_or(NonZeroUsize::new(1).unwrap())
632    }
633}
634
635node! {
636    /// An item in a bullet list: `- ...`.
637    ListItem
638}
639
640impl<'a> ListItem<'a> {
641    /// The contents of the list item.
642    pub fn body(self) -> Markup<'a> {
643        self.0.cast_first_match().unwrap_or_default()
644    }
645}
646
647node! {
648    /// An item in an enumeration (numbered list): `+ ...` or `1. ...`.
649    EnumItem
650}
651
652impl<'a> EnumItem<'a> {
653    /// The explicit numbering, if any: `23.`.
654    pub fn number(self) -> Option<usize> {
655        self.0.children().find_map(|node| match node.kind() {
656            SyntaxKind::EnumMarker => node.text().trim_end_matches('.').parse().ok(),
657            _ => Option::None,
658        })
659    }
660
661    /// The contents of the list item.
662    pub fn body(self) -> Markup<'a> {
663        self.0.cast_first_match().unwrap_or_default()
664    }
665}
666
667node! {
668    /// An item in a term list: `/ Term: Details`.
669    TermItem
670}
671
672impl<'a> TermItem<'a> {
673    /// The term described by the item.
674    pub fn term(self) -> Markup<'a> {
675        self.0.cast_first_match().unwrap_or_default()
676    }
677
678    /// The description of the term.
679    pub fn description(self) -> Markup<'a> {
680        self.0.cast_last_match().unwrap_or_default()
681    }
682}
683
684node! {
685    /// A mathematical equation: `$x$`, `$ x^2 $`.
686    Equation
687}
688
689impl<'a> Equation<'a> {
690    /// The contained math.
691    pub fn body(self) -> Math<'a> {
692        self.0.cast_first_match().unwrap_or_default()
693    }
694
695    /// Whether the equation should be displayed as a separate block.
696    pub fn block(self) -> bool {
697        let is_space = |node: Option<&SyntaxNode>| {
698            node.map(SyntaxNode::kind) == Some(SyntaxKind::Space)
699        };
700        is_space(self.0.children().nth(1)) && is_space(self.0.children().nth_back(1))
701    }
702}
703
704node! {
705    /// The contents of a mathematical equation: `x^2 + 1`.
706    Math
707}
708
709impl<'a> Math<'a> {
710    /// The expressions the mathematical content consists of.
711    pub fn exprs(self) -> impl DoubleEndedIterator<Item = Expr<'a>> {
712        self.0.children().filter_map(Expr::cast_with_space)
713    }
714}
715
716node! {
717    /// A lone text fragment in math: `x`, `25`, `3.1415`, `=`, `[`.
718    MathText
719}
720
721/// The underlying text kind.
722pub enum MathTextKind<'a> {
723    Character(char),
724    Number(&'a EcoString),
725}
726
727impl<'a> MathText<'a> {
728    /// Return the underlying text.
729    pub fn get(self) -> MathTextKind<'a> {
730        let text = self.0.text();
731        let mut chars = text.chars();
732        let c = chars.next().unwrap();
733        if c.is_numeric() {
734            // Numbers are potentially grouped as multiple characters. This is
735            // done in `Lexer::math_text()`.
736            MathTextKind::Number(text)
737        } else {
738            assert!(chars.next().is_none());
739            MathTextKind::Character(c)
740        }
741    }
742}
743
744node! {
745    /// An identifier in math: `pi`.
746    MathIdent
747}
748
749impl<'a> MathIdent<'a> {
750    /// Get the identifier.
751    pub fn get(self) -> &'a EcoString {
752        self.0.text()
753    }
754
755    /// Get the identifier as a string slice.
756    pub fn as_str(self) -> &'a str {
757        self.get()
758    }
759}
760
761impl Deref for MathIdent<'_> {
762    type Target = str;
763
764    /// Dereference to a string. Note that this shortens the lifetime, so you
765    /// may need to use [`get()`](Self::get) instead in some situations.
766    fn deref(&self) -> &Self::Target {
767        self.as_str()
768    }
769}
770
771node! {
772    /// A shorthand for a unicode codepoint in math: `a <= b`.
773    MathShorthand
774}
775
776impl MathShorthand<'_> {
777    /// A list of all shorthands in math mode.
778    pub const LIST: &'static [(&'static str, char)] = &[
779        ("...", '…'),
780        ("-", '−'),
781        ("*", '∗'),
782        ("~", '∼'),
783        ("!=", '≠'),
784        (":=", '≔'),
785        ("::=", '⩴'),
786        ("=:", '≕'),
787        ("<<", '≪'),
788        ("<<<", '⋘'),
789        (">>", '≫'),
790        (">>>", '⋙'),
791        ("<=", '≤'),
792        (">=", '≥'),
793        ("->", '→'),
794        ("-->", '⟶'),
795        ("|->", '↦'),
796        (">->", '↣'),
797        ("->>", '↠'),
798        ("<-", '←'),
799        ("<--", '⟵'),
800        ("<-<", '↢'),
801        ("<<-", '↞'),
802        ("<->", '↔'),
803        ("<-->", '⟷'),
804        ("~>", '⇝'),
805        ("~~>", '⟿'),
806        ("<~", '⇜'),
807        ("<~~", '⬳'),
808        ("=>", '⇒'),
809        ("|=>", '⤇'),
810        ("==>", '⟹'),
811        ("<==", '⟸'),
812        ("<=>", '⇔'),
813        ("<==>", '⟺'),
814        ("[|", '⟦'),
815        ("|]", '⟧'),
816        ("||", '‖'),
817    ];
818
819    /// Get the shorthanded character.
820    pub fn get(self) -> char {
821        let text = self.0.text();
822        Self::LIST
823            .iter()
824            .find(|&&(s, _)| s == text)
825            .map_or_else(char::default, |&(_, c)| c)
826    }
827}
828
829node! {
830    /// An alignment point in math: `&`.
831    MathAlignPoint
832}
833
834node! {
835    /// Matched delimiters in math: `[x + y]`.
836    MathDelimited
837}
838
839impl<'a> MathDelimited<'a> {
840    /// The opening delimiter.
841    pub fn open(self) -> Expr<'a> {
842        self.0.cast_first_match().unwrap_or_default()
843    }
844
845    /// The contents, including the delimiters.
846    pub fn body(self) -> Math<'a> {
847        self.0.cast_first_match().unwrap_or_default()
848    }
849
850    /// The closing delimiter.
851    pub fn close(self) -> Expr<'a> {
852        self.0.cast_last_match().unwrap_or_default()
853    }
854}
855
856node! {
857    /// A base with optional attachments in math: `a_1^2`.
858    MathAttach
859}
860
861impl<'a> MathAttach<'a> {
862    /// The base, to which things are attached.
863    pub fn base(self) -> Expr<'a> {
864        self.0.cast_first_match().unwrap_or_default()
865    }
866
867    /// The bottom attachment.
868    pub fn bottom(self) -> Option<Expr<'a>> {
869        self.0
870            .children()
871            .skip_while(|node| !matches!(node.kind(), SyntaxKind::Underscore))
872            .find_map(SyntaxNode::cast)
873    }
874
875    /// The top attachment.
876    pub fn top(self) -> Option<Expr<'a>> {
877        self.0
878            .children()
879            .skip_while(|node| !matches!(node.kind(), SyntaxKind::Hat))
880            .find_map(SyntaxNode::cast)
881    }
882
883    /// Extract attached primes if present.
884    pub fn primes(self) -> Option<MathPrimes<'a>> {
885        self.0
886            .children()
887            .skip_while(|node| node.cast::<Expr<'_>>().is_none())
888            .nth(1)
889            .and_then(|n| n.cast())
890    }
891}
892
893node! {
894    /// Grouped primes in math: `a'''`.
895    MathPrimes
896}
897
898impl MathPrimes<'_> {
899    /// The number of grouped primes.
900    pub fn count(self) -> usize {
901        self.0
902            .children()
903            .filter(|node| matches!(node.kind(), SyntaxKind::Prime))
904            .count()
905    }
906}
907
908node! {
909    /// A fraction in math: `x/2`
910    MathFrac
911}
912
913impl<'a> MathFrac<'a> {
914    /// The numerator.
915    pub fn num(self) -> Expr<'a> {
916        self.0.cast_first_match().unwrap_or_default()
917    }
918
919    /// The denominator.
920    pub fn denom(self) -> Expr<'a> {
921        self.0.cast_last_match().unwrap_or_default()
922    }
923}
924
925node! {
926    /// A root in math: `√x`, `∛x` or `∜x`.
927    MathRoot
928}
929
930impl<'a> MathRoot<'a> {
931    /// The index of the root.
932    pub fn index(self) -> Option<usize> {
933        match self.0.children().next().map(|node| node.text().as_str()) {
934            Some("∜") => Some(4),
935            Some("∛") => Some(3),
936            Some("√") => Option::None,
937            _ => Option::None,
938        }
939    }
940
941    /// The radicand.
942    pub fn radicand(self) -> Expr<'a> {
943        self.0.cast_first_match().unwrap_or_default()
944    }
945}
946
947node! {
948    /// An identifier: `it`.
949    Ident
950}
951
952impl<'a> Ident<'a> {
953    /// Get the identifier.
954    pub fn get(self) -> &'a EcoString {
955        self.0.text()
956    }
957
958    /// Get the identifier as a string slice.
959    pub fn as_str(self) -> &'a str {
960        self.get()
961    }
962}
963
964impl Deref for Ident<'_> {
965    type Target = str;
966
967    /// Dereference to a string. Note that this shortens the lifetime, so you
968    /// may need to use [`get()`](Self::get) instead in some situations.
969    fn deref(&self) -> &Self::Target {
970        self.as_str()
971    }
972}
973
974node! {
975    /// The `none` literal.
976    None
977}
978
979node! {
980    /// The `auto` literal.
981    Auto
982}
983
984node! {
985    /// A boolean: `true`, `false`.
986    Bool
987}
988
989impl Bool<'_> {
990    /// Get the boolean value.
991    pub fn get(self) -> bool {
992        self.0.text() == "true"
993    }
994}
995
996node! {
997    /// An integer: `120`.
998    Int
999}
1000
1001impl Int<'_> {
1002    /// Get the integer value.
1003    pub fn get(self) -> i64 {
1004        let text = self.0.text();
1005        if let Some(rest) = text.strip_prefix("0x") {
1006            i64::from_str_radix(rest, 16)
1007        } else if let Some(rest) = text.strip_prefix("0o") {
1008            i64::from_str_radix(rest, 8)
1009        } else if let Some(rest) = text.strip_prefix("0b") {
1010            i64::from_str_radix(rest, 2)
1011        } else {
1012            text.parse()
1013        }
1014        .unwrap_or_default()
1015    }
1016}
1017
1018node! {
1019    /// A floating-point number: `1.2`, `10e-4`.
1020    Float
1021}
1022
1023impl Float<'_> {
1024    /// Get the floating-point value.
1025    pub fn get(self) -> f64 {
1026        self.0.text().parse().unwrap_or_default()
1027    }
1028}
1029
1030node! {
1031    /// A numeric value with a unit: `12pt`, `3cm`, `2em`, `90deg`, `50%`.
1032    Numeric
1033}
1034
1035impl Numeric<'_> {
1036    /// Get the numeric value and unit.
1037    pub fn get(self) -> (f64, Unit) {
1038        let text = self.0.text();
1039        let count = text
1040            .chars()
1041            .rev()
1042            .take_while(|c| matches!(c, 'a'..='z' | '%'))
1043            .count();
1044
1045        let split = text.len() - count;
1046        let value = text[..split].parse().unwrap_or_default();
1047        let unit = match &text[split..] {
1048            "pt" => Unit::Pt,
1049            "mm" => Unit::Mm,
1050            "cm" => Unit::Cm,
1051            "in" => Unit::In,
1052            "deg" => Unit::Deg,
1053            "rad" => Unit::Rad,
1054            "em" => Unit::Em,
1055            "fr" => Unit::Fr,
1056            "%" => Unit::Percent,
1057            _ => Unit::Percent,
1058        };
1059
1060        (value, unit)
1061    }
1062}
1063
1064/// Unit of a numeric value.
1065#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1066pub enum Unit {
1067    /// Points.
1068    Pt,
1069    /// Millimeters.
1070    Mm,
1071    /// Centimeters.
1072    Cm,
1073    /// Inches.
1074    In,
1075    /// Radians.
1076    Rad,
1077    /// Degrees.
1078    Deg,
1079    /// Font-relative: `1em` is the same as the font size.
1080    Em,
1081    /// Fractions: `fr`.
1082    Fr,
1083    /// Percentage: `%`.
1084    Percent,
1085}
1086
1087node! {
1088    /// A quoted string: `"..."`.
1089    Str
1090}
1091
1092impl Str<'_> {
1093    /// Get the string value with resolved escape sequences.
1094    pub fn get(self) -> EcoString {
1095        let text = self.0.text();
1096        let unquoted = &text[1..text.len() - 1];
1097        if !unquoted.contains('\\') {
1098            return unquoted.into();
1099        }
1100
1101        let mut out = EcoString::with_capacity(unquoted.len());
1102        let mut s = Scanner::new(unquoted);
1103
1104        while let Some(c) = s.eat() {
1105            if c != '\\' {
1106                out.push(c);
1107                continue;
1108            }
1109
1110            let start = s.locate(-1);
1111            match s.eat() {
1112                Some('\\') => out.push('\\'),
1113                Some('"') => out.push('"'),
1114                Some('n') => out.push('\n'),
1115                Some('r') => out.push('\r'),
1116                Some('t') => out.push('\t'),
1117                Some('u') if s.eat_if('{') => {
1118                    let sequence = s.eat_while(char::is_ascii_hexdigit);
1119                    s.eat_if('}');
1120
1121                    match u32::from_str_radix(sequence, 16)
1122                        .ok()
1123                        .and_then(std::char::from_u32)
1124                    {
1125                        Some(c) => out.push(c),
1126                        Option::None => out.push_str(s.from(start)),
1127                    }
1128                }
1129                _ => out.push_str(s.from(start)),
1130            }
1131        }
1132
1133        out
1134    }
1135}
1136
1137node! {
1138    /// A code block: `{ let x = 1; x + 2 }`.
1139    CodeBlock
1140}
1141
1142impl<'a> CodeBlock<'a> {
1143    /// The contained code.
1144    pub fn body(self) -> Code<'a> {
1145        self.0.cast_first_match().unwrap_or_default()
1146    }
1147}
1148
1149node! {
1150    /// The body of a code block.
1151    Code
1152}
1153
1154impl<'a> Code<'a> {
1155    /// The list of expressions contained in the code.
1156    pub fn exprs(self) -> impl DoubleEndedIterator<Item = Expr<'a>> {
1157        self.0.children().filter_map(SyntaxNode::cast)
1158    }
1159}
1160
1161node! {
1162    /// A content block: `[*Hi* there!]`.
1163    ContentBlock
1164}
1165
1166impl<'a> ContentBlock<'a> {
1167    /// The contained markup.
1168    pub fn body(self) -> Markup<'a> {
1169        self.0.cast_first_match().unwrap_or_default()
1170    }
1171}
1172
1173node! {
1174    /// A grouped expression: `(1 + 2)`.
1175    Parenthesized
1176}
1177
1178impl<'a> Parenthesized<'a> {
1179    /// The wrapped expression.
1180    ///
1181    /// Should only be accessed if this is contained in an `Expr`.
1182    pub fn expr(self) -> Expr<'a> {
1183        self.0.cast_first_match().unwrap_or_default()
1184    }
1185
1186    /// The wrapped pattern.
1187    ///
1188    /// Should only be accessed if this is contained in a `Pattern`.
1189    pub fn pattern(self) -> Pattern<'a> {
1190        self.0.cast_first_match().unwrap_or_default()
1191    }
1192}
1193
1194node! {
1195    /// An array: `(1, "hi", 12cm)`.
1196    Array
1197}
1198
1199impl<'a> Array<'a> {
1200    /// The array's items.
1201    pub fn items(self) -> impl DoubleEndedIterator<Item = ArrayItem<'a>> {
1202        self.0.children().filter_map(SyntaxNode::cast)
1203    }
1204}
1205
1206/// An item in an array.
1207#[derive(Debug, Copy, Clone, Hash)]
1208pub enum ArrayItem<'a> {
1209    /// A bare expression: `12`.
1210    Pos(Expr<'a>),
1211    /// A spread expression: `..things`.
1212    Spread(Spread<'a>),
1213}
1214
1215impl<'a> AstNode<'a> for ArrayItem<'a> {
1216    fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
1217        match node.kind() {
1218            SyntaxKind::Spread => node.cast().map(Self::Spread),
1219            _ => node.cast().map(Self::Pos),
1220        }
1221    }
1222
1223    fn to_untyped(self) -> &'a SyntaxNode {
1224        match self {
1225            Self::Pos(v) => v.to_untyped(),
1226            Self::Spread(v) => v.to_untyped(),
1227        }
1228    }
1229}
1230
1231node! {
1232    /// A dictionary: `(thickness: 3pt, dash: "solid")`.
1233    Dict
1234}
1235
1236impl<'a> Dict<'a> {
1237    /// The dictionary's items.
1238    pub fn items(self) -> impl DoubleEndedIterator<Item = DictItem<'a>> {
1239        self.0.children().filter_map(SyntaxNode::cast)
1240    }
1241}
1242
1243/// An item in an dictionary expression.
1244#[derive(Debug, Copy, Clone, Hash)]
1245pub enum DictItem<'a> {
1246    /// A named pair: `thickness: 3pt`.
1247    Named(Named<'a>),
1248    /// A keyed pair: `"spacy key": true`.
1249    Keyed(Keyed<'a>),
1250    /// A spread expression: `..things`.
1251    Spread(Spread<'a>),
1252}
1253
1254impl<'a> AstNode<'a> for DictItem<'a> {
1255    fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
1256        match node.kind() {
1257            SyntaxKind::Named => node.cast().map(Self::Named),
1258            SyntaxKind::Keyed => node.cast().map(Self::Keyed),
1259            SyntaxKind::Spread => node.cast().map(Self::Spread),
1260            _ => Option::None,
1261        }
1262    }
1263
1264    fn to_untyped(self) -> &'a SyntaxNode {
1265        match self {
1266            Self::Named(v) => v.to_untyped(),
1267            Self::Keyed(v) => v.to_untyped(),
1268            Self::Spread(v) => v.to_untyped(),
1269        }
1270    }
1271}
1272
1273node! {
1274    /// A named pair: `thickness: 3pt`.
1275    Named
1276}
1277
1278impl<'a> Named<'a> {
1279    /// The name: `thickness`.
1280    pub fn name(self) -> Ident<'a> {
1281        self.0.cast_first_match().unwrap_or_default()
1282    }
1283
1284    /// The right-hand side of the pair: `3pt`.
1285    ///
1286    /// This should only be accessed if this `Named` is contained in a
1287    /// `DictItem`, `Arg`, or `Param`.
1288    pub fn expr(self) -> Expr<'a> {
1289        self.0.cast_last_match().unwrap_or_default()
1290    }
1291
1292    /// The right-hand side of the pair as a pattern.
1293    ///
1294    /// This should only be accessed if this `Named` is contained in a
1295    /// `Destructuring`.
1296    pub fn pattern(self) -> Pattern<'a> {
1297        self.0.cast_last_match().unwrap_or_default()
1298    }
1299}
1300
1301node! {
1302    /// A keyed pair: `"spacy key": true`.
1303    Keyed
1304}
1305
1306impl<'a> Keyed<'a> {
1307    /// The key: `"spacy key"`.
1308    pub fn key(self) -> Expr<'a> {
1309        self.0.cast_first_match().unwrap_or_default()
1310    }
1311
1312    /// The right-hand side of the pair: `true`.
1313    ///
1314    /// This should only be accessed if this `Keyed` is contained in a
1315    /// `DictItem`.
1316    pub fn expr(self) -> Expr<'a> {
1317        self.0.cast_last_match().unwrap_or_default()
1318    }
1319}
1320
1321node! {
1322    /// A spread: `..x` or `..x.at(0)`.
1323    Spread
1324}
1325
1326impl<'a> Spread<'a> {
1327    /// The spread expression.
1328    ///
1329    /// This should only be accessed if this `Spread` is contained in an
1330    /// `ArrayItem`, `DictItem`, or `Arg`.
1331    pub fn expr(self) -> Expr<'a> {
1332        self.0.cast_first_match().unwrap_or_default()
1333    }
1334
1335    /// The sink identifier, if present.
1336    ///
1337    /// This should only be accessed if this `Spread` is contained in a
1338    /// `Param` or binding `DestructuringItem`.
1339    pub fn sink_ident(self) -> Option<Ident<'a>> {
1340        self.0.cast_first_match()
1341    }
1342
1343    /// The sink expressions, if present.
1344    ///
1345    /// This should only be accessed if this `Spread` is contained in a
1346    /// `DestructuringItem`.
1347    pub fn sink_expr(self) -> Option<Expr<'a>> {
1348        self.0.cast_first_match()
1349    }
1350}
1351
1352node! {
1353    /// A unary operation: `-x`.
1354    Unary
1355}
1356
1357impl<'a> Unary<'a> {
1358    /// The operator: `-`.
1359    pub fn op(self) -> UnOp {
1360        self.0
1361            .children()
1362            .find_map(|node| UnOp::from_kind(node.kind()))
1363            .unwrap_or(UnOp::Pos)
1364    }
1365
1366    /// The expression to operate on: `x`.
1367    pub fn expr(self) -> Expr<'a> {
1368        self.0.cast_last_match().unwrap_or_default()
1369    }
1370}
1371
1372/// A unary operator.
1373#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1374pub enum UnOp {
1375    /// The plus operator: `+`.
1376    Pos,
1377    /// The negation operator: `-`.
1378    Neg,
1379    /// The boolean `not`.
1380    Not,
1381}
1382
1383impl UnOp {
1384    /// Try to convert the token into a unary operation.
1385    pub fn from_kind(token: SyntaxKind) -> Option<Self> {
1386        Some(match token {
1387            SyntaxKind::Plus => Self::Pos,
1388            SyntaxKind::Minus => Self::Neg,
1389            SyntaxKind::Not => Self::Not,
1390            _ => return Option::None,
1391        })
1392    }
1393
1394    /// The precedence of this operator.
1395    pub fn precedence(self) -> usize {
1396        match self {
1397            Self::Pos | Self::Neg => 7,
1398            Self::Not => 4,
1399        }
1400    }
1401
1402    /// The string representation of this operation.
1403    pub fn as_str(self) -> &'static str {
1404        match self {
1405            Self::Pos => "+",
1406            Self::Neg => "-",
1407            Self::Not => "not",
1408        }
1409    }
1410}
1411
1412node! {
1413    /// A binary operation: `a + b`.
1414    Binary
1415}
1416
1417impl<'a> Binary<'a> {
1418    /// The binary operator: `+`.
1419    pub fn op(self) -> BinOp {
1420        let mut not = false;
1421        self.0
1422            .children()
1423            .find_map(|node| match node.kind() {
1424                SyntaxKind::Not => {
1425                    not = true;
1426                    Option::None
1427                }
1428                SyntaxKind::In if not => Some(BinOp::NotIn),
1429                _ => BinOp::from_kind(node.kind()),
1430            })
1431            .unwrap_or(BinOp::Add)
1432    }
1433
1434    /// The left-hand side of the operation: `a`.
1435    pub fn lhs(self) -> Expr<'a> {
1436        self.0.cast_first_match().unwrap_or_default()
1437    }
1438
1439    /// The right-hand side of the operation: `b`.
1440    pub fn rhs(self) -> Expr<'a> {
1441        self.0.cast_last_match().unwrap_or_default()
1442    }
1443}
1444
1445/// A binary operator.
1446#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1447pub enum BinOp {
1448    /// The addition operator: `+`.
1449    Add,
1450    /// The subtraction operator: `-`.
1451    Sub,
1452    /// The multiplication operator: `*`.
1453    Mul,
1454    /// The division operator: `/`.
1455    Div,
1456    /// The short-circuiting boolean `and`.
1457    And,
1458    /// The short-circuiting boolean `or`.
1459    Or,
1460    /// The equality operator: `==`.
1461    Eq,
1462    /// The inequality operator: `!=`.
1463    Neq,
1464    /// The less-than operator: `<`.
1465    Lt,
1466    /// The less-than or equal operator: `<=`.
1467    Leq,
1468    /// The greater-than operator: `>`.
1469    Gt,
1470    /// The greater-than or equal operator: `>=`.
1471    Geq,
1472    /// The assignment operator: `=`.
1473    Assign,
1474    /// The containment operator: `in`.
1475    In,
1476    /// The inverse containment operator: `not in`.
1477    NotIn,
1478    /// The add-assign operator: `+=`.
1479    AddAssign,
1480    /// The subtract-assign operator: `-=`.
1481    SubAssign,
1482    /// The multiply-assign operator: `*=`.
1483    MulAssign,
1484    /// The divide-assign operator: `/=`.
1485    DivAssign,
1486}
1487
1488impl BinOp {
1489    /// Try to convert the token into a binary operation.
1490    pub fn from_kind(token: SyntaxKind) -> Option<Self> {
1491        Some(match token {
1492            SyntaxKind::Plus => Self::Add,
1493            SyntaxKind::Minus => Self::Sub,
1494            SyntaxKind::Star => Self::Mul,
1495            SyntaxKind::Slash => Self::Div,
1496            SyntaxKind::And => Self::And,
1497            SyntaxKind::Or => Self::Or,
1498            SyntaxKind::EqEq => Self::Eq,
1499            SyntaxKind::ExclEq => Self::Neq,
1500            SyntaxKind::Lt => Self::Lt,
1501            SyntaxKind::LtEq => Self::Leq,
1502            SyntaxKind::Gt => Self::Gt,
1503            SyntaxKind::GtEq => Self::Geq,
1504            SyntaxKind::Eq => Self::Assign,
1505            SyntaxKind::In => Self::In,
1506            SyntaxKind::PlusEq => Self::AddAssign,
1507            SyntaxKind::HyphEq => Self::SubAssign,
1508            SyntaxKind::StarEq => Self::MulAssign,
1509            SyntaxKind::SlashEq => Self::DivAssign,
1510            _ => return Option::None,
1511        })
1512    }
1513
1514    /// The precedence of this operator.
1515    pub fn precedence(self) -> usize {
1516        match self {
1517            Self::Mul => 6,
1518            Self::Div => 6,
1519            Self::Add => 5,
1520            Self::Sub => 5,
1521            Self::Eq => 4,
1522            Self::Neq => 4,
1523            Self::Lt => 4,
1524            Self::Leq => 4,
1525            Self::Gt => 4,
1526            Self::Geq => 4,
1527            Self::In => 4,
1528            Self::NotIn => 4,
1529            Self::And => 3,
1530            Self::Or => 2,
1531            Self::Assign => 1,
1532            Self::AddAssign => 1,
1533            Self::SubAssign => 1,
1534            Self::MulAssign => 1,
1535            Self::DivAssign => 1,
1536        }
1537    }
1538
1539    /// The associativity of this operator.
1540    pub fn assoc(self) -> Assoc {
1541        match self {
1542            Self::Add => Assoc::Left,
1543            Self::Sub => Assoc::Left,
1544            Self::Mul => Assoc::Left,
1545            Self::Div => Assoc::Left,
1546            Self::And => Assoc::Left,
1547            Self::Or => Assoc::Left,
1548            Self::Eq => Assoc::Left,
1549            Self::Neq => Assoc::Left,
1550            Self::Lt => Assoc::Left,
1551            Self::Leq => Assoc::Left,
1552            Self::Gt => Assoc::Left,
1553            Self::Geq => Assoc::Left,
1554            Self::In => Assoc::Left,
1555            Self::NotIn => Assoc::Left,
1556            Self::Assign => Assoc::Right,
1557            Self::AddAssign => Assoc::Right,
1558            Self::SubAssign => Assoc::Right,
1559            Self::MulAssign => Assoc::Right,
1560            Self::DivAssign => Assoc::Right,
1561        }
1562    }
1563
1564    /// The string representation of this operation.
1565    pub fn as_str(self) -> &'static str {
1566        match self {
1567            Self::Add => "+",
1568            Self::Sub => "-",
1569            Self::Mul => "*",
1570            Self::Div => "/",
1571            Self::And => "and",
1572            Self::Or => "or",
1573            Self::Eq => "==",
1574            Self::Neq => "!=",
1575            Self::Lt => "<",
1576            Self::Leq => "<=",
1577            Self::Gt => ">",
1578            Self::Geq => ">=",
1579            Self::In => "in",
1580            Self::NotIn => "not in",
1581            Self::Assign => "=",
1582            Self::AddAssign => "+=",
1583            Self::SubAssign => "-=",
1584            Self::MulAssign => "*=",
1585            Self::DivAssign => "/=",
1586        }
1587    }
1588}
1589
1590/// The associativity of a binary operator.
1591#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1592pub enum Assoc {
1593    /// Left-associative: `a + b + c` is equivalent to `(a + b) + c`.
1594    Left,
1595    /// Right-associative: `a = b = c` is equivalent to `a = (b = c)`.
1596    Right,
1597}
1598
1599node! {
1600    /// A field access: `properties.age`.
1601    FieldAccess
1602}
1603
1604impl<'a> FieldAccess<'a> {
1605    /// The expression to access the field on.
1606    pub fn target(self) -> Expr<'a> {
1607        self.0.cast_first_match().unwrap_or_default()
1608    }
1609
1610    /// The name of the field.
1611    pub fn field(self) -> Ident<'a> {
1612        self.0.cast_last_match().unwrap_or_default()
1613    }
1614}
1615
1616node! {
1617    /// An invocation of a function or method: `f(x, y)`.
1618    FuncCall
1619}
1620
1621impl<'a> FuncCall<'a> {
1622    /// The function to call.
1623    pub fn callee(self) -> Expr<'a> {
1624        self.0.cast_first_match().unwrap_or_default()
1625    }
1626
1627    /// The arguments to the function.
1628    pub fn args(self) -> Args<'a> {
1629        self.0.cast_last_match().unwrap_or_default()
1630    }
1631}
1632
1633node! {
1634    /// A function call's argument list: `(12pt, y)`.
1635    Args
1636}
1637
1638impl<'a> Args<'a> {
1639    /// The positional and named arguments.
1640    pub fn items(self) -> impl DoubleEndedIterator<Item = Arg<'a>> {
1641        self.0.children().filter_map(SyntaxNode::cast)
1642    }
1643
1644    /// Whether there is a comma at the end.
1645    pub fn trailing_comma(self) -> bool {
1646        self.0
1647            .children()
1648            .rev()
1649            .skip(1)
1650            .find(|n| !n.kind().is_trivia())
1651            .is_some_and(|n| n.kind() == SyntaxKind::Comma)
1652    }
1653}
1654
1655/// An argument to a function call.
1656#[derive(Debug, Copy, Clone, Hash)]
1657pub enum Arg<'a> {
1658    /// A positional argument: `12`.
1659    Pos(Expr<'a>),
1660    /// A named argument: `draw: false`.
1661    Named(Named<'a>),
1662    /// A spread argument: `..things`.
1663    Spread(Spread<'a>),
1664}
1665
1666impl<'a> AstNode<'a> for Arg<'a> {
1667    fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
1668        match node.kind() {
1669            SyntaxKind::Named => node.cast().map(Self::Named),
1670            SyntaxKind::Spread => node.cast().map(Self::Spread),
1671            _ => node.cast().map(Self::Pos),
1672        }
1673    }
1674
1675    fn to_untyped(self) -> &'a SyntaxNode {
1676        match self {
1677            Self::Pos(v) => v.to_untyped(),
1678            Self::Named(v) => v.to_untyped(),
1679            Self::Spread(v) => v.to_untyped(),
1680        }
1681    }
1682}
1683
1684node! {
1685    /// A closure: `(x, y) => z`.
1686    Closure
1687}
1688
1689impl<'a> Closure<'a> {
1690    /// The name of the closure.
1691    ///
1692    /// This only exists if you use the function syntax sugar: `let f(x) = y`.
1693    pub fn name(self) -> Option<Ident<'a>> {
1694        self.0.children().next()?.cast()
1695    }
1696
1697    /// The parameter bindings.
1698    pub fn params(self) -> Params<'a> {
1699        self.0.cast_first_match().unwrap_or_default()
1700    }
1701
1702    /// The body of the closure.
1703    pub fn body(self) -> Expr<'a> {
1704        self.0.cast_last_match().unwrap_or_default()
1705    }
1706}
1707
1708node! {
1709    /// A closure's parameters: `(x, y)`.
1710    Params
1711}
1712
1713impl<'a> Params<'a> {
1714    /// The parameter bindings.
1715    pub fn children(self) -> impl DoubleEndedIterator<Item = Param<'a>> {
1716        self.0.children().filter_map(SyntaxNode::cast)
1717    }
1718}
1719
1720/// A parameter to a closure.
1721#[derive(Debug, Copy, Clone, Hash)]
1722pub enum Param<'a> {
1723    /// A positional parameter: `x`.
1724    Pos(Pattern<'a>),
1725    /// A named parameter with a default value: `draw: false`.
1726    Named(Named<'a>),
1727    /// An argument sink: `..args` or `..`.
1728    Spread(Spread<'a>),
1729}
1730
1731impl<'a> AstNode<'a> for Param<'a> {
1732    fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
1733        match node.kind() {
1734            SyntaxKind::Named => node.cast().map(Self::Named),
1735            SyntaxKind::Spread => node.cast().map(Self::Spread),
1736            _ => node.cast().map(Self::Pos),
1737        }
1738    }
1739
1740    fn to_untyped(self) -> &'a SyntaxNode {
1741        match self {
1742            Self::Pos(v) => v.to_untyped(),
1743            Self::Named(v) => v.to_untyped(),
1744            Self::Spread(v) => v.to_untyped(),
1745        }
1746    }
1747}
1748
1749/// The kind of a pattern.
1750#[derive(Debug, Copy, Clone, Hash)]
1751pub enum Pattern<'a> {
1752    /// A single expression: `x`.
1753    Normal(Expr<'a>),
1754    /// A placeholder: `_`.
1755    Placeholder(Underscore<'a>),
1756    /// A parenthesized pattern.
1757    Parenthesized(Parenthesized<'a>),
1758    /// A destructuring pattern: `(x, _, ..y)`.
1759    Destructuring(Destructuring<'a>),
1760}
1761
1762impl<'a> AstNode<'a> for Pattern<'a> {
1763    fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
1764        match node.kind() {
1765            SyntaxKind::Underscore => node.cast().map(Self::Placeholder),
1766            SyntaxKind::Parenthesized => node.cast().map(Self::Parenthesized),
1767            SyntaxKind::Destructuring => node.cast().map(Self::Destructuring),
1768            _ => node.cast().map(Self::Normal),
1769        }
1770    }
1771
1772    fn to_untyped(self) -> &'a SyntaxNode {
1773        match self {
1774            Self::Normal(v) => v.to_untyped(),
1775            Self::Placeholder(v) => v.to_untyped(),
1776            Self::Parenthesized(v) => v.to_untyped(),
1777            Self::Destructuring(v) => v.to_untyped(),
1778        }
1779    }
1780}
1781
1782impl<'a> Pattern<'a> {
1783    /// Returns a list of all new bindings introduced by the pattern.
1784    pub fn bindings(self) -> Vec<Ident<'a>> {
1785        match self {
1786            Self::Normal(Expr::Ident(ident)) => vec![ident],
1787            Self::Parenthesized(v) => v.pattern().bindings(),
1788            Self::Destructuring(v) => v.bindings(),
1789            _ => vec![],
1790        }
1791    }
1792}
1793
1794impl Default for Pattern<'_> {
1795    fn default() -> Self {
1796        Self::Normal(Expr::default())
1797    }
1798}
1799
1800node! {
1801    /// An underscore: `_`
1802    Underscore
1803}
1804
1805node! {
1806    /// A destructuring pattern: `x` or `(x, _, ..y)`.
1807    Destructuring
1808}
1809
1810impl<'a> Destructuring<'a> {
1811    /// The items of the destructuring.
1812    pub fn items(self) -> impl DoubleEndedIterator<Item = DestructuringItem<'a>> {
1813        self.0.children().filter_map(SyntaxNode::cast)
1814    }
1815
1816    /// Returns a list of all new bindings introduced by the destructuring.
1817    pub fn bindings(self) -> Vec<Ident<'a>> {
1818        self.items()
1819            .flat_map(|binding| match binding {
1820                DestructuringItem::Pattern(pattern) => pattern.bindings(),
1821                DestructuringItem::Named(named) => named.pattern().bindings(),
1822                DestructuringItem::Spread(spread) => {
1823                    spread.sink_ident().into_iter().collect()
1824                }
1825            })
1826            .collect()
1827    }
1828}
1829
1830/// The kind of an element in a destructuring pattern.
1831#[derive(Debug, Copy, Clone, Hash)]
1832pub enum DestructuringItem<'a> {
1833    /// A sub-pattern: `x`.
1834    Pattern(Pattern<'a>),
1835    /// A renamed destructuring: `x: y`.
1836    Named(Named<'a>),
1837    /// A destructuring sink: `..y` or `..`.
1838    Spread(Spread<'a>),
1839}
1840
1841impl<'a> AstNode<'a> for DestructuringItem<'a> {
1842    fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
1843        match node.kind() {
1844            SyntaxKind::Named => node.cast().map(Self::Named),
1845            SyntaxKind::Spread => node.cast().map(Self::Spread),
1846            _ => node.cast().map(Self::Pattern),
1847        }
1848    }
1849
1850    fn to_untyped(self) -> &'a SyntaxNode {
1851        match self {
1852            Self::Pattern(v) => v.to_untyped(),
1853            Self::Named(v) => v.to_untyped(),
1854            Self::Spread(v) => v.to_untyped(),
1855        }
1856    }
1857}
1858
1859node! {
1860    /// A let binding: `let x = 1`.
1861    LetBinding
1862}
1863
1864/// The kind of a let binding, either a normal one or a closure.
1865#[derive(Debug)]
1866pub enum LetBindingKind<'a> {
1867    /// A normal binding: `let x = 1`.
1868    Normal(Pattern<'a>),
1869    /// A closure binding: `let f(x) = 1`.
1870    Closure(Ident<'a>),
1871}
1872
1873impl<'a> LetBindingKind<'a> {
1874    /// Returns a list of all new bindings introduced by the let binding.
1875    pub fn bindings(self) -> Vec<Ident<'a>> {
1876        match self {
1877            LetBindingKind::Normal(pattern) => pattern.bindings(),
1878            LetBindingKind::Closure(ident) => vec![ident],
1879        }
1880    }
1881}
1882
1883impl<'a> LetBinding<'a> {
1884    /// The kind of the let binding.
1885    pub fn kind(self) -> LetBindingKind<'a> {
1886        match self.0.cast_first_match::<Pattern>() {
1887            Some(Pattern::Normal(Expr::Closure(closure))) => {
1888                LetBindingKind::Closure(closure.name().unwrap_or_default())
1889            }
1890            pattern => LetBindingKind::Normal(pattern.unwrap_or_default()),
1891        }
1892    }
1893
1894    /// The expression the binding is initialized with.
1895    pub fn init(self) -> Option<Expr<'a>> {
1896        match self.kind() {
1897            LetBindingKind::Normal(Pattern::Normal(_) | Pattern::Parenthesized(_)) => {
1898                self.0.children().filter_map(SyntaxNode::cast).nth(1)
1899            }
1900            LetBindingKind::Normal(_) => self.0.cast_first_match(),
1901            LetBindingKind::Closure(_) => self.0.cast_first_match(),
1902        }
1903    }
1904}
1905
1906node! {
1907    /// An assignment expression `(x, y) = (1, 2)`.
1908    DestructAssignment
1909}
1910
1911impl<'a> DestructAssignment<'a> {
1912    /// The pattern of the assignment.
1913    pub fn pattern(self) -> Pattern<'a> {
1914        self.0.cast_first_match::<Pattern>().unwrap_or_default()
1915    }
1916
1917    /// The expression that is assigned.
1918    pub fn value(self) -> Expr<'a> {
1919        self.0.cast_last_match().unwrap_or_default()
1920    }
1921}
1922
1923node! {
1924    /// A set rule: `set text(...)`.
1925    SetRule
1926}
1927
1928impl<'a> SetRule<'a> {
1929    /// The function to set style properties for.
1930    pub fn target(self) -> Expr<'a> {
1931        self.0.cast_first_match().unwrap_or_default()
1932    }
1933
1934    /// The style properties to set.
1935    pub fn args(self) -> Args<'a> {
1936        self.0.cast_last_match().unwrap_or_default()
1937    }
1938
1939    /// A condition under which the set rule applies.
1940    pub fn condition(self) -> Option<Expr<'a>> {
1941        self.0
1942            .children()
1943            .skip_while(|child| child.kind() != SyntaxKind::If)
1944            .find_map(SyntaxNode::cast)
1945    }
1946}
1947
1948node! {
1949    /// A show rule: `show heading: it => emph(it.body)`.
1950    ShowRule
1951}
1952
1953impl<'a> ShowRule<'a> {
1954    /// Defines which nodes the show rule applies to.
1955    pub fn selector(self) -> Option<Expr<'a>> {
1956        self.0
1957            .children()
1958            .rev()
1959            .skip_while(|child| child.kind() != SyntaxKind::Colon)
1960            .find_map(SyntaxNode::cast)
1961    }
1962
1963    /// The transformation recipe.
1964    pub fn transform(self) -> Expr<'a> {
1965        self.0.cast_last_match().unwrap_or_default()
1966    }
1967}
1968
1969node! {
1970    /// A contextual expression: `context text.lang`.
1971    Contextual
1972}
1973
1974impl<'a> Contextual<'a> {
1975    /// The expression which depends on the context.
1976    pub fn body(self) -> Expr<'a> {
1977        self.0.cast_first_match().unwrap_or_default()
1978    }
1979}
1980
1981node! {
1982    /// An if-else conditional: `if x { y } else { z }`.
1983    Conditional
1984}
1985
1986impl<'a> Conditional<'a> {
1987    /// The condition which selects the body to evaluate.
1988    pub fn condition(self) -> Expr<'a> {
1989        self.0.cast_first_match().unwrap_or_default()
1990    }
1991
1992    /// The expression to evaluate if the condition is true.
1993    pub fn if_body(self) -> Expr<'a> {
1994        self.0
1995            .children()
1996            .filter_map(SyntaxNode::cast)
1997            .nth(1)
1998            .unwrap_or_default()
1999    }
2000
2001    /// The expression to evaluate if the condition is false.
2002    pub fn else_body(self) -> Option<Expr<'a>> {
2003        self.0.children().filter_map(SyntaxNode::cast).nth(2)
2004    }
2005}
2006
2007node! {
2008    /// A while loop: `while x { y }`.
2009    WhileLoop
2010}
2011
2012impl<'a> WhileLoop<'a> {
2013    /// The condition which selects whether to evaluate the body.
2014    pub fn condition(self) -> Expr<'a> {
2015        self.0.cast_first_match().unwrap_or_default()
2016    }
2017
2018    /// The expression to evaluate while the condition is true.
2019    pub fn body(self) -> Expr<'a> {
2020        self.0.cast_last_match().unwrap_or_default()
2021    }
2022}
2023
2024node! {
2025    /// A for loop: `for x in y { z }`.
2026    ForLoop
2027}
2028
2029impl<'a> ForLoop<'a> {
2030    /// The pattern to assign to.
2031    pub fn pattern(self) -> Pattern<'a> {
2032        self.0.cast_first_match().unwrap_or_default()
2033    }
2034
2035    /// The expression to iterate over.
2036    pub fn iterable(self) -> Expr<'a> {
2037        self.0
2038            .children()
2039            .skip_while(|&c| c.kind() != SyntaxKind::In)
2040            .find_map(SyntaxNode::cast)
2041            .unwrap_or_default()
2042    }
2043
2044    /// The expression to evaluate for each iteration.
2045    pub fn body(self) -> Expr<'a> {
2046        self.0.cast_last_match().unwrap_or_default()
2047    }
2048}
2049
2050node! {
2051    /// A module import: `import "utils.typ": a, b, c`.
2052    ModuleImport
2053}
2054
2055impl<'a> ModuleImport<'a> {
2056    /// The module or path from which the items should be imported.
2057    pub fn source(self) -> Expr<'a> {
2058        self.0.cast_first_match().unwrap_or_default()
2059    }
2060
2061    /// The items to be imported.
2062    pub fn imports(self) -> Option<Imports<'a>> {
2063        self.0.children().find_map(|node| match node.kind() {
2064            SyntaxKind::Star => Some(Imports::Wildcard),
2065            SyntaxKind::ImportItems => node.cast().map(Imports::Items),
2066            _ => Option::None,
2067        })
2068    }
2069
2070    /// The name that will be bound for a bare import. This name must be
2071    /// statically known. It can come from:
2072    /// - an identifier
2073    /// - a field access
2074    /// - a string that is a valid file path where the file stem is a valid
2075    ///   identifier
2076    /// - a string that is a valid package spec
2077    pub fn bare_name(self) -> Result<EcoString, BareImportError> {
2078        match self.source() {
2079            Expr::Ident(ident) => Ok(ident.get().clone()),
2080            Expr::FieldAccess(access) => Ok(access.field().get().clone()),
2081            Expr::Str(string) => {
2082                let string = string.get();
2083                let name = if string.starts_with('@') {
2084                    PackageSpec::from_str(&string)
2085                        .map_err(|_| BareImportError::PackageInvalid)?
2086                        .name
2087                } else {
2088                    Path::new(string.as_str())
2089                        .file_stem()
2090                        .and_then(|path| path.to_str())
2091                        .ok_or(BareImportError::PathInvalid)?
2092                        .into()
2093                };
2094
2095                if !is_ident(&name) {
2096                    return Err(BareImportError::PathInvalid);
2097                }
2098
2099                Ok(name)
2100            }
2101            _ => Err(BareImportError::Dynamic),
2102        }
2103    }
2104
2105    /// The name this module was assigned to, if it was renamed with `as`
2106    /// (`renamed` in `import "..." as renamed`).
2107    pub fn new_name(self) -> Option<Ident<'a>> {
2108        self.0
2109            .children()
2110            .skip_while(|child| child.kind() != SyntaxKind::As)
2111            .find_map(SyntaxNode::cast)
2112    }
2113}
2114
2115/// Reasons why a bare name cannot be determined for an import source.
2116#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
2117pub enum BareImportError {
2118    /// There is no statically resolvable binding name.
2119    Dynamic,
2120    /// The import source is not a valid path or the path stem not a valid
2121    /// identifier.
2122    PathInvalid,
2123    /// The import source is not a valid package spec.
2124    PackageInvalid,
2125}
2126
2127/// The items that ought to be imported from a file.
2128#[derive(Debug, Copy, Clone, Hash)]
2129pub enum Imports<'a> {
2130    /// All items in the scope of the file should be imported.
2131    Wildcard,
2132    /// The specified items from the file should be imported.
2133    Items(ImportItems<'a>),
2134}
2135
2136node! {
2137    /// Items to import from a module: `a, b, c`.
2138    ImportItems
2139}
2140
2141impl<'a> ImportItems<'a> {
2142    /// Returns an iterator over the items to import from the module.
2143    pub fn iter(self) -> impl DoubleEndedIterator<Item = ImportItem<'a>> {
2144        self.0.children().filter_map(|child| match child.kind() {
2145            SyntaxKind::RenamedImportItem => child.cast().map(ImportItem::Renamed),
2146            SyntaxKind::ImportItemPath => child.cast().map(ImportItem::Simple),
2147            _ => Option::None,
2148        })
2149    }
2150}
2151
2152node! {
2153    /// A path to a submodule's imported name: `a.b.c`.
2154    ImportItemPath
2155}
2156
2157impl<'a> ImportItemPath<'a> {
2158    /// An iterator over the path's components.
2159    pub fn iter(self) -> impl DoubleEndedIterator<Item = Ident<'a>> {
2160        self.0.children().filter_map(SyntaxNode::cast)
2161    }
2162
2163    /// The name of the imported item. This is the last segment in the path.
2164    pub fn name(self) -> Ident<'a> {
2165        self.iter().last().unwrap_or_default()
2166    }
2167}
2168
2169/// An imported item, potentially renamed to another identifier.
2170#[derive(Debug, Copy, Clone, Hash)]
2171pub enum ImportItem<'a> {
2172    /// A non-renamed import (the item's name in the scope is the same as its
2173    /// name).
2174    Simple(ImportItemPath<'a>),
2175    /// A renamed import (the item was bound to a different name in the scope
2176    /// than the one it was defined as).
2177    Renamed(RenamedImportItem<'a>),
2178}
2179
2180impl<'a> ImportItem<'a> {
2181    /// The path to the imported item.
2182    pub fn path(self) -> ImportItemPath<'a> {
2183        match self {
2184            Self::Simple(path) => path,
2185            Self::Renamed(renamed_item) => renamed_item.path(),
2186        }
2187    }
2188
2189    /// The original name of the imported item, at its source. This will be the
2190    /// equal to the bound name if the item wasn't renamed with 'as'.
2191    pub fn original_name(self) -> Ident<'a> {
2192        match self {
2193            Self::Simple(path) => path.name(),
2194            Self::Renamed(renamed_item) => renamed_item.original_name(),
2195        }
2196    }
2197
2198    /// The name which this import item was bound to. Corresponds to the new
2199    /// name, if it was renamed; otherwise, it's just its original name.
2200    pub fn bound_name(self) -> Ident<'a> {
2201        match self {
2202            Self::Simple(path) => path.name(),
2203            Self::Renamed(renamed_item) => renamed_item.new_name(),
2204        }
2205    }
2206}
2207
2208node! {
2209    /// A renamed import item: `a as d`
2210    RenamedImportItem
2211}
2212
2213impl<'a> RenamedImportItem<'a> {
2214    /// The path to the imported item.
2215    pub fn path(self) -> ImportItemPath<'a> {
2216        self.0.cast_first_match().unwrap_or_default()
2217    }
2218
2219    /// The original name of the imported item (`a` in `a as d` or `c.b.a as d`).
2220    pub fn original_name(self) -> Ident<'a> {
2221        self.path().name()
2222    }
2223
2224    /// The new name of the imported item (`d` in `a as d`).
2225    pub fn new_name(self) -> Ident<'a> {
2226        self.0
2227            .children()
2228            .filter_map(SyntaxNode::cast)
2229            .last()
2230            .unwrap_or_default()
2231    }
2232}
2233
2234node! {
2235    /// A module include: `include "chapter1.typ"`.
2236    ModuleInclude
2237}
2238
2239impl<'a> ModuleInclude<'a> {
2240    /// The module or path from which the content should be included.
2241    pub fn source(self) -> Expr<'a> {
2242        self.0.cast_last_match().unwrap_or_default()
2243    }
2244}
2245
2246node! {
2247    /// A break from a loop: `break`.
2248    LoopBreak
2249}
2250
2251node! {
2252    /// A continue in a loop: `continue`.
2253    LoopContinue
2254}
2255
2256node! {
2257    /// A return from a function: `return`, `return x + 1`.
2258    FuncReturn
2259}
2260
2261impl<'a> FuncReturn<'a> {
2262    /// The expression to return.
2263    pub fn body(self) -> Option<Expr<'a>> {
2264        self.0.cast_last_match()
2265    }
2266}
2267
2268#[cfg(test)]
2269mod tests {
2270    use super::*;
2271
2272    #[test]
2273    fn test_expr_default() {
2274        assert!(Expr::default().to_untyped().cast::<Expr>().is_some());
2275    }
2276}