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