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