skullbrain-parser 0.1.0

Parses skullbrain tokens into a concrete syntax tree (CST).
Documentation
use skullbrain_diagnostics::{EmitDiagnostics, ParseError};
use skullbrain_lexer::{PunctuationKind, TokenKind};

use crate::{
    state::{ConcreteSyntaxTreeBuilder, PeekSequence, ScopeContext},
    syntax::is_dot,
    Diagnostics, ParserState, SyntaxKind,
};

impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
    pub(super) fn parse_function_delc(&mut self) {
        self.parse_in_context(ScopeContext::Function, Self::_parse_function_delc)
    }

    fn _parse_function_delc(&mut self) {
        self.start_node(SyntaxKind::Function);
        self.parse_optional_modifiers();
        self.function_delc_without_body();
        self.skip_trivia();

        if self.in_valid_trait_delc_context() {
            match self.peek_raw() {
                // Perfect do absoluely nothing
                Some(item) if is_dot(item.kind()) => (),
                // If { then try only
                Some(item)
                    if item
                        .kind()
                        .punctuation_ref()
                        .is_some_and(|k| k.is_open_brace()) =>
                {
                    self.parse_brace_with_expression();
                }
                _ => {
                    // Allow the following parsers to do whatever they want
                    self.emit_expected_dot_before_next();
                }
            }
        } else {
            self.parse_brace_with_expression();
        }

        self.finish_node();
    }

    fn function_delc_without_body(&mut self) {
        self.parse_token(SyntaxKind::Keyword);
        self.skip_trivia();

        if self.peek().is_some_and(|i| i.kind().is_ident()) {
            self.parse_ident();
            self.skip_trivia();
        } else {
            self.emit_expected_func_name();
        }

        self.parse_function_signature();
    }

    fn emit_expected_func_name(&mut self) {
        let diagnostic_details = self.diagnostic_details();
        let error = ParseError::expected_function_name_before();
        self.emit_diagnostic(error, diagnostic_details);
    }

    fn emit_expected_dot_before_next(&mut self) {
        let diagnostic_details = self.diagnostic_details();
        let error = ParseError::expected_token_before(TokenKind::Punctuation(PunctuationKind::Dot));
        self.emit_diagnostic(error, diagnostic_details);
    }
}

impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
    pub(super) fn peek_sequence_for_function_signature(peekable: &mut PeekSequence) -> bool {
        Self::peek_sequence_for_function_parameters(peekable)
    }

    pub(super) fn peek_sequence_for_function_parameters(peekable: &mut PeekSequence) -> bool {
        Self::peek_sequence_for_parenthesis(
            peekable,
            Self::peek_sequence_for_seperated_by_comma(
                Self::peek_sequence_for_single_function_parameter,
            ),
        )
    }

    pub(super) fn parse_function_signature(&mut self) {
        let checkpoint = self.checkpoint();

        self.parse_function_parameters();
        self.skip_trivia();

        if self.try_function_signature_possibilites() {
            self.recover();

            if self.try_function_signature_possibilites() {
                if self.peek_sequence(Self::peek_sequence_for_type) {
                    let diagnostic_details = self.diagnostic_details();
                    let error = ParseError::expected_token_before(TokenKind::Punctuation(
                        PunctuationKind::Arrow,
                    ));
                    self.emit_diagnostic(error, diagnostic_details);

                    self.parse_type();
                } else {
                    // Oddly, ignoring this branch somehow works for function types. Not fully sure why, but it does.
                }
            }
        }

        self.start_node_at(checkpoint, SyntaxKind::FunctionSignature);
        self.finish_node();
    }

    fn try_function_signature_possibilites(&mut self) -> bool {
        if let Some(item) = self.peek() {
            match item.kind().punctuation_ref().is_some_and(|k| k.is_arrow()) {
                true => {
                    let Some(item) = self.next() else {
                        unreachable!()
                    };
                    self.token(SyntaxKind::Arrow, item);
                    self.parse_type();
                }
                false => return true,
            }
        }

        false
    }
}

impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
    pub(super) fn parse_function_parameters(&mut self) {
        self.parse_parenthesis_aggressive(|state| {
            state.parse_seperated_by_comma(
                Self::peek_sequence_for_single_function_parameter,
                Self::parse_single_function_parameter,
            )
        });
    }
}

impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
    pub(super) fn parse_single_function_parameter(&mut self) {
        self.start_node(SyntaxKind::Parameter);
        self.parse_ident();
        self.skip_trivia();

        match self.peek() {
            Some(item) => {
                match item.kind() {
                    TokenKind::Punctuation(PunctuationKind::Colon) => {
                        let Some(item) = self.next() else {
                            unreachable!()
                        };

                        self.token(SyntaxKind::Colon, item);
                    }
                    _ => {
                        let diagnostic_details = self.diagnostic_details();

                        self.emit_diagnostic(
                            ParseError::expected_token_before(TokenKind::Punctuation(
                                PunctuationKind::Colon,
                            )),
                            diagnostic_details,
                        );

                        self.recover();
                    }
                };

                self.skip_trivia();
                self.parse_type()
            }
            None => {
                let details = self.diagnostic_details();
                self.emit_diagnostic(
                    ParseError::missing_colon_and_type_in_parmameter_declaration(),
                    details,
                );
            }
        };

        self.finish_node();
    }

    /// Creates a predicate function for a function parmater parsing.
    /// A predicate function compatible with sequence parsing methods
    pub(super) fn peek_sequence_for_single_function_parameter(peekable: &mut PeekSequence) -> bool {
        if !Self::peek_sequence_for_ident(peekable) {
            return false;
        }

        let has_colon = peekable.peek_next_is(&TokenKind::Punctuation(PunctuationKind::Colon));

        if !has_colon {
            peekable.revert_by_one();
        }

        let has_type = Self::peek_sequence_for_type(peekable);

        has_colon || has_type
    }
}

#[cfg(test)]
mod test {
    use crate::test::create_parsing_test;

    #[test]
    fn test_single_parameter() {
        create_parsing_test("single_parameter/*.skull", |state| {
            state.parse_single_function_parameter();
        });
    }

    #[test]
    fn test_function_delc() {
        create_parsing_test("function_delc/*.skull", |state| {
            state.parse_function_delc();
        });
    }
}