alkale 2.0.0

A simple LL(1) lexer library for Rust.
Documentation
//! Module containing types for span information. Notably [`Span`].

use core::{
    cmp::Ordering,
    fmt::Debug,
    ops::{Deref, DerefMut},
};

use crate::token::Token;

/// A type that store source code position information.
///
/// Generally, these should not be manually created, instead prefer to use
/// methods on [`SourceCodeScanner`][crate::SourceCodeScanner] to generate them
/// and use composite methods to combine them together.
///
/// # Safety
/// A span is considered to be *well-formed* if `index + length < N`, where `N`
/// is the number of characters in the source code this Span points to.
///
/// Creating a span that isn't *well-formed* is undefined behavior.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct Span {
    /// The index in the source string that this Span begins on. (inclusive)
    index: usize,
    /// How many characters are encompassed by this span.
    length: usize,
}

impl Span {
    /// Construct a new [`Span`] over a string using a starting index
    /// and a length.
    ///
    /// # Safety
    /// Ensure the span is *well-formed*, refer to type documentation
    /// for details.
    #[inline]
    #[must_use]
    pub const unsafe fn new(index: usize, length: usize) -> Self {
        Self { index, length }
    }

    /// Construct a new [`Span`] at a given index that spans
    /// a single character.
    ///
    /// # Safety
    /// Ensure the span is *well-formed*, refer to type documentation
    /// for details.
    #[inline]
    #[must_use]
    pub const unsafe fn new_single(index: usize) -> Self {
        Self::new(index, 1)
    }

    /// Construct a new [`Span`] at a given index that spans
    /// no characters.
    ///
    /// # Safety
    /// Ensure the span is *well-formed*, refer to type documentation
    /// for details.
    #[inline]
    #[must_use]
    pub const unsafe fn new_empty(index: usize) -> Self {
        Self::new(index, 0)
    }

    /// Construct a new [`Span`] that spans the range between two indices.
    /// The second range is treated as exclusive, and the character at that index
    /// will not be encompassed.
    ///
    /// # Safety
    /// Ensure the span is *well-formed*, refer to type documentation
    /// for details.
    ///
    /// Ensure `end_index` is equal to or larger than `start_index`, or else
    /// this method will result in UB.
    #[inline]
    #[must_use]
    pub const unsafe fn new_range(start_index: usize, end_index: usize) -> Self {
        Self {
            index: start_index,
            length: end_index.unchecked_sub(start_index),
        }
    }

    /// Returns true if this span encompasses 0 characters. This
    /// can happen if the span has 0 lines, 0 columns, etc.
    ///
    /// # Examples
    /// ```rust
    /// # use alkale::span::Span;
    /// # unsafe {
    /// assert!(Span::new(0, 0).is_empty());
    /// assert!(Span::new(5, 0).is_empty());
    ///
    /// assert!(!Span::new(5, 3).is_empty());
    /// assert!(!Span::new(0, 3).is_empty());
    /// # }
    /// ```
    #[inline]
    #[must_use]
    pub const fn is_empty(&self) -> bool {
        self.length == 0
    }

    /// Get the index this [`Span`] begins on.
    ///
    /// # Examples
    /// ```rust
    /// # use alkale::span::Span;
    /// # unsafe {
    /// assert_eq!(Span::new(0, 0).index(), 0);
    /// assert_eq!(Span::new(3, 0).index(), 3);
    /// assert_eq!(Span::new(5, 2).index(), 5);
    /// assert_eq!(Span::new(8, 4).index(), 8);
    /// # }
    /// ```
    #[inline]
    #[must_use]
    pub const fn index(&self) -> usize {
        self.index
    }

    /// Get the index this [`Span`] ends at. This index
    /// does *not* represent a character within the original
    /// span.
    ///
    /// # Examples
    /// ```rust
    /// # use alkale::span::Span;
    /// # unsafe {
    /// assert_eq!(Span::new(0, 0).end_index(), 0);
    /// assert_eq!(Span::new(3, 0).end_index(), 3);
    /// assert_eq!(Span::new(5, 2).end_index(), 7);
    /// assert_eq!(Span::new(8, 4).end_index(), 12);
    /// # }
    /// ```
    #[inline]
    #[must_use]
    pub const fn end_index(&self) -> usize {
        // SAFETY: This is valid as long as the span
        // is well-formed, which it should be in safe code.
        unsafe { self.index.unchecked_add(self.length) }
    }

    /// Get the number of characters this [`Span`] encompasses.
    ///
    /// # Examples
    /// ```rust
    /// # use alkale::span::Span;
    /// # unsafe {
    /// assert_eq!(Span::new(0, 0).len(), 0);
    /// assert_eq!(Span::new(3, 0).len(), 0);
    /// assert_eq!(Span::new(5, 2).len(), 2);
    /// assert_eq!(Span::new(8, 4).len(), 4);
    /// # }
    /// ```
    #[inline]
    #[must_use]
    pub const fn len(&self) -> usize {
        self.length
    }

