1use std::error::Error;
10use std::fmt::{Display, Formatter, Result as FmtResult};
11use std::mem::take;
12use std::sync::Arc;
13use std::vec::IntoIter;
14
15#[cfg(feature = "tree-sitter")]
16use crate::CommentTrivia;
17use crate::lexer::{canonical_word_spelling, is_quote_specifier, newline_ends_cell};
18use crate::{
19 Lex, Punct, Source, Span, Spanned, TOKEN_PARSE_RULES, TokenParseRule, TokenStream, TokenTree,
20};
21
22#[derive(Debug, Clone, Default)]
23pub struct CellStream {
29 cells: Vec<TokenStream>,
30 source: Option<Arc<Source>>,
31}
32
33impl PartialEq for CellStream {
34 fn eq(&self, other: &Self) -> bool {
35 self.cells == other.cells
36 }
37}
38
39impl Eq for CellStream {}
40
41impl CellStream {
42 pub fn new(tokens: TokenStream) -> Result<Self, CellStreamError> {
49 let source = tokens.source();
50 let FramedCells { cells, incomplete } = frame_recovering(tokens);
51 match incomplete {
52 Some(tokens) => Err(CellStreamError {
53 span: end_span(&tokens),
54 }),
55 None => Ok(Self { cells, source }),
56 }
57 }
58
59 pub fn from_cells(cells: impl IntoIterator<Item = TokenStream>) -> Self {
61 Self::from_cells_with_source(cells, None)
62 }
63
64 pub(crate) fn from_cells_with_source(
65 cells: impl IntoIterator<Item = TokenStream>,
66 source: Option<Arc<Source>>,
67 ) -> Self {
68 Self {
69 cells: cells.into_iter().collect(),
70 source,
71 }
72 }
73
74 pub fn cells(&self) -> &[TokenStream] {
76 &self.cells
77 }
78
79 pub fn iter(&self) -> impl DoubleEndedIterator<Item = &TokenStream> + ExactSizeIterator {
81 self.cells.iter()
82 }
83
84 pub fn len(&self) -> usize {
86 self.cells.len()
87 }
88
89 pub fn is_empty(&self) -> bool {
91 self.cells.is_empty()
92 }
93
94 pub fn into_token_stream(self) -> TokenStream {
96 flatten_cells(self.cells)
97 }
98
99 pub fn to_token_stream(&self) -> TokenStream {
101 flatten_cells(self.cells.iter().cloned())
102 }
103
104 pub(crate) fn complete_source(&self) -> Option<Arc<Source>> {
105 let source = self.source.as_ref()?;
106 let mut cursor = 0;
107 for cell in &self.cells {
108 if !covers_stream(cell, source, &mut cursor) {
109 return None;
110 }
111 }
112 source.text()[cursor..]
113 .bytes()
114 .all(|byte| byte.is_ascii_whitespace())
115 .then(|| Arc::clone(source))
116 }
117
118 pub(crate) fn physical_eof(&self) -> Span {
119 if let Some(source) = self.complete_source() {
120 return Span::ranged(
121 Arc::clone(&source),
122 source.text().len()..source.text().len(),
123 );
124 }
125 self.cells
126 .last()
127 .map_or_else(|| Span::for_text(""), end_span)
128 }
129
130 #[cfg(feature = "tree-sitter")]
131 pub(crate) fn comment_ownership(&self) -> (Vec<CommentTrivia>, CommentTrivia) {
132 let mut cells = Vec::new();
133 let mut stream = CommentTrivia::default();
134 for tokens in &self.cells {
135 let mut tokens = tokens.clone();
136 if tokens.has_significant_tokens() {
137 cells.push(CommentTrivia::take_from_cell(&mut tokens));
138 } else {
139 stream.append(CommentTrivia::take_inner(&mut tokens));
140 }
141 }
142 (cells, stream)
143 }
144
145 pub(crate) fn from_source_file(source_file: &crate::SourceFile) -> Self {
146 let mut cells = source_file
147 .cells
148 .iter()
149 .map(Lex::to_token_stream)
150 .collect::<Vec<_>>();
151 if !source_file.comments.is_empty() {
152 if cells.is_empty() {
153 cells.push(TokenStream::new());
154 }
155 if let Some(cell) = cells.last_mut() {
156 source_file.comments.__apply_to(cell);
157 }
158 }
159 Self {
160 cells,
161 source: None,
162 }
163 }
164}
165
166fn covers_stream(stream: &TokenStream, source: &Arc<Source>, cursor: &mut usize) -> bool {
167 for trivia in stream.prefix_trivia() {
168 if !covers_span(trivia.span(), source, cursor) {
169 return false;
170 }
171 }
172 for token in stream.iter() {
173 for trivia in token.leading_trivia().as_slice() {
174 if !covers_span(trivia.span(), source, cursor) {
175 return false;
176 }
177 }
178 let covered = match token {
179 TokenTree::Group(group) => {
180 covers_span(group.delimiter().opening_span(), source, cursor)
181 && covers_stream(group.stream(), source, cursor)
182 && group
183 .delimiter()
184 .closing_span()
185 .is_none_or(|span| covers_span(span, source, cursor))
186 }
187 _ => covers_span(token.span(), source, cursor),
188 };
189 if !covered {
190 return false;
191 }
192 for trivia in token.trailing_trivia().as_slice() {
193 if !covers_span(trivia.span(), source, cursor) {
194 return false;
195 }
196 }
197 }
198 stream
199 .suffix_trivia()
200 .iter()
201 .all(|trivia| covers_span(trivia.span(), source, cursor))
202}
203
204fn covers_span(span: Span, source: &Arc<Source>, cursor: &mut usize) -> bool {
205 if !Arc::ptr_eq(span.source(), source) {
206 return false;
207 }
208 let (Some(start), Some(end)) = (span.start(), span.end()) else {
209 return false;
210 };
211 if start == end {
212 return start >= *cursor;
213 }
214 if start < *cursor
215 || !source.text()[*cursor..start]
216 .bytes()
217 .all(|byte| byte.is_ascii_whitespace())
218 {
219 return false;
220 }
221 *cursor = end;
222 true
223}
224
225impl Display for CellStream {
226 fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
227 let mut previous: Option<Span> = None;
228 for (index, cell) in self.cells.iter().enumerate() {
229 if index != 0 {
230 let next = cell_first_span(cell);
231 let mut recovered = false;
232 if let Some((left, right)) = previous.as_ref().zip(next.as_ref())
233 && left.shares_source(right)
234 && let (Some(end), Some(start)) = (left.end(), right.start())
235 && end <= start
236 {
237 let gap = &left.source().text()[end..start];
238 if !gap.is_empty() && gap.bytes().all(|byte| byte.is_ascii_whitespace()) {
239 formatter.write_str(gap)?;
240 recovered = true;
241 }
242 }
243 if !recovered {
244 formatter.write_str("\n")?;
245 }
246 }
247 cell.fmt(formatter)?;
248 previous = cell_last_span(cell);
249 }
250 Ok(())
251 }
252}
253
254fn cell_first_span(tokens: &TokenStream) -> Option<Span> {
255 tokens
256 .prefix_trivia()
257 .first()
258 .map(Spanned::span)
259 .or_else(|| {
260 tokens.iter().next().map(|token| {
261 token
262 .leading_trivia()
263 .as_slice()
264 .first()
265 .map_or_else(|| token.span(), Spanned::span)
266 })
267 })
268 .or_else(|| tokens.suffix_trivia().first().map(Spanned::span))
269}
270
271fn cell_last_span(tokens: &TokenStream) -> Option<Span> {
272 tokens
273 .suffix_trivia()
274 .last()
275 .map(Spanned::span)
276 .or_else(|| tokens.iter().next_back().map(token_end_span))
277 .or_else(|| tokens.prefix_trivia().last().map(Spanned::span))
278}
279
280impl IntoIterator for CellStream {
281 type Item = TokenStream;
282 type IntoIter = IntoIter<TokenStream>;
283
284 fn into_iter(self) -> Self::IntoIter {
285 self.cells.into_iter()
286 }
287}
288
289impl<'syntax> IntoIterator for &'syntax CellStream {
290 type Item = &'syntax TokenStream;
291 type IntoIter = std::slice::Iter<'syntax, TokenStream>;
292
293 fn into_iter(self) -> Self::IntoIter {
294 self.cells.iter()
295 }
296}
297
298impl FromIterator<TokenStream> for CellStream {
299 fn from_iter<T: IntoIterator<Item = TokenStream>>(iter: T) -> Self {
300 Self::from_cells(iter)
301 }
302}
303
304fn flatten_cells(cells: impl IntoIterator<Item = TokenStream>) -> TokenStream {
305 let mut output = TokenStream::new();
306 for cell in cells {
307 output.extend([cell]);
308 }
309 output
310}
311
312#[derive(Debug, Clone, PartialEq, Eq, Spanned)]
313pub struct CellStreamError {
315 span: Span,
316}
317
318impl Display for CellStreamError {
319 fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
320 formatter.write_str("input ends inside an incomplete cell")
321 }
322}
323
324impl Error for CellStreamError {}
325
326pub trait Tokenize {
332 fn tokenize(&self, output: &mut CellStream) -> Result<(), CellStreamError>;
339
340 fn to_cell_stream(&self) -> Result<CellStream, CellStreamError> {
347 let mut output = CellStream::default();
348 self.tokenize(&mut output)?;
349 Ok(output)
350 }
351}
352
353impl Tokenize for TokenStream {
354 fn tokenize(&self, output: &mut CellStream) -> Result<(), CellStreamError> {
355 let tokenized = CellStream::new(self.clone())?;
356 if output.cells.is_empty() {
357 output.source = tokenized.source;
358 } else {
359 output.source = None;
360 }
361 output.cells.extend(tokenized.cells);
362 Ok(())
363 }
364}
365
366impl Tokenize for crate::SourceFile {
367 fn tokenize(&self, output: &mut CellStream) -> Result<(), CellStreamError> {
368 output
369 .cells
370 .extend(CellStream::from_source_file(self).cells);
371 output.source = None;
372 Ok(())
373 }
374}
375
376impl Tokenize for crate::ParsedFile {
377 fn tokenize(&self, output: &mut CellStream) -> Result<(), CellStreamError> {
378 self.cst().tokenize(output)
379 }
380}
381
382impl<T: Tokenize + ?Sized> Tokenize for &T {
383 fn tokenize(&self, output: &mut CellStream) -> Result<(), CellStreamError> {
384 (*self).tokenize(output)
385 }
386}
387
388pub(crate) struct FramedCells {
389 pub(crate) cells: Vec<TokenStream>,
390 pub(crate) incomplete: Option<TokenStream>,
391}
392
393pub(crate) fn frame_recovering(tokens: TokenStream) -> FramedCells {
394 let mut tokens = tokens;
395 let prefix_trivia = tokens.take_prefix_trivia();
396 let suffix_trivia = tokens.take_suffix_trivia();
397 let mut cells = Vec::new();
398 let mut current = TokenStream::new();
399 current.extend(prefix_trivia.into_vec());
400 let mut pending_boundary = false;
401 let mut quote_next = false;
402
403 for token in tokens {
404 if pending_boundary {
405 cells.push(insert_adjacency(take(&mut current)));
406 pending_boundary = false;
407 quote_next = false;
408 } else if current
409 .last()
410 .is_some_and(|left| tokens_cross_line_break(left, &token))
411 && newline_ends_cell(¤t)
412 {
413 cells.push(insert_adjacency(take(&mut current)));
414 quote_next = false;
415 }
416
417 let quoted = take(&mut quote_next);
418 let quotes_next = !quoted
419 && matches!(&token, TokenTree::Ident(identifier)
420 if is_quote_specifier(canonical_word_spelling(identifier.text())));
421 let semicolon = !quoted && token.spelling() == Some(";");
422
423 current.extend([token]);
424 if semicolon {
425 pending_boundary = true;
426 }
427 if quotes_next {
428 quote_next = true;
429 }
430 }
431
432 current.extend(suffix_trivia.into_vec());
433
434 if pending_boundary {
435 cells.push(insert_adjacency(current));
436 return FramedCells {
437 cells,
438 incomplete: None,
439 };
440 }
441
442 if !has_syntax(¤t) {
443 if current.has_trivia() {
444 cells.push(current);
445 }
446 return FramedCells {
447 cells,
448 incomplete: None,
449 };
450 }
451
452 if !ends_in_incomplete_group(¤t) && newline_ends_cell(¤t) {
453 cells.push(insert_adjacency(current));
454 FramedCells {
455 cells,
456 incomplete: None,
457 }
458 } else {
459 FramedCells {
460 cells,
461 incomplete: Some(insert_adjacency(current)),
462 }
463 }
464}
465
466pub(crate) fn end_span(tokens: &TokenStream) -> Span {
467 let span = tokens
468 .iter()
469 .next_back()
470 .map(token_end_span)
471 .or_else(|| tokens.suffix_trivia().last().map(Spanned::span))
472 .or_else(|| tokens.prefix_trivia().last().map(Spanned::span));
473 let Some(span) = span else {
474 return Span::for_text("");
475 };
476 let Some(end) = span.end() else {
477 return Span::unlocated(Arc::clone(span.source()));
478 };
479 if !ends_with_cell_semicolon(tokens)
480 && let Some(newline) = span.source().text()[end..].find('\n')
481 {
482 return span.at(end + newline + 1);
483 }
484 span.at(end)
485}
486
487fn token_end_span(token: &TokenTree) -> Span {
488 if let Some(trivia) = token.trailing_trivia().as_slice().last() {
489 return trivia.span();
490 }
491 if let TokenTree::Group(group) = token
492 && !group.delimiter().is_closed()
493 && let Some(inner) = group.stream().iter().next_back()
494 {
495 return token_end_span(inner);
496 }
497 token.span()
498}
499
500pub(crate) fn ends_with_cell_semicolon(tokens: &TokenStream) -> bool {
501 let mut quote_next = false;
502 let mut final_semicolon = false;
503 for token in tokens.iter() {
504 let quoted = take(&mut quote_next);
505 final_semicolon = !quoted && token.spelling() == Some(";");
506 quote_next = !quoted
507 && matches!(token, TokenTree::Ident(identifier)
508 if is_quote_specifier(canonical_word_spelling(identifier.text())));
509 }
510 final_semicolon
511}
512
513fn has_syntax(stream: &TokenStream) -> bool {
514 !stream.is_empty()
515}
516
517fn ends_in_incomplete_group(stream: &TokenStream) -> bool {
518 matches!(stream.iter().next_back(), Some(TokenTree::Group(group)) if !group.delimiter().is_closed())
519}
520
521fn insert_adjacency(mut tokens: TokenStream) -> TokenStream {
522 let prefix = tokens.take_prefix_trivia();
523 let suffix = tokens.take_suffix_trivia();
524 let physical_eof = tokens.take_physical_eof();
525 let mut output = TokenStream::new();
526 output.extend(prefix.into_vec());
527 let mut left_can_end = false;
528 let mut quote_next = false;
529
530 for mut token in tokens {
531 if let TokenTree::Group(group) = &mut token {
532 let contents = take(group.stream_mut());
533 *group.stream_mut() = insert_adjacency(contents);
534 }
535
536 let quoted = take(&mut quote_next);
537 let crossed_newline = output
538 .last()
539 .is_some_and(|left| tokens_cross_line_break(left, &token));
540 if left_can_end && !quoted && !crossed_newline && can_begin_implicit_operand(&token) {
541 let span = token.expect_span().subspan(0, 0);
542 output.extend([TokenTree::Punct(Punct::from_span(span))]);
543 }
544
545 let quotes_next = !quoted
546 && matches!(&token, TokenTree::Ident(identifier)
547 if is_quote_specifier(canonical_word_spelling(identifier.text())));
548 left_can_end = quoted || can_end_operand(&token);
549 quote_next = quotes_next;
550 output.extend([token]);
551 }
552 for trivia in suffix.into_vec() {
553 output.push_leading_trivia(trivia);
554 }
555 if let Some(physical_eof) = physical_eof {
556 output.set_physical_eof(physical_eof);
557 }
558 output
559}
560
561fn tokens_cross_line_break(left: &TokenTree, right: &TokenTree) -> bool {
562 let left = left.expect_span();
563 let right = right.expect_span();
564 if !left.shares_source(&right) {
565 return false;
566 }
567 let (Some(end), Some(start)) = (left.end(), right.start()) else {
568 return false;
569 };
570 end <= start && left.source().text()[end..start].contains('\n')
571}
572
573fn token_parse_rule(token: &TokenTree) -> Option<TokenParseRule> {
574 let spelling = token.spelling().map(canonical_word_spelling)?;
575 TOKEN_PARSE_RULES
576 .binary_search_by_key(&spelling, |(declared, _)| *declared)
577 .ok()
578 .map(|index| TOKEN_PARSE_RULES[index].1)
579}
580
581fn can_begin_implicit_operand(token: &TokenTree) -> bool {
582 match token_parse_rule(token) {
583 Some(TokenParseRule::Prefix(_, _) | TokenParseRule::Unary(_, _)) => true,
584 Some(_) => false,
585 None => matches!(
586 token,
587 TokenTree::Ident(_) | TokenTree::Literal(_) | TokenTree::Group(_)
588 ),
589 }
590}
591
592fn can_end_operand(token: &TokenTree) -> bool {
593 match token_parse_rule(token) {
594 Some(TokenParseRule::Postfix(_)) => true,
595 Some(_) => false,
596 None => matches!(
597 token,
598 TokenTree::Ident(_) | TokenTree::Literal(_) | TokenTree::Group(_)
599 ),
600 }
601}
602
603#[cfg(test)]
604mod tests {
605 use super::CellStream;
606 use crate::{TokenStream, TokenTree, Tokenize, TriviaKind, lex_str};
607
608 #[test]
609 fn cells_are_structural_partitions() {
610 let cells = CellStream::new(lex_str("f \t-- note\n x").unwrap()).unwrap();
611 assert_eq!(cells.len(), 2);
612 assert_eq!(cells.to_string(), "f \t-- note\n x");
613 }
614
615 #[test]
616 fn same_line_application_gets_one_empty_punctuation() {
617 let cells = lex_str("f x").unwrap().to_cell_stream().unwrap();
618 assert_eq!(
619 cells
620 .iter()
621 .flat_map(TokenStream::iter)
622 .filter(|token| matches!(token, TokenTree::Punct(punct) if punct.text().is_empty()))
623 .count(),
624 1,
625 );
626 }
627
628 #[test]
629 fn line_comment_blocks_begin_the_cell_on_their_right() {
630 let cells = CellStream::new(lex_str("x\n-- first\n-- second\ny").unwrap()).unwrap();
631 assert_eq!(cells.len(), 2);
632 let comments = cells.cells()[1]
633 .iter()
634 .next()
635 .unwrap()
636 .leading_trivia()
637 .as_slice();
638 assert_eq!(comments.len(), 1);
639 assert_eq!(comments[0].kind(), TriviaKind::LineComment);
640 assert_eq!(comments[0].text(), "-- first\n-- second");
641 }
642
643 #[test]
644 fn a_blank_line_separates_outer_comment_blocks() {
645 let cells = CellStream::new(lex_str("-- first\n\n-- second\ny").unwrap()).unwrap();
646 let comments = cells.cells()[0]
647 .iter()
648 .next()
649 .unwrap()
650 .leading_trivia()
651 .as_slice();
652
653 assert_eq!(comments.len(), 2);
654 assert_eq!(comments[0].text(), "-- first");
655 assert_eq!(comments[1].text(), "-- second");
656 }
657
658 #[test]
659 fn line_comments_do_not_split_an_incomplete_cell() {
660 let cells = CellStream::new(lex_str("x + -- reason\ny").unwrap()).unwrap();
661 assert_eq!(cells.len(), 1);
662 assert!(cells.cells()[0].iter().any(|token| {
663 token
664 .trailing_trivia()
665 .as_slice()
666 .iter()
667 .any(|trivia| trivia.kind() == TriviaKind::LineComment)
668 }));
669 }
670}