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::lexer::{canonical_word_spelling, split_cells};
12use crate::nodes::*;
13use crate::parse::{Lookahead, SignificantToken};
14use crate::{
15    Delimited, DelimiterKind, DelimiterToken, DelimiterValue, LexError, LiteralKind, Parse,
16    ParseStream, Parser, Punctuated, Recovered, Span, Spanned, Token, TokenParseError, TokenStream,
17    TokenTree, lex_str, lex_str_recovering,
18};
19
20macro_rules! is_token {
21    ($token:expr, $($pattern:tt)*) => {
22        <Token![$($pattern)*] as Token>::matches_token_tree($token)
23    };
24}
25
26macro_rules! unexpected {
27    ($token:expr, $expected:expr) => {{
28        let token = $token;
29        NativeParseError::Unexpected {
30            span: token.span(),
31            found: token.description(),
32            expected: $expected,
33        }
34    }};
35}
36
37/// The built-in parser implemented directly from Macaulay2's `P/B/U` table.
38#[derive(Debug)]
39pub struct NativeParser;
40
41impl Parser for NativeParser {
42    type Error = NativeParseError;
43
44    fn parse_stream(&mut self, tokens: TokenStream) -> Result<SourceFile, Self::Error> {
45        parse_cells(tokens)
46    }
47}
48
49/// Parses a complete source file with the native precedence engine.
50///
51/// # Errors
52///
53/// Returns [`NativeParseError`] when lexing fails or the token sequence is not
54/// valid complete Macaulay2 syntax.
55pub fn parse_native(source: &str) -> Result<SourceFile, NativeParseError> {
56    let tokens = lex_str(source).map_err(NativeParseError::Lex)?;
57    NativeParser.parse_stream(tokens)
58}
59
60/// Parses source while retaining valid syntax inside unclosed delimiters.
61///
62/// Missing closing delimiters become zero-width closing spans at end of input
63/// and are returned as non-fatal lexical diagnostics. Other lexical or
64/// grammatical failures remain fatal.
65///
66/// # Errors
67///
68/// Returns [`NativeParseError`] when source cannot be recovered into a complete
69/// typed file.
70pub fn parse_native_recovering(
71    source: &str,
72) -> Result<Recovered<SourceFile, LexError>, NativeParseError> {
73    let recovered = lex_str_recovering(source).map_err(NativeParseError::Lex)?;
74    let (tokens, diagnostics) = recovered.into_parts();
75    let syntax = NativeParser.parse_stream(tokens)?;
76    Ok(Recovered::new(syntax, diagnostics))
77}
78
79#[derive(Debug, Spanned)]
80/// An error produced by the native lexer or precedence parser.
81pub enum NativeParseError {
82    /// Tokenization failed before parsing began.
83    Lex(LexError),
84    /// The parser encountered a token that cannot occur in this position.
85    Unexpected {
86        /// A readable description of the received token.
87        found: String,
88        /// A readable description of the required syntax.
89        expected: &'static str,
90        /// The received token or end-of-input position.
91        span: Span,
92    },
93    /// A prefix or binary operator has no operand.
94    MissingOperand {
95        /// The operator spelling.
96        operator: String,
97        /// The operator or end-of-input position.
98        span: Span,
99    },
100    /// Implicit function application crossed a top-level line break.
101    NewlineInApplication {
102        /// The token following the line break.
103        span: Span,
104    },
105    /// The left side of an assignment is not assignable.
106    InvalidAssignmentTarget {
107        /// The assignment operator span.
108        span: Span,
109    },
110    /// The left side of a lambda arrow is not a parameter form.
111    InvalidLambdaParameters {
112        /// The lambda arrow span.
113        span: Span,
114    },
115    /// The grammar recognizes syntax that this native backend cannot lower yet.
116    Unsupported {
117        /// The unsupported syntax spelling.
118        syntax: String,
119        /// The unsupported token span.
120        span: Span,
121    },
122}
123
124impl NativeParseError {
125    fn unexpected_end(expected: &'static str, span: Span) -> Self {
126        Self::Unexpected {
127            found: "end of input".into(),
128            expected,
129            span,
130        }
131    }
132
133    fn missing_operand_for(self, operator: &TokenTree) -> Self {
134        if matches!(self, Self::MissingOperand { .. }) {
135            Self::MissingOperand {
136                operator: operator.description(),
137                span: operator.span(),
138            }
139        } else {
140            self
141        }
142    }
143}
144
145impl Display for NativeParseError {
146    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
147        match self {
148            Self::Lex(error) => error.fmt(formatter),
149            Self::Unexpected {
150                found, expected, ..
151            } => write!(formatter, "expected {expected}, found `{found}`"),
152            Self::MissingOperand { operator, .. } => {
153                write!(
154                    formatter,
155                    "expected an operand after `{operator}`, found end of input"
156                )
157            }
158            Self::NewlineInApplication { .. } => {
159                formatter.write_str("implicit application cannot cross a line break")
160            }
161            Self::InvalidAssignmentTarget { .. } => {
162                formatter.write_str("invalid assignment target")
163            }
164            Self::InvalidLambdaParameters { .. } => {
165                formatter.write_str("invalid lambda parameter list")
166            }
167            Self::Unsupported { syntax, .. } => {
168                write!(
169                    formatter,
170                    "native parsing of `{syntax}` is not implemented yet"
171                )
172            }
173        }
174    }
175}
176
177impl Error for NativeParseError {
178    fn source(&self) -> Option<&(dyn Error + 'static)> {
179        match self {
180            Self::Lex(error) => Some(error),
181            _ => None,
182        }
183    }
184}
185
186#[derive(Debug, Clone, Copy)]
187/// Parser-local instruction for interpreting a token in unary position.
188///
189/// This is not a syntax category and is never stored in the token stream or
190/// typed CST. [`parse_info`] derives it temporarily from the current raw token
191/// so the precedence engine can select the corresponding parsing routine.
192enum UnaryAction {
193    Atom,
194    Prefix,
195    Statement,
196    Control,
197    Debug,
198    Delimiter,
199    Quote,
200    Error,
201}
202
203#[derive(Debug, Clone, Copy)]
204struct ParseInfo {
205    precedence: u8,
206    binary_strength: Option<u8>,
207    unary_strength: Option<u8>,
208    unary: UnaryAction,
209    postfix: bool,
210}
211
212impl From<TokenParseRule> for ParseInfo {
213    fn from(rule: TokenParseRule) -> Self {
214        match rule {
215            TokenParseRule::Precedence(precedence) => Self::stop(precedence),
216            TokenParseRule::Postfix(precedence) => Self::postfix(precedence),
217            TokenParseRule::Binary(precedence, strength) => Self::binary(precedence, strength),
218            TokenParseRule::Prefix(precedence, strength) => {
219                Self::unary(precedence, UnaryAction::Prefix, strength)
220            }
221            TokenParseRule::PrefixBinary(precedence, binary, unary) => {
222                Self::prefix_binary(precedence, binary, unary)
223            }
224            TokenParseRule::Unary(precedence, strength) => {
225                Self::unary(precedence, UnaryAction::Error, strength)
226            }
227        }
228    }
229}
230
231impl ParseInfo {
232    const fn atom(precedence: u8) -> Self {
233        Self {
234            precedence,
235            binary_strength: None,
236            unary_strength: None,
237            unary: UnaryAction::Atom,
238            postfix: false,
239        }
240    }
241
242    const fn binary(precedence: u8, strength: u8) -> Self {
243        Self {
244            precedence,
245            binary_strength: Some(strength),
246            unary_strength: None,
247            unary: UnaryAction::Error,
248            postfix: false,
249        }
250    }
251
252    const fn prefix_binary(precedence: u8, binary_strength: u8, unary_strength: u8) -> Self {
253        Self {
254            precedence,
255            binary_strength: Some(binary_strength),
256            unary_strength: Some(unary_strength),
257            unary: UnaryAction::Prefix,
258            postfix: false,
259        }
260    }
261
262    /// A token whose action in unary position is to introduce something that
263    /// binds at `strength`: a statement, a control form, a timing form, or a
264    /// quoted name.
265    const fn unary(precedence: u8, unary: UnaryAction, strength: u8) -> Self {
266        Self {
267            precedence,
268            binary_strength: None,
269            unary_strength: Some(strength),
270            unary,
271            postfix: false,
272        }
273    }
274
275    const fn postfix(precedence: u8) -> Self {
276        Self {
277            precedence,
278            binary_strength: None,
279            unary_strength: None,
280            unary: UnaryAction::Error,
281            postfix: true,
282        }
283    }
284
285    const fn delimiter(precedence: u8) -> Self {
286        Self {
287            precedence,
288            binary_strength: None,
289            unary_strength: Some(PREC_CLOSER),
290            unary: UnaryAction::Delimiter,
291            postfix: false,
292        }
293    }
294
295    const fn stop(precedence: u8) -> Self {
296        Self {
297            precedence,
298            binary_strength: None,
299            unary_strength: None,
300            unary: UnaryAction::Error,
301            postfix: false,
302        }
303    }
304}
305
306struct Engine {
307    input: ParseStream,
308}
309
310impl Engine {
311    fn new(tokens: TokenStream) -> Self {
312        Self {
313            input: ParseStream::new(tokens),
314        }
315    }
316
317    fn parse_cell(
318        mut self,
319        semicolon: Option<Span>,
320        eoc_span: Span,
321    ) -> Result<Option<crate::nodes::Cell>, NativeParseError> {
322        self.skip_trivia();
323        if matches!(self.peek(), Lookahead::End(_)) {
324            return match semicolon {
325                None => Ok(None),
326                Some(span) => Err(NativeParseError::Unexpected {
327                    found: ";".into(),
328                    expected: "a statement before `;`",
329                    span,
330                }),
331            };
332        }
333
334        let (components, _) = self.parse_components()?;
335
336        if let Lookahead::Token(token) = self.peek() {
337            return Err(unexpected!(token.token, "the end of the cell"));
338        }
339
340        let eoc = Eoc::new("", eoc_span);
341        let cell =
342            crate::nodes::Cell::new(ExprPack::new(components), semicolon.map(Token![;]), eoc);
343        Ok(Some(cell))
344    }
345
346    fn parse_components(&mut self) -> Result<(Punctuated<Component>, bool), NativeParseError> {
347        let first = if self.at_punctuation(",") {
348            Component::Empty(Empty::new("", empty_span_at(&self.peek().span())))
349        } else {
350            self.parse_required(PREC_COMMA)?.into()
351        };
352        let mut elements = Punctuated::from_single(first);
353        let mut has_comma = false;
354
355        while let Some(comma) = self.take_punctuation(",") {
356            has_comma = true;
357            let next = if self.at_end() || self.at_punctuation(";") || self.at_punctuation(",") {
358                Component::Empty(Empty::new("", empty_span_at(&self.peek().span())))
359            } else {
360                self.parse_required(PREC_COMMA)?.into()
361            };
362            elements.push(typed_token(comma), next);
363        }
364
365        Ok((elements, has_comma))
366    }
367
368    fn parse_optional_components(
369        &mut self,
370    ) -> Result<Option<(Punctuated<Component>, bool)>, NativeParseError> {
371        if self.at_end() || self.at_punctuation(";") {
372            Ok(None)
373        } else {
374            self.parse_components().map(Some)
375        }
376    }
377
378    fn parse_required(&mut self, level: u8) -> Result<Expr, NativeParseError> {
379        let token = match self.consume() {
380            Lookahead::Token(token) => token,
381            Lookahead::End(span) => {
382                return Err(NativeParseError::MissingOperand {
383                    operator: "end of input".into(),
384                    span,
385                });
386            }
387        };
388        self.parse_consumed(token, level)
389    }
390
391    fn parse_nullable(&mut self, level: u8) -> Result<Option<Expr>, NativeParseError> {
392        let lookahead = self.peek();
393        let Lookahead::Token(token) = lookahead else {
394            return Ok(None);
395        };
396        if parse_info(&token.token).precedence <= level {
397            return Ok(None);
398        }
399        let Lookahead::Token(token) = self.consume() else {
400            unreachable!("the token just peeked is consumable")
401        };
402        self.parse_consumed(token, level).map(Some)
403    }
404
405    fn parse_consumed(
406        &mut self,
407        token: SignificantToken,
408        level: u8,
409    ) -> Result<Expr, NativeParseError> {
410        let info = parse_info(&token.token);
411        let result = match info.unary {
412            UnaryAction::Atom => lower_atom(token.token)?,
413            UnaryAction::Prefix => {
414                let strength = info.unary_strength.expect("prefix operators have U");
415                let operand = self.parse_required(level.max(strength))?;
416                let span = token.token.span();
417                let spelling = token.token.description();
418                let operator = PrefixOperator::from_token_tree(token.token).ok_or({
419                    NativeParseError::Unsupported {
420                        syntax: spelling,
421                        span,
422                    }
423                })?;
424                PrefixExpr::new(operator, operand).into()
425            }
426            UnaryAction::Statement => {
427                let strength = info
428                    .unary_strength
429                    .expect("prefix statements have a unary binding strength");
430                let operand = if is_token!(&token.token, break)
431                    || is_token!(&token.token, continue)
432                    || is_token!(&token.token, return)
433                {
434                    self.parse_nullable(level.max(strength))?
435                } else {
436                    self.parse_required(level.max(strength))
437                        .map_err(|error| error.missing_operand_for(&token.token))?
438                        .into()
439                };
440                lower_prefix_statement(token.token, operand)
441            }
442            UnaryAction::Control => {
443                if is_token!(&token.token, if) {
444                    self.parse_if_statement(token.token)?
445                } else if is_token!(&token.token, for) {
446                    self.parse_for_loop(token.token)?
447                } else if is_token!(&token.token, while) {
448                    self.parse_while_loop(token.token)?
449                } else if is_token!(&token.token, try) {
450                    self.parse_try_statement(token.token)?
451                } else if is_token!(&token.token, new) {
452                    self.parse_new_statement(token.token)?
453                } else {
454                    unreachable!("control actions are assigned only to control keywords")
455                }
456            }
457            UnaryAction::Debug => self.parse_debug_clause(token.token)?,
458            UnaryAction::Delimiter => parse_delimited(token.token)?,
459            UnaryAction::Quote => {
460                let operator = token.token.description();
461                let quoted = match self.consume() {
462                    Lookahead::Token(quoted) => quoted.token,
463                    Lookahead::End(span) => {
464                        return Err(NativeParseError::MissingOperand { operator, span });
465                    }
466                };
467                let specifier = if is_token!(&token.token, symbol) {
468                    QuoteSpecifier::Symbol(typed_token(token.token))
469                } else if is_token!(&token.token, local) {
470                    QuoteSpecifier::Local(typed_token(token.token))
471                } else if is_token!(&token.token, global) {
472                    QuoteSpecifier::Global(typed_token(token.token))
473                } else if is_token!(&token.token, threadVariable) {
474                    QuoteSpecifier::ThreadVariable(typed_token(token.token))
475                } else if is_token!(&token.token, threadLocal) {
476                    QuoteSpecifier::ThreadLocal(typed_token(token.token))
477                } else {
478                    unreachable!("quote actions are assigned only to quote specifiers")
479                };
480                let quoted_span = quoted.span();
481                let Some(quoted_text) = quoted.spelling().map(str::to_owned) else {
482                    return Err(unexpected!(quoted, "a quoted token"));
483                };
484                let quoted = Symbol::new(quoted_text, quoted_span);
485                QuoteExpression::new(specifier, quoted).into()
486            }
487            UnaryAction::Error => return Err(unexpected!(token.token, "an expression")),
488        };
489        self.accumulate(result, level)
490    }
491
492    fn parse_if_statement(&mut self, keyword: TokenTree) -> Result<Expr, NativeParseError> {
493        let condition = self.required_clause_expression(PREC_CONTROL, &keyword)?;
494        let then_keyword = self.consume_exact("then")?;
495        let then_value = self.required_clause_expression(PREC_CONTROL, &then_keyword)?;
496        let then_clause = ThenClause::new(typed_token(then_keyword), then_value);
497        let else_clause = self
498            .take_exact("else")
499            .map(|else_keyword| {
500                let value = self.required_clause_expression(PREC_CONTROL, &else_keyword)?;
501                Ok::<_, NativeParseError>(ElseClause::new(typed_token(else_keyword), value))
502            })
503            .transpose()?;
504        Ok(IfStatement::new(typed_token(keyword), condition, then_clause, else_clause).into())
505    }
506
507    fn parse_for_loop(&mut self, keyword: TokenTree) -> Result<Expr, NativeParseError> {
508        let variable = self.consume_symbol("a loop variable")?;
509
510        let iteration_domain = if let Some(token) = self.take_exact("in") {
511            let collection = self.required_clause_expression(PREC_LOOP_CLAUSE, &token)?;
512            Some(IterIn::new(typed_token(token), collection).into())
513        } else {
514            let lower_bound = self
515                .take_exact("from")
516                .map(|token| {
517                    let value = self.required_clause_expression(PREC_LOOP_CLAUSE, &token)?;
518                    Ok::<_, NativeParseError>(RangeBoundFrom::new(typed_token(token), value))
519                })
520                .transpose()?;
521            let upper_bound = self
522                .take_exact("to")
523                .map(|token| {
524                    let value = self.required_clause_expression(PREC_LOOP_CLAUSE, &token)?;
525                    Ok::<_, NativeParseError>(RangeBoundTo::new(typed_token(token), value))
526                })
527                .transpose()?;
528            (lower_bound.is_some() || upper_bound.is_some())
529                .then(|| IterRange::new(lower_bound, upper_bound).into())
530        };
531
532        let when_condition = if let Some(token) = self.take_exact("when") {
533            let condition = self.required_clause_expression(PREC_LOOP_CLAUSE, &token)?;
534            Some(WhenCondition::new(typed_token(token), condition))
535        } else {
536            None
537        };
538        let body = self.parse_loop_body()?;
539
540        Ok(ForLoop::new(
541            typed_token(keyword),
542            variable,
543            iteration_domain,
544            when_condition,
545            body,
546        )
547        .into())
548    }
549
550    fn parse_while_loop(&mut self, keyword: TokenTree) -> Result<Expr, NativeParseError> {
551        let condition = self.required_clause_expression(PREC_CONTROL, &keyword)?;
552        let body = self.parse_loop_body()?;
553        Ok(WhileLoop::new(typed_token(keyword), condition, body).into())
554    }
555
556    fn parse_loop_body(&mut self) -> Result<LoopBody, NativeParseError> {
557        let (list_step, do_step) = if let Some(token) = self.take_exact("list") {
558            let listed_value = self.required_clause_expression(PREC_CONTROL, &token)?;
559            let list_step = Some(ListStep::new(typed_token(token), listed_value));
560            if let Some(token) = self.take_exact("do") {
561                let ignored_value = self.required_clause_expression(PREC_CONTROL, &token)?;
562                (
563                    list_step,
564                    Some(DoStep::new(typed_token(token), ignored_value)),
565                )
566            } else {
567                (list_step, None)
568            }
569        } else if let Some(token) = self.take_exact("do") {
570            let ignored_value = self.required_clause_expression(PREC_CONTROL, &token)?;
571            (None, Some(DoStep::new(typed_token(token), ignored_value)))
572        } else {
573            return Err(self.expected_at_lookahead("`list` or `do`"));
574        };
575
576        Ok(LoopBody::new(list_step, do_step))
577    }
578
579    fn parse_try_statement(&mut self, keyword: TokenTree) -> Result<Expr, NativeParseError> {
580        let value = self.required_clause_expression(PREC_CONTROL, &keyword)?;
581        let then_clause = self
582            .take_exact("then")
583            .map(|then_keyword| {
584                let value = self.required_clause_expression(PREC_CONTROL, &then_keyword)?;
585                Ok::<_, NativeParseError>(ThenClause::new(typed_token(then_keyword), value))
586            })
587            .transpose()?;
588
589        let fallback = if let Some(else_keyword) = self.take_exact("else") {
590            let value = self.required_clause_expression(PREC_CONTROL, &else_keyword)?;
591            Some(TryFallback::ElseClause(ElseClause::new(
592                typed_token(else_keyword),
593                value,
594            )))
595        } else if let Some(except_keyword) = self.take_exact("except") {
596            let exception = self.consume_symbol("an exception symbol")?;
597            let do_keyword = self.consume_exact("do")?;
598            let value = self.required_clause_expression(PREC_CONTROL, &do_keyword)?;
599            Some(TryFallback::ExceptDo(ExceptDo::new(
600                typed_token(except_keyword),
601                exception,
602                typed_token(do_keyword),
603                value,
604            )))
605        } else {
606            None
607        };
608
609        Ok(TryStatement::new(typed_token(keyword), value, then_clause, fallback).into())
610    }
611
612    fn parse_new_statement(&mut self, keyword: TokenTree) -> Result<Expr, NativeParseError> {
613        let class = self.required_clause_expression(PREC_LOOP_CLAUSE, &keyword)?;
614        let parent_class = if let Some(token) = self.take_exact("of") {
615            let parent = self.required_clause_expression(PREC_LOOP_CLAUSE, &token)?;
616            Some(OfClass::new(typed_token(token), parent))
617        } else {
618            None
619        };
620        let instance = if let Some(token) = self.take_exact("from") {
621            let instance = self.required_clause_expression(PREC_LOOP_CLAUSE, &token)?;
622            Some(FromInstance::new(typed_token(token), instance))
623        } else {
624            None
625        };
626
627        Ok(NewStatement::new(typed_token(keyword), class, parent_class, instance).into())
628    }
629
630    fn parse_debug_clause(&mut self, keyword: TokenTree) -> Result<Expr, NativeParseError> {
631        let spelling = parsing_spelling(&keyword).to_owned();
632        if matches!(
633            spelling.as_str(),
634            "time" | "timing" | "elapsedTime" | "elapsedTiming" | "profile"
635        ) {
636            let value = self
637                .parse_required(PREC_CONTROL)
638                .map_err(|error| error.missing_operand_for(&keyword))?;
639            let modifier = match spelling.as_str() {
640                "time" => ProfilingModifier::Time(typed_token(keyword)),
641                "timing" => ProfilingModifier::Timing(typed_token(keyword)),
642                "elapsedTime" => ProfilingModifier::ElapsedTime(typed_token(keyword)),
643                "elapsedTiming" => ProfilingModifier::ElapsedTiming(typed_token(keyword)),
644                "profile" => ProfilingModifier::Profile(typed_token(keyword)),
645                _ => unreachable!(),
646            };
647            return Ok(ProfilingExpr::new(modifier, value).into());
648        }
649        let value_is_optional = matches!(spelling.as_str(), "step" | "finish");
650        let value = if value_is_optional {
651            self.parse_nullable(PREC_CONTROL)?
652        } else {
653            Some(
654                self.parse_required(PREC_CONTROL)
655                    .map_err(|error| error.missing_operand_for(&keyword))?,
656            )
657        };
658        let keyword = match spelling.as_str() {
659            "step" => DebugKeyword::Step(typed_token(keyword)),
660            "finish" => DebugKeyword::Finish(typed_token(keyword)),
661            "shield" => DebugKeyword::Shield(typed_token(keyword)),
662            "TEST" => DebugKeyword::Test(typed_token(keyword)),
663            "breakpoint" => DebugKeyword::Breakpoint(typed_token(keyword)),
664            _ => unreachable!("debug actions are assigned only to debug keywords"),
665        };
666        Ok(DebugExpr::new(keyword, value).into())
667    }
668
669    fn required_clause_expression(
670        &mut self,
671        level: u8,
672        keyword: &TokenTree,
673    ) -> Result<Expr, NativeParseError> {
674        match self.parse_nullable(level)? {
675            None => Err(NativeParseError::MissingOperand {
676                operator: keyword.description(),
677                span: keyword.span(),
678            }),
679            Some(expression) => Ok(expression),
680        }
681    }
682
683    fn consume_symbol(&mut self, expected: &'static str) -> Result<Symbol, NativeParseError> {
684        match self.consume() {
685            Lookahead::Token(token)
686                if matches!(token.token, TokenTree::Ident(_))
687                    && matches!(parse_info(&token.token).unary, UnaryAction::Atom) =>
688            {
689                let span = token.token.span();
690                let text = token.token.description();
691                Ok(Symbol::new(text, span))
692            }
693            Lookahead::Token(token) => Err(unexpected!(token.token, expected)),
694            Lookahead::End(span) => Err(NativeParseError::unexpected_end(expected, span)),
695        }
696    }
697
698    fn consume_exact(&mut self, spelling: &'static str) -> Result<TokenTree, NativeParseError> {
699        match self.consume() {
700            Lookahead::Token(token) if parsing_spelling(&token.token) == spelling => {
701                Ok(token.token)
702            }
703            Lookahead::Token(token) => Err(unexpected!(token.token, spelling)),
704            Lookahead::End(span) => Err(NativeParseError::unexpected_end(spelling, span)),
705        }
706    }
707
708    fn take_exact(&mut self, spelling: &'static str) -> Option<TokenTree> {
709        if matches!(
710            self.peek(),
711            Lookahead::Token(token)
712                if parsing_spelling(&token.token) == spelling
713        ) {
714            let Lookahead::Token(token) = self.consume() else {
715                unreachable!("the token just peeked is consumable")
716            };
717            Some(token.token)
718        } else {
719            None
720        }
721    }
722
723    fn expected_at_lookahead(&self, expected: &'static str) -> NativeParseError {
724        match self.peek() {
725            Lookahead::Token(token) => unexpected!(token.token, expected),
726            Lookahead::End(span) => NativeParseError::unexpected_end(expected, span),
727        }
728    }
729
730    fn accumulate(&mut self, mut left: Expr, level: u8) -> Result<Expr, NativeParseError> {
731        loop {
732            let Lookahead::Token(next) = self.peek() else {
733                return Ok(left);
734            };
735            let info = parse_info(&next.token);
736            if info.precedence <= level {
737                return Ok(left);
738            }
739            let Lookahead::Token(operator) = self.consume() else {
740                unreachable!("the token just peeked is consumable")
741            };
742            left = if let Some(strength) = info.binary_strength {
743                let right = self
744                    .parse_required(strength)
745                    .map_err(|error| error.missing_operand_for(&operator.token))?;
746                lower_binary(left, operator.token, right)?
747            } else if info.postfix {
748                let span = operator.token.span();
749                let spelling = operator.token.description();
750                let operator = PostfixOperator::from_token_tree(operator.token).ok_or(
751                    NativeParseError::Unsupported {
752                        syntax: spelling,
753                        span,
754                    },
755                )?;
756                PostfixExpr::new(left, operator).into()
757            } else if !matches!(info.unary, UnaryAction::Error) {
758                if operator.crossed_newline {
759                    return Err(NativeParseError::NewlineInApplication {
760                        span: operator.token.span(),
761                    });
762                }
763                let adjacency_span = operator
764                    .token
765                    .span()
766                    .start()
767                    .map_or_else(Span::detached, Span::from_offset);
768                let right = self.parse_consumed(operator, PREC_APPLICATION_RIGHT)?;
769                AdjacentExpr::new(left, Adj::new("", adjacency_span), right).into()
770            } else {
771                return Err(unexpected!(operator.token, "an operator"));
772            };
773        }
774    }
775
776    fn only_expression(&self, components: Punctuated<Component>) -> Result<Expr, NativeParseError> {
777        let mut components = components.into_iter();
778        match (components.next(), components.next()) {
779            (Some(Component::Expr(value)), None) => Ok(*value),
780            (Some(value), _) => Err(NativeParseError::Unexpected {
781                found: "a sequence".into(),
782                expected: "one expression",
783                span: value.span(),
784            }),
785            (None, _) => Err(NativeParseError::MissingOperand {
786                operator: "empty expression".into(),
787                span: self.input.eof_span(),
788            }),
789        }
790    }
791
792    fn peek(&self) -> Lookahead {
793        self.input.lookahead()
794    }
795
796    fn consume(&mut self) -> Lookahead {
797        self.input.consume_lookahead()
798    }
799
800    fn skip_trivia(&mut self) {
801        self.input.skip_trivia();
802    }
803
804    fn at_end(&self) -> bool {
805        matches!(self.peek(), Lookahead::End(_))
806    }
807
808    fn at_punctuation(&self, spelling: &str) -> bool {
809        matches!(
810            self.peek(),
811            Lookahead::Token(token)
812                if token.token.spelling() == Some(spelling)
813        )
814    }
815
816    fn take_punctuation(&mut self, spelling: &str) -> Option<TokenTree> {
817        if !self.at_punctuation(spelling) {
818            return None;
819        }
820        let Lookahead::Token(token) = self.consume() else {
821            unreachable!("the punctuation just peeked is consumable")
822        };
823        Some(token.token)
824    }
825}
826
827fn parse_delimited(token: TokenTree) -> Result<Expr, NativeParseError> {
828    let TokenTree::Group(group) = token else {
829        return Err(unexpected!(token, "a delimited group"));
830    };
831    let kind = group.delim_kind();
832    let delimiter_spans = group.delimiter().spans.clone();
833    let mut contents = Engine::new(group.into_stream());
834    let mut muted = Vec::new();
835    let (elements, comma) = loop {
836        let elements = contents.parse_optional_components()?;
837        let Some(separator) = contents.take_punctuation(";") else {
838            break elements.map_or((None, false), |(elements, comma)| (Some(elements), comma));
839        };
840        let Some((elements, _)) = elements else {
841            return Err(unexpected!(separator, "a statement before `;`"));
842        };
843        muted.push(Statement::new(
844            ExprPack::new(elements),
845            typed_token(separator),
846        ));
847    };
848    if let Lookahead::Token(token) = contents.peek() {
849        return Err(unexpected!(token.token, "the end of the group"));
850    }
851
852    let expression: Expr = match kind {
853        DelimiterKind::Bracket => {
854            let mut value = Array::new(muted, elements.map(ExprPack::new));
855            value.delimiter = <Delimiter![[]] as DelimiterToken>::new(delimiter_spans);
856            value.into()
857        }
858        DelimiterKind::Brace => {
859            let mut value = List::new(muted, elements.map(ExprPack::new));
860            value.delimiter = <Delimiter![{}] as DelimiterToken>::new(delimiter_spans);
861            value.into()
862        }
863        DelimiterKind::AngleBar => {
864            let mut value = AngleBarList::new(muted, elements.map(ExprPack::new));
865            value.delimiter = <Delimiter![< | | >] as DelimiterToken>::new(delimiter_spans);
866            value.into()
867        }
868        DelimiterKind::Parenthesis if !muted.is_empty() || comma => {
869            let mut value = if comma {
870                Sequence::new(
871                    muted,
872                    Some(ExprPack::new(
873                        elements.expect("a comma pack always contains components"),
874                    )),
875                )
876            } else {
877                let value = elements
878                    .map(|elements| contents.only_expression(elements))
879                    .transpose()?;
880                let mut value = Holder::new(muted, value);
881                value.delimiter = <Delimiter![()] as DelimiterToken>::new(delimiter_spans);
882                return Ok(value.into());
883            };
884            value.delimiter = <Delimiter![()] as DelimiterToken>::new(delimiter_spans);
885            value.into()
886        }
887        DelimiterKind::Parenthesis => {
888            let value = elements
889                .map(|elements| contents.only_expression(elements))
890                .transpose()?;
891            let mut value = Holder::new(Vec::new(), value);
892            value.delimiter = <Delimiter![()] as DelimiterToken>::new(delimiter_spans);
893            value.into()
894        }
895    };
896    Ok(expression)
897}
898
899fn lower_prefix_statement(keyword: TokenTree, value: Option<Expr>) -> Expr {
900    if is_token!(&keyword, break) {
901        BreakExpr::new(typed_token(keyword), value).into()
902    } else if is_token!(&keyword, continue) {
903        ContinueExpr::new(typed_token(keyword), value).into()
904    } else if is_token!(&keyword, return) {
905        ReturnExpr::new(typed_token(keyword), value).into()
906    } else {
907        let value = value.expect("catch, throw, and trap require a value");
908        if is_token!(&keyword, catch) {
909            Catch::new(typed_token(keyword), value).into()
910        } else if is_token!(&keyword, throw) {
911            Throw::new(typed_token(keyword), value).into()
912        } else if is_token!(&keyword, trap) {
913            Trap::new(typed_token(keyword), value).into()
914        } else {
915            unreachable!("statement actions are assigned only to statement keywords")
916        }
917    }
918}
919
920fn lower_atom(token: TokenTree) -> Result<Expr, NativeParseError> {
921    match token {
922        TokenTree::Ident(token) => Ok(Symbol::new(token.text(), token.span()).into()),
923        TokenTree::Literal(token) => {
924            let span = token.span();
925            Ok(match token.kind {
926                LiteralKind::Integer => IntegerLiteral::new(token.text(), span).into(),
927                LiteralKind::Float => FloatLiteral::new(token.text(), span).into(),
928                LiteralKind::String => lower_string_literal(token.text(), span),
929                LiteralKind::RawString => lower_raw_string_literal(token.text(), span),
930            })
931        }
932        token => Err(unexpected!(token, "an expression")),
933    }
934}
935
936fn lower_binary(left: Expr, operator: TokenTree, right: Expr) -> Result<Expr, NativeParseError> {
937    let span = operator.span();
938    let spelling = operator
939        .spelling()
940        .expect("binary actions are assigned only to spelled tokens");
941    match spelling {
942        "=" => lower_assignment(left, operator, right, false),
943        ":=" => lower_assignment(left, operator, right, true),
944        "<-" => Ok(EvaluatedAssignment::new(left, typed_token(operator), right).into()),
945        "=>" => Ok(OptionExpression::new(left, typed_token(operator), right).into()),
946        "->" => {
947            let parameters = lambda_parameters(left)
948                .ok_or(NativeParseError::InvalidLambdaParameters { span })?;
949            Ok(LambdaExpression::new(parameters, typed_token(operator), right).into())
950        }
951        _ => {
952            let spelling = spelling.to_owned();
953            let operator =
954                BinaryOperator::from_token_tree(operator).ok_or(NativeParseError::Unsupported {
955                    syntax: spelling,
956                    span,
957                })?;
958            Ok(BinaryExpr::new(left, operator, right).into())
959        }
960    }
961}
962
963fn lower_assignment(
964    left: Expr,
965    operator: TokenTree,
966    right: Expr,
967    local: bool,
968) -> Result<Expr, NativeParseError> {
969    let span = operator.span();
970    match (local, left) {
971        (false, Expr::Symbol(variable)) => {
972            Ok(GlobalBinding::new(variable, typed_token(operator), right).into())
973        }
974        (true, Expr::Symbol(variable)) => {
975            Ok(LocalBinding::new(variable, typed_token(operator), right).into())
976        }
977        (false, Expr::OperatorExpr(argument_expr)) => {
978            Ok(OperatorAssignment::new(argument_expr, typed_token(operator), right).into())
979        }
980        (true, Expr::OperatorExpr(OperatorExpr::AdjacentExpr(application))) => {
981            let (typical_value, function) = installation_value(right);
982            Ok(MethodInstallation::new(
983                *application.left_operand,
984                *application.right_operand,
985                typed_token(operator),
986                typical_value,
987                function,
988            )
989            .into())
990        }
991        (true, Expr::OperatorExpr(domain_expr)) => {
992            let (typical_value, function) = installation_value(right);
993            Ok(OperatorInstallation::new(
994                domain_expr,
995                typed_token(operator),
996                typical_value,
997                function,
998            )
999            .into())
1000        }
1001        (true, Expr::NewStatement(new_statement)) => {
1002            Ok(NewInstallation::new(new_statement, typed_token(operator), right).into())
1003        }
1004        (local, left) => {
1005            let argument_pack =
1006                assignment_pack(left).ok_or(NativeParseError::InvalidAssignmentTarget { span })?;
1007            if local {
1008                Ok(
1009                    LocalParallelAssignment::new(argument_pack, typed_token(operator), right)
1010                        .into(),
1011                )
1012            } else {
1013                Ok(
1014                    GlobalParallelAssignment::new(argument_pack, typed_token(operator), right)
1015                        .into(),
1016                )
1017            }
1018        }
1019    }
1020}
1021
1022pub(crate) fn installation_value(value: Expr) -> (Option<TypeAnnotation>, Expr) {
1023    match value {
1024        Expr::OptionExpression(value) => (
1025            Some(TypeAnnotation::new(*value.key, value.operator)),
1026            *value.value,
1027        ),
1028        value => (None, value),
1029    }
1030}
1031
1032fn parse_cells(tokens: TokenStream) -> Result<SourceFile, NativeParseError> {
1033    let eof_span = ParseStream::new(tokens.clone()).eof_span();
1034    let mut elements = Vec::new();
1035    for cell in split_cells(tokens) {
1036        if let Some(element) = Engine::new(cell.stream).parse_cell(cell.semicolon, cell.eoc)? {
1037            elements.push(element);
1038        }
1039    }
1040    Ok(SourceFile::new(elements, Eof::new("", eof_span)))
1041}
1042
1043impl Parse for Expr {
1044    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
1045        let tokens = input.by_ref().collect::<TokenStream>();
1046        let mut engine = Engine::new(tokens);
1047        engine.skip_trivia();
1048        let value = engine
1049            .parse_required(PREC_CLOSER)
1050            .map_err(token_parse_error)?;
1051        engine.skip_trivia();
1052        match engine.peek() {
1053            Lookahead::End(_) => Ok(value),
1054            Lookahead::Token(token) => Err(TokenParseError::TrailingToken {
1055                found: token.token.description(),
1056                span: token.token.span(),
1057            }),
1058        }
1059    }
1060}
1061
1062impl Parse for SourceFile {
1063    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
1064        parse_cells(input.by_ref().collect()).map_err(token_parse_error)
1065    }
1066}
1067
1068fn token_parse_error(error: NativeParseError) -> TokenParseError {
1069    let span = error.span();
1070    TokenParseError::Syntax {
1071        message: error.to_string(),
1072        span,
1073    }
1074}
1075
1076fn parsing_spelling(token: &TokenTree) -> &str {
1077    token.spelling().map_or("", canonical_word_spelling)
1078}
1079
1080fn typed_token<T: Token>(token: TokenTree) -> T {
1081    let spelling = token.description();
1082    T::from_token_tree(token)
1083        .unwrap_or_else(|| panic!("`{spelling}` cannot be lowered to `{}`", T::SPELLING))
1084}
1085
1086fn empty_span_at(lookahead: &Span) -> Span {
1087    lookahead.subspan(0, 0)
1088}
1089
1090fn lower_string_literal(text: &str, span: Span) -> Expr {
1091    let body = &text[1..text.len() - 1];
1092    let mut elements = Vec::new();
1093    let mut content_start = 0;
1094    let mut cursor = 0;
1095
1096    while cursor < body.len() {
1097        if body.as_bytes()[cursor] != b'\\' {
1098            cursor += 1;
1099            continue;
1100        }
1101        if content_start < cursor {
1102            elements.push(StringElement::StringContent(StringContent::new(
1103                &body[content_start..cursor],
1104                span.subspan(content_start + 1, cursor + 1),
1105            )));
1106        }
1107        let escape_end = match body.as_bytes()[cursor + 1] {
1108            b'x' => cursor + 4,
1109            b'u' => cursor + 6,
1110            b'0'..=b'7' => {
1111                let mut end = cursor + 2;
1112                while end < body.len()
1113                    && end < cursor + 4
1114                    && body.as_bytes()[end].is_ascii_digit()
1115                    && body.as_bytes()[end] < b'8'
1116                {
1117                    end += 1;
1118                }
1119                end
1120            }
1121            _ => cursor + 2,
1122        };
1123        elements.push(StringElement::EscapeSequence(EscapeSequence::new(
1124            &body[cursor..escape_end],
1125            span.subspan(cursor + 1, escape_end + 1),
1126        )));
1127        cursor = escape_end;
1128        content_start = cursor;
1129    }
1130    if content_start < body.len() {
1131        elements.push(StringElement::StringContent(StringContent::new(
1132            &body[content_start..],
1133            span.subspan(content_start + 1, text.len() - 1),
1134        )));
1135    }
1136    StringLiteral::new(elements, span).into()
1137}
1138
1139fn lower_raw_string_literal(text: &str, span: Span) -> Expr {
1140    let body = &text[3..text.len() - 3];
1141    let mut elements = Vec::new();
1142    let mut content_start = 0;
1143    let mut cursor = 0;
1144
1145    while cursor + 1 < body.len() {
1146        if &body.as_bytes()[cursor..cursor + 2] != b"//" {
1147            cursor += 1;
1148            continue;
1149        }
1150        if content_start < cursor {
1151            elements.push(RawStringElement::RawStringContent(RawStringContent::new(
1152                &body[content_start..cursor],
1153                span.subspan(content_start + 3, cursor + 3),
1154            )));
1155        }
1156        elements.push(RawStringElement::EscapeSequence(EscapeSequence::new(
1157            "//",
1158            span.subspan(cursor + 3, cursor + 5),
1159        )));
1160        cursor += 2;
1161        content_start = cursor;
1162    }
1163    if content_start < body.len() {
1164        elements.push(RawStringElement::RawStringContent(RawStringContent::new(
1165            &body[content_start..],
1166            span.subspan(content_start + 3, text.len() - 3),
1167        )));
1168    }
1169    RawStringLiteral::new(elements, span).into()
1170}
1171
1172fn collection_contents(expression: Expr) -> Option<(crate::Delimiter, Option<ExprPack>)> {
1173    match expression {
1174        Expr::Collection(Collection::Sequence(value)) if value.statements.is_empty() => {
1175            Some((DelimiterValue::delimiter(&value.delimiter), value.elements))
1176        }
1177        Expr::Collection(Collection::List(value)) if value.statements.is_empty() => {
1178            Some((DelimiterValue::delimiter(&value.delimiter), value.elements))
1179        }
1180        Expr::Collection(Collection::Array(value)) if value.statements.is_empty() => {
1181            Some((DelimiterValue::delimiter(&value.delimiter), value.elements))
1182        }
1183        Expr::Collection(Collection::AngleBarList(value)) if value.statements.is_empty() => {
1184            Some((DelimiterValue::delimiter(&value.delimiter), value.elements))
1185        }
1186        Expr::Holder(value) if value.muted.is_empty() => {
1187            let elements = value
1188                .value
1189                .map(|value| ExprPack::new(Punctuated::from_single(Component::Expr(value))));
1190            Some((DelimiterValue::delimiter(&value.delimiter), elements))
1191        }
1192        _ => None,
1193    }
1194}
1195
1196pub(crate) fn assignment_pack(expression: Expr) -> Option<AssignmentPack> {
1197    let (delimiter, elements) = collection_contents(expression)?;
1198    let contents = match elements {
1199        Some(elements) => Some(map_punctuated(elements.pack, assignment_pack_component)?),
1200        None => None,
1201    };
1202    Some(AssignmentPack::new(Delimited::new(delimiter, contents)))
1203}
1204
1205fn assignment_pack_component(component: Component) -> Option<AssignmentPackComponent> {
1206    match component {
1207        Component::Empty(value) => Some(AssignmentPackComponent::Empty(value)),
1208        Component::Expr(value) => match *value {
1209            Expr::Symbol(value) => Some(AssignmentPackComponent::Symbol(value)),
1210            Expr::OperatorExpr(value) => Some(AssignmentPackComponent::OperatorExpr(value)),
1211            value => assignment_pack(value).map(Into::into),
1212        },
1213    }
1214}
1215
1216fn expr_pack_symbols(elements: ExprPack) -> Option<Punctuated<Symbol>> {
1217    map_punctuated(elements.pack, |component| match component {
1218        Component::Expr(value) => match *value {
1219            Expr::Symbol(value) => Some(value),
1220            _ => None,
1221        },
1222        Component::Empty(_) => None,
1223    })
1224}
1225
1226fn map_punctuated<S, T>(
1227    values: Punctuated<S>,
1228    mut map: impl FnMut(S) -> Option<T>,
1229) -> Option<Punctuated<T>> {
1230    let (first, rest) = values.into_parts();
1231    let first = map(first)?;
1232    let rest = rest
1233        .into_iter()
1234        .map(|(punctuation, value)| Some((punctuation, map(value)?)))
1235        .collect::<Option<Vec<_>>>()?;
1236    Some(Punctuated::new(first, rest))
1237}
1238
1239pub(crate) fn lambda_parameters(expression: Expr) -> Option<LambdaParameters> {
1240    match expression {
1241        Expr::Symbol(value) => Some(LambdaParameters::Variadic(Variadic::new(value))),
1242        expression => {
1243            let (delimiter, elements) = collection_contents(expression)?;
1244            let parameters = match elements {
1245                Some(elements) => Some(expr_pack_symbols(elements)?),
1246                None => None,
1247            };
1248            Some(LambdaParameters::FixedArity(FixedArity::new(
1249                Delimited::new(delimiter, parameters),
1250            )))
1251        }
1252    }
1253}
1254
1255/// The rule declared for a spelling, found in the generated table.
1256fn token_parse_rule(spelling: &str) -> Option<TokenParseRule> {
1257    TOKEN_PARSE_RULES
1258        .binary_search_by_key(&spelling, |(declared, _)| *declared)
1259        .ok()
1260        .map(|index| TOKEN_PARSE_RULES[index].1)
1261}
1262
1263fn keyword_action(spelling: &str) -> Option<UnaryAction> {
1264    Some(match spelling {
1265        "break" | "continue" | "return" | "catch" | "throw" | "trap" => UnaryAction::Statement,
1266        "if" | "while" | "try" | "for" | "new" => UnaryAction::Control,
1267        "TEST" | "time" | "timing" | "elapsedTime" | "elapsedTiming" | "breakpoint" | "profile"
1268        | "shield" | "step" | "finish" => UnaryAction::Debug,
1269        "symbol" | "global" | "local" | "threadLocal" | "threadVariable" => UnaryAction::Quote,
1270        _ => return None,
1271    })
1272}
1273
1274fn parse_info(token: &TokenTree) -> ParseInfo {
1275    if let TokenTree::Group(group) = token {
1276        return match group.delim_kind() {
1277            DelimiterKind::Bracket | DelimiterKind::AngleBar => {
1278                ParseInfo::delimiter(PREC_COLLECTION)
1279            }
1280            DelimiterKind::Parenthesis | DelimiterKind::Brace => {
1281                ParseInfo::delimiter(PREC_APPLICATION)
1282            }
1283        };
1284    }
1285
1286    let text = parsing_spelling(token);
1287    if let Some(rule) = token_parse_rule(text) {
1288        let mut info = ParseInfo::from(rule);
1289        if let Some(action) = keyword_action(text) {
1290            info.unary = action;
1291        }
1292        return info;
1293    }
1294
1295    match token {
1296        TokenTree::Ident(_) | TokenTree::Literal(_) => ParseInfo::atom(PREC_APPLICATION),
1297        _ => ParseInfo::stop(PREC_APPLICATION),
1298    }
1299}
1300
1301#[cfg(test)]
1302mod tests {
1303    use super::split_cells;
1304    use crate::lex_str;
1305
1306    /// Cell boundaries are decided here, from the flat stream, using the same
1307    /// operator/clause/quote state the grammar needs. Verified against M2
1308    /// v1.26.05 for each case.
1309    #[test]
1310    fn cells_use_operator_clause_and_delimiter_state() {
1311        fn cells(source: &str) -> usize {
1312            split_cells(lex_str(source).unwrap()).len()
1313        }
1314
1315        assert_eq!(cells("1\n2"), 2);
1316        assert_eq!(cells("1 +\n2"), 1);
1317        assert_eq!(cells("not\n1"), 1);
1318        assert_eq!(cells("1!\n2"), 2);
1319        assert_eq!(cells("if condition\nthen value"), 1);
1320        assert_eq!(cells("if condition\nthen value\nnext"), 2);
1321        assert_eq!(cells("symbol if\nnext"), 2);
1322        assert_eq!(cells("symbol +\nnext"), 2);
1323        assert_eq!(cells("1 -* comment\n*- 2"), 2);
1324        assert_eq!(cells("1 + -* comment\n*- 2"), 1);
1325        assert_eq!(cells("1;2"), 2);
1326        // A `;` inside brackets is an ordinary token, so the group stays one
1327        // cell and its contents are untouched by the embedding.
1328        assert_eq!(cells("(1\n2;3)"), 1);
1329        // The quoted `;` is data, not a boundary.
1330        assert_eq!(cells("symbol ;"), 1);
1331    }
1332}