mod look_aheadable;
mod position;
mod recovery;
use itertools::PeekingNext;
pub(crate) use look_aheadable::PeekSequence;
pub(crate) use position::PositionedToken;
use skullbrain_lexer::TokenKind;
use crate::syntax_kind::SyntaxKind;
use look_aheadable::Sequence;
use skullbrain_diagnostics::{DiagnosticDetails, EmitDiagnostics, ParseError};
use std::path::Path;
pub(crate) trait Diagnostics: EmitDiagnostics<DiagnosticType = ParseError> {}
impl<T: EmitDiagnostics<DiagnosticType = ParseError>> Diagnostics for T {}
pub(crate) struct ParserState<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> {
source: &'input Path,
diagnostic: D,
lexer: Sequence<'input>,
builder: C,
input_len: usize,
scope_contexts: Vec<ScopeContext>,
}
#[derive(PartialEq)]
pub(crate) enum ScopeContext {
Loop,
Function,
Class,
Enum,
TraitDelc,
}
pub(crate) trait ConcreteSyntaxTreeBuilder {
type Output;
type Checkpoint: Clone;
fn start_node(&mut self, kind: SyntaxKind);
fn start_node_at(&mut self, checkpoint: Self::Checkpoint, kind: SyntaxKind);
fn finish_node(&mut self);
fn token(&mut self, kind: SyntaxKind, item: PositionedToken);
fn checkpoint(&self) -> Self::Checkpoint;
fn finish(self) -> Self::Output;
}
impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
pub(crate) fn new(source: &'input Path, input: &'input str, diagnostic: D, builder: C) -> Self {
Self {
source,
diagnostic,
lexer: Sequence::new(input),
builder,
input_len: input.len(),
scope_contexts: Vec::new(),
}
}
pub(crate) fn peek(&mut self) -> Option<&PositionedToken> {
self.lexer.peek()
}
pub(crate) fn peek_raw(&mut self) -> Option<&PositionedToken> {
self.lexer.peek_raw()
}
pub(super) fn next_raw(&mut self) -> Option<<Self as Iterator>::Item> {
self.lexer.next_raw()
}
pub(crate) fn diagnostic_details_with_item(
&self,
item: &PositionedToken,
) -> DiagnosticDetails<'input> {
DiagnosticDetails::new(self.source, item.range.clone())
}
pub(crate) fn diagnostic_details(&mut self) -> DiagnosticDetails<'input> {
let source = self.source;
let range = self
.peek_raw()
.map(|i| i.range.clone())
.unwrap_or(self.input_len..self.input_len);
DiagnosticDetails::new(source, range)
}
pub(crate) fn peek_sequence<T>(
&mut self,
transform: impl FnOnce(&mut PeekSequence<'_, 'input>) -> T,
) -> T {
transform(&mut PeekSequence::new(&mut self.lexer))
}
}
impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
pub(crate) fn push_delimited(&mut self, closing: TokenKind) {
self.lexer.push_delimited(closing)
}
pub(crate) fn resume_normal_tokenstream(&mut self) {
self.lexer.resume_normal_tokenstream();
}
}
impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
pub(crate) fn parse_token_with(
&mut self,
kind: SyntaxKind,
block: impl FnOnce(&mut Self, &PositionedToken),
) {
let Some(item) = self.next() else {
unreachable!()
};
block(self, &item);
self.token(kind, item);
}
pub(crate) fn parse_token(&mut self, kind: SyntaxKind) {
let Some(item) = self.next() else {
unreachable!()
};
self.token(kind, item);
}
pub(crate) fn parse_raw_token(&mut self, kind: SyntaxKind) {
let Some(item) = self.next_raw() else {
unreachable!()
};
self.token(kind, item);
}
}
impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ParserState<'input, D, C> {
pub(crate) fn is_directly_in_ctx(&self, context: ScopeContext) -> bool {
self.scope_contexts
.last()
.is_some_and(|ctx| ctx == &context)
}
pub(crate) fn is_in_valid_class_context(&self) -> bool {
let mut in_valid_ctx = false;
let mut found_function = false;
for ctx in self.scope_contexts.iter().rev() {
match ctx {
ScopeContext::Loop => {
continue;
}
ScopeContext::Function => {
if found_function {
return false; }
found_function = true;
}
ScopeContext::Class => {
in_valid_ctx = true;
break;
}
ScopeContext::Enum | ScopeContext::TraitDelc => break,
}
}
in_valid_ctx
}
pub(crate) fn in_valid_trait_delc_context(&self) -> bool {
let mut iter = self.scope_contexts.iter().rev();
iter.next()
.is_some_and(|i| matches!(i, ScopeContext::Function))
&& iter
.next()
.is_some_and(|i| matches!(i, ScopeContext::TraitDelc))
}
pub(crate) fn parse_in_context<T>(
&mut self,
context: ScopeContext,
parse: impl FnOnce(&mut Self) -> T,
) -> T {
self.enter_context(context);
let out = parse(self);
self.exit_context();
out
}
fn enter_context(&mut self, context: ScopeContext) {
self.scope_contexts.push(context);
}
fn exit_context(&mut self) {
self.scope_contexts
.pop()
.expect("failed to pop scope context as it was empty");
}
}
impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> EmitDiagnostics
for ParserState<'input, D, C>
{
type DiagnosticType = <D as EmitDiagnostics>::DiagnosticType;
fn emit_diagnostic(
&mut self,
kind: Self::DiagnosticType,
diagnostic_details: DiagnosticDetails,
) {
self.diagnostic.emit_diagnostic(kind, diagnostic_details);
}
}
impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> Iterator for ParserState<'input, D, C> {
type Item = PositionedToken;
fn next(&mut self) -> Option<Self::Item> {
self.lexer.next()
}
}
impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> ConcreteSyntaxTreeBuilder
for ParserState<'input, D, C>
{
type Output = C::Output;
type Checkpoint = C::Checkpoint;
fn start_node(&mut self, kind: SyntaxKind) {
self.builder.start_node(kind);
}
fn finish_node(&mut self) {
self.builder.finish_node();
}
fn token(&mut self, kind: SyntaxKind, item: PositionedToken) {
self.builder.token(kind, item);
}
fn finish(self) -> Self::Output {
self.builder.finish()
}
fn checkpoint(&self) -> Self::Checkpoint {
self.builder.checkpoint()
}
fn start_node_at(&mut self, checkpoint: Self::Checkpoint, kind: SyntaxKind) {
self.builder.start_node_at(checkpoint, kind);
}
}
impl<'input, D: Diagnostics, C: ConcreteSyntaxTreeBuilder> PeekingNext
for ParserState<'input, D, C>
{
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
where
Self: Sized,
F: FnOnce(&Self::Item) -> bool,
{
match self.peek() {
Some(item) if accept(item) => {
let Some(item) = self.next() else {
unreachable!()
};
Some(item)
}
_ => None,
}
}
}