From 4875633acf4701705b9b3b014eb7d94268b897c2 Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Sat, 23 Oct 2021 19:03:27 +0200 Subject: Change parser --- src/syntax/expr.rs | 772 +++++++++++++++++++++++++++++++++++---------------- src/syntax/ident.rs | 12 +- src/syntax/markup.rs | 176 +++++++++--- src/syntax/mod.rs | 688 ++++++++++++++++++++++++++++++++++++++++++++- src/syntax/pretty.rs | 143 +++++----- src/syntax/span.rs | 15 + src/syntax/token.rs | 271 ++---------------- src/syntax/visit.rs | 263 ------------------ 8 files changed, 1492 insertions(+), 848 deletions(-) delete mode 100644 src/syntax/visit.rs (limited to 'src/syntax') diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 904515ba..d0d0c62f 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -1,75 +1,50 @@ -use std::rc::Rc; - -use super::{Ident, Markup, Span, Token}; +use super::{Ident, Markup, NodeKind, RedNode, RedTicket, 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(Box), + Ident(Ident), /// A literal: `1`, `true`, ... - Lit(Box), + Lit(Lit), /// An array expression: `(1, "hi", 12cm)`. - Array(Box), + Array(ArrayExpr), /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. - Dict(Box), + Dict(DictExpr), /// A template expression: `[*Hi* there!]`. - Template(Box), + Template(TemplateExpr), /// A grouped expression: `(1 + 2)`. - Group(Box), + Group(GroupExpr), /// A block expression: `{ let x = 1; x + 2 }`. - Block(Box), + Block(BlockExpr), /// A unary operation: `-x`. - Unary(Box), + Unary(UnaryExpr), /// A binary operation: `a + b`. - Binary(Box), + Binary(BinaryExpr), /// An invocation of a function: `f(x, y)`. - Call(Box), + Call(CallExpr), /// A closure expression: `(x, y) => z`. - Closure(Box), + Closure(ClosureExpr), /// A with expression: `f with (x, y: 1)`. - With(Box), + With(WithExpr), /// A let expression: `let x = 1`. - Let(Box), + Let(LetExpr), /// An if-else expression: `if x { y } else { z }`. - If(Box), + If(IfExpr), /// A while loop expression: `while x { y }`. - While(Box), + While(WhileExpr), /// A for loop expression: `for x in y { z }`. - For(Box), + For(ForExpr), /// An import expression: `import a, b, c from "utils.typ"`. - Import(Box), + Import(ImportExpr), /// An include expression: `include "chapter1.typ"`. - Include(Box), + Include(IncludeExpr), } impl Expr { - /// The source code location. - pub fn span(&self) -> Span { - match self { - Self::Ident(v) => v.span, - Self::Lit(v) => v.span(), - Self::Array(v) => v.span, - Self::Dict(v) => v.span, - Self::Template(v) => v.span, - Self::Group(v) => v.span, - Self::Block(v) => v.span, - Self::Unary(v) => v.span, - Self::Binary(v) => v.span, - Self::Call(v) => v.span, - Self::Closure(v) => v.span, - Self::With(v) => v.span, - Self::Let(v) => v.span, - Self::If(v) => v.span, - Self::While(v) => v.span, - Self::For(v) => v.span, - Self::Import(v) => v.span, - Self::Include(v) => v.span, - } - } - /// Whether the expression can be shortened in markup with a hashtag. pub fn has_short_form(&self) -> bool { matches!(self, @@ -83,6 +58,63 @@ impl Expr { | 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: RedTicket) -> Option { + match node.kind() { + NodeKind::Ident(_) => Some(Self::Ident(Ident::cast_from(node).unwrap())), + NodeKind::Array => Some(Self::Array(ArrayExpr::cast_from(node).unwrap())), + NodeKind::Dict => Some(Self::Dict(DictExpr::cast_from(node).unwrap())), + NodeKind::Template => { + Some(Self::Template(TemplateExpr::cast_from(node).unwrap())) + } + NodeKind::Group => Some(Self::Group(GroupExpr::cast_from(node).unwrap())), + NodeKind::Block => Some(Self::Block(BlockExpr::cast_from(node).unwrap())), + NodeKind::Unary => Some(Self::Unary(UnaryExpr::cast_from(node).unwrap())), + NodeKind::Binary => Some(Self::Binary(BinaryExpr::cast_from(node).unwrap())), + NodeKind::Call => Some(Self::Call(CallExpr::cast_from(node).unwrap())), + NodeKind::Closure => { + Some(Self::Closure(ClosureExpr::cast_from(node).unwrap())) + } + NodeKind::WithExpr => Some(Self::With(WithExpr::cast_from(node).unwrap())), + NodeKind::LetExpr => Some(Self::Let(LetExpr::cast_from(node).unwrap())), + NodeKind::IfExpr => Some(Self::If(IfExpr::cast_from(node).unwrap())), + NodeKind::WhileExpr => Some(Self::While(WhileExpr::cast_from(node).unwrap())), + NodeKind::ForExpr => Some(Self::For(ForExpr::cast_from(node).unwrap())), + NodeKind::ImportExpr => { + Some(Self::Import(ImportExpr::cast_from(node).unwrap())) + } + NodeKind::IncludeExpr => { + Some(Self::Include(IncludeExpr::cast_from(node).unwrap())) + } + _ => Some(Self::Lit(Lit::cast_from(node)?)), + } + } } /// A literal: `1`, `true`, ... @@ -113,94 +145,145 @@ pub enum Lit { Str(Span, EcoString), } +impl TypedNode for Lit { + fn cast_from(node: RedTicket) -> Option { + match node.kind() { + NodeKind::None => Some(Self::None(node.own().span())), + NodeKind::Auto => Some(Self::Auto(node.own().span())), + NodeKind::Bool(b) => Some(Self::Bool(node.own().span(), *b)), + NodeKind::Int(i) => Some(Self::Int(node.own().span(), *i)), + NodeKind::Float(f) => Some(Self::Float(node.own().span(), *f)), + NodeKind::Length(f, unit) => Some(Self::Length(node.own().span(), *f, *unit)), + NodeKind::Angle(f, unit) => Some(Self::Angle(node.own().span(), *f, *unit)), + NodeKind::Percentage(f) => Some(Self::Percent(node.own().span(), *f)), + NodeKind::Fraction(f) => Some(Self::Fractional(node.own().span(), *f)), + NodeKind::Str(s) => Some(Self::Str(node.own().span(), s.string.clone())), + _ => None, + } + } +} + impl Lit { - /// The source code location. 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, + 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, } } } -/// An array expression: `(1, "hi", 12cm)`. -#[derive(Debug, Clone, PartialEq)] -pub struct ArrayExpr { - /// The source code location. - pub span: Span, - /// The entries of the array. - pub items: Vec, -} +node!( + /// An array expression: `(1, "hi", 12cm)`. + Array => ArrayExpr +); -/// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. -#[derive(Debug, Clone, PartialEq)] -pub struct DictExpr { - /// The source code location. - pub span: Span, - /// The named dictionary entries. - pub items: Vec, +impl ArrayExpr { + /// The array items. + pub fn items(&self) -> Vec { + self.0.children().filter_map(RedTicket::cast).collect() + } } -/// A pair of a name and an expression: `pattern: dashed`. -#[derive(Debug, Clone, PartialEq)] -pub struct Named { - /// The name: `pattern`. - pub name: Ident, - /// The right-hand side of the pair: `dashed`. - pub expr: Expr, +node!( + /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. + Dict => DictExpr +); + +impl DictExpr { + /// The named dictionary items. + pub fn items(&self) -> Vec { + self.0.children().filter_map(RedTicket::cast).collect() + } } +node!( + /// A pair of a name and an expression: `pattern: dashed`. + Named +); + impl Named { - /// The source code location. - pub fn span(&self) -> Span { - self.name.span.join(self.expr.span()) + /// 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(RedTicket::cast) + .nth(1) + .expect("named pair is missing expression") } } -/// A template expression: `[*Hi* there!]`. -#[derive(Debug, Clone, PartialEq)] -pub struct TemplateExpr { - /// The source code location. - pub span: Span, +node!( + /// A template expression: `[*Hi* there!]`. + Template => TemplateExpr +); + +impl TemplateExpr { /// The contents of the template. - pub body: Markup, + pub fn body(&self) -> Markup { + self.0 + .cast_first_child() + .expect("template expression is missing body") + } } -/// A grouped expression: `(1 + 2)`. -#[derive(Debug, Clone, PartialEq)] -pub struct GroupExpr { - /// The source code location. - pub span: Span, +node!( + /// A grouped expression: `(1 + 2)`. + Group => GroupExpr +); + +impl GroupExpr { /// The wrapped expression. - pub expr: Expr, + pub fn expr(&self) -> Expr { + self.0 + .cast_first_child() + .expect("group expression is missing expression") + } } -/// A block expression: `{ let x = 1; x + 2 }`. -#[derive(Debug, Clone, PartialEq)] -pub struct BlockExpr { - /// The source code location. - pub span: Span, +node!( + /// A block expression: `{ let x = 1; x + 2 }`. + Block => BlockExpr +); + +impl BlockExpr { /// The list of expressions contained in the block. - pub exprs: Vec, + pub fn exprs(&self) -> Vec { + self.0.children().filter_map(RedTicket::cast).collect() + } } -/// A unary operation: `-x`. -#[derive(Debug, Clone, PartialEq)] -pub struct UnaryExpr { - /// The source code location. - pub span: Span, +node!( + /// A unary operation: `-x`. + Unary => UnaryExpr +); + +impl UnaryExpr { /// The operator: `-`. - pub op: UnOp, + pub fn op(&self) -> UnOp { + self.0 + .cast_first_child() + .expect("unary expression is missing operator") + } + /// The expression to operator on: `x`. - pub expr: Expr, + pub fn expr(&self) -> Expr { + self.0 + .cast_first_child() + .expect("unary expression is missing expression") + } } /// A unary operator. @@ -214,13 +297,19 @@ pub enum UnOp { Not, } +impl TypedNode for UnOp { + fn cast_from(node: RedTicket) -> Option { + Self::from_token(node.kind()) + } +} + impl UnOp { /// Try to convert the token into a unary operation. - pub fn from_token(token: Token) -> Option { + pub fn from_token(token: &NodeKind) -> Option { Some(match token { - Token::Plus => Self::Pos, - Token::Hyph => Self::Neg, - Token::Not => Self::Not, + NodeKind::Plus => Self::Pos, + NodeKind::Minus => Self::Neg, + NodeKind::Not => Self::Not, _ => return None, }) } @@ -229,7 +318,7 @@ impl UnOp { pub fn precedence(self) -> usize { match self { Self::Pos | Self::Neg => 8, - Self::Not => 3, + Self::Not => 4, } } @@ -243,17 +332,34 @@ impl UnOp { } } -/// A binary operation: `a + b`. -#[derive(Debug, Clone, PartialEq)] -pub struct BinaryExpr { - /// The source code location. - pub span: Span, +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 lhs: Expr, - /// The operator: `+`. - pub op: BinOp, + 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 rhs: Expr, + pub fn rhs(&self) -> Expr { + self.0 + .children() + .filter_map(RedTicket::cast) + .nth(1) + .expect("binary expression is missing right-hand side") + } } /// A binary operator. @@ -295,27 +401,33 @@ pub enum BinOp { DivAssign, } +impl TypedNode for BinOp { + fn cast_from(node: RedTicket) -> Option { + Self::from_token(node.kind()) + } +} + impl BinOp { /// Try to convert the token into a binary operation. - pub fn from_token(token: Token) -> Option { + pub fn from_token(token: &NodeKind) -> Option { Some(match token { - Token::Plus => Self::Add, - Token::Hyph => Self::Sub, - Token::Star => Self::Mul, - Token::Slash => Self::Div, - Token::And => Self::And, - Token::Or => Self::Or, - Token::EqEq => Self::Eq, - Token::ExclEq => Self::Neq, - Token::Lt => Self::Lt, - Token::LtEq => Self::Leq, - Token::Gt => Self::Gt, - Token::GtEq => Self::Geq, - Token::Eq => Self::Assign, - Token::PlusEq => Self::AddAssign, - Token::HyphEq => Self::SubAssign, - Token::StarEq => Self::MulAssign, - Token::SlashEq => Self::DivAssign, + 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, }) } @@ -392,27 +504,35 @@ pub enum Associativity { Right, } -/// An invocation of a function: `foo(...)`. -#[derive(Debug, Clone, PartialEq)] -pub struct CallExpr { - /// The source code location. - pub span: Span, +node!( + /// An invocation of a function: `foo(...)`. + Call => CallExpr +); + +impl CallExpr { /// The function to call. - pub callee: Expr, + pub fn callee(&self) -> Expr { + self.0.cast_first_child().expect("call expression is missing callee") + } + /// The arguments to the function. - pub args: CallArgs, + pub fn args(&self) -> CallArgs { + self.0 + .cast_first_child() + .expect("call expression is missing argument list") + } } -/// The arguments to a function: `12, draw: false`. -/// -/// In case of a bracketed invocation with a body, the body is _not_ -/// included in the span for the sake of clearer error messages. -#[derive(Debug, Clone, PartialEq)] -pub struct CallArgs { - /// The source code location. - pub span: Span, +node!( + /// The arguments to a function: `12, draw: false`. + CallArgs +); + +impl CallArgs { /// The positional and named arguments. - pub items: Vec, + pub fn items(&self) -> Vec { + self.0.children().filter_map(RedTicket::cast).collect() + } } /// An argument to a function call. @@ -426,30 +546,75 @@ pub enum CallArg { Spread(Expr), } +impl TypedNode for CallArg { + fn cast_from(node: RedTicket) -> Option { + match node.kind() { + NodeKind::Named => Some(CallArg::Named( + node.cast().expect("named call argument is missing name"), + )), + NodeKind::ParameterSink => Some(CallArg::Spread( + node.own() + .cast_first_child() + .expect("call argument sink is missing expression"), + )), + _ => Some(CallArg::Pos(node.cast()?)), + } + } +} + impl CallArg { - /// The source code location. + /// The name of this argument. pub fn span(&self) -> Span { match self { - Self::Pos(expr) => expr.span(), Self::Named(named) => named.span(), + Self::Pos(expr) => expr.span(), Self::Spread(expr) => expr.span(), } } } -/// A closure expression: `(x, y) => z`. -#[derive(Debug, Clone, PartialEq)] -pub struct ClosureExpr { - /// The source code location. - pub span: 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 name: Option, + 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 params: Vec, + pub fn params(&self) -> Vec { + self.0 + .children() + .find(|x| x.kind() == &NodeKind::ClosureParams) + .expect("closure is missing parameter list") + .own() + .children() + .filter_map(RedTicket::cast) + .collect() + } + /// The body of the closure. - pub body: Rc, + 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 ticket of the body of the closure. + pub fn body_ticket(&self) -> RedTicket { + self.0 + .children() + .filter(|x| x.cast::().is_some()) + .last() + .unwrap() + } } /// An parameter to a closure. @@ -463,50 +628,111 @@ pub enum ClosureParam { Sink(Ident), } -impl ClosureParam { - /// The source code location. - pub fn span(&self) -> Span { - match self { - Self::Pos(ident) => ident.span, - Self::Named(named) => named.span(), - Self::Sink(ident) => ident.span, +impl TypedNode for ClosureParam { + fn cast_from(node: RedTicket) -> Option { + match node.kind() { + NodeKind::Ident(i) => { + Some(ClosureParam::Pos(Ident::new(i, node.own().span()).unwrap())) + } + NodeKind::Named => Some(ClosureParam::Named( + node.cast().expect("named closure parameter is missing name"), + )), + NodeKind::ParameterSink => Some(ClosureParam::Sink( + node.own() + .cast_first_child() + .expect("closure parameter sink is missing identifier"), + )), + _ => Some(ClosureParam::Pos(node.cast()?)), } } } -/// A with expression: `f with (x, y: 1)`. -/// -/// Applies arguments to a function. -#[derive(Debug, Clone, PartialEq)] -pub struct WithExpr { - /// The source code location. - pub span: Span, +node!( + /// A with expression: `f with (x, y: 1)`. + WithExpr +); + +impl WithExpr { /// The function to apply the arguments to. - pub callee: Expr, + 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 args: CallArgs, + pub fn args(&self) -> CallArgs { + self.0 + .cast_first_child() + .expect("with expression is missing argument list") + } } -/// A let expression: `let x = 1`. -#[derive(Debug, Clone, PartialEq)] -pub struct LetExpr { - /// The source code location. - pub span: Span, +node!( + /// A let expression: `let x = 1`. + LetExpr +); + +impl LetExpr { /// The binding to assign to. - pub binding: Ident, + 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 init: Option, + pub fn init(&self) -> Option { + if self.0.cast_first_child::().is_some() { + self.0.children().filter_map(RedTicket::cast).nth(1) + } else { + Some( + self.0 + .cast_first_child() + .expect("let expression is missing a with expression"), + ) + } + } + + /// The ticket for the expression the binding is initialized with. + pub fn init_ticket(&self) -> RedTicket { + 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() + } } -/// An import expression: `import a, b, c from "utils.typ"`. -#[derive(Debug, Clone, PartialEq)] -pub struct ImportExpr { - /// The source code location. - pub span: Span, +node!( + /// An import expression: `import a, b, c from "utils.typ"`. + ImportExpr +); + +impl ImportExpr { /// The items to be imported. - pub imports: Imports, + pub fn imports(&self) -> Imports { + self.0 + .cast_first_child() + .expect("import expression is missing import list") + } + /// The location of the importable file. - pub path: Expr, + 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. @@ -518,67 +744,137 @@ pub enum Imports { Idents(Vec), } -/// An include expression: `include "chapter1.typ"`. -#[derive(Debug, Clone, PartialEq)] -pub struct IncludeExpr { - /// The source code location. - pub span: Span, +impl TypedNode for Imports { + fn cast_from(node: RedTicket) -> Option { + match node.kind() { + NodeKind::Star => Some(Imports::Wildcard), + NodeKind::ImportItems => { + let idents = node.own().children().filter_map(RedTicket::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 path: Expr, + pub fn path(&self) -> Expr { + self.0 + .cast_first_child() + .expect("include expression is missing path expression") + } } -/// An if-else expression: `if x { y } else { z }`. -#[derive(Debug, Clone, PartialEq)] -pub struct IfExpr { - /// The source code location. - pub span: Span, +node!( + /// An if-else expression: `if x { y } else { z }`. + IfExpr +); + +impl IfExpr { /// The condition which selects the body to evaluate. - pub condition: Expr, + 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 if_body: Expr, + pub fn if_body(&self) -> Expr { + self.0 + .children() + .filter_map(RedTicket::cast) + .nth(1) + .expect("if expression is missing if body") + } + /// The expression to evaluate if the condition is false. - pub else_body: Option, + pub fn else_body(&self) -> Option { + self.0.children().filter_map(RedTicket::cast).nth(2) + } } -/// A while loop expression: `while x { y }`. -#[derive(Debug, Clone, PartialEq)] -pub struct WhileExpr { - /// The source code location. - pub span: Span, +node!( + /// A while loop expression: `while x { y }`. + WhileExpr +); + +impl WhileExpr { /// The condition which selects whether to evaluate the body. - pub condition: Expr, + 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 body: Expr, + pub fn body(&self) -> Expr { + self.0 + .children() + .filter_map(RedTicket::cast) + .nth(1) + .expect("while loop expression is missing body") + } } -/// A for loop expression: `for x in y { z }`. -#[derive(Debug, Clone, PartialEq)] -pub struct ForExpr { - /// The source code location. - pub span: Span, +node!( + /// A for loop expression: `for x in y { z }`. + ForExpr +); + +impl ForExpr { /// The pattern to assign to. - pub pattern: ForPattern, + pub fn pattern(&self) -> ForPattern { + self.0 + .cast_first_child() + .expect("for loop expression is missing pattern") + } + /// The expression to iterate over. - pub iter: Expr, + 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 body: Expr, -} + pub fn body(&self) -> Expr { + self.0 + .children() + .filter_map(RedTicket::cast) + .last() + .expect("for loop expression is missing body") + } -/// A pattern in a for loop. -#[derive(Debug, Clone, PartialEq)] -pub enum ForPattern { - /// A value pattern: `for v in array`. - Value(Ident), - /// A key-value pattern: `for k, v in dict`. - KeyValue(Ident, Ident), + /// The ticket for the expression to evaluate for each iteration. + pub fn body_ticket(&self) -> RedTicket { + 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 { - /// The source code location. - pub fn span(&self) -> Span { - match self { - Self::Value(v) => v.span, - Self::KeyValue(k, v) => k.span.join(v.span), - } + pub fn key(&self) -> Option { + let mut items: Vec<_> = self.0.children().filter_map(RedTicket::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/ident.rs b/src/syntax/ident.rs index 398e2ff9..2c61329d 100644 --- a/src/syntax/ident.rs +++ b/src/syntax/ident.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use unicode_xid::UnicodeXID; -use super::Span; +use super::{NodeKind, RedTicket, Span, TypedNode}; use crate::util::EcoString; /// An unicode identifier with a few extra permissible characters. @@ -66,6 +66,16 @@ impl From<&Ident> for EcoString { } } +impl TypedNode for Ident { + fn cast_from(node: RedTicket) -> Option { + if let NodeKind::Ident(i) = node.kind() { + Some(Ident::new(i, node.own().span()).unwrap()) + } else { + None + } + } +} + /// Whether a string is a valid identifier. pub fn is_ident(string: &str) -> bool { let mut chars = string.chars(); diff --git a/src/syntax/markup.rs b/src/syntax/markup.rs index 09a37116..c12c0e81 100644 --- a/src/syntax/markup.rs +++ b/src/syntax/markup.rs @@ -1,41 +1,87 @@ -use super::{Expr, Ident, Span}; +use super::{Expr, Ident, NodeKind, RedNode, RedTicket, Span, TypedNode}; +use crate::node; use crate::util::EcoString; +use std::fmt::Write; /// The syntactical root capable of representing a full parsed document. pub type Markup = Vec; +impl TypedNode for Markup { + fn cast_from(node: RedTicket) -> Option { + if node.kind() != &NodeKind::Markup { + return None; + } + + let children = node.own().children().filter_map(TypedNode::cast_from).collect(); + Some(children) + } +} + /// A single piece of markup. #[derive(Debug, Clone, PartialEq)] pub enum MarkupNode { /// Whitespace containing less than two newlines. Space, /// A forced line break: `\`. - Linebreak(Span), + Linebreak, /// A paragraph break: Two or more newlines. - Parbreak(Span), + Parbreak, /// Strong text was enabled / disabled: `*`. - Strong(Span), + Strong, /// Emphasized text was enabled / disabled: `_`. - Emph(Span), + Emph, /// Plain text. Text(EcoString), /// A raw block with optional syntax highlighting: `` `...` ``. - Raw(Box), + Raw(RawNode), /// A section heading: `= Introduction`. - Heading(Box), + Heading(HeadingNode), /// An item in an unordered list: `- ...`. - List(Box), + List(ListNode), /// An item in an enumeration (ordered list): `1. ...`. - Enum(Box), + Enum(EnumNode), /// An expression. Expr(Expr), } +impl TypedNode for MarkupNode { + fn cast_from(node: RedTicket) -> 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(if let Some(s) = u.character { + s.into() + } else { + let mut eco = EcoString::with_capacity(u.sequence.len() + 4); + write!(&mut eco, "\\u{{{}}}", u.sequence).unwrap(); + eco + })) + } + 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(_) => Some(MarkupNode::Raw(RawNode::cast_from(node).unwrap())), + NodeKind::Heading => { + Some(MarkupNode::Heading(HeadingNode::cast_from(node).unwrap())) + } + NodeKind::List => Some(MarkupNode::List(ListNode::cast_from(node).unwrap())), + NodeKind::Enum => Some(MarkupNode::Enum(EnumNode::cast_from(node).unwrap())), + NodeKind::Error(_, _) => None, + _ => Some(MarkupNode::Expr(Expr::cast_from(node)?)), + } + } +} + /// A raw block with optional syntax highlighting: `` `...` ``. #[derive(Debug, Clone, PartialEq)] pub struct RawNode { - /// The source code location. - pub span: Span, /// 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 @@ -46,33 +92,97 @@ pub struct RawNode { pub block: bool, } -/// A section heading: `= Introduction`. -#[derive(Debug, Clone, PartialEq)] -pub struct HeadingNode { - /// The source code location. - pub span: Span, - /// The section depth (numer of equals signs). - pub level: usize, +impl TypedNode for RawNode { + fn cast_from(node: RedTicket) -> Option { + if let NodeKind::Raw(raw) = node.kind() { + let span = node.own().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(), + }) + } else { + None + } + } +} + +node!( + /// A section heading: `= Introduction`. + Heading => HeadingNode +); + +impl HeadingNode { /// The contents of the heading. - pub body: Markup, + 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) -> HeadingLevel { + self.0 + .cast_first_child() + .expect("heading node is missing heading level") + } } -/// An item in an unordered list: `- ...`. -#[derive(Debug, Clone, PartialEq)] -pub struct ListNode { - /// The source code location. - pub span: Span, +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct HeadingLevel(pub usize); + +impl TypedNode for HeadingLevel { + fn cast_from(node: RedTicket) -> Option { + if let NodeKind::HeadingLevel(l) = node.kind() { + Some(Self((*l).into())) + } else { + None + } + } +} + +node!( + /// An item in an unordered list: `- ...`. + List => ListNode +); + +impl ListNode { /// The contents of the list item. - pub body: Markup, + pub fn body(&self) -> Markup { + self.0.cast_first_child().expect("list node is missing body") + } } -/// An item in an enumeration (ordered list): `1. ...`. -#[derive(Debug, Clone, PartialEq)] -pub struct EnumNode { - /// The source code location. - pub span: Span, - /// The number, if any. - pub number: Option, +node!( + /// An item in an enumeration (ordered list): `1. ...`. + Enum => EnumNode +); + +impl EnumNode { /// The contents of the list item. - pub body: Markup, + pub fn body(&self) -> Markup { + self.0.cast_first_child().expect("enumeration node is missing body") + } + + /// The number, if any. + pub fn number(&self) -> EnumNumber { + self.0.cast_first_child().expect("enumeration node is missing number") + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct EnumNumber(pub Option); + +impl TypedNode for EnumNumber { + fn cast_from(node: RedTicket) -> Option { + if let NodeKind::EnumNumbering(x) = node.kind() { + Some(Self(*x)) + } else { + None + } + } } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 8dbb108d..88757f8e 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -6,7 +6,11 @@ mod markup; mod pretty; mod span; mod token; -pub mod visit; + +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; +use std::mem; +use std::rc::Rc; pub use expr::*; pub use ident::*; @@ -14,3 +18,685 @@ 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(RawToken), + /// Dollar signs surrounding inner contents. + Math(MathToken), + /// 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, + /// A node that should never appear in a finished tree. + Never, + /// Tokens that appear in the wrong place. + Error(ErrorPosition, 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(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::Never => "a node that should not be here", + 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::Error(_, src) => match src.as_str() { + "*/" => "end of block comment", + _ => "invalid token", + }, + }) + } +} + +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::Never | NodeKind::Error(_, _)) + } +} + +/// A syntactical node. +#[derive(Clone, PartialEq)] +pub struct GreenNode { + /// Node metadata. + meta: GreenData, + /// This node's children, losslessly make up this node. + children: Vec, +} + +/// Data shared between [`GreenNode`]s and [`GreenToken`]s. +#[derive(Clone, PartialEq)] +pub struct GreenData { + /// What kind of node this is (each kind would have its own struct in a + /// strongly typed AST). + kind: NodeKind, + /// The byte length of the node in the source. + len: usize, + /// Whether this node or any of its children are erroneous. + has_error: bool, +} + +impl GreenData { + pub fn new(kind: NodeKind, len: usize) -> Self { + Self { len, has_error: kind.is_error(), kind } + } + + pub fn kind(&self) -> &NodeKind { + &self.kind + } + + pub fn len(&self) -> usize { + self.len + } + + pub fn has_error(&self) -> bool { + self.has_error + } +} + +impl From for Green { + fn from(token: GreenData) -> Self { + Self::Token(token) + } +} + +/// 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), +} + +impl Green { + fn meta(&self) -> &GreenData { + match self { + Green::Token(t) => &t, + Green::Node(n) => &n.meta, + } + } + + pub fn kind(&self) -> &NodeKind { + self.meta().kind() + } + + pub fn len(&self) -> usize { + self.meta().len() + } + + pub fn has_error(&self) -> bool { + self.meta().has_error() + } + + pub fn children(&self) -> &[Green] { + match self { + Green::Token(_) => &[], + Green::Node(n) => &n.children(), + } + } +} + +impl GreenNode { + pub fn new(kind: NodeKind, len: usize) -> Self { + Self { + meta: GreenData::new(kind, len), + children: Vec::new(), + } + } + + pub fn with_children( + kind: NodeKind, + len: usize, + children: impl Iterator>, + ) -> Self { + let mut meta = GreenData::new(kind, len); + let children = children + .map(|x| { + let x = x.into(); + meta.has_error |= x.has_error(); + x + }) + .collect(); + Self { meta, children } + } + + pub fn with_child(kind: NodeKind, len: usize, child: impl Into) -> Self { + Self::with_children(kind, len, std::iter::once(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) + } +} + +impl Default for Green { + fn default() -> Self { + Self::Token(GreenData::new(NodeKind::Never, 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(()) + } +} + +#[derive(Copy, Clone, PartialEq)] +pub struct RedTicket<'a> { + id: SourceId, + offset: usize, + green: &'a Green, +} + +impl<'a> RedTicket<'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 cast(self) -> Option + where + T: TypedNode, + { + T::cast_from(self) + } +} + +#[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 span(&self) -> Span { + Span::new(self.id, self.offset, self.offset + self.green.len()) + } + + pub fn len(&self) -> usize { + self.green.len() + } + + pub fn kind(&self) -> &NodeKind { + self.green.kind() + } + + pub fn children<'a>(&'a self) -> impl Iterator> + Clone + 'a { + let children = match &self.green { + Green::Node(node) => node.children(), + Green::Token(_) => &[], + }; + + let mut offset = self.offset; + children.iter().map(move |green_child| { + let child_offset = offset; + offset += green_child.len(); + RedTicket { + id: self.id, + offset: child_offset, + green: &green_child, + } + }) + } + + pub fn has_error(&self) -> bool { + self.green.has_error() + } + + pub fn errors(&self) -> Vec<(Span, EcoString)> { + if !self.green.has_error() { + return vec![]; + } + + if let NodeKind::Error(pos, msg) = self.kind() { + let span = match pos { + ErrorPosition::Start => self.span().at_start(), + ErrorPosition::Full => self.span(), + ErrorPosition::End => self.span().at_end(), + }; + + vec![(span, msg.clone())] + } else if let NodeKind::Never = self.kind() { + vec![(self.span(), "found a never node".into())] + } else { + self.children() + .filter(|ticket| ticket.green.has_error()) + .flat_map(|ticket| ticket.own().errors()) + .collect() + } + } + + pub fn ticket<'a>(&'a self) -> RedTicket<'a> { + RedTicket { + id: self.id, + offset: self.offset, + green: &self.green, + } + } + + pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { + self.children() + .find(|x| mem::discriminant(x.kind()) == mem::discriminant(kind)) + .map(RedTicket::own) + } + + pub(crate) fn cast_first_child(&self) -> Option { + self.children().find_map(RedTicket::cast) + } + + pub(crate) fn cast_last_child(&self) -> Option { + self.children().filter_map(RedTicket::cast).last() + } +} + +impl Debug for RedNode { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{:?}: {:?}", self.kind(), self.span())?; + let children = self.children().collect::>(); + if !children.is_empty() { + f.write_str(" ")?; + f.debug_list() + .entries(children.into_iter().map(RedTicket::own)) + .finish()?; + } + Ok(()) + } +} + +pub trait TypedNode: Sized { + /// Performs the conversion. + fn cast_from(value: RedTicket) -> Option; +} + +#[macro_export] +macro_rules! node { + (#[doc = $doc:expr] $name:ident) => { + node!(#[doc = $doc] $name => $name); + }; + (#[doc = $doc:expr] $variant:ident => $name:ident) => { + #[doc = $doc] + #[derive(Debug, Clone, PartialEq)] + pub struct $name(RedNode); + + impl TypedNode for $name { + fn cast_from(node: RedTicket) -> Option { + if node.kind() != &NodeKind::$variant { + return None; + } + + Some(Self(node.own())) + } + } + + impl $name { + pub fn span(&self) -> Span { + self.0.span() + } + + pub fn underlying(&self) -> RedTicket { + self.0.ticket() + } + } + }; +} diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 3d02f39f..b1c7e02b 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -88,10 +88,10 @@ impl Pretty for MarkupNode { match self { // TODO: Handle escaping. Self::Space => p.push(' '), - Self::Linebreak(_) => p.push_str(r"\"), - Self::Parbreak(_) => p.push_str("\n\n"), - Self::Strong(_) => p.push('*'), - Self::Emph(_) => p.push('_'), + Self::Linebreak => p.push_str(r"\"), + Self::Parbreak => p.push_str("\n\n"), + Self::Strong => p.push('*'), + Self::Emph => p.push('_'), Self::Text(text) => p.push_str(text), Self::Raw(raw) => raw.pretty(p), Self::Heading(heading) => heading.pretty(p), @@ -165,28 +165,28 @@ impl Pretty for RawNode { impl Pretty for HeadingNode { fn pretty(&self, p: &mut Printer) { - for _ in 0 .. self.level { + for _ in 0 .. self.level().0 { p.push('='); } p.push(' '); - self.body.pretty(p); + self.body().pretty(p); } } impl Pretty for ListNode { fn pretty(&self, p: &mut Printer) { p.push_str("- "); - self.body.pretty(p); + self.body().pretty(p); } } impl Pretty for EnumNode { fn pretty(&self, p: &mut Printer) { - if let Some(number) = self.number { + if let Some(number) = self.number().0 { write!(p, "{}", number).unwrap(); } p.push_str(". "); - self.body.pretty(p); + self.body().pretty(p); } } @@ -235,8 +235,10 @@ impl Pretty for Lit { impl Pretty for ArrayExpr { fn pretty(&self, p: &mut Printer) { p.push('('); - p.join(&self.items, ", ", |item, p| item.pretty(p)); - if self.items.len() == 1 { + + let items = self.items(); + p.join(&items, ", ", |item, p| item.pretty(p)); + if items.len() == 1 { p.push(','); } p.push(')'); @@ -246,10 +248,12 @@ impl Pretty for ArrayExpr { impl Pretty for DictExpr { fn pretty(&self, p: &mut Printer) { p.push('('); - if self.items.is_empty() { + + let items = self.items(); + if items.is_empty() { p.push(':'); } else { - p.join(&self.items, ", ", |named, p| named.pretty(p)); + p.join(&items, ", ", |named, p| named.pretty(p)); } p.push(')'); } @@ -257,16 +261,16 @@ impl Pretty for DictExpr { impl Pretty for Named { fn pretty(&self, p: &mut Printer) { - self.name.pretty(p); + self.name().pretty(p); p.push_str(": "); - self.expr.pretty(p); + self.expr().pretty(p); } } impl Pretty for TemplateExpr { fn pretty(&self, p: &mut Printer) { p.push('['); - self.body.pretty(p); + self.body().pretty(p); p.push(']'); } } @@ -274,7 +278,7 @@ impl Pretty for TemplateExpr { impl Pretty for GroupExpr { fn pretty(&self, p: &mut Printer) { p.push('('); - self.expr.pretty(p); + self.expr().pretty(p); p.push(')'); } } @@ -282,11 +286,13 @@ impl Pretty for GroupExpr { impl Pretty for BlockExpr { fn pretty(&self, p: &mut Printer) { p.push('{'); - if self.exprs.len() > 1 { + + let exprs = self.exprs(); + if exprs.len() > 1 { p.push(' '); } - p.join(&self.exprs, "; ", |expr, p| expr.pretty(p)); - if self.exprs.len() > 1 { + p.join(&exprs, "; ", |expr, p| expr.pretty(p)); + if exprs.len() > 1 { p.push(' '); } p.push('}'); @@ -295,11 +301,12 @@ impl Pretty for BlockExpr { impl Pretty for UnaryExpr { fn pretty(&self, p: &mut Printer) { - self.op.pretty(p); - if self.op == UnOp::Not { + let op = self.op(); + op.pretty(p); + if op == UnOp::Not { p.push(' '); } - self.expr.pretty(p); + self.expr().pretty(p); } } @@ -311,11 +318,11 @@ impl Pretty for UnOp { impl Pretty for BinaryExpr { fn pretty(&self, p: &mut Printer) { - self.lhs.pretty(p); + self.lhs().pretty(p); p.push(' '); - self.op.pretty(p); + self.op().pretty(p); p.push(' '); - self.rhs.pretty(p); + self.rhs().pretty(p); } } @@ -327,7 +334,7 @@ impl Pretty for BinOp { impl Pretty for CallExpr { fn pretty(&self, p: &mut Printer) { - self.callee.pretty(p); + self.callee().pretty(p); let mut write_args = |items: &[CallArg]| { p.push('('); @@ -335,25 +342,26 @@ impl Pretty for CallExpr { p.push(')'); }; - match self.args.items.as_slice() { - // This can be moved behind the arguments. - // - // Example: Transforms "#v(a, [b])" => "#v(a)[b]". - [head @ .., CallArg::Pos(Expr::Template(template))] => { - if !head.is_empty() { - write_args(head); - } - template.pretty(p); - } + let arg_list = self.args(); + let args = arg_list.items(); - items => write_args(items), + if let Some(Expr::Template(template)) = args + .last() + .and_then(|x| if let CallArg::Pos(arg) = x { Some(arg) } else { None }) + { + if args.len() > 1 { + write_args(&args[0 .. args.len() - 1]); + } + template.pretty(p); + } else { + write_args(&args); } } } impl Pretty for CallArgs { fn pretty(&self, p: &mut Printer) { - p.join(&self.items, ", ", |item, p| item.pretty(p)); + p.join(&self.items(), ", ", |item, p| item.pretty(p)); } } @@ -372,15 +380,15 @@ impl Pretty for CallArg { impl Pretty for ClosureExpr { fn pretty(&self, p: &mut Printer) { - if let [param] = self.params.as_slice() { + if let [param] = self.params().as_slice() { param.pretty(p); } else { p.push('('); - p.join(self.params.iter(), ", ", |item, p| item.pretty(p)); + p.join(self.params().iter(), ", ", |item, p| item.pretty(p)); p.push(')'); } p.push_str(" => "); - self.body.pretty(p); + self.body().pretty(p); } } @@ -399,9 +407,9 @@ impl Pretty for ClosureParam { impl Pretty for WithExpr { fn pretty(&self, p: &mut Printer) { - self.callee.pretty(p); + self.callee().pretty(p); p.push_str(" with ("); - self.args.pretty(p); + self.args().pretty(p); p.push(')'); } } @@ -409,13 +417,13 @@ impl Pretty for WithExpr { impl Pretty for LetExpr { fn pretty(&self, p: &mut Printer) { p.push_str("let "); - self.binding.pretty(p); - if let Some(Expr::Closure(closure)) = &self.init { + self.binding().pretty(p); + if let Some(Expr::Closure(closure)) = &self.init() { p.push('('); - p.join(closure.params.iter(), ", ", |item, p| item.pretty(p)); + p.join(closure.params().iter(), ", ", |item, p| item.pretty(p)); p.push_str(") = "); - closure.body.pretty(p); - } else if let Some(init) = &self.init { + closure.body().pretty(p); + } else if let Some(init) = &self.init() { p.push_str(" = "); init.pretty(p); } @@ -425,10 +433,10 @@ impl Pretty for LetExpr { impl Pretty for IfExpr { fn pretty(&self, p: &mut Printer) { p.push_str("if "); - self.condition.pretty(p); + self.condition().pretty(p); p.push(' '); - self.if_body.pretty(p); - if let Some(expr) = &self.else_body { + self.if_body().pretty(p); + if let Some(expr) = &self.else_body() { p.push_str(" else "); expr.pretty(p); } @@ -438,42 +446,40 @@ impl Pretty for IfExpr { impl Pretty for WhileExpr { fn pretty(&self, p: &mut Printer) { p.push_str("while "); - self.condition.pretty(p); + self.condition().pretty(p); p.push(' '); - self.body.pretty(p); + self.body().pretty(p); } } impl Pretty for ForExpr { fn pretty(&self, p: &mut Printer) { p.push_str("for "); - self.pattern.pretty(p); + self.pattern().pretty(p); p.push_str(" in "); - self.iter.pretty(p); + self.iter().pretty(p); p.push(' '); - self.body.pretty(p); + self.body().pretty(p); } } impl Pretty for ForPattern { fn pretty(&self, p: &mut Printer) { - match self { - Self::Value(v) => v.pretty(p), - Self::KeyValue(k, v) => { - k.pretty(p); - p.push_str(", "); - v.pretty(p); - } + if let Some(key) = self.key() { + key.pretty(p); + p.push_str(", "); } + + self.value().pretty(p); } } impl Pretty for ImportExpr { fn pretty(&self, p: &mut Printer) { p.push_str("import "); - self.imports.pretty(p); + self.imports().pretty(p); p.push_str(" from "); - self.path.pretty(p); + self.path().pretty(p); } } @@ -489,7 +495,7 @@ impl Pretty for Imports { impl Pretty for IncludeExpr { fn pretty(&self, p: &mut Printer) { p.push_str("include "); - self.path.pretty(p); + self.path().pretty(p); } } @@ -502,7 +508,6 @@ impl Pretty for Ident { #[cfg(test)] mod tests { use super::*; - use crate::parse::parse; use crate::source::SourceFile; #[track_caller] @@ -513,7 +518,7 @@ mod tests { #[track_caller] fn test_parse(src: &str, expected: &str) { let source = SourceFile::detached(src); - let ast = parse(&source).unwrap(); + let ast: Markup = source.ast().unwrap(); let found = pretty(&ast); if found != expected { println!("tree: {:#?}", ast); diff --git a/src/syntax/span.rs b/src/syntax/span.rs index bfb9e755..ee7cba4c 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -109,6 +109,11 @@ impl Span { *self = self.join(other) } + /// Test whether a position is within the span. + pub fn contains_pos(&self, pos: Pos) -> bool { + self.start <= pos && self.end >= pos + } + /// Test whether one span complete contains the other span. pub fn contains(self, other: Self) -> bool { self.source == other.source && self.start <= other.start && self.end >= other.end @@ -118,6 +123,16 @@ impl Span { pub fn to_range(self) -> Range { self.start.to_usize() .. self.end.to_usize() } + + /// A new span at the position of this span's start. + pub fn at_start(&self) -> Span { + Self::at(self.source, self.start) + } + + /// A new span at the position of this span's end. + pub fn at_end(&self) -> Span { + Self::at(self.source, self.end) + } } impl Debug for Span { diff --git a/src/syntax/token.rs b/src/syntax/token.rs index 22dd104b..49613667 100644 --- a/src/syntax/token.rs +++ b/src/syntax/token.rs @@ -1,188 +1,38 @@ -use crate::geom::{AngularUnit, LengthUnit}; - -/// A minimal semantic entity of source code. -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum Token<'s> { - /// 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, - /// An underscore: `_`. - Underscore, - /// A tilde: `~`. - Tilde, - /// Two hyphens: `--`. - HyphHyph, - /// Three hyphens: `---`. - HyphHyphHyph, - /// A backslash followed by nothing or whitespace: `\`. - Backslash, - /// A comma: `,`. - Comma, - /// A semicolon: `;`. - Semicolon, - /// A colon: `:`. - Colon, - /// A plus: `+`. - Plus, - /// A hyphen: `-`. - Hyph, - /// 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 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. - /// - /// The contained `usize` denotes the number of newlines that were contained - /// in the whitespace. - Space(usize), - /// A consecutive non-markup string. - Text(&'s str), - /// A slash and the letter "u" followed by a hexadecimal unicode entity - /// enclosed in curly braces: `\u{1F5FA}`. - UnicodeEscape(UnicodeEscapeToken<'s>), - /// An arbitrary number of backticks followed by inner contents, terminated - /// with the same number of backticks: `` `...` ``. - Raw(RawToken<'s>), - /// One or two dollar signs followed by inner contents, terminated with the - /// same number of dollar signs. - Math(MathToken<'s>), - /// A numbering: `23.`. - /// - /// Can also exist without the number: `.`. - Numbering(Option), - /// An identifier: `center`. - Ident(&'s str), - /// 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). - Percent(f64), - /// A fraction unit: `3fr`. - Fraction(f64), - /// A quoted string: `"..."`. - Str(StrToken<'s>), - /// Two slashes followed by inner contents, terminated with a newline: - /// `//\n`. - LineComment(&'s str), - /// A slash and a star followed by inner contents, terminated with a star - /// and a slash: `/**/`. - /// - /// The comment can contain nested block comments. - BlockComment(&'s str), - /// Things that are not valid tokens. - Invalid(&'s str), -} +use crate::util::EcoString; /// A quoted string token: `"..."`. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct StrToken<'s> { +#[derive(Debug, Clone, PartialEq)] +pub struct StrToken { /// The string inside the quotes. /// /// _Note_: If the string contains escape sequences these are not yet /// applied to be able to just store a string slice here instead of /// a `String`. The resolving is done later in the parser. - pub string: &'s str, + pub string: EcoString, /// Whether the closing quote was present. pub terminated: bool, } /// A raw block token: `` `...` ``. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct RawToken<'s> { - /// The raw text between the backticks. - pub text: &'s str, +#[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: usize, + pub backticks: u8, /// Whether all closing backticks were present. pub terminated: bool, + /// Whether to display this as a block. + pub block: bool, } /// A math formula token: `$2pi + x$` or `$[f'(x) = x^2]$`. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct MathToken<'s> { +#[derive(Debug, Clone, PartialEq)] +pub struct MathToken { /// The formula between the dollars. - pub formula: &'s str, + pub formula: EcoString, /// Whether the formula is display-level, that is, it is surrounded by /// `$[..]`. pub display: bool, @@ -191,86 +41,21 @@ pub struct MathToken<'s> { } /// A unicode escape sequence token: `\u{1F5FA}`. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct UnicodeEscapeToken<'s> { +#[derive(Debug, Clone, PartialEq)] +pub struct UnicodeEscapeToken { /// The escape sequence between the braces. - pub sequence: &'s str, + pub sequence: EcoString, + /// The resulting unicode character. + pub character: Option, /// Whether the closing brace was present. pub terminated: bool, } -impl<'s> Token<'s> { - /// The English name of this token for use in error messages. - pub fn name(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::Underscore => "underscore", - Self::Tilde => "tilde", - Self::HyphHyph => "en dash", - Self::HyphHyphHyph => "em dash", - Self::Backslash => "backslash", - Self::Comma => "comma", - Self::Semicolon => "semicolon", - Self::Colon => "colon", - Self::Plus => "plus", - Self::Hyph => "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::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::Text(_) => "text", - Self::UnicodeEscape(_) => "unicode escape sequence", - Self::Raw(_) => "raw block", - Self::Math(_) => "math formula", - Self::Numbering(_) => "numbering", - Self::Ident(_) => "identifier", - Self::Bool(_) => "boolean", - Self::Int(_) => "integer", - Self::Float(_) => "float", - Self::Length(_, _) => "length", - Self::Angle(_, _) => "angle", - Self::Percent(_) => "percentage", - Self::Fraction(_) => "`fr` value", - Self::Str(_) => "string", - Self::LineComment(_) => "line comment", - Self::BlockComment(_) => "block comment", - Self::Invalid("*/") => "end of block comment", - Self::Invalid(_) => "invalid token", - } - } +/// A unit-bound number token: `1.2em`. +#[derive(Debug, Clone, PartialEq)] +pub struct UnitToken { + /// The number part. + pub number: std::ops::Range, + /// The unit part. + pub unit: std::ops::Range, } diff --git a/src/syntax/visit.rs b/src/syntax/visit.rs deleted file mode 100644 index 40e8eb93..00000000 --- a/src/syntax/visit.rs +++ /dev/null @@ -1,263 +0,0 @@ -//! Mutable and immutable syntax tree traversal. - -use super::*; - -/// Implement the immutable and the mutable visitor version. -macro_rules! impl_visitors { - ($($name:ident($($tts:tt)*) $body:block)*) => { - macro_rules! r { - (rc: $x:expr) => { $x.as_ref() }; - ($x:expr) => { &$x }; - } - - impl_visitor! { - Visit, - immutable, - immutably, - [$(($name($($tts)*) $body))*] - } - - macro_rules! r { - (rc: $x:expr) => { std::rc::Rc::make_mut(&mut $x) }; - ($x:expr) => { &mut $x }; - } - - impl_visitor! { - VisitMut, - mutable, - mutably, - [$(($name($($tts)*) $body mut))*] mut - } - }; -} - -/// Implement an immutable or mutable visitor. -macro_rules! impl_visitor { - ( - $visit:ident, - $mutability:ident, - $adjective:ident, - [$(( - $name:ident($v:ident, $node:ident: $ty:ty) - $body:block - $($fmut:tt)? - ))*] - $($mut:tt)? - ) => { - #[doc = concat!("Visit syntax trees ", stringify!($adjective), ".")] - pub trait $visit<'ast> { - /// Visit a definition of a binding. - /// - /// Bindings are, for example, left-hand side of let expressions, - /// and key/value patterns in for loops. - fn visit_binding(&mut self, _: &'ast $($mut)? Ident) {} - - /// Visit the entry into a scope. - fn visit_enter(&mut self) {} - - /// Visit the exit from a scope. - fn visit_exit(&mut self) {} - - $(fn $name(&mut self, $node: &'ast $($fmut)? $ty) { - $mutability::$name(self, $node); - })* - } - - #[doc = concat!("Visitor functions that are ", stringify!($mutability), ".")] - pub mod $mutability { - use super::*; - $( - #[doc = concat!("Visit a node of type [`", stringify!($ty), "`].")] - pub fn $name<'ast, V>($v: &mut V, $node: &'ast $($fmut)? $ty) - where - V: $visit<'ast> + ?Sized - $body - )* - } - }; -} - -impl_visitors! { - visit_tree(v, markup: Markup) { - for node in markup { - v.visit_node(node); - } - } - - visit_node(v, node: MarkupNode) { - match node { - MarkupNode::Space => {} - MarkupNode::Linebreak(_) => {} - MarkupNode::Parbreak(_) => {} - MarkupNode::Strong(_) => {} - MarkupNode::Emph(_) => {} - MarkupNode::Text(_) => {} - MarkupNode::Raw(_) => {} - MarkupNode::Heading(n) => v.visit_heading(n), - MarkupNode::List(n) => v.visit_list(n), - MarkupNode::Enum(n) => v.visit_enum(n), - MarkupNode::Expr(n) => v.visit_expr(n), - } - } - - visit_heading(v, heading: HeadingNode) { - v.visit_tree(r!(heading.body)); - } - - visit_list(v, list: ListNode) { - v.visit_tree(r!(list.body)); - } - - visit_enum(v, enum_: EnumNode) { - v.visit_tree(r!(enum_.body)); - } - - visit_expr(v, expr: Expr) { - match expr { - Expr::Ident(_) => {} - Expr::Lit(_) => {}, - Expr::Array(e) => v.visit_array(e), - Expr::Dict(e) => v.visit_dict(e), - Expr::Template(e) => v.visit_template(e), - Expr::Group(e) => v.visit_group(e), - Expr::Block(e) => v.visit_block(e), - Expr::Unary(e) => v.visit_unary(e), - Expr::Binary(e) => v.visit_binary(e), - Expr::Call(e) => v.visit_call(e), - Expr::Closure(e) => v.visit_closure(e), - Expr::With(e) => v.visit_with(e), - Expr::Let(e) => v.visit_let(e), - Expr::If(e) => v.visit_if(e), - Expr::While(e) => v.visit_while(e), - Expr::For(e) => v.visit_for(e), - Expr::Import(e) => v.visit_import(e), - Expr::Include(e) => v.visit_include(e), - } - } - - visit_array(v, array: ArrayExpr) { - for expr in r!(array.items) { - v.visit_expr(expr); - } - } - - visit_dict(v, dict: DictExpr) { - for named in r!(dict.items) { - v.visit_expr(r!(named.expr)); - } - } - - visit_template(v, template: TemplateExpr) { - v.visit_enter(); - v.visit_tree(r!(template.body)); - v.visit_exit(); - } - - visit_group(v, group: GroupExpr) { - v.visit_expr(r!(group.expr)); - } - - visit_block(v, block: BlockExpr) { - v.visit_enter(); - for expr in r!(block.exprs) { - v.visit_expr(expr); - } - v.visit_exit(); - } - - visit_binary(v, binary: BinaryExpr) { - v.visit_expr(r!(binary.lhs)); - v.visit_expr(r!(binary.rhs)); - } - - visit_unary(v, unary: UnaryExpr) { - v.visit_expr(r!(unary.expr)); - } - - visit_call(v, call: CallExpr) { - v.visit_expr(r!(call.callee)); - v.visit_args(r!(call.args)); - } - - visit_args(v, args: CallArgs) { - for arg in r!(args.items) { - v.visit_arg(arg); - } - } - - visit_arg(v, arg: CallArg) { - match arg { - CallArg::Pos(expr) => v.visit_expr(expr), - CallArg::Named(named) => v.visit_expr(r!(named.expr)), - CallArg::Spread(expr) => v.visit_expr(expr), - } - } - - visit_closure(v, closure: ClosureExpr) { - for param in r!(closure.params) { - v.visit_param(param); - } - v.visit_expr(r!(rc: closure.body)); - } - - visit_param(v, param: ClosureParam) { - match param { - ClosureParam::Pos(binding) => v.visit_binding(binding), - ClosureParam::Named(named) => { - v.visit_binding(r!(named.name)); - v.visit_expr(r!(named.expr)); - } - ClosureParam::Sink(binding) => v.visit_binding(binding), - } - } - - visit_with(v, with_expr: WithExpr) { - v.visit_expr(r!(with_expr.callee)); - v.visit_args(r!(with_expr.args)); - } - - visit_let(v, let_expr: LetExpr) { - if let Some(init) = r!(let_expr.init) { - v.visit_expr(init); - } - v.visit_binding(r!(let_expr.binding)); - } - - visit_if(v, if_expr: IfExpr) { - v.visit_expr(r!(if_expr.condition)); - v.visit_expr(r!(if_expr.if_body)); - if let Some(body) = r!(if_expr.else_body) { - v.visit_expr(body); - } - } - - visit_while(v, while_expr: WhileExpr) { - v.visit_expr(r!(while_expr.condition)); - v.visit_expr(r!(while_expr.body)); - } - - visit_for(v, for_expr: ForExpr) { - v.visit_expr(r!(for_expr.iter)); - match r!(for_expr.pattern) { - ForPattern::Value(value) => v.visit_binding(value), - ForPattern::KeyValue(key, value) => { - v.visit_binding(key); - v.visit_binding(value); - } - } - v.visit_expr(r!(for_expr.body)); - } - - visit_import(v, import_expr: ImportExpr) { - v.visit_expr(r!(import_expr.path)); - if let Imports::Idents(idents) = r!(import_expr.imports) { - for ident in idents { - v.visit_binding(ident); - } - } - } - - visit_include(v, include_expr: IncludeExpr) { - v.visit_expr(r!(include_expr.path)); - } -} -- cgit v1.2.3 From 84d35efee38d137a77e368c50421ac24327371c6 Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Sun, 31 Oct 2021 11:46:12 +0100 Subject: Less owning, more iterating --- src/syntax/expr.rs | 173 ++++++++++++++++++------------------ src/syntax/ident.rs | 11 ++- src/syntax/markup.rs | 104 ++++++++++------------ src/syntax/mod.rs | 241 ++++++++++++++++++++++++++------------------------- src/syntax/pretty.rs | 41 +++++---- 5 files changed, 282 insertions(+), 288 deletions(-) (limited to 'src/syntax') diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index d0d0c62f..8562a3a4 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -1,4 +1,4 @@ -use super::{Ident, Markup, NodeKind, RedNode, RedTicket, Span, TypedNode}; +use super::{Ident, Markup, NodeKind, RedNode, RedRef, Span, TypedNode}; use crate::geom::{AngularUnit, LengthUnit}; use crate::node; use crate::util::EcoString; @@ -85,7 +85,7 @@ impl Expr { } impl TypedNode for Expr { - fn cast_from(node: RedTicket) -> Option { + fn cast_from(node: RedRef) -> Option { match node.kind() { NodeKind::Ident(_) => Some(Self::Ident(Ident::cast_from(node).unwrap())), NodeKind::Array => Some(Self::Array(ArrayExpr::cast_from(node).unwrap())), @@ -146,18 +146,18 @@ pub enum Lit { } impl TypedNode for Lit { - fn cast_from(node: RedTicket) -> Option { + fn cast_from(node: RedRef) -> Option { match node.kind() { - NodeKind::None => Some(Self::None(node.own().span())), - NodeKind::Auto => Some(Self::Auto(node.own().span())), - NodeKind::Bool(b) => Some(Self::Bool(node.own().span(), *b)), - NodeKind::Int(i) => Some(Self::Int(node.own().span(), *i)), - NodeKind::Float(f) => Some(Self::Float(node.own().span(), *f)), - NodeKind::Length(f, unit) => Some(Self::Length(node.own().span(), *f, *unit)), - NodeKind::Angle(f, unit) => Some(Self::Angle(node.own().span(), *f, *unit)), - NodeKind::Percentage(f) => Some(Self::Percent(node.own().span(), *f)), - NodeKind::Fraction(f) => Some(Self::Fractional(node.own().span(), *f)), - NodeKind::Str(s) => Some(Self::Str(node.own().span(), s.string.clone())), + 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, } } @@ -180,34 +180,34 @@ impl Lit { } } -node!( +node! { /// An array expression: `(1, "hi", 12cm)`. Array => ArrayExpr -); +} impl ArrayExpr { /// The array items. - pub fn items(&self) -> Vec { - self.0.children().filter_map(RedTicket::cast).collect() + pub fn items<'a>(&'a self) -> impl Iterator + 'a { + self.0.children().filter_map(RedRef::cast) } } -node!( +node! { /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. Dict => DictExpr -); +} impl DictExpr { /// The named dictionary items. - pub fn items(&self) -> Vec { - self.0.children().filter_map(RedTicket::cast).collect() + pub fn items<'a>(&'a self) -> impl Iterator + 'a { + self.0.children().filter_map(RedRef::cast) } } -node!( +node! { /// A pair of a name and an expression: `pattern: dashed`. Named -); +} impl Named { /// The name: `pattern`. @@ -219,16 +219,16 @@ impl Named { pub fn expr(&self) -> Expr { self.0 .children() - .filter_map(RedTicket::cast) + .filter_map(RedRef::cast) .nth(1) .expect("named pair is missing expression") } } -node!( +node! { /// A template expression: `[*Hi* there!]`. Template => TemplateExpr -); +} impl TemplateExpr { /// The contents of the template. @@ -239,10 +239,10 @@ impl TemplateExpr { } } -node!( +node! { /// A grouped expression: `(1 + 2)`. Group => GroupExpr -); +} impl GroupExpr { /// The wrapped expression. @@ -253,22 +253,22 @@ impl GroupExpr { } } -node!( +node! { /// A block expression: `{ let x = 1; x + 2 }`. Block => BlockExpr -); +} impl BlockExpr { /// The list of expressions contained in the block. - pub fn exprs(&self) -> Vec { - self.0.children().filter_map(RedTicket::cast).collect() + pub fn exprs<'a>(&'a self) -> impl Iterator + 'a { + self.0.children().filter_map(RedRef::cast) } } -node!( +node! { /// A unary operation: `-x`. Unary => UnaryExpr -); +} impl UnaryExpr { /// The operator: `-`. @@ -298,7 +298,7 @@ pub enum UnOp { } impl TypedNode for UnOp { - fn cast_from(node: RedTicket) -> Option { + fn cast_from(node: RedRef) -> Option { Self::from_token(node.kind()) } } @@ -332,10 +332,10 @@ impl UnOp { } } -node!( +node! { /// A binary operation: `a + b`. Binary => BinaryExpr -); +} impl BinaryExpr { /// The binary operator: `+`. @@ -356,7 +356,7 @@ impl BinaryExpr { pub fn rhs(&self) -> Expr { self.0 .children() - .filter_map(RedTicket::cast) + .filter_map(RedRef::cast) .nth(1) .expect("binary expression is missing right-hand side") } @@ -402,7 +402,7 @@ pub enum BinOp { } impl TypedNode for BinOp { - fn cast_from(node: RedTicket) -> Option { + fn cast_from(node: RedRef) -> Option { Self::from_token(node.kind()) } } @@ -504,10 +504,10 @@ pub enum Associativity { Right, } -node!( +node! { /// An invocation of a function: `foo(...)`. Call => CallExpr -); +} impl CallExpr { /// The function to call. @@ -523,15 +523,15 @@ impl CallExpr { } } -node!( +node! { /// The arguments to a function: `12, draw: false`. CallArgs -); +} impl CallArgs { /// The positional and named arguments. - pub fn items(&self) -> Vec { - self.0.children().filter_map(RedTicket::cast).collect() + pub fn items<'a>(&'a self) -> impl Iterator + 'a { + self.0.children().filter_map(RedRef::cast) } } @@ -547,14 +547,13 @@ pub enum CallArg { } impl TypedNode for CallArg { - fn cast_from(node: RedTicket) -> Option { + 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.own() - .cast_first_child() + node.cast_first_child() .expect("call argument sink is missing expression"), )), _ => Some(CallArg::Pos(node.cast()?)), @@ -573,10 +572,10 @@ impl CallArg { } } -node!( +node! { /// A closure expression: `(x, y) => z`. Closure => ClosureExpr -); +} impl ClosureExpr { /// The name of the closure. @@ -589,15 +588,13 @@ impl ClosureExpr { } /// The parameter bindings. - pub fn params(&self) -> Vec { + pub fn params<'a>(&'a self) -> impl Iterator + 'a { self.0 .children() .find(|x| x.kind() == &NodeKind::ClosureParams) .expect("closure is missing parameter list") - .own() .children() - .filter_map(RedTicket::cast) - .collect() + .filter_map(RedRef::cast) } /// The body of the closure. @@ -607,8 +604,8 @@ impl ClosureExpr { self.0.cast_last_child().expect("closure is missing body") } - /// The ticket of the body of the closure. - pub fn body_ticket(&self) -> RedTicket { + /// 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()) @@ -629,17 +626,16 @@ pub enum ClosureParam { } impl TypedNode for ClosureParam { - fn cast_from(node: RedTicket) -> Option { + fn cast_from(node: RedRef) -> Option { match node.kind() { NodeKind::Ident(i) => { - Some(ClosureParam::Pos(Ident::new(i, node.own().span()).unwrap())) + 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.own() - .cast_first_child() + node.cast_first_child() .expect("closure parameter sink is missing identifier"), )), _ => Some(ClosureParam::Pos(node.cast()?)), @@ -647,10 +643,10 @@ impl TypedNode for ClosureParam { } } -node!( +node! { /// A with expression: `f with (x, y: 1)`. WithExpr -); +} impl WithExpr { /// The function to apply the arguments to. @@ -668,10 +664,10 @@ impl WithExpr { } } -node!( +node! { /// A let expression: `let x = 1`. LetExpr -); +} impl LetExpr { /// The binding to assign to. @@ -693,7 +689,7 @@ impl LetExpr { /// 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(RedTicket::cast).nth(1) + self.0.children().filter_map(RedRef::cast).nth(1) } else { Some( self.0 @@ -703,8 +699,9 @@ impl LetExpr { } } - /// The ticket for the expression the binding is initialized with. - pub fn init_ticket(&self) -> RedTicket { + /// 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 { @@ -714,10 +711,10 @@ impl LetExpr { } } -node!( +node! { /// An import expression: `import a, b, c from "utils.typ"`. ImportExpr -); +} impl ImportExpr { /// The items to be imported. @@ -745,11 +742,11 @@ pub enum Imports { } impl TypedNode for Imports { - fn cast_from(node: RedTicket) -> Option { + fn cast_from(node: RedRef) -> Option { match node.kind() { NodeKind::Star => Some(Imports::Wildcard), NodeKind::ImportItems => { - let idents = node.own().children().filter_map(RedTicket::cast).collect(); + let idents = node.children().filter_map(RedRef::cast).collect(); Some(Imports::Idents(idents)) } _ => None, @@ -757,10 +754,10 @@ impl TypedNode for Imports { } } -node!( +node! { /// An include expression: `include "chapter1.typ"`. IncludeExpr -); +} impl IncludeExpr { /// The location of the file to be included. @@ -771,10 +768,10 @@ impl IncludeExpr { } } -node!( +node! { /// An if-else expression: `if x { y } else { z }`. IfExpr -); +} impl IfExpr { /// The condition which selects the body to evaluate. @@ -788,21 +785,21 @@ impl IfExpr { pub fn if_body(&self) -> Expr { self.0 .children() - .filter_map(RedTicket::cast) + .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(RedTicket::cast).nth(2) + self.0.children().filter_map(RedRef::cast).nth(2) } } -node!( +node! { /// A while loop expression: `while x { y }`. WhileExpr -); +} impl WhileExpr { /// The condition which selects whether to evaluate the body. @@ -816,16 +813,16 @@ impl WhileExpr { pub fn body(&self) -> Expr { self.0 .children() - .filter_map(RedTicket::cast) + .filter_map(RedRef::cast) .nth(1) .expect("while loop expression is missing body") } } -node!( +node! { /// A for loop expression: `for x in y { z }`. ForExpr -); +} impl ForExpr { /// The pattern to assign to. @@ -846,13 +843,13 @@ impl ForExpr { pub fn body(&self) -> Expr { self.0 .children() - .filter_map(RedTicket::cast) + .filter_map(RedRef::cast) .last() .expect("for loop expression is missing body") } - /// The ticket for the expression to evaluate for each iteration. - pub fn body_ticket(&self) -> RedTicket { + /// 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()) @@ -861,14 +858,14 @@ impl ForExpr { } } -node!( +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(RedTicket::cast).collect(); + let mut items: Vec<_> = self.0.children().filter_map(RedRef::cast).collect(); if items.len() > 1 { Some(items.remove(0)) } else { None } } diff --git a/src/syntax/ident.rs b/src/syntax/ident.rs index 2c61329d..f5cc6330 100644 --- a/src/syntax/ident.rs +++ b/src/syntax/ident.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use unicode_xid::UnicodeXID; -use super::{NodeKind, RedTicket, Span, TypedNode}; +use super::{NodeKind, RedRef, Span, TypedNode}; use crate::util::EcoString; /// An unicode identifier with a few extra permissible characters. @@ -67,11 +67,10 @@ impl From<&Ident> for EcoString { } impl TypedNode for Ident { - fn cast_from(node: RedTicket) -> Option { - if let NodeKind::Ident(i) = node.kind() { - Some(Ident::new(i, node.own().span()).unwrap()) - } else { - None + fn cast_from(node: RedRef) -> Option { + match node.kind() { + NodeKind::Ident(i) => Some(Ident::new(i, node.span()).unwrap()), + _ => None, } } } diff --git a/src/syntax/markup.rs b/src/syntax/markup.rs index c12c0e81..de547f76 100644 --- a/src/syntax/markup.rs +++ b/src/syntax/markup.rs @@ -1,4 +1,4 @@ -use super::{Expr, Ident, NodeKind, RedNode, RedTicket, Span, TypedNode}; +use super::{Expr, Ident, NodeKind, RedNode, RedRef, Span, TypedNode}; use crate::node; use crate::util::EcoString; use std::fmt::Write; @@ -7,12 +7,12 @@ use std::fmt::Write; pub type Markup = Vec; impl TypedNode for Markup { - fn cast_from(node: RedTicket) -> Option { + fn cast_from(node: RedRef) -> Option { if node.kind() != &NodeKind::Markup { return None; } - let children = node.own().children().filter_map(TypedNode::cast_from).collect(); + let children = node.children().filter_map(TypedNode::cast_from).collect(); Some(children) } } @@ -45,7 +45,7 @@ pub enum MarkupNode { } impl TypedNode for MarkupNode { - fn cast_from(node: RedTicket) -> Option { + fn cast_from(node: RedRef) -> Option { match node.kind() { NodeKind::Space(_) => Some(MarkupNode::Space), NodeKind::Linebreak => Some(MarkupNode::Linebreak), @@ -53,15 +53,14 @@ impl TypedNode for MarkupNode { NodeKind::Strong => Some(MarkupNode::Strong), NodeKind::Emph => Some(MarkupNode::Emph), NodeKind::Text(s) => Some(MarkupNode::Text(s.clone())), - NodeKind::UnicodeEscape(u) => { - Some(MarkupNode::Text(if let Some(s) = u.character { - s.into() - } else { + NodeKind::UnicodeEscape(u) => Some(MarkupNode::Text(match u.character { + Some(c) => c.into(), + None => { let mut eco = EcoString::with_capacity(u.sequence.len() + 4); write!(&mut eco, "\\u{{{}}}", u.sequence).unwrap(); eco - })) - } + } + })), NodeKind::EnDash => Some(MarkupNode::Text(EcoString::from("\u{2013}"))), NodeKind::EmDash => Some(MarkupNode::Text(EcoString::from("\u{2014}"))), NodeKind::NonBreakingSpace => { @@ -93,28 +92,29 @@ pub struct RawNode { } impl TypedNode for RawNode { - fn cast_from(node: RedTicket) -> Option { - if let NodeKind::Raw(raw) = node.kind() { - let span = node.own().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(), - }) - } else { - None + 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!( +node! { /// A section heading: `= Introduction`. Heading => HeadingNode -); +} impl HeadingNode { /// The contents of the heading. @@ -125,30 +125,21 @@ impl HeadingNode { } /// The section depth (numer of equals signs). - pub fn level(&self) -> HeadingLevel { + pub fn level(&self) -> u8 { self.0 - .cast_first_child() + .children() + .find_map(|node| match node.kind() { + NodeKind::HeadingLevel(heading) => Some(*heading), + _ => None, + }) .expect("heading node is missing heading level") } } -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct HeadingLevel(pub usize); - -impl TypedNode for HeadingLevel { - fn cast_from(node: RedTicket) -> Option { - if let NodeKind::HeadingLevel(l) = node.kind() { - Some(Self((*l).into())) - } else { - None - } - } -} - -node!( +node! { /// An item in an unordered list: `- ...`. List => ListNode -); +} impl ListNode { /// The contents of the list item. @@ -157,10 +148,10 @@ impl ListNode { } } -node!( +node! { /// An item in an enumeration (ordered list): `1. ...`. Enum => EnumNode -); +} impl EnumNode { /// The contents of the list item. @@ -169,20 +160,13 @@ impl EnumNode { } /// The number, if any. - pub fn number(&self) -> EnumNumber { - self.0.cast_first_child().expect("enumeration node is missing number") - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct EnumNumber(pub Option); - -impl TypedNode for EnumNumber { - fn cast_from(node: RedTicket) -> Option { - if let NodeKind::EnumNumbering(x) = node.kind() { - Some(Self(*x)) - } else { - None - } + 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 88757f8e..8e04a569 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -160,8 +160,6 @@ pub enum NodeKind { /// /// The comment can contain nested block comments. BlockComment, - /// A node that should never appear in a finished tree. - Never, /// Tokens that appear in the wrong place. Error(ErrorPosition, EcoString), /// Template markup. @@ -246,7 +244,41 @@ pub enum ErrorPosition { impl Display for NodeKind { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.pad(match self { + 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", @@ -296,7 +328,6 @@ impl Display for NodeKind { Self::Math(_) => "math formula", Self::EnumNumbering(_) => "numbering", Self::Str(_) => "string", - Self::Never => "a node that should not be here", Self::LineComment => "line comment", Self::BlockComment => "block comment", Self::Markup => "markup", @@ -348,45 +379,15 @@ impl Display for NodeKind { "*/" => "end of block comment", _ => "invalid token", }, - }) - } -} - -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::Never | NodeKind::Error(_, _)) - } } /// A syntactical node. #[derive(Clone, PartialEq)] pub struct GreenNode { /// Node metadata. - meta: GreenData, + data: GreenData, /// This node's children, losslessly make up this node. children: Vec, } @@ -400,12 +401,12 @@ pub struct GreenData { /// The byte length of the node in the source. len: usize, /// Whether this node or any of its children are erroneous. - has_error: bool, + erroneous: bool, } impl GreenData { pub fn new(kind: NodeKind, len: usize) -> Self { - Self { len, has_error: kind.is_error(), kind } + Self { len, erroneous: kind.is_error(), kind } } pub fn kind(&self) -> &NodeKind { @@ -416,8 +417,8 @@ impl GreenData { self.len } - pub fn has_error(&self) -> bool { - self.has_error + pub fn erroneous(&self) -> bool { + self.erroneous } } @@ -437,23 +438,23 @@ pub enum Green { } impl Green { - fn meta(&self) -> &GreenData { + fn data(&self) -> &GreenData { match self { Green::Token(t) => &t, - Green::Node(n) => &n.meta, + Green::Node(n) => &n.data, } } pub fn kind(&self) -> &NodeKind { - self.meta().kind() + self.data().kind() } pub fn len(&self) -> usize { - self.meta().len() + self.data().len() } - pub fn has_error(&self) -> bool { - self.meta().has_error() + pub fn erroneous(&self) -> bool { + self.data().erroneous() } pub fn children(&self) -> &[Green] { @@ -467,29 +468,19 @@ impl Green { impl GreenNode { pub fn new(kind: NodeKind, len: usize) -> Self { Self { - meta: GreenData::new(kind, len), + data: GreenData::new(kind, len), children: Vec::new(), } } - pub fn with_children( - kind: NodeKind, - len: usize, - children: impl Iterator>, - ) -> Self { + pub fn with_children(kind: NodeKind, len: usize, children: Vec) -> Self { let mut meta = GreenData::new(kind, len); - let children = children - .map(|x| { - let x = x.into(); - meta.has_error |= x.has_error(); - x - }) - .collect(); - Self { meta, children } + 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, std::iter::once(child.into())) + Self::with_children(kind, len, vec![child.into()]) } pub fn children(&self) -> &[Green] { @@ -511,7 +502,7 @@ impl From> for Green { impl Default for Green { fn default() -> Self { - Self::Token(GreenData::new(NodeKind::Never, 0)) + Self::Token(GreenData::new(NodeKind::None, 0)) } } @@ -530,13 +521,13 @@ impl Debug for Green { } #[derive(Copy, Clone, PartialEq)] -pub struct RedTicket<'a> { +pub struct RedRef<'a> { id: SourceId, offset: usize, green: &'a Green, } -impl<'a> RedTicket<'a> { +impl<'a> RedRef<'a> { pub fn own(self) -> RedNode { RedNode { id: self.id, @@ -549,6 +540,9 @@ impl<'a> RedTicket<'a> { 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 @@ -556,6 +550,37 @@ impl<'a> RedTicket<'a> { { 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() + } } #[derive(Clone, PartialEq)] @@ -571,7 +596,7 @@ impl RedNode { } pub fn span(&self) -> Span { - Span::new(self.id, self.offset, self.offset + self.green.len()) + self.as_ref().span() } pub fn len(&self) -> usize { @@ -582,53 +607,36 @@ impl RedNode { self.green.kind() } - pub fn children<'a>(&'a self) -> impl Iterator> + Clone + 'a { - let children = match &self.green { - Green::Node(node) => node.children(), - Green::Token(_) => &[], - }; - - let mut offset = self.offset; - children.iter().map(move |green_child| { - let child_offset = offset; - offset += green_child.len(); - RedTicket { - id: self.id, - offset: child_offset, - green: &green_child, - } - }) - } - - pub fn has_error(&self) -> bool { - self.green.has_error() + pub fn children<'a>(&'a self) -> impl Iterator> + Clone { + self.as_ref().children() } pub fn errors(&self) -> Vec<(Span, EcoString)> { - if !self.green.has_error() { + if !self.green.erroneous() { return vec![]; } - if let NodeKind::Error(pos, msg) = self.kind() { - let span = match pos { - ErrorPosition::Start => self.span().at_start(), - ErrorPosition::Full => self.span(), - ErrorPosition::End => self.span().at_end(), - }; - - vec![(span, msg.clone())] - } else if let NodeKind::Never = self.kind() { - vec![(self.span(), "found a never node".into())] - } else { - self.children() - .filter(|ticket| ticket.green.has_error()) - .flat_map(|ticket| ticket.own().errors()) - .collect() + 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 ticket<'a>(&'a self) -> RedTicket<'a> { - RedTicket { + pub fn as_ref<'a>(&'a self) -> RedRef<'a> { + RedRef { id: self.id, offset: self.offset, green: &self.green, @@ -636,28 +644,26 @@ impl RedNode { } pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { - self.children() - .find(|x| mem::discriminant(x.kind()) == mem::discriminant(kind)) - .map(RedTicket::own) + self.as_ref().typed_child(kind).map(RedRef::own) } pub(crate) fn cast_first_child(&self) -> Option { - self.children().find_map(RedTicket::cast) + self.as_ref().cast_first_child() } pub(crate) fn cast_last_child(&self) -> Option { - self.children().filter_map(RedTicket::cast).last() + self.as_ref().cast_last_child() } } impl Debug for RedNode { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{:?}: {:?}", self.kind(), self.span())?; - let children = self.children().collect::>(); + let children = self.as_ref().children().collect::>(); if !children.is_empty() { f.write_str(" ")?; f.debug_list() - .entries(children.into_iter().map(RedTicket::own)) + .entries(children.into_iter().map(RedRef::own)) .finish()?; } Ok(()) @@ -666,21 +672,22 @@ impl Debug for RedNode { pub trait TypedNode: Sized { /// Performs the conversion. - fn cast_from(value: RedTicket) -> Option; + fn cast_from(value: RedRef) -> Option; } #[macro_export] macro_rules! node { - (#[doc = $doc:expr] $name:ident) => { - node!(#[doc = $doc] $name => $name); + ($(#[$attr:meta])* $name:ident) => { + node!{$(#[$attr])* $name => $name} }; - (#[doc = $doc:expr] $variant:ident => $name:ident) => { - #[doc = $doc] + ($(#[$attr:meta])* $variant:ident => $name:ident) => { #[derive(Debug, Clone, PartialEq)] + #[repr(transparent)] + $(#[$attr])* pub struct $name(RedNode); impl TypedNode for $name { - fn cast_from(node: RedTicket) -> Option { + fn cast_from(node: RedRef) -> Option { if node.kind() != &NodeKind::$variant { return None; } @@ -694,8 +701,8 @@ macro_rules! node { self.0.span() } - pub fn underlying(&self) -> RedTicket { - self.0.ticket() + pub fn underlying(&self) -> RedRef { + self.0.as_ref() } } }; diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index b1c7e02b..db364eaa 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -46,20 +46,25 @@ impl Printer { Write::write_fmt(self, fmt) } - /// Write a list of items joined by a joiner. - pub fn join(&mut self, items: I, joiner: &str, mut write_item: F) + /// Write a list of items joined by a joiner and return how many there were. + pub fn join(&mut self, items: I, joiner: &str, mut write_item: F) -> usize where I: IntoIterator, F: FnMut(T, &mut Self), { + let mut count = 0; let mut iter = items.into_iter(); if let Some(first) = iter.next() { write_item(first, self); + count += 1; } for item in iter { self.push_str(joiner); write_item(item, self); + count += 1; } + + count } /// Finish pretty printing and return the underlying buffer. @@ -165,7 +170,7 @@ impl Pretty for RawNode { impl Pretty for HeadingNode { fn pretty(&self, p: &mut Printer) { - for _ in 0 .. self.level().0 { + for _ in 0 .. self.level() { p.push('='); } p.push(' '); @@ -182,7 +187,7 @@ impl Pretty for ListNode { impl Pretty for EnumNode { fn pretty(&self, p: &mut Printer) { - if let Some(number) = self.number().0 { + if let Some(number) = self.number() { write!(p, "{}", number).unwrap(); } p.push_str(". "); @@ -237,8 +242,8 @@ impl Pretty for ArrayExpr { p.push('('); let items = self.items(); - p.join(&items, ", ", |item, p| item.pretty(p)); - if items.len() == 1 { + let len = p.join(items, ", ", |item, p| item.pretty(p)); + if len == 1 { p.push(','); } p.push(')'); @@ -249,11 +254,11 @@ impl Pretty for DictExpr { fn pretty(&self, p: &mut Printer) { p.push('('); - let items = self.items(); - if items.is_empty() { + let mut items = self.items().peekable(); + if items.peek().is_none() { p.push(':'); } else { - p.join(&items, ", ", |named, p| named.pretty(p)); + p.join(items, ", ", |named, p| named.pretty(p)); } p.push(')'); } @@ -287,7 +292,7 @@ impl Pretty for BlockExpr { fn pretty(&self, p: &mut Printer) { p.push('{'); - let exprs = self.exprs(); + let exprs: Vec<_> = self.exprs().collect(); if exprs.len() > 1 { p.push(' '); } @@ -342,8 +347,7 @@ impl Pretty for CallExpr { p.push(')'); }; - let arg_list = self.args(); - let args = arg_list.items(); + let args: Vec<_> = self.args().items().collect(); if let Some(Expr::Template(template)) = args .last() @@ -361,7 +365,7 @@ impl Pretty for CallExpr { impl Pretty for CallArgs { fn pretty(&self, p: &mut Printer) { - p.join(&self.items(), ", ", |item, p| item.pretty(p)); + p.join(self.items(), ", ", |item, p| item.pretty(p)); } } @@ -380,11 +384,12 @@ impl Pretty for CallArg { impl Pretty for ClosureExpr { fn pretty(&self, p: &mut Printer) { - if let [param] = self.params().as_slice() { + let params: Vec<_> = self.params().collect(); + if let [param] = params.as_slice() { param.pretty(p); } else { p.push('('); - p.join(self.params().iter(), ", ", |item, p| item.pretty(p)); + p.join(params.iter(), ", ", |item, p| item.pretty(p)); p.push(')'); } p.push_str(" => "); @@ -420,7 +425,7 @@ impl Pretty for LetExpr { self.binding().pretty(p); if let Some(Expr::Closure(closure)) = &self.init() { p.push('('); - p.join(closure.params().iter(), ", ", |item, p| item.pretty(p)); + p.join(closure.params(), ", ", |item, p| item.pretty(p)); p.push_str(") = "); closure.body().pretty(p); } else if let Some(init) = &self.init() { @@ -487,7 +492,9 @@ impl Pretty for Imports { fn pretty(&self, p: &mut Printer) { match self { Self::Wildcard => p.push('*'), - Self::Idents(idents) => p.join(idents, ", ", |item, p| item.pretty(p)), + Self::Idents(idents) => { + p.join(idents, ", ", |item, p| item.pretty(p)); + } } } } -- cgit v1.2.3 From 1c0ac793d2b9c403f1a8fa60a3748f4ff8623acb Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Sun, 31 Oct 2021 15:01:39 +0100 Subject: Slim `NodeKind` memory footprint --- src/syntax/mod.rs | 6 +++--- src/syntax/token.rs | 22 +--------------------- 2 files changed, 4 insertions(+), 24 deletions(-) (limited to 'src/syntax') diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 8e04a569..ca5b6a1b 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -121,12 +121,12 @@ pub enum NodeKind { Text(EcoString), /// A slash and the letter "u" followed by a hexadecimal unicode entity /// enclosed in curly braces: `\u{1F5FA}`. - UnicodeEscape(UnicodeEscapeToken), + UnicodeEscape(Rc), /// An arbitrary number of backticks followed by inner contents, terminated /// with the same number of backticks: `` `...` ``. - Raw(RawToken), + Raw(Rc), /// Dollar signs surrounding inner contents. - Math(MathToken), + Math(Rc), /// A numbering: `23.`. /// /// Can also exist without the number: `.`. diff --git a/src/syntax/token.rs b/src/syntax/token.rs index 49613667..5a621495 100644 --- a/src/syntax/token.rs +++ b/src/syntax/token.rs @@ -2,15 +2,10 @@ use crate::util::EcoString; /// A quoted string token: `"..."`. #[derive(Debug, Clone, PartialEq)] +#[repr(transparent)] pub struct StrToken { /// The string inside the quotes. - /// - /// _Note_: If the string contains escape sequences these are not yet - /// applied to be able to just store a string slice here instead of - /// a `String`. The resolving is done later in the parser. pub string: EcoString, - /// Whether the closing quote was present. - pub terminated: bool, } /// A raw block token: `` `...` ``. @@ -22,8 +17,6 @@ pub struct RawToken { pub lang: Option, /// The number of opening backticks. pub backticks: u8, - /// Whether all closing backticks were present. - pub terminated: bool, /// Whether to display this as a block. pub block: bool, } @@ -36,8 +29,6 @@ pub struct MathToken { /// Whether the formula is display-level, that is, it is surrounded by /// `$[..]`. pub display: bool, - /// Whether the closing dollars were present. - pub terminated: bool, } /// A unicode escape sequence token: `\u{1F5FA}`. @@ -47,15 +38,4 @@ pub struct UnicodeEscapeToken { pub sequence: EcoString, /// The resulting unicode character. pub character: Option, - /// Whether the closing brace was present. - pub terminated: bool, -} - -/// A unit-bound number token: `1.2em`. -#[derive(Debug, Clone, PartialEq)] -pub struct UnitToken { - /// The number part. - pub number: std::ops::Range, - /// The unit part. - pub unit: std::ops::Range, } -- cgit v1.2.3 From c569e14c07902b23b7b3e29df4076cea1f4496cf Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Sun, 31 Oct 2021 16:22:33 +0100 Subject: Improve error handling --- src/syntax/expr.rs | 44 ++++++++++++++++++-------------------------- src/syntax/markup.rs | 29 ++++++++++++----------------- src/syntax/mod.rs | 5 ++++- src/syntax/pretty.rs | 2 +- 4 files changed, 35 insertions(+), 45 deletions(-) (limited to 'src/syntax') diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 8562a3a4..1439cbdb 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -87,32 +87,24 @@ impl Expr { impl TypedNode for Expr { fn cast_from(node: RedRef) -> Option { match node.kind() { - NodeKind::Ident(_) => Some(Self::Ident(Ident::cast_from(node).unwrap())), - NodeKind::Array => Some(Self::Array(ArrayExpr::cast_from(node).unwrap())), - NodeKind::Dict => Some(Self::Dict(DictExpr::cast_from(node).unwrap())), - NodeKind::Template => { - Some(Self::Template(TemplateExpr::cast_from(node).unwrap())) - } - NodeKind::Group => Some(Self::Group(GroupExpr::cast_from(node).unwrap())), - NodeKind::Block => Some(Self::Block(BlockExpr::cast_from(node).unwrap())), - NodeKind::Unary => Some(Self::Unary(UnaryExpr::cast_from(node).unwrap())), - NodeKind::Binary => Some(Self::Binary(BinaryExpr::cast_from(node).unwrap())), - NodeKind::Call => Some(Self::Call(CallExpr::cast_from(node).unwrap())), - NodeKind::Closure => { - Some(Self::Closure(ClosureExpr::cast_from(node).unwrap())) - } - NodeKind::WithExpr => Some(Self::With(WithExpr::cast_from(node).unwrap())), - NodeKind::LetExpr => Some(Self::Let(LetExpr::cast_from(node).unwrap())), - NodeKind::IfExpr => Some(Self::If(IfExpr::cast_from(node).unwrap())), - NodeKind::WhileExpr => Some(Self::While(WhileExpr::cast_from(node).unwrap())), - NodeKind::ForExpr => Some(Self::For(ForExpr::cast_from(node).unwrap())), - NodeKind::ImportExpr => { - Some(Self::Import(ImportExpr::cast_from(node).unwrap())) - } - NodeKind::IncludeExpr => { - Some(Self::Include(IncludeExpr::cast_from(node).unwrap())) - } - _ => Some(Self::Lit(Lit::cast_from(node)?)), + 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), } } } diff --git a/src/syntax/markup.rs b/src/syntax/markup.rs index de547f76..49b2a519 100644 --- a/src/syntax/markup.rs +++ b/src/syntax/markup.rs @@ -3,17 +3,14 @@ use crate::node; use crate::util::EcoString; use std::fmt::Write; -/// The syntactical root capable of representing a full parsed document. -pub type Markup = Vec; - -impl TypedNode for Markup { - fn cast_from(node: RedRef) -> Option { - if node.kind() != &NodeKind::Markup { - return None; - } +node! { + /// The syntactical root capable of representing a full parsed document. + Markup +} - let children = node.children().filter_map(TypedNode::cast_from).collect(); - Some(children) +impl Markup { + pub fn nodes<'a>(&'a self) -> impl Iterator + 'a { + self.0.children().filter_map(RedRef::cast) } } @@ -66,14 +63,12 @@ impl TypedNode for MarkupNode { NodeKind::NonBreakingSpace => { Some(MarkupNode::Text(EcoString::from("\u{00A0}"))) } - NodeKind::Raw(_) => Some(MarkupNode::Raw(RawNode::cast_from(node).unwrap())), - NodeKind::Heading => { - Some(MarkupNode::Heading(HeadingNode::cast_from(node).unwrap())) - } - NodeKind::List => Some(MarkupNode::List(ListNode::cast_from(node).unwrap())), - NodeKind::Enum => Some(MarkupNode::Enum(EnumNode::cast_from(node).unwrap())), + 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, - _ => Some(MarkupNode::Expr(Expr::cast_from(node)?)), + _ => node.cast().map(MarkupNode::Expr), } } } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index ca5b6a1b..afa0ab86 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -162,6 +162,8 @@ pub enum NodeKind { BlockComment, /// Tokens that appear in the wrong place. Error(ErrorPosition, EcoString), + /// Unknown character sequences. + Unknown(EcoString), /// Template markup. Markup, /// A forced line break: `\`. @@ -375,10 +377,11 @@ impl NodeKind { Self::ImportExpr => "import expression", Self::ImportItems => "import items", Self::IncludeExpr => "include expression", - Self::Error(_, src) => match src.as_str() { + Self::Unknown(src) => match src.as_str() { "*/" => "end of block comment", _ => "invalid token", }, + Self::Error(_, _) => "parse error", } } } diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index db364eaa..da0bdd44 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -82,7 +82,7 @@ impl Write for Printer { impl Pretty for Markup { fn pretty(&self, p: &mut Printer) { - for node in self { + for node in self.nodes() { node.pretty(p); } } -- cgit v1.2.3 From 2e7d359e59a45849f53eea6e022ca83295f5a6e7 Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Sun, 31 Oct 2021 18:52:48 +0100 Subject: Unicode escape error moved to tokenizer --- src/syntax/markup.rs | 10 +--------- src/syntax/mod.rs | 2 +- src/syntax/token.rs | 5 ++--- 3 files changed, 4 insertions(+), 13 deletions(-) (limited to 'src/syntax') diff --git a/src/syntax/markup.rs b/src/syntax/markup.rs index 49b2a519..f43a618a 100644 --- a/src/syntax/markup.rs +++ b/src/syntax/markup.rs @@ -1,7 +1,6 @@ use super::{Expr, Ident, NodeKind, RedNode, RedRef, Span, TypedNode}; use crate::node; use crate::util::EcoString; -use std::fmt::Write; node! { /// The syntactical root capable of representing a full parsed document. @@ -50,14 +49,7 @@ impl TypedNode for MarkupNode { NodeKind::Strong => Some(MarkupNode::Strong), NodeKind::Emph => Some(MarkupNode::Emph), NodeKind::Text(s) => Some(MarkupNode::Text(s.clone())), - NodeKind::UnicodeEscape(u) => Some(MarkupNode::Text(match u.character { - Some(c) => c.into(), - None => { - let mut eco = EcoString::with_capacity(u.sequence.len() + 4); - write!(&mut eco, "\\u{{{}}}", u.sequence).unwrap(); - eco - } - })), + 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 => { diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index afa0ab86..9d4beb6c 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -121,7 +121,7 @@ pub enum NodeKind { Text(EcoString), /// A slash and the letter "u" followed by a hexadecimal unicode entity /// enclosed in curly braces: `\u{1F5FA}`. - UnicodeEscape(Rc), + UnicodeEscape(UnicodeEscapeToken), /// An arbitrary number of backticks followed by inner contents, terminated /// with the same number of backticks: `` `...` ``. Raw(Rc), diff --git a/src/syntax/token.rs b/src/syntax/token.rs index 5a621495..4f43bb4f 100644 --- a/src/syntax/token.rs +++ b/src/syntax/token.rs @@ -33,9 +33,8 @@ pub struct MathToken { /// A unicode escape sequence token: `\u{1F5FA}`. #[derive(Debug, Clone, PartialEq)] +#[repr(transparent)] pub struct UnicodeEscapeToken { - /// The escape sequence between the braces. - pub sequence: EcoString, /// The resulting unicode character. - pub character: Option, + pub character: char, } -- cgit v1.2.3 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 From 49fb3cd4e2a5d6997ad4046d3514f154d8c866dd Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Mon, 1 Nov 2021 13:03:18 +0100 Subject: Code Review: Life is Like a Box of Iterators --- src/syntax/ast.rs | 33 +++++++++++- src/syntax/mod.rs | 156 ++++++++++++++++++++++++------------------------------ 2 files changed, 102 insertions(+), 87 deletions(-) (limited to 'src/syntax') diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index bdd0767d..6ca271a9 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -1,8 +1,39 @@ use super::{Ident, NodeKind, RedNode, RedRef, Span, TypedNode}; use crate::geom::{AngularUnit, LengthUnit}; -use crate::node; use crate::util::EcoString; +macro_rules! node { + ($(#[$attr:meta])* $name:ident) => { + node!{$(#[$attr])* $name => $name} + }; + ($(#[$attr:meta])* $variant:ident => $name:ident) => { + #[derive(Debug, Clone, PartialEq)] + #[repr(transparent)] + $(#[$attr])* + pub struct $name(RedNode); + + impl TypedNode for $name { + fn cast_from(node: RedRef) -> Option { + if node.kind() != &NodeKind::$variant { + return None; + } + + Some(Self(node.own())) + } + } + + impl $name { + pub fn span(&self) -> Span { + self.0.span() + } + + pub fn underlying(&self) -> RedRef { + self.0.as_ref() + } + } + }; +} + node! { /// The syntactical root capable of representing a full parsed document. Markup diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 9fd2b21d..ca41d33f 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -15,6 +15,7 @@ pub use ident::*; pub use pretty::*; pub use span::*; +use crate::diag::Error; use crate::geom::{AngularUnit, LengthUnit}; use crate::source::SourceId; use crate::util::EcoString; @@ -94,9 +95,9 @@ impl GreenNode { } 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 } + let mut data = GreenData::new(kind, len); + data.erroneous |= children.iter().any(|c| c.erroneous()); + Self { data, children } } pub fn with_child(kind: NodeKind, len: usize, child: impl Into) -> Self { @@ -180,6 +181,10 @@ impl<'a> RedRef<'a> { Span::new(self.id, self.offset, self.offset + self.green.len()) } + pub fn len(&self) -> usize { + self.green.len() + } + pub fn cast(self) -> Option where T: TypedNode, @@ -205,6 +210,29 @@ impl<'a> RedRef<'a> { }) } + pub fn errors(&self) -> Vec { + 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![Error::new(span, msg.to_string())] + } + _ => self + .children() + .filter(|red| red.green.erroneous()) + .flat_map(|red| red.errors()) + .collect(), + } + } + pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { self.children() .find(|x| mem::discriminant(x.kind()) == mem::discriminant(kind)) @@ -219,6 +247,18 @@ impl<'a> RedRef<'a> { } } +impl Debug for RedRef<'_> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{:?}: {:?}", self.kind(), self.span())?; + let mut children = self.children().peekable(); + if children.peek().is_some() { + f.write_str(" ")?; + f.debug_list().entries(children.map(RedRef::own)).finish()?; + } + Ok(()) + } +} + #[derive(Clone, PartialEq)] pub struct RedNode { id: SourceId, @@ -231,12 +271,27 @@ impl RedNode { Self { id, offset: 0, green: root.into() } } + pub fn as_ref<'a>(&'a self) -> RedRef<'a> { + RedRef { + id: self.id, + offset: self.offset, + green: &self.green, + } + } + pub fn span(&self) -> Span { self.as_ref().span() } pub fn len(&self) -> usize { - self.green.len() + self.as_ref().len() + } + + pub fn cast(self) -> Option + where + T: TypedNode, + { + T::cast_from(self.as_ref()) } pub fn kind(&self) -> &NodeKind { @@ -247,36 +302,8 @@ impl RedNode { 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 fn errors<'a>(&'a self) -> Vec { + self.as_ref().errors() } pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { @@ -294,15 +321,7 @@ impl RedNode { 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()?; - } - Ok(()) + self.as_ref().fmt(f) } } @@ -419,7 +438,7 @@ pub enum NodeKind { EmDash, /// A slash and the letter "u" followed by a hexadecimal unicode entity /// enclosed in curly braces: `\u{1F5FA}`. - UnicodeEscape(UnicodeEscapeToken), + UnicodeEscape(UnicodeEscapeData), /// Strong text was enabled / disabled: `*`. Strong, /// Emphasized text was enabled / disabled: `_`. @@ -440,9 +459,9 @@ pub enum NodeKind { ListBullet, /// An arbitrary number of backticks followed by inner contents, terminated /// with the same number of backticks: `` `...` ``. - Raw(Rc), + Raw(Rc), /// Dollar signs surrounding inner contents. - Math(Rc), + Math(Rc), /// An identifier: `center`. Ident(EcoString), /// A boolean: `true`, `false`. @@ -463,7 +482,7 @@ pub enum NodeKind { /// A fraction unit: `3fr`. Fraction(f64), /// A quoted string: `"..."`. - Str(StrToken), + Str(StrData), /// An array expression: `(1, "hi", 12cm)`. Array, /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. @@ -534,15 +553,14 @@ pub enum ErrorPosition { /// A quoted string token: `"..."`. #[derive(Debug, Clone, PartialEq)] -#[repr(transparent)] -pub struct StrToken { +pub struct StrData { /// The string inside the quotes. pub string: EcoString, } /// A raw block token: `` `...` ``. #[derive(Debug, Clone, PartialEq)] -pub struct RawToken { +pub struct RawData { /// The raw text in the block. pub text: EcoString, /// The programming language of the raw text. @@ -555,7 +573,7 @@ pub struct RawToken { /// A math formula token: `$2pi + x$` or `$[f'(x) = x^2]$`. #[derive(Debug, Clone, PartialEq)] -pub struct MathToken { +pub struct MathData { /// The formula between the dollars. pub formula: EcoString, /// Whether the formula is display-level, that is, it is surrounded by @@ -565,8 +583,7 @@ pub struct MathToken { /// A unicode escape sequence token: `\u{1F5FA}`. #[derive(Debug, Clone, PartialEq)] -#[repr(transparent)] -pub struct UnicodeEscapeToken { +pub struct UnicodeEscapeData { /// The resulting unicode character. pub character: char, } @@ -712,36 +729,3 @@ impl NodeKind { } } } - -#[macro_export] -macro_rules! node { - ($(#[$attr:meta])* $name:ident) => { - node!{$(#[$attr])* $name => $name} - }; - ($(#[$attr:meta])* $variant:ident => $name:ident) => { - #[derive(Debug, Clone, PartialEq)] - #[repr(transparent)] - $(#[$attr])* - pub struct $name(RedNode); - - impl TypedNode for $name { - fn cast_from(node: RedRef) -> Option { - if node.kind() != &NodeKind::$variant { - return None; - } - - Some(Self(node.own())) - } - } - - impl $name { - pub fn span(&self) -> Span { - self.0.span() - } - - pub fn underlying(&self) -> RedRef { - self.0.as_ref() - } - } - }; -} -- cgit v1.2.3 From 42afb27cef5540535420fb6d8d9d2fcda7300a47 Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Mon, 1 Nov 2021 13:45:33 +0100 Subject: Add documentation --- src/syntax/mod.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 10 deletions(-) (limited to 'src/syntax') diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index ca41d33f..61e0bb7e 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -30,6 +30,7 @@ pub enum Green { } impl Green { + /// Returns the metadata of the node. fn data(&self) -> &GreenData { match self { Green::Node(n) => &n.data, @@ -37,18 +38,22 @@ impl Green { } } + /// The type of the node. pub fn kind(&self) -> &NodeKind { self.data().kind() } + /// The length of the node. pub fn len(&self) -> usize { self.data().len() } + /// Whether the node or its children contain an error. pub fn erroneous(&self) -> bool { self.data().erroneous() } + /// The node's children. pub fn children(&self) -> &[Green] { match self { Green::Node(n) => &n.children(), @@ -87,23 +92,19 @@ pub struct GreenNode { } impl GreenNode { - pub fn new(kind: NodeKind, len: usize) -> Self { - Self { - data: GreenData::new(kind, len), - children: Vec::new(), - } - } - + /// Creates a new node with the given kind and children. pub fn with_children(kind: NodeKind, len: usize, children: Vec) -> Self { let mut data = GreenData::new(kind, len); data.erroneous |= children.iter().any(|c| c.erroneous()); Self { data, children } } + /// Creates a new node with the given kind and a single child. pub fn with_child(kind: NodeKind, len: usize, child: impl Into) -> Self { Self::with_children(kind, len, vec![child.into()]) } + /// The node's children. pub fn children(&self) -> &[Green] { &self.children } @@ -121,7 +122,7 @@ impl From> for Green { } } -/// Data shared between [`GreenNode`]s and [`GreenToken`]s. +/// Data shared between [`GreenNode`]s and leaf nodes. #[derive(Clone, PartialEq)] pub struct GreenData { /// What kind of node this is (each kind would have its own struct in a @@ -134,18 +135,22 @@ pub struct GreenData { } impl GreenData { + /// Create new node metadata. pub fn new(kind: NodeKind, len: usize) -> Self { Self { len, erroneous: kind.is_error(), kind } } + /// The type of the node. pub fn kind(&self) -> &NodeKind { &self.kind } + /// The length of the node. pub fn len(&self) -> usize { self.len } + /// Whether the node or its children contain an error. pub fn erroneous(&self) -> bool { self.erroneous } @@ -157,6 +162,8 @@ impl From for Green { } } +/// A borrowed wrapper for the [`GreenNode`] type that allows to access spans, +/// error lists and cast to an AST. #[derive(Copy, Clone, PartialEq)] pub struct RedRef<'a> { id: SourceId, @@ -165,6 +172,7 @@ pub struct RedRef<'a> { } impl<'a> RedRef<'a> { + /// Convert to an owned representation. pub fn own(self) -> RedNode { RedNode { id: self.id, @@ -173,18 +181,22 @@ impl<'a> RedRef<'a> { } } + /// The type of the node. pub fn kind(&self) -> &NodeKind { self.green.kind() } + /// The span of the node. pub fn span(&self) -> Span { Span::new(self.id, self.offset, self.offset + self.green.len()) } + /// The length of the node. pub fn len(&self) -> usize { self.green.len() } + /// Convert the node to a typed AST node. pub fn cast(self) -> Option where T: TypedNode, @@ -192,10 +204,12 @@ impl<'a> RedRef<'a> { T::cast_from(self) } + /// Whether the node or its children contain an error. pub fn erroneous(&self) -> bool { self.green.erroneous() } + /// The node's children. pub fn children(self) -> impl Iterator> + Clone { let children = match &self.green { Green::Node(node) => node.children(), @@ -210,6 +224,7 @@ impl<'a> RedRef<'a> { }) } + /// The error messages for this node and its descendants. pub fn errors(&self) -> Vec { if !self.green.erroneous() { return vec![]; @@ -233,15 +248,18 @@ impl<'a> RedRef<'a> { } } + /// Get the first child of some type. pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { self.children() .find(|x| mem::discriminant(x.kind()) == mem::discriminant(kind)) } + /// Get the first child that can cast to some AST type. pub(crate) fn cast_first_child(&self) -> Option { self.children().find_map(RedRef::cast) } + /// Get the last child that can cast to some AST type. pub(crate) fn cast_last_child(&self) -> Option { self.children().filter_map(RedRef::cast).last() } @@ -259,6 +277,8 @@ impl Debug for RedRef<'_> { } } +/// An owned wrapper for the [`GreenNode`] type that allows to access spans, +/// error lists and cast to an AST. #[derive(Clone, PartialEq)] pub struct RedNode { id: SourceId, @@ -267,10 +287,12 @@ pub struct RedNode { } impl RedNode { + /// Create a new root node from a [`GreenNode`]. pub fn new_root(root: Rc, id: SourceId) -> Self { Self { id, offset: 0, green: root.into() } } + /// Convert to a borrowed representation. pub fn as_ref<'a>(&'a self) -> RedRef<'a> { RedRef { id: self.id, @@ -279,14 +301,17 @@ impl RedNode { } } + /// The span of the node. pub fn span(&self) -> Span { self.as_ref().span() } + /// The length of the node. pub fn len(&self) -> usize { self.as_ref().len() } + /// Convert the node to a typed AST node. pub fn cast(self) -> Option where T: TypedNode, @@ -294,26 +319,32 @@ impl RedNode { T::cast_from(self.as_ref()) } + /// The type of the node. pub fn kind(&self) -> &NodeKind { self.green.kind() } + /// The children of the node. pub fn children<'a>(&'a self) -> impl Iterator> + Clone { self.as_ref().children() } + /// The error messages for this node and its descendants. pub fn errors<'a>(&'a self) -> Vec { self.as_ref().errors() } + /// Get the first child of some type. pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { self.as_ref().typed_child(kind).map(RedRef::own) } + /// Get the first child that can cast to some AST type. pub(crate) fn cast_first_child(&self) -> Option { self.as_ref().cast_first_child() } + /// Get the last child that can cast to some AST type. pub(crate) fn cast_last_child(&self) -> Option { self.as_ref().cast_last_child() } @@ -477,7 +508,7 @@ pub enum NodeKind { /// A percentage: `50%`. /// /// _Note_: `50%` is stored as `50.0` here, as in the corresponding - /// [literal](super::Lit::Percent). + /// [literal](Lit::Percent). Percentage(f64), /// A fraction unit: `3fr`. Fraction(f64), @@ -595,6 +626,7 @@ impl Display for NodeKind { } impl NodeKind { + /// Whether this is some kind of parenthesis. pub fn is_paren(&self) -> bool { match self { Self::LeftParen => true, @@ -603,6 +635,7 @@ impl NodeKind { } } + /// Whether this is some kind of bracket. pub fn is_bracket(&self) -> bool { match self { Self::LeftBracket => true, @@ -611,6 +644,7 @@ impl NodeKind { } } + /// Whether this is some kind of brace. pub fn is_brace(&self) -> bool { match self { Self::LeftBrace => true, @@ -619,8 +653,9 @@ impl NodeKind { } } + /// Whether this is some kind of error. pub fn is_error(&self) -> bool { - matches!(self, NodeKind::Error(_, _)) + matches!(self, NodeKind::Error(_, _) | NodeKind::Unknown(_)) } pub fn as_str(&self) -> &'static str { -- cgit v1.2.3 From 65fac0e57c9852eb2131aa06c0bac43b70bfbfbc Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 2 Nov 2021 12:13:45 +0100 Subject: Refactoring Co-Authored-By: Martin --- src/syntax/ast.rs | 141 +++++++++++++++++++++++++++--------- src/syntax/ident.rs | 94 ------------------------ src/syntax/mod.rs | 197 ++++++++++++++++++++++++--------------------------- src/syntax/pretty.rs | 2 +- src/syntax/span.rs | 130 +++++++-------------------------- 5 files changed, 225 insertions(+), 339 deletions(-) delete mode 100644 src/syntax/ident.rs (limited to 'src/syntax') diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 6ca271a9..9ad04be5 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -1,7 +1,18 @@ -use super::{Ident, NodeKind, RedNode, RedRef, Span, TypedNode}; +//! A typed layer over the red-green tree. + +use std::ops::Deref; + +use super::{NodeKind, RedNode, RedRef, Span}; use crate::geom::{AngularUnit, LengthUnit}; +use crate::parse::is_ident; use crate::util::EcoString; +/// A typed AST node. +pub trait TypedNode: Sized { + /// Convert from a red node to a typed node. + fn from_red(value: RedRef) -> Option; +} + macro_rules! node { ($(#[$attr:meta])* $name:ident) => { node!{$(#[$attr])* $name => $name} @@ -13,7 +24,7 @@ macro_rules! node { pub struct $name(RedNode); impl TypedNode for $name { - fn cast_from(node: RedRef) -> Option { + fn from_red(node: RedRef) -> Option { if node.kind() != &NodeKind::$variant { return None; } @@ -23,10 +34,12 @@ macro_rules! node { } impl $name { + /// The source code location. pub fn span(&self) -> Span { self.0.span() } + /// The underlying red node. pub fn underlying(&self) -> RedRef { self.0.as_ref() } @@ -40,7 +53,8 @@ node! { } impl Markup { - pub fn nodes<'a>(&'a self) -> impl Iterator + 'a { + /// The markup nodes. + pub fn nodes(&self) -> impl Iterator + '_ { self.0.children().filter_map(RedRef::cast) } } @@ -73,7 +87,7 @@ pub enum MarkupNode { } impl TypedNode for MarkupNode { - fn cast_from(node: RedRef) -> Option { + fn from_red(node: RedRef) -> Option { match node.kind() { NodeKind::Space(_) => Some(MarkupNode::Space), NodeKind::Linebreak => Some(MarkupNode::Linebreak), @@ -81,17 +95,14 @@ impl TypedNode for MarkupNode { 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::UnicodeEscape(c) => Some(MarkupNode::Text((*c).into())), + NodeKind::EnDash => Some(MarkupNode::Text("\u{2013}".into())), + NodeKind::EmDash => Some(MarkupNode::Text("\u{2014}".into())), + NodeKind::NonBreakingSpace => Some(MarkupNode::Text("\u{00A0}".into())), 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), } } @@ -111,16 +122,16 @@ pub struct RawNode { } impl TypedNode for RawNode { - fn cast_from(node: RedRef) -> Option { + fn from_red(node: RedRef) -> Option { match node.kind() { NodeKind::Raw(raw) => { - let span = node.span(); - let start = span.start + raw.backticks as usize; + let full = node.span(); + let start = full.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) + lang: raw.lang.as_ref().and_then(|lang| { + let span = Span::new(full.source, start, start + lang.len()); + Ident::new(lang, span) }), text: raw.text.clone(), }) @@ -272,7 +283,7 @@ impl Expr { } impl TypedNode for Expr { - fn cast_from(node: RedRef) -> Option { + fn from_red(node: RedRef) -> Option { match node.kind() { NodeKind::Ident(_) => node.cast().map(Self::Ident), NodeKind::Array => node.cast().map(Self::Array), @@ -325,7 +336,7 @@ pub enum Lit { } impl TypedNode for Lit { - fn cast_from(node: RedRef) -> Option { + fn from_red(node: RedRef) -> Option { match node.kind() { NodeKind::None => Some(Self::None(node.span())), NodeKind::Auto => Some(Self::Auto(node.span())), @@ -336,13 +347,14 @@ impl TypedNode for Lit { 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())), + NodeKind::Str(s) => Some(Self::Str(node.span(), s.clone())), _ => None, } } } impl Lit { + /// The source code location. pub fn span(&self) -> Span { match self { Self::None(span) => *span, @@ -366,7 +378,7 @@ node! { impl ArrayExpr { /// The array items. - pub fn items<'a>(&'a self) -> impl Iterator + 'a { + pub fn items(&self) -> impl Iterator + '_ { self.0.children().filter_map(RedRef::cast) } } @@ -378,7 +390,7 @@ node! { impl DictExpr { /// The named dictionary items. - pub fn items<'a>(&'a self) -> impl Iterator + 'a { + pub fn items(&self) -> impl Iterator + '_ { self.0.children().filter_map(RedRef::cast) } } @@ -439,7 +451,7 @@ node! { impl BlockExpr { /// The list of expressions contained in the block. - pub fn exprs<'a>(&'a self) -> impl Iterator + 'a { + pub fn exprs(&self) -> impl Iterator + '_ { self.0.children().filter_map(RedRef::cast) } } @@ -477,7 +489,7 @@ pub enum UnOp { } impl TypedNode for UnOp { - fn cast_from(node: RedRef) -> Option { + fn from_red(node: RedRef) -> Option { Self::from_token(node.kind()) } } @@ -581,7 +593,7 @@ pub enum BinOp { } impl TypedNode for BinOp { - fn cast_from(node: RedRef) -> Option { + fn from_red(node: RedRef) -> Option { Self::from_token(node.kind()) } } @@ -709,7 +721,7 @@ node! { impl CallArgs { /// The positional and named arguments. - pub fn items<'a>(&'a self) -> impl Iterator + 'a { + pub fn items(&self) -> impl Iterator + '_ { self.0.children().filter_map(RedRef::cast) } } @@ -726,7 +738,7 @@ pub enum CallArg { } impl TypedNode for CallArg { - fn cast_from(node: RedRef) -> Option { + fn from_red(node: RedRef) -> Option { match node.kind() { NodeKind::Named => Some(CallArg::Named( node.cast().expect("named call argument is missing name"), @@ -767,7 +779,7 @@ impl ClosureExpr { } /// The parameter bindings. - pub fn params<'a>(&'a self) -> impl Iterator + 'a { + pub fn params(&self) -> impl Iterator + '_ { self.0 .children() .find(|x| x.kind() == &NodeKind::ClosureParams) @@ -805,10 +817,10 @@ pub enum ClosureParam { } impl TypedNode for ClosureParam { - fn cast_from(node: RedRef) -> Option { + fn from_red(node: RedRef) -> Option { match node.kind() { - NodeKind::Ident(i) => { - Some(ClosureParam::Pos(Ident::new(i, node.span()).unwrap())) + NodeKind::Ident(id) => { + Some(ClosureParam::Pos(Ident::new_unchecked(id, node.span()))) } NodeKind::Named => Some(ClosureParam::Named( node.cast().expect("named closure parameter is missing name"), @@ -921,7 +933,7 @@ pub enum Imports { } impl TypedNode for Imports { - fn cast_from(node: RedRef) -> Option { + fn from_red(node: RedRef) -> Option { match node.kind() { NodeKind::Star => Some(Imports::Wildcard), NodeKind::ImportItems => { @@ -1043,14 +1055,75 @@ node! { } impl ForPattern { + /// The key part of the pattern: index for arrays, name for dictionaries. 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 } + let mut children = self.0.children().filter_map(RedRef::cast); + let key = children.next(); + if children.next().is_some() { key } else { None } } + /// The value part of the pattern. pub fn value(&self) -> Ident { self.0 .cast_last_child() .expect("for-in loop pattern is missing value") } } + +/// An unicode identifier with a few extra permissible characters. +/// +/// In addition to what is specified in the [Unicode Standard][uax31], we allow: +/// - `_` as a starting character, +/// - `_` and `-` as continuing characters. +/// +/// [uax31]: http://www.unicode.org/reports/tr31/ +#[derive(Debug, Clone, PartialEq)] +pub struct Ident { + /// The source code location. + pub span: Span, + /// The identifier string. + pub string: EcoString, +} + +impl Ident { + /// Create a new identifier from a string checking that it is a valid. + pub fn new( + string: impl AsRef + Into, + span: impl Into, + ) -> Option { + is_ident(string.as_ref()) + .then(|| Self { span: span.into(), string: string.into() }) + } + + /// Create a new identifier from a string and a span. + /// + /// The `string` must be a valid identifier. + #[track_caller] + pub fn new_unchecked(string: impl Into, span: Span) -> Self { + let string = string.into(); + debug_assert!(is_ident(&string), "`{}` is not a valid identifier", string); + Self { span, string } + } + + /// Return a reference to the underlying string. + pub fn as_str(&self) -> &str { + &self.string + } +} + +impl Deref for Ident { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.as_str() + } +} + +impl TypedNode for Ident { + fn from_red(node: RedRef) -> Option { + match node.kind() { + NodeKind::Ident(string) => Some(Ident::new_unchecked(string, node.span())), + _ => None, + } + } +} diff --git a/src/syntax/ident.rs b/src/syntax/ident.rs deleted file mode 100644 index f5cc6330..00000000 --- a/src/syntax/ident.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::borrow::Borrow; -use std::ops::Deref; - -use unicode_xid::UnicodeXID; - -use super::{NodeKind, RedRef, Span, TypedNode}; -use crate::util::EcoString; - -/// An unicode identifier with a few extra permissible characters. -/// -/// In addition to what is specified in the [Unicode Standard][uax31], we allow: -/// - `_` as a starting character, -/// - `_` and `-` as continuing characters. -/// -/// [uax31]: http://www.unicode.org/reports/tr31/ -#[derive(Debug, Clone, PartialEq)] -pub struct Ident { - /// The source code location. - pub span: Span, - /// The identifier string. - pub string: EcoString, -} - -impl Ident { - /// Create a new identifier from a string checking that it is a valid. - pub fn new( - string: impl AsRef + Into, - span: impl Into, - ) -> Option { - if is_ident(string.as_ref()) { - Some(Self { span: span.into(), string: string.into() }) - } else { - None - } - } - - /// Return a reference to the underlying string. - pub fn as_str(&self) -> &str { - self - } -} - -impl Deref for Ident { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.string.as_str() - } -} - -impl AsRef for Ident { - fn as_ref(&self) -> &str { - self - } -} - -impl Borrow for Ident { - fn borrow(&self) -> &str { - self - } -} - -impl From<&Ident> for EcoString { - fn from(ident: &Ident) -> Self { - ident.string.clone() - } -} - -impl TypedNode for Ident { - fn cast_from(node: RedRef) -> Option { - match node.kind() { - NodeKind::Ident(i) => Some(Ident::new(i, node.span()).unwrap()), - _ => None, - } - } -} - -/// Whether a string is a valid identifier. -pub fn is_ident(string: &str) -> bool { - let mut chars = string.chars(); - chars - .next() - .map_or(false, |c| is_id_start(c) && chars.all(is_id_continue)) -} - -/// Whether a character can start an identifier. -pub fn is_id_start(c: char) -> bool { - c.is_xid_start() || c == '_' -} - -/// Whether a character can continue an identifier. -pub fn is_id_continue(c: char) -> bool { - c.is_xid_continue() || c == '_' || c == '-' -} diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 61e0bb7e..d26c6484 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -1,31 +1,28 @@ //! Syntax types. -mod ast; -mod ident; +pub mod ast; mod pretty; mod span; -use std::fmt; -use std::fmt::{Debug, Display, Formatter}; +use std::fmt::{self, Debug, Display, Formatter}; use std::mem; use std::rc::Rc; -pub use ast::*; -pub use ident::*; pub use pretty::*; pub use span::*; +use self::ast::TypedNode; use crate::diag::Error; use crate::geom::{AngularUnit, LengthUnit}; use crate::source::SourceId; use crate::util::EcoString; -/// Children of a [`GreenNode`]. +/// An inner of leaf node in the untyped green tree. #[derive(Clone, PartialEq)] pub enum Green { - /// A non-terminal node in an Rc. + /// A reference-counted inner node. Node(Rc), - /// A terminal owned token. + /// A terminal, owned token. Token(GreenData), } @@ -77,13 +74,12 @@ impl Debug for Green { f.debug_list().entries(&n.children).finish()?; } } - Ok(()) } } -/// A syntactical node. -#[derive(Clone, PartialEq)] +/// An inner node in the untyped green tree. +#[derive(Debug, Clone, PartialEq)] pub struct GreenNode { /// Node metadata. data: GreenData, @@ -122,15 +118,15 @@ impl From> for Green { } } -/// Data shared between [`GreenNode`]s and leaf nodes. -#[derive(Clone, PartialEq)] +/// Data shared between inner and leaf nodes. +#[derive(Debug, Clone, PartialEq)] pub struct GreenData { /// What kind of node this is (each kind would have its own struct in a /// strongly typed AST). kind: NodeKind, /// The byte length of the node in the source. len: usize, - /// Whether this node or any of its children are erroneous. + /// Whether this node or any of its children contain an error. erroneous: bool, } @@ -162,8 +158,9 @@ impl From for Green { } } -/// A borrowed wrapper for the [`GreenNode`] type that allows to access spans, -/// error lists and cast to an AST. +/// A borrowed wrapper for a [`GreenNode`] with span information. +/// +/// Borrowed variant of [`RedNode`]. Can be [cast](Self::cast) to an AST node. #[derive(Copy, Clone, PartialEq)] pub struct RedRef<'a> { id: SourceId, @@ -182,50 +179,27 @@ impl<'a> RedRef<'a> { } /// The type of the node. - pub fn kind(&self) -> &NodeKind { + pub fn kind(self) -> &'a NodeKind { self.green.kind() } - /// The span of the node. - pub fn span(&self) -> Span { - Span::new(self.id, self.offset, self.offset + self.green.len()) - } - /// The length of the node. - pub fn len(&self) -> usize { + pub fn len(self) -> usize { self.green.len() } - /// Convert the node to a typed AST node. - pub fn cast(self) -> Option - where - T: TypedNode, - { - T::cast_from(self) + /// The span of the node. + pub fn span(self) -> Span { + Span::new(self.id, self.offset, self.offset + self.green.len()) } /// Whether the node or its children contain an error. - pub fn erroneous(&self) -> bool { + pub fn erroneous(self) -> bool { self.green.erroneous() } - /// The node's children. - 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 } - }) - } - /// The error messages for this node and its descendants. - pub fn errors(&self) -> Vec { + pub fn errors(self) -> Vec { if !self.green.erroneous() { return vec![]; } @@ -248,19 +222,42 @@ impl<'a> RedRef<'a> { } } + /// Convert the node to a typed AST node. + pub fn cast(self) -> Option + where + T: TypedNode, + { + T::from_red(self) + } + + /// The node's children. + pub fn children(self) -> impl Iterator> { + 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 } + }) + } + /// Get the first child of some type. - pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { + pub(crate) fn typed_child(self, kind: &NodeKind) -> Option> { self.children() .find(|x| mem::discriminant(x.kind()) == mem::discriminant(kind)) } /// Get the first child that can cast to some AST type. - pub(crate) fn cast_first_child(&self) -> Option { + pub(crate) fn cast_first_child(self) -> Option { self.children().find_map(RedRef::cast) } /// Get the last child that can cast to some AST type. - pub(crate) fn cast_last_child(&self) -> Option { + pub(crate) fn cast_last_child(self) -> Option { self.children().filter_map(RedRef::cast).last() } } @@ -277,8 +274,9 @@ impl Debug for RedRef<'_> { } } -/// An owned wrapper for the [`GreenNode`] type that allows to access spans, -/// error lists and cast to an AST. +/// A owned wrapper for a [`GreenNode`] with span information. +/// +/// Owned variant of [`RedRef`]. Can be [cast](Self::cast) to an AST nodes. #[derive(Clone, PartialEq)] pub struct RedNode { id: SourceId, @@ -293,7 +291,7 @@ impl RedNode { } /// Convert to a borrowed representation. - pub fn as_ref<'a>(&'a self) -> RedRef<'a> { + pub fn as_ref(&self) -> RedRef<'_> { RedRef { id: self.id, offset: self.offset, @@ -301,9 +299,9 @@ impl RedNode { } } - /// The span of the node. - pub fn span(&self) -> Span { - self.as_ref().span() + /// The type of the node. + pub fn kind(&self) -> &NodeKind { + self.as_ref().kind() } /// The length of the node. @@ -311,29 +309,29 @@ impl RedNode { self.as_ref().len() } + /// The span of the node. + pub fn span(&self) -> Span { + self.as_ref().span() + } + + /// The error messages for this node and its descendants. + pub fn errors(&self) -> Vec { + self.as_ref().errors() + } + /// Convert the node to a typed AST node. pub fn cast(self) -> Option where T: TypedNode, { - T::cast_from(self.as_ref()) - } - - /// The type of the node. - pub fn kind(&self) -> &NodeKind { - self.green.kind() + self.as_ref().cast() } /// The children of the node. - pub fn children<'a>(&'a self) -> impl Iterator> + Clone { + pub fn children(&self) -> impl Iterator> { self.as_ref().children() } - /// The error messages for this node and its descendants. - pub fn errors<'a>(&'a self) -> Vec { - self.as_ref().errors() - } - /// Get the first child of some type. pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { self.as_ref().typed_child(kind).map(RedRef::own) @@ -356,11 +354,10 @@ impl Debug for RedNode { } } -pub trait TypedNode: Sized { - /// Performs the conversion. - fn cast_from(value: RedRef) -> Option; -} - +/// All syntactical building blocks that can be part of a Typst document. +/// +/// Can be emitted as a token by the tokenizer or as part of a green node by +/// the parser. #[derive(Debug, Clone, PartialEq)] pub enum NodeKind { /// A left square bracket: `[`. @@ -469,7 +466,7 @@ pub enum NodeKind { EmDash, /// A slash and the letter "u" followed by a hexadecimal unicode entity /// enclosed in curly braces: `\u{1F5FA}`. - UnicodeEscape(UnicodeEscapeData), + UnicodeEscape(char), /// Strong text was enabled / disabled: `*`. Strong, /// Emphasized text was enabled / disabled: `_`. @@ -508,12 +505,12 @@ pub enum NodeKind { /// A percentage: `50%`. /// /// _Note_: `50%` is stored as `50.0` here, as in the corresponding - /// [literal](Lit::Percent). + /// [literal](ast::Lit::Percent). Percentage(f64), /// A fraction unit: `3fr`. Fraction(f64), /// A quoted string: `"..."`. - Str(StrData), + Str(EcoString), /// An array expression: `(1, "hi", 12cm)`. Array, /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. @@ -572,24 +569,7 @@ pub enum NodeKind { 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)] -pub struct StrData { - /// The string inside the quotes. - pub string: EcoString, -} - -/// A raw block token: `` `...` ``. +/// Payload of a raw block: `` `...` ``. #[derive(Debug, Clone, PartialEq)] pub struct RawData { /// The raw text in the block. @@ -602,7 +582,7 @@ pub struct RawData { pub block: bool, } -/// A math formula token: `$2pi + x$` or `$[f'(x) = x^2]$`. +/// Payload of a math formula: `$2pi + x$` or `$[f'(x) = x^2]$`. #[derive(Debug, Clone, PartialEq)] pub struct MathData { /// The formula between the dollars. @@ -612,17 +592,15 @@ pub struct MathData { pub display: bool, } -/// A unicode escape sequence token: `\u{1F5FA}`. -#[derive(Debug, Clone, PartialEq)] -pub struct UnicodeEscapeData { - /// The resulting unicode character. - pub character: char, -} - -impl Display for NodeKind { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.pad(self.as_str()) - } +/// Where in a node an error should be annotated. +#[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 NodeKind { @@ -658,6 +636,7 @@ impl NodeKind { matches!(self, NodeKind::Error(_, _) | NodeKind::Unknown(_)) } + /// A human-readable name for the kind. pub fn as_str(&self) -> &'static str { match self { Self::LeftBracket => "opening bracket", @@ -764,3 +743,9 @@ impl NodeKind { } } } + +impl Display for NodeKind { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.pad(self.as_str()) + } +} diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index da0bdd44..b396a39c 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Arguments, Write}; -use super::*; +use super::ast::*; /// Pretty print an item and return the resulting string. pub fn pretty(item: &T) -> String diff --git a/src/syntax/span.rs b/src/syntax/span.rs index ee7cba4c..c26011bd 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; use std::fmt::{self, Debug, Formatter}; -use std::ops::{Add, Range}; +use std::ops::Range; use serde::{Deserialize, Serialize}; @@ -53,23 +53,19 @@ pub struct Span { /// The id of the source file. pub source: SourceId, /// The inclusive start position. - pub start: Pos, + pub start: usize, /// The inclusive end position. - pub end: Pos, + pub end: usize, } impl Span { /// Create a new span from start and end positions. - pub fn new(source: SourceId, start: impl Into, end: impl Into) -> Self { - Self { - source, - start: start.into(), - end: end.into(), - } + pub fn new(source: SourceId, start: usize, end: usize) -> Self { + Self { source, start, end } } /// Create a span including just a single position. - pub fn at(source: SourceId, pos: impl Into + Copy) -> Self { + pub fn at(source: SourceId, pos: usize) -> Self { Self::new(source, pos, pos) } @@ -77,19 +73,29 @@ impl Span { pub fn detached() -> Self { Self { source: SourceId::from_raw(0), - start: Pos::ZERO, - end: Pos::ZERO, + start: 0, + end: 0, } } /// Create a span with a different start position. - pub fn with_start(self, start: impl Into) -> Self { - Self { start: start.into(), ..self } + pub fn with_start(self, start: usize) -> Self { + Self { start, ..self } } /// Create a span with a different end position. - pub fn with_end(self, end: impl Into) -> Self { - Self { end: end.into(), ..self } + pub fn with_end(self, end: usize) -> Self { + Self { end, ..self } + } + + /// A new span at the position of this span's start. + pub fn at_start(&self) -> Span { + Self::at(self.source, self.start) + } + + /// A new span at the position of this span's end. + pub fn at_end(&self) -> Span { + Self::at(self.source, self.end) } /// Create a new span with the earlier start and later end position. @@ -110,28 +116,18 @@ impl Span { } /// Test whether a position is within the span. - pub fn contains_pos(&self, pos: Pos) -> bool { + pub fn contains(&self, pos: usize) -> bool { self.start <= pos && self.end >= pos } /// Test whether one span complete contains the other span. - pub fn contains(self, other: Self) -> bool { + pub fn surrounds(self, other: Self) -> bool { self.source == other.source && self.start <= other.start && self.end >= other.end } - /// Convert to a `Range` for indexing. + /// Convert to a `Range` for indexing. pub fn to_range(self) -> Range { - self.start.to_usize() .. self.end.to_usize() - } - - /// A new span at the position of this span's start. - pub fn at_start(&self) -> Span { - Self::at(self.source, self.start) - } - - /// A new span at the position of this span's end. - pub fn at_end(&self) -> Span { - Self::at(self.source, self.end) + self.start .. self.end } } @@ -150,77 +146,3 @@ impl PartialOrd for Span { } } } - -/// A byte position in source code. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] -pub struct Pos(pub u32); - -impl Pos { - /// The zero position. - pub const ZERO: Self = Self(0); - - /// Convert to a usize for indexing. - pub fn to_usize(self) -> usize { - self.0 as usize - } -} - -impl Debug for Pos { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Debug::fmt(&self.0, f) - } -} - -impl From for Pos { - fn from(index: u32) -> Self { - Self(index) - } -} - -impl From for Pos { - fn from(index: usize) -> Self { - Self(index as u32) - } -} - -impl Add for Pos -where - T: Into, -{ - type Output = Self; - - fn add(self, rhs: T) -> Self { - Pos(self.0 + rhs.into().0) - } -} - -/// Convert a position or range into a span. -pub trait IntoSpan { - /// Convert into a span by providing the source id. - fn into_span(self, source: SourceId) -> Span; -} - -impl IntoSpan for Span { - fn into_span(self, source: SourceId) -> Span { - debug_assert_eq!(self.source, source); - self - } -} - -impl IntoSpan for Pos { - fn into_span(self, source: SourceId) -> Span { - Span::new(source, self, self) - } -} - -impl IntoSpan for usize { - fn into_span(self, source: SourceId) -> Span { - Span::new(source, self, self) - } -} - -impl IntoSpan for Range { - fn into_span(self, source: SourceId) -> Span { - Span::new(source, self.start, self.end) - } -} -- cgit v1.2.3 From f0c9635db5efd0c66e01bef1be0a8f140fdbdd84 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Thu, 4 Nov 2021 15:16:46 +0100 Subject: Notes --- src/syntax/mod.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/syntax') diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index d26c6484..112fc220 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -40,6 +40,15 @@ impl Green { self.data().kind() } + /// Set the type of the node. + pub fn set_kind(&mut self, kind: NodeKind) { + let data = match self { + Self::Node(node) => &mut Rc::make_mut(node).data, + Self::Token(data) => data, + }; + data.set_kind(kind); + } + /// The length of the node. pub fn len(&self) -> usize { self.data().len() @@ -141,6 +150,11 @@ impl GreenData { &self.kind } + /// Set the type of the node. + pub fn set_kind(&mut self, kind: NodeKind) { + self.kind = kind; + } + /// The length of the node. pub fn len(&self) -> usize { self.len -- cgit v1.2.3 From 5c952d56d0d602a1dbcf85210ae30fa402219fca Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Thu, 4 Nov 2021 19:36:32 +0100 Subject: New error handling --- src/syntax/ast.rs | 12 +++--------- src/syntax/mod.rs | 7 ++----- 2 files changed, 5 insertions(+), 14 deletions(-) (limited to 'src/syntax') diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 9ad04be5..b6f64c67 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -156,13 +156,7 @@ impl HeadingNode { /// 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") + self.0.children().filter(|n| n.kind() == &NodeKind::Eq).count() as u8 } } @@ -743,7 +737,7 @@ impl TypedNode for CallArg { NodeKind::Named => Some(CallArg::Named( node.cast().expect("named call argument is missing name"), )), - NodeKind::ParameterSink => Some(CallArg::Spread( + NodeKind::Spread => Some(CallArg::Spread( node.cast_first_child() .expect("call argument sink is missing expression"), )), @@ -825,7 +819,7 @@ impl TypedNode for ClosureParam { NodeKind::Named => Some(ClosureParam::Named( node.cast().expect("named closure parameter is missing name"), )), - NodeKind::ParameterSink => Some(ClosureParam::Sink( + NodeKind::Spread => Some(ClosureParam::Sink( node.cast_first_child() .expect("closure parameter sink is missing identifier"), )), diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 112fc220..db3b0c9a 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -487,8 +487,6 @@ pub enum NodeKind { 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.`. @@ -546,7 +544,7 @@ pub enum NodeKind { /// A closure's parameters: `(x, y)`. ClosureParams, /// A parameter sink: `..x`. - ParameterSink, + Spread, /// A template expression: `[*Hi* there!]`. Template, /// A block expression: `{ let x = 1; x + 2 }`. @@ -709,7 +707,6 @@ impl NodeKind { 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", @@ -735,7 +732,7 @@ impl NodeKind { Self::CallArgs => "call arguments", Self::Closure => "closure", Self::ClosureParams => "closure parameters", - Self::ParameterSink => "parameter sink", + Self::Spread => "parameter sink", Self::Template => "template", Self::Block => "block", Self::ForExpr => "for-loop expression", -- cgit v1.2.3 From cf2e527a026e81269ef716b4d6675ae6d981d681 Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Fri, 5 Nov 2021 12:53:52 +0100 Subject: Code Review: No Patrick, question marks are not an instrument --- src/syntax/ast.rs | 6 +++++- src/syntax/mod.rs | 15 ++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) (limited to 'src/syntax') diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index b6f64c67..1198d6b1 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -156,7 +156,11 @@ impl HeadingNode { /// The section depth (numer of equals signs). pub fn level(&self) -> u8 { - self.0.children().filter(|n| n.kind() == &NodeKind::Eq).count() as u8 + self.0 + .children() + .filter(|n| n.kind() == &NodeKind::Eq) + .count() + .min(u8::MAX.into()) as u8 } } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index db3b0c9a..363cbe6e 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -98,15 +98,20 @@ pub struct GreenNode { impl GreenNode { /// Creates a new node with the given kind and children. - pub fn with_children(kind: NodeKind, len: usize, children: Vec) -> Self { - let mut data = GreenData::new(kind, len); - data.erroneous |= children.iter().any(|c| c.erroneous()); + pub fn with_children(kind: NodeKind, children: Vec) -> Self { + let mut data = GreenData::new(kind, 0); + let len = children + .iter() + .inspect(|c| data.erroneous |= c.erroneous()) + .map(Green::len) + .sum(); + data.len = len; Self { data, children } } /// Creates a new node with the given kind and a single child. - pub fn with_child(kind: NodeKind, len: usize, child: impl Into) -> Self { - Self::with_children(kind, len, vec![child.into()]) + pub fn with_child(kind: NodeKind, child: impl Into) -> Self { + Self::with_children(kind, vec![child.into()]) } /// The node's children. -- cgit v1.2.3 From 41bdafb5785dd85d20a3e79900b18e0010f6d71d Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sat, 6 Nov 2021 12:12:02 +0100 Subject: Faster parser --- src/syntax/mod.rs | 75 ++++++++++++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 43 deletions(-) (limited to 'src/syntax') diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 363cbe6e..022b51de 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -42,11 +42,10 @@ impl Green { /// Set the type of the node. pub fn set_kind(&mut self, kind: NodeKind) { - let data = match self { - Self::Node(node) => &mut Rc::make_mut(node).data, - Self::Token(data) => data, - }; - data.set_kind(kind); + match self { + Self::Node(node) => Rc::make_mut(node).data.set_kind(kind), + Self::Token(data) => data.set_kind(kind), + } } /// The length of the node. @@ -56,7 +55,10 @@ impl Green { /// Whether the node or its children contain an error. pub fn erroneous(&self) -> bool { - self.data().erroneous() + match self { + Self::Node(node) => node.erroneous, + Self::Token(data) => data.kind.is_error(), + } } /// The node's children. @@ -94,24 +96,30 @@ pub struct GreenNode { data: GreenData, /// This node's children, losslessly make up this node. children: Vec, + /// Whether this node or any of its children are erroneous. + erroneous: bool, } impl GreenNode { + /// Creates a new node with the given kind and a single child. + pub fn with_child(kind: NodeKind, child: impl Into) -> Self { + Self::with_children(kind, vec![child.into()]) + } + /// Creates a new node with the given kind and children. pub fn with_children(kind: NodeKind, children: Vec) -> Self { - let mut data = GreenData::new(kind, 0); + let mut erroneous = kind.is_error(); let len = children .iter() - .inspect(|c| data.erroneous |= c.erroneous()) + .inspect(|c| erroneous |= c.erroneous()) .map(Green::len) .sum(); - data.len = len; - Self { data, children } - } - /// Creates a new node with the given kind and a single child. - pub fn with_child(kind: NodeKind, child: impl Into) -> Self { - Self::with_children(kind, vec![child.into()]) + Self { + data: GreenData::new(kind, len), + children, + erroneous, + } } /// The node's children. @@ -140,14 +148,12 @@ pub struct GreenData { kind: NodeKind, /// The byte length of the node in the source. len: usize, - /// Whether this node or any of its children contain an error. - erroneous: bool, } impl GreenData { /// Create new node metadata. pub fn new(kind: NodeKind, len: usize) -> Self { - Self { len, erroneous: kind.is_error(), kind } + Self { len, kind } } /// The type of the node. @@ -164,11 +170,6 @@ impl GreenData { pub fn len(&self) -> usize { self.len } - - /// Whether the node or its children contain an error. - pub fn erroneous(&self) -> bool { - self.erroneous - } } impl From for Green { @@ -219,7 +220,7 @@ impl<'a> RedRef<'a> { /// The error messages for this node and its descendants. pub fn errors(self) -> Vec { - if !self.green.erroneous() { + if !self.erroneous() { return vec![]; } @@ -235,7 +236,7 @@ impl<'a> RedRef<'a> { } _ => self .children() - .filter(|red| red.green.erroneous()) + .filter(|red| red.erroneous()) .flat_map(|red| red.errors()) .collect(), } @@ -256,11 +257,11 @@ impl<'a> RedRef<'a> { Green::Token(_) => &[], }; - let mut offset = self.offset; + let mut cursor = self.offset; children.iter().map(move |green| { - let child_offset = offset; - offset += green.len(); - RedRef { id: self.id, offset: child_offset, green } + let offset = cursor; + cursor += green.len(); + RedRef { id: self.id, offset, green } }) } @@ -623,29 +624,17 @@ pub enum ErrorPosition { impl NodeKind { /// Whether this is some kind of parenthesis. pub fn is_paren(&self) -> bool { - match self { - Self::LeftParen => true, - Self::RightParen => true, - _ => false, - } + matches!(self, Self::LeftParen | Self::RightParen) } /// Whether this is some kind of bracket. pub fn is_bracket(&self) -> bool { - match self { - Self::LeftBracket => true, - Self::RightBracket => true, - _ => false, - } + matches!(self, Self::LeftBracket | Self::RightBracket) } /// Whether this is some kind of brace. pub fn is_brace(&self) -> bool { - match self { - Self::LeftBrace => true, - Self::RightBrace => true, - _ => false, - } + matches!(self, Self::LeftBrace | Self::RightBrace) } /// Whether this is some kind of error. -- cgit v1.2.3 From 95866d5fc9ae89a23c5754193c7de5d4fe4873b1 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 7 Nov 2021 22:05:48 +0100 Subject: Tidy up AST --- src/syntax/ast.rs | 350 ++++++++++++++++----------------------------------- src/syntax/mod.rs | 249 +++++++++++++++++++----------------- src/syntax/pretty.rs | 6 +- 3 files changed, 242 insertions(+), 363 deletions(-) (limited to 'src/syntax') diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 1198d6b1..dc71e229 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -1,10 +1,7 @@ //! A typed layer over the red-green tree. -use std::ops::Deref; - use super::{NodeKind, RedNode, RedRef, Span}; use crate::geom::{AngularUnit, LengthUnit}; -use crate::parse::is_ident; use crate::util::EcoString; /// A typed AST node. @@ -40,7 +37,7 @@ macro_rules! node { } /// The underlying red node. - pub fn underlying(&self) -> RedRef { + pub fn as_red(&self) -> RedRef { self.0.as_ref() } } @@ -112,7 +109,7 @@ impl TypedNode for MarkupNode { #[derive(Debug, Clone, PartialEq)] pub struct RawNode { /// An optional identifier specifying the language to syntax-highlight in. - pub lang: Option, + pub lang: Option, /// The raw text, determined as the raw string between the backticks trimmed /// according to the above rules. pub text: EcoString, @@ -124,18 +121,11 @@ pub struct RawNode { impl TypedNode for RawNode { fn from_red(node: RedRef) -> Option { match node.kind() { - NodeKind::Raw(raw) => { - let full = node.span(); - let start = full.start + raw.backticks as usize; - Some(Self { - block: raw.block, - lang: raw.lang.as_ref().and_then(|lang| { - let span = Span::new(full.source, start, start + lang.len()); - Ident::new(lang, span) - }), - text: raw.text.clone(), - }) - } + NodeKind::Raw(raw) => Some(Self { + block: raw.block, + lang: raw.lang.clone(), + text: raw.text.clone(), + }), _ => None, } } @@ -149,9 +139,7 @@ node! { impl HeadingNode { /// The contents of the heading. pub fn body(&self) -> Markup { - self.0 - .cast_first_child() - .expect("heading node is missing markup body") + self.0.cast_first_child().expect("heading is missing markup body") } /// The section depth (numer of equals signs). @@ -184,7 +172,7 @@ node! { impl EnumNode { /// The contents of the list item. pub fn body(&self) -> Markup { - self.0.cast_first_child().expect("enumeration node is missing body") + self.0.cast_first_child().expect("enum node is missing body") } /// The number, if any. @@ -195,7 +183,7 @@ impl EnumNode { NodeKind::EnumNumbering(num) => Some(num.clone()), _ => None, }) - .expect("enumeration node is missing number") + .expect("enum node is missing number") } } @@ -240,6 +228,31 @@ pub enum Expr { Include(IncludeExpr), } +impl TypedNode for Expr { + fn from_red(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), + } + } +} + impl Expr { /// Whether the expression can be shortened in markup with a hashtag. pub fn has_short_form(&self) -> bool { @@ -280,31 +293,6 @@ impl Expr { } } -impl TypedNode for Expr { - fn from_red(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 { @@ -335,17 +323,17 @@ pub enum Lit { impl TypedNode for Lit { fn from_red(node: RedRef) -> Option { - match node.kind() { + 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.clone())), + NodeKind::Bool(v) => Some(Self::Bool(node.span(), v)), + NodeKind::Int(v) => Some(Self::Int(node.span(), v)), + NodeKind::Float(v) => Some(Self::Float(node.span(), v)), + NodeKind::Length(v, unit) => Some(Self::Length(node.span(), v, unit)), + NodeKind::Angle(v, unit) => Some(Self::Angle(node.span(), v, unit)), + NodeKind::Percentage(v) => Some(Self::Percent(node.span(), v)), + NodeKind::Fraction(v) => Some(Self::Fractional(node.span(), v)), + NodeKind::Str(ref v) => Some(Self::Str(node.span(), v.clone())), _ => None, } } @@ -354,17 +342,17 @@ impl TypedNode for Lit { impl Lit { /// The source code location. 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, + 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, } } } @@ -401,16 +389,12 @@ node! { impl Named { /// The name: `pattern`. pub fn name(&self) -> Ident { - self.0.cast_first_child().expect("named pair is missing name ident") + self.0.cast_first_child().expect("named pair is missing name") } /// 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") + self.0.cast_last_child().expect("named pair is missing expression") } } @@ -422,9 +406,7 @@ node! { impl TemplateExpr { /// The contents of the template. pub fn body(&self) -> Markup { - self.0 - .cast_first_child() - .expect("template expression is missing body") + self.0.cast_first_child().expect("template is missing body") } } @@ -436,9 +418,7 @@ node! { impl GroupExpr { /// The wrapped expression. pub fn expr(&self) -> Expr { - self.0 - .cast_first_child() - .expect("group expression is missing expression") + self.0.cast_first_child().expect("group is missing expression") } } @@ -469,9 +449,7 @@ impl UnaryExpr { /// The expression to operator on: `x`. pub fn expr(&self) -> Expr { - self.0 - .cast_first_child() - .expect("unary expression is missing expression") + self.0.cast_last_child().expect("unary expression is missing child") } } @@ -506,7 +484,7 @@ impl UnOp { /// The precedence of this operator. pub fn precedence(self) -> usize { match self { - Self::Pos | Self::Neg => 8, + Self::Pos | Self::Neg => 7, Self::Not => 4, } } @@ -544,9 +522,7 @@ impl BinaryExpr { /// The right-hand side of the operation: `b`. pub fn rhs(&self) -> Expr { self.0 - .children() - .filter_map(RedRef::cast) - .nth(1) + .cast_last_child() .expect("binary expression is missing right-hand side") } } @@ -701,14 +677,12 @@ node! { impl CallExpr { /// The function to call. pub fn callee(&self) -> Expr { - self.0.cast_first_child().expect("call expression is missing callee") + self.0.cast_first_child().expect("call 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") + self.0.cast_last_child().expect("call is missing argument list") } } @@ -738,14 +712,9 @@ pub enum CallArg { impl TypedNode for CallArg { fn from_red(node: RedRef) -> Option { match node.kind() { - NodeKind::Named => Some(CallArg::Named( - node.cast().expect("named call argument is missing name"), - )), - NodeKind::Spread => Some(CallArg::Spread( - node.cast_first_child() - .expect("call argument sink is missing expression"), - )), - _ => Some(CallArg::Pos(node.cast()?)), + NodeKind::Named => node.cast().map(CallArg::Named), + NodeKind::Spread => node.cast_first_child().map(CallArg::Spread), + _ => node.cast().map(CallArg::Pos), } } } @@ -754,8 +723,8 @@ 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::Named(named) => named.span(), Self::Spread(expr) => expr.span(), } } @@ -771,8 +740,6 @@ impl ClosureExpr { /// /// 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() } @@ -788,22 +755,11 @@ impl ClosureExpr { /// 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. +/// A parameter to a closure. #[derive(Debug, Clone, PartialEq)] pub enum ClosureParam { /// A positional parameter: `x`. @@ -817,17 +773,10 @@ pub enum ClosureParam { impl TypedNode for ClosureParam { fn from_red(node: RedRef) -> Option { match node.kind() { - NodeKind::Ident(id) => { - Some(ClosureParam::Pos(Ident::new_unchecked(id, node.span()))) - } - NodeKind::Named => Some(ClosureParam::Named( - node.cast().expect("named closure parameter is missing name"), - )), - NodeKind::Spread => Some(ClosureParam::Sink( - node.cast_first_child() - .expect("closure parameter sink is missing identifier"), - )), - _ => Some(ClosureParam::Pos(node.cast()?)), + NodeKind::Ident(_) => node.cast().map(ClosureParam::Pos), + NodeKind::Named => node.cast().map(ClosureParam::Named), + NodeKind::Spread => node.cast_first_child().map(ClosureParam::Sink), + _ => None, } } } @@ -840,9 +789,7 @@ node! { 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") + self.0.cast_first_child().expect("with expression is missing callee") } /// The arguments to apply to the function. @@ -861,17 +808,16 @@ node! { 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") + match self.0.cast_first_child() { + Some(Expr::Ident(binding)) => binding, + Some(Expr::With(with)) => match with.callee() { + Expr::Ident(binding) => binding, + _ => panic!("let .. with callee must be identifier"), + }, + Some(Expr::Closure(closure)) => { + closure.name().expect("let-bound closure is missing name") + } + _ => panic!("let expression is missing binding"), } } @@ -880,23 +826,9 @@ impl LetExpr { 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()) + // This is a let .. with expression. + self.0.cast_first_child() } - .unwrap() } } @@ -908,16 +840,12 @@ node! { impl ImportExpr { /// The items to be imported. pub fn imports(&self) -> Imports { - self.0 - .cast_first_child() - .expect("import expression is missing import list") + self.0.cast_first_child().expect("import is missing items") } /// The location of the importable file. pub fn path(&self) -> Expr { - self.0 - .cast_first_child() - .expect("import expression is missing path expression") + self.0.cast_last_child().expect("import is missing path") } } @@ -926,8 +854,8 @@ impl ImportExpr { 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), + /// The specified items from the file should be imported. + Items(Vec), } impl TypedNode for Imports { @@ -935,8 +863,8 @@ impl TypedNode for Imports { match node.kind() { NodeKind::Star => Some(Imports::Wildcard), NodeKind::ImportItems => { - let idents = node.children().filter_map(RedRef::cast).collect(); - Some(Imports::Idents(idents)) + let items = node.children().filter_map(RedRef::cast).collect(); + Some(Imports::Items(items)) } _ => None, } @@ -951,9 +879,7 @@ node! { 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") + self.0.cast_last_child().expect("include is missing path") } } @@ -965,9 +891,7 @@ node! { 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") + self.0.cast_first_child().expect("if expression is missing condition") } /// The expression to evaluate if the condition is true. @@ -976,7 +900,7 @@ impl IfExpr { .children() .filter_map(RedRef::cast) .nth(1) - .expect("if expression is missing if body") + .expect("if expression is missing body") } /// The expression to evaluate if the condition is false. @@ -993,18 +917,12 @@ node! { 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") + self.0.cast_first_child().expect("while loop is missing condition") } /// 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") + self.0.cast_last_child().expect("while loop is missing body") } } @@ -1016,34 +934,17 @@ node! { impl ForExpr { /// The pattern to assign to. pub fn pattern(&self) -> ForPattern { - self.0 - .cast_first_child() - .expect("for loop expression is missing pattern") + self.0.cast_first_child().expect("for loop 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") + self.0.cast_first_child().expect("for loop is missing iterable") } /// 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() + self.0.cast_last_child().expect("for loop is missing body") } } @@ -1062,19 +963,11 @@ impl ForPattern { /// The value part of the pattern. pub fn value(&self) -> Ident { - self.0 - .cast_last_child() - .expect("for-in loop pattern is missing value") + self.0.cast_last_child().expect("for loop pattern is missing value") } } -/// An unicode identifier with a few extra permissible characters. -/// -/// In addition to what is specified in the [Unicode Standard][uax31], we allow: -/// - `_` as a starting character, -/// - `_` and `-` as continuing characters. -/// -/// [uax31]: http://www.unicode.org/reports/tr31/ +/// An identifier. #[derive(Debug, Clone, PartialEq)] pub struct Ident { /// The source code location. @@ -1083,44 +976,13 @@ pub struct Ident { pub string: EcoString, } -impl Ident { - /// Create a new identifier from a string checking that it is a valid. - pub fn new( - string: impl AsRef + Into, - span: impl Into, - ) -> Option { - is_ident(string.as_ref()) - .then(|| Self { span: span.into(), string: string.into() }) - } - - /// Create a new identifier from a string and a span. - /// - /// The `string` must be a valid identifier. - #[track_caller] - pub fn new_unchecked(string: impl Into, span: Span) -> Self { - let string = string.into(); - debug_assert!(is_ident(&string), "`{}` is not a valid identifier", string); - Self { span, string } - } - - /// Return a reference to the underlying string. - pub fn as_str(&self) -> &str { - &self.string - } -} - -impl Deref for Ident { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.as_str() - } -} - impl TypedNode for Ident { fn from_red(node: RedRef) -> Option { match node.kind() { - NodeKind::Ident(string) => Some(Ident::new_unchecked(string, node.span())), + NodeKind::Ident(string) => Some(Ident { + span: node.span(), + string: string.clone(), + }), _ => None, } } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 022b51de..fc05ad50 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -5,7 +5,6 @@ mod pretty; mod span; use std::fmt::{self, Debug, Display, Formatter}; -use std::mem; use std::rc::Rc; pub use pretty::*; @@ -40,14 +39,6 @@ impl Green { self.data().kind() } - /// Set the type of the node. - pub fn set_kind(&mut self, kind: NodeKind) { - match self { - Self::Node(node) => Rc::make_mut(node).data.set_kind(kind), - Self::Token(data) => data.set_kind(kind), - } - } - /// The length of the node. pub fn len(&self) -> usize { self.data().len() @@ -68,6 +59,18 @@ impl Green { Green::Token(_) => &[], } } + + /// Change the type of the node. + pub fn convert(&mut self, kind: NodeKind) { + match self { + Self::Node(node) => { + let node = Rc::make_mut(node); + node.erroneous |= kind.is_error(); + node.data.kind = kind; + } + Self::Token(data) => data.kind = kind, + } + } } impl Default for Green { @@ -161,11 +164,6 @@ impl GreenData { &self.kind } - /// Set the type of the node. - pub fn set_kind(&mut self, kind: NodeKind) { - self.kind = kind; - } - /// The length of the node. pub fn len(&self) -> usize { self.len @@ -178,7 +176,82 @@ impl From for Green { } } -/// A borrowed wrapper for a [`GreenNode`] with span information. +/// A owned wrapper for a green node with span information. +/// +/// Owned variant of [`RedRef`]. Can be [cast](Self::cast) to an AST nodes. +#[derive(Clone, PartialEq)] +pub struct RedNode { + id: SourceId, + offset: usize, + green: Green, +} + +impl RedNode { + /// Create a new root node from a [`GreenNode`]. + pub fn new_root(root: Rc, id: SourceId) -> Self { + Self { id, offset: 0, green: root.into() } + } + + /// Convert to a borrowed representation. + pub fn as_ref(&self) -> RedRef<'_> { + RedRef { + id: self.id, + offset: self.offset, + green: &self.green, + } + } + + /// The type of the node. + pub fn kind(&self) -> &NodeKind { + self.as_ref().kind() + } + + /// The length of the node. + pub fn len(&self) -> usize { + self.as_ref().len() + } + + /// The span of the node. + pub fn span(&self) -> Span { + self.as_ref().span() + } + + /// The error messages for this node and its descendants. + pub fn errors(&self) -> Vec { + self.as_ref().errors() + } + + /// Convert the node to a typed AST node. + pub fn cast(self) -> Option + where + T: TypedNode, + { + self.as_ref().cast() + } + + /// The children of the node. + pub fn children(&self) -> Children<'_> { + self.as_ref().children() + } + + /// Get the first child that can cast to some AST type. + pub fn cast_first_child(&self) -> Option { + self.as_ref().cast_first_child() + } + + /// Get the last child that can cast to some AST type. + pub fn cast_last_child(&self) -> Option { + self.as_ref().cast_last_child() + } +} + +impl Debug for RedNode { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.as_ref().fmt(f) + } +} + +/// A borrowed wrapper for a green node with span information. /// /// Borrowed variant of [`RedNode`]. Can be [cast](Self::cast) to an AST node. #[derive(Copy, Clone, PartialEq)] @@ -213,30 +286,25 @@ impl<'a> RedRef<'a> { Span::new(self.id, self.offset, self.offset + self.green.len()) } - /// Whether the node or its children contain an error. - pub fn erroneous(self) -> bool { - self.green.erroneous() - } - /// The error messages for this node and its descendants. pub fn errors(self) -> Vec { - if !self.erroneous() { + 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(), + ErrorPos::Start => self.span().at_start(), + ErrorPos::Full => self.span(), + ErrorPos::End => self.span().at_end(), }; vec![Error::new(span, msg.to_string())] } _ => self .children() - .filter(|red| red.erroneous()) + .filter(|red| red.green.erroneous()) .flat_map(|red| red.errors()) .collect(), } @@ -251,34 +319,28 @@ impl<'a> RedRef<'a> { } /// The node's children. - pub fn children(self) -> impl Iterator> { + pub fn children(self) -> Children<'a> { let children = match &self.green { Green::Node(node) => node.children(), Green::Token(_) => &[], }; - let mut cursor = self.offset; - children.iter().map(move |green| { - let offset = cursor; - cursor += green.len(); - RedRef { id: self.id, offset, green } - }) - } - - /// Get the first child of some type. - pub(crate) fn typed_child(self, kind: &NodeKind) -> Option> { - self.children() - .find(|x| mem::discriminant(x.kind()) == mem::discriminant(kind)) + Children { + id: self.id, + iter: children.iter(), + front: self.offset, + back: self.offset + self.len(), + } } /// Get the first child that can cast to some AST type. - pub(crate) fn cast_first_child(self) -> Option { + pub fn cast_first_child(self) -> Option { self.children().find_map(RedRef::cast) } /// Get the last child that can cast to some AST type. - pub(crate) fn cast_last_child(self) -> Option { - self.children().filter_map(RedRef::cast).last() + pub fn cast_last_child(self) -> Option { + self.children().rev().find_map(RedRef::cast) } } @@ -294,86 +356,41 @@ impl Debug for RedRef<'_> { } } -/// A owned wrapper for a [`GreenNode`] with span information. -/// -/// Owned variant of [`RedRef`]. Can be [cast](Self::cast) to an AST nodes. -#[derive(Clone, PartialEq)] -pub struct RedNode { +/// An iterator over the children of a red node. +pub struct Children<'a> { id: SourceId, - offset: usize, - green: Green, + iter: std::slice::Iter<'a, Green>, + front: usize, + back: usize, } -impl RedNode { - /// Create a new root node from a [`GreenNode`]. - pub fn new_root(root: Rc, id: SourceId) -> Self { - Self { id, offset: 0, green: root.into() } - } - - /// Convert to a borrowed representation. - pub fn as_ref(&self) -> RedRef<'_> { - RedRef { - id: self.id, - offset: self.offset, - green: &self.green, - } - } - - /// The type of the node. - pub fn kind(&self) -> &NodeKind { - self.as_ref().kind() - } - - /// The length of the node. - pub fn len(&self) -> usize { - self.as_ref().len() - } - - /// The span of the node. - pub fn span(&self) -> Span { - self.as_ref().span() - } - - /// The error messages for this node and its descendants. - pub fn errors(&self) -> Vec { - self.as_ref().errors() - } +impl<'a> Iterator for Children<'a> { + type Item = RedRef<'a>; - /// Convert the node to a typed AST node. - pub fn cast(self) -> Option - where - T: TypedNode, - { - self.as_ref().cast() - } - - /// The children of the node. - pub fn children(&self) -> impl Iterator> { - self.as_ref().children() - } - - /// Get the first child of some type. - pub(crate) fn typed_child(&self, kind: &NodeKind) -> Option { - self.as_ref().typed_child(kind).map(RedRef::own) - } - - /// Get the first child that can cast to some AST type. - pub(crate) fn cast_first_child(&self) -> Option { - self.as_ref().cast_first_child() + fn next(&mut self) -> Option { + self.iter.next().map(|green| { + let offset = self.front; + self.front += green.len(); + RedRef { id: self.id, offset, green } + }) } - /// Get the last child that can cast to some AST type. - pub(crate) fn cast_last_child(&self) -> Option { - self.as_ref().cast_last_child() + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() } } -impl Debug for RedNode { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.as_ref().fmt(f) +impl DoubleEndedIterator for Children<'_> { + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|green| { + self.back -= green.len(); + RedRef { id: self.id, offset: self.back, green } + }) } } +impl ExactSizeIterator for Children<'_> {} + /// All syntactical building blocks that can be part of a Typst document. /// /// Can be emitted as a token by the tokenizer or as part of a green node by @@ -533,7 +550,7 @@ pub enum NodeKind { Array, /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. Dict, - /// A named argument: `thickness: 3pt`. + /// A named pair: `thickness: 3pt`. Named, /// A grouped expression: `(1 + 2)`. Group, @@ -582,12 +599,12 @@ pub enum NodeKind { /// The comment can contain nested block comments. BlockComment, /// Tokens that appear in the wrong place. - Error(ErrorPosition, EcoString), + Error(ErrorPos, EcoString), /// Unknown character sequences. Unknown(EcoString), } -/// Payload of a raw block: `` `...` ``. +/// Payload of a raw block node. #[derive(Debug, Clone, PartialEq)] pub struct RawData { /// The raw text in the block. @@ -600,19 +617,19 @@ pub struct RawData { pub block: bool, } -/// Payload of a math formula: `$2pi + x$` or `$[f'(x) = x^2]$`. +/// Payload of a math formula node. #[derive(Debug, Clone, PartialEq)] pub struct MathData { - /// The formula between the dollars. + /// The formula between the dollars / brackets. pub formula: EcoString, /// Whether the formula is display-level, that is, it is surrounded by - /// `$[..]`. + /// `$[..]$`. pub display: bool, } /// Where in a node an error should be annotated. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum ErrorPosition { +pub enum ErrorPos { /// At the start of the node. Start, /// Over the full width of the node. diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index b396a39c..fa423e94 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -141,7 +141,7 @@ impl Pretty for RawNode { // Language tag. if let Some(lang) = &self.lang { - lang.pretty(p); + p.push_str(lang); } // Start untrimming. @@ -492,7 +492,7 @@ impl Pretty for Imports { fn pretty(&self, p: &mut Printer) { match self { Self::Wildcard => p.push('*'), - Self::Idents(idents) => { + Self::Items(idents) => { p.join(idents, ", ", |item, p| item.pretty(p)); } } @@ -508,7 +508,7 @@ impl Pretty for IncludeExpr { impl Pretty for Ident { fn pretty(&self, p: &mut Printer) { - p.push_str(self.as_str()); + p.push_str(&self.string); } } -- cgit v1.2.3 From 75fffc1f9b6ef8bf258b2b1845a4ba74a0f5f2c1 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 7 Nov 2021 23:31:42 +0100 Subject: Fine-grained capturing --- src/syntax/ast.rs | 350 +++++++++++++++++++++++++-------------------------- src/syntax/mod.rs | 15 ++- src/syntax/pretty.rs | 26 ++-- src/syntax/span.rs | 5 + 4 files changed, 202 insertions(+), 194 deletions(-) (limited to 'src/syntax') diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index dc71e229..067bd6da 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -1,6 +1,8 @@ //! A typed layer over the red-green tree. -use super::{NodeKind, RedNode, RedRef, Span}; +use std::ops::Deref; + +use super::{Green, GreenData, NodeKind, RedNode, RedRef, Span}; use crate::geom::{AngularUnit, LengthUnit}; use crate::util::EcoString; @@ -8,13 +10,24 @@ use crate::util::EcoString; pub trait TypedNode: Sized { /// Convert from a red node to a typed node. fn from_red(value: RedRef) -> Option; + + /// A reference to the underlying red node. + fn as_red(&self) -> RedRef<'_>; + + /// The source code location. + fn span(&self) -> Span { + self.as_red().span() + } } macro_rules! node { ($(#[$attr:meta])* $name:ident) => { - node!{$(#[$attr])* $name => $name} + node!{$(#[$attr])* $name: $name} }; - ($(#[$attr:meta])* $variant:ident => $name:ident) => { + ($(#[$attr:meta])* $name:ident: $variant:ident) => { + node!{$(#[$attr])* $name: NodeKind::$variant} + }; + ($(#[$attr:meta])* $name:ident: $($variant:pat)|*) => { #[derive(Debug, Clone, PartialEq)] #[repr(transparent)] $(#[$attr])* @@ -22,22 +35,14 @@ macro_rules! node { impl TypedNode for $name { fn from_red(node: RedRef) -> Option { - if node.kind() != &NodeKind::$variant { - return None; + if matches!(node.kind(), $($variant)|*) { + Some(Self(node.own())) + } else { + None } - - Some(Self(node.own())) } - } - impl $name { - /// The source code location. - pub fn span(&self) -> Span { - self.0.span() - } - - /// The underlying red node. - pub fn as_red(&self) -> RedRef { + fn as_red(&self) -> RedRef<'_> { self.0.as_ref() } } @@ -52,7 +57,27 @@ node! { impl Markup { /// The markup nodes. pub fn nodes(&self) -> impl Iterator + '_ { - self.0.children().filter_map(RedRef::cast) + self.0.children().filter_map(|node| 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(c) => Some(MarkupNode::Text((*c).into())), + NodeKind::EnDash => Some(MarkupNode::Text("\u{2013}".into())), + NodeKind::EmDash => Some(MarkupNode::Text("\u{2014}".into())), + NodeKind::NonBreakingSpace => Some(MarkupNode::Text("\u{00A0}".into())), + NodeKind::Raw(raw) => Some(MarkupNode::Raw(RawNode { + block: raw.block, + lang: raw.lang.clone(), + text: raw.text.clone(), + })), + NodeKind::Heading => node.cast().map(MarkupNode::Heading), + NodeKind::List => node.cast().map(MarkupNode::List), + NodeKind::Enum => node.cast().map(MarkupNode::Enum), + _ => node.cast().map(MarkupNode::Expr), + }) } } @@ -83,28 +108,6 @@ pub enum MarkupNode { Expr(Expr), } -impl TypedNode for MarkupNode { - fn from_red(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(c) => Some(MarkupNode::Text((*c).into())), - NodeKind::EnDash => Some(MarkupNode::Text("\u{2013}".into())), - NodeKind::EmDash => Some(MarkupNode::Text("\u{2014}".into())), - NodeKind::NonBreakingSpace => Some(MarkupNode::Text("\u{00A0}".into())), - 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), - _ => node.cast().map(MarkupNode::Expr), - } - } -} - /// A raw block with optional syntax highlighting: `` `...` ``. #[derive(Debug, Clone, PartialEq)] pub struct RawNode { @@ -118,22 +121,9 @@ pub struct RawNode { pub block: bool, } -impl TypedNode for RawNode { - fn from_red(node: RedRef) -> Option { - match node.kind() { - NodeKind::Raw(raw) => Some(Self { - block: raw.block, - lang: raw.lang.clone(), - text: raw.text.clone(), - }), - _ => None, - } - } -} - node! { /// A section heading: `= Introduction`. - Heading => HeadingNode + HeadingNode: Heading } impl HeadingNode { @@ -154,7 +144,7 @@ impl HeadingNode { node! { /// An item in an unordered list: `- ...`. - List => ListNode + ListNode: List } impl ListNode { @@ -166,7 +156,7 @@ impl ListNode { node! { /// An item in an enumeration (ordered list): `1. ...`. - Enum => EnumNode + EnumNode: Enum } impl EnumNode { @@ -190,10 +180,10 @@ impl EnumNode { /// An expression. #[derive(Debug, Clone, PartialEq)] pub enum Expr { - /// An identifier: `left`. - Ident(Ident), /// A literal: `1`, `true`, ... Lit(Lit), + /// An identifier: `left`. + Ident(Ident), /// An array expression: `(1, "hi", 12cm)`. Array(ArrayExpr), /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. @@ -251,6 +241,29 @@ impl TypedNode for Expr { _ => node.cast().map(Self::Lit), } } + + fn as_red(&self) -> RedRef<'_> { + match self { + Self::Lit(v) => v.as_red(), + Self::Ident(v) => v.as_red(), + Self::Array(v) => v.as_red(), + Self::Dict(v) => v.as_red(), + Self::Template(v) => v.as_red(), + Self::Group(v) => v.as_red(), + Self::Block(v) => v.as_red(), + Self::Unary(v) => v.as_red(), + Self::Binary(v) => v.as_red(), + Self::Call(v) => v.as_red(), + Self::Closure(v) => v.as_red(), + Self::With(v) => v.as_red(), + Self::Let(v) => v.as_red(), + Self::If(v) => v.as_red(), + Self::While(v) => v.as_red(), + Self::For(v) => v.as_red(), + Self::Import(v) => v.as_red(), + Self::Include(v) => v.as_red(), + } + } } impl Expr { @@ -267,99 +280,72 @@ impl Expr { | 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(), +node! { + /// A literal: `1`, `true`, ... + Lit: NodeKind::None + | NodeKind::Auto + | NodeKind::Bool(_) + | NodeKind::Int(_) + | NodeKind::Float(_) + | NodeKind::Length(_, _) + | NodeKind::Angle(_, _) + | NodeKind::Percentage(_) + | NodeKind::Fraction(_) + | NodeKind::Str(_) +} + +impl Lit { + /// The kind of literal. + pub fn kind(&self) -> LitKind { + match *self.0.kind() { + NodeKind::None => LitKind::None, + NodeKind::Auto => LitKind::Auto, + NodeKind::Bool(v) => LitKind::Bool(v), + NodeKind::Int(v) => LitKind::Int(v), + NodeKind::Float(v) => LitKind::Float(v), + NodeKind::Length(v, unit) => LitKind::Length(v, unit), + NodeKind::Angle(v, unit) => LitKind::Angle(v, unit), + NodeKind::Percentage(v) => LitKind::Percent(v), + NodeKind::Fraction(v) => LitKind::Fractional(v), + NodeKind::Str(ref v) => LitKind::Str(v.clone()), + _ => panic!("literal is of wrong kind"), } } } -/// A literal: `1`, `true`, ... +/// The kind of a literal. #[derive(Debug, Clone, PartialEq)] -pub enum Lit { +pub enum LitKind { /// The none literal: `none`. - None(Span), + None, /// The auto literal: `auto`. - Auto(Span), + Auto, /// A boolean literal: `true`, `false`. - Bool(Span, bool), + Bool(bool), /// An integer literal: `120`. - Int(Span, i64), + Int(i64), /// A floating-point literal: `1.2`, `10e-4`. - Float(Span, f64), + Float(f64), /// A length literal: `12pt`, `3cm`. - Length(Span, f64, LengthUnit), + Length(f64, LengthUnit), /// An angle literal: `1.5rad`, `90deg`. - Angle(Span, f64, AngularUnit), + Angle(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), + Percent(f64), /// A fraction unit literal: `1fr`. - Fractional(Span, f64), + Fractional(f64), /// A string literal: `"hello!"`. - Str(Span, EcoString), -} - -impl TypedNode for Lit { - fn from_red(node: RedRef) -> Option { - match *node.kind() { - NodeKind::None => Some(Self::None(node.span())), - NodeKind::Auto => Some(Self::Auto(node.span())), - NodeKind::Bool(v) => Some(Self::Bool(node.span(), v)), - NodeKind::Int(v) => Some(Self::Int(node.span(), v)), - NodeKind::Float(v) => Some(Self::Float(node.span(), v)), - NodeKind::Length(v, unit) => Some(Self::Length(node.span(), v, unit)), - NodeKind::Angle(v, unit) => Some(Self::Angle(node.span(), v, unit)), - NodeKind::Percentage(v) => Some(Self::Percent(node.span(), v)), - NodeKind::Fraction(v) => Some(Self::Fractional(node.span(), v)), - NodeKind::Str(ref v) => Some(Self::Str(node.span(), v.clone())), - _ => None, - } - } -} - -impl Lit { - /// The source code location. - 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, - } - } + Str(EcoString), } node! { /// An array expression: `(1, "hi", 12cm)`. - Array => ArrayExpr + ArrayExpr: Array } impl ArrayExpr { @@ -371,7 +357,7 @@ impl ArrayExpr { node! { /// A dictionary expression: `(thickness: 3pt, pattern: dashed)`. - Dict => DictExpr + DictExpr: Dict } impl DictExpr { @@ -400,7 +386,7 @@ impl Named { node! { /// A template expression: `[*Hi* there!]`. - Template => TemplateExpr + TemplateExpr: Template } impl TemplateExpr { @@ -412,7 +398,7 @@ impl TemplateExpr { node! { /// A grouped expression: `(1 + 2)`. - Group => GroupExpr + GroupExpr: Group } impl GroupExpr { @@ -424,7 +410,7 @@ impl GroupExpr { node! { /// A block expression: `{ let x = 1; x + 2 }`. - Block => BlockExpr + BlockExpr: Block } impl BlockExpr { @@ -436,14 +422,15 @@ impl BlockExpr { node! { /// A unary operation: `-x`. - Unary => UnaryExpr + UnaryExpr: Unary } impl UnaryExpr { /// The operator: `-`. pub fn op(&self) -> UnOp { self.0 - .cast_first_child() + .children() + .find_map(|node| UnOp::from_token(node.kind())) .expect("unary expression is missing operator") } @@ -464,12 +451,6 @@ pub enum UnOp { Not, } -impl TypedNode for UnOp { - fn from_red(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 { @@ -501,14 +482,15 @@ impl UnOp { node! { /// A binary operation: `a + b`. - Binary => BinaryExpr + BinaryExpr: Binary } impl BinaryExpr { /// The binary operator: `+`. pub fn op(&self) -> BinOp { self.0 - .cast_first_child() + .children() + .find_map(|node| BinOp::from_token(node.kind())) .expect("binary expression is missing operator") } @@ -566,12 +548,6 @@ pub enum BinOp { DivAssign, } -impl TypedNode for BinOp { - fn from_red(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 { @@ -671,7 +647,7 @@ pub enum Associativity { node! { /// An invocation of a function: `foo(...)`. - Call => CallExpr + CallExpr: Call } impl CallExpr { @@ -717,6 +693,14 @@ impl TypedNode for CallArg { _ => node.cast().map(CallArg::Pos), } } + + fn as_red(&self) -> RedRef<'_> { + match self { + Self::Pos(v) => v.as_red(), + Self::Named(v) => v.as_red(), + Self::Spread(v) => v.as_red(), + } + } } impl CallArg { @@ -732,7 +716,7 @@ impl CallArg { node! { /// A closure expression: `(x, y) => z`. - Closure => ClosureExpr + ClosureExpr: Closure } impl ClosureExpr { @@ -779,6 +763,14 @@ impl TypedNode for ClosureParam { _ => None, } } + + fn as_red(&self) -> RedRef<'_> { + match self { + Self::Pos(v) => v.as_red(), + Self::Named(v) => v.as_red(), + Self::Sink(v) => v.as_red(), + } + } } node! { @@ -840,7 +832,17 @@ node! { impl ImportExpr { /// The items to be imported. pub fn imports(&self) -> Imports { - self.0.cast_first_child().expect("import is missing items") + self.0 + .children() + .find_map(|node| match node.kind() { + NodeKind::Star => Some(Imports::Wildcard), + NodeKind::ImportItems => { + let items = node.children().filter_map(RedRef::cast).collect(); + Some(Imports::Items(items)) + } + _ => None, + }) + .expect("import is missing items") } /// The location of the importable file. @@ -858,19 +860,6 @@ pub enum Imports { Items(Vec), } -impl TypedNode for Imports { - fn from_red(node: RedRef) -> Option { - match node.kind() { - NodeKind::Star => Some(Imports::Wildcard), - NodeKind::ImportItems => { - let items = node.children().filter_map(RedRef::cast).collect(); - Some(Imports::Items(items)) - } - _ => None, - } - } -} - node! { /// An include expression: `include "chapter1.typ"`. IncludeExpr @@ -967,23 +956,28 @@ impl ForPattern { } } -/// An identifier. -#[derive(Debug, Clone, PartialEq)] -pub struct Ident { - /// The source code location. - pub span: Span, - /// The identifier string. - pub string: EcoString, +node! { + /// An identifier. + Ident: NodeKind::Ident(_) } -impl TypedNode for Ident { - fn from_red(node: RedRef) -> Option { - match node.kind() { - NodeKind::Ident(string) => Some(Ident { - span: node.span(), - string: string.clone(), - }), - _ => None, +impl Ident { + /// Take out the contained [`EcoString`]. + pub fn take(self) -> EcoString { + match self.0.green { + Green::Token(GreenData { kind: NodeKind::Ident(id), .. }) => id, + _ => panic!("identifier is of wrong kind"), + } + } +} + +impl Deref for Ident { + type Target = str; + + fn deref(&self) -> &Self::Target { + match &self.0.green { + Green::Token(GreenData { kind: NodeKind::Ident(id), .. }) => id, + _ => panic!("identifier is of wrong kind"), } } } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index fc05ad50..0660d57b 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -187,11 +187,20 @@ pub struct RedNode { } impl RedNode { - /// Create a new root node from a [`GreenNode`]. - pub fn new_root(root: Rc, id: SourceId) -> Self { + /// Create a new red node from a root [`GreenNode`]. + pub fn from_root(root: Rc, id: SourceId) -> Self { Self { id, offset: 0, green: root.into() } } + /// Create a new red node from a node kind and a span. + pub fn from_data(kind: NodeKind, span: Span) -> Self { + Self { + id: span.source, + offset: span.start, + green: Green::Token(GreenData { kind, len: span.len() }), + } + } + /// Convert to a borrowed representation. pub fn as_ref(&self) -> RedRef<'_> { RedRef { @@ -540,7 +549,7 @@ pub enum NodeKind { /// A percentage: `50%`. /// /// _Note_: `50%` is stored as `50.0` here, as in the corresponding - /// [literal](ast::Lit::Percent). + /// [literal](ast::LitKind::Percent). Percentage(f64), /// A fraction unit: `3fr`. Fraction(f64), diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index fa423e94..9e4510b6 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -198,8 +198,8 @@ impl Pretty for EnumNode { impl Pretty for Expr { fn pretty(&self, p: &mut Printer) { match self { - Self::Ident(v) => v.pretty(p), Self::Lit(v) => v.pretty(p), + Self::Ident(v) => v.pretty(p), Self::Array(v) => v.pretty(p), Self::Dict(v) => v.pretty(p), Self::Template(v) => v.pretty(p), @@ -222,17 +222,17 @@ impl Pretty for Expr { impl Pretty for Lit { fn pretty(&self, p: &mut Printer) { - match self { - Self::None(_) => p.push_str("none"), - Self::Auto(_) => p.push_str("auto"), - Self::Bool(_, v) => write!(p, "{}", v).unwrap(), - Self::Int(_, v) => write!(p, "{}", v).unwrap(), - Self::Float(_, v) => write!(p, "{}", v).unwrap(), - Self::Length(_, v, u) => write!(p, "{}{:?}", v, u).unwrap(), - Self::Angle(_, v, u) => write!(p, "{}{:?}", v, u).unwrap(), - Self::Percent(_, v) => write!(p, "{}%", v).unwrap(), - Self::Fractional(_, v) => write!(p, "{}fr", v).unwrap(), - Self::Str(_, v) => write!(p, "{:?}", v).unwrap(), + match self.kind() { + LitKind::None => p.push_str("none"), + LitKind::Auto => p.push_str("auto"), + LitKind::Bool(v) => write!(p, "{}", v).unwrap(), + LitKind::Int(v) => write!(p, "{}", v).unwrap(), + LitKind::Float(v) => write!(p, "{}", v).unwrap(), + LitKind::Length(v, u) => write!(p, "{}{:?}", v, u).unwrap(), + LitKind::Angle(v, u) => write!(p, "{}{:?}", v, u).unwrap(), + LitKind::Percent(v) => write!(p, "{}%", v).unwrap(), + LitKind::Fractional(v) => write!(p, "{}fr", v).unwrap(), + LitKind::Str(v) => write!(p, "{:?}", v).unwrap(), } } } @@ -508,7 +508,7 @@ impl Pretty for IncludeExpr { impl Pretty for Ident { fn pretty(&self, p: &mut Printer) { - p.push_str(&self.string); + p.push_str(self); } } diff --git a/src/syntax/span.rs b/src/syntax/span.rs index c26011bd..47d96589 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -88,6 +88,11 @@ impl Span { Self { end, ..self } } + /// The byte length of the spanned region. + pub fn len(self) -> usize { + self.end - self.start + } + /// A new span at the position of this span's start. pub fn at_start(&self) -> Span { Self::at(self.source, self.start) -- cgit v1.2.3 From 38c5c362419c5eee7a4fdc0b43d3a9dfb339a6d2 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 8 Nov 2021 12:13:32 +0100 Subject: Final touches --- src/syntax/ast.rs | 27 +++++++++++++--------- src/syntax/mod.rs | 43 ++++------------------------------- src/syntax/pretty.rs | 64 ++++++++++++++++++++++++++++++++-------------------- 3 files changed, 59 insertions(+), 75 deletions(-) (limited to 'src/syntax') diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 067bd6da..288c749a 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -68,11 +68,8 @@ impl Markup { NodeKind::EnDash => Some(MarkupNode::Text("\u{2013}".into())), NodeKind::EmDash => Some(MarkupNode::Text("\u{2014}".into())), NodeKind::NonBreakingSpace => Some(MarkupNode::Text("\u{00A0}".into())), - NodeKind::Raw(raw) => Some(MarkupNode::Raw(RawNode { - block: raw.block, - lang: raw.lang.clone(), - text: raw.text.clone(), - })), + NodeKind::Math(math) => Some(MarkupNode::Math(math.as_ref().clone())), + NodeKind::Raw(raw) => Some(MarkupNode::Raw(raw.as_ref().clone())), NodeKind::Heading => node.cast().map(MarkupNode::Heading), NodeKind::List => node.cast().map(MarkupNode::List), NodeKind::Enum => node.cast().map(MarkupNode::Enum), @@ -98,6 +95,8 @@ pub enum MarkupNode { Text(EcoString), /// A raw block with optional syntax highlighting: `` `...` ``. Raw(RawNode), + /// A math formula: `$a^2 = b^2 + c^2$`. + Math(MathNode), /// A section heading: `= Introduction`. Heading(HeadingNode), /// An item in an unordered list: `- ...`. @@ -121,6 +120,16 @@ pub struct RawNode { pub block: bool, } +/// A math formula: `$a^2 + b^2 = c^2$`. +#[derive(Debug, Clone, PartialEq)] +pub struct MathNode { + /// The formula between the dollars / brackets. + pub formula: EcoString, + /// Whether the formula is display-level, that is, it is surrounded by + /// `$[..]$`. + pub display: bool, +} + node! { /// A section heading: `= Introduction`. HeadingNode: Heading @@ -133,12 +142,8 @@ impl HeadingNode { } /// The section depth (numer of equals signs). - pub fn level(&self) -> u8 { - self.0 - .children() - .filter(|n| n.kind() == &NodeKind::Eq) - .count() - .min(u8::MAX.into()) as u8 + pub fn level(&self) -> usize { + self.0.children().filter(|n| n.kind() == &NodeKind::Eq).count() } } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 0660d57b..ca6ed243 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -10,7 +10,7 @@ use std::rc::Rc; pub use pretty::*; pub use span::*; -use self::ast::TypedNode; +use self::ast::{MathNode, RawNode, TypedNode}; use crate::diag::Error; use crate::geom::{AngularUnit, LengthUnit}; use crate::source::SourceId; @@ -178,7 +178,7 @@ impl From for Green { /// A owned wrapper for a green node with span information. /// -/// Owned variant of [`RedRef`]. Can be [cast](Self::cast) to an AST nodes. +/// Owned variant of [`RedRef`]. Can be [cast](Self::cast) to an AST node. #[derive(Clone, PartialEq)] pub struct RedNode { id: SourceId, @@ -192,15 +192,6 @@ impl RedNode { Self { id, offset: 0, green: root.into() } } - /// Create a new red node from a node kind and a span. - pub fn from_data(kind: NodeKind, span: Span) -> Self { - Self { - id: span.source, - offset: span.start, - green: Green::Token(GreenData { kind, len: span.len() }), - } - } - /// Convert to a borrowed representation. pub fn as_ref(&self) -> RedRef<'_> { RedRef { @@ -527,13 +518,11 @@ pub enum NodeKind { 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), + Raw(Rc), /// Dollar signs surrounding inner contents. - Math(Rc), + Math(Rc), /// An identifier: `center`. Ident(EcoString), /// A boolean: `true`, `false`. @@ -613,29 +602,6 @@ pub enum NodeKind { Unknown(EcoString), } -/// Payload of a raw block node. -#[derive(Debug, Clone, PartialEq)] -pub struct RawData { - /// 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, -} - -/// Payload of a math formula node. -#[derive(Debug, Clone, PartialEq)] -pub struct MathData { - /// The formula between the dollars / brackets. - pub formula: EcoString, - /// Whether the formula is display-level, that is, it is surrounded by - /// `$[..]$`. - pub display: bool, -} - /// Where in a node an error should be annotated. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ErrorPos { @@ -730,7 +696,6 @@ impl NodeKind { 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", diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 9e4510b6..c453fb56 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -63,7 +63,6 @@ impl Printer { write_item(item, self); count += 1; } - count } @@ -99,6 +98,7 @@ impl Pretty for MarkupNode { Self::Emph => p.push('_'), Self::Text(text) => p.push_str(text), Self::Raw(raw) => raw.pretty(p), + Self::Math(math) => math.pretty(p), Self::Heading(heading) => heading.pretty(p), Self::List(list) => list.pretty(p), Self::Enum(enum_) => enum_.pretty(p), @@ -168,6 +168,20 @@ impl Pretty for RawNode { } } +impl Pretty for MathNode { + fn pretty(&self, p: &mut Printer) { + p.push('$'); + if self.display { + p.push('['); + } + p.push_str(&self.formula); + if self.display { + p.push(']'); + } + p.push('$'); + } +} + impl Pretty for HeadingNode { fn pretty(&self, p: &mut Printer) { for _ in 0 .. self.level() { @@ -253,12 +267,9 @@ impl Pretty for ArrayExpr { impl Pretty for DictExpr { fn pretty(&self, p: &mut Printer) { p.push('('); - - let mut items = self.items().peekable(); - if items.peek().is_none() { + let len = p.join(self.items(), ", ", |named, p| named.pretty(p)); + if len == 0 { p.push(':'); - } else { - p.join(items, ", ", |named, p| named.pretty(p)); } p.push(')'); } @@ -291,13 +302,11 @@ impl Pretty for GroupExpr { impl Pretty for BlockExpr { fn pretty(&self, p: &mut Printer) { p.push('{'); - - let exprs: Vec<_> = self.exprs().collect(); - if exprs.len() > 1 { + if self.exprs().count() > 1 { p.push(' '); } - p.join(&exprs, "; ", |expr, p| expr.pretty(p)); - if exprs.len() > 1 { + let len = p.join(self.exprs(), "; ", |expr, p| expr.pretty(p)); + if len > 1 { p.push(' '); } p.push('}'); @@ -348,17 +357,17 @@ impl Pretty for CallExpr { }; let args: Vec<_> = self.args().items().collect(); - - if let Some(Expr::Template(template)) = args - .last() - .and_then(|x| if let CallArg::Pos(arg) = x { Some(arg) } else { None }) - { - if args.len() > 1 { - write_args(&args[0 .. args.len() - 1]); + match args.as_slice() { + // This can be moved behind the arguments. + // + // Example: Transforms "#v(a, [b])" => "#v(a)[b]". + [head @ .., CallArg::Pos(Expr::Template(template))] => { + if !head.is_empty() { + write_args(head); + } + template.pretty(p); } - template.pretty(p); - } else { - write_args(&args); + items => write_args(items), } } } @@ -423,12 +432,12 @@ impl Pretty for LetExpr { fn pretty(&self, p: &mut Printer) { p.push_str("let "); self.binding().pretty(p); - if let Some(Expr::Closure(closure)) = &self.init() { + if let Some(Expr::Closure(closure)) = self.init() { p.push('('); p.join(closure.params(), ", ", |item, p| item.pretty(p)); p.push_str(") = "); closure.body().pretty(p); - } else if let Some(init) = &self.init() { + } else if let Some(init) = self.init() { p.push_str(" = "); init.pretty(p); } @@ -441,7 +450,7 @@ impl Pretty for IfExpr { self.condition().pretty(p); p.push(' '); self.if_body().pretty(p); - if let Some(expr) = &self.else_body() { + if let Some(expr) = self.else_body() { p.push_str(" else "); expr.pretty(p); } @@ -525,7 +534,7 @@ mod tests { #[track_caller] fn test_parse(src: &str, expected: &str) { let source = SourceFile::detached(src); - let ast: Markup = source.ast().unwrap(); + let ast = source.ast().unwrap(); let found = pretty(&ast); if found != expected { println!("tree: {:#?}", ast); @@ -563,6 +572,11 @@ mod tests { test_parse("``` 1```", "`1`"); test_parse("``` 1 ```", "`1 `"); test_parse("```` ` ````", "``` ` ```"); + + // Math node. + roundtrip("$$"); + roundtrip("$a+b$"); + roundtrip("$[ a^2 + b^2 = c^2 ]$"); } #[test] -- cgit v1.2.3