From bc1b4216a802d09e8d00dd277a0e204d49bcaa7f Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 30 Sep 2020 12:38:02 +0200 Subject: [PATCH] =?UTF-8?q?Reorganize=20syntax=20types=20into=20two=20modu?= =?UTF-8?q?les=20=F0=9F=93=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/compute/table.rs | 2 +- src/compute/value.rs | 3 +- src/diagnostic.rs | 6 +- src/layout/mod.rs | 2 +- src/layout/tree.rs | 6 +- src/lib.rs | 17 +-- src/{syntax/parsing => parse}/escaping.rs | 2 +- src/parse/mod.rs | 11 ++ src/{syntax/parsing => parse}/parser.rs | 6 +- src/{syntax/parsing => parse}/tests.rs | 44 +++++- src/{syntax/tokens.rs => parse/tokenizer.rs} | 153 +------------------ src/prelude.rs | 4 +- src/syntax/mod.rs | 57 ++----- src/syntax/parsing/mod.rs | 9 -- src/syntax/span.rs | 12 +- src/syntax/token.rs | 152 ++++++++++++++++++ src/syntax/tree.rs | 2 +- 17 files changed, 243 insertions(+), 245 deletions(-) rename src/{syntax/parsing => parse}/escaping.rs (99%) create mode 100644 src/parse/mod.rs rename src/{syntax/parsing => parse}/parser.rs (99%) rename src/{syntax/parsing => parse}/tests.rs (94%) rename src/{syntax/tokens.rs => parse/tokenizer.rs} (81%) delete mode 100644 src/syntax/parsing/mod.rs create mode 100644 src/syntax/token.rs diff --git a/src/compute/table.rs b/src/compute/table.rs index b969c8dcf..7abb565be 100644 --- a/src/compute/table.rs +++ b/src/compute/table.rs @@ -4,7 +4,7 @@ use std::collections::BTreeMap; use std::fmt::{self, Debug, Display, Formatter}; use std::ops::Index; -use crate::syntax::span::{Span, Spanned}; +use crate::syntax::{Span, Spanned}; /// A table data structure, which maps from integers (`u64`) or strings to a /// generic value type. diff --git a/src/compute/value.rs b/src/compute/value.rs index 975c1f463..95219a5c6 100644 --- a/src/compute/value.rs +++ b/src/compute/value.rs @@ -11,8 +11,7 @@ use crate::color::RgbaColor; use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign}; use crate::length::{Length, ScaleLength}; use crate::paper::Paper; -use crate::syntax::span::{Span, Spanned}; -use crate::syntax::tree::{Ident, SyntaxNode, SyntaxTree}; +use crate::syntax::{Ident, Span, Spanned, SyntaxNode, SyntaxTree}; use crate::{DynFuture, Feedback, Pass}; /// A computational value. diff --git a/src/diagnostic.rs b/src/diagnostic.rs index 834cd928e..fc016c461 100644 --- a/src/diagnostic.rs +++ b/src/diagnostic.rs @@ -7,7 +7,7 @@ #[cfg(feature = "serialize")] use serde::Serialize; -use crate::syntax::span::SpanVec; +use crate::syntax::SpanVec; /// A list of spanned diagnostics. pub type Diagnostics = SpanVec; @@ -42,7 +42,7 @@ impl Diagnostic { /// /// ``` /// # use typstc::error; -/// # use typstc::syntax::span::Span; +/// # use typstc::syntax::Span; /// # use typstc::Feedback; /// # let span = Span::ZERO; /// # let mut feedback = Feedback::new(); @@ -87,7 +87,7 @@ macro_rules! __impl_diagnostic { }; ($level:expr; $span:expr, $fmt:literal $($tts:tt)*) => { - $crate::syntax::span::Spanned::new( + $crate::syntax::Spanned::new( $crate::__impl_diagnostic!($level; $fmt $($tts)*), $span, ) diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 853a66ecd..c053cda0b 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -25,7 +25,7 @@ use crate::compute::scope::Scope; use crate::font::SharedFontLoader; use crate::geom::{Margins, Size}; use crate::style::{LayoutStyle, PageStyle, TextStyle}; -use crate::syntax::tree::SyntaxTree; +use crate::syntax::SyntaxTree; use elements::LayoutElements; use prelude::*; diff --git a/src/layout/tree.rs b/src/layout/tree.rs index fe12cc337..b6f9ab47f 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -4,9 +4,9 @@ use super::line::{LineContext, LineLayouter}; use super::text::{layout_text, TextContext}; use super::*; use crate::style::LayoutStyle; -use crate::syntax::decoration::Decoration; -use crate::syntax::span::{Span, Spanned}; -use crate::syntax::tree::{CallExpr, Code, Heading, SyntaxNode, SyntaxTree}; +use crate::syntax::{ + CallExpr, Code, Decoration, Heading, Span, Spanned, SyntaxNode, SyntaxTree, +}; use crate::{DynFuture, Feedback, Pass}; /// Layout a syntax tree into a collection of boxes. diff --git a/src/lib.rs b/src/lib.rs index 1436dae1b..7681f5b78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,9 +2,9 @@ //! //! # Steps //! - **Parsing:** The parsing step first transforms a plain string into an -//! [iterator of tokens][tokens]. Then, a parser constructs a syntax tree from -//! the token stream. The structures describing the tree can be found in the -//! [syntax] module. +//! [iterator of tokens][tokens]. Then, a [parser] constructs a syntax tree +//! from the token stream. The structures describing the tree can be found in +//! the [syntax] module. //! - **Layouting:** The next step is to transform the syntax tree into a //! portable representation of the typesetted document. Types for these can be //! found in the [layout] module. A finished layout ready for exporting is a @@ -13,7 +13,8 @@ //! format. Submodules for these formats are located in the [export] module. //! Currently, the only supported output format is [_PDF_]. //! -//! [tokens]: syntax/tokens/struct.Tokens.html +//! [tokens]: parse/struct.Tokens.html +//! [parser]: parse/fn.parse.html //! [syntax]: syntax/index.html //! [layout]: layout/index.html //! [export]: export/index.html @@ -34,6 +35,7 @@ pub mod layout; pub mod length; pub mod library; pub mod paper; +pub mod parse; pub mod prelude; pub mod style; pub mod syntax; @@ -48,10 +50,7 @@ use crate::diagnostic::Diagnostics; use crate::font::SharedFontLoader; use crate::layout::{Commands, MultiLayout}; use crate::style::{LayoutStyle, PageStyle, TextStyle}; -use crate::syntax::decoration::Decorations; -use crate::syntax::parsing::parse; -use crate::syntax::span::{Offset, Pos}; -use crate::syntax::tree::SyntaxTree; +use crate::syntax::{Decorations, Offset, Pos, SyntaxTree}; /// Transforms source code into typesetted layouts. /// @@ -87,7 +86,7 @@ impl Typesetter { /// Parse source code into a syntax tree. pub fn parse(&self, src: &str) -> Pass { - parse(src) + parse::parse(src) } /// Layout a syntax tree and return the produced layout. diff --git a/src/syntax/parsing/escaping.rs b/src/parse/escaping.rs similarity index 99% rename from src/syntax/parsing/escaping.rs rename to src/parse/escaping.rs index 5f06388eb..55b1fe67b 100644 --- a/src/syntax/parsing/escaping.rs +++ b/src/parse/escaping.rs @@ -1,4 +1,4 @@ -use crate::syntax::tokens::is_newline_char; +use super::is_newline_char; /// Resolves all escape sequences in a string. pub fn unescape_string(string: &str) -> String { diff --git a/src/parse/mod.rs b/src/parse/mod.rs new file mode 100644 index 000000000..340e89eac --- /dev/null +++ b/src/parse/mod.rs @@ -0,0 +1,11 @@ +//! Parsing and tokenization. + +mod escaping; +mod parser; +mod tokenizer; + +pub use parser::*; +pub use tokenizer::*; + +#[cfg(test)] +mod tests; diff --git a/src/syntax/parsing/parser.rs b/src/parse/parser.rs similarity index 99% rename from src/syntax/parsing/parser.rs rename to src/parse/parser.rs index ca41bf131..2ff30397d 100644 --- a/src/syntax/parsing/parser.rs +++ b/src/parse/parser.rs @@ -1,12 +1,10 @@ use std::str::FromStr; use super::escaping::*; +use super::*; use crate::color::RgbaColor; use crate::compute::table::SpannedEntry; -use crate::syntax::decoration::Decoration; -use crate::syntax::span::{Pos, Span, Spanned}; -use crate::syntax::tokens::{Token, TokenMode, Tokens}; -use crate::syntax::tree::*; +use crate::syntax::*; use crate::{Feedback, Pass}; /// Parse a string of source code. diff --git a/src/syntax/parsing/tests.rs b/src/parse/tests.rs similarity index 94% rename from src/syntax/parsing/tests.rs rename to src/parse/tests.rs index 7fdf02ca3..70517bd82 100644 --- a/src/syntax/parsing/tests.rs +++ b/src/parse/tests.rs @@ -1,16 +1,16 @@ #![allow(non_snake_case)] +use std::fmt::Debug; + use super::parse; use crate::color::RgbaColor; use crate::compute::table::SpannedEntry; use crate::length::Length; -use crate::syntax::decoration::Decoration::*; -use crate::syntax::span::Spanned; -use crate::syntax::tests::*; -use crate::syntax::tree::*; +use crate::syntax::*; // ------------------------------ Construct Syntax Nodes ------------------------------ // +use Decoration::*; use SyntaxNode::{ Linebreak as L, Parbreak as P, Spacing as S, ToggleBolder as B, ToggleItalic as I, }; @@ -161,6 +161,42 @@ macro_rules! d { }; } +/// Assert that expected and found are equal, printing both and panicking +/// and the source of their test case if they aren't. +/// +/// When `cmp_spans` is false, spans are ignored. +pub fn check(src: &str, exp: T, found: T, cmp_spans: bool) +where + T: Debug + PartialEq, +{ + Span::set_cmp(cmp_spans); + let equal = exp == found; + Span::set_cmp(true); + + if !equal { + println!("source: {:?}", src); + if cmp_spans { + println!("expected: {:#?}", exp); + println!("found: {:#?}", found); + } else { + println!("expected: {:?}", exp); + println!("found: {:?}", found); + } + panic!("test failed"); + } +} + +pub fn s(sl: usize, sc: usize, el: usize, ec: usize, v: T) -> Spanned { + Spanned::new(v, Span::new(Pos::new(sl, sc), Pos::new(el, ec))) +} + +// Enables tests to optionally specify spans. +impl From for Spanned { + fn from(t: T) -> Self { + Spanned::zero(t) + } +} + // --------------------------------------- Tests -------------------------------------- // #[test] diff --git a/src/syntax/tokens.rs b/src/parse/tokenizer.rs similarity index 81% rename from src/syntax/tokens.rs rename to src/parse/tokenizer.rs index 0c37e9922..a251d9605 100644 --- a/src/syntax/tokens.rs +++ b/src/parse/tokenizer.rs @@ -4,158 +4,11 @@ use std::iter::Peekable; use std::str::Chars; use unicode_xid::UnicodeXID; -use super::span::{Pos, Span, Spanned}; use crate::length::Length; +use crate::syntax::{Pos, Span, Spanned, Token}; use Token::*; use TokenMode::*; -/// A minimal semantic entity of source code. -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum Token<'s> { - /// One or more whitespace characters. The contained `usize` denotes the - /// number of newlines that were contained in the whitespace. - Space(usize), - - /// A line comment with inner string contents `//\n`. - LineComment(&'s str), - /// A block comment with inner string contents `/**/`. The comment - /// can contain nested block comments. - BlockComment(&'s str), - - /// A left bracket starting a function invocation or body: `[`. - LeftBracket, - /// A right bracket ending a function invocation or body: `]`. - RightBracket, - /// A left parenthesis in a function header: `(`. - LeftParen, - /// A right parenthesis in a function header: `)`. - RightParen, - /// A left brace in a function header: `{`. - LeftBrace, - /// A right brace in a function header: `}`. - RightBrace, - /// A double forward chevron in a function header: `>>`. - Chain, - - /// A colon in a function header: `:`. - Colon, - /// A comma in a function header: `,`. - Comma, - /// An equals sign in a function header: `=`. - Equals, - - /// An identifier in a function header: `center`. - Ident(&'s str), - /// A quoted string in a function header: `"..."`. - Str { - /// The string inside the quotes. - /// - /// _Note_: If the string contains escape sequences these are not yet - /// applied to be able to just store a string slice here instead of - /// a String. The escaping is done later in the parser. - string: &'s str, - /// Whether the closing quote was present. - terminated: bool, - }, - /// A boolean in a function header: `true | false`. - Bool(bool), - /// A number in a function header: `3.14`. - Number(f64), - /// A length in a function header: `12pt`. - Length(Length), - /// A hex value in a function header: `#20d82a`. - Hex(&'s str), - /// A plus in a function header, signifying the addition of expressions. - Plus, - /// A hyphen in a function header, signifying the subtraction of - /// expressions. - Hyphen, - /// A slash in a function header, signifying the division of expressions. - Slash, - - /// A star. It can appear in a function header where it signifies the - /// multiplication of expressions or the body where it modifies the styling. - Star, - /// An underscore in body-text. - Underscore, - /// A backslash followed by whitespace in text. - Backslash, - - /// A hashtag token in the body can indicate compute mode or headings. - Hashtag, - - /// A unicode escape sequence. - UnicodeEscape { - /// The escape sequence between two braces. - sequence: &'s str, - /// Whether the closing brace was present. - terminated: bool, - }, - - /// Raw text. - Raw { - /// The raw text (not yet unescaped as for strings). - raw: &'s str, - /// Whether the closing backtick was present. - terminated: bool, - }, - - /// Multi-line code block. - Code { - /// The language of the code block, if specified. - lang: Option>, - /// The raw text (not yet unescaped as for strings). - raw: &'s str, - /// Whether the closing backticks were present. - terminated: bool, - }, - - /// Any other consecutive string. - Text(&'s str), - - /// Things that are not valid in the context they appeared in. - Invalid(&'s str), -} - -impl<'s> Token<'s> { - /// The natural-language name for this token for use in error messages. - pub fn name(self) -> &'static str { - match self { - Space(_) => "space", - LineComment(_) => "line comment", - BlockComment(_) => "block comment", - LeftBracket => "opening bracket", - RightBracket => "closing bracket", - LeftParen => "opening paren", - RightParen => "closing paren", - LeftBrace => "opening brace", - RightBrace => "closing brace", - Chain => "function chain operator", - Colon => "colon", - Comma => "comma", - Equals => "equals sign", - Ident(_) => "identifier", - Str { .. } => "string", - Bool(_) => "bool", - Number(_) => "number", - Length(_) => "length", - Hex(_) => "hex value", - Plus => "plus", - Hyphen => "minus", - Slash => "slash", - Star => "star", - Underscore => "underscore", - Backslash => "backslash", - Hashtag => "hashtag", - UnicodeEscape { .. } => "unicode escape sequence", - Raw { .. } => "raw text", - Code { .. } => "code block", - Text(_) => "text", - Invalid("*/") => "end of block comment", - Invalid(_) => "invalid token", - } - } -} /// An iterator over the tokens of a string of source code. #[derive(Debug)] @@ -602,10 +455,10 @@ pub fn is_identifier(string: &str) -> bool { #[cfg(test)] #[allow(non_snake_case)] mod tests { - use super::super::span::Spanned; use super::*; use crate::length::Length; - use crate::syntax::tests::*; + use crate::parse::tests::{check, s}; + use Token::{ BlockComment as BC, Bool, Chain, Hex, Hyphen as Min, Ident as Id, LeftBrace as LB, LeftBracket as L, LeftParen as LP, Length as Len, diff --git a/src/prelude.rs b/src/prelude.rs index aa727e6d9..953f15132 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -6,7 +6,5 @@ pub use crate::layout::prelude::*; pub use crate::layout::Command::{self, *}; pub use crate::layout::Commands; pub use crate::style::*; -pub use crate::syntax::parsing::parse; -pub use crate::syntax::span::{Pos, Span, SpanVec, Spanned}; -pub use crate::syntax::tree::*; +pub use crate::syntax::*; pub use crate::{Feedback, Pass}; diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 70935e796..1b9f8ba8f 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -1,50 +1,11 @@ -//! Syntax trees, parsing and tokenization. +//! Syntax types. -pub mod decoration; -pub mod parsing; -pub mod span; -pub mod tokens; -pub mod tree; +mod decoration; +mod span; +mod token; +mod tree; -#[cfg(test)] -mod tests { - use super::span; - use crate::prelude::*; - use std::fmt::Debug; - - /// Assert that expected and found are equal, printing both and panicking - /// and the source of their test case if they aren't. - /// - /// When `cmp_spans` is false, spans are ignored. - pub fn check(src: &str, exp: T, found: T, cmp_spans: bool) - where - T: Debug + PartialEq, - { - span::set_cmp(cmp_spans); - let equal = exp == found; - span::set_cmp(true); - - if !equal { - println!("source: {:?}", src); - if cmp_spans { - println!("expected: {:#?}", exp); - println!("found: {:#?}", found); - } else { - println!("expected: {:?}", exp); - println!("found: {:?}", found); - } - panic!("test failed"); - } - } - - pub fn s(sl: usize, sc: usize, el: usize, ec: usize, v: T) -> Spanned { - Spanned::new(v, Span::new(Pos::new(sl, sc), Pos::new(el, ec))) - } - - // Enables tests to optionally specify spans. - impl From for Spanned { - fn from(t: T) -> Self { - Spanned::zero(t) - } - } -} +pub use decoration::*; +pub use span::*; +pub use token::*; +pub use tree::*; diff --git a/src/syntax/parsing/mod.rs b/src/syntax/parsing/mod.rs deleted file mode 100644 index bf34340f7..000000000 --- a/src/syntax/parsing/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Parsing of source code into syntax trees. - -mod escaping; -mod parser; - -pub use parser::parse; - -#[cfg(test)] -mod tests; diff --git a/src/syntax/span.rs b/src/syntax/span.rs index 89f773c75..9357c3452 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -14,12 +14,6 @@ thread_local! { static CMP_SPANS: Cell = Cell::new(true); } -/// When set to `false` comparisons with `PartialEq` ignore spans. -#[cfg(test)] -pub(crate) fn set_cmp(cmp: bool) { - CMP_SPANS.with(|cell| cell.set(cmp)); -} - /// Span offsetting. pub trait Offset { /// Offset all spans contained in `Self` by the given position. @@ -132,6 +126,12 @@ impl Span { pub fn expand(&mut self, other: Self) { *self = Self::merge(*self, other) } + + /// When set to `false` comparisons with `PartialEq` ignore spans. + #[cfg(test)] + pub(crate) fn set_cmp(cmp: bool) { + CMP_SPANS.with(|cell| cell.set(cmp)); + } } impl Offset for Span { diff --git a/src/syntax/token.rs b/src/syntax/token.rs new file mode 100644 index 000000000..e91a780ca --- /dev/null +++ b/src/syntax/token.rs @@ -0,0 +1,152 @@ +//! Tokenization. + +use super::span::Spanned; +use crate::length::Length; + +/// A minimal semantic entity of source code. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum Token<'s> { + /// One or more whitespace characters. The contained `usize` denotes the + /// number of newlines that were contained in the whitespace. + Space(usize), + + /// A line comment with inner string contents `//\n`. + LineComment(&'s str), + /// A block comment with inner string contents `/**/`. The comment + /// can contain nested block comments. + BlockComment(&'s str), + + /// A left bracket starting a function invocation or body: `[`. + LeftBracket, + /// A right bracket ending a function invocation or body: `]`. + RightBracket, + /// A left parenthesis in a function header: `(`. + LeftParen, + /// A right parenthesis in a function header: `)`. + RightParen, + /// A left brace in a function header: `{`. + LeftBrace, + /// A right brace in a function header: `}`. + RightBrace, + /// A double forward chevron in a function header: `>>`. + Chain, + + /// A colon in a function header: `:`. + Colon, + /// A comma in a function header: `,`. + Comma, + /// An equals sign in a function header: `=`. + Equals, + + /// An identifier in a function header: `center`. + Ident(&'s str), + /// A quoted string in a function header: `"..."`. + Str { + /// The string inside the quotes. + /// + /// _Note_: If the string contains escape sequences these are not yet + /// applied to be able to just store a string slice here instead of + /// a String. The escaping is done later in the parser. + string: &'s str, + /// Whether the closing quote was present. + terminated: bool, + }, + /// A boolean in a function header: `true | false`. + Bool(bool), + /// A number in a function header: `3.14`. + Number(f64), + /// A length in a function header: `12pt`. + Length(Length), + /// A hex value in a function header: `#20d82a`. + Hex(&'s str), + /// A plus in a function header, signifying the addition of expressions. + Plus, + /// A hyphen in a function header, signifying the subtraction of + /// expressions. + Hyphen, + /// A slash in a function header, signifying the division of expressions. + Slash, + + /// A star. It can appear in a function header where it signifies the + /// multiplication of expressions or the body where it modifies the styling. + Star, + /// An underscore in body-text. + Underscore, + /// A backslash followed by whitespace in text. + Backslash, + + /// A hashtag token in the body can indicate compute mode or headings. + Hashtag, + + /// A unicode escape sequence. + UnicodeEscape { + /// The escape sequence between two braces. + sequence: &'s str, + /// Whether the closing brace was present. + terminated: bool, + }, + + /// Raw text. + Raw { + /// The raw text (not yet unescaped as for strings). + raw: &'s str, + /// Whether the closing backtick was present. + terminated: bool, + }, + + /// Multi-line code block. + Code { + /// The language of the code block, if specified. + lang: Option>, + /// The raw text (not yet unescaped as for strings). + raw: &'s str, + /// Whether the closing backticks were present. + terminated: bool, + }, + + /// Any other consecutive string. + Text(&'s str), + + /// Things that are not valid in the context they appeared in. + Invalid(&'s str), +} + +impl<'s> Token<'s> { + /// The natural-language name for this token for use in error messages. + pub fn name(self) -> &'static str { + match self { + Self::Space(_) => "space", + Self::LineComment(_) => "line comment", + Self::BlockComment(_) => "block comment", + Self::LeftBracket => "opening bracket", + Self::RightBracket => "closing bracket", + Self::LeftParen => "opening paren", + Self::RightParen => "closing paren", + Self::LeftBrace => "opening brace", + Self::RightBrace => "closing brace", + Self::Chain => "function chain operator", + Self::Colon => "colon", + Self::Comma => "comma", + Self::Equals => "equals sign", + Self::Ident(_) => "identifier", + Self::Str { .. } => "string", + Self::Bool(_) => "bool", + Self::Number(_) => "number", + Self::Length(_) => "length", + Self::Hex(_) => "hex value", + Self::Plus => "plus", + Self::Hyphen => "minus", + Self::Slash => "slash", + Self::Star => "star", + Self::Underscore => "underscore", + Self::Backslash => "backslash", + Self::Hashtag => "hashtag", + Self::UnicodeEscape { .. } => "unicode escape sequence", + Self::Raw { .. } => "raw text", + Self::Code { .. } => "code block", + Self::Text(_) => "text", + Self::Invalid("*/") => "end of block comment", + Self::Invalid(_) => "invalid token", + } + } +} diff --git a/src/syntax/tree.rs b/src/syntax/tree.rs index 715db1099..f243e67aa 100644 --- a/src/syntax/tree.rs +++ b/src/syntax/tree.rs @@ -4,12 +4,12 @@ use std::fmt::{self, Debug, Formatter}; use super::decoration::Decoration; use super::span::{SpanVec, Spanned}; -use super::tokens::is_identifier; use crate::color::RgbaColor; use crate::compute::table::{SpannedEntry, Table}; use crate::compute::value::{TableValue, Value}; use crate::layout::LayoutContext; use crate::length::Length; +use crate::parse::is_identifier; use crate::{DynFuture, Feedback}; /// A collection of nodes which form a tree together with the nodes' children.