summaryrefslogtreecommitdiff
path: root/src/syntax/ast.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/syntax/ast.rs')
-rw-r--r--src/syntax/ast.rs1994
1 files changed, 0 insertions, 1994 deletions
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
deleted file mode 100644
index 7d5e2989..00000000
--- a/src/syntax/ast.rs
+++ /dev/null
@@ -1,1994 +0,0 @@
-//! A typed layer over the untyped syntax tree.
-//!
-//! The AST is rooted in the [`Markup`] node.
-
-use std::num::NonZeroUsize;
-use std::ops::Deref;
-
-use ecow::EcoString;
-use unscanny::Scanner;
-
-use super::{
- is_id_continue, is_id_start, is_newline, split_newlines, Span, SyntaxKind, SyntaxNode,
-};
-use crate::geom::{AbsUnit, AngleUnit};
-use crate::util::NonZeroExt;
-
-/// A typed AST node.
-pub trait AstNode: Sized {
- /// Convert a node into its typed variant.
- fn from_untyped(node: &SyntaxNode) -> Option<Self>;
-
- /// A reference to the underlying syntax node.
- fn as_untyped(&self) -> &SyntaxNode;
-
- /// The source code location.
- fn span(&self) -> Span {
- self.as_untyped().span()
- }
-}
-
-macro_rules! node {
- ($(#[$attr:meta])* $name:ident) => {
- #[derive(Debug, Default, Clone, Hash)]
- #[repr(transparent)]
- $(#[$attr])*
- pub struct $name(SyntaxNode);
-
- impl AstNode for $name {
- fn from_untyped(node: &SyntaxNode) -> Option<Self> {
- if matches!(node.kind(), SyntaxKind::$name) {
- Some(Self(node.clone()))
- } else {
- Option::None
- }
- }
-
- fn as_untyped(&self) -> &SyntaxNode {
- &self.0
- }
- }
- };
-}
-
-node! {
- /// The syntactical root capable of representing a full parsed document.
- Markup
-}
-
-impl Markup {
- /// The expressions.
- pub fn exprs(&self) -> impl DoubleEndedIterator<Item = Expr> + '_ {
- let mut was_stmt = false;
- self.0
- .children()
- .filter(move |node| {
- // Ignore newline directly after statements without semicolons.
- let kind = node.kind();
- let keep = !was_stmt || node.kind() != SyntaxKind::Space;
- was_stmt = kind.is_stmt();
- keep
- })
- .filter_map(Expr::cast_with_space)
- }
-}
-
-/// An expression in markup, math or code.
-#[derive(Debug, Clone, Hash)]
-pub enum Expr {
- /// Plain text without markup.
- Text(Text),
- /// Whitespace in markup or math. Has at most one newline in markup, as more
- /// indicate a paragraph break.
- Space(Space),
- /// A forced line break: `\`.
- Linebreak(Linebreak),
- /// A paragraph break, indicated by one or multiple blank lines.
- Parbreak(Parbreak),
- /// An escape sequence: `\#`, `\u{1F5FA}`.
- Escape(Escape),
- /// A shorthand for a unicode codepoint. For example, `~` for non-breaking
- /// space or `-?` for a soft hyphen.
- Shorthand(Shorthand),
- /// A smart quote: `'` or `"`.
- SmartQuote(SmartQuote),
- /// Strong content: `*Strong*`.
- Strong(Strong),
- /// Emphasized content: `_Emphasized_`.
- Emph(Emph),
- /// Raw text with optional syntax highlighting: `` `...` ``.
- Raw(Raw),
- /// A hyperlink: `https://typst.org`.
- Link(Link),
- /// A label: `<intro>`.
- Label(Label),
- /// A reference: `@target`, `@target[..]`.
- Ref(Ref),
- /// A section heading: `= Introduction`.
- Heading(Heading),
- /// An item in a bullet list: `- ...`.
- List(ListItem),
- /// An item in an enumeration (numbered list): `+ ...` or `1. ...`.
- Enum(EnumItem),
- /// An item in a term list: `/ Term: Details`.
- Term(TermItem),
- /// A mathematical equation: `$x$`, `$ x^2 $`.
- Equation(Equation),
- /// The contents of a mathematical equation: `x^2 + 1`.
- Math(Math),
- /// An identifier in math: `pi`.
- MathIdent(MathIdent),
- /// An alignment point in math: `&`.
- MathAlignPoint(MathAlignPoint),
- /// Matched delimiters in math: `[x + y]`.
- MathDelimited(MathDelimited),
- /// A base with optional attachments in math: `a_1^2`.
- MathAttach(MathAttach),
- /// A fraction in math: `x/2`.
- MathFrac(MathFrac),
- /// A root in math: `√x`, `∛x` or `∜x`.
- MathRoot(MathRoot),
- /// An identifier: `left`.
- Ident(Ident),
- /// The `none` literal.
- None(None),
- /// The `auto` literal.
- Auto(Auto),
- /// A boolean: `true`, `false`.
- Bool(Bool),
- /// An integer: `120`.
- Int(Int),
- /// A floating-point number: `1.2`, `10e-4`.
- Float(Float),
- /// A numeric value with a unit: `12pt`, `3cm`, `2em`, `90deg`, `50%`.
- Numeric(Numeric),
- /// A quoted string: `"..."`.
- Str(Str),
- /// A code block: `{ let x = 1; x + 2 }`.
- Code(CodeBlock),
- /// A content block: `[*Hi* there!]`.
- Content(ContentBlock),
- /// A grouped expression: `(1 + 2)`.
- Parenthesized(Parenthesized),
- /// An array: `(1, "hi", 12cm)`.
- Array(Array),
- /// A dictionary: `(thickness: 3pt, pattern: dashed)`.
- Dict(Dict),
- /// A unary operation: `-x`.
- Unary(Unary),
- /// A binary operation: `a + b`.
- Binary(Binary),
- /// A field access: `properties.age`.
- FieldAccess(FieldAccess),
- /// An invocation of a function or method: `f(x, y)`.
- FuncCall(FuncCall),
- /// A closure: `(x, y) => z`.
- Closure(Closure),
- /// A let binding: `let x = 1`.
- Let(LetBinding),
- //// A destructuring assignment: `(x, y) = (1, 2)`.
- DestructAssign(DestructAssignment),
- /// A set rule: `set text(...)`.
- Set(SetRule),
- /// A show rule: `show heading: it => emph(it.body)`.
- Show(ShowRule),
- /// An if-else conditional: `if x { y } else { z }`.
- Conditional(Conditional),
- /// A while loop: `while x { y }`.
- While(WhileLoop),
- /// A for loop: `for x in y { z }`.
- For(ForLoop),
- /// A module import: `import "utils.typ": a, b, c`.
- Import(ModuleImport),
- /// A module include: `include "chapter1.typ"`.
- Include(ModuleInclude),
- /// A break from a loop: `break`.
- Break(LoopBreak),
- /// A continue in a loop: `continue`.
- Continue(LoopContinue),
- /// A return from a function: `return`, `return x + 1`.
- Return(FuncReturn),
-}
-
-impl Expr {
- fn cast_with_space(node: &SyntaxNode) -> Option<Self> {
- match node.kind() {
- SyntaxKind::Space => node.cast().map(Self::Space),
- _ => Self::from_untyped(node),
- }
- }
-}
-
-impl AstNode for Expr {
- fn from_untyped(node: &SyntaxNode) -> Option<Self> {
- match node.kind() {
- SyntaxKind::Linebreak => node.cast().map(Self::Linebreak),
- SyntaxKind::Parbreak => node.cast().map(Self::Parbreak),
- SyntaxKind::Text => node.cast().map(Self::Text),
- SyntaxKind::Escape => node.cast().map(Self::Escape),
- SyntaxKind::Shorthand => node.cast().map(Self::Shorthand),
- SyntaxKind::SmartQuote => node.cast().map(Self::SmartQuote),
- SyntaxKind::Strong => node.cast().map(Self::Strong),
- SyntaxKind::Emph => node.cast().map(Self::Emph),
- SyntaxKind::Raw => node.cast().map(Self::Raw),
- SyntaxKind::Link => node.cast().map(Self::Link),
- SyntaxKind::Label => node.cast().map(Self::Label),
- SyntaxKind::Ref => node.cast().map(Self::Ref),
- SyntaxKind::Heading => node.cast().map(Self::Heading),
- SyntaxKind::ListItem => node.cast().map(Self::List),
- SyntaxKind::EnumItem => node.cast().map(Self::Enum),
- SyntaxKind::TermItem => node.cast().map(Self::Term),
- SyntaxKind::Equation => node.cast().map(Self::Equation),
- SyntaxKind::Math => node.cast().map(Self::Math),
- SyntaxKind::MathIdent => node.cast().map(Self::MathIdent),
- SyntaxKind::MathAlignPoint => node.cast().map(Self::MathAlignPoint),
- SyntaxKind::MathDelimited => node.cast().map(Self::MathDelimited),
- SyntaxKind::MathAttach => node.cast().map(Self::MathAttach),
- SyntaxKind::MathFrac => node.cast().map(Self::MathFrac),
- SyntaxKind::MathRoot => node.cast().map(Self::MathRoot),
- SyntaxKind::Ident => node.cast().map(Self::Ident),
- SyntaxKind::None => node.cast().map(Self::None),
- SyntaxKind::Auto => node.cast().map(Self::Auto),
- SyntaxKind::Bool => node.cast().map(Self::Bool),
- SyntaxKind::Int => node.cast().map(Self::Int),
- SyntaxKind::Float => node.cast().map(Self::Float),
- SyntaxKind::Numeric => node.cast().map(Self::Numeric),
- SyntaxKind::Str => node.cast().map(Self::Str),
- SyntaxKind::CodeBlock => node.cast().map(Self::Code),
- SyntaxKind::ContentBlock => node.cast().map(Self::Content),
- SyntaxKind::Parenthesized => node.cast().map(Self::Parenthesized),
- SyntaxKind::Array => node.cast().map(Self::Array),
- SyntaxKind::Dict => node.cast().map(Self::Dict),
- SyntaxKind::Unary => node.cast().map(Self::Unary),
- SyntaxKind::Binary => node.cast().map(Self::Binary),
- SyntaxKind::FieldAccess => node.cast().map(Self::FieldAccess),
- SyntaxKind::FuncCall => node.cast().map(Self::FuncCall),
- SyntaxKind::Closure => node.cast().map(Self::Closure),
- SyntaxKind::LetBinding => node.cast().map(Self::Let),
- SyntaxKind::DestructAssignment => node.cast().map(Self::DestructAssign),
- SyntaxKind::SetRule => node.cast().map(Self::Set),
- SyntaxKind::ShowRule => node.cast().map(Self::Show),
- SyntaxKind::Conditional => node.cast().map(Self::Conditional),
- SyntaxKind::WhileLoop => node.cast().map(Self::While),
- SyntaxKind::ForLoop => node.cast().map(Self::For),
- SyntaxKind::ModuleImport => node.cast().map(Self::Import),
- SyntaxKind::ModuleInclude => node.cast().map(Self::Include),
- SyntaxKind::LoopBreak => node.cast().map(Self::Break),
- SyntaxKind::LoopContinue => node.cast().map(Self::Continue),
- SyntaxKind::FuncReturn => node.cast().map(Self::Return),
- _ => Option::None,
- }
- }
-
- fn as_untyped(&self) -> &SyntaxNode {
- match self {
- Self::Text(v) => v.as_untyped(),
- Self::Space(v) => v.as_untyped(),
- Self::Linebreak(v) => v.as_untyped(),
- Self::Parbreak(v) => v.as_untyped(),
- Self::Escape(v) => v.as_untyped(),
- Self::Shorthand(v) => v.as_untyped(),
- Self::SmartQuote(v) => v.as_untyped(),
- Self::Strong(v) => v.as_untyped(),
- Self::Emph(v) => v.as_untyped(),
- Self::Raw(v) => v.as_untyped(),
- Self::Link(v) => v.as_untyped(),
- Self::Label(v) => v.as_untyped(),
- Self::Ref(v) => v.as_untyped(),
- Self::Heading(v) => v.as_untyped(),
- Self::List(v) => v.as_untyped(),
- Self::Enum(v) => v.as_untyped(),
- Self::Term(v) => v.as_untyped(),
- Self::Equation(v) => v.as_untyped(),
- Self::Math(v) => v.as_untyped(),
- Self::MathIdent(v) => v.as_untyped(),
- Self::MathAlignPoint(v) => v.as_untyped(),
- Self::MathDelimited(v) => v.as_untyped(),
- Self::MathAttach(v) => v.as_untyped(),
- Self::MathFrac(v) => v.as_untyped(),
- Self::MathRoot(v) => v.as_untyped(),
- Self::Ident(v) => v.as_untyped(),
- Self::None(v) => v.as_untyped(),
- Self::Auto(v) => v.as_untyped(),
- Self::Bool(v) => v.as_untyped(),
- Self::Int(v) => v.as_untyped(),
- Self::Float(v) => v.as_untyped(),
- Self::Numeric(v) => v.as_untyped(),
- Self::Str(v) => v.as_untyped(),
- Self::Code(v) => v.as_untyped(),
- Self::Content(v) => v.as_untyped(),
- Self::Array(v) => v.as_untyped(),
- Self::Dict(v) => v.as_untyped(),
- Self::Parenthesized(v) => v.as_untyped(),
- Self::Unary(v) => v.as_untyped(),
- Self::Binary(v) => v.as_untyped(),
- Self::FieldAccess(v) => v.as_untyped(),
- Self::FuncCall(v) => v.as_untyped(),
- Self::Closure(v) => v.as_untyped(),
- Self::Let(v) => v.as_untyped(),
- Self::DestructAssign(v) => v.as_untyped(),
- Self::Set(v) => v.as_untyped(),
- Self::Show(v) => v.as_untyped(),
- Self::Conditional(v) => v.as_untyped(),
- Self::While(v) => v.as_untyped(),
- Self::For(v) => v.as_untyped(),
- Self::Import(v) => v.as_untyped(),
- Self::Include(v) => v.as_untyped(),
- Self::Break(v) => v.as_untyped(),
- Self::Continue(v) => v.as_untyped(),
- Self::Return(v) => v.as_untyped(),
- }
- }
-}
-
-impl Expr {
- /// Can this expression be embedded into markup with a hashtag?
- pub fn hashtag(&self) -> bool {
- matches!(
- self,
- Self::Ident(_)
- | Self::None(_)
- | Self::Auto(_)
- | Self::Bool(_)
- | Self::Int(_)
- | Self::Float(_)
- | Self::Numeric(_)
- | Self::Str(_)
- | Self::Code(_)
- | Self::Content(_)
- | Self::Array(_)
- | Self::Dict(_)
- | Self::Parenthesized(_)
- | Self::FieldAccess(_)
- | Self::FuncCall(_)
- | Self::Let(_)
- | Self::Set(_)
- | Self::Show(_)
- | Self::Conditional(_)
- | Self::While(_)
- | Self::For(_)
- | Self::Import(_)
- | Self::Include(_)
- | Self::Break(_)
- | Self::Continue(_)
- | Self::Return(_)
- )
- }
-
- /// Is this a literal?
- pub fn is_literal(&self) -> bool {
- matches!(
- self,
- Self::None(_)
- | Self::Auto(_)
- | Self::Bool(_)
- | Self::Int(_)
- | Self::Float(_)
- | Self::Numeric(_)
- | Self::Str(_)
- )
- }
-}
-
-impl Default for Expr {
- fn default() -> Self {
- Expr::Space(Space::default())
- }
-}
-
-node! {
- /// Plain text without markup.
- Text
-}
-
-impl Text {
- /// Get the text.
- pub fn get(&self) -> &EcoString {
- self.0.text()
- }
-}
-
-node! {
- /// Whitespace in markup or math. Has at most one newline in markup, as more
- /// indicate a paragraph break.
- Space
-}
-
-node! {
- /// A forced line break: `\`.
- Linebreak
-}
-
-node! {
- /// A paragraph break, indicated by one or multiple blank lines.
- Parbreak
-}
-
-node! {
- /// An escape sequence: `\#`, `\u{1F5FA}`.
- Escape
-}
-
-impl Escape {
- /// Get the escaped character.
- pub fn get(&self) -> char {
- let mut s = Scanner::new(self.0.text());
- s.expect('\\');
- if s.eat_if("u{") {
- let hex = s.eat_while(char::is_ascii_hexdigit);
- u32::from_str_radix(hex, 16)
- .ok()
- .and_then(std::char::from_u32)
- .unwrap_or_default()
- } else {
- s.eat().unwrap_or_default()
- }
- }
-}
-
-node! {
- /// A shorthand for a unicode codepoint. For example, `~` for a non-breaking
- /// space or `-?` for a soft hyphen.
- Shorthand
-}
-
-impl Shorthand {
- /// A list of all shorthands.
- pub const LIST: &[(&'static str, char)] = &[
- // Both.
- ("...", '…'),
- // Text only.
- ("~", '\u{00A0}'),
- ("--", '\u{2013}'),
- ("---", '\u{2014}'),
- ("-?", '\u{00AD}'),
- // Math only.
- ("-", '\u{2212}'),
- ("'", '′'),
- ("*", '∗'),
- ("!=", '≠'),
- (":=", '≔'),
- ("::=", '⩴'),
- ("=:", '≕'),
- ("<<", '≪'),
- ("<<<", '⋘'),
- (">>", '≫'),
- (">>>", '⋙'),
- ("<=", '≤'),
- (">=", '≥'),
- ("->", '→'),
- ("-->", '⟶'),
- ("|->", '↦'),
- (">->", '↣'),
- ("->>", '↠'),
- ("<-", '←'),
- ("<--", '⟵'),
- ("<-<", '↢'),
- ("<<-", '↞'),
- ("<->", '↔'),
- ("<-->", '⟷'),
- ("~>", '⇝'),
- ("~~>", '⟿'),
- ("<~", '⇜'),
- ("<~~", '⬳'),
- ("=>", '⇒'),
- ("|=>", '⤇'),
- ("==>", '⟹'),
- ("<==", '⟸'),
- ("<=>", '⇔'),
- ("<==>", '⟺'),
- ("[|", '⟦'),
- ("|]", '⟧'),
- ("||", '‖'),
- ];
-
- /// Get the shorthanded character.
- pub fn get(&self) -> char {
- let text = self.0.text();
- Self::LIST
- .iter()
- .find(|&&(s, _)| s == text)
- .map_or_else(char::default, |&(_, c)| c)
- }
-}
-
-node! {
- /// A smart quote: `'` or `"`.
- SmartQuote
-}
-
-impl SmartQuote {
- /// Whether this is a double quote.
- pub fn double(&self) -> bool {
- self.0.text() == "\""
- }
-}
-
-node! {
- /// Strong content: `*Strong*`.
- Strong
-}
-
-impl Strong {
- /// The contents of the strong node.
- pub fn body(&self) -> Markup {
- self.0.cast_first_match().unwrap_or_default()
- }
-}
-
-node! {
- /// Emphasized content: `_Emphasized_`.
- Emph
-}
-
-impl Emph {
- /// The contents of the emphasis node.
- pub fn body(&self) -> Markup {
- self.0.cast_first_match().unwrap_or_default()
- }
-}
-
-node! {
- /// Raw text with optional syntax highlighting: `` `...` ``.
- Raw
-}
-
-impl Raw {
- /// The trimmed raw text.
- pub fn text(&self) -> EcoString {
- let mut text = self.0.text().as_str();
- let blocky = text.starts_with("```");
- text = text.trim_matches('`');
-
- // Trim tag, one space at the start, and one space at the end if the
- // last non-whitespace char is a backtick.
- if blocky {
- let mut s = Scanner::new(text);
- if s.eat_if(is_id_start) {
- s.eat_while(is_id_continue);
- }
- text = s.after();
- text = text.strip_prefix(' ').unwrap_or(text);
- if text.trim_end().ends_with('`') {
- text = text.strip_suffix(' ').unwrap_or(text);
- }
- }
-
- // Split into lines.
- let mut lines = split_newlines(text);
-
- if blocky {
- let dedent = lines
- .iter()
- .skip(1)
- .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
- .min()
- .unwrap_or(0);
-
- // Dedent based on column, but not for the first line.
- for line in lines.iter_mut().skip(1) {
- let offset = line.chars().take(dedent).map(char::len_utf8).sum();
- *line = &line[offset..];
- }
-
- let is_whitespace = |line: &&str| line.chars().all(char::is_whitespace);
-
- // Trims a sequence of whitespace followed by a newline at the start.
- if lines.first().map_or(false, is_whitespace) {
- lines.remove(0);
- }
-
- // Trims a newline followed by a sequence of whitespace at the end.
- if lines.last().map_or(false, is_whitespace) {
- lines.pop();
- }
- }
-
- lines.join("\n").into()
- }
-
- /// An optional identifier specifying the language to syntax-highlight in.
- pub fn lang(&self) -> Option<&str> {
- let text = self.0.text();
-
- // Only blocky literals are supposed to contain a language.
- if !text.starts_with("```") {
- return Option::None;
- }
-
- let inner = text.trim_start_matches('`');
- let mut s = Scanner::new(inner);
- s.eat_if(is_id_start).then(|| {
- s.eat_while(is_id_continue);
- s.before()
- })
- }
-
- /// Whether the raw text should be displayed in a separate block.
- pub fn block(&self) -> bool {
- let text = self.0.text();
- text.starts_with("```") && text.chars().any(is_newline)
- }
-}
-
-node! {
- /// A hyperlink: `https://typst.org`.
- Link
-}
-
-impl Link {
- /// Get the URL.
- pub fn get(&self) -> &EcoString {
- self.0.text()
- }
-}
-
-node! {
- /// A label: `<intro>`.
- Label
-}
-
-impl Label {
- /// Get the label's text.
- pub fn get(&self) -> &str {
- self.0.text().trim_start_matches('<').trim_end_matches('>')
- }
-}
-
-node! {
- /// A reference: `@target`, `@target[..]`.
- Ref
-}
-
-impl Ref {
- /// Get the target.
- pub fn target(&self) -> &str {
- self.0
- .children()
- .find(|node| node.kind() == SyntaxKind::RefMarker)
- .map(|node| node.text().trim_start_matches('@'))
- .unwrap_or_default()
- }
-
- /// Get the supplement.
- pub fn supplement(&self) -> Option<ContentBlock> {
- self.0.cast_last_match()
- }
-}
-
-node! {
- /// A section heading: `= Introduction`.
- Heading
-}
-
-impl Heading {
- /// The contents of the heading.
- pub fn body(&self) -> Markup {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The section depth (number of equals signs).
- pub fn level(&self) -> NonZeroUsize {
- self.0
- .children()
- .find(|node| node.kind() == SyntaxKind::HeadingMarker)
- .and_then(|node| node.len().try_into().ok())
- .unwrap_or(NonZeroUsize::ONE)
- }
-}
-
-node! {
- /// An item in a bullet list: `- ...`.
- ListItem
-}
-
-impl ListItem {
- /// The contents of the list item.
- pub fn body(&self) -> Markup {
- self.0.cast_first_match().unwrap_or_default()
- }
-}
-
-node! {
- /// An item in an enumeration (numbered list): `+ ...` or `1. ...`.
- EnumItem
-}
-
-impl EnumItem {
- /// The explicit numbering, if any: `23.`.
- pub fn number(&self) -> Option<usize> {
- self.0.children().find_map(|node| match node.kind() {
- SyntaxKind::EnumMarker => node.text().trim_end_matches('.').parse().ok(),
- _ => Option::None,
- })
- }
-
- /// The contents of the list item.
- pub fn body(&self) -> Markup {
- self.0.cast_first_match().unwrap_or_default()
- }
-}
-
-node! {
- /// An item in a term list: `/ Term: Details`.
- TermItem
-}
-
-impl TermItem {
- /// The term described by the item.
- pub fn term(&self) -> Markup {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The description of the term.
- pub fn description(&self) -> Markup {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-node! {
- /// A mathemathical equation: `$x$`, `$ x^2 $`.
- Equation
-}
-
-impl Equation {
- /// The contained math.
- pub fn body(&self) -> Math {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// Whether the equation should be displayed as a separate block.
- pub fn block(&self) -> bool {
- let is_space = |node: Option<&SyntaxNode>| {
- node.map(SyntaxNode::kind) == Some(SyntaxKind::Space)
- };
- is_space(self.0.children().nth(1)) && is_space(self.0.children().nth_back(1))
- }
-}
-
-node! {
- /// The contents of a mathematical equation: `x^2 + 1`.
- Math
-}
-
-impl Math {
- /// The expressions the mathematical content consists of.
- pub fn exprs(&self) -> impl DoubleEndedIterator<Item = Expr> + '_ {
- self.0.children().filter_map(Expr::cast_with_space)
- }
-}
-
-node! {
- /// An identifier in math: `pi`.
- MathIdent
-}
-
-impl MathIdent {
- /// Get the identifier.
- pub fn get(&self) -> &EcoString {
- self.0.text()
- }
-
- /// Take out the contained identifier.
- pub fn take(self) -> EcoString {
- self.0.into_text()
- }
-
- /// Get the identifier as a string slice.
- pub fn as_str(&self) -> &str {
- self.get()
- }
-}
-
-impl Deref for MathIdent {
- type Target = str;
-
- fn deref(&self) -> &Self::Target {
- self.as_str()
- }
-}
-
-node! {
- /// An alignment point in math: `&`.
- MathAlignPoint
-}
-
-node! {
- /// Matched delimiters in math: `[x + y]`.
- MathDelimited
-}
-
-impl MathDelimited {
- /// The opening delimiter.
- pub fn open(&self) -> Expr {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The contents, including the delimiters.
- pub fn body(&self) -> Math {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The closing delimiter.
- pub fn close(&self) -> Expr {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-node! {
- /// A base with optional attachments in math: `a_1^2`.
- MathAttach
-}
-
-impl MathAttach {
- /// The base, to which things are attached.
- pub fn base(&self) -> Expr {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The bottom attachment.
- pub fn bottom(&self) -> Option<Expr> {
- self.0
- .children()
- .skip_while(|node| !matches!(node.kind(), SyntaxKind::Underscore))
- .find_map(SyntaxNode::cast)
- }
-
- /// The top attachment.
- pub fn top(&self) -> Option<Expr> {
- self.0
- .children()
- .skip_while(|node| !matches!(node.kind(), SyntaxKind::Hat))
- .find_map(SyntaxNode::cast)
- }
-}
-
-node! {
- /// A fraction in math: `x/2`
- MathFrac
-}
-
-impl MathFrac {
- /// The numerator.
- pub fn num(&self) -> Expr {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The denominator.
- pub fn denom(&self) -> Expr {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-node! {
- /// A root in math: `√x`, `∛x` or `∜x`.
- MathRoot
-}
-
-impl MathRoot {
- /// The index of the root.
- pub fn index(&self) -> Option<usize> {
- match self.0.children().next().map(|node| node.text().as_str()) {
- Some("∜") => Some(4),
- Some("∛") => Some(3),
- Some("√") => Option::None,
- _ => Option::None,
- }
- }
-
- /// The radicand.
- pub fn radicand(&self) -> Expr {
- self.0.cast_first_match().unwrap_or_default()
- }
-}
-
-node! {
- /// An identifier: `it`.
- Ident
-}
-
-impl Ident {
- /// Get the identifier.
- pub fn get(&self) -> &EcoString {
- self.0.text()
- }
-
- /// Take out the contained identifier.
- pub fn take(self) -> EcoString {
- self.0.into_text()
- }
-
- /// Get the identifier as a string slice.
- pub fn as_str(&self) -> &str {
- self.get()
- }
-}
-
-impl Deref for Ident {
- type Target = str;
-
- fn deref(&self) -> &Self::Target {
- self.as_str()
- }
-}
-
-node! {
- /// The `none` literal.
- None
-}
-
-node! {
- /// The `auto` literal.
- Auto
-}
-
-node! {
- /// A boolean: `true`, `false`.
- Bool
-}
-
-impl Bool {
- /// Get the boolean value.
- pub fn get(&self) -> bool {
- self.0.text() == "true"
- }
-}
-
-node! {
- /// An integer: `120`.
- Int
-}
-
-impl Int {
- /// Get the integer value.
- pub fn get(&self) -> i64 {
- let text = self.0.text();
- if let Some(rest) = text.strip_prefix("0x") {
- i64::from_str_radix(rest, 16)
- } else if let Some(rest) = text.strip_prefix("0o") {
- i64::from_str_radix(rest, 8)
- } else if let Some(rest) = text.strip_prefix("0b") {
- i64::from_str_radix(rest, 2)
- } else {
- text.parse()
- }
- .unwrap_or_default()
- }
-}
-
-node! {
- /// A floating-point number: `1.2`, `10e-4`.
- Float
-}
-
-impl Float {
- /// Get the floating-point value.
- pub fn get(&self) -> f64 {
- self.0.text().parse().unwrap_or_default()
- }
-}
-
-node! {
- /// A numeric value with a unit: `12pt`, `3cm`, `2em`, `90deg`, `50%`.
- Numeric
-}
-
-impl Numeric {
- /// Get the numeric value and unit.
- pub fn get(&self) -> (f64, Unit) {
- let text = self.0.text();
- let count = text
- .chars()
- .rev()
- .take_while(|c| matches!(c, 'a'..='z' | '%'))
- .count();
-
- let split = text.len() - count;
- let value = text[..split].parse().unwrap_or_default();
- let unit = match &text[split..] {
- "pt" => Unit::Length(AbsUnit::Pt),
- "mm" => Unit::Length(AbsUnit::Mm),
- "cm" => Unit::Length(AbsUnit::Cm),
- "in" => Unit::Length(AbsUnit::In),
- "deg" => Unit::Angle(AngleUnit::Deg),
- "rad" => Unit::Angle(AngleUnit::Rad),
- "em" => Unit::Em,
- "fr" => Unit::Fr,
- "%" => Unit::Percent,
- _ => Unit::Percent,
- };
-
- (value, unit)
- }
-}
-
-/// Unit of a numeric value.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Unit {
- /// An absolute length unit.
- Length(AbsUnit),
- /// An angular unit.
- Angle(AngleUnit),
- /// Font-relative: `1em` is the same as the font size.
- Em,
- /// Fractions: `fr`.
- Fr,
- /// Percentage: `%`.
- Percent,
-}
-
-node! {
- /// A quoted string: `"..."`.
- Str
-}
-
-impl Str {
- /// Get the string value with resolved escape sequences.
- pub fn get(&self) -> EcoString {
- let text = self.0.text();
- let unquoted = &text[1..text.len() - 1];
- if !unquoted.contains('\\') {
- return unquoted.into();
- }
-
- let mut out = EcoString::with_capacity(unquoted.len());
- let mut s = Scanner::new(unquoted);
-
- while let Some(c) = s.eat() {
- if c != '\\' {
- out.push(c);
- continue;
- }
-
- let start = s.locate(-1);
- match s.eat() {
- Some('\\') => out.push('\\'),
- Some('"') => out.push('"'),
- Some('n') => out.push('\n'),
- Some('r') => out.push('\r'),
- Some('t') => out.push('\t'),
- Some('u') if s.eat_if('{') => {
- let sequence = s.eat_while(char::is_ascii_hexdigit);
- s.eat_if('}');
-
- match u32::from_str_radix(sequence, 16)
- .ok()
- .and_then(std::char::from_u32)
- {
- Some(c) => out.push(c),
- Option::None => out.push_str(s.from(start)),
- }
- }
- _ => out.push_str(s.from(start)),
- }
- }
-
- out
- }
-}
-
-node! {
- /// A code block: `{ let x = 1; x + 2 }`.
- CodeBlock
-}
-
-impl CodeBlock {
- /// The contained code.
- pub fn body(&self) -> Code {
- self.0.cast_first_match().unwrap_or_default()
- }
-}
-
-node! {
- /// Code.
- Code
-}
-
-impl Code {
- /// The list of expressions contained in the code.
- pub fn exprs(&self) -> impl DoubleEndedIterator<Item = Expr> + '_ {
- self.0.children().filter_map(SyntaxNode::cast)
- }
-}
-
-node! {
- /// A content block: `[*Hi* there!]`.
- ContentBlock
-}
-
-impl ContentBlock {
- /// The contained markup.
- pub fn body(&self) -> Markup {
- self.0.cast_first_match().unwrap_or_default()
- }
-}
-
-node! {
- /// A grouped expression: `(1 + 2)`.
- Parenthesized
-}
-
-impl Parenthesized {
- /// The wrapped expression.
- pub fn expr(&self) -> Expr {
- self.0.cast_first_match().unwrap_or_default()
- }
-}
-
-node! {
- /// An array: `(1, "hi", 12cm)`.
- Array
-}
-
-impl Array {
- /// The array's items.
- pub fn items(&self) -> impl DoubleEndedIterator<Item = ArrayItem> + '_ {
- self.0.children().filter_map(SyntaxNode::cast)
- }
-}
-
-/// An item in an array.
-#[derive(Debug, Clone, Hash)]
-pub enum ArrayItem {
- /// A bare expression: `12`.
- Pos(Expr),
- /// A spread expression: `..things`.
- Spread(Expr),
-}
-
-impl AstNode for ArrayItem {
- fn from_untyped(node: &SyntaxNode) -> Option<Self> {
- match node.kind() {
- SyntaxKind::Spread => node.cast_first_match().map(Self::Spread),
- _ => node.cast().map(Self::Pos),
- }
- }
-
- fn as_untyped(&self) -> &SyntaxNode {
- match self {
- Self::Pos(v) => v.as_untyped(),
- Self::Spread(v) => v.as_untyped(),
- }
- }
-}
-
-node! {
- /// A dictionary: `(thickness: 3pt, pattern: dashed)`.
- Dict
-}
-
-impl Dict {
- /// The dictionary's items.
- pub fn items(&self) -> impl DoubleEndedIterator<Item = DictItem> + '_ {
- self.0.children().filter_map(SyntaxNode::cast)
- }
-}
-
-/// An item in an dictionary expression.
-#[derive(Debug, Clone, Hash)]
-pub enum DictItem {
- /// A named pair: `thickness: 3pt`.
- Named(Named),
- /// A keyed pair: `"spacy key": true`.
- Keyed(Keyed),
- /// A spread expression: `..things`.
- Spread(Expr),
-}
-
-impl AstNode for DictItem {
- fn from_untyped(node: &SyntaxNode) -> Option<Self> {
- match node.kind() {
- SyntaxKind::Named => node.cast().map(Self::Named),
- SyntaxKind::Keyed => node.cast().map(Self::Keyed),
- SyntaxKind::Spread => node.cast_first_match().map(Self::Spread),
- _ => Option::None,
- }
- }
-
- fn as_untyped(&self) -> &SyntaxNode {
- match self {
- Self::Named(v) => v.as_untyped(),
- Self::Keyed(v) => v.as_untyped(),
- Self::Spread(v) => v.as_untyped(),
- }
- }
-}
-
-node! {
- /// A named pair: `thickness: 3pt`.
- Named
-}
-
-impl Named {
- /// The name: `thickness`.
- pub fn name(&self) -> Ident {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The right-hand side of the pair: `3pt`.
- pub fn expr(&self) -> Expr {
- self.0.cast_last_match().unwrap_or_default()
- }
-
- /// The right-hand side of the pair as an identifier.
- pub fn expr_ident(&self) -> Option<Ident> {
- self.0.cast_last_match()
- }
-}
-
-node! {
- /// A keyed pair: `"spacy key": true`.
- Keyed
-}
-
-impl Keyed {
- /// The key: `"spacy key"`.
- pub fn key(&self) -> Str {
- self.0
- .children()
- .find_map(|node| node.cast::<Str>())
- .unwrap_or_default()
- }
-
- /// The right-hand side of the pair: `true`.
- pub fn expr(&self) -> Expr {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-node! {
- /// A unary operation: `-x`.
- Unary
-}
-
-impl Unary {
- /// The operator: `-`.
- pub fn op(&self) -> UnOp {
- self.0
- .children()
- .find_map(|node| UnOp::from_kind(node.kind()))
- .unwrap_or(UnOp::Pos)
- }
-
- /// The expression to operate on: `x`.
- pub fn expr(&self) -> Expr {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-/// A unary operator.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum UnOp {
- /// The plus operator: `+`.
- Pos,
- /// The negation operator: `-`.
- Neg,
- /// The boolean `not`.
- Not,
-}
-
-impl UnOp {
- /// Try to convert the token into a unary operation.
- pub fn from_kind(token: SyntaxKind) -> Option<Self> {
- Some(match token {
- SyntaxKind::Plus => Self::Pos,
- SyntaxKind::Minus => Self::Neg,
- SyntaxKind::Not => Self::Not,
- _ => return Option::None,
- })
- }
-
- /// The precedence of this operator.
- pub fn precedence(self) -> usize {
- match self {
- Self::Pos | Self::Neg => 7,
- Self::Not => 4,
- }
- }
-
- /// The string representation of this operation.
- pub fn as_str(self) -> &'static str {
- match self {
- Self::Pos => "+",
- Self::Neg => "-",
- Self::Not => "not",
- }
- }
-}
-
-node! {
- /// A binary operation: `a + b`.
- Binary
-}
-
-impl Binary {
- /// The binary operator: `+`.
- pub fn op(&self) -> BinOp {
- let mut not = false;
- self.0
- .children()
- .find_map(|node| match node.kind() {
- SyntaxKind::Not => {
- not = true;
- Option::None
- }
- SyntaxKind::In if not => Some(BinOp::NotIn),
- _ => BinOp::from_kind(node.kind()),
- })
- .unwrap_or(BinOp::Add)
- }
-
- /// The left-hand side of the operation: `a`.
- pub fn lhs(&self) -> Expr {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The right-hand side of the operation: `b`.
- pub fn rhs(&self) -> Expr {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-/// A binary operator.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum BinOp {
- /// The addition operator: `+`.
- Add,
- /// The subtraction operator: `-`.
- Sub,
- /// The multiplication operator: `*`.
- Mul,
- /// The division operator: `/`.
- Div,
- /// The short-circuiting boolean `and`.
- And,
- /// The short-circuiting boolean `or`.
- Or,
- /// The equality operator: `==`.
- Eq,
- /// The inequality operator: `!=`.
- Neq,
- /// The less-than operator: `<`.
- Lt,
- /// The less-than or equal operator: `<=`.
- Leq,
- /// The greater-than operator: `>`.
- Gt,
- /// The greater-than or equal operator: `>=`.
- Geq,
- /// The assignment operator: `=`.
- Assign,
- /// The containment operator: `in`.
- In,
- /// The inversed containment operator: `not in`.
- NotIn,
- /// The add-assign operator: `+=`.
- AddAssign,
- /// The subtract-assign oeprator: `-=`.
- SubAssign,
- /// The multiply-assign operator: `*=`.
- MulAssign,
- /// The divide-assign operator: `/=`.
- DivAssign,
-}
-
-impl BinOp {
- /// Try to convert the token into a binary operation.
- pub fn from_kind(token: SyntaxKind) -> Option<Self> {
- Some(match token {
- SyntaxKind::Plus => Self::Add,
- SyntaxKind::Minus => Self::Sub,
- SyntaxKind::Star => Self::Mul,
- SyntaxKind::Slash => Self::Div,
- SyntaxKind::And => Self::And,
- SyntaxKind::Or => Self::Or,
- SyntaxKind::EqEq => Self::Eq,
- SyntaxKind::ExclEq => Self::Neq,
- SyntaxKind::Lt => Self::Lt,
- SyntaxKind::LtEq => Self::Leq,
- SyntaxKind::Gt => Self::Gt,
- SyntaxKind::GtEq => Self::Geq,
- SyntaxKind::Eq => Self::Assign,
- SyntaxKind::In => Self::In,
- SyntaxKind::PlusEq => Self::AddAssign,
- SyntaxKind::HyphEq => Self::SubAssign,
- SyntaxKind::StarEq => Self::MulAssign,
- SyntaxKind::SlashEq => Self::DivAssign,
- _ => return Option::None,
- })
- }
-
- /// The precedence of this operator.
- pub fn precedence(self) -> usize {
- match self {
- Self::Mul => 6,
- Self::Div => 6,
- Self::Add => 5,
- Self::Sub => 5,
- Self::Eq => 4,
- Self::Neq => 4,
- Self::Lt => 4,
- Self::Leq => 4,
- Self::Gt => 4,
- Self::Geq => 4,
- Self::In => 4,
- Self::NotIn => 4,
- Self::And => 3,
- Self::Or => 2,
- Self::Assign => 1,
- Self::AddAssign => 1,
- Self::SubAssign => 1,
- Self::MulAssign => 1,
- Self::DivAssign => 1,
- }
- }
-
- /// The associativity of this operator.
- pub fn assoc(self) -> Assoc {
- match self {
- Self::Add => Assoc::Left,
- Self::Sub => Assoc::Left,
- Self::Mul => Assoc::Left,
- Self::Div => Assoc::Left,
- Self::And => Assoc::Left,
- Self::Or => Assoc::Left,
- Self::Eq => Assoc::Left,
- Self::Neq => Assoc::Left,
- Self::Lt => Assoc::Left,
- Self::Leq => Assoc::Left,
- Self::Gt => Assoc::Left,
- Self::Geq => Assoc::Left,
- Self::In => Assoc::Left,
- Self::NotIn => Assoc::Left,
- Self::Assign => Assoc::Right,
- Self::AddAssign => Assoc::Right,
- Self::SubAssign => Assoc::Right,
- Self::MulAssign => Assoc::Right,
- Self::DivAssign => Assoc::Right,
- }
- }
-
- /// The string representation of this operation.
- pub fn as_str(self) -> &'static str {
- match self {
- Self::Add => "+",
- Self::Sub => "-",
- Self::Mul => "*",
- Self::Div => "/",
- Self::And => "and",
- Self::Or => "or",
- Self::Eq => "==",
- Self::Neq => "!=",
- Self::Lt => "<",
- Self::Leq => "<=",
- Self::Gt => ">",
- Self::Geq => ">=",
- Self::In => "in",
- Self::NotIn => "not in",
- Self::Assign => "=",
- Self::AddAssign => "+=",
- Self::SubAssign => "-=",
- Self::MulAssign => "*=",
- Self::DivAssign => "/=",
- }
- }
-}
-
-/// The associativity of a binary operator.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Assoc {
- /// Left-associative: `a + b + c` is equivalent to `(a + b) + c`.
- Left,
- /// Right-associative: `a = b = c` is equivalent to `a = (b = c)`.
- Right,
-}
-
-node! {
- /// A field access: `properties.age`.
- FieldAccess
-}
-
-impl FieldAccess {
- /// The expression to access the field on.
- pub fn target(&self) -> Expr {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The name of the field.
- pub fn field(&self) -> Ident {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-node! {
- /// An invocation of a function or method: `f(x, y)`.
- FuncCall
-}
-
-impl FuncCall {
- /// The function to call.
- pub fn callee(&self) -> Expr {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The arguments to the function.
- pub fn args(&self) -> Args {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-node! {
- /// A function call's argument list: `(12pt, y)`.
- Args
-}
-
-impl Args {
- /// The positional and named arguments.
- pub fn items(&self) -> impl DoubleEndedIterator<Item = Arg> + '_ {
- self.0.children().filter_map(SyntaxNode::cast)
- }
-}
-
-/// An argument to a function call.
-#[derive(Debug, Clone, Hash)]
-pub enum Arg {
- /// A positional argument: `12`.
- Pos(Expr),
- /// A named argument: `draw: false`.
- Named(Named),
- /// A spread argument: `..things`.
- Spread(Expr),
-}
-
-impl AstNode for Arg {
- fn from_untyped(node: &SyntaxNode) -> Option<Self> {
- match node.kind() {
- SyntaxKind::Named => node.cast().map(Self::Named),
- SyntaxKind::Spread => node.cast_first_match().map(Self::Spread),
- _ => node.cast().map(Self::Pos),
- }
- }
-
- fn as_untyped(&self) -> &SyntaxNode {
- match self {
- Self::Pos(v) => v.as_untyped(),
- Self::Named(v) => v.as_untyped(),
- Self::Spread(v) => v.as_untyped(),
- }
- }
-}
-
-node! {
- /// A closure: `(x, y) => z`.
- Closure
-}
-
-impl Closure {
- /// The name of the closure.
- ///
- /// This only exists if you use the function syntax sugar: `let f(x) = y`.
- pub fn name(&self) -> Option<Ident> {
- self.0.children().next()?.cast()
- }
-
- /// The parameter bindings.
- pub fn params(&self) -> Params {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The body of the closure.
- pub fn body(&self) -> Expr {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-node! {
- /// A closure's parameters: `(x, y)`.
- Params
-}
-
-impl Params {
- /// The parameter bindings.
- pub fn children(&self) -> impl DoubleEndedIterator<Item = Param> + '_ {
- self.0.children().filter_map(SyntaxNode::cast)
- }
-}
-
-node! {
- /// A spread: `..x` or `..x.at(0)`.
- Spread
-}
-
-impl Spread {
- /// Try to get an identifier.
- pub fn name(&self) -> Option<Ident> {
- self.0.cast_first_match()
- }
-
- /// Try to get an expression.
- pub fn expr(&self) -> Option<Expr> {
- self.0.cast_first_match()
- }
-}
-
-node! {
- /// An underscore: `_`
- Underscore
-}
-
-/// A parameter to a closure.
-#[derive(Debug, Clone, Hash)]
-pub enum Param {
- /// A positional parameter: `x`.
- Pos(Pattern),
- /// A named parameter with a default value: `draw: false`.
- Named(Named),
- /// An argument sink: `..args`.
- Sink(Spread),
-}
-
-impl AstNode for Param {
- fn from_untyped(node: &SyntaxNode) -> Option<Self> {
- match node.kind() {
- SyntaxKind::Named => node.cast().map(Self::Named),
- SyntaxKind::Spread => node.cast().map(Self::Sink),
- _ => node.cast().map(Self::Pos),
- }
- }
-
- fn as_untyped(&self) -> &SyntaxNode {
- match self {
- Self::Pos(v) => v.as_untyped(),
- Self::Named(v) => v.as_untyped(),
- Self::Sink(v) => v.as_untyped(),
- }
- }
-}
-
-node! {
- /// A destructuring pattern: `x` or `(x, _, ..y)`.
- Destructuring
-}
-
-impl Destructuring {
- /// The bindings of the destructuring.
- pub fn bindings(&self) -> impl Iterator<Item = DestructuringKind> + '_ {
- self.0.children().filter_map(SyntaxNode::cast)
- }
-
- // Returns a list of all identifiers in the pattern.
- pub fn idents(&self) -> impl Iterator<Item = Ident> + '_ {
- self.bindings().filter_map(|binding| match binding {
- DestructuringKind::Normal(Expr::Ident(ident)) => Some(ident),
- DestructuringKind::Sink(spread) => spread.name(),
- DestructuringKind::Named(named) => named.expr_ident(),
- _ => Option::None,
- })
- }
-}
-
-/// The kind of an element in a destructuring pattern.
-#[derive(Debug, Clone, Hash)]
-pub enum DestructuringKind {
- /// An expression: `x`.
- Normal(Expr),
- /// An argument sink: `..y`.
- Sink(Spread),
- /// Named arguments: `x: 1`.
- Named(Named),
- /// A placeholder: `_`.
- Placeholder(Underscore),
-}
-
-impl AstNode for DestructuringKind {
- fn from_untyped(node: &SyntaxNode) -> Option<Self> {
- match node.kind() {
- SyntaxKind::Named => node.cast().map(Self::Named),
- SyntaxKind::Spread => node.cast().map(Self::Sink),
- SyntaxKind::Underscore => node.cast().map(Self::Placeholder),
- _ => node.cast().map(Self::Normal),
- }
- }
-
- fn as_untyped(&self) -> &SyntaxNode {
- match self {
- Self::Normal(v) => v.as_untyped(),
- Self::Named(v) => v.as_untyped(),
- Self::Sink(v) => v.as_untyped(),
- Self::Placeholder(v) => v.as_untyped(),
- }
- }
-}
-
-/// The kind of a pattern.
-#[derive(Debug, Clone, Hash)]
-pub enum Pattern {
- /// A single expression: `x`.
- Normal(Expr),
- /// A placeholder: `_`.
- Placeholder(Underscore),
- /// A destructuring pattern: `(x, _, ..y)`.
- Destructuring(Destructuring),
-}
-
-impl AstNode for Pattern {
- fn from_untyped(node: &SyntaxNode) -> Option<Self> {
- match node.kind() {
- SyntaxKind::Destructuring => node.cast().map(Self::Destructuring),
- SyntaxKind::Underscore => node.cast().map(Self::Placeholder),
- _ => node.cast().map(Self::Normal),
- }
- }
-
- fn as_untyped(&self) -> &SyntaxNode {
- match self {
- Self::Normal(v) => v.as_untyped(),
- Self::Destructuring(v) => v.as_untyped(),
- Self::Placeholder(v) => v.as_untyped(),
- }
- }
-}
-
-impl Pattern {
- // Returns a list of all identifiers in the pattern.
- pub fn idents(&self) -> Vec<Ident> {
- match self {
- Pattern::Normal(Expr::Ident(ident)) => vec![ident.clone()],
- Pattern::Destructuring(destruct) => destruct.idents().collect(),
- _ => vec![],
- }
- }
-}
-
-impl Default for Pattern {
- fn default() -> Self {
- Self::Normal(Expr::default())
- }
-}
-
-node! {
- /// A let binding: `let x = 1`.
- LetBinding
-}
-
-#[derive(Debug)]
-pub enum LetBindingKind {
- /// A normal binding: `let x = 1`.
- Normal(Pattern),
- /// A closure binding: `let f(x) = 1`.
- Closure(Ident),
-}
-
-impl LetBindingKind {
- // Returns a list of all identifiers in the pattern.
- pub fn idents(&self) -> Vec<Ident> {
- match self {
- LetBindingKind::Normal(pattern) => pattern.idents(),
- LetBindingKind::Closure(ident) => {
- vec![ident.clone()]
- }
- }
- }
-}
-
-impl LetBinding {
- /// The kind of the let binding.
- pub fn kind(&self) -> LetBindingKind {
- match self.0.cast_first_match::<Pattern>() {
- Some(Pattern::Normal(Expr::Closure(closure))) => {
- LetBindingKind::Closure(closure.name().unwrap_or_default())
- }
- pattern => LetBindingKind::Normal(pattern.unwrap_or_default()),
- }
- }
-
- /// The expression the binding is initialized with.
- pub fn init(&self) -> Option<Expr> {
- match self.kind() {
- LetBindingKind::Normal(Pattern::Normal(_)) => {
- self.0.children().filter_map(SyntaxNode::cast).nth(1)
- }
- LetBindingKind::Normal(_) => self.0.cast_first_match(),
- LetBindingKind::Closure(_) => self.0.cast_first_match(),
- }
- }
-}
-
-node! {
- /// An assignment expression `(x, y) = (1, 2)`.
- DestructAssignment
-}
-
-impl DestructAssignment {
- /// The pattern of the assignment.
- pub fn pattern(&self) -> Pattern {
- self.0.cast_first_match::<Pattern>().unwrap_or_default()
- }
-
- /// The expression that is assigned.
- pub fn value(&self) -> Expr {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-node! {
- /// A set rule: `set text(...)`.
- SetRule
-}
-
-impl SetRule {
- /// The function to set style properties for.
- pub fn target(&self) -> Expr {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The style properties to set.
- pub fn args(&self) -> Args {
- self.0.cast_last_match().unwrap_or_default()
- }
-
- /// A condition under which the set rule applies.
- pub fn condition(&self) -> Option<Expr> {
- self.0
- .children()
- .skip_while(|child| child.kind() != SyntaxKind::If)
- .find_map(SyntaxNode::cast)
- }
-}
-
-node! {
- /// A show rule: `show heading: it => emph(it.body)`.
- ShowRule
-}
-
-impl ShowRule {
- /// Defines which nodes the show rule applies to.
- pub fn selector(&self) -> Option<Expr> {
- self.0
- .children()
- .rev()
- .skip_while(|child| child.kind() != SyntaxKind::Colon)
- .find_map(SyntaxNode::cast)
- }
-
- /// The transformation recipe.
- pub fn transform(&self) -> Expr {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-node! {
- /// An if-else conditional: `if x { y } else { z }`.
- Conditional
-}
-
-impl Conditional {
- /// The condition which selects the body to evaluate.
- pub fn condition(&self) -> Expr {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The expression to evaluate if the condition is true.
- pub fn if_body(&self) -> Expr {
- self.0
- .children()
- .filter_map(SyntaxNode::cast)
- .nth(1)
- .unwrap_or_default()
- }
-
- /// The expression to evaluate if the condition is false.
- pub fn else_body(&self) -> Option<Expr> {
- self.0.children().filter_map(SyntaxNode::cast).nth(2)
- }
-}
-
-node! {
- /// A while loop: `while x { y }`.
- WhileLoop
-}
-
-impl WhileLoop {
- /// The condition which selects whether to evaluate the body.
- pub fn condition(&self) -> Expr {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The expression to evaluate while the condition is true.
- pub fn body(&self) -> Expr {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-node! {
- /// A for loop: `for x in y { z }`.
- ForLoop
-}
-
-impl ForLoop {
- /// The pattern to assign to.
- pub fn pattern(&self) -> Pattern {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The expression to iterate over.
- pub fn iter(&self) -> Expr {
- self.0
- .children()
- .skip_while(|&c| c.kind() != SyntaxKind::In)
- .find_map(SyntaxNode::cast)
- .unwrap_or_default()
- }
-
- /// The expression to evaluate for each iteration.
- pub fn body(&self) -> Expr {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-node! {
- /// A module import: `import "utils.typ": a, b, c`.
- ModuleImport
-}
-
-impl ModuleImport {
- /// The module or path from which the items should be imported.
- pub fn source(&self) -> Expr {
- self.0.cast_first_match().unwrap_or_default()
- }
-
- /// The items to be imported.
- pub fn imports(&self) -> Option<Imports> {
- self.0.children().find_map(|node| match node.kind() {
- SyntaxKind::Star => Some(Imports::Wildcard),
- SyntaxKind::ImportItems => {
- let items = node.children().filter_map(SyntaxNode::cast).collect();
- Some(Imports::Items(items))
- }
- _ => Option::None,
- })
- }
-}
-
-/// The items that ought to be imported from a file.
-#[derive(Debug, Clone, Hash)]
-pub enum Imports {
- /// All items in the scope of the file should be imported.
- Wildcard,
- /// The specified items from the file should be imported.
- Items(Vec<Ident>),
-}
-
-node! {
- /// A module include: `include "chapter1.typ"`.
- ModuleInclude
-}
-
-impl ModuleInclude {
- /// The module or path from which the content should be included.
- pub fn source(&self) -> Expr {
- self.0.cast_last_match().unwrap_or_default()
- }
-}
-
-node! {
- /// A break from a loop: `break`.
- LoopBreak
-}
-
-node! {
- /// A continue in a loop: `continue`.
- LoopContinue
-}
-
-node! {
- /// A return from a function: `return`, `return x + 1`.
- FuncReturn
-}
-
-impl FuncReturn {
- /// The expression to return.
- pub fn body(&self) -> Option<Expr> {
- self.0.cast_last_match()
- }
-}