Skip to main content

m2_syn/parser/
native.rs

1//! Direct construction of the typed graph from a flat token stream.
2//!
3//! The native precedence engine owns parsing decisions only. Token storage,
4//! cursor position, trivia skipping, and newline detection belong to
5//! [`ParseStream`], shared with generated [`Parse`]
6//! implementations.
7
8use std::error::Error;
9use std::fmt::{Display, Formatter};
10
11use crate::cell_stream::{end_span, ends_with_cell_semicolon, frame_recovering};
12use crate::lexer::canonical_word_spelling;
13use crate::nodes::*;
14use crate::parse::{Lookahead, SignificantToken};
15use crate::{
16    CellStream, CellStreamError, CommentTrivia, Delimited, DelimiterKind, DelimiterToken,
17    DelimiterValue, LexError, LexErrorKind, LiteralKind, Parse, ParseStream, Parser, Punctuated,
18    Recovered, Span, Spanned, Terminal, Token, TokenParseError, TokenStream, TokenTree, Tokenize,
19    TrailingTrivia, lex_str, lex_str_recovering,
20};
21
22macro_rules! is_token {
23    ($token:expr, $($pattern:tt)*) => {
24        <Token![$($pattern)*] as Token>::matches_token_tree($token)
25    };
26}
27
28macro_rules! unexpected {
29    ($token:expr, $expected:expr) => {{
30        let token = $token;
31        NativeParseError::Unexpected {
32            span: token.expect_span(),
33            found: token.description(),
34            expected: $expected,
35        }
36    }};
37}
38
39/// The built-in parser implemented directly from Macaulay2's `P/B/U` table.
40#[derive(Debug)]
41pub struct NativeParser;
42
43impl Parser for NativeParser {
44    type Error = NativeParseError;
45
46    fn parse_cells(&mut self, cells: CellStream) -> Result<SourceFile, Self::Error> {
47        parse_cell_stream(cells)
48    }
49}
50
51/// Parses a complete source file with the native precedence engine.
52///
53/// # Errors
54///
55/// Returns [`NativeParseError`] when lexing fails or the token sequence is not
56/// valid complete Macaulay2 syntax.
57pub fn parse_native(source: &str) -> Result<SourceFile, NativeParseError> {
58    let tokens = lex_str(source).map_err(NativeParseError::Lex)?;
59    let cells = tokenize_for_native_parser(tokens)?;
60    NativeParser.parse_cells(cells)
61}
62
63/// Parses source while retaining valid syntax inside unclosed delimiters.
64///
65/// Missing closing delimiters become zero-width closing spans at end of input
66/// and are returned as non-fatal lexical diagnostics. Other lexical or
67/// grammatical failures remain fatal.
68///
69/// # Errors
70///
71/// Returns [`NativeParseError`] when source cannot be recovered into a complete
72/// typed file.
73pub fn parse_native_recovering(
74    source: &str,
75) -> Result<Recovered<SourceFile, LexError>, NativeParseError> {
76    let recovered = lex_str_recovering(source).map_err(NativeParseError::Lex)?;
77    let (tokens, diagnostics) = recovered.into_parts();
78    let source = tokens.source();
79    let framed = frame_recovering(tokens);
80    let cells = CellStream::from_cells_with_source(
81        framed.cells.into_iter().chain(framed.incomplete),
82        source,
83    );
84    let syntax = NativeParser.parse_cells(cells)?;
85    Ok(Recovered::new(syntax, diagnostics))
86}
87
88/// The current state of an append-oriented native cell parser.
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
90pub enum NativeCellState {
91    /// No submitted syntax is waiting and no completed cell is queued.
92    Empty,
93    /// The current syntax is complete or a completed shell cell is queued.
94    Complete,
95    /// The active source is a prefix that more input may complete.
96    Incomplete,
97    /// The active source contains an error that appended input cannot repair.
98    Invalid,
99}
100
101/// One completed native cell together with its exact submitted source.
102#[derive(Debug)]
103pub struct CompletedNativeCell {
104    source: Span,
105    syntax: crate::nodes::Cell,
106}
107
108impl CompletedNativeCell {
109    /// Returns the exact source submitted for this evaluation unit.
110    ///
111    /// # Panics
112    ///
113    /// Panics if the internally retained source span cannot resolve its text.
114    pub fn source(&self) -> &str {
115        self.source
116            .text()
117            .expect("completed cell source spans must resolve to text")
118    }
119
120    /// Returns the parsed typed cell.
121    pub fn syntax(&self) -> &crate::nodes::Cell {
122        &self.syntax
123    }
124
125    /// Separates the exact source span from the typed cell.
126    pub fn into_parts(self) -> (Span, crate::nodes::Cell) {
127        (self.source, self.syntax)
128    }
129}
130
131/// A retained native parser for line-oriented shell input.
132///
133/// Each call to [`Self::push_line`] appends one submitted physical line. Cells
134/// completed by a top-level newline or semicolon are removed from the active
135/// buffer and never parsed again. Only the unfinished tail is lexed and parsed
136/// on later submissions.
137///
138/// Missing delimiter closers and parser requirements at end of input are
139/// classified as [`NativeCellState::Incomplete`]. A recoverable typed cell is
140/// available through [`Self::active_syntax`] when synthetic delimiter closers
141/// were enough to parse its contents.
142///
143/// ```
144/// use m2_syn::{Lex, NativeCellParser, NativeCellState};
145///
146/// let mut parser = NativeCellParser::new();
147/// assert_eq!(parser.push_line("f = x -> ("), NativeCellState::Incomplete);
148/// assert!(parser.active_syntax().is_some());
149/// assert_eq!(parser.push_line("x + 1"), NativeCellState::Incomplete);
150/// assert_eq!(parser.push_line(")"), NativeCellState::Complete);
151///
152/// let cells = parser.take_completed();
153/// assert_eq!(cells[0].syntax().to_m2(), "f = x -> (\nx + 1\n)");
154/// ```
155#[derive(Debug)]
156pub struct NativeCellParser {
157    active_source: String,
158    active_tokens: TokenStream,
159    active_syntax: Option<crate::nodes::Cell>,
160    completed: Vec<CompletedNativeCell>,
161    lexer_diagnostics: Vec<LexError>,
162    parse_error: Option<NativeParseError>,
163    state: NativeCellState,
164}
165
166impl Default for NativeCellParser {
167    fn default() -> Self {
168        Self::new()
169    }
170}
171
172impl NativeCellParser {
173    /// Creates an empty native shell parser.
174    pub fn new() -> Self {
175        Self {
176            active_source: String::new(),
177            active_tokens: TokenStream::new(),
178            active_syntax: None,
179            completed: Vec::new(),
180            lexer_diagnostics: Vec::new(),
181            parse_error: None,
182            state: NativeCellState::Empty,
183        }
184    }
185
186    /// Appends one submitted physical line and refreshes the active cell.
187    ///
188    /// A line feed is appended when `line` does not already end in one. This is
189    /// the submission boundary: a syntactically complete final expression may
190    /// become a completed cell, while an operator, required clause, or open
191    /// delimiter keeps the cell active.
192    pub fn push_line(&mut self, line: &str) -> NativeCellState {
193        self.active_source.push_str(line);
194        if !line.ends_with('\n') {
195            self.active_source.push('\n');
196        }
197        self.refresh()
198    }
199
200    /// Returns the current completion classification.
201    pub fn state(&self) -> NativeCellState {
202        self.state
203    }
204
205    /// Returns the exact source still belonging to the unfinished cell.
206    pub fn active_source(&self) -> &str {
207        &self.active_source
208    }
209
210    /// Returns recovered tokens for the unfinished cell.
211    pub fn active_tokens(&self) -> &TokenStream {
212        &self.active_tokens
213    }
214
215    /// Returns recovered typed syntax for the unfinished cell when available.
216    pub fn active_syntax(&self) -> Option<&crate::nodes::Cell> {
217        self.active_syntax.as_ref()
218    }
219
220    /// Returns lexer diagnostics for the active cell.
221    pub fn lexer_diagnostics(&self) -> &[LexError] {
222        &self.lexer_diagnostics
223    }
224
225    /// Returns the parser failure for an incomplete or invalid active cell.
226    pub fn parse_error(&self) -> Option<&NativeParseError> {
227        self.parse_error.as_ref()
228    }
229
230    /// Returns completed evaluation units that have not yet been taken.
231    pub fn completed(&self) -> &[CompletedNativeCell] {
232        &self.completed
233    }
234
235    /// Takes every completed evaluation unit without disturbing an active cell.
236    pub fn take_completed(&mut self) -> Vec<CompletedNativeCell> {
237        let completed = std::mem::take(&mut self.completed);
238        if self.state == NativeCellState::Complete {
239            self.state = NativeCellState::Empty;
240        }
241        completed
242    }
243
244    /// Discards the unfinished cell and its diagnostics.
245    pub fn reset_active(&mut self) {
246        self.active_source.clear();
247        self.active_tokens = TokenStream::new();
248        self.active_syntax = None;
249        self.lexer_diagnostics.clear();
250        self.parse_error = None;
251        self.state = if self.completed.is_empty() {
252            NativeCellState::Empty
253        } else {
254            NativeCellState::Complete
255        };
256    }
257
258    fn refresh(&mut self) -> NativeCellState {
259        self.active_tokens = TokenStream::new();
260        self.active_syntax = None;
261        self.lexer_diagnostics.clear();
262        self.parse_error = None;
263
264        loop {
265            let recovered = match lex_str_recovering(&self.active_source) {
266                Ok(recovered) => recovered,
267                Err(error) => {
268                    self.state = if incomplete_lex_error(error.kind()) {
269                        NativeCellState::Incomplete
270                    } else {
271                        NativeCellState::Invalid
272                    };
273                    self.lexer_diagnostics.push(error);
274                    return self.state;
275                }
276            };
277            let (tokens, diagnostics) = recovered.into_parts();
278            let framed = frame_recovering(tokens.clone());
279            let has_incomplete = framed.incomplete.is_some();
280            let mut cells = framed.cells.into_iter().map(|cell| (cell, true));
281            let first = cells
282                .next()
283                .or_else(|| framed.incomplete.map(|cell| (cell, false)));
284            let Some((cell, cell_complete)) = first else {
285                self.active_tokens = tokens;
286                self.lexer_diagnostics = diagnostics;
287                self.state = if self.completed.is_empty() {
288                    NativeCellState::Empty
289                } else {
290                    NativeCellState::Complete
291                };
292                return self.state;
293            };
294
295            let has_following_cell = cells.next().is_some() || has_incomplete;
296            let ended_by_semicolon = ends_with_cell_semicolon(&cell);
297            let boundary = end_span(&cell);
298            let end = boundary.end().unwrap_or(self.active_source.len());
299            let source = Span::ranged(std::sync::Arc::clone(boundary.source()), 0..end);
300
301            match parse_raw_cell(cell) {
302                Ok(Some(syntax)) if cell_complete && has_following_cell => {
303                    self.completed.push(CompletedNativeCell { source, syntax });
304                    self.active_source.replace_range(..end, "");
305                }
306                Ok(Some(syntax)) if cell_complete && diagnostics.is_empty() => {
307                    self.completed.push(CompletedNativeCell { source, syntax });
308                    self.active_source.clear();
309                    self.active_tokens = TokenStream::new();
310                    self.state = NativeCellState::Complete;
311                    return self.state;
312                }
313                Ok(Some(syntax)) => {
314                    self.active_tokens = tokens;
315                    self.active_syntax = Some(syntax);
316                    self.lexer_diagnostics = diagnostics;
317                    self.state = NativeCellState::Incomplete;
318                    return self.state;
319                }
320                Ok(None) => {
321                    self.active_tokens = tokens;
322                    self.lexer_diagnostics = diagnostics;
323                    self.state = if self.completed.is_empty() {
324                        NativeCellState::Empty
325                    } else {
326                        NativeCellState::Complete
327                    };
328                    return self.state;
329                }
330                Err(error) => {
331                    let can_append_to_cell =
332                        !cell_complete || (!has_following_cell && !ended_by_semicolon);
333                    self.state = if can_append_to_cell && incomplete_parse_error(&error) {
334                        NativeCellState::Incomplete
335                    } else {
336                        NativeCellState::Invalid
337                    };
338                    self.active_tokens = tokens;
339                    self.lexer_diagnostics = diagnostics;
340                    self.parse_error = Some(error);
341                    return self.state;
342                }
343            }
344        }
345    }
346}
347
348pub(crate) fn incomplete_lex_error(kind: LexErrorKind) -> bool {
349    matches!(
350        kind,
351        LexErrorKind::UnterminatedGroup
352            | LexErrorKind::UnterminatedString
353            | LexErrorKind::UnterminatedRawString
354            | LexErrorKind::UnterminatedBlockComment
355    )
356}
357
358pub(crate) fn incomplete_parse_error(error: &NativeParseError) -> bool {
359    match error {
360        NativeParseError::MissingOperand { .. } => true,
361        NativeParseError::Unexpected { found, .. } => found == "end of input",
362        NativeParseError::Lex(error) => incomplete_lex_error(error.kind()),
363        NativeParseError::Tokenize(_) => true,
364        NativeParseError::NewlineInApplication { .. }
365        | NativeParseError::InvalidAssignmentTarget { .. }
366        | NativeParseError::InvalidLambdaParameters { .. }
367        | NativeParseError::Unsupported { .. } => false,
368    }
369}
370
371#[derive(Debug, Spanned)]
372/// An error produced by the native lexer or precedence parser.
373pub enum NativeParseError {
374    /// Tokenization failed before parsing began.
375    Lex(LexError),
376    /// The raw token stream could not be partitioned into complete cells.
377    Tokenize(CellStreamError),
378    /// The parser encountered a token that cannot occur in this position.
379    Unexpected {
380        /// A readable description of the received token.
381        found: String,
382        /// A readable description of the required syntax.
383        expected: &'static str,
384        /// The received token or end-of-input position.
385        span: Span,
386    },
387    /// A prefix or binary operator has no operand.
388    MissingOperand {
389        /// The operator spelling.
390        operator: String,
391        /// The operator or end-of-input position.
392        span: Span,
393    },
394    /// Implicit function application crossed a top-level line break.
395    NewlineInApplication {
396        /// The token following the line break.
397        span: Span,
398    },
399    /// The left side of an assignment is not assignable.
400    InvalidAssignmentTarget {
401        /// The assignment operator span.
402        span: Span,
403    },
404    /// The left side of a lambda arrow is not a parameter form.
405    InvalidLambdaParameters {
406        /// The lambda arrow span.
407        span: Span,
408    },
409    /// The grammar recognizes syntax that this native backend cannot lower yet.
410    Unsupported {
411        /// The unsupported syntax spelling.
412        syntax: String,
413        /// The unsupported token span.
414        span: Span,
415    },
416}
417
418impl NativeParseError {
419    fn unexpected_end(expected: &'static str, span: Span) -> Self {
420        Self::Unexpected {
421            found: "end of input".into(),
422            expected,
423            span,
424        }
425    }
426
427    fn missing_operand_for(self, operator: &TokenTree) -> Self {
428        if matches!(self, Self::MissingOperand { .. }) {
429            Self::MissingOperand {
430                operator: operator.description(),
431                span: operator.expect_span(),
432            }
433        } else {
434            self
435        }
436    }
437}
438
439impl Display for NativeParseError {
440    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
441        match self {
442            Self::Lex(error) => error.fmt(formatter),
443            Self::Tokenize(error) => error.fmt(formatter),
444            Self::Unexpected {
445                found, expected, ..
446            } => write!(formatter, "expected {expected}, found `{found}`"),
447            Self::MissingOperand { operator, .. } => {
448                write!(
449                    formatter,
450                    "expected an operand after `{operator}`, found end of input"
451                )
452            }
453            Self::NewlineInApplication { .. } => {
454                formatter.write_str("implicit application cannot cross a line break")
455            }
456            Self::InvalidAssignmentTarget { .. } => {
457                formatter.write_str("invalid assignment target")
458            }
459            Self::InvalidLambdaParameters { .. } => {
460                formatter.write_str("invalid lambda parameter list")
461            }
462            Self::Unsupported { syntax, .. } => {
463                write!(
464                    formatter,
465                    "native parsing of `{syntax}` is not implemented yet"
466                )
467            }
468        }
469    }
470}
471
472impl Error for NativeParseError {
473    fn source(&self) -> Option<&(dyn Error + 'static)> {
474        match self {
475            Self::Lex(error) => Some(error),
476            Self::Tokenize(error) => Some(error),
477            _ => None,
478        }
479    }
480}
481
482#[derive(Debug, Clone, Copy)]
483/// Parser-local instruction for interpreting a token in unary position.
484///
485/// This is not a syntax category and is never stored in the token stream or
486/// typed CST. [`parse_info`] derives it temporarily from the current raw token
487/// so the precedence engine can select the corresponding parsing routine.
488enum UnaryAction {
489    Atom,
490    Prefix,
491    Statement,
492    Control,
493    Debug,
494    Delimiter,
495    Quote,
496    Error,
497}
498
499#[derive(Debug, Clone, Copy)]
500struct ParseInfo {
501    precedence: u8,
502    binary_strength: Option<u8>,
503    unary_strength: Option<u8>,
504    unary: UnaryAction,
505    postfix: bool,
506}
507
508impl From<TokenParseRule> for ParseInfo {
509    fn from(rule: TokenParseRule) -> Self {
510        match rule {
511            TokenParseRule::Precedence(precedence) => Self::stop(precedence),
512            TokenParseRule::Postfix(precedence) => Self::postfix(precedence),
513            TokenParseRule::Binary(precedence, strength) => Self::binary(precedence, strength),
514            TokenParseRule::Prefix(precedence, strength) => {
515                Self::unary(precedence, UnaryAction::Prefix, strength)
516            }
517            TokenParseRule::PrefixBinary(precedence, binary, unary) => {
518                Self::prefix_binary(precedence, binary, unary)
519            }
520            TokenParseRule::Unary(precedence, strength) => {
521                Self::unary(precedence, UnaryAction::Error, strength)
522            }
523        }
524    }
525}
526
527impl ParseInfo {
528    const fn atom(precedence: u8) -> Self {
529        Self {
530            precedence,
531            binary_strength: None,
532            unary_strength: None,
533            unary: UnaryAction::Atom,
534            postfix: false,
535        }
536    }
537
538    const fn binary(precedence: u8, strength: u8) -> Self {
539        Self {
540            precedence,
541            binary_strength: Some(strength),
542            unary_strength: None,
543            unary: UnaryAction::Error,
544            postfix: false,
545        }
546    }
547
548    const fn prefix_binary(precedence: u8, binary_strength: u8, unary_strength: u8) -> Self {
549        Self {
550            precedence,
551            binary_strength: Some(binary_strength),
552            unary_strength: Some(unary_strength),
553            unary: UnaryAction::Prefix,
554            postfix: false,
555        }
556    }
557
558    /// A token whose action in unary position is to introduce something that
559    /// binds at `strength`: a statement, a control form, a timing form, or a
560    /// quoted name.
561    const fn unary(precedence: u8, unary: UnaryAction, strength: u8) -> Self {
562        Self {
563            precedence,
564            binary_strength: None,
565            unary_strength: Some(strength),
566            unary,
567            postfix: false,
568        }
569    }
570
571    const fn postfix(precedence: u8) -> Self {
572        Self {
573            precedence,
574            binary_strength: None,
575            unary_strength: None,
576            unary: UnaryAction::Error,
577            postfix: true,
578        }
579    }
580
581    const fn delimiter(precedence: u8) -> Self {
582        Self {
583            precedence,
584            binary_strength: None,
585            unary_strength: Some(PREC_CLOSER),
586            unary: UnaryAction::Delimiter,
587            postfix: false,
588        }
589    }
590
591    const fn stop(precedence: u8) -> Self {
592        Self {
593            precedence,
594            binary_strength: None,
595            unary_strength: None,
596            unary: UnaryAction::Error,
597            postfix: false,
598        }
599    }
600}
601
602struct Engine {
603    input: ParseStream,
604}
605
606impl Engine {
607    fn new(tokens: TokenStream) -> Self {
608        Self {
609            input: ParseStream::new(tokens),
610        }
611    }
612
613    fn parse_cell(
614        mut self,
615        semicolon: Option<Token![;]>,
616        eoc_span: Span,
617    ) -> Result<Option<crate::nodes::Cell>, NativeParseError> {
618        self.skip_trivia();
619        if matches!(self.peek(), Lookahead::End(_)) {
620            return match semicolon {
621                None => Ok(None),
622                Some(semicolon) => Err(NativeParseError::Unexpected {
623                    found: ";".into(),
624                    expected: "a statement before `;`",
625                    span: semicolon.expect_span(),
626                }),
627            };
628        }
629
630        let (components, _) = self.parse_components()?;
631
632        if let Lookahead::Token(token) = self.peek() {
633            return Err(unexpected!(token.token, "the end of the cell"));
634        }
635
636        let eoc = EOC::new("", eoc_span);
637        Ok(Some(crate::nodes::Cell::new(
638            ExprPack::new(components),
639            semicolon,
640            eoc,
641        )))
642    }
643
644    fn parse_components(&mut self) -> Result<(Punctuated<Component>, bool), NativeParseError> {
645        let first = if self.at_punctuation(",") {
646            Component::Empty(Empty::new("", empty_span_at(&self.peek().expect_span())))
647        } else {
648            self.parse_required(PREC_COMMA)?.into()
649        };
650        let mut elements = Punctuated::from_single(first);
651        let mut has_comma = false;
652
653        while let Some(comma) = self.take_punctuation(",") {
654            has_comma = true;
655            let next = if self.at_end() || self.at_punctuation(";") || self.at_punctuation(",") {
656                Component::Empty(Empty::new("", empty_span_at(&self.peek().expect_span())))
657            } else {
658                self.parse_required(PREC_COMMA)?.into()
659            };
660            elements.push(typed_token(comma), next);
661        }
662
663        Ok((elements, has_comma))
664    }
665
666    fn parse_optional_components(
667        &mut self,
668    ) -> Result<Option<(Punctuated<Component>, bool)>, NativeParseError> {
669        if self.at_end() || self.at_punctuation(";") {
670            Ok(None)
671        } else {
672            self.parse_components().map(Some)
673        }
674    }
675
676    fn parse_required(&mut self, level: u8) -> Result<Expr, NativeParseError> {
677        let token = match self.consume() {
678            Lookahead::Token(token) => token,
679            Lookahead::End(span) => {
680                return Err(NativeParseError::MissingOperand {
681                    operator: "end of input".into(),
682                    span,
683                });
684            }
685        };
686        self.parse_consumed(token, level)
687    }
688
689    fn parse_nullable(&mut self, level: u8) -> Result<Option<Expr>, NativeParseError> {
690        let lookahead = self.peek();
691        let Lookahead::Token(token) = lookahead else {
692            return Ok(None);
693        };
694        if parse_info(&token.token).precedence <= level {
695            return Ok(None);
696        }
697        let Lookahead::Token(token) = self.consume() else {
698            unreachable!("the token just peeked is consumable")
699        };
700        self.parse_consumed(token, level).map(Some)
701    }
702
703    fn parse_consumed(
704        &mut self,
705        token: SignificantToken,
706        level: u8,
707    ) -> Result<Expr, NativeParseError> {
708        let info = parse_info(&token.token);
709        let result = match info.unary {
710            UnaryAction::Atom => lower_atom(token)?,
711            UnaryAction::Prefix => {
712                let strength = info.unary_strength.expect("prefix operators have U");
713                let operand = self.parse_required(level.max(strength))?;
714                let span = token.token.expect_span();
715                let spelling = token.token.description();
716                let operator =
717                    PrefixOperator::from_token_tree_with_trivia(token.token, token.trailing_trivia)
718                        .ok_or({
719                            NativeParseError::Unsupported {
720                                syntax: spelling,
721                                span,
722                            }
723                        })?;
724                PrefixExpr::new(operator, operand).into()
725            }
726            UnaryAction::Statement => {
727                let strength = info
728                    .unary_strength
729                    .expect("prefix statements have a unary binding strength");
730                let operand = if is_token!(&token.token, break)
731                    || is_token!(&token.token, continue)
732                    || is_token!(&token.token, return)
733                {
734                    self.parse_nullable(level.max(strength))?
735                } else {
736                    self.parse_required(level.max(strength))
737                        .map_err(|error| error.missing_operand_for(&token.token))?
738                        .into()
739                };
740                lower_prefix_statement(token, operand)
741            }
742            UnaryAction::Control => {
743                if is_token!(&token.token, if) {
744                    self.parse_if_statement(token)?
745                } else if is_token!(&token.token, for) {
746                    self.parse_for_loop(token)?
747                } else if is_token!(&token.token, while) {
748                    self.parse_while_loop(token)?
749                } else if is_token!(&token.token, try) {
750                    self.parse_try_statement(token)?
751                } else if is_token!(&token.token, new) {
752                    self.parse_new_statement(token)?
753                } else {
754                    unreachable!("control actions are assigned only to control keywords")
755                }
756            }
757            UnaryAction::Debug => self.parse_debug_clause(token)?,
758            UnaryAction::Delimiter => parse_delimited(token)?,
759            UnaryAction::Quote => {
760                let operator = token.token.description();
761                let quoted = match self.consume() {
762                    Lookahead::Token(quoted) => quoted,
763                    Lookahead::End(span) => {
764                        return Err(NativeParseError::MissingOperand { operator, span });
765                    }
766                };
767                let specifier = if is_token!(&token.token, symbol) {
768                    QuoteSpecifier::Symbol(typed_token(token))
769                } else if is_token!(&token.token, local) {
770                    QuoteSpecifier::Local(typed_token(token))
771                } else if is_token!(&token.token, global) {
772                    QuoteSpecifier::Global(typed_token(token))
773                } else if is_token!(&token.token, threadVariable) {
774                    QuoteSpecifier::ThreadVariable(typed_token(token))
775                } else if is_token!(&token.token, threadLocal) {
776                    QuoteSpecifier::ThreadLocal(typed_token(token))
777                } else {
778                    unreachable!("quote actions are assigned only to quote specifiers")
779                };
780                let quoted_span = quoted.token.expect_span();
781                let Some(quoted_text) = quoted.token.spelling().map(str::to_owned) else {
782                    return Err(unexpected!(quoted.token, "a quoted token"));
783                };
784                let quoted = typed_terminal(
785                    Symbol::new(quoted_text, quoted_span),
786                    quoted.trailing_trivia,
787                );
788                QuoteExpression::new(specifier, quoted).into()
789            }
790            UnaryAction::Error => {
791                return Err(unexpected!(token.token, "an expression"));
792            }
793        };
794        self.accumulate(result, level)
795    }
796
797    fn parse_if_statement(&mut self, keyword: SignificantToken) -> Result<Expr, NativeParseError> {
798        let condition = self.required_clause_expression(PREC_CONTROL, &keyword.token)?;
799        let then_keyword = self.consume_exact("then")?;
800        let then_value = self.required_clause_expression(PREC_CONTROL, &then_keyword.token)?;
801        let then_clause = ThenClause::new(typed_token(then_keyword), then_value);
802        let else_clause = self
803            .take_exact("else")
804            .map(|else_keyword| {
805                let value = self.required_clause_expression(PREC_CONTROL, &else_keyword.token)?;
806                Ok::<_, NativeParseError>(ElseClause::new(typed_token(else_keyword), value))
807            })
808            .transpose()?;
809        Ok(IfStatement::new(typed_token(keyword), condition, then_clause, else_clause).into())
810    }
811
812    fn parse_for_loop(&mut self, keyword: SignificantToken) -> Result<Expr, NativeParseError> {
813        let variable = self.consume_symbol("a loop variable")?;
814
815        let iteration_domain = if let Some(token) = self.take_exact("in") {
816            let collection = self.required_clause_expression(PREC_LOOP_CLAUSE, &token.token)?;
817            Some(IterIn::new(typed_token(token), collection).into())
818        } else {
819            let lower_bound = self
820                .take_exact("from")
821                .map(|token| {
822                    let value = self.required_clause_expression(PREC_LOOP_CLAUSE, &token.token)?;
823                    Ok::<_, NativeParseError>(RangeBoundFrom::new(typed_token(token), value))
824                })
825                .transpose()?;
826            let upper_bound = self
827                .take_exact("to")
828                .map(|token| {
829                    let value = self.required_clause_expression(PREC_LOOP_CLAUSE, &token.token)?;
830                    Ok::<_, NativeParseError>(RangeBoundTo::new(typed_token(token), value))
831                })
832                .transpose()?;
833            (lower_bound.is_some() || upper_bound.is_some())
834                .then(|| IterRange::new(lower_bound, upper_bound).into())
835        };
836
837        let when_condition = if let Some(token) = self.take_exact("when") {
838            let condition = self.required_clause_expression(PREC_LOOP_CLAUSE, &token.token)?;
839            Some(WhenCondition::new(typed_token(token), condition))
840        } else {
841            None
842        };
843        let body = self.parse_loop_body()?;
844
845        Ok(ForLoop::new(
846            typed_token(keyword),
847            variable,
848            iteration_domain,
849            when_condition,
850            body,
851        )
852        .into())
853    }
854
855    fn parse_while_loop(&mut self, keyword: SignificantToken) -> Result<Expr, NativeParseError> {
856        let condition = self.required_clause_expression(PREC_CONTROL, &keyword.token)?;
857        let body = self.parse_loop_body()?;
858        Ok(WhileLoop::new(typed_token(keyword), condition, body).into())
859    }
860
861    fn parse_loop_body(&mut self) -> Result<LoopBody, NativeParseError> {
862        let (list_step, do_step) = if let Some(token) = self.take_exact("list") {
863            let listed_value = self.required_clause_expression(PREC_CONTROL, &token.token)?;
864            let list_step = Some(ListStep::new(typed_token(token), listed_value));
865            if let Some(token) = self.take_exact("do") {
866                let ignored_value = self.required_clause_expression(PREC_CONTROL, &token.token)?;
867                (
868                    list_step,
869                    Some(DoStep::new(typed_token(token), ignored_value)),
870                )
871            } else {
872                (list_step, None)
873            }
874        } else if let Some(token) = self.take_exact("do") {
875            let ignored_value = self.required_clause_expression(PREC_CONTROL, &token.token)?;
876            (None, Some(DoStep::new(typed_token(token), ignored_value)))
877        } else {
878            return Err(self.expected_at_lookahead("`list` or `do`"));
879        };
880
881        Ok(LoopBody::new(list_step, do_step))
882    }
883
884    fn parse_try_statement(&mut self, keyword: SignificantToken) -> Result<Expr, NativeParseError> {
885        let value = self.required_clause_expression(PREC_CONTROL, &keyword.token)?;
886        let then_clause = self
887            .take_exact("then")
888            .map(|then_keyword| {
889                let value = self.required_clause_expression(PREC_CONTROL, &then_keyword.token)?;
890                Ok::<_, NativeParseError>(ThenClause::new(typed_token(then_keyword), value))
891            })
892            .transpose()?;
893
894        let fallback = if let Some(else_keyword) = self.take_exact("else") {
895            let value = self.required_clause_expression(PREC_CONTROL, &else_keyword.token)?;
896            Some(TryFallback::ElseClause(ElseClause::new(
897                typed_token(else_keyword),
898                value,
899            )))
900        } else if let Some(except_keyword) = self.take_exact("except") {
901            let exception = self.consume_symbol("an exception symbol")?;
902            let do_keyword = self.consume_exact("do")?;
903            let value = self.required_clause_expression(PREC_CONTROL, &do_keyword.token)?;
904            Some(TryFallback::ExceptDo(ExceptDo::new(
905                typed_token(except_keyword),
906                exception,
907                typed_token(do_keyword),
908                value,
909            )))
910        } else {
911            None
912        };
913
914        Ok(TryStatement::new(typed_token(keyword), value, then_clause, fallback).into())
915    }
916
917    fn parse_new_statement(&mut self, keyword: SignificantToken) -> Result<Expr, NativeParseError> {
918        let class = self.required_clause_expression(PREC_LOOP_CLAUSE, &keyword.token)?;
919        let parent_class = if let Some(token) = self.take_exact("of") {
920            let parent = self.required_clause_expression(PREC_LOOP_CLAUSE, &token.token)?;
921            Some(OfClass::new(typed_token(token), parent))
922        } else {
923            None
924        };
925        let instance = if let Some(token) = self.take_exact("from") {
926            let instance = self.required_clause_expression(PREC_LOOP_CLAUSE, &token.token)?;
927            Some(FromInstance::new(typed_token(token), instance))
928        } else {
929            None
930        };
931
932        Ok(NewStatement::new(typed_token(keyword), class, parent_class, instance).into())
933    }
934
935    fn parse_debug_clause(&mut self, keyword: SignificantToken) -> Result<Expr, NativeParseError> {
936        let spelling = parsing_spelling(&keyword.token).to_owned();
937        if matches!(
938            spelling.as_str(),
939            "time" | "timing" | "elapsedTime" | "elapsedTiming" | "profile"
940        ) {
941            let value = self
942                .parse_required(PREC_CONTROL)
943                .map_err(|error| error.missing_operand_for(&keyword.token))?;
944            let modifier = match spelling.as_str() {
945                "time" => ProfilingModifier::Time(typed_token(keyword)),
946                "timing" => ProfilingModifier::Timing(typed_token(keyword)),
947                "elapsedTime" => ProfilingModifier::ElapsedTime(typed_token(keyword)),
948                "elapsedTiming" => ProfilingModifier::ElapsedTiming(typed_token(keyword)),
949                "profile" => ProfilingModifier::Profile(typed_token(keyword)),
950                _ => unreachable!(),
951            };
952            return Ok(ProfilingExpr::new(modifier, value).into());
953        }
954        let value_is_optional = matches!(spelling.as_str(), "step" | "finish");
955        let value = if value_is_optional {
956            self.parse_nullable(PREC_CONTROL)?
957        } else {
958            Some(
959                self.parse_required(PREC_CONTROL)
960                    .map_err(|error| error.missing_operand_for(&keyword.token))?,
961            )
962        };
963        let keyword = match spelling.as_str() {
964            "step" => DebugKeyword::Step(typed_token(keyword)),
965            "finish" => DebugKeyword::Finish(typed_token(keyword)),
966            "shield" => DebugKeyword::Shield(typed_token(keyword)),
967            "TEST" => DebugKeyword::Test(typed_token(keyword)),
968            "breakpoint" => DebugKeyword::Breakpoint(typed_token(keyword)),
969            _ => unreachable!("debug actions are assigned only to debug keywords"),
970        };
971        Ok(DebugExpr::new(keyword, value).into())
972    }
973
974    fn required_clause_expression(
975        &mut self,
976        level: u8,
977        keyword: &TokenTree,
978    ) -> Result<Expr, NativeParseError> {
979        match self.parse_nullable(level)? {
980            None => Err(NativeParseError::MissingOperand {
981                operator: keyword.description(),
982                span: keyword.expect_span(),
983            }),
984            Some(expression) => Ok(expression),
985        }
986    }
987
988    fn consume_symbol(&mut self, expected: &'static str) -> Result<Symbol, NativeParseError> {
989        match self.consume() {
990            Lookahead::Token(token)
991                if matches!(token.token, TokenTree::Ident(_))
992                    && matches!(parse_info(&token.token).unary, UnaryAction::Atom) =>
993            {
994                let span = token.token.expect_span();
995                let text = token.token.description();
996                Ok(typed_terminal(
997                    Symbol::new(text, span),
998                    token.trailing_trivia,
999                ))
1000            }
1001            Lookahead::Token(token) => Err(unexpected!(token.token, expected)),
1002            Lookahead::End(span) => Err(NativeParseError::unexpected_end(expected, span)),
1003        }
1004    }
1005
1006    fn consume_exact(
1007        &mut self,
1008        spelling: &'static str,
1009    ) -> Result<SignificantToken, NativeParseError> {
1010        match self.consume() {
1011            Lookahead::Token(token) if parsing_spelling(&token.token) == spelling => Ok(token),
1012            Lookahead::Token(token) => Err(unexpected!(token.token, spelling)),
1013            Lookahead::End(span) => Err(NativeParseError::unexpected_end(spelling, span)),
1014        }
1015    }
1016
1017    fn take_exact(&mut self, spelling: &'static str) -> Option<SignificantToken> {
1018        if matches!(
1019            self.peek(),
1020            Lookahead::Token(token)
1021                if parsing_spelling(&token.token) == spelling
1022        ) {
1023            let Lookahead::Token(token) = self.consume() else {
1024                unreachable!("the token just peeked is consumable")
1025            };
1026            Some(token)
1027        } else {
1028            None
1029        }
1030    }
1031
1032    fn expected_at_lookahead(&self, expected: &'static str) -> NativeParseError {
1033        match self.peek() {
1034            Lookahead::Token(token) => unexpected!(token.token, expected),
1035            Lookahead::End(span) => NativeParseError::unexpected_end(expected, span),
1036        }
1037    }
1038
1039    fn accumulate(&mut self, mut left: Expr, level: u8) -> Result<Expr, NativeParseError> {
1040        loop {
1041            let Lookahead::Token(next) = self.peek() else {
1042                return Ok(left);
1043            };
1044            let info = if matches!(&next.token, TokenTree::Punct(punct) if punct.text().is_empty())
1045            {
1046                self.adjacency_info()
1047            } else {
1048                parse_info(&next.token)
1049            };
1050            if info.precedence <= level {
1051                return Ok(left);
1052            }
1053            let Lookahead::Token(operator) = self.consume() else {
1054                unreachable!("the token just peeked is consumable")
1055            };
1056            left = if matches!(&operator.token, TokenTree::Punct(punct) if punct.text().is_empty())
1057            {
1058                let right = self
1059                    .parse_required(PREC_APPLICATION_RIGHT)
1060                    .map_err(|error| error.missing_operand_for(&operator.token))?;
1061                let adjacency = typed_terminal(
1062                    Adj::new("", operator.token.expect_span()),
1063                    operator.trailing_trivia,
1064                );
1065                AdjacentExpr::new(left, adjacency, right).into()
1066            } else if let Some(strength) = info.binary_strength {
1067                let left = BinaryLeft::prepare(left, &operator.token)?;
1068                let right = self
1069                    .parse_required(strength)
1070                    .map_err(|error| error.missing_operand_for(&operator.token))?;
1071                lower_binary(left, operator, right)?
1072            } else if info.postfix {
1073                let span = operator.token.expect_span();
1074                let spelling = operator.token.description();
1075                let operator = PostfixOperator::from_token_tree_with_trivia(
1076                    operator.token,
1077                    operator.trailing_trivia,
1078                )
1079                .ok_or(NativeParseError::Unsupported {
1080                    syntax: spelling,
1081                    span,
1082                })?;
1083                PostfixExpr::new(left, operator).into()
1084            } else if !matches!(info.unary, UnaryAction::Error) {
1085                if operator.crossed_newline {
1086                    return Err(NativeParseError::NewlineInApplication {
1087                        span: operator.token.expect_span(),
1088                    });
1089                }
1090                let adjacency_span = operator.token.expect_span().subspan(0, 0);
1091                let right = self.parse_consumed(operator, PREC_APPLICATION_RIGHT)?;
1092                AdjacentExpr::new(left, Adj::new("", adjacency_span), right).into()
1093            } else {
1094                return Err(unexpected!(operator.token, "an operator"));
1095            };
1096        }
1097    }
1098
1099    fn adjacency_info(&self) -> ParseInfo {
1100        let mut input = self.input.fork();
1101        let _ = input.next_significant();
1102        input.next_significant().map_or_else(
1103            || ParseInfo::stop(PREC_APPLICATION),
1104            |right| ParseInfo::binary(parse_info(&right.token).precedence, PREC_APPLICATION_RIGHT),
1105        )
1106    }
1107
1108    fn only_expression(&self, components: Punctuated<Component>) -> Result<Expr, NativeParseError> {
1109        let mut components = components.into_iter();
1110        match (components.next(), components.next()) {
1111            (Some(Component::Expr(value)), None) => Ok(*value),
1112            (Some(value), _) => Err(NativeParseError::Unexpected {
1113                found: "a sequence".into(),
1114                expected: "one expression",
1115                span: value.expect_span(),
1116            }),
1117            (None, _) => Err(NativeParseError::MissingOperand {
1118                operator: "empty expression".into(),
1119                span: self.input.eof_span(),
1120            }),
1121        }
1122    }
1123
1124    fn peek(&self) -> Lookahead {
1125        self.input.lookahead()
1126    }
1127
1128    fn consume(&mut self) -> Lookahead {
1129        self.input.consume_lookahead()
1130    }
1131
1132    fn skip_trivia(&mut self) {
1133        self.input.skip_trivia();
1134    }
1135
1136    fn at_end(&self) -> bool {
1137        matches!(self.peek(), Lookahead::End(_))
1138    }
1139
1140    fn at_punctuation(&self, spelling: &str) -> bool {
1141        matches!(
1142            self.peek(),
1143            Lookahead::Token(token)
1144                if token.token.spelling() == Some(spelling)
1145        )
1146    }
1147
1148    fn take_punctuation(&mut self, spelling: &str) -> Option<SignificantToken> {
1149        if !self.at_punctuation(spelling) {
1150            return None;
1151        }
1152        let Lookahead::Token(token) = self.consume() else {
1153            unreachable!("the punctuation just peeked is consumable")
1154        };
1155        Some(token)
1156    }
1157}
1158
1159fn parse_delimited(token: SignificantToken) -> Result<Expr, NativeParseError> {
1160    let group_trailing = token.trailing_trivia;
1161    let group = match token.token {
1162        TokenTree::Group(group) => group,
1163        token => return Err(unexpected!(token, "a delimited group")),
1164    };
1165    let kind = group.delim_kind();
1166    let delimiter_spans = group.delimiter().spans.clone();
1167    let mut group_contents = group.into_stream();
1168    let opening_trivia = group_contents.take_leading_trivia();
1169    let mut contents = Engine::new(group_contents);
1170    let mut muted = Vec::new();
1171    let (elements, comma) = loop {
1172        let elements = contents.parse_optional_components()?;
1173        let Some(separator) = contents.take_punctuation(";") else {
1174            break elements.map_or((None, false), |(elements, comma)| (Some(elements), comma));
1175        };
1176        let Some((elements, _)) = elements else {
1177            return Err(unexpected!(separator.token, "a statement before `;`"));
1178        };
1179        muted.push(Statement::new(
1180            ExprPack::new(elements),
1181            typed_token(separator),
1182        ));
1183    };
1184    if let Lookahead::Token(token) = contents.peek() {
1185        return Err(unexpected!(token.token, "the end of the group"));
1186    }
1187
1188    let expression: Expr = match kind {
1189        DelimiterKind::Bracket => {
1190            let mut value = Array::new(muted, elements.map(ExprPack::new));
1191            value.delimiter = typed_delimiter(delimiter_spans, opening_trivia, group_trailing);
1192            value.into()
1193        }
1194        DelimiterKind::Brace => {
1195            let mut value = List::new(muted, elements.map(ExprPack::new));
1196            value.delimiter = typed_delimiter(delimiter_spans, opening_trivia, group_trailing);
1197            value.into()
1198        }
1199        DelimiterKind::AngleBar => {
1200            let mut value = AngleBarList::new(muted, elements.map(ExprPack::new));
1201            value.delimiter = typed_delimiter(delimiter_spans, opening_trivia, group_trailing);
1202            value.into()
1203        }
1204        DelimiterKind::Parenthesis if !muted.is_empty() || comma => {
1205            let mut value = if comma {
1206                Sequence::new(
1207                    muted,
1208                    Some(ExprPack::new(
1209                        elements.expect("a comma pack always contains components"),
1210                    )),
1211                )
1212            } else {
1213                let value = elements
1214                    .map(|elements| contents.only_expression(elements))
1215                    .transpose()?;
1216                let mut value = Holder::new(muted, value);
1217                value.delimiter = typed_delimiter(delimiter_spans, opening_trivia, group_trailing);
1218                return Ok(value.into());
1219            };
1220            value.delimiter = typed_delimiter(delimiter_spans, opening_trivia, group_trailing);
1221            value.into()
1222        }
1223        DelimiterKind::Parenthesis => {
1224            let value = elements
1225                .map(|elements| contents.only_expression(elements))
1226                .transpose()?;
1227            let mut value = Holder::new(Vec::new(), value);
1228            value.delimiter = typed_delimiter(delimiter_spans, opening_trivia, group_trailing);
1229            value.into()
1230        }
1231    };
1232    Ok(expression)
1233}
1234
1235fn typed_delimiter<D: DelimiterToken>(
1236    spans: crate::DoubleSpan,
1237    opening_trivia: TrailingTrivia,
1238    closing_trivia: TrailingTrivia,
1239) -> D {
1240    let mut delimiter = D::new(spans);
1241    *delimiter.opening_trivia_mut() = opening_trivia;
1242    *delimiter.closing_trivia_mut() = closing_trivia;
1243    delimiter
1244}
1245
1246fn lower_prefix_statement(keyword: SignificantToken, value: Option<Expr>) -> Expr {
1247    if is_token!(&keyword.token, break) {
1248        BreakExpr::new(typed_token(keyword), value).into()
1249    } else if is_token!(&keyword.token, continue) {
1250        ContinueExpr::new(typed_token(keyword), value).into()
1251    } else if is_token!(&keyword.token, return) {
1252        ReturnExpr::new(typed_token(keyword), value).into()
1253    } else {
1254        let value = value.expect("catch, throw, and trap require a value");
1255        if is_token!(&keyword.token, catch) {
1256            Catch::new(typed_token(keyword), value).into()
1257        } else if is_token!(&keyword.token, throw) {
1258            Throw::new(typed_token(keyword), value).into()
1259        } else if is_token!(&keyword.token, trap) {
1260            Trap::new(typed_token(keyword), value).into()
1261        } else {
1262            unreachable!("statement actions are assigned only to statement keywords")
1263        }
1264    }
1265}
1266
1267fn lower_atom(token: SignificantToken) -> Result<Expr, NativeParseError> {
1268    match token.token {
1269        TokenTree::Ident(raw) => Ok(typed_terminal(
1270            Symbol::new(raw.text(), raw.expect_span()),
1271            token.trailing_trivia,
1272        )
1273        .into()),
1274        TokenTree::Literal(raw) => {
1275            let span = raw.expect_span();
1276            Ok(match raw.kind {
1277                LiteralKind::Integer => {
1278                    typed_terminal(IntegerLiteral::new(raw.text(), span), token.trailing_trivia)
1279                        .into()
1280                }
1281                LiteralKind::Float => {
1282                    typed_terminal(FloatLiteral::new(raw.text(), span), token.trailing_trivia)
1283                        .into()
1284                }
1285                LiteralKind::String => {
1286                    lower_string_literal(raw.text(), span, token.trailing_trivia)
1287                }
1288                LiteralKind::RawString => {
1289                    lower_raw_string_literal(raw.text(), span, token.trailing_trivia)
1290                }
1291            })
1292        }
1293        token => Err(unexpected!(token, "an expression")),
1294    }
1295}
1296
1297/// A left operand whose operator-specific structural requirements are valid.
1298enum BinaryLeft {
1299    Expression(Expr),
1300    Assignment {
1301        target: AssignmentTarget,
1302        local: bool,
1303    },
1304    Lambda(LambdaParameters),
1305}
1306
1307impl BinaryLeft {
1308    fn prepare(left: Expr, operator: &TokenTree) -> Result<Self, NativeParseError> {
1309        let span = operator.expect_span();
1310        Ok(match operator.spelling() {
1311            Some("=") => Self::Assignment {
1312                target: AssignmentTarget::parse(left, false, span)?,
1313                local: false,
1314            },
1315            Some(":=") => Self::Assignment {
1316                target: AssignmentTarget::parse(left, true, span)?,
1317                local: true,
1318            },
1319            Some("->") => Self::Lambda(
1320                lambda_parameters(left)
1321                    .ok_or(NativeParseError::InvalidLambdaParameters { span })?,
1322            ),
1323            _ => Self::Expression(left),
1324        })
1325    }
1326}
1327
1328/// A parsed expression that can occur to the left of an assignment operator.
1329enum AssignmentTarget {
1330    Symbol(Symbol),
1331    Operator(OperatorExpr),
1332    New(NewStatement),
1333    Pack(AssignmentPack),
1334}
1335
1336impl AssignmentTarget {
1337    fn parse(expression: Expr, local: bool, span: Span) -> Result<Self, NativeParseError> {
1338        match expression {
1339            Expr::Symbol(variable) => Ok(Self::Symbol(variable)),
1340            Expr::OperatorExpr(expression) => Ok(Self::Operator(expression)),
1341            Expr::NewStatement(statement) if local => Ok(Self::New(statement)),
1342            expression => assignment_pack(expression)
1343                .map(Self::Pack)
1344                .ok_or(NativeParseError::InvalidAssignmentTarget { span }),
1345        }
1346    }
1347}
1348
1349fn lower_binary(
1350    left: BinaryLeft,
1351    operator: SignificantToken,
1352    right: Expr,
1353) -> Result<Expr, NativeParseError> {
1354    let span = operator.token.expect_span();
1355    let spelling = operator
1356        .token
1357        .spelling()
1358        .expect("binary actions are assigned only to spelled tokens");
1359    match left {
1360        BinaryLeft::Assignment { target, local } => {
1361            Ok(lower_assignment(target, operator, right, local))
1362        }
1363        BinaryLeft::Lambda(parameters) => {
1364            Ok(LambdaExpression::new(parameters, typed_token(operator), right).into())
1365        }
1366        BinaryLeft::Expression(left) if spelling == "<-" => {
1367            Ok(EvaluatedAssignment::new(left, typed_token(operator), right).into())
1368        }
1369        BinaryLeft::Expression(left) if spelling == "=>" => {
1370            Ok(OptionExpression::new(left, typed_token(operator), right).into())
1371        }
1372        BinaryLeft::Expression(left) => {
1373            let spelling = spelling.to_owned();
1374            let operator = BinaryOperator::from_token_tree_with_trivia(
1375                operator.token,
1376                operator.trailing_trivia,
1377            )
1378            .ok_or(NativeParseError::Unsupported {
1379                syntax: spelling,
1380                span,
1381            })?;
1382            Ok(BinaryExpr::new(left, operator, right).into())
1383        }
1384    }
1385}
1386
1387fn lower_assignment(
1388    target: AssignmentTarget,
1389    operator: SignificantToken,
1390    right: Expr,
1391    local: bool,
1392) -> Expr {
1393    match (local, target) {
1394        (false, AssignmentTarget::Symbol(variable)) => {
1395            GlobalBinding::new(variable, typed_token(operator), right).into()
1396        }
1397        (true, AssignmentTarget::Symbol(variable)) => {
1398            LocalBinding::new(variable, typed_token(operator), right).into()
1399        }
1400        (false, AssignmentTarget::Operator(argument_expr)) => {
1401            OperatorAssignment::new(argument_expr, typed_token(operator), right).into()
1402        }
1403        (true, AssignmentTarget::Operator(OperatorExpr::AdjacentExpr(application))) => {
1404            let (typical_value, function) = installation_value(right);
1405            MethodInstallation::new(
1406                *application.left_operand,
1407                *application.right_operand,
1408                typed_token(operator),
1409                typical_value,
1410                function,
1411            )
1412            .into()
1413        }
1414        (true, AssignmentTarget::Operator(domain_expr)) => {
1415            let (typical_value, function) = installation_value(right);
1416            OperatorInstallation::new(domain_expr, typed_token(operator), typical_value, function)
1417                .into()
1418        }
1419        (true, AssignmentTarget::New(new_statement)) => {
1420            NewInstallation::new(new_statement, typed_token(operator), right).into()
1421        }
1422        (local, AssignmentTarget::Pack(argument_pack)) => {
1423            if local {
1424                LocalParallelAssignment::new(argument_pack, typed_token(operator), right).into()
1425            } else {
1426                GlobalParallelAssignment::new(argument_pack, typed_token(operator), right).into()
1427            }
1428        }
1429        (false, AssignmentTarget::New(_)) => {
1430            unreachable!("global assignment preparation rejects new statements")
1431        }
1432    }
1433}
1434
1435pub(crate) fn installation_value(value: Expr) -> (Option<TypeAnnotation>, Expr) {
1436    match value {
1437        Expr::OptionExpression(value) => (
1438            Some(TypeAnnotation::new(*value.key, value.operator)),
1439            *value.value,
1440        ),
1441        value => (None, value),
1442    }
1443}
1444
1445fn parse_cell_stream(cells: CellStream) -> Result<SourceFile, NativeParseError> {
1446    let mut elements = Vec::new();
1447    let eof_span = cells.physical_eof();
1448    let mut comments = CommentTrivia::default();
1449    let mut cells = cells.into_iter().peekable();
1450    while let Some(mut cell) = cells.next() {
1451        if !cell.has_significant_tokens() {
1452            comments.append(CommentTrivia::take_inner(&mut cell));
1453            continue;
1454        }
1455        let eoc = cells
1456            .peek()
1457            .and_then(cell_start_span)
1458            .unwrap_or_else(|| end_span(&cell));
1459        if let Some(element) = parse_raw_cell_at(cell, eoc)? {
1460            elements.push(element);
1461        }
1462    }
1463    let mut source_file = SourceFile::new(elements, EOF::new("", eof_span));
1464    source_file.comments = comments;
1465    Ok(source_file)
1466}
1467
1468fn tokenize_for_native_parser(tokens: TokenStream) -> Result<CellStream, NativeParseError> {
1469    match tokens.to_cell_stream() {
1470        Ok(cells) => Ok(cells),
1471        Err(tokenize_error) => {
1472            let incomplete = frame_recovering(tokens).incomplete;
1473            if let Some(cell) = incomplete
1474                && let Err(parse_error) = parse_raw_cell(cell)
1475            {
1476                return Err(parse_error);
1477            }
1478            Err(NativeParseError::Tokenize(tokenize_error))
1479        }
1480    }
1481}
1482
1483pub(crate) fn parse_raw_cell(
1484    tokens: TokenStream,
1485) -> Result<Option<crate::nodes::Cell>, NativeParseError> {
1486    let eoc = end_span(&tokens);
1487    parse_raw_cell_at(tokens, eoc)
1488}
1489
1490fn parse_raw_cell_at(
1491    mut tokens: TokenStream,
1492    eoc: Span,
1493) -> Result<Option<crate::nodes::Cell>, NativeParseError> {
1494    let comments = CommentTrivia::take_from_cell(&mut tokens);
1495    let semicolon = ends_with_cell_semicolon(&tokens)
1496        .then(|| tokens.take_final_punctuation(";"))
1497        .flatten()
1498        .map(|(punctuation, trailing_trivia)| {
1499            <Token![;] as Token>::from_token_tree_with_trivia(
1500                TokenTree::Punct(punctuation),
1501                trailing_trivia,
1502            )
1503            .expect("a semicolon punctuation token lowers to Token![;]")
1504        });
1505    let mut cell = Engine::new(tokens).parse_cell(semicolon, eoc)?;
1506    if let Some(cell) = &mut cell {
1507        cell.comments = comments;
1508    }
1509    Ok(cell)
1510}
1511
1512fn cell_start_span(tokens: &TokenStream) -> Option<Span> {
1513    let token = tokens.iter().next()?;
1514    let span = token
1515        .leading_trivia()
1516        .as_slice()
1517        .first()
1518        .map_or_else(|| token.span(), Spanned::span);
1519    span.start().map(|start| span.at(start))
1520}
1521
1522impl Parse for Expr {
1523    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
1524        let tokens = input.by_ref().collect::<TokenStream>();
1525        let mut engine = Engine::new(tokens);
1526        engine.skip_trivia();
1527        let value = engine
1528            .parse_required(PREC_CLOSER)
1529            .map_err(token_parse_error)?;
1530        engine.skip_trivia();
1531        match engine.peek() {
1532            Lookahead::End(_) => Ok(value),
1533            Lookahead::Token(token) => Err(TokenParseError::TrailingToken {
1534                found: token.token.description(),
1535                span: token.token.expect_span(),
1536            }),
1537        }
1538    }
1539}
1540
1541impl Parse for SourceFile {
1542    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
1543        let tokens = input.by_ref().collect();
1544        tokenize_for_native_parser(tokens)
1545            .and_then(parse_cell_stream)
1546            .map_err(token_parse_error)
1547    }
1548}
1549
1550fn token_parse_error(error: NativeParseError) -> TokenParseError {
1551    let span = error.expect_span();
1552    TokenParseError::Syntax {
1553        message: error.to_string(),
1554        span,
1555    }
1556}
1557
1558fn parsing_spelling(token: &TokenTree) -> &str {
1559    token.spelling().map_or("", canonical_word_spelling)
1560}
1561
1562fn typed_token<T: Token>(token: SignificantToken) -> T {
1563    let spelling = token.token.description();
1564    T::from_token_tree_with_trivia(token.token, token.trailing_trivia)
1565        .unwrap_or_else(|| panic!("`{spelling}` cannot be lowered to `{}`", T::SPELLING))
1566}
1567
1568fn typed_terminal<T: Terminal>(mut terminal: T, trailing_trivia: TrailingTrivia) -> T {
1569    *terminal.trailing_trivia_mut() = trailing_trivia;
1570    terminal
1571}
1572
1573fn empty_span_at(lookahead: &Span) -> Span {
1574    lookahead.subspan(0, 0)
1575}
1576
1577fn lower_string_literal(text: &str, span: Span, trailing_trivia: TrailingTrivia) -> Expr {
1578    let body = &text[1..text.len() - 1];
1579    let mut elements = Vec::new();
1580    let mut content_start = 0;
1581    let mut cursor = 0;
1582
1583    while cursor < body.len() {
1584        if body.as_bytes()[cursor] != b'\\' {
1585            cursor += 1;
1586            continue;
1587        }
1588        if content_start < cursor {
1589            elements.push(StringElement::StringContent(StringContent::new(
1590                &body[content_start..cursor],
1591                span.subspan(content_start + 1, cursor + 1),
1592            )));
1593        }
1594        let escape_end = match body.as_bytes()[cursor + 1] {
1595            b'x' => cursor + 4,
1596            b'u' => cursor + 6,
1597            b'0'..=b'7' => {
1598                let mut end = cursor + 2;
1599                while end < body.len()
1600                    && end < cursor + 4
1601                    && body.as_bytes()[end].is_ascii_digit()
1602                    && body.as_bytes()[end] < b'8'
1603                {
1604                    end += 1;
1605                }
1606                end
1607            }
1608            _ => cursor + 2,
1609        };
1610        elements.push(StringElement::EscapeSequence(EscapeSequence::new(
1611            &body[cursor..escape_end],
1612            span.subspan(cursor + 1, escape_end + 1),
1613        )));
1614        cursor = escape_end;
1615        content_start = cursor;
1616    }
1617    if content_start < body.len() {
1618        elements.push(StringElement::StringContent(StringContent::new(
1619            &body[content_start..],
1620            span.subspan(content_start + 1, text.len() - 1),
1621        )));
1622    }
1623    typed_terminal(StringLiteral::new(elements, span), trailing_trivia).into()
1624}
1625
1626fn lower_raw_string_literal(text: &str, span: Span, trailing_trivia: TrailingTrivia) -> Expr {
1627    let body = &text[3..text.len() - 3];
1628    let mut elements = Vec::new();
1629    let mut content_start = 0;
1630    let mut cursor = 0;
1631
1632    while cursor + 1 < body.len() {
1633        if &body.as_bytes()[cursor..cursor + 2] != b"//" {
1634            cursor += 1;
1635            continue;
1636        }
1637        if content_start < cursor {
1638            elements.push(RawStringElement::RawStringContent(RawStringContent::new(
1639                &body[content_start..cursor],
1640                span.subspan(content_start + 3, cursor + 3),
1641            )));
1642        }
1643        elements.push(RawStringElement::EscapeSequence(EscapeSequence::new(
1644            "//",
1645            span.subspan(cursor + 3, cursor + 5),
1646        )));
1647        cursor += 2;
1648        content_start = cursor;
1649    }
1650    if content_start < body.len() {
1651        elements.push(RawStringElement::RawStringContent(RawStringContent::new(
1652            &body[content_start..],
1653            span.subspan(content_start + 3, text.len() - 3),
1654        )));
1655    }
1656    typed_terminal(RawStringLiteral::new(elements, span), trailing_trivia).into()
1657}
1658
1659fn collection_contents(expression: Expr) -> Option<(crate::Delimiter, Option<ExprPack>)> {
1660    match expression {
1661        Expr::Collection(Collection::Sequence(value)) if value.statements.is_empty() => {
1662            Some((DelimiterValue::delimiter(&value.delimiter), value.elements))
1663        }
1664        Expr::Collection(Collection::List(value)) if value.statements.is_empty() => {
1665            Some((DelimiterValue::delimiter(&value.delimiter), value.elements))
1666        }
1667        Expr::Collection(Collection::Array(value)) if value.statements.is_empty() => {
1668            Some((DelimiterValue::delimiter(&value.delimiter), value.elements))
1669        }
1670        Expr::Collection(Collection::AngleBarList(value)) if value.statements.is_empty() => {
1671            Some((DelimiterValue::delimiter(&value.delimiter), value.elements))
1672        }
1673        Expr::Holder(value) if value.muted.is_empty() => {
1674            let elements = value
1675                .value
1676                .map(|value| ExprPack::new(Punctuated::from_single(Component::Expr(value))));
1677            Some((DelimiterValue::delimiter(&value.delimiter), elements))
1678        }
1679        _ => None,
1680    }
1681}
1682
1683pub(crate) fn assignment_pack(expression: Expr) -> Option<AssignmentPack> {
1684    let (delimiter, elements) = collection_contents(expression)?;
1685    let contents = match elements {
1686        Some(elements) => Some(map_punctuated(elements.pack, assignment_pack_component)?),
1687        None => None,
1688    };
1689    Some(AssignmentPack::new(Delimited::new(delimiter, contents)))
1690}
1691
1692fn assignment_pack_component(component: Component) -> Option<AssignmentPackComponent> {
1693    match component {
1694        Component::Empty(value) => Some(AssignmentPackComponent::Empty(value)),
1695        Component::Expr(value) => match *value {
1696            Expr::Symbol(value) => Some(AssignmentPackComponent::Symbol(value)),
1697            Expr::OperatorExpr(value) => Some(AssignmentPackComponent::OperatorExpr(value)),
1698            value => assignment_pack(value).map(Into::into),
1699        },
1700    }
1701}
1702
1703fn expr_pack_symbols(elements: ExprPack) -> Option<Punctuated<Symbol>> {
1704    map_punctuated(elements.pack, |component| match component {
1705        Component::Expr(value) => match *value {
1706            Expr::Symbol(value) => Some(value),
1707            _ => None,
1708        },
1709        Component::Empty(_) => None,
1710    })
1711}
1712
1713fn map_punctuated<S, T>(
1714    values: Punctuated<S>,
1715    mut map: impl FnMut(S) -> Option<T>,
1716) -> Option<Punctuated<T>> {
1717    let (first, rest) = values.into_parts();
1718    let first = map(first)?;
1719    let rest = rest
1720        .into_iter()
1721        .map(|(punctuation, value)| Some((punctuation, map(value)?)))
1722        .collect::<Option<Vec<_>>>()?;
1723    Some(Punctuated::new(first, rest))
1724}
1725
1726pub(crate) fn lambda_parameters(expression: Expr) -> Option<LambdaParameters> {
1727    match expression {
1728        Expr::Symbol(value) => Some(LambdaParameters::Variadic(Variadic::new(value))),
1729        expression => {
1730            let (delimiter, elements) = collection_contents(expression)?;
1731            let parameters = match elements {
1732                Some(elements) => Some(expr_pack_symbols(elements)?),
1733                None => None,
1734            };
1735            Some(LambdaParameters::FixedArity(FixedArity::new(
1736                Delimited::new(delimiter, parameters),
1737            )))
1738        }
1739    }
1740}
1741
1742/// The rule declared for a spelling, found in the generated table.
1743fn token_parse_rule(spelling: &str) -> Option<TokenParseRule> {
1744    TOKEN_PARSE_RULES
1745        .binary_search_by_key(&spelling, |(declared, _)| *declared)
1746        .ok()
1747        .map(|index| TOKEN_PARSE_RULES[index].1)
1748}
1749
1750fn keyword_action(spelling: &str) -> Option<UnaryAction> {
1751    Some(match spelling {
1752        "break" | "continue" | "return" | "catch" | "throw" | "trap" => UnaryAction::Statement,
1753        "if" | "while" | "try" | "for" | "new" => UnaryAction::Control,
1754        "TEST" | "time" | "timing" | "elapsedTime" | "elapsedTiming" | "breakpoint" | "profile"
1755        | "shield" | "step" | "finish" => UnaryAction::Debug,
1756        "symbol" | "global" | "local" | "threadLocal" | "threadVariable" => UnaryAction::Quote,
1757        _ => return None,
1758    })
1759}
1760
1761fn parse_info(token: &TokenTree) -> ParseInfo {
1762    if matches!(token, TokenTree::Punct(punctuation) if punctuation.text().is_empty()) {
1763        return ParseInfo::binary(PREC_APPLICATION, PREC_APPLICATION_RIGHT);
1764    }
1765    if let TokenTree::Group(group) = token {
1766        return match group.delim_kind() {
1767            DelimiterKind::Bracket | DelimiterKind::AngleBar => {
1768                ParseInfo::delimiter(PREC_COLLECTION)
1769            }
1770            DelimiterKind::Parenthesis | DelimiterKind::Brace => {
1771                ParseInfo::delimiter(PREC_APPLICATION)
1772            }
1773        };
1774    }
1775
1776    let text = parsing_spelling(token);
1777    if let Some(rule) = token_parse_rule(text) {
1778        let mut info = ParseInfo::from(rule);
1779        if let Some(action) = keyword_action(text) {
1780            info.unary = action;
1781        }
1782        return info;
1783    }
1784
1785    match token {
1786        TokenTree::Ident(_) | TokenTree::Literal(_) => ParseInfo::atom(PREC_APPLICATION),
1787        _ => ParseInfo::stop(PREC_APPLICATION),
1788    }
1789}
1790
1791#[cfg(test)]
1792mod tests {
1793    use super::{NativeCellParser, NativeCellState};
1794    use crate::cell_stream::frame_recovering;
1795    use crate::{Spanned, lex_str};
1796
1797    /// Cell boundaries are decided here, from the flat stream, using the same
1798    /// operator/clause/quote state the grammar needs. Verified against M2
1799    /// v1.26.05 for each case.
1800    #[test]
1801    fn cells_use_operator_clause_and_delimiter_state() {
1802        fn cells(source: &str) -> usize {
1803            let framed = frame_recovering(lex_str(source).unwrap());
1804            framed.cells.len() + usize::from(framed.incomplete.is_some())
1805        }
1806
1807        assert_eq!(cells("1\n2"), 2);
1808        assert_eq!(cells("1 +\n2"), 1);
1809        assert_eq!(cells("not\n1"), 1);
1810        assert_eq!(cells("1!\n2"), 2);
1811        assert_eq!(cells("if condition\nthen value"), 1);
1812        assert_eq!(cells("if condition\nthen value\nnext"), 2);
1813        assert_eq!(cells("symbol if\nnext"), 2);
1814        assert_eq!(cells("symbol +\nnext"), 2);
1815        assert_eq!(cells("1 -* comment\n*- 2"), 2);
1816        assert_eq!(cells("1 + -* comment\n*- 2"), 1);
1817        assert_eq!(cells("1;2"), 2);
1818        // A `;` inside brackets is an ordinary token, so the group stays one
1819        // cell and its contents are untouched by the embedding.
1820        assert_eq!(cells("(1\n2;3)"), 1);
1821        // The quoted `;` is data, not a boundary.
1822        assert_eq!(cells("symbol ;"), 1);
1823    }
1824
1825    #[test]
1826    fn invalid_binary_left_side_wins_over_a_missing_right_operand() {
1827        for (source, state, diagnostic, diagnostic_text) in [
1828            (
1829                "1 + 1 =",
1830                NativeCellState::Incomplete,
1831                "expected an operand after `=`, found end of input",
1832                "=",
1833            ),
1834            (
1835                "2 =",
1836                NativeCellState::Invalid,
1837                "invalid assignment target",
1838                "=",
1839            ),
1840            (
1841                "(x, y) ->",
1842                NativeCellState::Incomplete,
1843                "expected an operand after `->`, found end of input",
1844                "->",
1845            ),
1846            (
1847                "(x + y) ->",
1848                NativeCellState::Invalid,
1849                "invalid lambda parameter list",
1850                "->",
1851            ),
1852        ] {
1853            let mut parser = NativeCellParser::new();
1854            assert_eq!(parser.push_line(source), state, "source: `{source}`");
1855            let error = parser
1856                .parse_error()
1857                .unwrap_or_else(|| panic!("expected a diagnostic for `{source}`"));
1858            assert_eq!(error.to_string(), diagnostic, "source: `{source}`");
1859            assert_eq!(error.span().text(), Some(diagnostic_text));
1860        }
1861    }
1862}