diff options
Diffstat (limited to 'src/syntax')
| -rw-r--r-- | src/syntax/ast.rs | 6 | ||||
| -rw-r--r-- | src/syntax/highlight.rs | 3 | ||||
| -rw-r--r-- | src/syntax/mod.rs | 142 |
3 files changed, 143 insertions, 8 deletions
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index ae8ecdc9..bea4ef00 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -53,7 +53,7 @@ macro_rules! node { node! { /// The syntactical root capable of representing a full parsed document. - Markup + Markup: NodeKind::Markup(_) } impl Markup { @@ -65,7 +65,9 @@ impl Markup { NodeKind::Parbreak => Some(MarkupNode::Parbreak), NodeKind::Strong => Some(MarkupNode::Strong), NodeKind::Emph => Some(MarkupNode::Emph), - NodeKind::Text(s) => Some(MarkupNode::Text(s.clone())), + NodeKind::Text(s) | NodeKind::TextInLine(s) => { + Some(MarkupNode::Text(s.clone())) + } NodeKind::Escape(c) => Some(MarkupNode::Text((*c).into())), NodeKind::EnDash => Some(MarkupNode::Text('\u{2013}'.into())), NodeKind::EmDash => Some(MarkupNode::Text('\u{2014}'.into())), diff --git a/src/syntax/highlight.rs b/src/syntax/highlight.rs index 85fbef12..9f7365a8 100644 --- a/src/syntax/highlight.rs +++ b/src/syntax/highlight.rs @@ -154,10 +154,11 @@ impl Category { NodeKind::Str(_) => Some(Category::String), NodeKind::Error(_, _) => Some(Category::Invalid), NodeKind::Unknown(_) => Some(Category::Invalid), - NodeKind::Markup => None, + NodeKind::Markup(_) => None, NodeKind::Space(_) => None, NodeKind::Parbreak => None, NodeKind::Text(_) => None, + NodeKind::TextInLine(_) => None, NodeKind::List => None, NodeKind::Enum => None, NodeKind::Array => None, diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index d9ad42a8..3a0f3a5e 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -6,6 +6,7 @@ mod pretty; mod span; use std::fmt::{self, Debug, Display, Formatter}; +use std::ops::Range; use std::rc::Rc; pub use highlight::*; @@ -15,6 +16,7 @@ pub use span::*; use self::ast::{MathNode, RawNode, TypedNode}; use crate::diag::Error; use crate::geom::{AngularUnit, LengthUnit}; +use crate::parse::TokenMode; use crate::source::SourceId; use crate::util::EcoString; @@ -62,6 +64,14 @@ impl Green { } } + /// Whether the node is a leaf node in the green tree. + pub fn is_leaf(&self) -> bool { + match self { + Green::Node(n) => n.children().is_empty(), + Green::Token(_) => true, + } + } + /// Change the type of the node. pub fn convert(&mut self, kind: NodeKind) { match self { @@ -127,6 +137,52 @@ impl GreenNode { pub fn children(&self) -> &[Green] { &self.children } + + /// The node's metadata. + fn data(&self) -> &GreenData { + &self.data + } + + /// The node's type. + pub fn kind(&self) -> &NodeKind { + self.data().kind() + } + + /// The node's length. + pub fn len(&self) -> usize { + self.data().len() + } + + /// The node's children, mutably. + pub(crate) fn children_mut(&mut self) -> &mut [Green] { + &mut self.children + } + + /// Replaces a range of children with some replacement. + pub(crate) fn replace_children( + &mut self, + range: Range<usize>, + replacement: Vec<Green>, + ) { + let superseded = &self.children[range.clone()]; + let superseded_len: usize = superseded.iter().map(Green::len).sum(); + let replacement_len: usize = replacement.iter().map(Green::len).sum(); + + // If we're erroneous, but not due to the superseded range, then we will + // still be erroneous after the replacement. + let still_erroneous = self.erroneous && !superseded.iter().any(Green::erroneous); + + self.children.splice(range, replacement); + self.data.len = self.data.len + replacement_len - superseded_len; + self.erroneous = still_erroneous || self.children.iter().any(Green::erroneous); + } + + /// Update the length of this node given the old and new length of + /// replaced children. + pub(crate) fn update_parent(&mut self, new_len: usize, old_len: usize) { + self.data.len = self.data.len() + new_len - old_len; + self.erroneous = self.children.iter().any(Green::erroneous); + } } impl From<GreenNode> for Green { @@ -266,7 +322,7 @@ impl Debug for RedNode { } } -/// A borrowed wrapper for a green node with span information. +/// 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)] @@ -301,6 +357,11 @@ impl<'a> RedRef<'a> { Span::new(self.id, self.offset, self.offset + self.green.len()) } + /// Whether the node is a leaf node. + pub fn is_leaf(self) -> bool { + self.green.is_leaf() + } + /// The error messages for this node and its descendants. pub fn errors(self) -> Vec<Error> { if !self.green.erroneous() { @@ -325,6 +386,15 @@ impl<'a> RedRef<'a> { } } + /// Returns all leaf descendants of this node (may include itself). + pub fn leafs(self) -> Vec<Self> { + if self.is_leaf() { + vec![self] + } else { + self.children().flat_map(Self::leafs).collect() + } + } + /// Convert the node to a typed AST node. pub fn cast<T>(self) -> Option<T> where @@ -502,8 +572,8 @@ pub enum NodeKind { Include, /// The `from` keyword. From, - /// Template markup. - Markup, + /// Template markup of which all lines must start in some column. + Markup(usize), /// One or more whitespace characters. Space(usize), /// A forced line break: `\`. @@ -512,6 +582,8 @@ pub enum NodeKind { Parbreak, /// A consecutive non-markup string. Text(EcoString), + /// A text node that cannot appear at the beginning of a source line. + TextInLine(EcoString), /// A non-breaking space: `~`. NonBreakingSpace, /// An en-dash: `--`. @@ -648,11 +720,71 @@ impl NodeKind { matches!(self, Self::LeftParen | Self::RightParen) } + /// Whether this is whitespace. + pub fn is_whitespace(&self) -> bool { + matches!(self, Self::Space(_) | Self::Parbreak) + } + + /// Whether this is trivia. + pub fn is_trivia(&self) -> bool { + self.is_whitespace() || matches!(self, Self::LineComment | Self::BlockComment) + } + /// Whether this is some kind of error. pub fn is_error(&self) -> bool { matches!(self, NodeKind::Error(_, _) | NodeKind::Unknown(_)) } + /// Whether this node is `at_start` given the previous value of the property. + pub fn is_at_start(&self, prev: bool) -> bool { + match self { + Self::Space(n) if *n > 0 => true, + Self::Parbreak => true, + Self::LineComment | Self::BlockComment => prev, + _ => false, + } + } + + /// Whether this token appears in Markup. + pub fn mode(&self) -> Option<TokenMode> { + match self { + Self::Markup(_) + | Self::Linebreak + | Self::Parbreak + | Self::Text(_) + | Self::TextInLine(_) + | Self::NonBreakingSpace + | Self::EnDash + | Self::EmDash + | Self::Escape(_) + | Self::Strong + | Self::Emph + | Self::Heading + | Self::Enum + | Self::EnumNumbering(_) + | Self::List + | Self::Raw(_) + | Self::Math(_) => Some(TokenMode::Markup), + Self::Template + | Self::Space(_) + | Self::Block + | Self::Ident(_) + | Self::LetExpr + | Self::IfExpr + | Self::WhileExpr + | Self::ForExpr + | Self::ImportExpr + | Self::Call + | Self::IncludeExpr + | Self::LineComment + | Self::BlockComment + | Self::Error(_, _) + | Self::Minus + | Self::Eq => None, + _ => Some(TokenMode::Code), + } + } + /// A human-readable name for the kind. pub fn as_str(&self) -> &'static str { match self { @@ -701,11 +833,11 @@ impl NodeKind { Self::Import => "keyword `import`", Self::Include => "keyword `include`", Self::From => "keyword `from`", - Self::Markup => "markup", + Self::Markup(_) => "markup", Self::Space(_) => "space", Self::Linebreak => "forced linebreak", Self::Parbreak => "paragraph break", - Self::Text(_) => "text", + Self::Text(_) | Self::TextInLine(_) => "text", Self::NonBreakingSpace => "non-breaking space", Self::EnDash => "en dash", Self::EmDash => "em dash", |
