Rename two syntax types

This commit is contained in:
Laurenz 2022-11-22 14:48:08 +01:00
parent 2ce727fc95
commit b476de87b7
11 changed files with 825 additions and 821 deletions

View File

@ -11,7 +11,7 @@ use super::{
}; };
use crate::diag::{bail, error, At, SourceResult, StrResult, Trace, Tracepoint}; use crate::diag::{bail, error, At, SourceResult, StrResult, Trace, Tracepoint};
use crate::geom::{Abs, Angle, Em, Fr, Ratio}; use crate::geom::{Abs, Angle, Em, Fr, Ratio};
use crate::syntax::ast::TypedNode; use crate::syntax::ast::AstNode;
use crate::syntax::{ast, SourceId, Span, Spanned, Unit}; use crate::syntax::{ast, SourceId, Span, Spanned, Unit};
use crate::util::{format_eco, EcoString}; use crate::util::{format_eco, EcoString};
use crate::World; use crate::World;

View File

@ -8,7 +8,7 @@ use super::{
Args, Eval, Flow, Node, NodeId, Route, Scope, Scopes, Selector, StyleMap, Value, Vm, Args, Eval, Flow, Node, NodeId, Route, Scope, Scopes, Selector, StyleMap, Value, Vm,
}; };
use crate::diag::{bail, SourceResult, StrResult}; use crate::diag::{bail, SourceResult, StrResult};
use crate::syntax::ast::{self, Expr, TypedNode}; use crate::syntax::ast::{self, AstNode, Expr};
use crate::syntax::{SourceId, Span, SyntaxNode}; use crate::syntax::{SourceId, Span, SyntaxNode};
use crate::util::EcoString; use crate::util::EcoString;
use crate::World; use crate::World;

View File

