From 7d34a548ccd14debe0668e23454e1ced70e485ec Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Mon, 1 Nov 2021 10:57:45 +0100 Subject: Reorganize syntax module --- src/syntax/ast.rs | 1025 +++++++++++++++++++++++++++++++++++++++++++++ src/syntax/expr.rs | 869 -------------------------------------- src/syntax/markup.rs | 159 ------- src/syntax/mod.rs | 1137 ++++++++++++++++++++++++++------------------------ src/syntax/token.rs | 40 -- 5 files changed, 1611 insertions(+), 1619 deletions(-) create mode 100644 src/syntax/ast.rs delete mode 100644 src/syntax/expr.rs delete mode 100644 src/syntax/markup.rs delete mode 100644 src/syntax/token.rs (limited to 'src/syntax') diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs new file mode 100644 index 00000000..bdd0767d --- /dev/null +++ b/src/syntax/ast.rs @@ -0,0 +1,1025 @@ +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") + } +} diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs deleted file mode 100644 index 1439cbdb..00000000 --- a/src/syntax/expr.rs +++ /dev/null @@ -1,869 +0,0 @@ -use super::{Ident, Markup, NodeKind, RedNode, RedRef, Span, TypedNode}; -use crate::geom::{AngularUnit, LengthUnit}; -use crate::node; -use crate::util::EcoString; - -/// 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") - } -} diff --git a/src/syntax/markup.rs b/src/syntax/markup.rs deleted file mode 100644 index f43a618a..00000000 --- a/src/syntax/markup.rs +++ /dev/null @@ -1,159 +0,0 @@ -use super::{Expr, Ident, NodeKind, RedNode, RedRef, Span, TypedNode}; -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") - } -} diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 9d4beb6c..9fd2b21d 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -1,388 +1,78 @@ //! Syntax types. -mod expr; +mod ast; mod ident; -mod markup; mod pretty; mod span; -mod token; use std::fmt; use std::fmt::{Debug, Display, Formatter}; use std::mem; use std::rc::Rc; -pub use expr::*; +pub use ast::*; pub use ident::*; -pub use markup::*; pub use pretty::*; pub use span::*; -pub use token::*; use crate::geom::{AngularUnit, LengthUnit}; use crate::source::SourceId; use crate::util::EcoString; -#[derive(Debug, Clone, PartialEq)] -pub enum NodeKind { - /// A left square bracket: `[`. - LeftBracket, - /// A right square bracket: `]`. - RightBracket, - /// A left curly brace: `{`. - LeftBrace, - /// A right curly brace: `}`. - RightBrace, - /// A left round parenthesis: `(`. - LeftParen, - /// A right round parenthesis: `)`. - RightParen, - /// An asterisk: `*`. - Star, - /// A comma: `,`. - Comma, - /// A semicolon: `;`. - Semicolon, - /// A colon: `:`. - Colon, - /// A plus: `+`. - Plus, - /// A hyphen: `-`. - Minus, - /// A slash: `/`. - Slash, - /// A single equals sign: `=`. - Eq, - /// Two equals signs: `==`. - EqEq, - /// An exclamation mark followed by an equals sign: `!=`. - ExclEq, - /// A less-than sign: `<`. - Lt, - /// A less-than sign followed by an equals sign: `<=`. - LtEq, - /// A greater-than sign: `>`. - Gt, - /// A greater-than sign followed by an equals sign: `>=`. - GtEq, - /// A plus followed by an equals sign: `+=`. - PlusEq, - /// A hyphen followed by an equals sign: `-=`. - HyphEq, - /// An asterisk followed by an equals sign: `*=`. - StarEq, - /// A slash followed by an equals sign: `/=`. - SlashEq, - /// Two dots: `..`. - Dots, - /// An equals sign followed by a greater-than sign: `=>`. - Arrow, - /// The `not` operator. - Not, - /// The `and` operator. - And, - /// The `or` operator. - Or, - /// The `with` operator. - With, - /// The `with` expression: `with (1)`. - WithExpr, - /// The none literal: `none`. - None, - /// The auto literal: `auto`. - Auto, - /// The `let` keyword. - Let, - /// 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, - /// One or more whitespace characters. - Space(usize), - /// A consecutive non-markup string. - Text(EcoString), - /// A slash and the letter "u" followed by a hexadecimal unicode entity - /// enclosed in curly braces: `\u{1F5FA}`. - UnicodeEscape(UnicodeEscapeToken), - /// An arbitrary number of backticks followed by inner contents, terminated - /// with the same number of backticks: `` `...` ``. - Raw(Rc), - /// Dollar signs surrounding inner contents. - Math(Rc), - /// A numbering: `23.`. - /// - /// Can also exist without the number: `.`. - EnumNumbering(Option), - /// An identifier: `center`. - Ident(EcoString), - /// A boolean: `true`, `false`. - Bool(bool), - /// An integer: `120`. - Int(i64), - /// A floating-point number: `1.2`, `10e-4`. - Float(f64), - /// A length: `12pt`, `3cm`. - Length(f64, LengthUnit), - /// An angle: `90deg`. - Angle(f64, AngularUnit), - /// A percentage: `50%`. - /// - /// _Note_: `50%` is stored as `50.0` here, as in the corresponding - /// [literal](super::Lit::Percent). - Percentage(f64), - /// A fraction unit: `3fr`. - Fraction(f64), - /// A quoted string: `"..."`. - Str(StrToken), - /// Two slashes followed by inner contents, terminated with a newline: - /// `//\n`. - LineComment, - /// A slash and a star followed by inner contents, terminated with a star - /// and a slash: `/**/`. - /// - /// The comment can contain nested block comments. - BlockComment, - /// Tokens that appear in the wrong place. - Error(ErrorPosition, EcoString), - /// Unknown character sequences. - Unknown(EcoString), - /// Template markup. - Markup, - /// 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, - /// A non-breaking space: `~`. - NonBreakingSpace, - /// An en-dash: `--`. - EnDash, - /// An em-dash: `---`. - EmDash, - /// A section heading: `= Introduction`. - Heading, - /// A heading's level: `=`, `==`, `===`, etc. - HeadingLevel(u8), - /// An item in an unordered list: `- ...`. - List, - /// The bullet character of an item in an unordered list: `-`. - ListBullet, - /// An item in an enumeration (ordered list): `1. ...`. - Enum, - /// An array expression: `(1, "hi", 12cm)`. - Array, - /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. - Dict, - /// A named argument: `thickness: 3pt`. - Named, - /// A template expression: `[*Hi* there!]`. - Template, - /// A grouped expression: `(1 + 2)`. - Group, - /// A block expression: `{ let x = 1; x + 2 }`. - Block, - /// A unary operation: `-x`. - Unary, - /// A binary operation: `a + b`. - Binary, - /// An invocation of a function: `f(x, y)`. - Call, - /// A function call's argument list: `(x, y)`. - CallArgs, - /// A closure expression: `(x, y) => z`. - Closure, - /// A closure's parameters: `(x, y)`. - ClosureParams, - /// A parameter sink: `..x`. - ParameterSink, - /// A for loop expression: `for x in y { ... }`. - ForExpr, - /// A while loop expression: `while x { ... }`. - WhileExpr, - /// An if expression: `if x { ... }`. - IfExpr, - /// A let expression: `let x = 1`. - LetExpr, - /// A for loop's destructuring pattern: `x` or `x, y`. - ForPattern, - /// The import expression: `import x from "foo.typ"`. - ImportExpr, - /// Items to import: `a, b, c`. - ImportItems, - /// The include expression: `include "foo.typ"`. - IncludeExpr, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum ErrorPosition { - /// At the start of the node. - Start, - /// Over the full width of the node. - Full, - /// At the end of the node. - End, -} - -impl Display for NodeKind { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.pad(self.as_str()) - } -} - -impl NodeKind { - pub fn is_parenthesis(&self) -> bool { - match self { - Self::LeftParen => true, - Self::RightParen => true, - _ => false, - } - } - - pub fn is_bracket(&self) -> bool { - match self { - Self::LeftBracket => true, - Self::RightBracket => true, - _ => false, - } - } - - pub fn is_brace(&self) -> bool { - match self { - Self::LeftBrace => true, - Self::RightBrace => true, - _ => false, - } - } - - pub fn is_error(&self) -> bool { - matches!(self, NodeKind::Error(_, _)) - } - - pub fn as_str(&self) -> &'static str { - match self { - Self::LeftBracket => "opening bracket", - Self::RightBracket => "closing bracket", - Self::LeftBrace => "opening brace", - Self::RightBrace => "closing brace", - Self::LeftParen => "opening paren", - Self::RightParen => "closing paren", - Self::Star => "star", - Self::Comma => "comma", - Self::Semicolon => "semicolon", - Self::Colon => "colon", - Self::Plus => "plus", - Self::Minus => "minus", - Self::Slash => "slash", - 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::With => "operator `with`", - Self::WithExpr => "`with` expression", - Self::None => "`none`", - Self::Auto => "`auto`", - Self::Let => "keyword `let`", - 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::Space(_) => "space", - Self::Math(_) => "math formula", - Self::EnumNumbering(_) => "numbering", - Self::Str(_) => "string", - Self::LineComment => "line comment", - Self::BlockComment => "block comment", - Self::Markup => "markup", - Self::Linebreak => "forced linebreak", - Self::Parbreak => "paragraph break", - Self::Strong => "strong", - Self::Emph => "emphasis", - Self::Text(_) => "text", - Self::NonBreakingSpace => "non-breaking space", - Self::EnDash => "en dash", - Self::EmDash => "em dash", - Self::UnicodeEscape(_) => "unicode escape sequence", - Self::Raw(_) => "raw block", - Self::Heading => "heading", - Self::HeadingLevel(_) => "heading level", - Self::List => "list", - Self::ListBullet => "list bullet", - Self::Enum => "enum", - Self::Ident(_) => "identifier", - Self::Bool(_) => "boolean", - Self::Int(_) => "integer", - Self::Float(_) => "float", - Self::Length(_, _) => "length", - Self::Angle(_, _) => "angle", - Self::Percentage(_) => "percentage", - Self::Fraction(_) => "`fr` value", - Self::Array => "array", - Self::Dict => "dictionary", - Self::Named => "named argument", - Self::Template => "template", - Self::Group => "group", - Self::Block => "block", - Self::Unary => "unary expression", - Self::Binary => "binary expression", - Self::Call => "call", - Self::CallArgs => "call arguments", - Self::Closure => "closure", - Self::ClosureParams => "closure parameters", - Self::ParameterSink => "parameter sink", - Self::ForExpr => "for-loop expression", - Self::WhileExpr => "while-loop expression", - Self::IfExpr => "if expression", - Self::LetExpr => "let expression", - Self::ForPattern => "for-loop destructuring pattern", - Self::ImportExpr => "import expression", - Self::ImportItems => "import items", - Self::IncludeExpr => "include expression", - Self::Unknown(src) => match src.as_str() { - "*/" => "end of block comment", - _ => "invalid token", - }, - Self::Error(_, _) => "parse error", +/// Children of a [`GreenNode`]. +#[derive(Clone, PartialEq)] +pub enum Green { + /// A non-terminal node in an Rc. + Node(Rc), + /// A terminal owned token. + Token(GreenData), +} + +impl Green { + fn data(&self) -> &GreenData { + match self { + Green::Node(n) => &n.data, + Green::Token(t) => &t, + } + } + + pub fn kind(&self) -> &NodeKind { + self.data().kind() + } + + pub fn len(&self) -> usize { + self.data().len() + } + + pub fn erroneous(&self) -> bool { + self.data().erroneous() + } + + pub fn children(&self) -> &[Green] { + match self { + Green::Node(n) => &n.children(), + Green::Token(_) => &[], + } + } +} + +impl Default for Green { + fn default() -> Self { + Self::Token(GreenData::new(NodeKind::None, 0)) + } +} + +impl Debug for Green { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{:?}: {}", self.kind(), self.len())?; + if let Self::Node(n) = self { + if !n.children.is_empty() { + f.write_str(" ")?; + f.debug_list().entries(&n.children).finish()?; + } } + + Ok(()) } } @@ -395,6 +85,41 @@ pub struct GreenNode { children: Vec, } +impl GreenNode { + pub fn new(kind: NodeKind, len: usize) -> Self { + Self { + data: GreenData::new(kind, len), + children: Vec::new(), + } + } + + pub fn with_children(kind: NodeKind, len: usize, children: Vec) -> Self { + let mut meta = GreenData::new(kind, len); + meta.erroneous |= children.iter().any(|c| c.erroneous()); + Self { data: meta, children } + } + + pub fn with_child(kind: NodeKind, len: usize, child: impl Into) -> Self { + Self::with_children(kind, len, vec![child.into()]) + } + + pub fn children(&self) -> &[Green] { + &self.children + } +} + +impl From for Green { + fn from(node: GreenNode) -> Self { + Rc::new(node).into() + } +} + +impl From> for Green { + fn from(node: Rc) -> Self { + Self::Node(node) + } +} + /// Data shared between [`GreenNode`]s and [`GreenToken`]s. #[derive(Clone, PartialEq)] pub struct GreenData { @@ -431,253 +156,563 @@ impl From for Green { } } -/// Children of a [`GreenNode`]. -#[derive(Clone, PartialEq)] -pub enum Green { - /// A terminal owned token. - Token(GreenData), - /// A non-terminal node in an Rc. - Node(Rc), +#[derive(Copy, Clone, PartialEq)] +pub struct RedRef<'a> { + id: SourceId, + offset: usize, + green: &'a Green, } -impl Green { - fn data(&self) -> &GreenData { - match self { - Green::Token(t) => &t, - Green::Node(n) => &n.data, +impl<'a> RedRef<'a> { + pub fn own(self) -> RedNode { + RedNode { + id: self.id, + offset: self.offset, + green: self.green.clone(), } } pub fn kind(&self) -> &NodeKind { - self.data().kind() + self.green.kind() } - pub fn len(&self) -> usize { - self.data().len() + pub fn span(&self) -> Span { + Span::new(self.id, self.offset, self.offset + self.green.len()) + } + + pub fn cast(self) -> Option + where + T: TypedNode, + { + T::cast_from(self) } pub fn erroneous(&self) -> bool { - self.data().erroneous() + self.green.erroneous() } - pub fn children(&self) -> &[Green] { - match self { + pub fn children(self) -> impl Iterator> + Clone { + let children = match &self.green { + Green::Node(node) => node.children(), Green::Token(_) => &[], - Green::Node(n) => &n.children(), - } + }; + + let mut offset = self.offset; + children.iter().map(move |green| { + let child_offset = offset; + offset += green.len(); + RedRef { id: self.id, offset: child_offset, green } + }) + } + + pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { + self.children() + .find(|x| mem::discriminant(x.kind()) == mem::discriminant(kind)) + } + + pub(crate) fn cast_first_child(&self) -> Option { + self.children().find_map(RedRef::cast) + } + + pub(crate) fn cast_last_child(&self) -> Option { + self.children().filter_map(RedRef::cast).last() } } -impl GreenNode { - pub fn new(kind: NodeKind, len: usize) -> Self { - Self { - data: GreenData::new(kind, len), - children: Vec::new(), - } +#[derive(Clone, PartialEq)] +pub struct RedNode { + id: SourceId, + offset: usize, + green: Green, +} + +impl RedNode { + pub fn new_root(root: Rc, id: SourceId) -> Self { + Self { id, offset: 0, green: root.into() } } - pub fn with_children(kind: NodeKind, len: usize, children: Vec) -> Self { - let mut meta = GreenData::new(kind, len); - meta.erroneous |= children.iter().any(|c| c.erroneous()); - Self { data: meta, children } + pub fn span(&self) -> Span { + self.as_ref().span() } - pub fn with_child(kind: NodeKind, len: usize, child: impl Into) -> Self { - Self::with_children(kind, len, vec![child.into()]) + pub fn len(&self) -> usize { + self.green.len() } - pub fn children(&self) -> &[Green] { - &self.children + pub fn kind(&self) -> &NodeKind { + self.green.kind() } -} -impl From for Green { - fn from(node: GreenNode) -> Self { - Rc::new(node).into() + pub fn children<'a>(&'a self) -> impl Iterator> + Clone { + self.as_ref().children() + } + + pub fn errors(&self) -> Vec<(Span, EcoString)> { + if !self.green.erroneous() { + return vec![]; + } + + match self.kind() { + NodeKind::Error(pos, msg) => { + let span = match pos { + ErrorPosition::Start => self.span().at_start(), + ErrorPosition::Full => self.span(), + ErrorPosition::End => self.span().at_end(), + }; + + vec![(span, msg.clone())] + } + _ => self + .as_ref() + .children() + .filter(|red| red.green.erroneous()) + .flat_map(|red| red.own().errors()) + .collect(), + } + } + + pub fn as_ref<'a>(&'a self) -> RedRef<'a> { + RedRef { + id: self.id, + offset: self.offset, + green: &self.green, + } + } + + pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { + self.as_ref().typed_child(kind).map(RedRef::own) } -} -impl From> for Green { - fn from(node: Rc) -> Self { - Self::Node(node) + pub(crate) fn cast_first_child(&self) -> Option { + self.as_ref().cast_first_child() } -} -impl Default for Green { - fn default() -> Self { - Self::Token(GreenData::new(NodeKind::None, 0)) + pub(crate) fn cast_last_child(&self) -> Option { + self.as_ref().cast_last_child() } } -impl Debug for Green { +impl Debug for RedNode { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{:?}: {}", self.kind(), self.len())?; - if let Self::Node(n) = self { - if !n.children.is_empty() { - f.write_str(" ")?; - f.debug_list().entries(&n.children).finish()?; - } + write!(f, "{:?}: {:?}", self.kind(), self.span())?; + let children = self.as_ref().children().collect::>(); + if !children.is_empty() { + f.write_str(" ")?; + f.debug_list() + .entries(children.into_iter().map(RedRef::own)) + .finish()?; } - Ok(()) } } -#[derive(Copy, Clone, PartialEq)] -pub struct RedRef<'a> { - id: SourceId, - offset: usize, - green: &'a Green, +pub trait TypedNode: Sized { + /// Performs the conversion. + fn cast_from(value: RedRef) -> Option; +} + +#[derive(Debug, Clone, PartialEq)] +pub enum NodeKind { + /// A left square bracket: `[`. + LeftBracket, + /// A right square bracket: `]`. + RightBracket, + /// A left curly brace: `{`. + LeftBrace, + /// A right curly brace: `}`. + RightBrace, + /// A left round parenthesis: `(`. + LeftParen, + /// A right round parenthesis: `)`. + RightParen, + /// An asterisk: `*`. + Star, + /// A comma: `,`. + Comma, + /// A semicolon: `;`. + Semicolon, + /// A colon: `:`. + Colon, + /// A plus: `+`. + Plus, + /// A hyphen: `-`. + Minus, + /// A slash: `/`. + Slash, + /// A single equals sign: `=`. + Eq, + /// Two equals signs: `==`. + EqEq, + /// An exclamation mark followed by an equals sign: `!=`. + ExclEq, + /// A less-than sign: `<`. + Lt, + /// A less-than sign followed by an equals sign: `<=`. + LtEq, + /// A greater-than sign: `>`. + Gt, + /// A greater-than sign followed by an equals sign: `>=`. + GtEq, + /// A plus followed by an equals sign: `+=`. + PlusEq, + /// A hyphen followed by an equals sign: `-=`. + HyphEq, + /// An asterisk followed by an equals sign: `*=`. + StarEq, + /// A slash followed by an equals sign: `/=`. + SlashEq, + /// The `not` operator. + Not, + /// The `and` operator. + And, + /// The `or` operator. + Or, + /// The `with` operator. + With, + /// Two dots: `..`. + Dots, + /// An equals sign followed by a greater-than sign: `=>`. + Arrow, + /// The none literal: `none`. + None, + /// The auto literal: `auto`. + Auto, + /// The `let` keyword. + Let, + /// 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, + /// Template markup. + Markup, + /// One or more whitespace characters. + Space(usize), + /// A forced line break: `\`. + Linebreak, + /// A paragraph break: Two or more newlines. + Parbreak, + /// A consecutive non-markup string. + Text(EcoString), + /// A non-breaking space: `~`. + NonBreakingSpace, + /// An en-dash: `--`. + EnDash, + /// An em-dash: `---`. + EmDash, + /// A slash and the letter "u" followed by a hexadecimal unicode entity + /// enclosed in curly braces: `\u{1F5FA}`. + UnicodeEscape(UnicodeEscapeToken), + /// Strong text was enabled / disabled: `*`. + Strong, + /// Emphasized text was enabled / disabled: `_`. + Emph, + /// A section heading: `= Introduction`. + Heading, + /// A heading's level: `=`, `==`, `===`, etc. + HeadingLevel(u8), + /// An item in an enumeration (ordered list): `1. ...`. + Enum, + /// A numbering: `23.`. + /// + /// Can also exist without the number: `.`. + EnumNumbering(Option), + /// An item in an unordered list: `- ...`. + List, + /// The bullet character of an item in an unordered list: `-`. + ListBullet, + /// An arbitrary number of backticks followed by inner contents, terminated + /// with the same number of backticks: `` `...` ``. + Raw(Rc), + /// Dollar signs surrounding inner contents. + Math(Rc), + /// An identifier: `center`. + Ident(EcoString), + /// A boolean: `true`, `false`. + Bool(bool), + /// An integer: `120`. + Int(i64), + /// A floating-point number: `1.2`, `10e-4`. + Float(f64), + /// A length: `12pt`, `3cm`. + Length(f64, LengthUnit), + /// An angle: `90deg`. + Angle(f64, AngularUnit), + /// A percentage: `50%`. + /// + /// _Note_: `50%` is stored as `50.0` here, as in the corresponding + /// [literal](super::Lit::Percent). + Percentage(f64), + /// A fraction unit: `3fr`. + Fraction(f64), + /// A quoted string: `"..."`. + Str(StrToken), + /// An array expression: `(1, "hi", 12cm)`. + Array, + /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. + Dict, + /// A named argument: `thickness: 3pt`. + Named, + /// A grouped expression: `(1 + 2)`. + Group, + /// A unary operation: `-x`. + Unary, + /// A binary operation: `a + b`. + Binary, + /// An invocation of a function: `f(x, y)`. + Call, + /// A function call's argument list: `(x, y)`. + CallArgs, + /// A closure expression: `(x, y) => z`. + Closure, + /// A closure's parameters: `(x, y)`. + ClosureParams, + /// A parameter sink: `..x`. + ParameterSink, + /// A template expression: `[*Hi* there!]`. + Template, + /// A block expression: `{ let x = 1; x + 2 }`. + Block, + /// A for loop expression: `for x in y { ... }`. + ForExpr, + /// A while loop expression: `while x { ... }`. + WhileExpr, + /// An if expression: `if x { ... }`. + IfExpr, + /// A let expression: `let x = 1`. + LetExpr, + /// The `with` expression: `with (1)`. + WithExpr, + /// A for loop's destructuring pattern: `x` or `x, y`. + ForPattern, + /// The import expression: `import x from "foo.typ"`. + ImportExpr, + /// Items to import: `a, b, c`. + ImportItems, + /// The include expression: `include "foo.typ"`. + IncludeExpr, + /// Two slashes followed by inner contents, terminated with a newline: + /// `//\n`. + LineComment, + /// A slash and a star followed by inner contents, terminated with a star + /// and a slash: `/**/`. + /// + /// The comment can contain nested block comments. + BlockComment, + /// Tokens that appear in the wrong place. + Error(ErrorPosition, EcoString), + /// Unknown character sequences. + Unknown(EcoString), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ErrorPosition { + /// At the start of the node. + Start, + /// Over the full width of the node. + Full, + /// At the end of the node. + End, +} + +/// A quoted string token: `"..."`. +#[derive(Debug, Clone, PartialEq)] +#[repr(transparent)] +pub struct StrToken { + /// The string inside the quotes. + pub string: EcoString, } -impl<'a> RedRef<'a> { - pub fn own(self) -> RedNode { - RedNode { - id: self.id, - offset: self.offset, - green: self.green.clone(), - } - } - - pub fn kind(&self) -> &NodeKind { - self.green.kind() - } - - pub fn span(&self) -> Span { - Span::new(self.id, self.offset, self.offset + self.green.len()) - } - - pub fn cast(self) -> Option - where - T: TypedNode, - { - T::cast_from(self) - } - - pub fn erroneous(&self) -> bool { - self.green.erroneous() - } - - pub fn children(self) -> impl Iterator> + Clone { - let children = match &self.green { - Green::Node(node) => node.children(), - Green::Token(_) => &[], - }; - - let mut offset = self.offset; - children.iter().map(move |green| { - let child_offset = offset; - offset += green.len(); - RedRef { id: self.id, offset: child_offset, green } - }) - } - - pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { - self.children() - .find(|x| mem::discriminant(x.kind()) == mem::discriminant(kind)) - } - - pub(crate) fn cast_first_child(&self) -> Option { - self.children().find_map(RedRef::cast) - } - - pub(crate) fn cast_last_child(&self) -> Option { - self.children().filter_map(RedRef::cast).last() - } +/// A raw block token: `` `...` ``. +#[derive(Debug, Clone, PartialEq)] +pub struct RawToken { + /// The raw text in the block. + pub text: EcoString, + /// The programming language of the raw text. + pub lang: Option, + /// The number of opening backticks. + pub backticks: u8, + /// Whether to display this as a block. + pub block: bool, } -#[derive(Clone, PartialEq)] -pub struct RedNode { - id: SourceId, - offset: usize, - green: Green, +/// A math formula token: `$2pi + x$` or `$[f'(x) = x^2]$`. +#[derive(Debug, Clone, PartialEq)] +pub struct MathToken { + /// The formula between the dollars. + pub formula: EcoString, + /// Whether the formula is display-level, that is, it is surrounded by + /// `$[..]`. + pub display: bool, } -impl RedNode { - pub fn new_root(root: Rc, id: SourceId) -> Self { - Self { id, offset: 0, green: root.into() } - } - - pub fn span(&self) -> Span { - self.as_ref().span() - } - - pub fn len(&self) -> usize { - self.green.len() - } - - pub fn kind(&self) -> &NodeKind { - self.green.kind() - } +/// A unicode escape sequence token: `\u{1F5FA}`. +#[derive(Debug, Clone, PartialEq)] +#[repr(transparent)] +pub struct UnicodeEscapeToken { + /// The resulting unicode character. + pub character: char, +} - pub fn children<'a>(&'a self) -> impl Iterator> + Clone { - self.as_ref().children() +impl Display for NodeKind { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.pad(self.as_str()) } +} - pub fn errors(&self) -> Vec<(Span, EcoString)> { - if !self.green.erroneous() { - return vec![]; - } - - match self.kind() { - NodeKind::Error(pos, msg) => { - let span = match pos { - ErrorPosition::Start => self.span().at_start(), - ErrorPosition::Full => self.span(), - ErrorPosition::End => self.span().at_end(), - }; - - vec![(span, msg.clone())] - } - _ => self - .as_ref() - .children() - .filter(|red| red.green.erroneous()) - .flat_map(|red| red.own().errors()) - .collect(), +impl NodeKind { + pub fn is_paren(&self) -> bool { + match self { + Self::LeftParen => true, + Self::RightParen => true, + _ => false, } } - pub fn as_ref<'a>(&'a self) -> RedRef<'a> { - RedRef { - id: self.id, - offset: self.offset, - green: &self.green, + pub fn is_bracket(&self) -> bool { + match self { + Self::LeftBracket => true, + Self::RightBracket => true, + _ => false, } } - pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { - self.as_ref().typed_child(kind).map(RedRef::own) - } - - pub(crate) fn cast_first_child(&self) -> Option { - self.as_ref().cast_first_child() + pub fn is_brace(&self) -> bool { + match self { + Self::LeftBrace => true, + Self::RightBrace => true, + _ => false, + } } - pub(crate) fn cast_last_child(&self) -> Option { - self.as_ref().cast_last_child() + pub fn is_error(&self) -> bool { + matches!(self, NodeKind::Error(_, _)) } -} -impl Debug for RedNode { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{:?}: {:?}", self.kind(), self.span())?; - let children = self.as_ref().children().collect::>(); - if !children.is_empty() { - f.write_str(" ")?; - f.debug_list() - .entries(children.into_iter().map(RedRef::own)) - .finish()?; + pub fn as_str(&self) -> &'static str { + match self { + Self::LeftBracket => "opening bracket", + Self::RightBracket => "closing bracket", + Self::LeftBrace => "opening brace", + Self::RightBrace => "closing brace", + Self::LeftParen => "opening paren", + Self::RightParen => "closing paren", + Self::Star => "star", + Self::Comma => "comma", + Self::Semicolon => "semicolon", + Self::Colon => "colon", + Self::Plus => "plus", + Self::Minus => "minus", + Self::Slash => "slash", + 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::Not => "operator `not`", + Self::And => "operator `and`", + Self::Or => "operator `or`", + Self::With => "operator `with`", + Self::Dots => "dots", + Self::Arrow => "arrow", + Self::None => "`none`", + Self::Auto => "`auto`", + Self::Let => "keyword `let`", + 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::Space(_) => "space", + Self::Linebreak => "forced linebreak", + Self::Parbreak => "paragraph break", + Self::Text(_) => "text", + Self::NonBreakingSpace => "non-breaking space", + Self::EnDash => "en dash", + Self::EmDash => "em dash", + Self::UnicodeEscape(_) => "unicode escape sequence", + Self::Strong => "strong", + Self::Emph => "emphasis", + Self::Heading => "heading", + Self::HeadingLevel(_) => "heading level", + Self::Enum => "enumeration item", + Self::EnumNumbering(_) => "enumeration item numbering", + Self::List => "list item", + Self::ListBullet => "list bullet", + Self::Raw(_) => "raw block", + Self::Math(_) => "math formula", + Self::Ident(_) => "identifier", + Self::Bool(_) => "boolean", + Self::Int(_) => "integer", + Self::Float(_) => "float", + Self::Length(_, _) => "length", + Self::Angle(_, _) => "angle", + Self::Percentage(_) => "percentage", + Self::Fraction(_) => "`fr` value", + Self::Str(_) => "string", + Self::Array => "array", + Self::Dict => "dictionary", + Self::Named => "named argument", + Self::Group => "group", + Self::Unary => "unary expression", + Self::Binary => "binary expression", + Self::Call => "call", + Self::CallArgs => "call arguments", + Self::Closure => "closure", + Self::ClosureParams => "closure parameters", + Self::ParameterSink => "parameter sink", + Self::Template => "template", + Self::Block => "block", + Self::ForExpr => "for-loop expression", + Self::WhileExpr => "while-loop expression", + Self::IfExpr => "`if` expression", + Self::LetExpr => "`let` expression", + Self::WithExpr => "`with` expression", + Self::ForPattern => "for-loop destructuring pattern", + Self::ImportExpr => "`import` expression", + Self::ImportItems => "import items", + Self::IncludeExpr => "`include` expression", + Self::LineComment => "line comment", + Self::BlockComment => "block comment", + Self::Error(_, _) => "parse error", + Self::Unknown(src) => match src.as_str() { + "*/" => "end of block comment", + _ => "invalid token", + }, } - Ok(()) } } -pub trait TypedNode: Sized { - /// Performs the conversion. - fn cast_from(value: RedRef) -> Option; -} - #[macro_export] macro_rules! node { ($(#[$attr:meta])* $name:ident) => { diff --git a/src/syntax/token.rs b/src/syntax/token.rs deleted file mode 100644 index 4f43bb4f..00000000 --- a/src/syntax/token.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::util::EcoString; - -/// A quoted string token: `"..."`. -#[derive(Debug, Clone, PartialEq)] -#[repr(transparent)] -pub struct StrToken { - /// The string inside the quotes. - pub string: EcoString, -} - -/// A raw block token: `` `...` ``. -#[derive(Debug, Clone, PartialEq)] -pub struct RawToken { - /// The raw text in the block. - pub text: EcoString, - /// The programming language of the raw text. - pub lang: Option, - /// The number of opening backticks. - pub backticks: u8, - /// Whether to display this as a block. - pub block: bool, -} - -/// A math formula token: `$2pi + x$` or `$[f'(x) = x^2]$`. -#[derive(Debug, Clone, PartialEq)] -pub struct MathToken { - /// The formula between the dollars. - pub formula: EcoString, - /// Whether the formula is display-level, that is, it is surrounded by - /// `$[..]`. - pub display: bool, -} - -/// A unicode escape sequence token: `\u{1F5FA}`. -#[derive(Debug, Clone, PartialEq)] -#[repr(transparent)] -pub struct UnicodeEscapeToken { - /// The resulting unicode character. - pub character: char, -} -- cgit v1.2.3