Skip to main content

m2_syn/lex/
cell_stream.rs

1//! Structural cell boundaries over raw Macaulay2 token trees.
2//!
3//! [`TokenStream`] retains lexical atoms, comments, source spans, and nested
4//! delimiter groups. [`CellStream`] is the next representation in the parsing
5//! pipeline: a sequence of token streams that can each be parsed independently.
6//! End-of-cell and end-of-file are properties of that sequence rather than
7//! virtual [`TokenTree`] variants.
8//!
9//! Framing recognizes top-level semicolons and physical newlines at which the
10//! current operator, clause, and delimiter state can end. It also inserts the
11//! one non-lexical atom required by the native parser: zero-width
12//! [`CellTokenTree::Adj`]. Framing does not otherwise parse or validate the
13//! Macaulay2 grammar.
14//!
15//! [`CellStream`] behaves as a borrowed slice of [`CellTokenStream`] values. It
16//! can be indexed and iterated through the standard slice APIs, and consuming
17//! it yields owned cells. Clones share their backing allocation until mutation.
18//!
19//! # Examples
20//!
21//! ```
22//! use m2_syn::{Tokenize, lex_str};
23//!
24//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
25//! let cells = lex_str("x = 1;\ny = 2")?.to_cell_stream()?;
26//!
27//! assert_eq!(cells.len(), 2);
28//! assert_eq!(cells[0].to_string(), "x = 1;");
29//! assert_eq!(cells.iter().map(ToString::to_string).collect::<Vec<_>>(), [
30//!     "x = 1;",
31//!     "y = 2",
32//! ]);
33//! # Ok(())
34//! # }
35//! ```
36
37use std::error::Error;
38use std::fmt::{Display, Formatter, Result as FmtResult};
39use std::mem::take;
40use std::ops::Deref;
41use std::slice::Iter;
42use std::sync::Arc;
43use std::vec::IntoIter;
44
45use crate::lexer::{canonical_word_spelling, is_quote_specifier, newline_ends_cell};
46use crate::token_stream::TokenStreamParts;
47use crate::{
48    Delimiter, DelimiterKind, IdentToken, LeadingTrivia, Lex, LiteralToken, Punct, Source, Span,
49    Spanned, TOKEN_PARSE_RULES, Terminal, TokenGroup, TokenParseRule, TokenStream, TokenTree,
50    TrailingTrivia, Trivia,
51};
52
53#[derive(Debug, Clone, Default)]
54/// Internal state for CellTokenStreamData.
55struct CellTokenStreamData {
56    /// The trees.
57    trees: Vec<CellTokenTree>,
58    /// The prefix.
59    prefix: TrailingTrivia,
60    /// The suffix.
61    suffix: LeadingTrivia,
62    /// The source.
63    source: Option<Arc<Source>>,
64}
65
66impl PartialEq for CellTokenStreamData {
67    fn eq(&self, other: &Self) -> bool {
68        self.trees == other.trees
69            && self.prefix == other.prefix
70            && self.suffix == other.suffix
71            && match (&self.source, &other.source) {
72                (Some(left), Some(right)) => Arc::ptr_eq(left, right),
73                (None, None) => true,
74                _ => false,
75            }
76    }
77}
78
79impl Eq for CellTokenStreamData {}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
82/// One token tree in a framed Macaulay2 cell.
83///
84/// Lexical atoms retain their raw token types. Delimiter groups recursively
85/// contain [`CellTokenStream`], while [`Self::Adj`] represents the inferred
86/// application boundary that does not exist in [`TokenTree`].
87pub enum CellTokenTree {
88    /// A recursively framed delimiter group.
89    Group(CellTokenGroup),
90    /// A lexical literal.
91    Literal(LiteralToken),
92    /// Lexical punctuation.
93    Punct(Punct),
94    /// A lexical identifier or keyword.
95    Ident(IdentToken),
96    /// Inferred same-line adjacency at its zero-width source position.
97    Adj(Span),
98}
99
100impl CellTokenTree {
101    /// Returns the lexical spelling of a flat token or the empty adjacency spelling.
102    pub fn spelling(&self) -> Option<&str> {
103        match self {
104            Self::Group(_) => None,
105            Self::Literal(token) => Some(token.text()),
106            Self::Punct(token) => Some(token.text()),
107            Self::Ident(token) => Some(token.text()),
108            Self::Adj(_) => Some(""),
109        }
110    }
111
112    /// Returns comments attached before this cell token.
113    pub fn leading_trivia(&self) -> &[Trivia] {
114        match self {
115            Self::Group(token) => token.leading_trivia().as_slice(),
116            Self::Literal(token) => token.leading_trivia().as_slice(),
117            Self::Punct(token) => token.leading_trivia().as_slice(),
118            Self::Ident(token) => token.leading_trivia().as_slice(),
119            Self::Adj(_) => &[],
120        }
121    }
122
123    /// Returns comments attached after this cell token.
124    pub fn trailing_trivia(&self) -> &[Trivia] {
125        match self {
126            Self::Group(token) => token.trailing_trivia().as_slice(),
127            Self::Literal(token) => token.trailing_trivia().as_slice(),
128            Self::Punct(token) => token.trailing_trivia().as_slice(),
129            Self::Ident(token) => token.trailing_trivia().as_slice(),
130            Self::Adj(_) => &[],
131        }
132    }
133
134    /// Creates the value from augmented.
135    fn from_augmented(tree: TokenTree) -> Self {
136        match tree {
137            TokenTree::Group(group) => Self::Group(CellTokenGroup::from_augmented(group)),
138            TokenTree::Literal(token) => Self::Literal(token),
139            TokenTree::Punct(token) if token.text().is_empty() => Self::Adj(token.expect_span()),
140            TokenTree::Punct(token) => Self::Punct(token),
141            TokenTree::Ident(token) => Self::Ident(token),
142        }
143    }
144
145    /// Converts the value into lexical.
146    fn into_lexical(self) -> Option<TokenTree> {
147        match self {
148            Self::Group(group) => Some(TokenTree::Group(group.into_lexical())),
149            Self::Literal(token) => Some(TokenTree::Literal(token)),
150            Self::Punct(token) => Some(TokenTree::Punct(token)),
151            Self::Ident(token) => Some(TokenTree::Ident(token)),
152            Self::Adj(_) => None,
153        }
154    }
155
156    /// Converts the value into parser token.
157    fn into_parser_token(self) -> TokenTree {
158        match self {
159            Self::Group(group) => TokenTree::Group(group.into_parser_group()),
160            Self::Literal(token) => TokenTree::Literal(token),
161            Self::Punct(token) => TokenTree::Punct(token),
162            Self::Ident(token) => TokenTree::Ident(token),
163            Self::Adj(span) => TokenTree::Punct(Punct::from_span(span)),
164        }
165    }
166}
167
168impl Spanned for CellTokenTree {
169    fn try_span(&self) -> Option<Span> {
170        match self {
171            Self::Group(token) => token.try_span(),
172            Self::Literal(token) => token.try_span(),
173            Self::Punct(token) => token.try_span(),
174            Self::Ident(token) => token.try_span(),
175            Self::Adj(span) => Some(span.clone()),
176        }
177    }
178}
179
180#[derive(Debug, Clone, PartialEq, Eq)]
181/// A delimiter group whose contents have passed through cell framing.
182pub struct CellTokenGroup {
183    /// The delimiter.
184    delimiter: Delimiter,
185    /// The stream.
186    stream: CellTokenStream,
187    /// The leading trivia.
188    leading_trivia: LeadingTrivia,
189    /// The trailing trivia.
190    trailing_trivia: TrailingTrivia,
191}
192
193impl CellTokenGroup {
194    /// Returns the group's delimiter data.
195    pub fn delimiter(&self) -> &Delimiter {
196        &self.delimiter
197    }
198
199    /// Returns the group's delimiter family.
200    pub fn delim_kind(&self) -> DelimiterKind {
201        self.delimiter.kind
202    }
203
204    /// Returns the recursively framed contents.
205    pub fn stream(&self) -> &CellTokenStream {
206        &self.stream
207    }
208
209    /// Consumes the group and returns its recursively framed contents.
210    pub fn into_stream(self) -> CellTokenStream {
211        self.stream
212    }
213
214    /// Creates the value from augmented.
215    fn from_augmented(group: TokenGroup) -> Self {
216        let (delimiter, stream, leading_trivia, trailing_trivia) = group.into_parts();
217        Self {
218            delimiter,
219            stream: CellTokenStream::from_augmented(stream),
220            leading_trivia,
221            trailing_trivia,
222        }
223    }
224
225    /// Converts the value into lexical.
226    fn into_lexical(self) -> TokenGroup {
227        TokenGroup::from_parts(
228            self.delimiter,
229            self.stream.into_token_stream(),
230            self.leading_trivia,
231            self.trailing_trivia,
232        )
233    }
234
235    /// Converts the value into parser group.
236    fn into_parser_group(self) -> TokenGroup {
237        TokenGroup::from_parts(
238            self.delimiter,
239            self.stream.into_parser_token_stream(),
240            self.leading_trivia,
241            self.trailing_trivia,
242        )
243    }
244}
245
246impl Spanned for CellTokenGroup {
247    fn try_span(&self) -> Option<Span> {
248        self.delimiter.try_span()
249    }
250}
251
252impl Terminal for CellTokenGroup {
253    fn leading_trivia(&self) -> &LeadingTrivia {
254        &self.leading_trivia
255    }
256
257    fn leading_trivia_mut(&mut self) -> &mut LeadingTrivia {
258        &mut self.leading_trivia
259    }
260
261    fn trailing_trivia(&self) -> &TrailingTrivia {
262        &self.trailing_trivia
263    }
264
265    fn trailing_trivia_mut(&mut self) -> &mut TrailingTrivia {
266        &mut self.trailing_trivia
267    }
268}
269
270#[derive(Debug, Clone, Default, PartialEq, Eq)]
271/// The framed tokens of one independently parseable Macaulay2 cell.
272///
273/// Unlike a lexical [`TokenStream`], this representation has passed through
274/// cell framing. It may contain inferred zero-width adjacency punctuation at
275/// every delimiter depth, and its surrounding [`CellStream`] records the
276/// structural boundary after it.
277///
278/// The stream dereferences to `[CellTokenTree]` for parser access. Converting
279/// it back into a [`TokenStream`] removes inferred adjacency recursively,
280/// because adjacency is not a lexical token.
281pub struct CellTokenStream(Arc<CellTokenStreamData>);
282
283impl CellTokenStream {
284    /// Creates a constant-time fork over the same framed token storage.
285    ///
286    /// Mutating either fork internally detaches only the modified path.
287    pub fn fork(&self) -> Self {
288        self.clone()
289    }
290
291    /// Consumes this framed cell and returns its lexical token representation.
292    ///
293    /// Inferred adjacency is removed recursively. Comments, delimiter groups,
294    /// source spans, and physical spellings are preserved.
295    pub fn into_token_stream(self) -> TokenStream {
296        let CellTokenStreamData {
297            trees,
298            prefix,
299            suffix,
300            source,
301        } = self.into_data();
302        TokenStream::from_parts(TokenStreamParts {
303            trees: trees
304                .into_iter()
305                .filter_map(CellTokenTree::into_lexical)
306                .collect(),
307            prefix,
308            suffix,
309            source,
310        })
311    }
312
313    /// Returns this framed cell's lexical token representation.
314    ///
315    /// This is the borrowing counterpart of [`Self::into_token_stream`].
316    pub fn to_token_stream(&self) -> TokenStream {
317        self.clone().into_token_stream()
318    }
319
320    /// Borrows the shared backing data.
321    fn data(&self) -> &CellTokenStreamData {
322        &self.0
323    }
324
325    /// Converts the value into data.
326    fn into_data(self) -> CellTokenStreamData {
327        Arc::try_unwrap(self.0).unwrap_or_else(|data| (*data).clone())
328    }
329
330    /// Creates the value from augmented.
331    pub(crate) fn from_augmented(tokens: TokenStream) -> Self {
332        let TokenStreamParts {
333            trees,
334            prefix,
335            suffix,
336            source,
337        } = tokens.into_parts();
338        Self(Arc::new(CellTokenStreamData {
339            trees: trees
340                .into_iter()
341                .map(CellTokenTree::from_augmented)
342                .collect(),
343            prefix,
344            suffix,
345            source,
346        }))
347    }
348
349    /// Returns the associated source snapshot.
350    pub(crate) fn source(&self) -> Option<Arc<Source>> {
351        self.data().source.clone().or_else(|| {
352            self.iter()
353                .find_map(Spanned::try_span)
354                .map(|span| Arc::clone(span.source()))
355        })
356    }
357
358    #[cfg(test)]
359    pub(crate) fn shares_storage(&self, other: &Self) -> bool {
360        Arc::ptr_eq(&self.0, &other.0)
361    }
362
363    /// Converts the value into parser token stream.
364    ///
365    /// Comments are settled onto the tokens they follow, so a parser finds
366    /// them in trailing trivia and in the stream prefix only.
367    pub(crate) fn into_parser_token_stream(self) -> TokenStream {
368        let CellTokenStreamData {
369            trees,
370            prefix,
371            suffix,
372            source,
373        } = self.into_data();
374        let mut tokens = TokenStream::from_parts(TokenStreamParts {
375            trees: trees
376                .into_iter()
377                .map(CellTokenTree::into_parser_token)
378                .collect(),
379            prefix,
380            suffix,
381            source,
382        });
383        tokens.settle_trivia();
384        tokens
385    }
386
387    /// Creates the value from framed cells.
388    pub(crate) fn from_framed_cells(cells: impl IntoIterator<Item = CellTokenStream>) -> Self {
389        let mut tokens = TokenStream::new();
390        for cell in cells {
391            tokens.extend([cell.into_parser_token_stream()]);
392        }
393        Self::from_augmented(tokens)
394    }
395}
396
397impl Deref for CellTokenStream {
398    type Target = [CellTokenTree];
399
400    fn deref(&self) -> &Self::Target {
401        &self.data().trees
402    }
403}
404
405impl AsRef<[CellTokenTree]> for CellTokenStream {
406    fn as_ref(&self) -> &[CellTokenTree] {
407        self
408    }
409}
410
411impl Spanned for CellTokenStream {
412    fn try_span(&self) -> Option<Span> {
413        Span::join_all(
414            self.data()
415                .prefix
416                .try_span()
417                .into_iter()
418                .chain(self.iter().filter_map(Spanned::try_span))
419                .chain(self.data().suffix.try_span()),
420        )
421    }
422}
423
424impl Display for CellTokenStream {
425    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
426        self.to_token_stream().fmt(formatter)
427    }
428}
429
430impl IntoIterator for CellTokenStream {
431    type Item = CellTokenTree;
432    type IntoIter = IntoIter<CellTokenTree>;
433
434    fn into_iter(self) -> Self::IntoIter {
435        self.into_data().trees.into_iter()
436    }
437}
438
439impl<'syntax> IntoIterator for &'syntax CellTokenStream {
440    type Item = &'syntax CellTokenTree;
441    type IntoIter = Iter<'syntax, CellTokenTree>;
442
443    fn into_iter(self) -> Self::IntoIter {
444        self.iter()
445    }
446}
447
448#[derive(Debug, Clone, Default)]
449struct CellStreamData {
450    cells: Vec<CellTokenStream>,
451    source: Option<Arc<Source>>,
452    /// The span of the layout that precedes the first cell.
453    bof: Option<Span>,
454    /// The span of the layout that follows the final cell.
455    eof: Option<Span>,
456}
457
458#[derive(Debug, Clone, Default)]
459/// A sequence of independently parseable Macaulay2 token streams.
460///
461/// Cell boundaries are expressed by the collection itself. The boundary after
462/// its final element is also the end-of-file boundary, so neither fact needs a
463/// virtual token in the contained streams. Clones and [`Self::fork`] share
464/// immutable backing storage, with mutations detaching through copy-on-write.
465///
466/// The stream dereferences to `[CellTokenStream]`, so slice operations such as
467/// indexing, [`slice::iter`], [`slice::len`], and [`slice::is_empty`] are
468/// available directly. Iterating `&CellStream` borrows cells; iterating an
469/// owned value consumes it and yields owned cells.
470pub struct CellStream(Arc<CellStreamData>);
471
472impl PartialEq for CellStream {
473    fn eq(&self, other: &Self) -> bool {
474        self.as_ref() == other.as_ref()
475    }
476}
477
478impl Eq for CellStream {}
479
480impl Deref for CellStream {
481    type Target = [CellTokenStream];
482
483    fn deref(&self) -> &Self::Target {
484        &self.data().cells
485    }
486}
487
488impl AsRef<[CellTokenStream]> for CellStream {
489    fn as_ref(&self) -> &[CellTokenStream] {
490        self
491    }
492}
493
494impl CellStream {
495    /// Partitions a complete raw token stream into independently parseable cells.
496    ///
497    /// Top-level cell boundaries are retained structurally, and inferred
498    /// adjacency punctuation is inserted recursively. This operation checks
499    /// whether the final cell requires more input, but it does not validate the
500    /// complete Macaulay2 grammar.
501    ///
502    /// # Errors
503    ///
504    /// Returns [`CellStreamError`] when the final tokens require more input
505    /// before they can form an independently parseable cell, such as after an
506    /// operator without its right operand or inside an unclosed delimiter.
507    pub fn new(tokens: TokenStream) -> Result<Self, CellStreamError> {
508        let source = tokens.source();
509        let bof = leading_layout_span(source.as_ref());
510        let eof = trailing_layout_span(source.as_ref());
511        let FramedCells { cells, incomplete } = frame_recovering(tokens);
512        match incomplete {
513            Some(tokens) => Err(CellStreamError {
514                span: end_span(&tokens),
515            }),
516            None => Ok(Self(Arc::new(CellStreamData {
517                cells,
518                source,
519                bof,
520                eof,
521            }))),
522        }
523    }
524
525    /// Creates a stream from token streams already known to be complete cells.
526    ///
527    /// This constructor does not inspect, repartition, or insert adjacency into
528    /// its input. Use [`Self::new`] or [`Tokenize::to_cell_stream`] when starting
529    /// from an unframed raw stream.
530    pub fn from_cells(cells: impl IntoIterator<Item = CellTokenStream>) -> Self {
531        Self::from_cells_with_source(cells, None)
532    }
533
534    /// Creates the value from cells with source.
535    pub(crate) fn from_cells_with_source(
536        cells: impl IntoIterator<Item = CellTokenStream>,
537        source: Option<Arc<Source>>,
538    ) -> Self {
539        Self(Arc::new(CellStreamData {
540            cells: cells.into_iter().collect(),
541            bof: leading_layout_span(source.as_ref()),
542            eof: trailing_layout_span(source.as_ref()),
543            source,
544        }))
545    }
546
547    /// Creates a constant-time fork over the same cell storage.
548    ///
549    /// The fork initially shares both the cell collection and every contained
550    /// [`TokenStream`]. Mutating either value preserves the other and copies
551    /// only the backing structures along the modified path. This is equivalent
552    /// to [`Clone::clone`], with a name that emphasizes speculative use.
553    pub fn fork(&self) -> Self {
554        self.clone()
555    }
556
557    /// Borrows the shared backing data.
558    fn data(&self) -> &CellStreamData {
559        &self.0
560    }
561
562    /// Borrows the shared backing data mutably.
563    fn data_mut(&mut self) -> &mut CellStreamData {
564        Arc::make_mut(&mut self.0)
565    }
566
567    /// Converts the value into data.
568    fn into_data(self) -> CellStreamData {
569        Arc::try_unwrap(self.0).unwrap_or_else(|data| (*data).clone())
570    }
571
572    /// Consumes the cells and flattens them into one raw token stream.
573    ///
574    /// Cell boundaries are not emitted as tokens and therefore cannot be
575    /// recovered structurally from the result alone. Original whitespace
576    /// between cells remains renderable when their spans share one source.
577    pub fn into_token_stream(self) -> TokenStream {
578        let source = self.complete_source();
579        let mut tokens = flatten_cells(self.into_data().cells);
580        if let Some(source) = source {
581            tokens.set_source(source);
582        }
583        tokens
584    }
585
586    /// Flattens borrowed cells into one raw token stream.
587    ///
588    /// This has the same boundary semantics as [`Self::into_token_stream`] but
589    /// leaves `self` available for reuse.
590    pub fn to_token_stream(&self) -> TokenStream {
591        let mut tokens = flatten_cells(self.iter().cloned());
592        if let Some(source) = self.complete_source() {
593            tokens.set_source(source);
594        }
595        tokens
596    }
597
598    /// Handles complete source.
599    pub(crate) fn complete_source(&self) -> Option<Arc<Source>> {
600        let source = self.data().source.as_ref()?;
601        let mut cursor = 0;
602        for cell in self {
603            if !covers_stream(cell, source, &mut cursor) {
604                return None;
605            }
606        }
607        source.text()[cursor..]
608            .bytes()
609            .all(|byte| byte.is_ascii_whitespace())
610            .then(|| Arc::clone(source))
611    }
612
613    /// Handles physical beginning-of-file.
614    ///
615    /// The span covers the layout before the first significant byte, so the
616    /// beginning-of-file marker built from it retains input that no cell owns.
617    /// For a stream whose cells do not cover one shared source snapshot it is
618    /// the empty span where the first cell starts.
619    pub(crate) fn physical_bof(&self) -> Span {
620        if let Some(source) = self.complete_source() {
621            let leading = source.leading_whitespace();
622            return Span::ranged(Arc::clone(&source), leading);
623        }
624        self.first()
625            .and_then(cell_first_span)
626            .and_then(|span| span.start().map(|start| span.at(start)))
627            .unwrap_or_else(|| Span::for_text(""))
628    }
629
630    /// Handles physical end-of-file.
631    ///
632    /// The span covers the layout between the last significant byte and the
633    /// physical end, so the end-of-file marker built from it retains input that
634    /// no cell owns. It stays empty for a stream whose cells do not cover one
635    /// shared source snapshot.
636    pub(crate) fn physical_eof(&self) -> Span {
637        if let Some(source) = self.complete_source() {
638            let trailing = source.trailing_whitespace();
639            return Span::ranged(Arc::clone(&source), trailing);
640        }
641        self.last().map_or_else(|| Span::for_text(""), end_span)
642    }
643
644    /// Creates the value from source file.
645    pub(crate) fn from_source_file(source_file: &crate::SourceFile) -> Self {
646        let mut cells = source_file
647            .cells
648            .iter()
649            .map(Lex::to_token_stream)
650            .collect::<Vec<_>>();
651        for (index, comments) in [
652            (0, &source_file.leading_trivia),
653            (cells.len(), source_file.eof.leading_trivia()),
654        ]
655        .into_iter()
656        .rev()
657        {
658            if !comments.is_empty() {
659                let mut comment_cell = TokenStream::new();
660                comment_cell.extend(comments.as_slice().iter().cloned());
661                cells.insert(index, comment_cell);
662            }
663        }
664        Self(Arc::new(CellStreamData {
665            cells: cells
666                .into_iter()
667                .map(CellTokenStream::from_augmented)
668                .collect(),
669            source: None,
670            bof: source_file.bof.try_span(),
671            eof: source_file.eof.try_span(),
672        }))
673    }
674}
675
676/// Returns whether the value covers stream.
677fn covers_stream(stream: &CellTokenStream, source: &Arc<Source>, cursor: &mut usize) -> bool {
678    for trivia in stream.data().prefix.as_slice() {
679        if !covers_span(trivia.span(), source, cursor) {
680            return false;
681        }
682    }
683    for token in stream.iter() {
684        for trivia in token.leading_trivia() {
685            if !covers_span(trivia.span(), source, cursor) {
686                return false;
687            }
688        }
689        let covered = match token {
690            CellTokenTree::Group(group) => {
691                covers_span(group.delimiter().opening_span(), source, cursor)
692                    && covers_stream(group.stream(), source, cursor)
693                    && group
694                        .delimiter()
695                        .closing_span()
696                        .is_none_or(|span| covers_span(span, source, cursor))
697            }
698            _ => covers_span(token.span(), source, cursor),
699        };
700        if !covered {
701            return false;
702        }
703        for trivia in token.trailing_trivia() {
704            if !covers_span(trivia.span(), source, cursor) {
705                return false;
706            }
707        }
708    }
709    stream
710        .data()
711        .suffix
712        .as_slice()
713        .iter()
714        .all(|trivia| covers_span(trivia.span(), source, cursor))
715}
716
717/// Returns whether the value covers span.
718fn covers_span(span: Span, source: &Arc<Source>, cursor: &mut usize) -> bool {
719    if !Arc::ptr_eq(span.source(), source) {
720        return false;
721    }
722    let (Some(start), Some(end)) = (span.start(), span.end()) else {
723        return false;
724    };
725    if start == end {
726        return start >= *cursor;
727    }
728    if start < *cursor
729        || !source.text()[*cursor..start]
730            .bytes()
731            .all(|byte| byte.is_ascii_whitespace())
732    {
733        return false;
734    }
735    *cursor = end;
736    true
737}
738
739impl Display for CellStream {
740    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
741        write_leading_layout(
742            formatter,
743            self.data().bof.as_ref(),
744            self.first().and_then(cell_first_span).as_ref(),
745        )?;
746        let mut previous: Option<Span> = None;
747        for (index, cell) in self.iter().enumerate() {
748            if index != 0 {
749                let next = cell_first_span(cell);
750                let mut recovered = false;
751                if let Some((left, right)) = previous.as_ref().zip(next.as_ref())
752                    && left.shares_source(right)
753                    && let (Some(end), Some(start)) = (left.end(), right.start())
754                    && end <= start
755                {
756                    let gap = &left.source().text()[end..start];
757                    // Synthetic text is assembled without layout, so cells
758                    // touching there were never written on one line.
759                    let written = !gap.is_empty() || !left.source().file().is_synthetic();
760                    if written && gap.bytes().all(|byte| byte.is_ascii_whitespace()) {
761                        formatter.write_str(gap)?;
762                        recovered = true;
763                    }
764                }
765                if !recovered {
766                    formatter.write_str("\n")?;
767                }
768            }
769            cell.fmt(formatter)?;
770            previous = cell_last_span(cell);
771        }
772        write_trailing_layout(formatter, self.data().eof.as_ref(), previous.as_ref())
773    }
774}
775
776/// Spans the layout opening a source snapshot the cells were framed from.
777fn leading_layout_span(source: Option<&Arc<Source>>) -> Option<Span> {
778    source.map(|source| Span::ranged(Arc::clone(source), source.leading_whitespace()))
779}
780
781/// Writes the layout the beginning-of-file span retains before the first cell.
782///
783/// The prefix is emitted only when it is the whitespace physically preceding
784/// the first cell, so a marker left before an edited or reassembled stream
785/// contributes nothing rather than stale text. Without cells, the
786/// end-of-file marker owns all layout.
787fn write_leading_layout(
788    formatter: &mut Formatter<'_>,
789    bof: Option<&Span>,
790    first_cell: Option<&Span>,
791) -> FmtResult {
792    let (Some(bof), Some(first)) = (bof, first_cell) else {
793        return Ok(());
794    };
795    if !first.shares_source(bof) {
796        return Ok(());
797    }
798    let Some((start, stop)) = bof.start().zip(first.start()) else {
799        return Ok(());
800    };
801    match bof.source().text().get(start..stop) {
802        Some(prefix) if prefix.bytes().all(|byte| byte.is_ascii_whitespace()) => {
803            formatter.write_str(prefix)
804        }
805        _ => Ok(()),
806    }
807}
808
809/// Spans the layout closing a source snapshot the cells were framed from.
810fn trailing_layout_span(source: Option<&Arc<Source>>) -> Option<Span> {
811    source.map(|source| Span::ranged(Arc::clone(source), source.trailing_whitespace()))
812}
813
814/// Writes the layout the end-of-file span retains after the final cell.
815///
816/// The suffix is emitted only when it is the whitespace physically following
817/// the last cell, so a marker left behind by an edited or reassembled stream
818/// contributes nothing rather than stale text.
819fn write_trailing_layout(
820    formatter: &mut Formatter<'_>,
821    eof: Option<&Span>,
822    last_cell: Option<&Span>,
823) -> FmtResult {
824    let Some(eof) = eof else {
825        return Ok(());
826    };
827    let resume = match last_cell {
828        Some(last) if last.shares_source(eof) => last.end(),
829        Some(_) => return Ok(()),
830        None => eof.start(),
831    };
832    let Some((resume, stop)) = resume.zip(eof.end()) else {
833        return Ok(());
834    };
835    match eof.source().text().get(resume..stop) {
836        Some(suffix) if suffix.bytes().all(|byte| byte.is_ascii_whitespace()) => {
837            formatter.write_str(suffix)
838        }
839        _ => Ok(()),
840    }
841}
842
843/// Handles cell first span.
844fn cell_first_span(tokens: &CellTokenStream) -> Option<Span> {
845    tokens
846        .data()
847        .prefix
848        .as_slice()
849        .first()
850        .map(Spanned::span)
851        .or_else(|| {
852            tokens.iter().next().map(|token| {
853                token
854                    .leading_trivia()
855                    .first()
856                    .map_or_else(|| token.span(), Spanned::span)
857            })
858        })
859        .or_else(|| tokens.data().suffix.as_slice().first().map(Spanned::span))
860}
861
862/// Handles cell last span.
863fn cell_last_span(tokens: &CellTokenStream) -> Option<Span> {
864    tokens
865        .data()
866        .suffix
867        .as_slice()
868        .last()
869        .map(Spanned::span)
870        .or_else(|| tokens.iter().next_back().map(token_end_span))
871        .or_else(|| tokens.data().prefix.as_slice().last().map(Spanned::span))
872}
873
874impl IntoIterator for CellStream {
875    type Item = CellTokenStream;
876    type IntoIter = IntoIter<CellTokenStream>;
877
878    fn into_iter(self) -> Self::IntoIter {
879        self.into_data().cells.into_iter()
880    }
881}
882
883impl<'syntax> IntoIterator for &'syntax CellStream {
884    type Item = &'syntax CellTokenStream;
885    type IntoIter = Iter<'syntax, CellTokenStream>;
886
887    fn into_iter(self) -> Self::IntoIter {
888        self.iter()
889    }
890}
891
892impl FromIterator<CellTokenStream> for CellStream {
893    fn from_iter<T: IntoIterator<Item = CellTokenStream>>(iter: T) -> Self {
894        Self::from_cells(iter)
895    }
896}
897
898/// Handles flatten cells.
899fn flatten_cells(cells: impl IntoIterator<Item = CellTokenStream>) -> TokenStream {
900    let mut output = TokenStream::new();
901    for cell in cells {
902        output.extend([cell.into_token_stream()]);
903    }
904    output
905}
906
907#[derive(Debug, Clone, PartialEq, Eq, Spanned)]
908/// An incomplete final cell encountered while framing a raw token stream.
909///
910/// The error's [`Spanned`] implementation points at the position where more
911/// input is required. It describes incompleteness only; malformed syntax that
912/// already has a structural boundary is diagnosed by a parser backend.
913pub struct CellStreamError {
914    /// The span.
915    span: Span,
916}
917
918impl Display for CellStreamError {
919    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
920        formatter.write_str("input ends inside an incomplete cell")
921    }
922}
923
924impl Error for CellStreamError {}
925
926/// Converts syntax or raw tokens into structural cells.
927///
928/// This boundary is intentionally distinct from [`Lex`]: most macros should
929/// preserve cell partitioning, while function-like expansion may explicitly
930/// choose a raw [`TokenStream`] when syntax is not valid before expansion.
931/// Implementations append to the supplied output rather than replacing its
932/// existing cells.
933///
934/// # Examples
935///
936/// ```
937/// use m2_syn::{CellStream, Tokenize, lex_str};
938///
939/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
940/// let first = lex_str("x = 1;")?;
941/// let second = lex_str("y = 2")?;
942/// let mut cells = CellStream::default();
943///
944/// first.tokenize(&mut cells)?;
945/// second.tokenize(&mut cells)?;
946///
947/// assert_eq!(cells.len(), 2);
948/// # Ok(())
949/// # }
950/// ```
951pub trait Tokenize {
952    /// Appends this value's independently parseable cells to `output`.
953    ///
954    /// # Errors
955    ///
956    /// Returns [`CellStreamError`] when a raw token prefix does not end at a
957    /// complete cell boundary.
958    fn tokenize(&self, output: &mut CellStream) -> Result<(), CellStreamError>;
959
960    /// Tokenizes this value into a fresh cell stream.
961    ///
962    /// This is the allocating convenience form of [`Self::tokenize`].
963    ///
964    /// # Errors
965    ///
966    /// Returns [`CellStreamError`] when a raw token prefix does not end at a
967    /// complete cell boundary.
968    fn to_cell_stream(&self) -> Result<CellStream, CellStreamError> {
969        let mut output = CellStream::default();
970        self.tokenize(&mut output)?;
971        Ok(output)
972    }
973}
974
975impl Tokenize for TokenStream {
976    fn tokenize(&self, output: &mut CellStream) -> Result<(), CellStreamError> {
977        let tokenized = CellStream::new(self.clone())?.into_data();
978        let output = output.data_mut();
979        if output.cells.is_empty() {
980            output.source = tokenized.source;
981            output.bof = tokenized.bof;
982            output.eof = tokenized.eof;
983        } else {
984            output.source = None;
985            output.eof = None;
986        }
987        output.cells.extend(tokenized.cells);
988        Ok(())
989    }
990}
991
992impl crate::SourceFile {
993    /// Places comments that follow every cell: on the end-of-file marker, or
994    /// on the file itself when it has no cells for them to follow.
995    pub(crate) fn set_closing_comments(&mut self, comments: Vec<Trivia>) {
996        if self.cells.is_empty() {
997            self.leading_trivia = LeadingTrivia::new(comments);
998        } else {
999            self.eof.set_leading_trivia(comments);
1000        }
1001    }
1002}
1003
1004impl Tokenize for crate::SourceFile {
1005    fn tokenize(&self, output: &mut CellStream) -> Result<(), CellStreamError> {
1006        let tokenized = CellStream::from_source_file(self).into_data();
1007        let output = output.data_mut();
1008        if output.cells.is_empty() {
1009            output.bof = tokenized.bof;
1010        }
1011        output.eof = output.cells.is_empty().then_some(tokenized.eof).flatten();
1012        output.cells.extend(tokenized.cells);
1013        output.source = None;
1014        Ok(())
1015    }
1016}
1017
1018impl Tokenize for crate::ParsedFile {
1019    fn tokenize(&self, output: &mut CellStream) -> Result<(), CellStreamError> {
1020        self.cst().tokenize(output)
1021    }
1022}
1023
1024impl<T: Tokenize + ?Sized> Tokenize for &T {
1025    fn tokenize(&self, output: &mut CellStream) -> Result<(), CellStreamError> {
1026        (*self).tokenize(output)
1027    }
1028}
1029
1030/// The recovering result of partitioning one raw stream into source cells.
1031///
1032/// Only the final source prefix can be incomplete: every confirmed boundary
1033/// before it is retained in [`Self::cells`]. This lets realtime and recovering
1034/// parsers reuse completed work while continuing to accumulate the tail.
1035pub(crate) struct FramedCells {
1036    /// Complete cells in their original source order.
1037    pub(crate) cells: Vec<CellTokenStream>,
1038    /// The final token prefix that still requires more input, when present.
1039    pub(crate) incomplete: Option<CellTokenStream>,
1040}
1041
1042/// Partitions raw tokens at confirmed top-level cell boundaries.
1043///
1044/// An unquoted top-level semicolon always confirms a boundary. A physical
1045/// newline confirms one only when [`newline_ends_cell`] says that the current
1046/// operator/clause state can end there. At end of input, a nonempty tail is
1047/// complete under the same rule unless its final delimiter group is unclosed;
1048/// otherwise the tail is returned through [`FramedCells::incomplete`]. Empty
1049/// or trivia-only input never becomes an incomplete cell.
1050///
1051/// The pass does not parse expressions or reject malformed grammar. It retains
1052/// comments and physical source spans, observes quote specifiers so their next
1053/// token cannot act as syntax, and inserts the zero-width adjacency punctuation
1054/// required by the native parser into both complete and incomplete results.
1055pub(crate) fn frame_recovering(tokens: TokenStream) -> FramedCells {
1056    let mut tokens = tokens;
1057    // Stream-level trivia must follow the first/last framed cell instead of
1058    // being lost when the significant tokens are distributed between cells.
1059    let prefix_trivia = tokens.take_prefix_trivia();
1060    let suffix_trivia = tokens.take_suffix_trivia();
1061    let mut cells = Vec::new();
1062    let mut current = TokenStream::new();
1063    current.extend(prefix_trivia.into_vec());
1064    let mut pending_boundary = false;
1065    let mut quote_next = false;
1066
1067    for token in tokens {
1068        // Delay a semicolon boundary until the next token so any stream-level
1069        // suffix trivia discovered at EOF remains owned by that final cell.
1070        if pending_boundary {
1071            cells.push(insert_adjacency(take(&mut current)));
1072            pending_boundary = false;
1073            quote_next = false;
1074        } else if current
1075            .last()
1076            .is_some_and(|left| tokens_cross_line_break(left, &token))
1077            && newline_ends_cell(&current)
1078        {
1079            cells.push(insert_adjacency(take(&mut current)));
1080            quote_next = false;
1081        }
1082
1083        let quoted = take(&mut quote_next);
1084        let quotes_next = !quoted
1085            && matches!(&token, TokenTree::Ident(identifier)
1086                if is_quote_specifier(canonical_word_spelling(identifier.text())));
1087        let semicolon = !quoted && token.spelling() == Some(";");
1088
1089        current.extend([token]);
1090        if semicolon {
1091            pending_boundary = true;
1092        }
1093        if quotes_next {
1094            quote_next = true;
1095        }
1096    }
1097
1098    current.extend(suffix_trivia.into_vec());
1099
1100    // A final semicolon confirms the current cell independently of its other
1101    // operator or delimiter state.
1102    if pending_boundary {
1103        cells.push(insert_adjacency(current));
1104        return FramedCells {
1105            cells,
1106            incomplete: None,
1107        };
1108    }
1109
1110    // Comments without syntax are complete retained input, not a request for
1111    // another line of source.
1112    if !has_syntax(&current) {
1113        if current.has_trivia() {
1114            cells.push(CellTokenStream::from_augmented(current));
1115        }
1116        return FramedCells {
1117            cells,
1118            incomplete: None,
1119        };
1120    }
1121
1122    // EOF behaves like a possible line boundary, except that an unclosed group
1123    // always keeps its containing cell recoverable.
1124    if !ends_in_incomplete_group(&current) && newline_ends_cell(&current) {
1125        cells.push(insert_adjacency(current));
1126        FramedCells {
1127            cells,
1128            incomplete: None,
1129        }
1130    } else {
1131        FramedCells {
1132            cells,
1133            incomplete: Some(insert_adjacency(current)),
1134        }
1135    }
1136}
1137
1138/// Handles end span.
1139pub(crate) fn end_span(tokens: &CellTokenStream) -> Span {
1140    let ends_with_semicolon = ends_with_cell_semicolon(tokens);
1141    let span = tokens
1142        .iter()
1143        .next_back()
1144        .map(token_end_span)
1145        .or_else(|| tokens.data().suffix.as_slice().last().map(Spanned::span))
1146        .or_else(|| tokens.data().prefix.as_slice().last().map(Spanned::span));
1147    let Some(span) = span else {
1148        return Span::for_text("");
1149    };
1150    let Some(end) = span.end() else {
1151        return Span::unlocated(Arc::clone(span.source()));
1152    };
1153    if !ends_with_semicolon && let Some(newline) = span.source().text()[end..].find('\n') {
1154        return span.at(end + newline + 1);
1155    }
1156    span.at(end)
1157}
1158
1159/// Handles token end span.
1160fn token_end_span(token: &CellTokenTree) -> Span {
1161    if let Some(trivia) = token.trailing_trivia().last() {
1162        return trivia.span();
1163    }
1164    if let CellTokenTree::Group(group) = token
1165        && !group.delimiter().is_closed()
1166    {
1167        let contents = group.stream();
1168        if let Some(inner) = contents.iter().next_back() {
1169            return token_end_span(inner);
1170        }
1171        if let Some(trivia) = contents
1172            .data()
1173            .suffix
1174            .as_slice()
1175            .last()
1176            .or_else(|| contents.data().prefix.as_slice().last())
1177        {
1178            return trivia.span();
1179        }
1180    }
1181    token.span()
1182}
1183
1184/// Returns whether the value ends with cell semicolon.
1185pub(crate) fn ends_with_cell_semicolon(tokens: &CellTokenStream) -> bool {
1186    let mut quote_next = false;
1187    let mut final_semicolon = false;
1188    for token in tokens.iter() {
1189        let quoted = take(&mut quote_next);
1190        final_semicolon = !quoted && token.spelling() == Some(";");
1191        quote_next = !quoted
1192            && matches!(token, CellTokenTree::Ident(identifier)
1193                if is_quote_specifier(canonical_word_spelling(identifier.text())));
1194    }
1195    final_semicolon
1196}
1197
1198/// Returns whether the value has syntax.
1199fn has_syntax(stream: &TokenStream) -> bool {
1200    !stream.is_empty()
1201}
1202
1203/// Returns whether the value ends in incomplete group.
1204fn ends_in_incomplete_group(stream: &TokenStream) -> bool {
1205    matches!(stream.iter().next_back(), Some(TokenTree::Group(group)) if !group.delimiter().is_closed())
1206}
1207
1208/// Inserts adjacency.
1209fn insert_adjacency(mut tokens: TokenStream) -> CellTokenStream {
1210    let prefix = tokens.take_prefix_trivia();
1211    let suffix = tokens.take_suffix_trivia();
1212    let mut output = TokenStream::new();
1213    output.extend(prefix.into_vec());
1214    let mut left_can_end = false;
1215    let mut quote_next = false;
1216
1217    for mut token in tokens {
1218        if let TokenTree::Group(group) = &mut token {
1219            let contents = take(group.stream_mut());
1220            *group.stream_mut() = insert_adjacency(contents).into_parser_token_stream();
1221        }
1222
1223        let quoted = take(&mut quote_next);
1224        let crossed_newline = output
1225            .last()
1226            .is_some_and(|left| tokens_cross_line_break(left, &token));
1227        if left_can_end && !quoted && !crossed_newline && can_begin_implicit_operand(&token) {
1228            let span = token.expect_span().subspan(0, 0);
1229            output.extend([TokenTree::Punct(Punct::from_span(span))]);
1230        }
1231
1232        let quotes_next = !quoted
1233            && matches!(&token, TokenTree::Ident(identifier)
1234                if is_quote_specifier(canonical_word_spelling(identifier.text())));
1235        left_can_end = quoted || can_end_operand(&token);
1236        quote_next = quotes_next;
1237        output.extend([token]);
1238    }
1239    for trivia in suffix.into_vec() {
1240        output.push_leading_trivia(trivia);
1241    }
1242    CellTokenStream::from_augmented(output)
1243}
1244
1245/// Handles tokens cross line break.
1246fn tokens_cross_line_break(left: &TokenTree, right: &TokenTree) -> bool {
1247    let left = left.expect_span();
1248    let right = right.expect_span();
1249    if !left.shares_source(&right) {
1250        return false;
1251    }
1252    let (Some(end), Some(start)) = (left.end(), right.start()) else {
1253        return false;
1254    };
1255    end <= start && left.source().text()[end..start].contains('\n')
1256}
1257
1258/// Handles token parse rule.
1259fn token_parse_rule(token: &TokenTree) -> Option<TokenParseRule> {
1260    let spelling = token.spelling().map(canonical_word_spelling)?;
1261    TOKEN_PARSE_RULES
1262        .binary_search_by_key(&spelling, |(declared, _)| *declared)
1263        .ok()
1264        .map(|index| TOKEN_PARSE_RULES[index].1)
1265}
1266
1267/// Returns whether the value can begin implicit operand.
1268fn can_begin_implicit_operand(token: &TokenTree) -> bool {
1269    match token_parse_rule(token) {
1270        Some(TokenParseRule::Prefix(_, _) | TokenParseRule::Unary(_, _)) => true,
1271        Some(_) => false,
1272        None => matches!(
1273            token,
1274            TokenTree::Ident(_) | TokenTree::Literal(_) | TokenTree::Group(_)
1275        ),
1276    }
1277}
1278
1279/// Returns whether the value can end operand.
1280fn can_end_operand(token: &TokenTree) -> bool {
1281    match token_parse_rule(token) {
1282        Some(TokenParseRule::Postfix(_)) => true,
1283        Some(_) => false,
1284        None => matches!(
1285            token,
1286            TokenTree::Ident(_) | TokenTree::Literal(_) | TokenTree::Group(_)
1287        ),
1288    }
1289}
1290
1291#[cfg(test)]
1292mod tests {
1293    use super::{Arc, CellStream};
1294    use crate::{CellTokenTree, TokenTree, Tokenize, TriviaKind, lex_str};
1295
1296    fn count_cell_adjacency(tokens: &[CellTokenTree]) -> usize {
1297        tokens
1298            .iter()
1299            .map(|token| match token {
1300                CellTokenTree::Group(group) => count_cell_adjacency(group.stream()),
1301                CellTokenTree::Adj(_) => 1,
1302                _ => 0,
1303            })
1304            .sum()
1305    }
1306
1307    fn count_raw_adjacency(tokens: &[TokenTree]) -> usize {
1308        tokens
1309            .iter()
1310            .map(|token| match token {
1311                TokenTree::Group(group) => count_raw_adjacency(group.stream()),
1312                TokenTree::Punct(punctuation) if punctuation.text().is_empty() => 1,
1313                _ => 0,
1314            })
1315            .sum()
1316    }
1317
1318    #[test]
1319    fn cells_are_structural_partitions() {
1320        let cells = CellStream::new(lex_str("f \t-- note\n x").unwrap()).unwrap();
1321        assert_eq!(cells.len(), 2);
1322        assert_eq!(cells.to_string(), "f \t-- note\n x");
1323    }
1324
1325    #[test]
1326    fn same_line_application_gets_one_empty_punctuation() {
1327        let cells = lex_str("f x").unwrap().to_cell_stream().unwrap();
1328        assert_eq!(
1329            cells
1330                .iter()
1331                .flat_map(|cell| cell.iter())
1332                .filter(|token| matches!(token, CellTokenTree::Adj(_)))
1333                .count(),
1334            1,
1335        );
1336    }
1337
1338    #[test]
1339    fn flattening_a_framed_cell_restores_lexical_tokens() {
1340        let raw = lex_str("f (g x)").unwrap();
1341        assert_eq!(count_raw_adjacency(&raw), 0);
1342
1343        let cells = raw.to_cell_stream().unwrap();
1344        assert_eq!(count_cell_adjacency(&cells[0]), 2);
1345
1346        let raw = cells.into_token_stream();
1347        assert_eq!(count_raw_adjacency(&raw), 0);
1348        assert_eq!(raw.to_string(), "f (g x)");
1349    }
1350
1351    #[test]
1352    fn line_comment_blocks_begin_the_cell_on_their_right() {
1353        let cells = CellStream::new(lex_str("x\n-- first\n-- second\ny").unwrap()).unwrap();
1354        assert_eq!(cells.len(), 2);
1355        let comments = cells[1].iter().next().unwrap().leading_trivia();
1356        assert_eq!(comments.len(), 1);
1357        assert_eq!(comments[0].kind(), TriviaKind::LineComment);
1358        assert_eq!(comments[0].text(), "-- first\n-- second");
1359    }
1360
1361    #[test]
1362    fn a_blank_line_separates_outer_comment_blocks() {
1363        let cells = CellStream::new(lex_str("-- first\n\n-- second\ny").unwrap()).unwrap();
1364        let comments = cells[0].iter().next().unwrap().leading_trivia();
1365
1366        assert_eq!(comments.len(), 2);
1367        assert_eq!(comments[0].text(), "-- first");
1368        assert_eq!(comments[1].text(), "-- second");
1369    }
1370
1371    #[test]
1372    fn line_comments_do_not_split_an_incomplete_cell() {
1373        let cells = CellStream::new(lex_str("x + -- reason\ny").unwrap()).unwrap();
1374        assert_eq!(cells.len(), 1);
1375        assert!(cells[0].iter().any(|token| {
1376            token
1377                .trailing_trivia()
1378                .iter()
1379                .any(|trivia| trivia.kind() == TriviaKind::LineComment)
1380        }));
1381    }
1382
1383    #[test]
1384    fn fork_copies_only_the_modified_cell_path() {
1385        let original = CellStream::new(lex_str("left; right").unwrap()).unwrap();
1386        let mut fork = original.fork();
1387
1388        assert!(Arc::ptr_eq(&original.0, &fork.0));
1389
1390        fork.data_mut().cells.pop();
1391
1392        assert!(!Arc::ptr_eq(&original.0, &fork.0));
1393        assert_eq!(original.to_string(), "left; right");
1394        assert_eq!(fork.to_string(), "left;");
1395        assert!(original[0].shares_storage(&fork[0]));
1396    }
1397}