use std::hash::{Hash, Hasher}; use std::sync::Arc; use crate::geom::{AbsUnit, AngleUnit}; use crate::util::EcoString; /// All syntactical building blocks that can be part of a Typst document. /// /// Can be created by the tokenizer or by the parser. #[derive(Debug, Clone, PartialEq)] pub enum SyntaxKind { /// A line comment: `// ...`. LineComment, /// A block comment: `/* ... */`. BlockComment, /// One or more whitespace characters. Single spaces are collapsed into text /// nodes if they would otherwise be surrounded by text nodes. /// /// Also stores how many newlines are contained. Space { newlines: usize }, /// A left curly brace, starting a code block: `{`. LeftBrace, /// A right curly brace, terminating a code block: `}`. RightBrace, /// A left square bracket, starting a content block: `[`. LeftBracket, /// A right square bracket, terminating a content block: `]`. RightBracket, /// A left round parenthesis, starting a grouped expression, collection, /// argument or parameter list: `(`. LeftParen, /// A right round parenthesis, terminating a grouped expression, collection, /// argument or parameter list: `)`. RightParen, /// A comma separator in a sequence: `,`. Comma, /// A semicolon terminating an expression: `;`. Semicolon, /// A colon between name/key and value in a dictionary, argument or /// parameter list, or between the term and body of a description list /// term: `:`. Colon, /// The strong text toggle, multiplication operator, and wildcard import /// symbol: `*`. Star, /// Toggles emphasized text and indicates a subscript in a formula: `_`. Underscore, /// Starts and ends a math formula: `$`. Dollar, /// The unary plus, binary addition operator, and start of enum items: `+`. Plus, /// The unary negation, binary subtraction operator, and start of list /// items: `-`. Minus, /// The division operator, start of description list items, and fraction /// operator in a formula: `/`. Slash, /// The superscript operator in a formula: `^`. Hat, /// The alignment operator in a formula: `&`. Amp, /// The field access and method call operator: `.`. Dot, /// The assignment operator: `=`. Eq, /// The equality operator: `==`. EqEq, /// The inequality operator: `!=`. ExclEq, /// The less-than operator: `<`. Lt, /// The less-than or equal operator: `<=`. LtEq, /// The greater-than operator: `>`. Gt, /// The greater-than or equal operator: `>=`. GtEq, /// The add-assign operator: `+=`. PlusEq, /// The subtract-assign operator: `-=`. HyphEq, /// The multiply-assign operator: `*=`. StarEq, /// The divide-assign operator: `/=`. SlashEq, /// The spread operator: `..`. Dots, /// An arrow between a closure's parameters and body: `=>`. Arrow, /// The `not` operator. Not, /// The `and` operator. And, /// The `or` operator. Or, /// The `none` literal. None, /// The `auto` literal. Auto, /// The `let` keyword. Let, /// The `set` keyword. Set, /// The `show` keyword. Show, /// The `if` keyword. If, /// The `else` keyword. Else, /// The `for` keyword. For, /// The `in` keyword. In, /// The `while` keyword. While, /// The `break` keyword. Break, /// The `continue` keyword. Continue, /// The `return` keyword. Return, /// The `import` keyword. Import, /// The `include` keyword. Include, /// The `from` keyword. From, /// Markup of which all lines must have a minimal indentation. /// /// Notably, the number does not determine in which column the markup /// started, but to the right of which column all markup elements must be, /// so it is zero except inside indent-aware constructs like lists. Markup { min_indent: usize }, /// Plain text without markup. Text(EcoString), /// A forced line break: `\`. Linebreak, /// An escape sequence: `\#`, `\u{1F5FA}`. Escape(char), /// A shorthand for a unicode codepoint. For example, `~` for non-breaking /// space or `-?` for a soft hyphen. Shorthand(char), /// A smart quote: `'` or `"`. SmartQuote { double: bool }, /// Strong content: `*Strong*`. Strong, /// Emphasized content: `_Emphasized_`. Emph, /// Raw text with optional syntax highlighting: `` `...` ``. Raw(Arc), /// A hyperlink: `https://typst.org`. Link(EcoString), /// A label: ``. Label(EcoString), /// A reference: `@target`. Ref(EcoString), /// A section heading: `= Introduction`. Heading, /// An item in an unordered list: `- ...`. ListItem, /// An item in an enumeration (ordered list): `+ ...` or `1. ...`. EnumItem, /// An explicit enumeration numbering: `23.`. EnumNumbering(usize), /// An item in a description list: `/ Term: Details`. DescItem, /// A mathematical formula: `$x$`, `$ x^2 $`. Math, /// An atom in a formula: `x`, `+`, `12`. Atom(EcoString), /// A base with optional sub- and superscripts in a formula: `a_1^2`. Script, /// A fraction in a formula: `x/2`. Frac, /// An alignment indicator in a formula: `&`, `&&`. Align, /// An identifier: `it`. Ident(EcoString), /// A boolean: `true`, `false`. Bool(bool), /// An integer: `120`. Int(i64), /// A floating-point number: `1.2`, `10e-4`. Float(f64), /// A numeric value with a unit: `12pt`, `3cm`, `2em`, `90deg`, `50%`. Numeric(f64, Unit), /// A quoted string: `"..."`. Str(EcoString), /// A code block: `{ let x = 1; x + 2 }`. CodeBlock, /// A content block: `[*Hi* there!]`. ContentBlock, /// A grouped expression: `(1 + 2)`. Parenthesized, /// An array: `(1, "hi", 12cm)`. Array, /// A dictionary: `(thickness: 3pt, pattern: dashed)`. Dict, /// A named pair: `thickness: 3pt`. Named, /// A keyed pair: `"spacy key": true`. Keyed, /// A unary operation: `-x`. Unary, /// A binary operation: `a + b`. Binary, /// A field access: `properties.age`. FieldAccess, /// An invocation of a function: `f(x, y)`. FuncCall, /// An invocation of a method: `array.push(v)`. MethodCall, /// A function call's argument list: `(12pt, y)`. Args, /// Spreaded arguments or an argument sink: `..x`. Spread, /// A closure: `(x, y) => z`. Closure, /// A closure's parameters: `(x, y)`. Params, /// A let binding: `let x = 1`. LetBinding, /// A set rule: `set text(...)`. SetRule, /// A show rule: `show heading: it => [*{it.body}*]`. ShowRule, /// An if-else conditional: `if x { y } else { z }`. Conditional, /// A while loop: `while x { y }`. WhileLoop, /// A for loop: `for x in y { z }`. ForLoop, /// A for loop's destructuring pattern: `x` or `x, y`. ForPattern, /// A module import: `import a, b, c from "utils.typ"`. ModuleImport, /// Items to import from a module: `a, b, c`. ImportItems, /// A module include: `include "chapter1.typ"`. ModuleInclude, /// A break from a loop: `break`. LoopBreak, /// A continue in a loop: `continue`. LoopContinue, /// A return from a function: `return`, `return x + 1`. FuncReturn, /// An invalid sequence of characters. Error(ErrorPos, EcoString), } /// Fields of the raw syntax kind. #[derive(Debug, Clone, PartialEq, Hash)] pub struct RawFields { /// An optional identifier specifying the language to syntax-highlight in. pub lang: Option, /// The raw text, determined as the raw string between the backticks trimmed /// according to the above rules. pub text: EcoString, /// Whether the element is block-level, that is, it has 3+ backticks /// and contains at least one newline. pub block: bool, } /// 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, } /// Where in a node an error should be annotated, #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum ErrorPos { /// Over the full width of the node. Full, /// At the start of the node. Start, /// At the end of the node. End, } impl SyntaxKind { /// Whether this is trivia. pub fn is_trivia(&self) -> bool { self.is_space() || matches!(self, Self::LineComment | Self::BlockComment) } /// Whether this is a space. pub fn is_space(&self) -> bool { matches!(self, Self::Space { .. }) } /// Whether this is a left or right parenthesis. pub fn is_paren(&self) -> bool { matches!(self, Self::LeftParen | Self::RightParen) } /// Whether this is an error. pub fn is_error(&self) -> bool { matches!(self, SyntaxKind::Error(_, _)) } /// Does this node need termination through a semicolon or linebreak? pub fn is_stmt(&self) -> bool { matches!( self, SyntaxKind::LetBinding | SyntaxKind::SetRule | SyntaxKind::ShowRule | SyntaxKind::ModuleImport | SyntaxKind::ModuleInclude ) } /// A human-readable name for the kind. pub fn name(&self) -> &'static str { match self { Self::LineComment => "line comment", Self::BlockComment => "block comment", Self::Space { .. } => "space", Self::LeftBrace => "opening brace", Self::RightBrace => "closing brace", Self::LeftBracket => "opening bracket", Self::RightBracket => "closing bracket", Self::LeftParen => "opening paren", Self::RightParen => "closing paren", Self::Comma => "comma", Self::Semicolon => "semicolon", Self::Colon => "colon", Self::Star => "star", Self::Underscore => "underscore", Self::Dollar => "dollar sign", Self::SmartQuote { double: false } => "single quote", Self::SmartQuote { double: true } => "double quote", Self::Plus => "plus", Self::Minus => "minus", Self::Slash => "slash", Self::Hat => "hat", Self::Amp => "ampersand", Self::Dot => "dot", Self::Eq => "assignment operator", Self::EqEq => "equality operator", Self::ExclEq => "inequality operator", Self::Lt => "less-than operator", Self::LtEq => "less-than or equal operator", Self::Gt => "greater-than operator", Self::GtEq => "greater-than or equal operator", Self::PlusEq => "add-assign operator", Self::HyphEq => "subtract-assign operator", Self::StarEq => "multiply-assign operator", Self::SlashEq => "divide-assign operator", Self::Dots => "dots", Self::Arrow => "arrow", Self::Not => "operator `not`", Self::And => "operator `and`", Self::Or => "operator `or`", Self::None => "`none`", Self::Auto => "`auto`", Self::Let => "keyword `let`", Self::Set => "keyword `set`", Self::Show => "keyword `show`", Self::If => "keyword `if`", Self::Else => "keyword `else`", Self::For => "keyword `for`", Self::In => "keyword `in`", Self::While => "keyword `while`", Self::Break => "keyword `break`", Self::Continue => "keyword `continue`", Self::Return => "keyword `return`", Self::Import => "keyword `import`", Self::Include => "keyword `include`", Self::From => "keyword `from`", Self::Markup { .. } => "markup", Self::Text(_) => "text", Self::Linebreak => "linebreak", Self::Escape(_) => "escape sequence", Self::Shorthand(_) => "shorthand", Self::Strong => "strong content", Self::Emph => "emphasized content", Self::Raw(_) => "raw block", Self::Link(_) => "link", Self::Label(_) => "label", Self::Ref(_) => "reference", Self::Heading => "heading", Self::ListItem => "list item", Self::EnumItem => "enumeration item", Self::EnumNumbering(_) => "enumeration item numbering", Self::DescItem => "description list item", Self::Math => "math formula", Self::Atom(_) => "math atom", Self::Script => "script", Self::Frac => "fraction", Self::Align => "alignment indicator", Self::Ident(_) => "identifier", Self::Bool(_) => "boolean", Self::Int(_) => "integer", Self::Float(_) => "float", Self::Numeric(_, _) => "numeric value", Self::Str(_) => "string", Self::CodeBlock => "code block", Self::ContentBlock => "content block", Self::Parenthesized => "group", Self::Array => "array", Self::Dict => "dictionary", Self::Named => "named pair", Self::Keyed => "keyed pair", Self::Unary => "unary expression", Self::Binary => "binary expression", Self::FieldAccess => "field access", Self::FuncCall => "function call", Self::MethodCall => "method call", Self::Args => "call arguments", Self::Spread => "spread", Self::Closure => "closure", Self::Params => "closure parameters", Self::LetBinding => "`let` expression", Self::SetRule => "`set` expression", Self::ShowRule => "`show` expression", Self::Conditional => "`if` expression", Self::WhileLoop => "while-loop expression", Self::ForLoop => "for-loop expression", Self::ForPattern => "for-loop destructuring pattern", Self::ModuleImport => "`import` expression", Self::ImportItems => "import items", Self::ModuleInclude => "`include` expression", Self::LoopBreak => "`break` expression", Self::LoopContinue => "`continue` expression", Self::FuncReturn => "`return` expression", Self::Error(_, _) => "syntax error", } } } impl Hash for SyntaxKind { fn hash(&self, state: &mut H) { std::mem::discriminant(self).hash(state); match self { Self::LineComment => {} Self::BlockComment => {} Self::Space { newlines } => newlines.hash(state), Self::LeftBrace => {} Self::RightBrace => {} Self::LeftBracket => {} Self::RightBracket => {} Self::LeftParen => {} Self::RightParen => {} Self::Comma => {} Self::Semicolon => {} Self::Colon => {} Self::Star => {} Self::Underscore => {} Self::Dollar => {} Self::Plus => {} Self::Minus => {} Self::Slash => {} Self::Hat => {} Self::Amp => {} Self::Dot => {} Self::Eq => {} Self::EqEq => {} Self::ExclEq => {} Self::Lt => {} Self::LtEq => {} Self::Gt => {} Self::GtEq => {} Self::PlusEq => {} Self::HyphEq => {} Self::StarEq => {} Self::SlashEq => {} Self::Dots => {} Self::Arrow => {} Self::Not => {} Self::And => {} Self::Or => {} Self::None => {} Self::Auto => {} Self::Let => {} Self::Set => {} Self::Show => {} Self::If => {} Self::Else => {} Self::For => {} Self::In => {} Self::While => {} Self::Break => {} Self::Continue => {} Self::Return => {} Self::Import => {} Self::Include => {} Self::From => {} Self::Markup { min_indent } => min_indent.hash(state), Self::Text(s) => s.hash(state), Self::Linebreak => {} Self::Escape(c) => c.hash(state), Self::Shorthand(c) => c.hash(state), Self::SmartQuote { double } => double.hash(state), Self::Strong => {} Self::Emph => {} Self::Raw(raw) => raw.hash(state), Self::Link(link) => link.hash(state), Self::Label(c) => c.hash(state), Self::Ref(c) => c.hash(state), Self::Heading => {} Self::ListItem => {} Self::EnumItem => {} Self::EnumNumbering(num) => num.hash(state), Self::DescItem => {} Self::Math => {} Self::Atom(c) => c.hash(state), Self::Script => {} Self::Frac => {} Self::Align => {} Self::Ident(v) => v.hash(state), Self::Bool(v) => v.hash(state), Self::Int(v) => v.hash(state), Self::Float(v) => v.to_bits().hash(state), Self::Numeric(v, u) => (v.to_bits(), u).hash(state), Self::Str(v) => v.hash(state), Self::CodeBlock => {} Self::ContentBlock => {} Self::Parenthesized => {} Self::Array => {} Self::Dict => {} Self::Named => {} Self::Keyed => {} Self::Unary => {} Self::Binary => {} Self::FieldAccess => {} Self::FuncCall => {} Self::MethodCall => {} Self::Args => {} Self::Spread => {} Self::Closure => {} Self::Params => {} Self::LetBinding => {} Self::SetRule => {} Self::ShowRule => {} Self::Conditional => {} Self::WhileLoop => {} Self::ForLoop => {} Self::ForPattern => {} Self::ModuleImport => {} Self::ImportItems => {} Self::ModuleInclude => {} Self::LoopBreak => {} Self::LoopContinue => {} Self::FuncReturn => {} Self::Error(pos, msg) => (pos, msg).hash(state), } } }