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