skullbrain-parser 0.1.0

Parses skullbrain tokens into a concrete syntax tree (CST).
Documentation
use crate::primitive::is_trivia;

use super::{position::PositionTokenIter, PositionedToken};
use itertools::{peek_nth, PeekNth};
use skullbrain_lexer::{PunctuationKind, TokenKind};

pub(super) struct Sequence<'input> {
    raw_iter: PeekNth<PositionTokenIter<'input>>,
    /// A stack representing the currently active closing delimiters in nested delimited constructs.
    ///
    /// Each entry signals the end of the delimited group.
    /// The stack depth corresponds to the nesting depth of delimited constructs.
    delimiter_stack: Vec<TokenKind>,
}

/// A lookahead-only wrapper over a `Sequence` that tracks internal peek state.
///
/// This struct is used in `Parser::peek_sequence(...)` to check if a sequence
/// of upcoming tokens matches a given grammar rule
///
/// Each call to [`peek_next`] returns the next token in sequence, using `peek_nth(index)`,
/// and internally increments the `peek_index`
pub(crate) struct PeekSequence<'a, 'input> {
    iter: &'a mut Sequence<'input>,
    peek_index: usize,
}

impl Iterator for Sequence<'_> {
    type Item = PositionedToken;
    fn next(&mut self) -> Option<Self::Item> {
        if self.is_blocked_by_outer_delimiter() {
            None
        } else {
            self.next_raw()
        }
    }
}

impl<'input> Sequence<'input> {
    pub(super) fn new(input: &'input str) -> Self {
        Self {
            raw_iter: peek_nth(PositionTokenIter::new(input)),
            delimiter_stack: vec![TokenKind::Punctuation(PunctuationKind::Dot)],
        }
    }

    pub(super) fn next_raw(&mut self) -> Option<<Self as Iterator>::Item> {
        self.raw_iter.next()
    }

    pub(super) fn peek_raw(&mut self) -> Option<&<Self as Iterator>::Item> {
        self.raw_iter.peek()
    }

    pub(super) fn peek(&mut self) -> Option<&<Self as Iterator>::Item> {
        if self.is_blocked_by_outer_delimiter() {
            None
        } else {
            self.peek_raw()
        }
    }
}
impl Sequence<'_> {
    /// Push a new delimited context
    ///
    /// # Parameters
    /// * `closing` - The expected closing token for this delimited region (e.g., `}` or `)`).
    pub(super) fn push_delimited(&mut self, closing: TokenKind) {
        self.delimiter_stack.push(closing);
    }

    /// Checks whether the next token is a closing token for a *parent* delimited context,
    /// and if so, simulates EOF to the current parsing scope.
    ///
    /// This prevents expressions inside delimited regions from accidentally consuming
    /// a delimiter intended for the parent parser.
    fn is_blocked_by_outer_delimiter(&mut self) -> bool {
        let Some(token) = self.raw_iter.peek() else {
            return false;
        };

        let Some(active_closer) = self.delimiter_stack.last() else {
            return false;
        };

        // If we are in nested context and see a closing token from a parent, block it
        token.kind().is(active_closer)
    }

    pub(super) fn resume_normal_tokenstream(&mut self) {
        self.delimiter_stack
            .pop()
            .expect("Failed to resume normal tokenstream since stack was empty");
    }
}

impl<'a, 'input> PeekSequence<'a, 'input> {
    /// Creates a new `PeekSequence` over the given iterator.
    pub(super) const fn new(iter: &'a mut Sequence<'input>) -> Self {
        Self {
            iter,
            peek_index: 0,
        }
    }

    /// Peeks at the next non-trivia token in sequence and advances the internal peek index.
    pub(crate) fn peek_next(&mut self) -> Option<&PositionedToken> {
        loop {
            if self.iter.is_blocked_by_outer_delimiter() {
                break None;
            } else if let Some(item) = self.iter.raw_iter.peek_nth(self.peek_index) {
                if !is_trivia(item.kind()) {
                    // Advance after finding non-trivia token
                    self.peek_index += 1;
                    return self.iter.raw_iter.peek_nth(self.peek_index - 1);
                }

                self.peek_index += 1;
            } else {
                break None;
            }
        }
    }

    pub(crate) fn peek_next_is(&mut self, kind: &TokenKind) -> bool {
        self.peek_next().is_some_and(|i| i.kind().is(kind))
    }

    #[deprecated]
    pub(crate) fn revert_by_one(&mut self) {
        self.peek_index = self.peek_index.saturating_sub(1)
    }
}