use super::{Ident, NodeKind, RedNode, RedRef, Span, TypedNode}; use crate::geom::{AngularUnit, LengthUnit}; use crate::node; use crate::util::EcoString; node! { /// The syntactical root capable of representing a full parsed document. Markup } impl Markup { pub fn nodes<'a>(&'a self) -> impl Iterator + 'a { self.0.children().filter_map(RedRef::cast) } } /// A single piece of markup. #[derive(Debug, Clone, PartialEq)] pub enum MarkupNode { /// Whitespace containing less than two newlines. Space, /// A forced line break: `\`. Linebreak, /// A paragraph break: Two or more newlines. Parbreak, /// Strong text was enabled / disabled: `*`. Strong, /// Emphasized text was enabled / disabled: `_`. Emph, /// Plain text. Text(EcoString), /// A raw block with optional syntax highlighting: `` `...` ``. Raw(RawNode), /// A section heading: `= Introduction`. Heading(HeadingNode), /// An item in an unordered list: `- ...`. List(ListNode), /// An item in an enumeration (ordered list): `1. ...`. Enum(EnumNode), /// An expression. Expr(Expr), } impl TypedNode for MarkupNode { fn cast_from(node: RedRef) -> Option { match node.kind() { NodeKind::Space(_) => Some(MarkupNode::Space), NodeKind::Linebreak => Some(MarkupNode::Linebreak), NodeKind::Parbreak => Some(MarkupNode::Parbreak), NodeKind::Strong => Some(MarkupNode::Strong), NodeKind::Emph => Some(MarkupNode::Emph), NodeKind::Text(s) => Some(MarkupNode::Text(s.clone())), NodeKind::UnicodeEscape(u) => Some(MarkupNode::Text(u.character.into())), NodeKind::EnDash => Some(MarkupNode::Text(EcoString::from("\u{2013}"))), NodeKind::EmDash => Some(MarkupNode::Text(EcoString::from("\u{2014}"))), NodeKind::NonBreakingSpace => { Some(MarkupNode::Text(EcoString::from("\u{00A0}"))) } NodeKind::Raw(_) => node.cast().map(MarkupNode::Raw), NodeKind::Heading => node.cast().map(MarkupNode::Heading), NodeKind::List => node.cast().map(MarkupNode::List), NodeKind::Enum => node.cast().map(MarkupNode::Enum), NodeKind::Error(_, _) => None, _ => node.cast().map(MarkupNode::Expr), } } } /// A raw block with optional syntax highlighting: `` `...` ``. #[derive(Debug, Clone, PartialEq)] pub struct RawNode { /// 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, } impl TypedNode for RawNode { fn cast_from(node: RedRef) -> Option { match node.kind() { NodeKind::Raw(raw) => { let span = node.span(); let start = span.start + raw.backticks as usize; Some(Self { block: raw.block, lang: raw.lang.as_ref().and_then(|x| { let span = Span::new(span.source, start, start + x.len()); Ident::new(x, span) }), text: raw.text.clone(), }) } _ => None, } } } node! { /// A section heading: `= Introduction`. Heading => HeadingNode } impl HeadingNode { /// The contents of the heading. pub fn body(&self) -> Markup { self.0 .cast_first_child() .expect("heading node is missing markup body") } /// The section depth (numer of equals signs). pub fn level(&self) -> u8 { self.0 .children() .find_map(|node| match node.kind() { NodeKind::HeadingLevel(heading) => Some(*heading), _ => None, }) .expect("heading node is missing heading level") } } node! { /// An item in an unordered list: `- ...`. List => ListNode } impl ListNode { /// The contents of the list item. pub fn body(&self) -> Markup { self.0.cast_first_child().expect("list node is missing body") } } node! { /// An item in an enumeration (ordered list): `1. ...`. Enum => EnumNode } impl EnumNode { /// The contents of the list item. pub fn body(&self) -> Markup { self.0.cast_first_child().expect("enumeration node is missing body") } /// The number, if any. pub fn number(&self) -> Option { self.0 .children() .find_map(|node| match node.kind() { NodeKind::EnumNumbering(num) => Some(num.clone()), _ => None, }) .expect("enumeration node is missing number") } } /// An expression. #[derive(Debug, Clone, PartialEq)] pub enum Expr { /// An identifier: `left`. Ident(Ident), /// A literal: `1`, `true`, ... Lit(Lit), /// An array expression: `(1, "hi", 12cm)`. Array(ArrayExpr), /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. Dict(DictExpr), /// A template expression: `[*Hi* there!]`. Template(TemplateExpr), /// A grouped expression: `(1 + 2)`. Group(GroupExpr), /// A block expression: `{ let x = 1; x + 2 }`. Block(BlockExpr), /// A unary operation: `-x`. Unary(UnaryExpr), /// A binary operation: `a + b`. Binary(BinaryExpr), /// An invocation of a function: `f(x, y)`. Call(CallExpr), /// A closure expression: `(x, y) => z`. Closure(ClosureExpr), /// A with expression: `f with (x, y: 1)`. With(WithExpr), /// A let expression: `let x = 1`. Let(LetExpr), /// An if-else expression: `if x { y } else { z }`. If(IfExpr), /// A while loop expression: `while x { y }`. While(WhileExpr), /// A for loop expression: `for x in y { z }`. For(ForExpr), /// An import expression: `import a, b, c from "utils.typ"`. Import(ImportExpr), /// An include expression: `include "chapter1.typ"`. Include(IncludeExpr), } impl Expr { /// Whether the expression can be shortened in markup with a hashtag. pub fn has_short_form(&self) -> bool { matches!(self, Self::Ident(_) | Self::Call(_) | Self::Let(_) | Self::If(_) | Self::While(_) | Self::For(_) | Self::Import(_) | Self::Include(_) ) } /// Return the expression's span. pub fn span(&self) -> Span { match self { Self::Ident(ident) => ident.span, Self::Lit(lit) => lit.span(), Self::Array(array) => array.span(), Self::Dict(dict) => dict.span(), Self::Template(template) => template.span(), Self::Group(group) => group.span(), Self::Block(block) => block.span(), Self::Unary(unary) => unary.span(), Self::Binary(binary) => binary.span(), Self::Call(call) => call.span(), Self::Closure(closure) => closure.span(), Self::With(with) => with.span(), Self::Let(let_) => let_.span(), Self::If(if_) => if_.span(), Self::While(while_) => while_.span(), Self::For(for_) => for_.span(), Self::Import(import) => import.span(), Self::Include(include) => include.span(), } } } impl TypedNode for Expr { fn cast_from(node: RedRef) -> Option { match node.kind() { NodeKind::Ident(_) => node.cast().map(Self::Ident), NodeKind::Array => node.cast().map(Self::Array), NodeKind::Dict => node.cast().map(Self::Dict), NodeKind::Template => node.cast().map(Self::Template), NodeKind::Group => node.cast().map(Self::Group), NodeKind::Block => node.cast().map(Self::Block), NodeKind::Unary => node.cast().map(Self::Unary), NodeKind::Binary => node.cast().map(Self::Binary), NodeKind::Call => node.cast().map(Self::Call), NodeKind::Closure => node.cast().map(Self::Closure), NodeKind::WithExpr => node.cast().map(Self::With), NodeKind::LetExpr => node.cast().map(Self::Let), NodeKind::IfExpr => node.cast().map(Self::If), NodeKind::WhileExpr => node.cast().map(Self::While), NodeKind::ForExpr => node.cast().map(Self::For), NodeKind::ImportExpr => node.cast().map(Self::Import), NodeKind::IncludeExpr => node.cast().map(Self::Include), _ => node.cast().map(Self::Lit), } } } /// A literal: `1`, `true`, ... #[derive(Debug, Clone, PartialEq)] pub enum Lit { /// The none literal: `none`. None(Span), /// The auto literal: `auto`. Auto(Span), /// A boolean literal: `true`, `false`. Bool(Span, bool), /// An integer literal: `120`. Int(Span, i64), /// A floating-point literal: `1.2`, `10e-4`. Float(Span, f64), /// A length literal: `12pt`, `3cm`. Length(Span, f64, LengthUnit), /// An angle literal: `1.5rad`, `90deg`. Angle(Span, f64, AngularUnit), /// A percent literal: `50%`. /// /// _Note_: `50%` is stored as `50.0` here, but as `0.5` in the /// corresponding [value](crate::geom::Relative). Percent(Span, f64), /// A fraction unit literal: `1fr`. Fractional(Span, f64), /// A string literal: `"hello!"`. Str(Span, EcoString), } impl TypedNode for Lit { fn cast_from(node: RedRef) -> Option { match node.kind() { NodeKind::None => Some(Self::None(node.span())), NodeKind::Auto => Some(Self::Auto(node.span())), NodeKind::Bool(b) => Some(Self::Bool(node.span(), *b)), NodeKind::Int(i) => Some(Self::Int(node.span(), *i)), NodeKind::Float(f) => Some(Self::Float(node.span(), *f)), NodeKind::Length(f, unit) => Some(Self::Length(node.span(), *f, *unit)), NodeKind::Angle(f, unit) => Some(Self::Angle(node.span(), *f, *unit)), NodeKind::Percentage(f) => Some(Self::Percent(node.span(), *f)), NodeKind::Fraction(f) => Some(Self::Fractional(node.span(), *f)), NodeKind::Str(s) => Some(Self::Str(node.span(), s.string.clone())), _ => None, } } } impl Lit { pub fn span(&self) -> Span { match self { Self::None(span) => *span, Self::Auto(span) => *span, Self::Bool(span, _) => *span, Self::Int(span, _) => *span, Self::Float(span, _) => *span, Self::Length(span, _, _) => *span, Self::Angle(span, _, _) => *span, Self::Percent(span, _) => *span, Self::Fractional(span, _) => *span, Self::Str(span, _) => *span, } } } node! { /// An array expression: `(1, "hi", 12cm)`. Array => ArrayExpr } impl ArrayExpr { /// The array items. pub fn items<'a>(&'a self) -> impl Iterator + 'a { self.0.children().filter_map(RedRef::cast) } } node! { /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. Dict => DictExpr } impl DictExpr { /// The named dictionary items. pub fn items<'a>(&'a self) -> impl Iterator + 'a { self.0.children().filter_map(RedRef::cast) } } node! { /// A pair of a name and an expression: `pattern: dashed`. Named } impl Named { /// The name: `pattern`. pub fn name(&self) -> Ident { self.0.cast_first_child().expect("named pair is missing name ident") } /// The right-hand side of the pair: `dashed`. pub fn expr(&self) -> Expr { self.0 .children() .filter_map(RedRef::cast) .nth(1) .expect("named pair is missing expression") } } node! { /// A template expression: `[*Hi* there!]`. Template => TemplateExpr } impl TemplateExpr { /// The contents of the template. pub fn body(&self) -> Markup { self.0 .cast_first_child() .expect("template expression is missing body") } } node! { /// A grouped expression: `(1 + 2)`. Group => GroupExpr } impl GroupExpr { /// The wrapped expression. pub fn expr(&self) -> Expr { self.0 .cast_first_child() .expect("group expression is missing expression") } } node! { /// A block expression: `{ let x = 1; x + 2 }`. Block => BlockExpr } impl BlockExpr { /// The list of expressions contained in the block. pub fn exprs<'a>(&'a self) -> impl Iterator + 'a { self.0.children().filter_map(RedRef::cast) } } node! { /// A unary operation: `-x`. Unary => UnaryExpr } impl UnaryExpr { /// The operator: `-`. pub fn op(&self) -> UnOp { self.0 .cast_first_child() .expect("unary expression is missing operator") } /// The expression to operator on: `x`. pub fn expr(&self) -> Expr { self.0 .cast_first_child() .expect("unary expression is missing expression") } } /// A unary operator. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum UnOp { /// The plus operator: `+`. Pos, /// The negation operator: `-`. Neg, /// The boolean `not`. Not, } impl TypedNode for UnOp { fn cast_from(node: RedRef) -> Option { Self::from_token(node.kind()) } } impl UnOp { /// Try to convert the token into a unary operation. pub fn from_token(token: &NodeKind) -> Option { Some(match token { NodeKind::Plus => Self::Pos, NodeKind::Minus => Self::Neg, NodeKind::Not => Self::Not, _ => return None, }) } /// The precedence of this operator. pub fn precedence(self) -> usize { match self { Self::Pos | Self::Neg => 8, 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 => BinaryExpr } impl BinaryExpr { /// The binary operator: `+`. pub fn op(&self) -> BinOp { self.0 .cast_first_child() .expect("binary expression is missing operator") } /// The left-hand side of the operation: `a`. pub fn lhs(&self) -> Expr { self.0 .cast_first_child() .expect("binary expression is missing left-hand side") } /// The right-hand side of the operation: `b`. pub fn rhs(&self) -> Expr { self.0 .children() .filter_map(RedRef::cast) .nth(1) .expect("binary expression is missing right-hand side") } } /// A binary operator. #[derive(Debug, Copy, Clone, Eq, PartialEq)] 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 add-assign operator: `+=`. AddAssign, /// The subtract-assign oeprator: `-=`. SubAssign, /// The multiply-assign operator: `*=`. MulAssign, /// The divide-assign operator: `/=`. DivAssign, } impl TypedNode for BinOp { fn cast_from(node: RedRef) -> Option { Self::from_token(node.kind()) } } impl BinOp { /// Try to convert the token into a binary operation. pub fn from_token(token: &NodeKind) -> Option { Some(match token { NodeKind::Plus => Self::Add, NodeKind::Minus => Self::Sub, NodeKind::Star => Self::Mul, NodeKind::Slash => Self::Div, NodeKind::And => Self::And, NodeKind::Or => Self::Or, NodeKind::EqEq => Self::Eq, NodeKind::ExclEq => Self::Neq, NodeKind::Lt => Self::Lt, NodeKind::LtEq => Self::Leq, NodeKind::Gt => Self::Gt, NodeKind::GtEq => Self::Geq, NodeKind::Eq => Self::Assign, NodeKind::PlusEq => Self::AddAssign, NodeKind::HyphEq => Self::SubAssign, NodeKind::StarEq => Self::MulAssign, NodeKind::SlashEq => Self::DivAssign, _ => return None, }) } /// The precedence of this operator. pub fn precedence(self) -> usize { match self { Self::Mul | Self::Div => 6, Self::Add | Self::Sub => 5, Self::Eq | Self::Neq | Self::Lt | Self::Leq | Self::Gt | Self::Geq => 4, Self::And => 3, Self::Or => 2, Self::Assign | Self::AddAssign | Self::SubAssign | Self::MulAssign | Self::DivAssign => 1, } } /// The associativity of this operator. pub fn associativity(self) -> Associativity { match self { Self::Add | Self::Sub | Self::Mul | Self::Div | Self::And | Self::Or | Self::Eq | Self::Neq | Self::Lt | Self::Leq | Self::Gt | Self::Geq => Associativity::Left, Self::Assign | Self::AddAssign | Self::SubAssign | Self::MulAssign | Self::DivAssign => Associativity::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::Assign => "=", Self::AddAssign => "+=", Self::SubAssign => "-=", Self::MulAssign => "*=", Self::DivAssign => "/=", } } } /// The associativity of a binary operator. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Associativity { /// 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! { /// An invocation of a function: `foo(...)`. Call => CallExpr } impl CallExpr { /// The function to call. pub fn callee(&self) -> Expr { self.0.cast_first_child().expect("call expression is missing callee") } /// The arguments to the function. pub fn args(&self) -> CallArgs { self.0 .cast_first_child() .expect("call expression is missing argument list") } } node! { /// The arguments to a function: `12, draw: false`. CallArgs } impl CallArgs { /// The positional and named arguments. pub fn items<'a>(&'a self) -> impl Iterator + 'a { self.0.children().filter_map(RedRef::cast) } } /// An argument to a function call. #[derive(Debug, Clone, PartialEq)] pub enum CallArg { /// A positional argument: `12`. Pos(Expr), /// A named argument: `draw: false`. Named(Named), /// A spreaded argument: `..things`. Spread(Expr), } impl TypedNode for CallArg { fn cast_from(node: RedRef) -> Option { match node.kind() { NodeKind::Named => Some(CallArg::Named( node.cast().expect("named call argument is missing name"), )), NodeKind::ParameterSink => Some(CallArg::Spread( node.cast_first_child() .expect("call argument sink is missing expression"), )), _ => Some(CallArg::Pos(node.cast()?)), } } } impl CallArg { /// The name of this argument. pub fn span(&self) -> Span { match self { Self::Named(named) => named.span(), Self::Pos(expr) => expr.span(), Self::Spread(expr) => expr.span(), } } } node! { /// A closure expression: `(x, y) => z`. Closure => ClosureExpr } impl ClosureExpr { /// The name of the closure. /// /// This only exists if you use the function syntax sugar: `let f(x) = y`. pub fn name(&self) -> Option { // `first_convert_child` does not work here because of the Option in the // Result. self.0.cast_first_child() } /// The parameter bindings. pub fn params<'a>(&'a self) -> impl Iterator + 'a { self.0 .children() .find(|x| x.kind() == &NodeKind::ClosureParams) .expect("closure is missing parameter list") .children() .filter_map(RedRef::cast) } /// The body of the closure. pub fn body(&self) -> Expr { // The filtering for the NodeKind is necessary here because otherwise, // `first_convert_child` will use the Ident if present. self.0.cast_last_child().expect("closure is missing body") } /// The red node reference of the body of the closure. pub fn body_ref(&self) -> RedRef { self.0 .children() .filter(|x| x.cast::().is_some()) .last() .unwrap() } } /// An parameter to a closure. #[derive(Debug, Clone, PartialEq)] pub enum ClosureParam { /// A positional parameter: `x`. Pos(Ident), /// A named parameter with a default value: `draw: false`. Named(Named), /// A parameter sink: `..args`. Sink(Ident), } impl TypedNode for ClosureParam { fn cast_from(node: RedRef) -> Option { match node.kind() { NodeKind::Ident(i) => { Some(ClosureParam::Pos(Ident::new(i, node.span()).unwrap())) } NodeKind::Named => Some(ClosureParam::Named( node.cast().expect("named closure parameter is missing name"), )), NodeKind::ParameterSink => Some(ClosureParam::Sink( node.cast_first_child() .expect("closure parameter sink is missing identifier"), )), _ => Some(ClosureParam::Pos(node.cast()?)), } } } node! { /// A with expression: `f with (x, y: 1)`. WithExpr } impl WithExpr { /// The function to apply the arguments to. pub fn callee(&self) -> Expr { self.0 .cast_first_child() .expect("with expression is missing callee expression") } /// The arguments to apply to the function. pub fn args(&self) -> CallArgs { self.0 .cast_first_child() .expect("with expression is missing argument list") } } node! { /// A let expression: `let x = 1`. LetExpr } impl LetExpr { /// The binding to assign to. pub fn binding(&self) -> Ident { if let Some(c) = self.0.cast_first_child() { c } else if let Some(w) = self.0.typed_child(&NodeKind::WithExpr) { // Can't do an `first_convert_child` here because the WithExpr's // callee has to be an identifier. w.cast_first_child() .expect("with expression is missing an identifier callee") } else if let Some(Expr::Closure(c)) = self.0.cast_last_child() { c.name().expect("closure is missing an identifier name") } else { panic!("let expression is missing either an identifier or a with expression") } } /// The expression the binding is initialized with. pub fn init(&self) -> Option { if self.0.cast_first_child::().is_some() { self.0.children().filter_map(RedRef::cast).nth(1) } else { Some( self.0 .cast_first_child() .expect("let expression is missing a with expression"), ) } } /// The red node reference for the expression the binding is initialized /// with. pub fn init_ref(&self) -> RedRef { if self.0.cast_first_child::().is_some() { self.0.children().filter(|x| x.cast::().is_some()).nth(1) } else { self.0.children().find(|x| x.cast::().is_some()) } .unwrap() } } node! { /// An import expression: `import a, b, c from "utils.typ"`. ImportExpr } impl ImportExpr { /// The items to be imported. pub fn imports(&self) -> Imports { self.0 .cast_first_child() .expect("import expression is missing import list") } /// The location of the importable file. pub fn path(&self) -> Expr { self.0 .cast_first_child() .expect("import expression is missing path expression") } } /// The items that ought to be imported from a file. #[derive(Debug, Clone, PartialEq)] pub enum Imports { /// All items in the scope of the file should be imported. Wildcard, /// The specified identifiers from the file should be imported. Idents(Vec), } impl TypedNode for Imports { fn cast_from(node: RedRef) -> Option { match node.kind() { NodeKind::Star => Some(Imports::Wildcard), NodeKind::ImportItems => { let idents = node.children().filter_map(RedRef::cast).collect(); Some(Imports::Idents(idents)) } _ => None, } } } node! { /// An include expression: `include "chapter1.typ"`. IncludeExpr } impl IncludeExpr { /// The location of the file to be included. pub fn path(&self) -> Expr { self.0 .cast_first_child() .expect("include expression is missing path expression") } } node! { /// An if-else expression: `if x { y } else { z }`. IfExpr } impl IfExpr { /// The condition which selects the body to evaluate. pub fn condition(&self) -> Expr { self.0 .cast_first_child() .expect("if expression is missing condition expression") } /// The expression to evaluate if the condition is true. pub fn if_body(&self) -> Expr { self.0 .children() .filter_map(RedRef::cast) .nth(1) .expect("if expression is missing if body") } /// The expression to evaluate if the condition is false. pub fn else_body(&self) -> Option { self.0.children().filter_map(RedRef::cast).nth(2) } } node! { /// A while loop expression: `while x { y }`. WhileExpr } impl WhileExpr { /// The condition which selects whether to evaluate the body. pub fn condition(&self) -> Expr { self.0 .cast_first_child() .expect("while loop expression is missing condition expression") } /// The expression to evaluate while the condition is true. pub fn body(&self) -> Expr { self.0 .children() .filter_map(RedRef::cast) .nth(1) .expect("while loop expression is missing body") } } node! { /// A for loop expression: `for x in y { z }`. ForExpr } impl ForExpr { /// The pattern to assign to. pub fn pattern(&self) -> ForPattern { self.0 .cast_first_child() .expect("for loop expression is missing pattern") } /// The expression to iterate over. pub fn iter(&self) -> Expr { self.0 .cast_first_child() .expect("for loop expression is missing iterable expression") } /// The expression to evaluate for each iteration. pub fn body(&self) -> Expr { self.0 .children() .filter_map(RedRef::cast) .last() .expect("for loop expression is missing body") } /// The red node reference for the expression to evaluate for each iteration. pub fn body_ref(&self) -> RedRef { self.0 .children() .filter(|x| x.cast::().is_some()) .last() .unwrap() } } node! { /// A for-in loop expression: `for x in y { z }`. ForPattern } impl ForPattern { pub fn key(&self) -> Option { let mut items: Vec<_> = self.0.children().filter_map(RedRef::cast).collect(); if items.len() > 1 { Some(items.remove(0)) } else { None } } pub fn value(&self) -> Ident { self.0 .cast_last_child() .expect("for-in loop pattern is missing value") } }