use crate::{state::ConcreteSyntaxTreeBuilder, Diagnostics, ParserState, SyntaxKind};
use skullbrain_diagnostics::{DiagnosticDetails, EmitDiagnostics, ParseError};
use skullbrain_lexer::{ModifierKind, TokenKind};
impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
pub(super) fn parse_optional_modifiers(&mut self) {
if self.peek().is_none_or(|i| i.kind().is_not_modifier()) {
return;
}
self.start_node(SyntaxKind::Modifiers);
let mut context = Context::default();
let mut diagnostic_details = self.diagnostic_details();
while let Some(item) = self.peek() {
match item.kind() {
TokenKind::Modifier(kind) => {
context.update_for(kind);
diagnostic_details.span.end = item.range().end;
let Some(item) = self.next() else {
unreachable!()
};
self.token(SyntaxKind::Keyword, item);
self.skip_trivia();
}
_ => break,
}
}
self.emit_diagnostic_for_context(diagnostic_details, context);
self.finish_node();
}
fn emit_diagnostic_for_context(
&mut self,
diagnostic_details: DiagnosticDetails,
context: Context,
) {
if context.public == SeenState::Duplicate {
self.emit_diagnostic(
ParseError::duplicate_modifier(ModifierKind::VisibilityPublic),
diagnostic_details.clone(),
);
}
if context.instance_access == SeenState::Duplicate {
self.emit_diagnostic(
ParseError::duplicate_modifier(ModifierKind::InstanceAccess),
diagnostic_details.clone(),
);
}
}
}
#[derive(Default)]
struct Context {
public: SeenState,
instance_access: SeenState,
}
#[derive(Default, PartialEq)]
enum SeenState {
#[default]
NotSeen,
SeenOnce,
Duplicate,
}
impl Context {
fn update_for(&mut self, kind: &ModifierKind) {
let state = match kind {
ModifierKind::VisibilityPublic => &mut self.public,
ModifierKind::InstanceAccess => &mut self.instance_access,
};
match state {
SeenState::NotSeen => *state = SeenState::SeenOnce,
SeenState::SeenOnce => *state = SeenState::Duplicate,
SeenState::Duplicate => (),
};
}
}
mod test {
use crate::state::ConcreteSyntaxTreeBuilder;
#[test]
fn test_modifiers() {
crate::test::create_parsing_test("modifiers/*.skull", |state| {
state.start_node(crate::SyntaxKind::Unknown);
state.parse_optional_modifiers();
state.finish_node();
});
}
}