    /// Create a 0-length [`Span`] at the beginning of this one.
    ///
    /// # Examples
    /// ```rust
    /// # use alkale::span::Span;
    /// # unsafe {
    /// assert_eq!(Span::new(0, 0).beginning(), Span::new(0, 0));
    /// assert_eq!(Span::new(3, 0).beginning(), Span::new(3, 0));
    /// assert_eq!(Span::new(5, 2).beginning(), Span::new(5, 0));
    /// assert_eq!(Span::new(8, 4).beginning(), Span::new(8, 0));
    /// # }
    /// ```
    #[inline]
    #[must_use]
    pub const fn beginning(&self) -> Self {
        // SAFETY: This is valid as long as the span
        // is well-formed, which it should be in safe code.
        unsafe { Self::new(self.index, 0) }
    }

    /// Create a 0-length [`Span`] at the ending of this one.
    ///
    /// # Examples
    /// ```rust
    /// # use alkale::span::Span;
    /// # unsafe {
    /// assert_eq!(Span::new(0, 0).ending(), Span::new(0, 0));
    /// assert_eq!(Span::new(3, 0).ending(), Span::new(3, 0));
    /// assert_eq!(Span::new(5, 2).ending(), Span::new(7, 0));
    /// assert_eq!(Span::new(8, 4).ending(), Span::new(12, 0));
    /// # }
    /// ```
    #[inline]
    #[must_use]
    pub const fn ending(&self) -> Self {
        // SAFETY: This is valid as long as the span
        // is well-formed, which it should be in safe code.
        unsafe { Self::new(self.index.unchecked_add(self.length), 0) }
    }

    /// Create a [`Span`] that entirely encompasses the two input spans.
    /// This will also encompass all characters between the two.
    ///
    /// # Examples
    /// ```rust
    /// # use alkale::span::Span;
    /// # unsafe {
    /// assert_eq!(
    ///     Span::new(0, 0).range(&Span::new(10, 0)),
    ///     Span::new(0, 10)
    /// );
    ///
    /// assert_eq!(
    ///     Span::new(3, 2).range(&Span::new(15, 4)),
    ///     Span::new(3, 16)
    /// );
    ///
    /// assert_eq!(
    ///     Span::new(10, 8).range(&Span::new(3, 4)),
    ///     Span::new(3, 15)
    /// );
    /// # }
    /// ```
    #[inline]
    #[must_use]
    pub fn range(&self, other: &Self) -> Self {
        // SAFETY: This is valid as long as the two
        // spans are both well-formed. This should be
        // the case in safe code.
        unsafe {
            Self::new_range(
                self.index.min(other.index),
                self.end_index().max(other.end_index()),
            )
        }
    }

    /// Create a new [`Span`] that begin at this span and ends
    /// before the other span's first character. The new span
    /// will not include any characters from the second argument
    /// span.
    ///
    /// # Panics
    /// This method will panic if `other`'s index comes before
    /// `self`'s index.
    ///
    /// # Examples
    /// ```rust
    /// # use alkale::span::Span;
    /// # unsafe {
    /// assert_eq!(
    ///     Span::new(0, 0).up_to(&Span::new(10, 0)),
    ///     Span::new(0, 10)
    /// );
    ///
    /// assert_eq!(
    ///     Span::new(3, 2).up_to(&Span::new(15, 4)),
    ///     Span::new(3, 12)
    /// );
    ///
    /// assert_eq!(
    ///     Span::new(5, 20).up_to(&Span::new(8, 0)),
    ///     Span::new(5, 3)
    /// );
    /// # }
    /// ```
    #[inline]
    #[must_use]
    pub const fn up_to(&self, other: &Self) -> Self {
        assert!(
            other.index >= self.index,
            "up_to called with out-of-order arguments"
        );

        // SAFETY: This is valid as long as the two
        // spans are both well-formed. This should be
        // the case in safe code.
        unsafe { Self::new_range(self.index, other.index) }
    }

    /// Wrap another type with this span, creating a [`Spanned`].
    /// This is an alternative to method to [`Spannable::spanned`] if code is more
    /// readable to have the span first, but otherwise that method should be used
    /// instead.
    #[inline]
    pub fn wrap<T>(self, other: T) -> Spanned<T> {
        other.spanned(self)
    }
}

impl PartialOrd for Span {
    #[inline]
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Span {
    #[inline]
    fn cmp(&self, other: &Self) -> Ordering {
        self.index.cmp(&other.index)
    }
}

/// Wrapper for a type that holds a span.
///
/// The span can be accessed with `self.span`, while the original data can be
/// accessed with `self.data`. This type will [`Deref`] into its own data so
/// usually `self.data` isn't necessary.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[expect(clippy::exhaustive_structs)]
pub struct Spanned<T: ?Sized> {
    /// The span information held by the data of this type.
    pub span: Span,
    /// The primary data for this type, [`Spanned`] auto-derefs to this.
    pub data: T,
}

impl<T: ?Sized> Deref for Spanned<T> {
    type Target = T;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

impl<T: ?Sized> DerefMut for Spanned<T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.data
    }
}

impl<T> From<Token<T>> for Spanned<T> {
    #[inline]
    fn from(value: Token<T>) -> Self {
        value.into_spanned()
    }
}

/// Indicates that this type can be spanned— auto-implemented for all types.
pub trait Spannable {
    /// Attach [Span] information to this type by converting
    /// it into a [Spanned].
    fn spanned(self, span: Span) -> Spanned<Self>;
}

impl<T> Spannable for T {
    #[inline]
    fn spanned(self, span: Span) -> Spanned<Self> {
        Spanned { span, data: self }
    }
}