Skip to main content

m2_syn/parser/
parse.rs

1//! Parsing boundaries over the shared raw token representation.
2//!
3//! [`Parser`] consumes a whole [`TokenStream`] and is the backend interface.
4//! [`Parse`] consumes one value from a [`ParseStream`] and is the generated
5//! dual of [`crate::Lex`]. Both native parsing and generated token parsing use the
6//! same cursor facts instead of maintaining parallel token arrays.
7
8use std::error::Error;
9use std::fmt::{Display, Formatter};
10use std::iter::FusedIterator;
11use std::rc::Rc;
12
13use crate::lexer::split_cells;
14use crate::{
15    ExternalCstNode, IdentToken, LexError, LiteralToken, Punct, Reconstruct, ReconstructError,
16    SourceFile, Span, Spanned, Token, TokenGroup, TokenStream, TokenTree, Trivia, lex_str,
17};
18
19/// A parser backend that produces one typed syntax target.
20///
21/// Backends may construct `T` directly or adapt their concrete syntax tree
22/// through [`ExternalCstNode`] and [`reconstruct`].
23pub trait Parser<T = SourceFile> {
24    /// The backend-specific parse failure.
25    type Error: Error;
26
27    /// Parses `tokens` into one typed syntax target.
28    ///
29    /// # Errors
30    ///
31    /// Returns the backend's [`Self::Error`] when it cannot produce `T`.
32    fn parse_stream(&mut self, tokens: TokenStream) -> Result<T, Self::Error>;
33}
34
35/// Parses an existing token stream with an explicitly selected backend.
36///
37/// # Errors
38///
39/// Returns the selected backend's error when parsing fails.
40pub fn parse_with<T, P>(parser: &mut P, tokens: TokenStream) -> Result<T, P::Error>
41where
42    P: Parser<T>,
43{
44    parser.parse_stream(tokens)
45}
46
47/// A typed syntax value that can consume itself from an M2 token cursor.
48pub trait Parse: Sized {
49    /// Consumes this value from `input`.
50    ///
51    /// # Errors
52    ///
53    /// Returns [`TokenParseError`] when the next tokens do not encode `Self`.
54    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError>;
55}
56
57impl<T: Parse> Parse for Box<T> {
58    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
59        T::parse(input).map(Self::new)
60    }
61}
62
63impl Parse for TokenTree {
64    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
65        input
66            .next_raw_token()
67            .ok_or_else(|| TokenParseError::unexpected_end("a token tree", input.eof_span()))
68    }
69}
70
71macro_rules! parse_raw_variant {
72    ($ty:ty, $variant:ident, $expected:literal) => {
73        impl Parse for $ty {
74            fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
75                let token = TokenTree::parse(input)?;
76                match token {
77                    TokenTree::$variant(value) => Ok(value),
78                    token => Err(TokenParseError::unexpected_token($expected, &token)),
79                }
80            }
81        }
82    };
83}
84
85parse_raw_variant!(TokenGroup, Group, "a delimited group");
86parse_raw_variant!(LiteralToken, Literal, "a literal");
87parse_raw_variant!(Punct, Punct, "punctuation");
88parse_raw_variant!(IdentToken, Ident, "an identifier");
89parse_raw_variant!(Trivia, Trivia, "trivia");
90
91impl Parse for TokenStream {
92    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
93        Ok(input.by_ref().collect())
94    }
95}
96
97/// Parses one complete token stream as `T`.
98///
99/// # Errors
100///
101/// Returns [`TokenParseError`] when `T` cannot be parsed or leaves trailing
102/// tokens.
103pub fn parse2<T: Parse>(tokens: TokenStream) -> Result<T, TokenParseError> {
104    let mut input = ParseStream::new(tokens);
105    let value = T::parse(&mut input)?;
106    input.finish()?;
107    Ok(value)
108}
109
110/// Lexes one source cell and parses it as a typed syntax fragment.
111///
112/// This applies the grammar expressed by `T`'s [`Parse`] implementation. It
113/// does not validate the fragment against the complete Macaulay2 grammar; use
114/// [`crate::parse_native`] or [`crate::parse_file`] when parsing a source file.
115/// A delimited fragment containing semicolons is still one cell because cell
116/// boundaries are recognized only at the top level.
117///
118/// # Errors
119///
120/// Returns [`FragmentParseError`] when lexing fails, the source contains
121/// multiple top-level cells, or the cell does not parse as `T`.
122pub fn parse_fragment_str<T: Parse>(source: &str) -> Result<T, FragmentParseError> {
123    let mut cells = split_cells(lex_str(source)?);
124    if cells.len() > 1 {
125        return Err(FragmentParseError::MultipleCells { count: cells.len() });
126    }
127    let tokens = cells
128        .pop()
129        .map_or_else(TokenStream::new, |cell| cell.stream);
130    parse2(tokens).map_err(Into::into)
131}
132
133/// An error produced while lexing or parsing one typed syntax fragment.
134#[derive(Debug, Clone, PartialEq, Eq)]
135pub enum FragmentParseError {
136    /// The source could not be converted into token trees.
137    Lex(LexError),
138    /// The token trees did not match the requested fragment type.
139    Parse(TokenParseError),
140    /// The source contained top-level cell boundaries and therefore was not a
141    /// single fragment.
142    MultipleCells {
143        /// The number of cells produced by the lexer.
144        count: usize,
145    },
146}
147
148impl Display for FragmentParseError {
149    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
150        match self {
151            Self::Lex(error) => error.fmt(formatter),
152            Self::Parse(error) => error.fmt(formatter),
153            Self::MultipleCells { count } => {
154                write!(formatter, "expected one source cell, found {count}")
155            }
156        }
157    }
158}
159
160impl Error for FragmentParseError {
161    fn source(&self) -> Option<&(dyn Error + 'static)> {
162        match self {
163            Self::Lex(error) => Some(error),
164            Self::Parse(error) => Some(error),
165            Self::MultipleCells { .. } => None,
166        }
167    }
168}
169
170impl From<LexError> for FragmentParseError {
171    fn from(error: LexError) -> Self {
172        Self::Lex(error)
173    }
174}
175
176impl From<TokenParseError> for FragmentParseError {
177    fn from(error: TokenParseError) -> Self {
178        Self::Parse(error)
179    }
180}
181
182/// One non-trivia token together with facts about the trivia immediately
183/// before it. Parser backends share this view instead of maintaining their own
184/// token vectors, cursor positions, and newline scanners.
185#[derive(Debug, Clone)]
186pub(crate) struct SignificantToken {
187    pub(crate) token: TokenTree,
188    pub(crate) crossed_newline: bool,
189}
190
191#[derive(Debug, Clone)]
192pub(crate) enum Lookahead {
193    Token(SignificantToken),
194    End(Span),
195}
196
197impl Spanned for Lookahead {
198    fn span(&self) -> Span {
199        match self {
200            Self::Token(token) => token.token.span(),
201            Self::End(span) => span.clone(),
202        }
203    }
204}
205
206/// An owned, cheaply forkable cursor over one immutable token stream.
207///
208/// Forks share the token storage through [`Rc`] and copy only their current
209/// position. Advancing a fork never changes the cursor from which it came.
210#[derive(Clone)]
211pub struct ParseStream {
212    tokens: Rc<TokenStream>,
213    position: usize,
214    /// The zero-width position just past the last token.
215    ///
216    /// End of input is the absence of a token, which the cursor already
217    /// reports. Only the position needs carrying, for diagnostics that point
218    /// at where input ran out.
219    eof_span: Span,
220}
221
222impl ParseStream {
223    /// Creates a cursor at the beginning of `tokens`.
224    pub fn new(tokens: TokenStream) -> Self {
225        let eof_span = tokens
226            .last()
227            .and_then(|token| {
228                let end = token.span().end()?;
229                Some(Span::from_offset(end))
230            })
231            .unwrap_or_else(Span::detached);
232        Self {
233            tokens: Rc::new(tokens),
234            position: 0,
235            eof_span,
236        }
237    }
238
239    /// Creates an independent cursor over the same immutable token storage.
240    pub fn fork(&self) -> Self {
241        self.clone()
242    }
243
244    /// Commits this cursor to a position reached by one of its forks.
245    ///
246    /// # Panics
247    ///
248    /// Panics if `fork` belongs to another token stream.
249    pub fn advance_to(&mut self, fork: &Self) {
250        assert!(
251            Rc::ptr_eq(&self.tokens, &fork.tokens),
252            "cannot advance to a cursor from another token stream"
253        );
254        self.position = fork.position;
255    }
256
257    /// Returns the next non-trivia token without consuming it.
258    pub fn peek(&self) -> Option<&TokenTree> {
259        self.tokens().get(self.significant_position())
260    }
261
262    pub(crate) fn lookahead(&self) -> Lookahead {
263        let mut position = self.position;
264        let mut crossed_newline = false;
265        while let Some(TokenTree::Trivia(trivia)) = self.tokens().get(position) {
266            crossed_newline |= trivia.contains_line_break();
267            position += 1;
268        }
269        let Some(token) = self.tokens().get(position).cloned() else {
270            return Lookahead::End(self.eof_span.clone());
271        };
272        Lookahead::Token(SignificantToken {
273            token,
274            crossed_newline,
275        })
276    }
277
278    pub(crate) fn consume_lookahead(&mut self) -> Lookahead {
279        let lookahead = self.lookahead();
280        if matches!(lookahead, Lookahead::Token(_)) {
281            self.set_position(self.significant_position() + 1);
282        }
283        lookahead
284    }
285
286    pub(crate) fn skip_trivia(&mut self) {
287        self.set_position(self.significant_position());
288    }
289
290    pub(crate) fn eof_span(&self) -> Span {
291        self.eof_span.clone()
292    }
293
294    /// Consumes and returns the next non-trivia token.
295    pub fn next_token(&mut self) -> Option<TokenTree> {
296        self.set_position(self.significant_position());
297        self.next_raw_token()
298    }
299
300    /// Consumes the next token, including trivia.
301    pub fn next_raw_token(&mut self) -> Option<TokenTree> {
302        self.next()
303    }
304
305    /// Parses one generated typed token from its matching raw atom.
306    ///
307    /// # Errors
308    ///
309    /// Returns [`TokenParseError`] when input ends or the next significant token does
310    /// not match `T`.
311    pub fn parse_token<T: Token>(&mut self) -> Result<T, TokenParseError> {
312        let token = self.next_token();
313        let Some(token) = token else {
314            return Err(TokenParseError::unexpected_end(
315                T::SPELLING,
316                self.eof_span(),
317            ));
318        };
319        let error = TokenParseError::unexpected_token(T::SPELLING, &token);
320        T::from_token_tree(token).ok_or(error)
321    }
322
323    /// Returns whether no significant token remains.
324    pub fn is_eof(&self) -> bool {
325        self.peek().is_none()
326    }
327
328    fn significant_position(&self) -> usize {
329        let mut position = self.position;
330        while matches!(self.tokens().get(position), Some(TokenTree::Trivia(_))) {
331            position += 1;
332        }
333        position
334    }
335
336    fn tokens(&self) -> &TokenStream {
337        &self.tokens
338    }
339
340    fn set_position(&mut self, position: usize) {
341        self.position = position;
342    }
343
344    fn finish(&mut self) -> Result<(), TokenParseError> {
345        for token in self.by_ref() {
346            if matches!(token, TokenTree::Trivia(_) | TokenTree::Eoc(_)) {
347                continue;
348            }
349            return Err(TokenParseError::TrailingToken {
350                found: token.description(),
351                span: token.span(),
352            });
353        }
354        Ok(())
355    }
356}
357
358impl Iterator for ParseStream {
359    type Item = TokenTree;
360
361    fn next(&mut self) -> Option<Self::Item> {
362        let token = self.tokens().get(self.position)?.clone();
363        self.position += 1;
364        Some(token)
365    }
366
367    fn size_hint(&self) -> (usize, Option<usize>) {
368        let remaining = self.tokens().len().saturating_sub(self.position);
369        (remaining, Some(remaining))
370    }
371}
372
373impl ExactSizeIterator for ParseStream {}
374impl FusedIterator for ParseStream {}
375
376#[derive(Debug, Clone, PartialEq, Eq, Spanned)]
377/// A failure while consuming typed syntax from a [`ParseStream`].
378pub enum TokenParseError {
379    /// Input ended before the required syntax appeared.
380    UnexpectedEnd {
381        /// The expected syntax description or spelling.
382        expected: &'static str,
383        /// The end-of-input position.
384        span: Span,
385    },
386    /// The next token did not match the required syntax.
387    UnexpectedToken {
388        /// The expected syntax description or spelling.
389        expected: &'static str,
390        /// A readable description of the received token.
391        found: String,
392        /// The received token span.
393        span: Span,
394    },
395    /// A complete parse left a significant token unconsumed.
396    TrailingToken {
397        /// A readable description of the remaining token.
398        found: String,
399        /// The remaining token span.
400        span: Span,
401    },
402    /// A parser backend reported a higher-level syntax failure.
403    Syntax {
404        /// The backend's diagnostic message.
405        message: String,
406        /// The syntax associated with the failure.
407        span: Span,
408    },
409}
410
411impl TokenParseError {
412    pub(crate) fn unexpected_end(expected: &'static str, span: Span) -> Self {
413        Self::UnexpectedEnd { expected, span }
414    }
415
416    pub(crate) fn unexpected(expected: &'static str, found: impl Into<String>, span: Span) -> Self {
417        Self::UnexpectedToken {
418            expected,
419            found: found.into(),
420            span,
421        }
422    }
423
424    pub(crate) fn unexpected_token(expected: &'static str, token: &TokenTree) -> Self {
425        Self::unexpected(expected, token.description(), token.span())
426    }
427}
428
429impl Display for TokenParseError {
430    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
431        match self {
432            Self::UnexpectedEnd { expected, .. } => {
433                write!(formatter, "expected `{expected}`, found the end of input")
434            }
435            Self::UnexpectedToken {
436                expected, found, ..
437            } => write!(formatter, "expected `{expected}`, found `{found}`"),
438            Self::TrailingToken { found, .. } => {
439                write!(formatter, "expected the end of input, found `{found}`")
440            }
441            Self::Syntax { message, .. } => formatter.write_str(message),
442        }
443    }
444}
445
446impl Error for TokenParseError {}
447
448/// Reconstructs a typed syntax target from a parser-specific CST root.
449///
450/// # Errors
451///
452/// Returns [`ReconstructError`] when the root cannot reconstruct `T`.
453pub fn reconstruct<T, N>(root: N) -> Result<T, ReconstructError>
454where
455    N: ExternalCstNode,
456    T: Reconstruct<N>,
457{
458    T::reconstruct(root)
459}
460
461#[cfg(test)]
462mod tests {
463    use super::*;
464    use crate::{LiteralKind, quote_m2};
465
466    #[test]
467    fn parse_stream_iteration_excludes_its_eof_token() {
468        let mut input = ParseStream::new(quote_m2!(name + 1));
469        assert_eq!(input.len(), 3);
470        let emitted = input.by_ref().collect::<TokenStream>();
471
472        assert_eq!(emitted.to_string(), "name+1");
473        assert!(input.is_eof());
474        assert_eq!(input.next(), None);
475        assert_eq!(input.next(), None);
476    }
477
478    #[test]
479    fn exact_size_counts_every_remaining_token() {
480        let mut input = ParseStream::new(quote_m2!(name));
481
482        assert_eq!(input.len(), 1);
483        assert!(input.next().is_some());
484        assert_eq!(input.len(), 0);
485    }
486
487    #[test]
488    fn raw_token_categories_implement_parse() {
489        let identifier = parse2::<IdentToken>(quote_m2!(name)).unwrap();
490        let literal = parse2::<LiteralToken>(quote_m2!(1)).unwrap();
491        let punctuation = parse2::<Punct>(quote_m2!(+)).unwrap();
492        let group = parse2::<TokenGroup>(quote_m2!((name))).unwrap();
493
494        assert_eq!(identifier.text(), "name");
495        assert_eq!(literal.kind, LiteralKind::Integer);
496        assert_eq!(punctuation.text(), "+");
497        assert_eq!(group.to_string(), "(name)");
498    }
499
500    #[test]
501    fn lookahead_reports_the_stored_eof_span_through_spanned() {
502        let tokens = lex_str("name").unwrap();
503        let mut input = ParseStream::new(tokens);
504        input.next_token();
505
506        let span = input.lookahead().span();
507        assert_eq!(span.start().unwrap(), "name".len());
508        assert_eq!(span, input.eof_span());
509    }
510}