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() {
Some(item) if is_dot(item.kind()) => (),
Some(item)
if item
.kind()
.punctuation_ref()
.is_some_and(|k| k.is_open_brace()) =>
{
self.parse_brace_with_expression();
}
_ => {
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 {
}
}
}
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();
}
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();
});
}
}