@ -5,11 +5,11 @@
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use std::ops::Deref; use std::ops::Deref;
use super::{NodeKind, RawFields, Span, SyntaxNode, Unit}; use super::{RawFields, Span, SyntaxKind, SyntaxNode, Unit};
use crate::util::EcoString; use crate::util::EcoString;
/// A typed AST node. /// A typed AST node.
pub trait TypedNode: Sized { pub trait AstNode: Sized {
/// Convert a node into its typed variant. /// Convert a node into its typed variant.
fn from_untyped(node: &SyntaxNode) -> Option<Self>; fn from_untyped(node: &SyntaxNode) -> Option<Self>;
@ -24,7 +24,7 @@ pub trait TypedNode: Sized {
macro_rules! node { macro_rules! node {
($(#[$attr:meta])* $name:ident) => { ($(#[$attr:meta])* $name:ident) => {
node!{ $(#[$attr])* $name: NodeKind::$name { .. } } node!{ $(#[$attr])* $name: SyntaxKind::$name { .. } }
}; };
($(#[$attr:meta])* $name:ident: $variants:pat) => { ($(#[$attr:meta])* $name:ident: $variants:pat) => {
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, PartialEq, Hash)]
@ -32,7 +32,7 @@ macro_rules! node {
$(#[$attr])* $(#[$attr])*
pub struct $name(SyntaxNode); pub struct $name(SyntaxNode);
impl TypedNode for $name { impl AstNode for $name {
fn from_untyped(node: &SyntaxNode) -> Option<Self> { fn from_untyped(node: &SyntaxNode) -> Option<Self> {
if matches!(node.kind(), $variants) { if matches!(node.kind(), $variants) {
Some(Self(node.clone())) Some(Self(node.clone()))
@ -62,7 +62,8 @@ impl Markup {
.filter(move |node| { .filter(move |node| {
// Ignore linebreak directly after statements without semicolons. // Ignore linebreak directly after statements without semicolons.
let kind = node.kind(); let kind = node.kind();
let keep = !was_stmt || !matches!(kind, NodeKind::Space { newlines: 1 }); let keep =
!was_stmt || !matches!(kind, SyntaxKind::Space { newlines: 1 });
was_stmt = kind.is_stmt(); was_stmt = kind.is_stmt();
keep keep
}) })
@ -112,26 +113,26 @@ pub enum MarkupNode {
Expr(Expr), Expr(Expr),
} }
impl TypedNode for MarkupNode { impl AstNode for MarkupNode {
fn from_untyped(node: &SyntaxNode) -> Option<Self> { fn from_untyped(node: &SyntaxNode) -> Option<Self> {
match node.kind() { match node.kind() {
NodeKind::Space { .. } => node.cast().map(Self::Space), SyntaxKind::Space { .. } => node.cast().map(Self::Space),
NodeKind::Linebreak => node.cast().map(Self::Linebreak), SyntaxKind::Linebreak => node.cast().map(Self::Linebreak),
NodeKind::Text(_) => node.cast().map(Self::Text), SyntaxKind::Text(_) => node.cast().map(Self::Text),
NodeKind::Escape(_) => node.cast().map(Self::Escape), SyntaxKind::Escape(_) => node.cast().map(Self::Escape),
NodeKind::Shorthand(_) => node.cast().map(Self::Shorthand), SyntaxKind::Shorthand(_) => node.cast().map(Self::Shorthand),
NodeKind::SmartQuote { .. } => node.cast().map(Self::SmartQuote), SyntaxKind::SmartQuote { .. } => node.cast().map(Self::SmartQuote),
NodeKind::Strong => node.cast().map(Self::Strong), SyntaxKind::Strong => node.cast().map(Self::Strong),
NodeKind::Emph => node.cast().map(Self::Emph), SyntaxKind::Emph => node.cast().map(Self::Emph),
NodeKind::Raw(_) => node.cast().map(Self::Raw), SyntaxKind::Raw(_) => node.cast().map(Self::Raw),
NodeKind::Link(_) => node.cast().map(Self::Link), SyntaxKind::Link(_) => node.cast().map(Self::Link),
NodeKind::Label(_) => node.cast().map(Self::Label), SyntaxKind::Label(_) => node.cast().map(Self::Label),
NodeKind::Ref(_) => node.cast().map(Self::Ref), SyntaxKind::Ref(_) => node.cast().map(Self::Ref),
NodeKind::Heading => node.cast().map(Self::Heading), SyntaxKind::Heading => node.cast().map(Self::Heading),
NodeKind::ListItem => node.cast().map(Self::List), SyntaxKind::ListItem => node.cast().map(Self::List),
NodeKind::EnumItem => node.cast().map(Self::Enum), SyntaxKind::EnumItem => node.cast().map(Self::Enum),
NodeKind::DescItem => node.cast().map(Self::Desc), SyntaxKind::DescItem => node.cast().map(Self::Desc),
NodeKind::Math => node.cast().map(Self::Math), SyntaxKind::Math => node.cast().map(Self::Math),
_ => node.cast().map(Self::Expr), _ => node.cast().map(Self::Expr),
} }
} }
@ -169,7 +170,7 @@ impl Space {
/// Get the number of newlines. /// Get the number of newlines.
pub fn newlines(&self) -> usize { pub fn newlines(&self) -> usize {
match self.0.kind() { match self.0.kind() {
&NodeKind::Space { newlines } => newlines, &SyntaxKind::Space { newlines } => newlines,
_ => panic!("space is of wrong kind"), _ => panic!("space is of wrong kind"),
} }
} }
@ -189,7 +190,7 @@ impl Text {
/// Get the text. /// Get the text.
pub fn get(&self) -> &EcoString { pub fn get(&self) -> &EcoString {
match self.0.kind() { match self.0.kind() {
NodeKind::Text(v) => v, SyntaxKind::Text(v) => v,
_ => panic!("text is of wrong kind"), _ => panic!("text is of wrong kind"),
} }
} }
@ -204,7 +205,7 @@ impl Escape {
/// Get the escaped character. /// Get the escaped character.
pub fn get(&self) -> char { pub fn get(&self) -> char {
match self.0.kind() { match self.0.kind() {
&NodeKind::Escape(v) => v, &SyntaxKind::Escape(v) => v,
_ => panic!("escape is of wrong kind"), _ => panic!("escape is of wrong kind"),
} }
} }
@ -220,7 +221,7 @@ impl Shorthand {
/// Get the shorthanded character. /// Get the shorthanded character.
pub fn get(&self) -> char { pub fn get(&self) -> char {
match self.0.kind() { match self.0.kind() {
&NodeKind::Shorthand(v) => v, &SyntaxKind::Shorthand(v) => v,
_ => panic!("shorthand is of wrong kind"), _ => panic!("shorthand is of wrong kind"),
} }
} }
@ -235,7 +236,7 @@ impl SmartQuote {
/// Whether this is a double quote. /// Whether this is a double quote.
pub fn double(&self) -> bool { pub fn double(&self) -> bool {
match self.0.kind() { match self.0.kind() {
&NodeKind::SmartQuote { double } => double, &SyntaxKind::SmartQuote { double } => double,
_ => panic!("smart quote is of wrong kind"), _ => panic!("smart quote is of wrong kind"),
} }
} }
@ -291,7 +292,7 @@ impl Raw {
/// The raw fields. /// The raw fields.
fn get(&self) -> &RawFields { fn get(&self) -> &RawFields {
match self.0.kind() { match self.0.kind() {
NodeKind::Raw(v) => v.as_ref(), SyntaxKind::Raw(v) => v.as_ref(),
_ => panic!("raw is of wrong kind"), _ => panic!("raw is of wrong kind"),
} }
} }
@ -306,7 +307,7 @@ impl Link {
/// Get the URL. /// Get the URL.
pub fn url(&self) -> &EcoString { pub fn url(&self) -> &EcoString {
match self.0.kind() { match self.0.kind() {
NodeKind::Link(url) => url, SyntaxKind::Link(url) => url,
_ => panic!("link is of wrong kind"), _ => panic!("link is of wrong kind"),
} }
} }
@ -321,7 +322,7 @@ impl Label {
/// Get the label. /// Get the label.
pub fn get(&self) -> &EcoString { pub fn get(&self) -> &EcoString {
match self.0.kind() { match self.0.kind() {
NodeKind::Label(v) => v, SyntaxKind::Label(v) => v,
_ => panic!("label is of wrong kind"), _ => panic!("label is of wrong kind"),
} }
} }
@ -336,7 +337,7 @@ impl Ref {
/// Get the target. /// Get the target.
pub fn get(&self) -> &EcoString { pub fn get(&self) -> &EcoString {
match self.0.kind() { match self.0.kind() {
NodeKind::Ref(v) => v, SyntaxKind::Ref(v) => v,
_ => panic!("reference is of wrong kind"), _ => panic!("reference is of wrong kind"),
} }
} }
@ -357,7 +358,7 @@ impl Heading {
pub fn level(&self) -> NonZeroUsize { pub fn level(&self) -> NonZeroUsize {
self.0 self.0
.children() .children()
.filter(|n| n.kind() == &NodeKind::Eq) .filter(|n| n.kind() == &SyntaxKind::Eq)
.count() .count()
.try_into() .try_into()
.expect("heading is missing equals sign") .expect("heading is missing equals sign")
@ -385,7 +386,7 @@ impl EnumItem {
/// The explicit numbering, if any: `23.`. /// The explicit numbering, if any: `23.`.
pub fn number(&self) -> Option<usize> { pub fn number(&self) -> Option<usize> {
self.0.children().find_map(|node| match node.kind() { self.0.children().find_map(|node| match node.kind() {
NodeKind::EnumNumbering(num) => Some(*num), SyntaxKind::EnumNumbering(num) => Some(*num),
_ => None, _ => None,
}) })
} }
@ -458,17 +459,17 @@ pub enum MathNode {
Expr(Expr), Expr(Expr),
} }
impl TypedNode for MathNode { impl AstNode for MathNode {
fn from_untyped(node: &SyntaxNode) -> Option<Self> { fn from_untyped(node: &SyntaxNode) -> Option<Self> {
match node.kind() { match node.kind() {
NodeKind::Space { .. } => node.cast().map(Self::Space), SyntaxKind::Space { .. } => node.cast().map(Self::Space),
NodeKind::Linebreak => node.cast().map(Self::Linebreak), SyntaxKind::Linebreak => node.cast().map(Self::Linebreak),
NodeKind::Escape(_) => node.cast().map(Self::Escape), SyntaxKind::Escape(_) => node.cast().map(Self::Escape),
NodeKind::Atom(_) => node.cast().map(Self::Atom), SyntaxKind::Atom(_) => node.cast().map(Self::Atom),
NodeKind::Script => node.cast().map(Self::Script), SyntaxKind::Script => node.cast().map(Self::Script),
NodeKind::Frac => node.cast().map(Self::Frac), SyntaxKind::Frac => node.cast().map(Self::Frac),
NodeKind::Align => node.cast().map(Self::Align), SyntaxKind::Align => node.cast().map(Self::Align),
NodeKind::Math => node.cast().map(Self::Group), SyntaxKind::Math => node.cast().map(Self::Group),
_ => node.cast().map(Self::Expr), _ => node.cast().map(Self::Expr),
} }
} }
@ -497,7 +498,7 @@ impl Atom {
/// Get the atom's text. /// Get the atom's text.
pub fn get(&self) -> &EcoString { pub fn get(&self) -> &EcoString {
match self.0.kind() { match self.0.kind() {
NodeKind::Atom(v) => v, SyntaxKind::Atom(v) => v,
_ => panic!("atom is of wrong kind"), _ => panic!("atom is of wrong kind"),
} }
} }
@ -518,7 +519,7 @@ impl Script {
pub fn sub(&self) -> Option<MathNode> { pub fn sub(&self) -> Option<MathNode> {
self.0 self.0
.children() .children()
.skip_while(|node| !matches!(node.kind(), NodeKind::Underscore)) .skip_while(|node| !matches!(node.kind(), SyntaxKind::Underscore))
.nth(1) .nth(1)
.map(|node| node.cast().expect("script node has invalid subscript")) .map(|node| node.cast().expect("script node has invalid subscript"))
} }
@ -527,7 +528,7 @@ impl Script {
pub fn sup(&self) -> Option<MathNode> { pub fn sup(&self) -> Option<MathNode> {
self.0 self.0
.children() .children()
.skip_while(|node| !matches!(node.kind(), NodeKind::Hat)) .skip_while(|node| !matches!(node.kind(), SyntaxKind::Hat))
.nth(1) .nth(1)
.map(|node| node.cast().expect("script node has invalid superscript")) .map(|node| node.cast().expect("script node has invalid superscript"))
} }
@ -558,7 +559,7 @@ node! {
impl Align { impl Align {
/// The number of ampersands. /// The number of ampersands.
pub fn count(&self) -> usize { pub fn count(&self) -> usize {
self.0.children().filter(|n| n.kind() == &NodeKind::Amp).count() self.0.children().filter(|n| n.kind() == &SyntaxKind::Amp).count()
} }
} }
@ -615,32 +616,32 @@ pub enum Expr {
Return(FuncReturn), Return(FuncReturn),
} }
impl TypedNode for Expr { impl AstNode for Expr {
fn from_untyped(node: &SyntaxNode) -> Option<Self> { fn from_untyped(node: &SyntaxNode) -> Option<Self> {
match node.kind() { match node.kind() {
NodeKind::Ident(_) => node.cast().map(Self::Ident), SyntaxKind::Ident(_) => node.cast().map(Self::Ident),
NodeKind::CodeBlock => node.cast().map(Self::Code), SyntaxKind::CodeBlock => node.cast().map(Self::Code),
NodeKind::ContentBlock => node.cast().map(Self::Content), SyntaxKind::ContentBlock => node.cast().map(Self::Content),
NodeKind::Parenthesized => node.cast().map(Self::Parenthesized), SyntaxKind::Parenthesized => node.cast().map(Self::Parenthesized),
NodeKind::Array => node.cast().map(Self::Array), SyntaxKind::Array => node.cast().map(Self::Array),
NodeKind::Dict => node.cast().map(Self::Dict), SyntaxKind::Dict => node.cast().map(Self::Dict),
NodeKind::Unary => node.cast().map(Self::Unary), SyntaxKind::Unary => node.cast().map(Self::Unary),
NodeKind::Binary => node.cast().map(Self::Binary), SyntaxKind::Binary => node.cast().map(Self::Binary),
NodeKind::FieldAccess => node.cast().map(Self::FieldAccess), SyntaxKind::FieldAccess => node.cast().map(Self::FieldAccess),
NodeKind::FuncCall => node.cast().map(Self::FuncCall), SyntaxKind::FuncCall => node.cast().map(Self::FuncCall),
NodeKind::MethodCall => node.cast().map(Self::MethodCall), SyntaxKind::MethodCall => node.cast().map(Self::MethodCall),
NodeKind::Closure => node.cast().map(Self::Closure), SyntaxKind::Closure => node.cast().map(Self::Closure),
NodeKind::LetBinding => node.cast().map(Self::Let), SyntaxKind::LetBinding => node.cast().map(Self::Let),
NodeKind::SetRule => node.cast().map(Self::Set), SyntaxKind::SetRule => node.cast().map(Self::Set),
NodeKind::ShowRule => node.cast().map(Self::Show), SyntaxKind::ShowRule => node.cast().map(Self::Show),
NodeKind::Conditional => node.cast().map(Self::Conditional), SyntaxKind::Conditional => node.cast().map(Self::Conditional),
NodeKind::WhileLoop => node.cast().map(Self::While), SyntaxKind::WhileLoop => node.cast().map(Self::While),
NodeKind::ForLoop => node.cast().map(Self::For), SyntaxKind::ForLoop => node.cast().map(Self::For),
NodeKind::ModuleImport => node.cast().map(Self::Import), SyntaxKind::ModuleImport => node.cast().map(Self::Import),
NodeKind::ModuleInclude => node.cast().map(Self::Include), SyntaxKind::ModuleInclude => node.cast().map(Self::Include),
NodeKind::LoopBreak => node.cast().map(Self::Break), SyntaxKind::LoopBreak => node.cast().map(Self::Break),
NodeKind::LoopContinue => node.cast().map(Self::Continue), SyntaxKind::LoopContinue => node.cast().map(Self::Continue),
NodeKind::FuncReturn => node.cast().map(Self::Return), SyntaxKind::FuncReturn => node.cast().map(Self::Return),
_ => node.cast().map(Self::Lit), _ => node.cast().map(Self::Lit),
} }
} }
@ -696,26 +697,26 @@ impl Expr {
node! { node! {
/// A literal: `1`, `true`, ... /// A literal: `1`, `true`, ...
Lit: NodeKind::None Lit: SyntaxKind::None
| NodeKind::Auto | SyntaxKind::Auto
| NodeKind::Bool(_) | SyntaxKind::Bool(_)
| NodeKind::Int(_) | SyntaxKind::Int(_)
| NodeKind::Float(_) | SyntaxKind::Float(_)
| NodeKind::Numeric(_, _) | SyntaxKind::Numeric(_, _)
| NodeKind::Str(_) | SyntaxKind::Str(_)
} }
impl Lit { impl Lit {
/// The kind of literal. /// The kind of literal.
pub fn kind(&self) -> LitKind { pub fn kind(&self) -> LitKind {
match *self.0.kind() { match *self.0.kind() {
NodeKind::None => LitKind::None, SyntaxKind::None => LitKind::None,
NodeKind::Auto => LitKind::Auto, SyntaxKind::Auto => LitKind::Auto,
NodeKind::Bool(v) => LitKind::Bool(v), SyntaxKind::Bool(v) => LitKind::Bool(v),
NodeKind::Int(v) => LitKind::Int(v), SyntaxKind::Int(v) => LitKind::Int(v),
NodeKind::Float(v) => LitKind::Float(v), SyntaxKind::Float(v) => LitKind::Float(v),
NodeKind::Numeric(v, unit) => LitKind::Numeric(v, unit), SyntaxKind::Numeric(v, unit) => LitKind::Numeric(v, unit),
NodeKind::Str(ref v) => LitKind::Str(v.clone()), SyntaxKind::Str(ref v) => LitKind::Str(v.clone()),
_ => panic!("literal is of wrong kind"), _ => panic!("literal is of wrong kind"),
} }
} }
@ -799,10 +800,10 @@ pub enum ArrayItem {
Spread(Expr), Spread(Expr),
} }
impl TypedNode for ArrayItem { impl AstNode for ArrayItem {
fn from_untyped(node: &SyntaxNode) -> Option<Self> { fn from_untyped(node: &SyntaxNode) -> Option<Self> {
match node.kind() { match node.kind() {
NodeKind::Spread => node.cast_first_child().map(Self::Spread), SyntaxKind::Spread => node.cast_first_child().map(Self::Spread),
_ => node.cast().map(Self::Pos), _ => node.cast().map(Self::Pos),
} }
} }
@ -838,12 +839,12 @@ pub enum DictItem {
Spread(Expr), Spread(Expr),
} }
impl TypedNode for DictItem { impl AstNode for DictItem {
fn from_untyped(node: &SyntaxNode) -> Option<Self> { fn from_untyped(node: &SyntaxNode) -> Option<Self> {
match node.kind() { match node.kind() {
NodeKind::Named => node.cast().map(Self::Named), SyntaxKind::Named => node.cast().map(Self::Named),
NodeKind::Keyed => node.cast().map(Self::Keyed), SyntaxKind::Keyed => node.cast().map(Self::Keyed),
NodeKind::Spread => node.cast_first_child().map(Self::Spread), SyntaxKind::Spread => node.cast_first_child().map(Self::Spread),
_ => None, _ => None,
} }
} }
@ -885,7 +886,7 @@ impl Keyed {
self.0 self.0
.children() .children()
.find_map(|node| match node.kind() { .find_map(|node| match node.kind() {
NodeKind::Str(key) => Some(key.clone()), SyntaxKind::Str(key) => Some(key.clone()),
_ => None, _ => None,
}) })
.expect("keyed pair is missing key") .expect("keyed pair is missing key")
@ -930,11 +931,11 @@ pub enum UnOp {
impl UnOp { impl UnOp {
/// Try to convert the token into a unary operation. /// Try to convert the token into a unary operation.
pub fn from_token(token: &NodeKind) -> Option<Self> { pub fn from_token(token: &SyntaxKind) -> Option<Self> {
Some(match token { Some(match token {
NodeKind::Plus => Self::Pos, SyntaxKind::Plus => Self::Pos,
NodeKind::Minus => Self::Neg, SyntaxKind::Minus => Self::Neg,
NodeKind::Not => Self::Not, SyntaxKind::Not => Self::Not,
_ => return None, _ => return None,
}) })
} }
@ -969,11 +970,11 @@ impl Binary {
self.0 self.0
.children() .children()
.find_map(|node| match node.kind() { .find_map(|node| match node.kind() {
NodeKind::Not => { SyntaxKind::Not => {
not = true; not = true;
None None
} }
NodeKind::In if not => Some(BinOp::NotIn), SyntaxKind::In if not => Some(BinOp::NotIn),
_ => BinOp::from_token(node.kind()), _ => BinOp::from_token(node.kind()),
}) })
.expect("binary operation is missing operator") .expect("binary operation is missing operator")
@ -1039,26 +1040,26 @@ pub enum BinOp {
impl BinOp { impl BinOp {
/// Try to convert the token into a binary operation. /// Try to convert the token into a binary operation.
pub fn from_token(token: &NodeKind) -> Option<Self> { pub fn from_token(token: &SyntaxKind) -> Option<Self> {
Some(match token { Some(match token {
NodeKind::Plus => Self::Add, SyntaxKind::Plus => Self::Add,
NodeKind::Minus => Self::Sub, SyntaxKind::Minus => Self::Sub,
NodeKind::Star => Self::Mul, SyntaxKind::Star => Self::Mul,
NodeKind::Slash => Self::Div, SyntaxKind::Slash => Self::Div,
NodeKind::And => Self::And, SyntaxKind::And => Self::And,
NodeKind::Or => Self::Or, SyntaxKind::Or => Self::Or,
NodeKind::EqEq => Self::Eq, SyntaxKind::EqEq => Self::Eq,
NodeKind::ExclEq => Self::Neq, SyntaxKind::ExclEq => Self::Neq,
NodeKind::Lt => Self::Lt, SyntaxKind::Lt => Self::Lt,
NodeKind::LtEq => Self::Leq, SyntaxKind::LtEq => Self::Leq,
NodeKind::Gt => Self::Gt, SyntaxKind::Gt => Self::Gt,
NodeKind::GtEq => Self::Geq, SyntaxKind::GtEq => Self::Geq,
NodeKind::Eq => Self::Assign, SyntaxKind::Eq => Self::Assign,
NodeKind::In => Self::In, SyntaxKind::In => Self::In,
NodeKind::PlusEq => Self::AddAssign, SyntaxKind::PlusEq => Self::AddAssign,
NodeKind::HyphEq => Self::SubAssign, SyntaxKind::HyphEq => Self::SubAssign,
NodeKind::StarEq => Self::MulAssign, SyntaxKind::StarEq => Self::MulAssign,
NodeKind::SlashEq => Self::DivAssign, SyntaxKind::SlashEq => Self::DivAssign,
_ => return None, _ => return None,
}) })
} }
@ -1231,11 +1232,11 @@ pub enum Arg {
Spread(Expr), Spread(Expr),
} }
impl TypedNode for Arg { impl AstNode for Arg {
fn from_untyped(node: &SyntaxNode) -> Option<Self> { fn from_untyped(node: &SyntaxNode) -> Option<Self> {
match node.kind() { match node.kind() {
NodeKind::Named => node.cast().map(Self::Named), SyntaxKind::Named => node.cast().map(Self::Named),
NodeKind::Spread => node.cast_first_child().map(Self::Spread), SyntaxKind::Spread => node.cast_first_child().map(Self::Spread),
_ => node.cast().map(Self::Pos), _ => node.cast().map(Self::Pos),
} }
} }
@ -1266,7 +1267,7 @@ impl Closure {
pub fn params(&self) -> impl DoubleEndedIterator<Item = Param> + '_ { pub fn params(&self) -> impl DoubleEndedIterator<Item = Param> + '_ {
self.0 self.0
.children() .children()
.find(|x| x.kind() == &NodeKind::Params) .find(|x| x.kind() == &SyntaxKind::Params)
.expect("closure is missing parameter list") .expect("closure is missing parameter list")
.children() .children()
.filter_map(SyntaxNode::cast) .filter_map(SyntaxNode::cast)
@ -1289,12 +1290,12 @@ pub enum Param {
Sink(Ident), Sink(Ident),
} }
impl TypedNode for Param { impl AstNode for Param {
fn from_untyped(node: &SyntaxNode) -> Option<Self> { fn from_untyped(node: &SyntaxNode) -> Option<Self> {
match node.kind() { match node.kind() {
NodeKind::Ident(_) => node.cast().map(Self::Pos), SyntaxKind::Ident(_) => node.cast().map(Self::Pos),
NodeKind::Named => node.cast().map(Self::Named), SyntaxKind::Named => node.cast().map(Self::Named),
NodeKind::Spread => node.cast_first_child().map(Self::Sink), SyntaxKind::Spread => node.cast_first_child().map(Self::Sink),
_ => None, _ => None,
} }
} }
@ -1357,7 +1358,7 @@ impl SetRule {
pub fn condition(&self) -> Option<Expr> { pub fn condition(&self) -> Option<Expr> {
self.0 self.0
.children() .children()
.skip_while(|child| child.kind() != &NodeKind::If) .skip_while(|child| child.kind() != &SyntaxKind::If)
.find_map(SyntaxNode::cast) .find_map(SyntaxNode::cast)
} }
} }
@ -1373,7 +1374,7 @@ impl ShowRule {
self.0 self.0
.children() .children()
.rev() .rev()
.skip_while(|child| child.kind() != &NodeKind::Colon) .skip_while(|child| child.kind() != &SyntaxKind::Colon)
.find_map(SyntaxNode::cast) .find_map(SyntaxNode::cast)
} }
@ -1482,8 +1483,8 @@ impl ModuleImport {
self.0 self.0
.children() .children()
.find_map(|node| match node.kind() { .find_map(|node| match node.kind() {
NodeKind::Star => Some(Imports::Wildcard), SyntaxKind::Star => Some(Imports::Wildcard),
NodeKind::ImportItems => { SyntaxKind::ImportItems => {
let items = node.children().filter_map(SyntaxNode::cast).collect(); let items = node.children().filter_map(SyntaxNode::cast).collect();
Some(Imports::Items(items)) Some(Imports::Items(items))
} }
@ -1550,7 +1551,7 @@ impl Ident {
/// Get the identifier. /// Get the identifier.
pub fn get(&self) -> &EcoString { pub fn get(&self) -> &EcoString {
match self.0.kind() { match self.0.kind() {
NodeKind::Ident(id) => id, SyntaxKind::Ident(id) => id,
_ => panic!("identifier is of wrong kind"), _ => panic!("identifier is of wrong kind"),
} }
} }
@ -1558,7 +1559,7 @@ impl Ident {
/// Take out the container identifier. /// Take out the container identifier.
pub fn take(self) -> EcoString { pub fn take(self) -> EcoString {
match self.0.take() { match self.0.take() {
NodeKind::Ident(id) => id, SyntaxKind::Ident(id) => id,
_ => panic!("identifier is of wrong kind"), _ => panic!("identifier is of wrong kind"),
} }
} }

View File

@ -6,7 +6,7 @@ use std::ops::Range;
use syntect::highlighting::{Color, FontStyle, Highlighter, Style, Theme}; use syntect::highlighting::{Color, FontStyle, Highlighter, Style, Theme};
use syntect::parsing::Scope; use syntect::parsing::Scope;
use super::{parse, NodeKind, SyntaxNode}; use super::{parse, SyntaxKind, SyntaxNode};
/// Highlight source text into a standalone HTML document. /// Highlight source text into a standalone HTML document.
pub fn highlight_html(text: &str, theme: &Theme) -> String { pub fn highlight_html(text: &str, theme: &Theme) -> String {
@ -194,170 +194,170 @@ impl Category {
i: usize, i: usize,
) -> Option<Category> { ) -> Option<Category> {
match child.kind() { match child.kind() {
NodeKind::LineComment => Some(Category::Comment), SyntaxKind::LineComment => Some(Category::Comment),
NodeKind::BlockComment => Some(Category::Comment), SyntaxKind::BlockComment => Some(Category::Comment),
NodeKind::Space { .. } => None, SyntaxKind::Space { .. } => None,
NodeKind::LeftBrace => Some(Category::Bracket), SyntaxKind::LeftBrace => Some(Category::Bracket),
NodeKind::RightBrace => Some(Category::Bracket), SyntaxKind::RightBrace => Some(Category::Bracket),
NodeKind::LeftBracket => Some(Category::Bracket), SyntaxKind::LeftBracket => Some(Category::Bracket),
NodeKind::RightBracket => Some(Category::Bracket), SyntaxKind::RightBracket => Some(Category::Bracket),
NodeKind::LeftParen => Some(Category::Bracket), SyntaxKind::LeftParen => Some(Category::Bracket),
NodeKind::RightParen => Some(Category::Bracket), SyntaxKind::RightParen => Some(Category::Bracket),
NodeKind::Comma => Some(Category::Punctuation), SyntaxKind::Comma => Some(Category::Punctuation),
NodeKind::Semicolon => Some(Category::Punctuation), SyntaxKind::Semicolon => Some(Category::Punctuation),
NodeKind::Colon => Some(Category::Punctuation), SyntaxKind::Colon => Some(Category::Punctuation),
NodeKind::Star => match parent.kind() { SyntaxKind::Star => match parent.kind() {
NodeKind::Strong => None, SyntaxKind::Strong => None,
_ => Some(Category::Operator), _ => Some(Category::Operator),
}, },
NodeKind::Underscore => match parent.kind() { SyntaxKind::Underscore => match parent.kind() {
NodeKind::Script => Some(Category::MathOperator), SyntaxKind::Script => Some(Category::MathOperator),
_ => None, _ => None,
}, },
NodeKind::Dollar => Some(Category::MathDelimiter), SyntaxKind::Dollar => Some(Category::MathDelimiter),
NodeKind::Plus => Some(match parent.kind() { SyntaxKind::Plus => Some(match parent.kind() {
NodeKind::EnumItem => Category::ListMarker, SyntaxKind::EnumItem => Category::ListMarker,
_ => Category::Operator, _ => Category::Operator,
}), }),
NodeKind::Minus => Some(match parent.kind() { SyntaxKind::Minus => Some(match parent.kind() {
NodeKind::ListItem => Category::ListMarker, SyntaxKind::ListItem => Category::ListMarker,
_ => Category::Operator, _ => Category::Operator,
}), }),
NodeKind::Slash => Some(match parent.kind() { SyntaxKind::Slash => Some(match parent.kind() {
NodeKind::DescItem => Category::ListMarker, SyntaxKind::DescItem => Category::ListMarker,
NodeKind::Frac => Category::MathOperator, SyntaxKind::Frac => Category::MathOperator,
_ => Category::Operator, _ => Category::Operator,
}), }),
NodeKind::Hat => Some(Category::MathOperator), SyntaxKind::Hat => Some(Category::MathOperator),
NodeKind::Amp => Some(Category::MathOperator), SyntaxKind::Amp => Some(Category::MathOperator),
NodeKind::Dot => Some(Category::Punctuation), SyntaxKind::Dot => Some(Category::Punctuation),
NodeKind::Eq => match parent.kind() { SyntaxKind::Eq => match parent.kind() {
NodeKind::Heading => None, SyntaxKind::Heading => None,
_ => Some(Category::Operator), _ => Some(Category::Operator),
}, },
NodeKind::EqEq => Some(Category::Operator), SyntaxKind::EqEq => Some(Category::Operator),
NodeKind::ExclEq => Some(Category::Operator), SyntaxKind::ExclEq => Some(Category::Operator),
NodeKind::Lt => Some(Category::Operator), SyntaxKind::Lt => Some(Category::Operator),
NodeKind::LtEq => Some(Category::Operator), SyntaxKind::LtEq => Some(Category::Operator),
NodeKind::Gt => Some(Category::Operator), SyntaxKind::Gt => Some(Category::Operator),
NodeKind::GtEq => Some(Category::Operator), SyntaxKind::GtEq => Some(Category::Operator),
NodeKind::PlusEq => Some(Category::Operator), SyntaxKind::PlusEq => Some(Category::Operator),
NodeKind::HyphEq => Some(Category::Operator), SyntaxKind::HyphEq => Some(Category::Operator),
NodeKind::StarEq => Some(Category::Operator), SyntaxKind::StarEq => Some(Category::Operator),
NodeKind::SlashEq => Some(Category::Operator), SyntaxKind::SlashEq => Some(Category::Operator),
NodeKind::Dots => Some(Category::Operator), SyntaxKind::Dots => Some(Category::Operator),
NodeKind::Arrow => Some(Category::Operator), SyntaxKind::Arrow => Some(Category::Operator),
NodeKind::Not => Some(Category::Keyword), SyntaxKind::Not => Some(Category::Keyword),
NodeKind::And => Some(Category::Keyword), SyntaxKind::And => Some(Category::Keyword),
NodeKind::Or => Some(Category::Keyword), SyntaxKind::Or => Some(Category::Keyword),
NodeKind::None => Some(Category::KeywordLiteral), SyntaxKind::None => Some(Category::KeywordLiteral),
NodeKind::Auto => Some(Category::KeywordLiteral), SyntaxKind::Auto => Some(Category::KeywordLiteral),
NodeKind::Let => Some(Category::Keyword), SyntaxKind::Let => Some(Category::Keyword),
NodeKind::Set => Some(Category::Keyword), SyntaxKind::Set => Some(Category::Keyword),
NodeKind::Show => Some(Category::Keyword), SyntaxKind::Show => Some(Category::Keyword),
NodeKind::If => Some(Category::Keyword), SyntaxKind::If => Some(Category::Keyword),
NodeKind::Else => Some(Category::Keyword), SyntaxKind::Else => Some(Category::Keyword),
NodeKind::For => Some(Category::Keyword), SyntaxKind::For => Some(Category::Keyword),
NodeKind::In => Some(Category::Keyword), SyntaxKind::In => Some(Category::Keyword),
NodeKind::While => Some(Category::Keyword), SyntaxKind::While => Some(Category::Keyword),
NodeKind::Break => Some(Category::Keyword), SyntaxKind::Break => Some(Category::Keyword),
NodeKind::Continue => Some(Category::Keyword), SyntaxKind::Continue => Some(Category::Keyword),
NodeKind::Return => Some(Category::Keyword), SyntaxKind::Return => Some(Category::Keyword),
NodeKind::Import => Some(Category::Keyword), SyntaxKind::Import => Some(Category::Keyword),
NodeKind::Include => Some(Category::Keyword), SyntaxKind::Include => Some(Category::Keyword),
NodeKind::From => Some(Category::Keyword), SyntaxKind::From => Some(Category::Keyword),
NodeKind::Markup { .. } => match parent.kind() { SyntaxKind::Markup { .. } => match parent.kind() {
NodeKind::DescItem SyntaxKind::DescItem
if parent if parent
.children() .children()
.take_while(|child| child.kind() != &NodeKind::Colon) .take_while(|child| child.kind() != &SyntaxKind::Colon)
.find(|c| matches!(c.kind(), NodeKind::Markup { .. })) .find(|c| matches!(c.kind(), SyntaxKind::Markup { .. }))
.map_or(false, |ident| std::ptr::eq(ident, child)) => .map_or(false, |ident| std::ptr::eq(ident, child)) =>
{ {
Some(Category::ListTerm) Some(Category::ListTerm)
} }
_ => None, _ => None,
}, },
NodeKind::Text(_) => None, SyntaxKind::Text(_) => None,
NodeKind::Linebreak => Some(Category::Escape), SyntaxKind::Linebreak => Some(Category::Escape),
NodeKind::Escape(_) => Some(Category::Escape), SyntaxKind::Escape(_) => Some(Category::Escape),
NodeKind::Shorthand(_) => Some(Category::Shorthand), SyntaxKind::Shorthand(_) => Some(Category::Shorthand),
NodeKind::SmartQuote { .. } => Some(Category::SmartQuote), SyntaxKind::SmartQuote { .. } => Some(Category::SmartQuote),
NodeKind::Strong => Some(Category::Strong), SyntaxKind::Strong => Some(Category::Strong),
NodeKind::Emph => Some(Category::Emph), SyntaxKind::Emph => Some(Category::Emph),
NodeKind::Raw(_) => Some(Category::Raw), SyntaxKind::Raw(_) => Some(Category::Raw),
NodeKind::Link(_) => Some(Category::Link), SyntaxKind::Link(_) => Some(Category::Link),
NodeKind::Label(_) => Some(Category::Label), SyntaxKind::Label(_) => Some(Category::Label),
NodeKind::Ref(_) => Some(Category::Ref), SyntaxKind::Ref(_) => Some(Category::Ref),
NodeKind::Heading => Some(Category::Heading), SyntaxKind::Heading => Some(Category::Heading),
NodeKind::ListItem => Some(Category::ListItem), SyntaxKind::ListItem => Some(Category::ListItem),
NodeKind::EnumItem => Some(Category::ListItem), SyntaxKind::EnumItem => Some(Category::ListItem),
NodeKind::EnumNumbering(_) => Some(Category::ListMarker), SyntaxKind::EnumNumbering(_) => Some(Category::ListMarker),
NodeKind::DescItem => Some(Category::ListItem), SyntaxKind::DescItem => Some(Category::ListItem),
NodeKind::Math => Some(Category::Math), SyntaxKind::Math => Some(Category::Math),
NodeKind::Atom(_) => None, SyntaxKind::Atom(_) => None,
NodeKind::Script => None, SyntaxKind::Script => None,
NodeKind::Frac => None, SyntaxKind::Frac => None,
NodeKind::Align => None, SyntaxKind::Align => None,
NodeKind::Ident(_) => match parent.kind() { SyntaxKind::Ident(_) => match parent.kind() {
NodeKind::Markup { .. } => Some(Category::Interpolated), SyntaxKind::Markup { .. } => Some(Category::Interpolated),
NodeKind::Math => Some(Category::Interpolated), SyntaxKind::Math => Some(Category::Interpolated),
NodeKind::FuncCall => Some(Category::Function), SyntaxKind::FuncCall => Some(Category::Function),
NodeKind::MethodCall if i > 0 => Some(Category::Function), SyntaxKind::MethodCall if i > 0 => Some(Category::Function),
NodeKind::Closure if i == 0 => Some(Category::Function), SyntaxKind::Closure if i == 0 => Some(Category::Function),
NodeKind::SetRule => Some(Category::Function), SyntaxKind::SetRule => Some(Category::Function),
NodeKind::ShowRule SyntaxKind::ShowRule
if parent if parent
.children() .children()
.rev() .rev()
.skip_while(|child| child.kind() != &NodeKind::Colon) .skip_while(|child| child.kind() != &SyntaxKind::Colon)
.find(|c| matches!(c.kind(), NodeKind::Ident(_))) .find(|c| matches!(c.kind(), SyntaxKind::Ident(_)))
.map_or(false, |ident| std::ptr::eq(ident, child)) => .map_or(false, |ident| std::ptr::eq(ident, child)) =>
{ {
Some(Category::Function) Some(Category::Function)
} }
_ => None, _ => None,
}, },
NodeKind::Bool(_) => Some(Category::KeywordLiteral), SyntaxKind::Bool(_) => Some(Category::KeywordLiteral),
NodeKind::Int(_) => Some(Category::Number), SyntaxKind::Int(_) => Some(Category::Number),
NodeKind::Float(_) => Some(Category::Number), SyntaxKind::Float(_) => Some(Category::Number),
NodeKind::Numeric(_, _) => Some(Category::Number), SyntaxKind::Numeric(_, _) => Some(Category::Number),
NodeKind::Str(_) => Some(Category::String), SyntaxKind::Str(_) => Some(Category::String),
NodeKind::CodeBlock => None, SyntaxKind::CodeBlock => None,
NodeKind::ContentBlock => None, SyntaxKind::ContentBlock => None,
NodeKind::Parenthesized => None, SyntaxKind::Parenthesized => None,
NodeKind::Array => None, SyntaxKind::Array => None,
NodeKind::Dict => None, SyntaxKind::Dict => None,
NodeKind::Named => None, SyntaxKind::Named => None,
NodeKind::Keyed => None, SyntaxKind::Keyed => None,
NodeKind::Unary => None, SyntaxKind::Unary => None,
NodeKind::Binary => None, SyntaxKind::Binary => None,
NodeKind::FieldAccess => None, SyntaxKind::FieldAccess => None,
NodeKind::FuncCall => None, SyntaxKind::FuncCall => None,
NodeKind::MethodCall => None, SyntaxKind::MethodCall => None,
NodeKind::Args => None, SyntaxKind::Args => None,
NodeKind::Spread => None, SyntaxKind::Spread => None,
NodeKind::Closure => None, SyntaxKind::Closure => None,
NodeKind::Params => None, SyntaxKind::Params => None,
NodeKind::LetBinding => None, SyntaxKind::LetBinding => None,
NodeKind::SetRule => None, SyntaxKind::SetRule => None,
NodeKind::ShowRule => None, SyntaxKind::ShowRule => None,
NodeKind::Conditional => None, SyntaxKind::Conditional => None,
NodeKind::WhileLoop => None, SyntaxKind::WhileLoop => None,
NodeKind::ForLoop => None, SyntaxKind::ForLoop => None,
NodeKind::ForPattern => None, SyntaxKind::ForPattern => None,
NodeKind::ModuleImport => None, SyntaxKind::ModuleImport => None,
NodeKind::ImportItems => None, SyntaxKind::ImportItems => None,
NodeKind::ModuleInclude => None, SyntaxKind::ModuleInclude => None,
NodeKind::LoopBreak => None, SyntaxKind::LoopBreak => None,
NodeKind::LoopContinue => None, SyntaxKind::LoopContinue => None,
NodeKind::FuncReturn => None, SyntaxKind::FuncReturn => None,
NodeKind::Error(_, _) => Some(Category::Error), SyntaxKind::Error(_, _) => Some(Category::Error),
} }
} }

View File

@ -2,7 +2,7 @@ use std::ops::Range;
use super::{ use super::{
is_newline, parse, reparse_code_block, reparse_content_block, is_newline, parse, reparse_code_block, reparse_content_block,
reparse_markup_elements, NodeKind, Span, SyntaxNode, reparse_markup_elements, Span, SyntaxKind, SyntaxNode,
}; };
/// Refresh the given syntax node with as little parsing as possible. /// Refresh the given syntax node with as little parsing as possible.
@ -36,7 +36,7 @@ fn try_reparse(
outermost: bool, outermost: bool,
safe_to_replace: bool, safe_to_replace: bool,
) -> Option<Range<usize>> { ) -> Option<Range<usize>> {
let is_markup = matches!(node.kind(), NodeKind::Markup { .. }); let is_markup = matches!(node.kind(), SyntaxKind::Markup { .. });
let original_count = node.children().len(); let original_count = node.children().len();
let original_offset = offset; let original_offset = offset;
@ -78,7 +78,7 @@ fn try_reparse(
} else { } else {
// Update compulsary state of `ahead_nontrivia`. // Update compulsary state of `ahead_nontrivia`.
if let Some(ahead_nontrivia) = ahead.as_mut() { if let Some(ahead_nontrivia) = ahead.as_mut() {
if let NodeKind::Space { newlines: (1..) } = child.kind() { if let SyntaxKind::Space { newlines: (1..) } = child.kind() {
ahead_nontrivia.newline(); ahead_nontrivia.newline();
} }
} }
@ -87,8 +87,8 @@ fn try_reparse(
// reject text that points to the special case for URL // reject text that points to the special case for URL
// evasion and line comments. // evasion and line comments.
if !child.kind().is_space() if !child.kind().is_space()
&& child.kind() != &NodeKind::Semicolon && child.kind() != &SyntaxKind::Semicolon
&& child.kind() != &NodeKind::Text('/'.into()) && child.kind() != &SyntaxKind::Text('/'.into())
&& (ahead.is_none() || change.replaced.start > child_span.end) && (ahead.is_none() || change.replaced.start > child_span.end)
&& !ahead.map_or(false, Ahead::is_compulsory) && !ahead.map_or(false, Ahead::is_compulsory)
{ {
@ -153,8 +153,8 @@ fn try_reparse(
let superseded_span = pos.offset..pos.offset + prev_len; let superseded_span = pos.offset..pos.offset + prev_len;
let func: Option<ReparseMode> = match child.kind() { let func: Option<ReparseMode> = match child.kind() {
NodeKind::CodeBlock => Some(ReparseMode::Code), SyntaxKind::CodeBlock => Some(ReparseMode::Code),
NodeKind::ContentBlock => Some(ReparseMode::Content), SyntaxKind::ContentBlock => Some(ReparseMode::Content),
_ => None, _ => None,
}; };
@ -177,7 +177,7 @@ fn try_reparse(
// Make sure this is a markup node and that we may replace. If so, save // Make sure this is a markup node and that we may replace. If so, save
// the current indent. // the current indent.
let min_indent = match node.kind() { let min_indent = match node.kind() {
NodeKind::Markup { min_indent } if safe_to_replace => *min_indent, SyntaxKind::Markup { min_indent } if safe_to_replace => *min_indent,
_ => return None, _ => return None,
}; };
@ -375,26 +375,28 @@ enum ReparseMode {
/// Whether changes _inside_ this node are safely encapsulated, so that only /// Whether changes _inside_ this node are safely encapsulated, so that only
/// this node must be reparsed. /// this node must be reparsed.
fn is_bounded(kind: &NodeKind) -> bool { fn is_bounded(kind: &SyntaxKind) -> bool {
matches!( matches!(
kind, kind,
NodeKind::CodeBlock SyntaxKind::CodeBlock
| NodeKind::ContentBlock | SyntaxKind::ContentBlock
| NodeKind::Linebreak | SyntaxKind::Linebreak
| NodeKind::SmartQuote { .. } | SyntaxKind::SmartQuote { .. }
| NodeKind::BlockComment | SyntaxKind::BlockComment
| NodeKind::Space { .. } | SyntaxKind::Space { .. }
| NodeKind::Escape(_) | SyntaxKind::Escape(_)
| NodeKind::Shorthand(_) | SyntaxKind::Shorthand(_)
) )
} }
/// Whether `at_start` would still be true after this node given the /// Whether `at_start` would still be true after this node given the
/// previous value of the property. /// previous value of the property.
fn next_at_start(kind: &NodeKind, prev: bool) -> bool { fn next_at_start(kind: &SyntaxKind, prev: bool) -> bool {
match kind { match kind {
NodeKind::Space { newlines: (1..) } => true, SyntaxKind::Space { newlines: (1..) } => true,
NodeKind::Space { .. } | NodeKind::LineComment | NodeKind::BlockComment => prev, SyntaxKind::Space { .. } | SyntaxKind::LineComment | SyntaxKind::BlockComment => {
prev
}
_ => false, _ => false,
} }
} }

View File

@ -6,10 +6,9 @@ use crate::util::EcoString;
/// All syntactical building blocks that can be part of a Typst document. /// 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 syntax node by /// Can be created by the tokenizer or by the parser.
/// the parser.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum NodeKind { pub enum SyntaxKind {
/// A line comment: `// ...`. /// A line comment: `// ...`.
LineComment, LineComment,
/// A block comment: `/* ... */`. /// A block comment: `/* ... */`.
@ -254,7 +253,7 @@ pub enum NodeKind {
Error(ErrorPos, EcoString), Error(ErrorPos, EcoString),
} }
/// Fields of a [`Raw`](NodeKind::Raw) node. /// Fields of the raw syntax kind.
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, PartialEq, Hash)]
pub struct RawFields { pub struct RawFields {
/// An optional identifier specifying the language to syntax-highlight in. /// An optional identifier specifying the language to syntax-highlight in.
@ -293,7 +292,7 @@ pub enum ErrorPos {
End, End,
} }
impl NodeKind { impl SyntaxKind {
/// Whether this is trivia. /// Whether this is trivia.
pub fn is_trivia(&self) -> bool { pub fn is_trivia(&self) -> bool {
self.is_space() || matches!(self, Self::LineComment | Self::BlockComment) self.is_space() || matches!(self, Self::LineComment | Self::BlockComment)
@ -311,18 +310,18 @@ impl NodeKind {
/// Whether this is an error. /// Whether this is an error.
pub fn is_error(&self) -> bool { pub fn is_error(&self) -> bool {
matches!(self, NodeKind::Error(_, _)) matches!(self, SyntaxKind::Error(_, _))
} }
/// Does this node need termination through a semicolon or linebreak? /// Does this node need termination through a semicolon or linebreak?
pub fn is_stmt(&self) -> bool { pub fn is_stmt(&self) -> bool {
matches!( matches!(
self, self,
NodeKind::LetBinding SyntaxKind::LetBinding
| NodeKind::SetRule | SyntaxKind::SetRule
| NodeKind::ShowRule | SyntaxKind::ShowRule
| NodeKind::ModuleImport | SyntaxKind::ModuleImport
| NodeKind::ModuleInclude | SyntaxKind::ModuleInclude
) )
} }
@ -445,7 +444,7 @@ impl NodeKind {
} }
} }
impl Hash for NodeKind { impl Hash for SyntaxKind {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state); std::mem::discriminant(self).hash(state);
match self { match self {

View File

@ -1,9 +1,9 @@
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Display, Formatter};
use std::ops::Range; use std::ops::Range;
use std::sync::Arc; use std::sync::Arc;
use super::ast::TypedNode; use super::ast::AstNode;
use super::{NodeKind, NumberingResult, SourceId, Span, Unnumberable}; use super::{SourceId, Span, SyntaxKind};
use crate::diag::SourceError; use crate::diag::SourceError;
/// A node in the untyped syntax tree. /// A node in the untyped syntax tree.
@ -21,22 +21,22 @@ enum Repr {
impl SyntaxNode { impl SyntaxNode {
/// Create a new leaf node. /// Create a new leaf node.
pub fn leaf(kind: NodeKind, len: usize) -> Self { pub fn leaf(kind: SyntaxKind, len: usize) -> Self {
Self(Repr::Leaf(NodeData::new(kind, len))) Self(Repr::Leaf(NodeData::new(kind, len)))
} }
/// Create a new inner node with children. /// Create a new inner node with children.
pub fn inner(kind: NodeKind, children: Vec<SyntaxNode>) -> Self { pub fn inner(kind: SyntaxKind, children: Vec<SyntaxNode>) -> Self {
Self(Repr::Inner(Arc::new(InnerNode::with_children(kind, children)))) Self(Repr::Inner(Arc::new(InnerNode::with_children(kind, children))))
} }
/// The type of the node. /// The type of the node.
pub fn kind(&self) -> &NodeKind { pub fn kind(&self) -> &SyntaxKind {
&self.data().kind &self.data().kind
} }
/// Take the kind out of the node. /// Take the kind out of the node.
pub fn take(self) -> NodeKind { pub fn take(self) -> SyntaxKind {
match self.0 { match self.0 {
Repr::Leaf(leaf) => leaf.kind, Repr::Leaf(leaf) => leaf.kind,
Repr::Inner(inner) => inner.data.kind.clone(), Repr::Inner(inner) => inner.data.kind.clone(),
@ -72,18 +72,18 @@ impl SyntaxNode {
/// Convert the node to a typed AST node. /// Convert the node to a typed AST node.
pub fn cast<T>(&self) -> Option<T> pub fn cast<T>(&self) -> Option<T>
where where
T: TypedNode, T: AstNode,
{ {
T::from_untyped(self) T::from_untyped(self)
} }
/// Get the first child that can cast to the AST type `T`. /// Get the first child that can cast to the AST type `T`.
pub fn cast_first_child<T: TypedNode>(&self) -> Option<T> { pub fn cast_first_child<T: AstNode>(&self) -> Option<T> {
self.children().find_map(Self::cast) self.children().find_map(Self::cast)
} }
/// Get the last child that can cast to the AST type `T`. /// Get the last child that can cast to the AST type `T`.
pub fn cast_last_child<T: TypedNode>(&self) -> Option<T> { pub fn cast_last_child<T: AstNode>(&self) -> Option<T> {
self.children().rev().find_map(Self::cast) self.children().rev().find_map(Self::cast)
} }
@ -102,7 +102,7 @@ impl SyntaxNode {
} }
match self.kind() { match self.kind() {
NodeKind::Error(pos, message) => { SyntaxKind::Error(pos, message) => {
vec![SourceError::new(self.span(), message.clone()).with_pos(*pos)] vec![SourceError::new(self.span(), message.clone()).with_pos(*pos)]
} }
_ => self _ => self
@ -114,7 +114,7 @@ impl SyntaxNode {
} }
/// Change the type of the node. /// Change the type of the node.
pub(super) fn convert(&mut self, kind: NodeKind) { pub(super) fn convert(&mut self, kind: SyntaxKind) {
match &mut self.0 { match &mut self.0 {
Repr::Inner(inner) => { Repr::Inner(inner) => {
let node = Arc::make_mut(inner); let node = Arc::make_mut(inner);
@ -226,7 +226,7 @@ impl Debug for SyntaxNode {
impl Default for SyntaxNode { impl Default for SyntaxNode {
fn default() -> Self { fn default() -> Self {
Self::leaf(NodeKind::None, 0) Self::leaf(SyntaxKind::None, 0)
} }
} }
@ -247,7 +247,7 @@ struct InnerNode {
impl InnerNode { impl InnerNode {
/// Create a new inner node with the given kind and children. /// Create a new inner node with the given kind and children.
fn with_children(kind: NodeKind, children: Vec<SyntaxNode>) -> Self { fn with_children(kind: SyntaxKind, children: Vec<SyntaxNode>) -> Self {
let mut len = 0; let mut len = 0;
let mut descendants = 1; let mut descendants = 1;
let mut erroneous = kind.is_error(); let mut erroneous = kind.is_error();
@ -305,7 +305,7 @@ impl InnerNode {
} }
} }
// Number this node itself. // Number the node itself.
let mut start = within.start; let mut start = within.start;
if range.is_none() { if range.is_none() {
let end = start + stride; let end = start + stride;
@ -480,7 +480,7 @@ impl PartialEq for InnerNode {
struct NodeData { struct NodeData {
/// What kind of node this is (each kind would have its own struct in a /// What kind of node this is (each kind would have its own struct in a
/// strongly typed AST). /// strongly typed AST).
kind: NodeKind, kind: SyntaxKind,
/// The byte length of the node in the source. /// The byte length of the node in the source.
len: usize, len: usize,
/// The node's span. /// The node's span.
@ -489,7 +489,7 @@ struct NodeData {
impl NodeData { impl NodeData {
/// Create new node metadata. /// Create new node metadata.
fn new(kind: NodeKind, len: usize) -> Self { fn new(kind: SyntaxKind, len: usize) -> Self {
Self { len, kind, span: Span::detached() } Self { len, kind, span: Span::detached() }
} }
@ -525,3 +525,18 @@ impl PartialEq for NodeData {
self.kind == other.kind && self.len == other.len self.kind == other.kind && self.len == other.len
} }
} }
/// Result of numbering a node within an interval.
pub(super) type NumberingResult = Result<(), Unnumberable>;
/// Indicates that a node cannot be numbered within a given interval.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(super) struct Unnumberable;
impl Display for Unnumberable {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("cannot number within this interval")
}
}
impl std::error::Error for Unnumberable {}

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use std::mem; use std::mem;
use std::ops::Range; use std::ops::Range;
use super::{ErrorPos, NodeKind, SyntaxNode, TokenMode, Tokens}; use super::{ErrorPos, SyntaxKind, SyntaxNode, TokenMode, Tokens};
use crate::util::{format_eco, EcoString}; use crate::util::{format_eco, EcoString};
/// A convenient token-based parser. /// A convenient token-based parser.
@ -12,7 +12,7 @@ pub struct Parser<'s> {
/// Whether we are at the end of the file or of a group. /// Whether we are at the end of the file or of a group.
eof: bool, eof: bool,
/// The current token. /// The current token.
current: Option<NodeKind>, current: Option<SyntaxKind>,
/// The end byte index of the last non-trivia token. /// The end byte index of the last non-trivia token.
prev_end: usize, prev_end: usize,
/// The start byte index of the peeked token. /// The start byte index of the peeked token.
@ -82,7 +82,7 @@ impl<'s> Parser<'s> {
} }
/// Perform a subparse that wraps its result in a node with the given kind. /// Perform a subparse that wraps its result in a node with the given kind.
pub fn perform<F, T>(&mut self, kind: NodeKind, f: F) -> T pub fn perform<F, T>(&mut self, kind: SyntaxKind, f: F) -> T
where where
F: FnOnce(&mut Self) -> T, F: FnOnce(&mut Self) -> T,
{ {
@ -112,9 +112,9 @@ impl<'s> Parser<'s> {
/// Consume the current token and also trailing trivia. /// Consume the current token and also trailing trivia.
pub fn eat(&mut self) { pub fn eat(&mut self) {
self.stray_terminator |= match self.current { self.stray_terminator |= match self.current {
Some(NodeKind::RightParen) => !self.inside(Group::Paren), Some(SyntaxKind::RightParen) => !self.inside(Group::Paren),
Some(NodeKind::RightBracket) => !self.inside(Group::Bracket), Some(SyntaxKind::RightBracket) => !self.inside(Group::Bracket),
Some(NodeKind::RightBrace) => !self.inside(Group::Brace), Some(SyntaxKind::RightBrace) => !self.inside(Group::Brace),
_ => false, _ => false,
}; };
@ -132,7 +132,7 @@ impl<'s> Parser<'s> {
} }
/// Consume the current token if it is the given one. /// Consume the current token if it is the given one.
pub fn eat_if(&mut self, kind: NodeKind) -> bool { pub fn eat_if(&mut self, kind: SyntaxKind) -> bool {
let at = self.at(kind); let at = self.at(kind);
if at { if at {
self.eat(); self.eat();
@ -143,7 +143,7 @@ impl<'s> Parser<'s> {
/// Eat tokens while the condition is true. /// Eat tokens while the condition is true.
pub fn eat_while<F>(&mut self, mut f: F) pub fn eat_while<F>(&mut self, mut f: F)
where where
F: FnMut(&NodeKind) -> bool, F: FnMut(&SyntaxKind) -> bool,
{ {
while self.peek().map_or(false, |t| f(t)) { while self.peek().map_or(false, |t| f(t)) {
self.eat(); self.eat();
@ -152,7 +152,7 @@ impl<'s> Parser<'s> {
/// Consume the current token if it is the given one and produce an error if /// Consume the current token if it is the given one and produce an error if
/// not. /// not.
pub fn expect(&mut self, kind: NodeKind) -> ParseResult { pub fn expect(&mut self, kind: SyntaxKind) -> ParseResult {
let at = self.peek() == Some(&kind); let at = self.peek() == Some(&kind);
if at { if at {
self.eat(); self.eat();
@ -165,18 +165,18 @@ impl<'s> Parser<'s> {
/// Consume the current token, debug-asserting that it is the given one. /// Consume the current token, debug-asserting that it is the given one.
#[track_caller] #[track_caller]
pub fn assert(&mut self, kind: NodeKind) { pub fn assert(&mut self, kind: SyntaxKind) {
debug_assert_eq!(self.peek(), Some(&kind)); debug_assert_eq!(self.peek(), Some(&kind));
self.eat(); self.eat();
} }
/// Whether the current token is of the given type. /// Whether the current token is of the given type.
pub fn at(&self, kind: NodeKind) -> bool { pub fn at(&self, kind: SyntaxKind) -> bool {
self.peek() == Some(&kind) self.peek() == Some(&kind)
} }
/// Peek at the current token without consuming it. /// Peek at the current token without consuming it.
pub fn peek(&self) -> Option<&NodeKind> { pub fn peek(&self) -> Option<&SyntaxKind> {
if self.eof { if self.eof {
None None
} else { } else {
@ -186,7 +186,7 @@ impl<'s> Parser<'s> {
/// Peek at the current token, but only if it follows immediately after the /// Peek at the current token, but only if it follows immediately after the
/// last one without any trivia in between. /// last one without any trivia in between.
pub fn peek_direct(&self) -> Option<&NodeKind> { pub fn peek_direct(&self) -> Option<&SyntaxKind> {
if self.prev_end() == self.current_start() { if self.prev_end() == self.current_start() {
self.peek() self.peek()
} else { } else {
@ -249,12 +249,12 @@ impl<'s> Parser<'s> {
}); });
match kind { match kind {
Group::Brace => self.assert(NodeKind::LeftBrace), Group::Brace => self.assert(SyntaxKind::LeftBrace),
Group::Bracket => self.assert(NodeKind::LeftBracket), Group::Bracket => self.assert(SyntaxKind::LeftBracket),
Group::Paren => self.assert(NodeKind::LeftParen), Group::Paren => self.assert(SyntaxKind::LeftParen),
Group::Strong => self.assert(NodeKind::Star), Group::Strong => self.assert(SyntaxKind::Star),
Group::Emph => self.assert(NodeKind::Underscore), Group::Emph => self.assert(SyntaxKind::Underscore),
Group::Math => self.assert(NodeKind::Dollar), Group::Math => self.assert(SyntaxKind::Dollar),
Group::Expr => self.repeek(), Group::Expr => self.repeek(),
Group::Imports => self.repeek(), Group::Imports => self.repeek(),
} }
@ -273,13 +273,13 @@ impl<'s> Parser<'s> {
// Eat the end delimiter if there is one. // Eat the end delimiter if there is one.
if let Some((end, required)) = match group.kind { if let Some((end, required)) = match group.kind {
Group::Brace => Some((NodeKind::RightBrace, true)), Group::Brace => Some((SyntaxKind::RightBrace, true)),
Group::Bracket => Some((NodeKind::RightBracket, true)), Group::Bracket => Some((SyntaxKind::RightBracket, true)),
Group::Paren => Some((NodeKind::RightParen, true)), Group::Paren => Some((SyntaxKind::RightParen, true)),
Group::Strong => Some((NodeKind::Star, true)), Group::Strong => Some((SyntaxKind::Star, true)),
Group::Emph => Some((NodeKind::Underscore, true)), Group::Emph => Some((SyntaxKind::Underscore, true)),
Group::Math => Some((NodeKind::Dollar, true)), Group::Math => Some((SyntaxKind::Dollar, true)),
Group::Expr => Some((NodeKind::Semicolon, false)), Group::Expr => Some((SyntaxKind::Semicolon, false)),
Group::Imports => None, Group::Imports => None,
} { } {
if self.current.as_ref() == Some(&end) { if self.current.as_ref() == Some(&end) {
@ -339,26 +339,26 @@ impl<'s> Parser<'s> {
/// group. /// group.
fn repeek(&mut self) { fn repeek(&mut self) {
self.eof = match &self.current { self.eof = match &self.current {
Some(NodeKind::RightBrace) => self.inside(Group::Brace), Some(SyntaxKind::RightBrace) => self.inside(Group::Brace),
Some(NodeKind::RightBracket) => self.inside(Group::Bracket), Some(SyntaxKind::RightBracket) => self.inside(Group::Bracket),
Some(NodeKind::RightParen) => self.inside(Group::Paren), Some(SyntaxKind::RightParen) => self.inside(Group::Paren),
Some(NodeKind::Star) => self.inside(Group::Strong), Some(SyntaxKind::Star) => self.inside(Group::Strong),
Some(NodeKind::Underscore) => self.inside(Group::Emph), Some(SyntaxKind::Underscore) => self.inside(Group::Emph),
Some(NodeKind::Dollar) => self.inside(Group::Math), Some(SyntaxKind::Dollar) => self.inside(Group::Math),
Some(NodeKind::Semicolon) => self.inside(Group::Expr), Some(SyntaxKind::Semicolon) => self.inside(Group::Expr),
Some(NodeKind::From) => self.inside(Group::Imports), Some(SyntaxKind::From) => self.inside(Group::Imports),
Some(NodeKind::Space { newlines }) => self.space_ends_group(*newlines), Some(SyntaxKind::Space { newlines }) => self.space_ends_group(*newlines),
Some(_) => false, Some(_) => false,
None => true, None => true,
}; };
} }
/// Returns whether the given type can be skipped over. /// Returns whether the given type can be skipped over.
fn is_trivia(&self, token: &NodeKind) -> bool { fn is_trivia(&self, token: &SyntaxKind) -> bool {
match token { match token {
NodeKind::Space { newlines } => !self.space_ends_group(*newlines), SyntaxKind::Space { newlines } => !self.space_ends_group(*newlines),
NodeKind::LineComment => true, SyntaxKind::LineComment => true,
NodeKind::BlockComment => true, SyntaxKind::BlockComment => true,
_ => false, _ => false,
} }
} }
@ -378,7 +378,7 @@ impl<'s> Parser<'s> {
!= Some(Group::Brace) != Some(Group::Brace)
|| !matches!( || !matches!(
self.tokens.clone().next(), self.tokens.clone().next(),
Some(NodeKind::Else | NodeKind::Dot) Some(SyntaxKind::Else | SyntaxKind::Dot)
) )
} }
_ => false, _ => false,
@ -401,7 +401,7 @@ impl Parser<'_> {
pub fn unexpected(&mut self) { pub fn unexpected(&mut self) {
if let Some(found) = self.peek() { if let Some(found) = self.peek() {
let msg = format_eco!("unexpected {}", found.name()); let msg = format_eco!("unexpected {}", found.name());
let error = NodeKind::Error(ErrorPos::Full, msg); let error = SyntaxKind::Error(ErrorPos::Full, msg);
self.perform(error, Self::eat); self.perform(error, Self::eat);
} }
} }
@ -415,7 +415,7 @@ impl Parser<'_> {
/// Insert an error message that `what` was expected at the marker position. /// Insert an error message that `what` was expected at the marker position.
pub fn expected_at(&mut self, marker: Marker, what: &str) { pub fn expected_at(&mut self, marker: Marker, what: &str) {
let msg = format_eco!("expected {}", what); let msg = format_eco!("expected {}", what);
let error = NodeKind::Error(ErrorPos::Full, msg); let error = SyntaxKind::Error(ErrorPos::Full, msg);
self.children.insert(marker.0, SyntaxNode::leaf(error, 0)); self.children.insert(marker.0, SyntaxNode::leaf(error, 0));
} }
@ -425,7 +425,7 @@ impl Parser<'_> {
match self.peek() { match self.peek() {
Some(found) => { Some(found) => {
let msg = format_eco!("expected {}, found {}", thing, found.name()); let msg = format_eco!("expected {}, found {}", thing, found.name());
let error = NodeKind::Error(ErrorPos::Full, msg); let error = SyntaxKind::Error(ErrorPos::Full, msg);
self.perform(error, Self::eat); self.perform(error, Self::eat);
} }
None => self.expected(thing), None => self.expected(thing),
@ -449,7 +449,7 @@ impl Marker {
} }
/// Convert the child directly after marker. /// Convert the child directly after marker.
pub fn convert(self, p: &mut Parser, kind: NodeKind) { pub fn convert(self, p: &mut Parser, kind: SyntaxKind) {
if let Some(child) = p.children.get_mut(self.0) { if let Some(child) = p.children.get_mut(self.0) {
child.convert(kind); child.convert(kind);
} }
@ -457,7 +457,7 @@ impl Marker {
/// Perform a subparse that wraps all children after the marker in a node /// Perform a subparse that wraps all children after the marker in a node
/// with the given kind. /// with the given kind.
pub fn perform<T, F>(self, p: &mut Parser, kind: NodeKind, f: F) -> T pub fn perform<T, F>(self, p: &mut Parser, kind: SyntaxKind, f: F) -> T
where where
F: FnOnce(&mut Parser) -> T, F: FnOnce(&mut Parser) -> T,
{ {
@ -468,7 +468,7 @@ impl Marker {
/// Wrap all children after the marker (excluding trailing trivia) in a node /// Wrap all children after the marker (excluding trailing trivia) in a node
/// with the given `kind`. /// with the given `kind`.
pub fn end(self, p: &mut Parser, kind: NodeKind) { pub fn end(self, p: &mut Parser, kind: SyntaxKind) {
let until = p.trivia_start().0.max(self.0); let until = p.trivia_start().0.max(self.0);
let children = p.children.drain(self.0..until).collect(); let children = p.children.drain(self.0..until).collect();
p.children.insert(self.0, SyntaxNode::inner(kind, children)); p.children.insert(self.0, SyntaxNode::inner(kind, children));
@ -496,7 +496,7 @@ impl Marker {
msg.push_str(", found "); msg.push_str(", found ");
msg.push_str(child.kind().name()); msg.push_str(child.kind().name());
} }
let error = NodeKind::Error(ErrorPos::Full, msg); let error = SyntaxKind::Error(ErrorPos::Full, msg);
let inner = mem::take(child); let inner = mem::take(child);
*child = SyntaxNode::inner(error, vec![inner]); *child = SyntaxNode::inner(error, vec![inner]);
} }

View File

@ -2,7 +2,7 @@ use std::collections::HashSet;
use super::ast::{Assoc, BinOp, UnOp}; use super::ast::{Assoc, BinOp, UnOp};
use super::{ use super::{
ErrorPos, Group, Marker, NodeKind, ParseError, ParseResult, Parser, SyntaxNode, ErrorPos, Group, Marker, ParseError, ParseResult, Parser, SyntaxKind, SyntaxNode,
TokenMode, TokenMode,
}; };
use crate::util::EcoString; use crate::util::EcoString;
@ -17,7 +17,7 @@ pub fn parse(text: &str) -> SyntaxNode {
/// Parse code directly, only used for syntax highlighting. /// Parse code directly, only used for syntax highlighting.
pub fn parse_code(text: &str) -> SyntaxNode { pub fn parse_code(text: &str) -> SyntaxNode {
let mut p = Parser::new(text, TokenMode::Code); let mut p = Parser::new(text, TokenMode::Code);
p.perform(NodeKind::CodeBlock, code); p.perform(SyntaxKind::CodeBlock, code);
p.finish().into_iter().next().unwrap() p.finish().into_iter().next().unwrap()
} }
@ -30,7 +30,7 @@ pub(crate) fn reparse_code_block(
end_pos: usize, end_pos: usize,
) -> Option<(Vec<SyntaxNode>, bool, usize)> { ) -> Option<(Vec<SyntaxNode>, bool, usize)> {
let mut p = Parser::with_prefix(prefix, text, TokenMode::Code); let mut p = Parser::with_prefix(prefix, text, TokenMode::Code);
if !p.at(NodeKind::LeftBrace) { if !p.at(SyntaxKind::LeftBrace) {
return None; return None;
} }
@ -54,7 +54,7 @@ pub(crate) fn reparse_content_block(
end_pos: usize, end_pos: usize,
) -> Option<(Vec<SyntaxNode>, bool, usize)> { ) -> Option<(Vec<SyntaxNode>, bool, usize)> {
let mut p = Parser::with_prefix(prefix, text, TokenMode::Code); let mut p = Parser::with_prefix(prefix, text, TokenMode::Code);
if !p.at(NodeKind::LeftBracket) { if !p.at(SyntaxKind::LeftBracket) {
return None; return None;
} }
@ -90,7 +90,7 @@ pub(crate) fn reparse_markup_elements(
let mut stopped = false; let mut stopped = false;
'outer: while !p.eof() { 'outer: while !p.eof() {
if let Some(NodeKind::Space { newlines: (1..) }) = p.peek() { if let Some(SyntaxKind::Space { newlines: (1..) }) = p.peek() {
if p.column(p.current_end()) < min_indent { if p.column(p.current_end()) < min_indent {
return None; return None;
} }
@ -147,7 +147,7 @@ pub(crate) fn reparse_markup_elements(
/// If `at_start` is true, things like headings that may only appear at the /// If `at_start` is true, things like headings that may only appear at the
/// beginning of a line or content block are initially allowed. /// beginning of a line or content block are initially allowed.
fn markup(p: &mut Parser, mut at_start: bool) { fn markup(p: &mut Parser, mut at_start: bool) {
p.perform(NodeKind::Markup { min_indent: 0 }, |p| { p.perform(SyntaxKind::Markup { min_indent: 0 }, |p| {
while !p.eof() { while !p.eof() {
markup_node(p, &mut at_start); markup_node(p, &mut at_start);
} }
@ -157,8 +157,8 @@ fn markup(p: &mut Parser, mut at_start: bool) {
/// Parse markup that stays right of the given `column`. /// Parse markup that stays right of the given `column`.
fn markup_indented(p: &mut Parser, min_indent: usize) { fn markup_indented(p: &mut Parser, min_indent: usize) {
p.eat_while(|t| match t { p.eat_while(|t| match t {
NodeKind::Space { newlines } => *newlines == 0, SyntaxKind::Space { newlines } => *newlines == 0,
NodeKind::LineComment | NodeKind::BlockComment => true, SyntaxKind::LineComment | SyntaxKind::BlockComment => true,
_ => false, _ => false,
}); });
@ -167,7 +167,7 @@ fn markup_indented(p: &mut Parser, min_indent: usize) {
while !p.eof() { while !p.eof() {
match p.peek() { match p.peek() {
Some(NodeKind::Space { newlines: (1..) }) Some(SyntaxKind::Space { newlines: (1..) })
if p.column(p.current_end()) < min_indent => if p.column(p.current_end()) < min_indent =>
{ {
break; break;
@ -178,24 +178,24 @@ fn markup_indented(p: &mut Parser, min_indent: usize) {
markup_node(p, &mut at_start); markup_node(p, &mut at_start);
} }
marker.end(p, NodeKind::Markup { min_indent }); marker.end(p, SyntaxKind::Markup { min_indent });
} }
/// Parse a line of markup that can prematurely end if `f` returns true. /// Parse a line of markup that can prematurely end if `f` returns true.
fn markup_line<F>(p: &mut Parser, mut f: F) fn markup_line<F>(p: &mut Parser, mut f: F)
where where
F: FnMut(&NodeKind) -> bool, F: FnMut(&SyntaxKind) -> bool,
{ {
p.eat_while(|t| match t { p.eat_while(|t| match t {
NodeKind::Space { newlines } => *newlines == 0, SyntaxKind::Space { newlines } => *newlines == 0,
NodeKind::LineComment | NodeKind::BlockComment => true, SyntaxKind::LineComment | SyntaxKind::BlockComment => true,
_ => false, _ => false,
}); });
p.perform(NodeKind::Markup { min_indent: usize::MAX }, |p| { p.perform(SyntaxKind::Markup { min_indent: usize::MAX }, |p| {
let mut at_start = false; let mut at_start = false;
while let Some(kind) = p.peek() { while let Some(kind) = p.peek() {
if let NodeKind::Space { newlines: (1..) } = kind { if let SyntaxKind::Space { newlines: (1..) } = kind {
break; break;
} }
@ -212,68 +212,68 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) {
let Some(token) = p.peek() else { return }; let Some(token) = p.peek() else { return };
match token { match token {
// Whitespace. // Whitespace.
NodeKind::Space { newlines } => { SyntaxKind::Space { newlines } => {
*at_start |= *newlines > 0; *at_start |= *newlines > 0;
p.eat(); p.eat();
return; return;
} }
// Comments. // Comments.
NodeKind::LineComment | NodeKind::BlockComment => { SyntaxKind::LineComment | SyntaxKind::BlockComment => {
p.eat(); p.eat();
return; return;
} }
// Text and markup. // Text and markup.
NodeKind::Text(_) SyntaxKind::Text(_)
| NodeKind::Linebreak | SyntaxKind::Linebreak
| NodeKind::SmartQuote { .. } | SyntaxKind::SmartQuote { .. }
| NodeKind::Escape(_) | SyntaxKind::Escape(_)
| NodeKind::Shorthand(_) | SyntaxKind::Shorthand(_)
| NodeKind::Link(_) | SyntaxKind::Link(_)
| NodeKind::Raw(_) | SyntaxKind::Raw(_)
| NodeKind::Label(_) | SyntaxKind::Label(_)
| NodeKind::Ref(_) => p.eat(), | SyntaxKind::Ref(_) => p.eat(),
// Math. // Math.
NodeKind::Dollar => math(p), SyntaxKind::Dollar => math(p),
// Strong, emph, heading. // Strong, emph, heading.
NodeKind::Star => strong(p), SyntaxKind::Star => strong(p),
NodeKind::Underscore => emph(p), SyntaxKind::Underscore => emph(p),
NodeKind::Eq => heading(p, *at_start), SyntaxKind::Eq => heading(p, *at_start),
// Lists. // Lists.
NodeKind::Minus => list_item(p, *at_start), SyntaxKind::Minus => list_item(p, *at_start),
NodeKind::Plus | NodeKind::EnumNumbering(_) => enum_item(p, *at_start), SyntaxKind::Plus | SyntaxKind::EnumNumbering(_) => enum_item(p, *at_start),
NodeKind::Slash => { SyntaxKind::Slash => {
desc_item(p, *at_start).ok(); desc_item(p, *at_start).ok();
} }
NodeKind::Colon => { SyntaxKind::Colon => {
let marker = p.marker(); let marker = p.marker();
p.eat(); p.eat();
marker.convert(p, NodeKind::Text(':'.into())); marker.convert(p, SyntaxKind::Text(':'.into()));
} }
// Hashtag + keyword / identifier. // Hashtag + keyword / identifier.
NodeKind::Ident(_) SyntaxKind::Ident(_)
| NodeKind::Let | SyntaxKind::Let
| NodeKind::Set | SyntaxKind::Set
| NodeKind::Show | SyntaxKind::Show
| NodeKind::If | SyntaxKind::If
| NodeKind::While | SyntaxKind::While
| NodeKind::For | SyntaxKind::For
| NodeKind::Import | SyntaxKind::Import
| NodeKind::Include | SyntaxKind::Include
| NodeKind::Break | SyntaxKind::Break
| NodeKind::Continue | SyntaxKind::Continue
| NodeKind::Return => markup_expr(p), | SyntaxKind::Return => markup_expr(p),
// Code and content block. // Code and content block.
NodeKind::LeftBrace => code_block(p), SyntaxKind::LeftBrace => code_block(p),
NodeKind::LeftBracket => content_block(p), SyntaxKind::LeftBracket => content_block(p),
NodeKind::Error(_, _) => p.eat(), SyntaxKind::Error(_, _) => p.eat(),
_ => p.unexpected(), _ => p.unexpected(),
}; };
@ -281,7 +281,7 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) {
} }
fn strong(p: &mut Parser) { fn strong(p: &mut Parser) {
p.perform(NodeKind::Strong, |p| { p.perform(SyntaxKind::Strong, |p| {
p.start_group(Group::Strong); p.start_group(Group::Strong);
markup(p, false); markup(p, false);
p.end_group(); p.end_group();
@ -289,7 +289,7 @@ fn strong(p: &mut Parser) {
} }
fn emph(p: &mut Parser) { fn emph(p: &mut Parser) {
p.perform(NodeKind::Emph, |p| { p.perform(SyntaxKind::Emph, |p| {
p.start_group(Group::Emph); p.start_group(Group::Emph);
markup(p, false); markup(p, false);
p.end_group(); p.end_group();
@ -299,30 +299,30 @@ fn emph(p: &mut Parser) {
fn heading(p: &mut Parser, at_start: bool) { fn heading(p: &mut Parser, at_start: bool) {
let marker = p.marker(); let marker = p.marker();
let current_start = p.current_start(); let current_start = p.current_start();
p.assert(NodeKind::Eq); p.assert(SyntaxKind::Eq);
while p.eat_if(NodeKind::Eq) {} while p.eat_if(SyntaxKind::Eq) {}
if at_start && p.peek().map_or(true, |kind| kind.is_space()) { if at_start && p.peek().map_or(true, |kind| kind.is_space()) {
p.eat_while(|kind| *kind == NodeKind::Space { newlines: 0 }); p.eat_while(|kind| *kind == SyntaxKind::Space { newlines: 0 });
markup_line(p, |kind| matches!(kind, NodeKind::Label(_))); markup_line(p, |kind| matches!(kind, SyntaxKind::Label(_)));
marker.end(p, NodeKind::Heading); marker.end(p, SyntaxKind::Heading);
} else { } else {
let text = p.get(current_start..p.prev_end()).into(); let text = p.get(current_start..p.prev_end()).into();
marker.convert(p, NodeKind::Text(text)); marker.convert(p, SyntaxKind::Text(text));
} }
} }
fn list_item(p: &mut Parser, at_start: bool) { fn list_item(p: &mut Parser, at_start: bool) {
let marker = p.marker(); let marker = p.marker();
let text: EcoString = p.peek_src().into(); let text: EcoString = p.peek_src().into();
p.assert(NodeKind::Minus); p.assert(SyntaxKind::Minus);
let min_indent = p.column(p.prev_end()); let min_indent = p.column(p.prev_end());
if at_start && p.eat_if(NodeKind::Space { newlines: 0 }) && !p.eof() { if at_start && p.eat_if(SyntaxKind::Space { newlines: 0 }) && !p.eof() {
markup_indented(p, min_indent); markup_indented(p, min_indent);
marker.end(p, NodeKind::ListItem); marker.end(p, SyntaxKind::ListItem);
} else { } else {
marker.convert(p, NodeKind::Text(text)); marker.convert(p, SyntaxKind::Text(text));
} }
} }
@ -332,11 +332,11 @@ fn enum_item(p: &mut Parser, at_start: bool) {
p.eat(); p.eat();
let min_indent = p.column(p.prev_end()); let min_indent = p.column(p.prev_end());
if at_start && p.eat_if(NodeKind::Space { newlines: 0 }) && !p.eof() { if at_start && p.eat_if(SyntaxKind::Space { newlines: 0 }) && !p.eof() {
markup_indented(p, min_indent); markup_indented(p, min_indent);
marker.end(p, NodeKind::EnumItem); marker.end(p, SyntaxKind::EnumItem);
} else { } else {
marker.convert(p, NodeKind::Text(text)); marker.convert(p, SyntaxKind::Text(text));
} }
} }
@ -346,13 +346,13 @@ fn desc_item(p: &mut Parser, at_start: bool) -> ParseResult {
p.eat(); p.eat();
let min_indent = p.column(p.prev_end()); let min_indent = p.column(p.prev_end());
if at_start && p.eat_if(NodeKind::Space { newlines: 0 }) && !p.eof() { if at_start && p.eat_if(SyntaxKind::Space { newlines: 0 }) && !p.eof() {
markup_line(p, |node| matches!(node, NodeKind::Colon)); markup_line(p, |node| matches!(node, SyntaxKind::Colon));
p.expect(NodeKind::Colon)?; p.expect(SyntaxKind::Colon)?;
markup_indented(p, min_indent); markup_indented(p, min_indent);
marker.end(p, NodeKind::DescItem); marker.end(p, SyntaxKind::DescItem);
} else { } else {
marker.convert(p, NodeKind::Text(text)); marker.convert(p, SyntaxKind::Text(text));
} }
Ok(()) Ok(())
@ -363,11 +363,11 @@ fn markup_expr(p: &mut Parser) {
let stmt = matches!( let stmt = matches!(
p.peek(), p.peek(),
Some( Some(
NodeKind::Let SyntaxKind::Let
| NodeKind::Set | SyntaxKind::Set
| NodeKind::Show | SyntaxKind::Show
| NodeKind::Import | SyntaxKind::Import
| NodeKind::Include | SyntaxKind::Include
) )
); );
@ -380,7 +380,7 @@ fn markup_expr(p: &mut Parser) {
} }
fn math(p: &mut Parser) { fn math(p: &mut Parser) {
p.perform(NodeKind::Math, |p| { p.perform(SyntaxKind::Math, |p| {
p.start_group(Group::Math); p.start_group(Group::Math);
while !p.eof() { while !p.eof() {
math_node(p); math_node(p);
@ -393,20 +393,20 @@ fn math_node(p: &mut Parser) {
math_node_prec(p, 0, None) math_node_prec(p, 0, None)
} }
fn math_node_prec(p: &mut Parser, min_prec: usize, stop: Option<NodeKind>) { fn math_node_prec(p: &mut Parser, min_prec: usize, stop: Option<SyntaxKind>) {
let marker = p.marker(); let marker = p.marker();
math_primary(p); math_primary(p);
loop { loop {
let (kind, mut prec, assoc, stop) = match p.peek() { let (kind, mut prec, assoc, stop) = match p.peek() {
v if v == stop.as_ref() => break, v if v == stop.as_ref() => break,
Some(NodeKind::Underscore) => { Some(SyntaxKind::Underscore) => {
(NodeKind::Script, 2, Assoc::Right, Some(NodeKind::Hat)) (SyntaxKind::Script, 2, Assoc::Right, Some(SyntaxKind::Hat))
} }
Some(NodeKind::Hat) => { Some(SyntaxKind::Hat) => {
(NodeKind::Script, 2, Assoc::Right, Some(NodeKind::Underscore)) (SyntaxKind::Script, 2, Assoc::Right, Some(SyntaxKind::Underscore))
} }
Some(NodeKind::Slash) => (NodeKind::Frac, 1, Assoc::Left, None), Some(SyntaxKind::Slash) => (SyntaxKind::Frac, 1, Assoc::Left, None),
_ => break, _ => break,
}; };
@ -424,7 +424,7 @@ fn math_node_prec(p: &mut Parser, min_prec: usize, stop: Option<NodeKind>) {
// Allow up to two different scripts. We do not risk encountering the // Allow up to two different scripts. We do not risk encountering the
// previous script kind again here due to right-associativity. // previous script kind again here due to right-associativity.
if p.eat_if(NodeKind::Underscore) || p.eat_if(NodeKind::Hat) { if p.eat_if(SyntaxKind::Underscore) || p.eat_if(SyntaxKind::Hat) {
math_node_prec(p, prec, None); math_node_prec(p, prec, None);
} }
@ -437,42 +437,42 @@ fn math_primary(p: &mut Parser) {
let Some(token) = p.peek() else { return }; let Some(token) = p.peek() else { return };
match token { match token {
// Spaces, atoms and expressions. // Spaces, atoms and expressions.
NodeKind::Space { .. } SyntaxKind::Space { .. }
| NodeKind::Linebreak | SyntaxKind::Linebreak
| NodeKind::Escape(_) | SyntaxKind::Escape(_)
| NodeKind::Atom(_) | SyntaxKind::Atom(_)
| NodeKind::Ident(_) => p.eat(), | SyntaxKind::Ident(_) => p.eat(),
// Groups. // Groups.
NodeKind::LeftParen => math_group(p, Group::Paren, '(', ')'), SyntaxKind::LeftParen => math_group(p, Group::Paren, '(', ')'),
NodeKind::LeftBracket => math_group(p, Group::Bracket, '[', ']'), SyntaxKind::LeftBracket => math_group(p, Group::Bracket, '[', ']'),
NodeKind::LeftBrace => math_group(p, Group::Brace, '{', '}'), SyntaxKind::LeftBrace => math_group(p, Group::Brace, '{', '}'),
// Alignment indactor. // Alignment indactor.
NodeKind::Amp => math_align(p), SyntaxKind::Amp => math_align(p),
_ => p.unexpected(), _ => p.unexpected(),
} }
} }
fn math_group(p: &mut Parser, group: Group, l: char, r: char) { fn math_group(p: &mut Parser, group: Group, l: char, r: char) {
p.perform(NodeKind::Math, |p| { p.perform(SyntaxKind::Math, |p| {
let marker = p.marker(); let marker = p.marker();
p.start_group(group); p.start_group(group);
marker.convert(p, NodeKind::Atom(l.into())); marker.convert(p, SyntaxKind::Atom(l.into()));
while !p.eof() { while !p.eof() {
math_node(p); math_node(p);
} }
let marker = p.marker(); let marker = p.marker();
p.end_group(); p.end_group();
marker.convert(p, NodeKind::Atom(r.into())); marker.convert(p, SyntaxKind::Atom(r.into()));
}) })
} }
fn math_align(p: &mut Parser) { fn math_align(p: &mut Parser) {
p.perform(NodeKind::Align, |p| { p.perform(SyntaxKind::Align, |p| {
p.assert(NodeKind::Amp); p.assert(SyntaxKind::Amp);
while p.eat_if(NodeKind::Amp) {} while p.eat_if(SyntaxKind::Amp) {}
}) })
} }
@ -496,15 +496,15 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult {
p.eat(); p.eat();
let prec = op.precedence(); let prec = op.precedence();
expr_prec(p, atomic, prec)?; expr_prec(p, atomic, prec)?;
marker.end(p, NodeKind::Unary); marker.end(p, SyntaxKind::Unary);
} }
_ => primary(p, atomic)?, _ => primary(p, atomic)?,
}; };
loop { loop {
// Parenthesis or bracket means this is a function call. // Parenthesis or bracket means this is a function call.
if let Some(NodeKind::LeftParen | NodeKind::LeftBracket) = p.peek_direct() { if let Some(SyntaxKind::LeftParen | SyntaxKind::LeftBracket) = p.peek_direct() {
marker.perform(p, NodeKind::FuncCall, args)?; marker.perform(p, SyntaxKind::FuncCall, args)?;
continue; continue;
} }
@ -513,18 +513,19 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult {
} }
// Method call or field access. // Method call or field access.
if p.eat_if(NodeKind::Dot) { if p.eat_if(SyntaxKind::Dot) {
ident(p)?; ident(p)?;
if let Some(NodeKind::LeftParen | NodeKind::LeftBracket) = p.peek_direct() { if let Some(SyntaxKind::LeftParen | SyntaxKind::LeftBracket) = p.peek_direct()
marker.perform(p, NodeKind::MethodCall, args)?; {
marker.perform(p, SyntaxKind::MethodCall, args)?;
} else { } else {
marker.end(p, NodeKind::FieldAccess); marker.end(p, SyntaxKind::FieldAccess);
} }
continue; continue;
} }
let op = if p.eat_if(NodeKind::Not) { let op = if p.eat_if(SyntaxKind::Not) {
if p.at(NodeKind::In) { if p.at(SyntaxKind::In) {
BinOp::NotIn BinOp::NotIn
} else { } else {
p.expected("keyword `in`"); p.expected("keyword `in`");
@ -549,7 +550,7 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult {
Assoc::Right => {} Assoc::Right => {}
} }
marker.perform(p, NodeKind::Binary, |p| expr_prec(p, atomic, prec))?; marker.perform(p, SyntaxKind::Binary, |p| expr_prec(p, atomic, prec))?;
} }
Ok(()) Ok(())
@ -562,39 +563,39 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
match p.peek() { match p.peek() {
// Things that start with an identifier. // Things that start with an identifier.
Some(NodeKind::Ident(_)) => { Some(SyntaxKind::Ident(_)) => {
let marker = p.marker(); let marker = p.marker();
p.eat(); p.eat();
// Arrow means this is a closure's lone parameter. // Arrow means this is a closure's lone parameter.
if !atomic && p.at(NodeKind::Arrow) { if !atomic && p.at(SyntaxKind::Arrow) {
marker.end(p, NodeKind::Params); marker.end(p, SyntaxKind::Params);
p.assert(NodeKind::Arrow); p.assert(SyntaxKind::Arrow);
marker.perform(p, NodeKind::Closure, expr) marker.perform(p, SyntaxKind::Closure, expr)
} else { } else {
Ok(()) Ok(())
} }
} }
// Structures. // Structures.
Some(NodeKind::LeftParen) => parenthesized(p, atomic), Some(SyntaxKind::LeftParen) => parenthesized(p, atomic),
Some(NodeKind::LeftBrace) => Ok(code_block(p)), Some(SyntaxKind::LeftBrace) => Ok(code_block(p)),
Some(NodeKind::LeftBracket) => Ok(content_block(p)), Some(SyntaxKind::LeftBracket) => Ok(content_block(p)),
// Keywords. // Keywords.
Some(NodeKind::Let) => let_binding(p), Some(SyntaxKind::Let) => let_binding(p),
Some(NodeKind::Set) => set_rule(p), Some(SyntaxKind::Set) => set_rule(p),
Some(NodeKind::Show) => show_rule(p), Some(SyntaxKind::Show) => show_rule(p),
Some(NodeKind::If) => conditional(p), Some(SyntaxKind::If) => conditional(p),
Some(NodeKind::While) => while_loop(p), Some(SyntaxKind::While) => while_loop(p),
Some(NodeKind::For) => for_loop(p), Some(SyntaxKind::For) => for_loop(p),
Some(NodeKind::Import) => module_import(p), Some(SyntaxKind::Import) => module_import(p),
Some(NodeKind::Include) => module_include(p), Some(SyntaxKind::Include) => module_include(p),
Some(NodeKind::Break) => break_stmt(p), Some(SyntaxKind::Break) => break_stmt(p),
Some(NodeKind::Continue) => continue_stmt(p), Some(SyntaxKind::Continue) => continue_stmt(p),
Some(NodeKind::Return) => return_stmt(p), Some(SyntaxKind::Return) => return_stmt(p),
Some(NodeKind::Error(_, _)) => { Some(SyntaxKind::Error(_, _)) => {
p.eat(); p.eat();
Err(ParseError) Err(ParseError)
} }
@ -610,13 +611,13 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
fn literal(p: &mut Parser) -> bool { fn literal(p: &mut Parser) -> bool {
match p.peek() { match p.peek() {
Some( Some(
NodeKind::None SyntaxKind::None
| NodeKind::Auto | SyntaxKind::Auto
| NodeKind::Int(_) | SyntaxKind::Int(_)
| NodeKind::Float(_) | SyntaxKind::Float(_)
| NodeKind::Bool(_) | SyntaxKind::Bool(_)
| NodeKind::Numeric(_, _) | SyntaxKind::Numeric(_, _)
| NodeKind::Str(_), | SyntaxKind::Str(_),
) => { ) => {
p.eat(); p.eat();
true true
@ -627,7 +628,7 @@ fn literal(p: &mut Parser) -> bool {
fn ident(p: &mut Parser) -> ParseResult { fn ident(p: &mut Parser) -> ParseResult {
match p.peek() { match p.peek() {
Some(NodeKind::Ident(_)) => { Some(SyntaxKind::Ident(_)) => {
p.eat(); p.eat();
Ok(()) Ok(())
} }
@ -647,7 +648,7 @@ fn parenthesized(p: &mut Parser, atomic: bool) -> ParseResult {
let marker = p.marker(); let marker = p.marker();
p.start_group(Group::Paren); p.start_group(Group::Paren);
let colon = p.eat_if(NodeKind::Colon); let colon = p.eat_if(SyntaxKind::Colon);
let kind = collection(p, true).0; let kind = collection(p, true).0;
p.end_group(); p.end_group();
@ -658,15 +659,15 @@ fn parenthesized(p: &mut Parser, atomic: bool) -> ParseResult {
} }
// Arrow means this is a closure's parameter list. // Arrow means this is a closure's parameter list.
if !atomic && p.at(NodeKind::Arrow) { if !atomic && p.at(SyntaxKind::Arrow) {
params(p, marker); params(p, marker);
p.assert(NodeKind::Arrow); p.assert(SyntaxKind::Arrow);
return marker.perform(p, NodeKind::Closure, expr); return marker.perform(p, SyntaxKind::Closure, expr);
} }
// Transform into the identified collection. // Transform into the identified collection.
match kind { match kind {
CollectionKind::Group => marker.end(p, NodeKind::Parenthesized), CollectionKind::Group => marker.end(p, SyntaxKind::Parenthesized),
CollectionKind::Positional => array(p, marker), CollectionKind::Positional => array(p, marker),
CollectionKind::Named => dict(p, marker), CollectionKind::Named => dict(p, marker),
} }
@ -698,14 +699,14 @@ fn collection(p: &mut Parser, keyed: bool) -> (CollectionKind, usize) {
while !p.eof() { while !p.eof() {
let Ok(item_kind) = item(p, keyed) else { let Ok(item_kind) = item(p, keyed) else {
p.eat_if(NodeKind::Comma); p.eat_if(SyntaxKind::Comma);
collection_kind = Some(CollectionKind::Group); collection_kind = Some(CollectionKind::Group);
continue; continue;
}; };
match item_kind { match item_kind {
NodeKind::Spread => can_group = false, SyntaxKind::Spread => can_group = false,
NodeKind::Named if collection_kind.is_none() => { SyntaxKind::Named if collection_kind.is_none() => {
collection_kind = Some(CollectionKind::Named); collection_kind = Some(CollectionKind::Named);
can_group = false; can_group = false;
} }
@ -725,7 +726,7 @@ fn collection(p: &mut Parser, keyed: bool) -> (CollectionKind, usize) {
break; break;
} }
if p.eat_if(NodeKind::Comma) { if p.eat_if(SyntaxKind::Comma) {
can_group = false; can_group = false;
} else { } else {
missing_coma = Some(p.trivia_start()); missing_coma = Some(p.trivia_start());
@ -741,24 +742,24 @@ fn collection(p: &mut Parser, keyed: bool) -> (CollectionKind, usize) {
(kind, items) (kind, items)
} }
fn item(p: &mut Parser, keyed: bool) -> ParseResult<NodeKind> { fn item(p: &mut Parser, keyed: bool) -> ParseResult<SyntaxKind> {
let marker = p.marker(); let marker = p.marker();
if p.eat_if(NodeKind::Dots) { if p.eat_if(SyntaxKind::Dots) {
marker.perform(p, NodeKind::Spread, expr)?; marker.perform(p, SyntaxKind::Spread, expr)?;
return Ok(NodeKind::Spread); return Ok(SyntaxKind::Spread);
} }
expr(p)?; expr(p)?;
if p.at(NodeKind::Colon) { if p.at(SyntaxKind::Colon) {
match marker.after(p).map(|c| c.kind()) { match marker.after(p).map(|c| c.kind()) {
Some(NodeKind::Ident(_)) => { Some(SyntaxKind::Ident(_)) => {
p.eat(); p.eat();
marker.perform(p, NodeKind::Named, expr)?; marker.perform(p, SyntaxKind::Named, expr)?;
} }
Some(NodeKind::Str(_)) if keyed => { Some(SyntaxKind::Str(_)) if keyed => {
p.eat(); p.eat();
marker.perform(p, NodeKind::Keyed, expr)?; marker.perform(p, SyntaxKind::Keyed, expr)?;
} }
kind => { kind => {
let mut msg = EcoString::from("expected identifier"); let mut msg = EcoString::from("expected identifier");
@ -769,34 +770,34 @@ fn item(p: &mut Parser, keyed: bool) -> ParseResult<NodeKind> {
msg.push_str(", found "); msg.push_str(", found ");
msg.push_str(kind.name()); msg.push_str(kind.name());
} }
let error = NodeKind::Error(ErrorPos::Full, msg); let error = SyntaxKind::Error(ErrorPos::Full, msg);
marker.end(p, error); marker.end(p, error);
p.eat(); p.eat();
marker.perform(p, NodeKind::Named, expr).ok(); marker.perform(p, SyntaxKind::Named, expr).ok();
return Err(ParseError); return Err(ParseError);
} }
} }
Ok(NodeKind::Named) Ok(SyntaxKind::Named)
} else { } else {
Ok(NodeKind::None) Ok(SyntaxKind::None)
} }
} }
fn array(p: &mut Parser, marker: Marker) { fn array(p: &mut Parser, marker: Marker) {
marker.filter_children(p, |x| match x.kind() { marker.filter_children(p, |x| match x.kind() {
NodeKind::Named | NodeKind::Keyed => Err("expected expression"), SyntaxKind::Named | SyntaxKind::Keyed => Err("expected expression"),
_ => Ok(()), _ => Ok(()),
}); });
marker.end(p, NodeKind::Array); marker.end(p, SyntaxKind::Array);
} }
fn dict(p: &mut Parser, marker: Marker) { fn dict(p: &mut Parser, marker: Marker) {
let mut used = HashSet::new(); let mut used = HashSet::new();
marker.filter_children(p, |x| match x.kind() { marker.filter_children(p, |x| match x.kind() {
kind if kind.is_paren() => Ok(()), kind if kind.is_paren() => Ok(()),
NodeKind::Named | NodeKind::Keyed => { SyntaxKind::Named | SyntaxKind::Keyed => {
if let Some(NodeKind::Ident(key) | NodeKind::Str(key)) = if let Some(SyntaxKind::Ident(key) | SyntaxKind::Str(key)) =
x.children().next().map(|child| child.kind()) x.children().next().map(|child| child.kind())
{ {
if !used.insert(key.clone()) { if !used.insert(key.clone()) {
@ -805,32 +806,32 @@ fn dict(p: &mut Parser, marker: Marker) {
} }
Ok(()) Ok(())
} }
NodeKind::Spread | NodeKind::Comma | NodeKind::Colon => Ok(()), SyntaxKind::Spread | SyntaxKind::Comma | SyntaxKind::Colon => Ok(()),
_ => Err("expected named or keyed pair"), _ => Err("expected named or keyed pair"),
}); });
marker.end(p, NodeKind::Dict); marker.end(p, SyntaxKind::Dict);
} }
fn params(p: &mut Parser, marker: Marker) { fn params(p: &mut Parser, marker: Marker) {
marker.filter_children(p, |x| match x.kind() { marker.filter_children(p, |x| match x.kind() {
kind if kind.is_paren() => Ok(()), kind if kind.is_paren() => Ok(()),
NodeKind::Named | NodeKind::Ident(_) | NodeKind::Comma => Ok(()), SyntaxKind::Named | SyntaxKind::Ident(_) | SyntaxKind::Comma => Ok(()),
NodeKind::Spread SyntaxKind::Spread
if matches!( if matches!(
x.children().last().map(|child| child.kind()), x.children().last().map(|child| child.kind()),
Some(&NodeKind::Ident(_)) Some(&SyntaxKind::Ident(_))
) => ) =>
{ {
Ok(()) Ok(())
} }
_ => Err("expected identifier, named pair or argument sink"), _ => Err("expected identifier, named pair or argument sink"),
}); });
marker.end(p, NodeKind::Params); marker.end(p, SyntaxKind::Params);
} }
/// Parse a code block: `{...}`. /// Parse a code block: `{...}`.
fn code_block(p: &mut Parser) { fn code_block(p: &mut Parser) {
p.perform(NodeKind::CodeBlock, |p| { p.perform(SyntaxKind::CodeBlock, |p| {
p.start_group(Group::Brace); p.start_group(Group::Brace);
code(p); code(p);
p.end_group(); p.end_group();
@ -846,12 +847,12 @@ fn code(p: &mut Parser) {
p.end_group(); p.end_group();
// Forcefully skip over newlines since the group's contents can't. // Forcefully skip over newlines since the group's contents can't.
p.eat_while(NodeKind::is_space); p.eat_while(SyntaxKind::is_space);
} }
} }
fn content_block(p: &mut Parser) { fn content_block(p: &mut Parser) {
p.perform(NodeKind::ContentBlock, |p| { p.perform(SyntaxKind::ContentBlock, |p| {
p.start_group(Group::Bracket); p.start_group(Group::Bracket);
markup(p, true); markup(p, true);
p.end_group(); p.end_group();
@ -860,16 +861,16 @@ fn content_block(p: &mut Parser) {
fn args(p: &mut Parser) -> ParseResult { fn args(p: &mut Parser) -> ParseResult {
match p.peek_direct() { match p.peek_direct() {
Some(NodeKind::LeftParen) => {} Some(SyntaxKind::LeftParen) => {}
Some(NodeKind::LeftBracket) => {} Some(SyntaxKind::LeftBracket) => {}
_ => { _ => {
p.expected_found("argument list"); p.expected_found("argument list");
return Err(ParseError); return Err(ParseError);
} }
} }
p.perform(NodeKind::Args, |p| { p.perform(SyntaxKind::Args, |p| {
if p.at(NodeKind::LeftParen) { if p.at(SyntaxKind::LeftParen) {
let marker = p.marker(); let marker = p.marker();
p.start_group(Group::Paren); p.start_group(Group::Paren);
collection(p, false); collection(p, false);
@ -877,8 +878,8 @@ fn args(p: &mut Parser) -> ParseResult {
let mut used = HashSet::new(); let mut used = HashSet::new();
marker.filter_children(p, |x| match x.kind() { marker.filter_children(p, |x| match x.kind() {
NodeKind::Named => { SyntaxKind::Named => {
if let Some(NodeKind::Ident(ident)) = if let Some(SyntaxKind::Ident(ident)) =
x.children().next().map(|child| child.kind()) x.children().next().map(|child| child.kind())
{ {
if !used.insert(ident.clone()) { if !used.insert(ident.clone()) {
@ -891,7 +892,7 @@ fn args(p: &mut Parser) -> ParseResult {
}); });
} }
while p.peek_direct() == Some(&NodeKind::LeftBracket) { while p.peek_direct() == Some(&SyntaxKind::LeftBracket) {
content_block(p); content_block(p);
} }
}); });
@ -900,14 +901,14 @@ fn args(p: &mut Parser) -> ParseResult {
} }
fn let_binding(p: &mut Parser) -> ParseResult { fn let_binding(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::LetBinding, |p| { p.perform(SyntaxKind::LetBinding, |p| {
p.assert(NodeKind::Let); p.assert(SyntaxKind::Let);
let marker = p.marker(); let marker = p.marker();
ident(p)?; ident(p)?;
// If a parenthesis follows, this is a function definition. // If a parenthesis follows, this is a function definition.
let has_params = p.peek_direct() == Some(&NodeKind::LeftParen); let has_params = p.peek_direct() == Some(&SyntaxKind::LeftParen);
if has_params { if has_params {
let marker = p.marker(); let marker = p.marker();
p.start_group(Group::Paren); p.start_group(Group::Paren);
@ -916,7 +917,7 @@ fn let_binding(p: &mut Parser) -> ParseResult {
params(p, marker); params(p, marker);
} }
if p.eat_if(NodeKind::Eq) { if p.eat_if(SyntaxKind::Eq) {
expr(p)?; expr(p)?;
} else if has_params { } else if has_params {
// Function definitions must have a body. // Function definitions must have a body.
@ -925,7 +926,7 @@ fn let_binding(p: &mut Parser) -> ParseResult {
// Rewrite into a closure expression if it's a function definition. // Rewrite into a closure expression if it's a function definition.
if has_params { if has_params {
marker.end(p, NodeKind::Closure); marker.end(p, SyntaxKind::Closure);
} }
Ok(()) Ok(())
@ -933,11 +934,11 @@ fn let_binding(p: &mut Parser) -> ParseResult {
} }
fn set_rule(p: &mut Parser) -> ParseResult { fn set_rule(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::SetRule, |p| { p.perform(SyntaxKind::SetRule, |p| {
p.assert(NodeKind::Set); p.assert(SyntaxKind::Set);
ident(p)?; ident(p)?;
args(p)?; args(p)?;
if p.eat_if(NodeKind::If) { if p.eat_if(SyntaxKind::If) {
expr(p)?; expr(p)?;
} }
Ok(()) Ok(())
@ -945,10 +946,10 @@ fn set_rule(p: &mut Parser) -> ParseResult {
} }
fn show_rule(p: &mut Parser) -> ParseResult { fn show_rule(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::ShowRule, |p| { p.perform(SyntaxKind::ShowRule, |p| {
p.assert(NodeKind::Show); p.assert(SyntaxKind::Show);
expr(p)?; expr(p)?;
if p.eat_if(NodeKind::Colon) { if p.eat_if(SyntaxKind::Colon) {
expr(p)?; expr(p)?;
} }
Ok(()) Ok(())
@ -956,14 +957,14 @@ fn show_rule(p: &mut Parser) -> ParseResult {
} }
fn conditional(p: &mut Parser) -> ParseResult { fn conditional(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::Conditional, |p| { p.perform(SyntaxKind::Conditional, |p| {
p.assert(NodeKind::If); p.assert(SyntaxKind::If);
expr(p)?; expr(p)?;
body(p)?; body(p)?;
if p.eat_if(NodeKind::Else) { if p.eat_if(SyntaxKind::Else) {
if p.at(NodeKind::If) { if p.at(SyntaxKind::If) {
conditional(p)?; conditional(p)?;
} else { } else {
body(p)?; body(p)?;
@ -975,27 +976,27 @@ fn conditional(p: &mut Parser) -> ParseResult {
} }
fn while_loop(p: &mut Parser) -> ParseResult { fn while_loop(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::WhileLoop, |p| { p.perform(SyntaxKind::WhileLoop, |p| {
p.assert(NodeKind::While); p.assert(SyntaxKind::While);
expr(p)?; expr(p)?;
body(p) body(p)
}) })
} }
fn for_loop(p: &mut Parser) -> ParseResult { fn for_loop(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::ForLoop, |p| { p.perform(SyntaxKind::ForLoop, |p| {
p.assert(NodeKind::For); p.assert(SyntaxKind::For);
for_pattern(p)?; for_pattern(p)?;
p.expect(NodeKind::In)?; p.expect(SyntaxKind::In)?;
expr(p)?; expr(p)?;
body(p) body(p)
}) })
} }
fn for_pattern(p: &mut Parser) -> ParseResult { fn for_pattern(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::ForPattern, |p| { p.perform(SyntaxKind::ForPattern, |p| {
ident(p)?; ident(p)?;
if p.eat_if(NodeKind::Comma) { if p.eat_if(SyntaxKind::Comma) {
ident(p)?; ident(p)?;
} }
Ok(()) Ok(())
@ -1003,12 +1004,12 @@ fn for_pattern(p: &mut Parser) -> ParseResult {
} }
fn module_import(p: &mut Parser) -> ParseResult { fn module_import(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::ModuleImport, |p| { p.perform(SyntaxKind::ModuleImport, |p| {
p.assert(NodeKind::Import); p.assert(SyntaxKind::Import);
if !p.eat_if(NodeKind::Star) { if !p.eat_if(SyntaxKind::Star) {
// This is the list of identifiers scenario. // This is the list of identifiers scenario.
p.perform(NodeKind::ImportItems, |p| { p.perform(SyntaxKind::ImportItems, |p| {
p.start_group(Group::Imports); p.start_group(Group::Imports);
let marker = p.marker(); let marker = p.marker();
let items = collection(p, false).1; let items = collection(p, false).1;
@ -1018,42 +1019,42 @@ fn module_import(p: &mut Parser) -> ParseResult {
p.end_group(); p.end_group();
marker.filter_children(p, |n| match n.kind() { marker.filter_children(p, |n| match n.kind() {
NodeKind::Ident(_) | NodeKind::Comma => Ok(()), SyntaxKind::Ident(_) | SyntaxKind::Comma => Ok(()),
_ => Err("expected identifier"), _ => Err("expected identifier"),
}); });
}); });
}; };
p.expect(NodeKind::From)?; p.expect(SyntaxKind::From)?;
expr(p) expr(p)
}) })
} }
fn module_include(p: &mut Parser) -> ParseResult { fn module_include(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::ModuleInclude, |p| { p.perform(SyntaxKind::ModuleInclude, |p| {
p.assert(NodeKind::Include); p.assert(SyntaxKind::Include);
expr(p) expr(p)
}) })
} }
fn break_stmt(p: &mut Parser) -> ParseResult { fn break_stmt(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::LoopBreak, |p| { p.perform(SyntaxKind::LoopBreak, |p| {
p.assert(NodeKind::Break); p.assert(SyntaxKind::Break);
Ok(()) Ok(())
}) })
} }
fn continue_stmt(p: &mut Parser) -> ParseResult { fn continue_stmt(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::LoopContinue, |p| { p.perform(SyntaxKind::LoopContinue, |p| {
p.assert(NodeKind::Continue); p.assert(SyntaxKind::Continue);
Ok(()) Ok(())
}) })
} }
fn return_stmt(p: &mut Parser) -> ParseResult { fn return_stmt(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::FuncReturn, |p| { p.perform(SyntaxKind::FuncReturn, |p| {
p.assert(NodeKind::Return); p.assert(SyntaxKind::Return);
if !p.at(NodeKind::Comma) && !p.eof() { if !p.at(SyntaxKind::Comma) && !p.eof() {
expr(p)?; expr(p)?;
} }
Ok(()) Ok(())
@ -1062,8 +1063,8 @@ fn return_stmt(p: &mut Parser) -> ParseResult {
fn body(p: &mut Parser) -> ParseResult { fn body(p: &mut Parser) -> ParseResult {
match p.peek() { match p.peek() {
Some(NodeKind::LeftBracket) => Ok(content_block(p)), Some(SyntaxKind::LeftBracket) => Ok(content_block(p)),
Some(NodeKind::LeftBrace) => Ok(code_block(p)), Some(SyntaxKind::LeftBrace) => Ok(code_block(p)),
_ => { _ => {
p.expected("body"); p.expected("body");
Err(ParseError) Err(ParseError)

View File

@ -1,9 +1,79 @@
use std::fmt::{self, Debug, Display, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::num::NonZeroU64; use std::num::NonZeroU64;
use std::ops::Range; use std::ops::Range;
use super::SourceId; use super::SourceId;
/// A unique identifier for a syntax node.
///
/// This is used throughout the compiler to track which source section an error
/// or element stems from. Can be [mapped back](super::Source::range) to a byte
/// range for user facing display.
///
/// During editing, the span values stay mostly stable, even for nodes behind an
/// insertion. This is not true for simple ranges as they would shift. Spans can
/// be used as inputs to memoized functions without hurting cache performance
/// when text is inserted somewhere in the document other than the end.
///
/// Span ids are ordered in the syntax tree to enable quickly finding the node
/// with some id:
/// - The id of a parent is always smaller than the ids of any of its children.
/// - The id of a node is always greater than any id in the subtrees of any left
/// sibling and smaller than any id in the subtrees of any right sibling.
///
/// This type takes up 8 bytes and is null-optimized (i.e. `Option<Span>` also
/// takes 8 bytes).
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Span(NonZeroU64);
impl Span {
// Data layout:
// | 16 bits source id | 48 bits number |
// Number of bits for and minimum and maximum numbers assignable to spans.
const BITS: usize = 48;
const DETACHED: u64 = 1;
/// The full range of numbers available for span numbering.
pub const FULL: Range<u64> = 2..(1 << Self::BITS);
/// Create a new span from a source id and a unique number.
///
/// Panics if the `number` is not contained in `FULL`.
pub const fn new(id: SourceId, number: u64) -> Self {
assert!(
Self::FULL.start <= number && number < Self::FULL.end,
"span number outside valid range"
);
let bits = ((id.into_u16() as u64) << Self::BITS) | number;
Self(to_non_zero(bits))
}
/// A span that does not point into any source file.
pub const fn detached() -> Self {
Self(to_non_zero(Self::DETACHED))
}
/// The id of the source file the span points into.
pub const fn source(self) -> SourceId {
SourceId::from_u16((self.0.get() >> Self::BITS) as u16)
}
/// The unique number of the span within its source file.
pub const fn number(self) -> u64 {
self.0.get() & ((1 << Self::BITS) - 1)
}
}
/// Convert to a non zero u64.
const fn to_non_zero(v: u64) -> NonZeroU64 {
match NonZeroU64::new(v) {
Some(v) => v,
None => panic!("span encoding is zero"),
}
}
/// A value with a span locating it in the source code. /// A value with a span locating it in the source code.
#[derive(Copy, Clone, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Spanned<T> { pub struct Spanned<T> {
@ -39,91 +109,6 @@ impl<T: Debug> Debug for Spanned<T> {
} }
} }
/// A unique identifier for a syntax node.
///
/// This is used throughout the compiler to track which source section an error
/// or element stems from. Can be [mapped back](super::Source::range) to a byte
/// range for user facing display.
///
/// Span ids are ordered in the tree to enable quickly finding the node with
/// some id:
/// - The id of a parent is always smaller than the ids of any of its children.
/// - The id of a node is always greater than any id in the subtrees of any left
/// sibling and smaller than any id in the subtrees of any right sibling.
///
/// The internal ids of spans stay mostly stable, even for nodes behind an
/// insertion. This is not true for simple ranges as they would shift. Spans can
/// be used as inputs to memoized functions without hurting cache performance
/// when text is inserted somewhere in the document other than the end.
///
/// This type takes up 8 bytes and is null-optimized (i.e. `Option<Span>` also
/// takes 8 bytes).
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Span(NonZeroU64);
impl Span {
// Data layout:
// | 16 bits source id | 48 bits number |
// Number of bits for and minimum and maximum numbers assignable to spans.
const BITS: usize = 48;
const DETACHED: u64 = 1;
/// The full range of numbers available to spans.
pub const FULL: Range<u64> = 2..(1 << Self::BITS);
/// Create a new span from a source id and a unique number.
///
/// Panics if the `number` is not contained in `FULL`.
pub const fn new(id: SourceId, number: u64) -> Self {
assert!(
Self::FULL.start <= number && number < Self::FULL.end,
"span number outside valid range"
);
let bits = ((id.into_u16() as u64) << Self::BITS) | number;
Self(to_non_zero(bits))
}
/// A span that does not point into any source file.
pub const fn detached() -> Self {
Self(to_non_zero(Self::DETACHED))
}
/// The id of the source file the span points into.
pub const fn source(self) -> SourceId {
SourceId::from_u16((self.0.get() >> Self::BITS) as u16)
}
/// The unique number of the span within the source file.
pub const fn number(self) -> u64 {
self.0.get() & ((1 << Self::BITS) - 1)
}
}
/// Convert to a non zero u64.
const fn to_non_zero(v: u64) -> NonZeroU64 {
match NonZeroU64::new(v) {
Some(v) => v,
None => panic!("span encoding is zero"),
}
}
/// Result of numbering a node within an interval.
pub(super) type NumberingResult = Result<(), Unnumberable>;
/// Indicates that a node cannot be numbered within a given interval.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(super) struct Unnumberable;
impl Display for Unnumberable {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("cannot number within this interval")
}
}
impl std::error::Error for Unnumberable {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{SourceId, Span}; use super::{SourceId, Span};

View File

@ -4,7 +4,7 @@ use unicode_xid::UnicodeXID;
use unscanny::Scanner; use unscanny::Scanner;
use super::resolve::{resolve_hex, resolve_raw, resolve_string}; use super::resolve::{resolve_hex, resolve_raw, resolve_string};
use super::{ErrorPos, NodeKind, RawFields, Unit}; use super::{ErrorPos, RawFields, SyntaxKind, Unit};
use crate::geom::{AbsUnit, AngleUnit}; use crate::geom::{AbsUnit, AngleUnit};
use crate::util::{format_eco, EcoString}; use crate::util::{format_eco, EcoString};
@ -96,7 +96,7 @@ impl<'s> Tokens<'s> {
} }
impl<'s> Iterator for Tokens<'s> { impl<'s> Iterator for Tokens<'s> {
type Item = NodeKind; type Item = SyntaxKind;
/// Parse the next token in the source code. /// Parse the next token in the source code.
#[inline] #[inline]
@ -107,9 +107,10 @@ impl<'s> Iterator for Tokens<'s> {
// Trivia. // Trivia.
'/' if self.s.eat_if('/') => self.line_comment(), '/' if self.s.eat_if('/') => self.line_comment(),
'/' if self.s.eat_if('*') => self.block_comment(), '/' if self.s.eat_if('*') => self.block_comment(),
'*' if self.s.eat_if('/') => { '*' if self.s.eat_if('/') => SyntaxKind::Error(
NodeKind::Error(ErrorPos::Full, "unexpected end of block comment".into()) ErrorPos::Full,
} "unexpected end of block comment".into(),
),
c if c.is_whitespace() => self.whitespace(c), c if c.is_whitespace() => self.whitespace(c),
// Other things. // Other things.
@ -123,15 +124,15 @@ impl<'s> Iterator for Tokens<'s> {
} }
impl<'s> Tokens<'s> { impl<'s> Tokens<'s> {
fn line_comment(&mut self) -> NodeKind { fn line_comment(&mut self) -> SyntaxKind {
self.s.eat_until(is_newline); self.s.eat_until(is_newline);
if self.s.peek().is_none() { if self.s.peek().is_none() {
self.terminated = false; self.terminated = false;
} }
NodeKind::LineComment SyntaxKind::LineComment
} }
fn block_comment(&mut self) -> NodeKind { fn block_comment(&mut self) -> SyntaxKind {
let mut state = '_'; let mut state = '_';
let mut depth = 1; let mut depth = 1;
self.terminated = false; self.terminated = false;
@ -159,12 +160,12 @@ impl<'s> Tokens<'s> {
} }
} }
NodeKind::BlockComment SyntaxKind::BlockComment
} }
fn whitespace(&mut self, c: char) -> NodeKind { fn whitespace(&mut self, c: char) -> SyntaxKind {
if c == ' ' && !self.s.at(char::is_whitespace) { if c == ' ' && !self.s.at(char::is_whitespace) {
return NodeKind::Space { newlines: 0 }; return SyntaxKind::Space { newlines: 0 };
} }
self.s.uneat(); self.s.uneat();
@ -185,21 +186,21 @@ impl<'s> Tokens<'s> {
} }
} }
NodeKind::Space { newlines } SyntaxKind::Space { newlines }
} }
#[inline] #[inline]
fn markup(&mut self, start: usize, c: char) -> NodeKind { fn markup(&mut self, start: usize, c: char) -> SyntaxKind {
match c { match c {
// Blocks. // Blocks.
'{' => NodeKind::LeftBrace, '{' => SyntaxKind::LeftBrace,
'}' => NodeKind::RightBrace, '}' => SyntaxKind::RightBrace,
'[' => NodeKind::LeftBracket, '[' => SyntaxKind::LeftBracket,
']' => NodeKind::RightBracket, ']' => SyntaxKind::RightBracket,
// Multi-char things. // Multi-char things.
'#' => self.hash(start), '#' => self.hash(start),
'.' if self.s.eat_if("..") => NodeKind::Shorthand('\u{2026}'), '.' if self.s.eat_if("..") => SyntaxKind::Shorthand('\u{2026}'),
'-' => self.hyph(), '-' => self.hyph(),
'h' if self.s.eat_if("ttp://") || self.s.eat_if("ttps://") => { 'h' if self.s.eat_if("ttp://") || self.s.eat_if("ttps://") => {
self.link(start) self.link(start)
@ -213,16 +214,16 @@ impl<'s> Tokens<'s> {
'\\' => self.backslash(), '\\' => self.backslash(),
// Single-char things. // Single-char things.
'~' => NodeKind::Shorthand('\u{00A0}'), '~' => SyntaxKind::Shorthand('\u{00A0}'),
'\'' => NodeKind::SmartQuote { double: false }, '\'' => SyntaxKind::SmartQuote { double: false },
'"' => NodeKind::SmartQuote { double: true }, '"' => SyntaxKind::SmartQuote { double: true },
'*' if !self.in_word() => NodeKind::Star, '*' if !self.in_word() => SyntaxKind::Star,
'_' if !self.in_word() => NodeKind::Underscore, '_' if !self.in_word() => SyntaxKind::Underscore,
'$' => NodeKind::Dollar, '$' => SyntaxKind::Dollar,
'=' => NodeKind::Eq, '=' => SyntaxKind::Eq,
'+' => NodeKind::Plus, '+' => SyntaxKind::Plus,
'/' => NodeKind::Slash, '/' => SyntaxKind::Slash,
':' => NodeKind::Colon, ':' => SyntaxKind::Colon,
// Plain text. // Plain text.
_ => self.text(start), _ => self.text(start),
@ -230,7 +231,7 @@ impl<'s> Tokens<'s> {
} }
#[inline] #[inline]
fn text(&mut self, start: usize) -> NodeKind { fn text(&mut self, start: usize) -> SyntaxKind {
macro_rules! table { macro_rules! table {
($(|$c:literal)*) => {{ ($(|$c:literal)*) => {{
let mut t = [false; 128]; let mut t = [false; 128];
@ -266,67 +267,67 @@ impl<'s> Tokens<'s> {
self.s = s; self.s = s;
} }
NodeKind::Text(self.s.from(start).into()) SyntaxKind::Text(self.s.from(start).into())
} }
fn backslash(&mut self) -> NodeKind { fn backslash(&mut self) -> SyntaxKind {
match self.s.peek() { match self.s.peek() {
Some('u') if self.s.eat_if("u{") => { Some('u') if self.s.eat_if("u{") => {
let sequence = self.s.eat_while(char::is_ascii_alphanumeric); let sequence = self.s.eat_while(char::is_ascii_alphanumeric);
if self.s.eat_if('}') { if self.s.eat_if('}') {
if let Some(c) = resolve_hex(sequence) { if let Some(c) = resolve_hex(sequence) {
NodeKind::Escape(c) SyntaxKind::Escape(c)
} else { } else {
NodeKind::Error( SyntaxKind::Error(
ErrorPos::Full, ErrorPos::Full,
"invalid unicode escape sequence".into(), "invalid unicode escape sequence".into(),
) )
} }
} else { } else {
self.terminated = false; self.terminated = false;
NodeKind::Error(ErrorPos::End, "expected closing brace".into()) SyntaxKind::Error(ErrorPos::End, "expected closing brace".into())
} }
} }
// Linebreaks. // Linebreaks.
Some(c) if c.is_whitespace() => NodeKind::Linebreak, Some(c) if c.is_whitespace() => SyntaxKind::Linebreak,
None => NodeKind::Linebreak, None => SyntaxKind::Linebreak,
// Escapes. // Escapes.
Some(c) => { Some(c) => {
self.s.expect(c); self.s.expect(c);
NodeKind::Escape(c) SyntaxKind::Escape(c)
} }
} }
} }
fn hash(&mut self, start: usize) -> NodeKind { fn hash(&mut self, start: usize) -> SyntaxKind {
if self.s.at(is_id_start) { if self.s.at(is_id_start) {
let read = self.s.eat_while(is_id_continue); let read = self.s.eat_while(is_id_continue);
match keyword(read) { match keyword(read) {
Some(keyword) => keyword, Some(keyword) => keyword,
None => NodeKind::Ident(read.into()), None => SyntaxKind::Ident(read.into()),
} }
} else { } else {
self.text(start) self.text(start)
} }
} }
fn hyph(&mut self) -> NodeKind { fn hyph(&mut self) -> SyntaxKind {
if self.s.eat_if('-') { if self.s.eat_if('-') {
if self.s.eat_if('-') { if self.s.eat_if('-') {
NodeKind::Shorthand('\u{2014}') SyntaxKind::Shorthand('\u{2014}')
} else { } else {
NodeKind::Shorthand('\u{2013}') SyntaxKind::Shorthand('\u{2013}')
} }
} else if self.s.eat_if('?') { } else if self.s.eat_if('?') {
NodeKind::Shorthand('\u{00AD}') SyntaxKind::Shorthand('\u{00AD}')
} else { } else {
NodeKind::Minus SyntaxKind::Minus
} }
} }
fn link(&mut self, start: usize) -> NodeKind { fn link(&mut self, start: usize) -> SyntaxKind {
#[rustfmt::skip] #[rustfmt::skip]
self.s.eat_while(|c: char| matches!(c, self.s.eat_while(|c: char| matches!(c,
| '0' ..= '9' | '0' ..= '9'
@ -338,10 +339,10 @@ impl<'s> Tokens<'s> {
if self.s.scout(-1) == Some('.') { if self.s.scout(-1) == Some('.') {
self.s.uneat(); self.s.uneat();
} }
NodeKind::Link(self.s.from(start).into()) SyntaxKind::Link(self.s.from(start).into())
} }
fn raw(&mut self) -> NodeKind { fn raw(&mut self) -> SyntaxKind {
let column = self.column(self.s.cursor() - 1); let column = self.column(self.s.cursor() - 1);
let mut backticks = 1; let mut backticks = 1;
@ -351,7 +352,7 @@ impl<'s> Tokens<'s> {
// Special case for empty inline block. // Special case for empty inline block.
if backticks == 2 { if backticks == 2 {
return NodeKind::Raw(Arc::new(RawFields { return SyntaxKind::Raw(Arc::new(RawFields {
text: EcoString::new(), text: EcoString::new(),
lang: None, lang: None,
block: false, block: false,
@ -370,7 +371,7 @@ impl<'s> Tokens<'s> {
if found == backticks { if found == backticks {
let end = self.s.cursor() - found as usize; let end = self.s.cursor() - found as usize;
NodeKind::Raw(Arc::new(resolve_raw( SyntaxKind::Raw(Arc::new(resolve_raw(
column, column,
backticks, backticks,
self.s.get(start..end), self.s.get(start..end),
@ -379,7 +380,7 @@ impl<'s> Tokens<'s> {
self.terminated = false; self.terminated = false;
let remaining = backticks - found; let remaining = backticks - found;
let noun = if remaining == 1 { "backtick" } else { "backticks" }; let noun = if remaining == 1 { "backtick" } else { "backticks" };
NodeKind::Error( SyntaxKind::Error(
ErrorPos::End, ErrorPos::End,
if found == 0 { if found == 0 {
format_eco!("expected {} {}", remaining, noun) format_eco!("expected {} {}", remaining, noun)
@ -390,114 +391,114 @@ impl<'s> Tokens<'s> {
} }
} }
fn numbering(&mut self, start: usize) -> NodeKind { fn numbering(&mut self, start: usize) -> SyntaxKind {
self.s.eat_while(char::is_ascii_digit); self.s.eat_while(char::is_ascii_digit);
let read = self.s.from(start); let read = self.s.from(start);
if self.s.eat_if('.') { if self.s.eat_if('.') {
if let Ok(number) = read.parse() { if let Ok(number) = read.parse() {
return NodeKind::EnumNumbering(number); return SyntaxKind::EnumNumbering(number);
} }
} }
self.text(start) self.text(start)
} }
fn label(&mut self) -> NodeKind { fn label(&mut self) -> SyntaxKind {
let label = self.s.eat_while(is_id_continue); let label = self.s.eat_while(is_id_continue);
if self.s.eat_if('>') { if self.s.eat_if('>') {
if !label.is_empty() { if !label.is_empty() {
NodeKind::Label(label.into()) SyntaxKind::Label(label.into())
} else { } else {
NodeKind::Error(ErrorPos::Full, "label cannot be empty".into()) SyntaxKind::Error(ErrorPos::Full, "label cannot be empty".into())
} }
} else { } else {
self.terminated = false; self.terminated = false;
NodeKind::Error(ErrorPos::End, "expected closing angle bracket".into()) SyntaxKind::Error(ErrorPos::End, "expected closing angle bracket".into())
} }
} }
fn reference(&mut self, start: usize) -> NodeKind { fn reference(&mut self, start: usize) -> SyntaxKind {
let label = self.s.eat_while(is_id_continue); let label = self.s.eat_while(is_id_continue);
if !label.is_empty() { if !label.is_empty() {
NodeKind::Ref(label.into()) SyntaxKind::Ref(label.into())
} else { } else {
self.text(start) self.text(start)
} }
} }
fn math(&mut self, start: usize, c: char) -> NodeKind { fn math(&mut self, start: usize, c: char) -> SyntaxKind {
match c { match c {
// Escape sequences. // Escape sequences.
'\\' => self.backslash(), '\\' => self.backslash(),
// Single-char things. // Single-char things.
'_' => NodeKind::Underscore, '_' => SyntaxKind::Underscore,
'^' => NodeKind::Hat, '^' => SyntaxKind::Hat,
'/' => NodeKind::Slash, '/' => SyntaxKind::Slash,
'&' => NodeKind::Amp, '&' => SyntaxKind::Amp,
'$' => NodeKind::Dollar, '$' => SyntaxKind::Dollar,
// Brackets. // Brackets.
'{' => NodeKind::LeftBrace, '{' => SyntaxKind::LeftBrace,
'}' => NodeKind::RightBrace, '}' => SyntaxKind::RightBrace,
'[' => NodeKind::LeftBracket, '[' => SyntaxKind::LeftBracket,
']' => NodeKind::RightBracket, ']' => SyntaxKind::RightBracket,
'(' => NodeKind::LeftParen, '(' => SyntaxKind::LeftParen,
')' => NodeKind::RightParen, ')' => SyntaxKind::RightParen,
// Identifiers. // Identifiers.
c if is_math_id_start(c) && self.s.at(is_math_id_continue) => { c if is_math_id_start(c) && self.s.at(is_math_id_continue) => {
self.s.eat_while(is_math_id_continue); self.s.eat_while(is_math_id_continue);
NodeKind::Ident(self.s.from(start).into()) SyntaxKind::Ident(self.s.from(start).into())
} }
// Numbers. // Numbers.
c if c.is_numeric() => { c if c.is_numeric() => {
self.s.eat_while(char::is_numeric); self.s.eat_while(char::is_numeric);
NodeKind::Atom(self.s.from(start).into()) SyntaxKind::Atom(self.s.from(start).into())
} }
// Other math atoms. // Other math atoms.
c => NodeKind::Atom(c.into()), c => SyntaxKind::Atom(c.into()),
} }
} }
fn code(&mut self, start: usize, c: char) -> NodeKind { fn code(&mut self, start: usize, c: char) -> SyntaxKind {
match c { match c {
// Blocks. // Blocks.
'{' => NodeKind::LeftBrace, '{' => SyntaxKind::LeftBrace,
'}' => NodeKind::RightBrace, '}' => SyntaxKind::RightBrace,
'[' => NodeKind::LeftBracket, '[' => SyntaxKind::LeftBracket,
']' => NodeKind::RightBracket, ']' => SyntaxKind::RightBracket,
// Parentheses. // Parentheses.
'(' => NodeKind::LeftParen, '(' => SyntaxKind::LeftParen,
')' => NodeKind::RightParen, ')' => SyntaxKind::RightParen,
// Two-char operators. // Two-char operators.
'=' if self.s.eat_if('=') => NodeKind::EqEq, '=' if self.s.eat_if('=') => SyntaxKind::EqEq,
'!' if self.s.eat_if('=') => NodeKind::ExclEq, '!' if self.s.eat_if('=') => SyntaxKind::ExclEq,
'<' if self.s.eat_if('=') => NodeKind::LtEq, '<' if self.s.eat_if('=') => SyntaxKind::LtEq,
'>' if self.s.eat_if('=') => NodeKind::GtEq, '>' if self.s.eat_if('=') => SyntaxKind::GtEq,
'+' if self.s.eat_if('=') => NodeKind::PlusEq, '+' if self.s.eat_if('=') => SyntaxKind::PlusEq,
'-' if self.s.eat_if('=') => NodeKind::HyphEq, '-' if self.s.eat_if('=') => SyntaxKind::HyphEq,
'*' if self.s.eat_if('=') => NodeKind::StarEq, '*' if self.s.eat_if('=') => SyntaxKind::StarEq,
'/' if self.s.eat_if('=') => NodeKind::SlashEq, '/' if self.s.eat_if('=') => SyntaxKind::SlashEq,
'.' if self.s.eat_if('.') => NodeKind::Dots, '.' if self.s.eat_if('.') => SyntaxKind::Dots,
'=' if self.s.eat_if('>') => NodeKind::Arrow, '=' if self.s.eat_if('>') => SyntaxKind::Arrow,
// Single-char operators. // Single-char operators.
',' => NodeKind::Comma, ',' => SyntaxKind::Comma,
';' => NodeKind::Semicolon, ';' => SyntaxKind::Semicolon,
':' => NodeKind::Colon, ':' => SyntaxKind::Colon,
'+' => NodeKind::Plus, '+' => SyntaxKind::Plus,
'-' => NodeKind::Minus, '-' => SyntaxKind::Minus,
'*' => NodeKind::Star, '*' => SyntaxKind::Star,
'/' => NodeKind::Slash, '/' => SyntaxKind::Slash,
'=' => NodeKind::Eq, '=' => SyntaxKind::Eq,
'<' => NodeKind::Lt, '<' => SyntaxKind::Lt,
'>' => NodeKind::Gt, '>' => SyntaxKind::Gt,
'.' if !self.s.at(char::is_ascii_digit) => NodeKind::Dot, '.' if !self.s.at(char::is_ascii_digit) => SyntaxKind::Dot,
// Identifiers. // Identifiers.
c if is_id_start(c) => self.ident(start), c if is_id_start(c) => self.ident(start),
@ -511,22 +512,22 @@ impl<'s> Tokens<'s> {
'"' => self.string(), '"' => self.string(),
// Invalid token. // Invalid token.
_ => NodeKind::Error(ErrorPos::Full, "not valid here".into()), _ => SyntaxKind::Error(ErrorPos::Full, "not valid here".into()),
} }
} }
fn ident(&mut self, start: usize) -> NodeKind { fn ident(&mut self, start: usize) -> SyntaxKind {
self.s.eat_while(is_id_continue); self.s.eat_while(is_id_continue);
match self.s.from(start) { match self.s.from(start) {
"none" => NodeKind::None, "none" => SyntaxKind::None,
"auto" => NodeKind::Auto, "auto" => SyntaxKind::Auto,
"true" => NodeKind::Bool(true), "true" => SyntaxKind::Bool(true),
"false" => NodeKind::Bool(false), "false" => SyntaxKind::Bool(false),
id => keyword(id).unwrap_or_else(|| NodeKind::Ident(id.into())), id => keyword(id).unwrap_or_else(|| SyntaxKind::Ident(id.into())),
} }
} }
fn number(&mut self, start: usize, c: char) -> NodeKind { fn number(&mut self, start: usize, c: char) -> SyntaxKind {
// Read the first part (integer or fractional depending on `first`). // Read the first part (integer or fractional depending on `first`).
self.s.eat_while(char::is_ascii_digit); self.s.eat_while(char::is_ascii_digit);
@ -554,30 +555,30 @@ impl<'s> Tokens<'s> {
// Find out whether it is a simple number. // Find out whether it is a simple number.
if suffix.is_empty() { if suffix.is_empty() {
if let Ok(i) = number.parse::<i64>() { if let Ok(i) = number.parse::<i64>() {
return NodeKind::Int(i); return SyntaxKind::Int(i);
} }
} }
let Ok(v) = number.parse::<f64>() else { let Ok(v) = number.parse::<f64>() else {
return NodeKind::Error(ErrorPos::Full, "invalid number".into()); return SyntaxKind::Error(ErrorPos::Full, "invalid number".into());
}; };
match suffix { match suffix {
"" => NodeKind::Float(v), "" => SyntaxKind::Float(v),
"pt" => NodeKind::Numeric(v, Unit::Length(AbsUnit::Pt)), "pt" => SyntaxKind::Numeric(v, Unit::Length(AbsUnit::Pt)),
"mm" => NodeKind::Numeric(v, Unit::Length(AbsUnit::Mm)), "mm" => SyntaxKind::Numeric(v, Unit::Length(AbsUnit::Mm)),
"cm" => NodeKind::Numeric(v, Unit::Length(AbsUnit::Cm)), "cm" => SyntaxKind::Numeric(v, Unit::Length(AbsUnit::Cm)),
"in" => NodeKind::Numeric(v, Unit::Length(AbsUnit::In)), "in" => SyntaxKind::Numeric(v, Unit::Length(AbsUnit::In)),
"deg" => NodeKind::Numeric(v, Unit::Angle(AngleUnit::Deg)), "deg" => SyntaxKind::Numeric(v, Unit::Angle(AngleUnit::Deg)),
"rad" => NodeKind::Numeric(v, Unit::Angle(AngleUnit::Rad)), "rad" => SyntaxKind::Numeric(v, Unit::Angle(AngleUnit::Rad)),
"em" => NodeKind::Numeric(v, Unit::Em), "em" => SyntaxKind::Numeric(v, Unit::Em),
"fr" => NodeKind::Numeric(v, Unit::Fr), "fr" => SyntaxKind::Numeric(v, Unit::Fr),
"%" => NodeKind::Numeric(v, Unit::Percent), "%" => SyntaxKind::Numeric(v, Unit::Percent),
_ => NodeKind::Error(ErrorPos::Full, "invalid number suffix".into()), _ => SyntaxKind::Error(ErrorPos::Full, "invalid number suffix".into()),
} }
} }
fn string(&mut self) -> NodeKind { fn string(&mut self) -> SyntaxKind {
let mut escaped = false; let mut escaped = false;
let verbatim = self.s.eat_until(|c| { let verbatim = self.s.eat_until(|c| {
if c == '"' && !escaped { if c == '"' && !escaped {
@ -590,10 +591,10 @@ impl<'s> Tokens<'s> {
let string = resolve_string(verbatim); let string = resolve_string(verbatim);
if self.s.eat_if('"') { if self.s.eat_if('"') {
NodeKind::Str(string) SyntaxKind::Str(string)
} else { } else {
self.terminated = false; self.terminated = false;
NodeKind::Error(ErrorPos::End, "expected quote".into()) SyntaxKind::Error(ErrorPos::End, "expected quote".into())
} }
} }
@ -605,25 +606,25 @@ impl<'s> Tokens<'s> {
} }
} }
fn keyword(ident: &str) -> Option<NodeKind> { fn keyword(ident: &str) -> Option<SyntaxKind> {
Some(match ident { Some(match ident {
"not" => NodeKind::Not, "not" => SyntaxKind::Not,
"and" => NodeKind::And, "and" => SyntaxKind::And,
"or" => NodeKind::Or, "or" => SyntaxKind::Or,
"let" => NodeKind::Let, "let" => SyntaxKind::Let,
"set" => NodeKind::Set, "set" => SyntaxKind::Set,
"show" => NodeKind::Show, "show" => SyntaxKind::Show,
"if" => NodeKind::If, "if" => SyntaxKind::If,
"else" => NodeKind::Else, "else" => SyntaxKind::Else,
"for" => NodeKind::For, "for" => SyntaxKind::For,
"in" => NodeKind::In, "in" => SyntaxKind::In,
"while" => NodeKind::While, "while" => SyntaxKind::While,
"break" => NodeKind::Break, "break" => SyntaxKind::Break,
"continue" => NodeKind::Continue, "continue" => SyntaxKind::Continue,
"return" => NodeKind::Return, "return" => SyntaxKind::Return,
"import" => NodeKind::Import, "import" => SyntaxKind::Import,
"include" => NodeKind::Include, "include" => SyntaxKind::Include,
"from" => NodeKind::From, "from" => SyntaxKind::From,
_ => return None, _ => return None,
}) })
} }
@ -715,36 +716,36 @@ mod tests {
use super::*; use super::*;
use ErrorPos::*; use ErrorPos::*;
use NodeKind::*;
use Option::None; use Option::None;
use SyntaxKind::*;
use TokenMode::{Code, Markup}; use TokenMode::{Code, Markup};
fn Space(newlines: usize) -> NodeKind { fn Space(newlines: usize) -> SyntaxKind {
NodeKind::Space { newlines } SyntaxKind::Space { newlines }
} }
fn Raw(text: &str, lang: Option<&str>, block: bool) -> NodeKind { fn Raw(text: &str, lang: Option<&str>, block: bool) -> SyntaxKind {
NodeKind::Raw(Arc::new(RawFields { SyntaxKind::Raw(Arc::new(RawFields {
text: text.into(), text: text.into(),
lang: lang.map(Into::into), lang: lang.map(Into::into),
block, block,
})) }))
} }
fn Str(string: &str) -> NodeKind { fn Str(string: &str) -> SyntaxKind {
NodeKind::Str(string.into()) SyntaxKind::Str(string.into())
} }
fn Text(string: &str) -> NodeKind { fn Text(string: &str) -> SyntaxKind {
NodeKind::Text(string.into()) SyntaxKind::Text(string.into())
} }
fn Ident(ident: &str) -> NodeKind { fn Ident(ident: &str) -> SyntaxKind {
NodeKind::Ident(ident.into()) SyntaxKind::Ident(ident.into())
} }
fn Error(pos: ErrorPos, message: &str) -> NodeKind { fn Error(pos: ErrorPos, message: &str) -> SyntaxKind {
NodeKind::Error(pos, message.into()) SyntaxKind::Error(pos, message.into())
} }
/// Building blocks for suffix testing. /// Building blocks for suffix testing.
@ -769,7 +770,7 @@ mod tests {
// - the suffix string // - the suffix string
// - the resulting suffix NodeKind // - the resulting suffix NodeKind
fn suffixes( fn suffixes(
) -> impl Iterator<Item = (char, Option<TokenMode>, &'static str, NodeKind)> { ) -> impl Iterator<Item = (char, Option<TokenMode>, &'static str, SyntaxKind)> {
[ [
// Whitespace suffixes. // Whitespace suffixes.
(' ', None, " ", Space(0)), (' ', None, " ", Space(0)),
@ -1089,7 +1090,7 @@ mod tests {
// Combined integers and floats. // Combined integers and floats.
let nums = ints.iter().map(|&(k, v)| (k, v as f64)).chain(floats); let nums = ints.iter().map(|&(k, v)| (k, v as f64)).chain(floats);
let suffixes: &[(&str, fn(f64) -> NodeKind)] = &[ let suffixes: &[(&str, fn(f64) -> SyntaxKind)] = &[
("mm", |x| Numeric(x, Unit::Length(AbsUnit::Mm))), ("mm", |x| Numeric(x, Unit::Length(AbsUnit::Mm))),
("pt", |x| Numeric(x, Unit::Length(AbsUnit::Pt))), ("pt", |x| Numeric(x, Unit::Length(AbsUnit::Pt))),
("cm", |x| Numeric(x, Unit::Length(AbsUnit::Cm))), ("cm", |x| Numeric(x, Unit::Length(AbsUnit::Cm))),