skullbrain-parser 0.1.0

Parses skullbrain tokens into a concrete syntax tree (CST).
Documentation
use skullbrain_diagnostics::DiagnosticDetails;

use super::*;

impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
    pub(super) fn parse_parathesis_comma_separated_expression(&mut self) {
        self.parse_parenthesis_aggressive(|state| {
            state.parse_seperated_by_comma(
                |_| true,
                |state| {
                    state.parse_repeated_expression(|k| {
                        Self::parse_parenthesis_with_expression_default_break(k)
                            || k.punctuation_ref().is_some_and(|p| p.is_comma())
                    });
                },
            );
        });
    }
}

impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
    pub(super) fn parse_name_followed_by_braced_expression(&mut self, error: ParseError) {
        self.skip_trivia();

        if self.peek().is_some_and(|i| i.kind().is_ident()) {
            self.parse_ident();
        } else {
            let diagnostic_details = self.diagnostic_details();
            self.emit_diagnostic(error, diagnostic_details);
        }

        self.skip_trivia();

        // Any other expressions are implicit init
        self.parse_brace_with_expression();
    }
}

impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
    /// Parse expression followed by braces: disambiguate between expression and block
    /// Handles cases where braces could be either part of an expression or a separate block
    /// Examples: "for x in {iterable} {body}" vs "for x in {body}", "match {expr} {arms}" vs "match {arms}", etc.
    pub(super) fn parse_expression_followed_by_braces(&mut self) {
        // Assumme we are parsing `for x in ...` and we are currently after the `in` token

        // Case (1): Next token is '{' or '('
        // Two possibilities:
        // A) for x in {iterable} {body}
        // B) for x in {body} (missing iterable)

        let details_before_block = self.diagnostic_details();
        let next_was_block = self
            .peek()
            .is_some_and(|i| is_open_brace_or_paren(i.kind()));

        // Always parse expression first (could be iterable or body)
        self.parse_expression();
        self.skip_trivia();

        if next_was_block {
            // We started with { or (, now check what follows
            if self
                .peek()
                .is_some_and(|i| is_open_brace_or_paren(i.kind()))
            {
                // Next token IS { or (
                // This means: for x in {iterable} {body}
                self.parse_brace_with_expression();
            } else {
                // Next token is NOT { or (
                // This means: for x in {body} (missing iterable)
                self.emit_expected_expression_before(details_before_block);
            }
        } else {
            // Started with regular token (not { or ()
            // This means: for x in iterable {body}
            self.parse_brace_with_expression();
        }
    }

    fn emit_expected_expression_before(&mut self, details_before_block: DiagnosticDetails) {
        let error = ParseError::expected_expression_before();
        self.emit_diagnostic(error, details_before_block);
    }
}

impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
    pub(super) fn parse_condition_and_body(&mut self) {
        self.parse_parenthesis_followed_by_brace_with_expression(
            SyntaxKind::Condition,
            SyntaxKind::Body,
        );
    }

    pub(super) fn parse_condition_and_body_until(&mut self, until: impl Fn(&TokenKind) -> bool) {
        self.parse_parenthesis_followed_by_brace_with_expression_until(
            SyntaxKind::Condition,
            SyntaxKind::Body,
            until,
        );
    }

    fn parse_parenthesis_followed_by_brace_with_expression(
        &mut self,
        wrap_paren: SyntaxKind,
        wrap_brace: SyntaxKind,
    ) {
        let checkpoint = self.checkpoint();
        self.parse_parenthesis_with_expression();

        self.start_node_at(checkpoint, wrap_paren);
        self.finish_node();

        self.skip_trivia();

        let checkpoint = self.checkpoint();

        self.parse_brace_with_expression();

        self.start_node_at(checkpoint, wrap_brace);
        self.finish_node();
    }

    fn parse_parenthesis_followed_by_brace_with_expression_until(
        &mut self,
        wrap_paren: SyntaxKind,
        wrap_brace: SyntaxKind,
        until: impl Fn(&TokenKind) -> bool,
    ) {
        let checkpoint = self.checkpoint();

        self.parse_parenthesis_with_expression_until(&until);

        self.start_node_at(checkpoint, wrap_paren);
        self.finish_node();

        self.skip_trivia();

        let checkpoint = self.checkpoint();

        self.parse_brace_with_expression_until(until);

        self.start_node_at(checkpoint, wrap_brace);
        self.finish_node();
    }
}

const OPEN_PAREN: TokenKind = TokenKind::Punctuation(PunctuationKind::OpenParen);
const CLOSE_PAREN: TokenKind = TokenKind::Punctuation(PunctuationKind::CloseParen);

pub(super) const OPEN_BRACE: TokenKind = TokenKind::Punctuation(PunctuationKind::OpenBrace);
pub(super) const CLOSE_BRACE: TokenKind = TokenKind::Punctuation(PunctuationKind::CloseBrace);

impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
    pub(super) fn parse_parenthesis_with_expression(&mut self) {
        self.parse_parenthesis_with_expression_until(|_| false)
    }

    pub(super) fn parse_brace_with_expression(&mut self) {
        self.parse_brace_with_expression_until(|_| false)
    }

    fn parse_parenthesis_with_expression_until(&mut self, until: impl Fn(&TokenKind) -> bool) {
        let _ = self.parse_parenthesis_aggressive(|state| {
            state.parse_repeated_expression(|k| {
                Self::parse_parenthesis_with_expression_default_break(k)
                    || is_open_or_close_brace(k)
                    || until(k)
            });
        });
    }

    pub(super) fn parse_brace_with_expression_until(&mut self, until: impl Fn(&TokenKind) -> bool) {
        self.parse_brace_aggressive(|state| {
            state.parse_repeated_expression(|k| k.is(&CLOSE_BRACE) || until(k));
        });
    }

    fn parse_parenthesis_with_expression_default_break(k: &TokenKind) -> bool {
        k.is(&CLOSE_PAREN)
    }

    pub(super) fn parse_repeated_expression(&mut self, should_break: impl Fn(&TokenKind) -> bool) {
        loop {
            self.skip_trivia();
            match self.peek_raw() {
                None => break,
                Some(item) if is_dot(item.kind()) || should_break(item.kind()) => break,
                Some(_) => self.parse_expression(),
            }
        }
    }
}

fn is_open_or_close_brace(kind: &TokenKind) -> bool {
    kind.is(&OPEN_BRACE) || kind.is(&CLOSE_BRACE)
}

fn is_open_brace_or_paren(kind: &TokenKind) -> bool {
    kind.is(&OPEN_PAREN) || kind.is(&OPEN_BRACE)
}