use skullbrain_diagnostics::{EmitDiagnostics, ParseError};
use skullbrain_lexer::TokenKind;
use crate::{
state::{ConcreteSyntaxTreeBuilder, PeekSequence},
Diagnostics, ParserState, SyntaxKind,
};
impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
pub(super) fn parse_import_expression(&mut self) {
self.start_node(SyntaxKind::Import);
self.parse_token(SyntaxKind::Keyword);
if self.try_possibilities_after_import_keyword() {
self.recover();
if self.try_possibilities_after_import_keyword() {
self.emit_missing_import_source_before_diagnostic();
}
}
self.finish_node();
}
fn try_possibilities_after_import_keyword(&mut self) -> bool {
self.skip_trivia();
match self.peek() {
None => {
self.emit_missing_import_source_at_diagnostic();
false
}
Some(item) if possible_import_source_token(item.kind()) => {
let kind = if item.kind().is_ident() {
SyntaxKind::Ident
} else {
SyntaxKind::Literal
};
self.start_node(SyntaxKind::ImportSource);
self.parse_token(kind);
self.finish_node();
self.try_possibilities_after_import_source();
false
}
Some(_) => {
if self.peek_sequence(|peekable| Self::peek_sequence_for_imported_items(peekable)) {
self.emit_missing_import_source_before_diagnostic();
self.parse_imported_items();
false
} else {
true
}
}
}
}
fn try_possibilities_after_import_source(&mut self) {
self.skip_trivia();
match self.peek() {
None => (),
Some(item) if is_alias(item.kind()) => {
self.parse_alias_and_remaining();
}
Some(_) => {
if self.peek_sequence(|peekable| Self::peek_sequence_for_imported_items(peekable)) {
self.parse_imported_items();
}
}
}
}
}
impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
fn peek_sequence_for_imported_items(peekable: &mut PeekSequence) -> bool {
Self::peek_sequence_for_brace(
peekable,
Self::peek_sequence_for_seperated_by_comma(Self::peek_sequence_for_import_item),
)
}
}
impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
fn parse_alias_and_remaining(&mut self) {
self.parse_token(SyntaxKind::ImportAlias);
self.skip_trivia();
self.parse_imported_items();
}
fn parse_imported_items(&mut self) {
self.start_node(SyntaxKind::ImportItem);
if self.peek_sequence(|peekable| Self::peek_sequence_for_ident(peekable)) {
self.parse_ident();
} else {
self.parse_brace_aggressive(
|state| {
state.parse_seperated_by_comma(Self::peek_sequence_for_import_item, |state| {
state.parse_import_item()
});
},
);
}
self.finish_node();
}
fn parse_import_item(&mut self) {
self.parse_ident();
}
fn peek_sequence_for_import_item(peekable: &mut PeekSequence) -> bool {
peekable
.peek_next()
.map_or(false, |item| item.kind().is_ident())
}
}
impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
fn emit_missing_import_source_at_diagnostic(&mut self) {
let diagnostic_details = self.diagnostic_details();
let error = ParseError::missing_import_source_at();
self.emit_diagnostic(error, diagnostic_details);
}
fn emit_missing_import_source_before_diagnostic(&mut self) {
let diagnostic_details = self.diagnostic_details();
let error = ParseError::missing_import_source_before();
self.emit_diagnostic(error, diagnostic_details);
}
}
fn possible_import_source_token(kind: &TokenKind) -> bool {
kind.literial_ref().is_some_and(|k| k.is_str()) || kind.is_ident()
}
fn is_alias(kind: &TokenKind) -> bool {
kind.declaration_ref().is_some_and(|k| k.is_alias())
}
#[cfg(test)]
mod test {
use crate::test::create_parsing_test;
#[test]
fn test_import() {
create_parsing_test("import/*.skull", |state| {
state.parse_import_expression();
});
}
}