laburnum 1.17.0

An LSP framework for building language servers and compilers, powered by an incremental query tree with content-addressed storage, task-based dataflow, and parallel queries.
Documentation
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

// TODO: lets maybe move this to another crate in the laburnum repo specific to writing parsers for laburnum

//! Tools for using [chumsky](https://docs.rs/chumsky) and laburnum together.
//!
//! Provides parser state, span creation, token streaming, and macros for
//! building lexers and CST node databases.
//!
//! # Key Types
//!
//! - [`State`] - Parser state passed to `parse_with_state()`, manages span cache
//! - [`LexExtra`] - Type alias for chumsky's `extra::Full` configured for laburnum
//! - [`SpanCreator`] - Trait for converting `SimpleSpan` to laburnum `Span`
//! - [`LaburnumSpanExt`] - Extension trait for `MapExtra` to create spans in closures
//!
//! # Workflow
//!
//! ```ignore
//! use laburnum::chumsky::State;
//!
//! let source_key = SourceKey::new(file_id, version);
//! let mut span_cache = SpanCache::default();
//! let mut state = State::new(source_key, &mut span_cache);
//!
//! let (tokens, errors) = lexer()
//!     .parse_with_state(src, &mut state)
//!     .into_output_errors();
//! ```

use chumsky::input::{
  Input, MapExtra,
};
use chumsky::prelude::*;

use chumsky::extra::ParserExtra;

use crate::{SourceKey, Span, SpanCache};

pub use laburnum_syntax_macro::laburnum_syntax;

pub mod ast;
pub mod lexer;
pub mod node_db;
pub mod stream;

// Re-export lexer types for convenience
pub use lexer::{Trivia, define_tokens, wrap};
pub use lexer::trivia::print_trivia_bluegum;

/// The lexer extra type, generic over error type E.
///
/// Uses laburnum's `State` for span creation.
///
/// - Use `Rich<'src, char>` for detailed errors
/// - Use `EmptyErr` for fast parsing
pub type LexExtra<'src, E> = chumsky::extra::Full<E, State<'src>, ()>;

/// Parser state for chumsky integration, managing source keys and span caching.
///
/// This state is passed through the chumsky parser using the
/// `parse_with_state()` method, enabling automatic span tracking and source key
/// association.
///
/// # Example
///
/// ```ignore
/// use laburnum::chumsky::State;
/// use chumsky::Parser;
///
/// let source_key = SourceKey::new(file_id, version);
/// let mut state = State::new(source_key);
///
/// let (result, errors) = my_parser()
///   .parse_with_state(input, &mut state)
///   .into_output_errors();
///
/// State now contains span cache for position mapping
/// source.set_span_cache(state.span_cache);
/// ```
pub struct State<'src> {
  pub source_key: SourceKey,
  pub span_cache: &'src mut SpanCache,
}

impl<'src> State<'src> {
  /// Creates a new parser state for the given source file version.
  pub fn new(source_key: SourceKey, span_cache: &'src mut SpanCache) -> Self {
    Self {
      source_key,
      span_cache,
    }
  }
}

impl<'src, I: chumsky::input::Input<'src>>
  chumsky::inspector::Inspector<'src, I> for State<'src>
{
  type Checkpoint = ();

  #[inline(always)]
  fn on_token(&mut self, _: &<I as chumsky::input::Input<'src>>::Token) {}

  #[inline(always)]
  fn on_save<'parse>(
    &self,
    _: &chumsky::input::Cursor<'src, 'parse, I>,
  ) -> Self::Checkpoint {
  }

  #[inline(always)]
  fn on_rewind<'parse>(
    &mut self,
    _: &chumsky::input::Checkpoint<'src, 'parse, I, Self::Checkpoint>,
  ) {
  }
}

/// Converts chumsky's `SimpleSpan` to laburnum's `Span`.
///
/// Implemented by parser state types that have access to a [`SpanCache`].
/// This enables [`LaburnumSpanExt`] methods to create proper spans during parsing.
///
/// The [`State`] type implements this trait, as do CST state types generated
/// by [`define_node_db!`](crate::chumsky::node_db::define_node_db).
pub trait SpanCreator {
  fn span_from(&mut self, span: SimpleSpan) -> Span;
}

impl SpanCreator for State<'_> {
  fn span_from(&mut self, span: SimpleSpan) -> Span {
    self
      .span_cache
      .create_span(span.start, span.end.saturating_sub(span.start))
  }
}

/// Extension trait for chumsky's `MapExtra` to create laburnum spans.
///
/// Provides convenient methods in `.map_with()` closures to convert the
/// current parse position into a laburnum [`Span`].
///
/// # Example
///
/// ```ignore
/// use laburnum::chumsky::LaburnumSpanExt;
///
/// my_parser
///     .map_with(|value, e| {
///         let span = e.create_span();
///         MyNode { value, span }
///     })
/// ```
///
/// # Requirements
///
/// The parser extra's state must implement [`SpanCreator`].
pub trait LaburnumSpanExt<'src, 'b, I, E>
where
  I: Input<'src, Span = SimpleSpan>,
  E: ParserExtra<'src, I>,
{
  /// Create a laburnum `Span` from the current parse position.
  fn create_span(&mut self) -> Span
  where
    E::State: SpanCreator;

  /// Wrap a value in a `Spanned` with the current parse position.
  fn create_spanned<V>(&mut self, inner: V) -> Spanned<V, Span>
  where
    E::State: SpanCreator;
}

impl<'src, 'b, I, E> LaburnumSpanExt<'src, 'b, I, E>
  for MapExtra<'src, 'b, I, E>
where
  I: Input<'src, Span = SimpleSpan>,
  E: ParserExtra<'src, I>,
{
  fn create_span(&mut self) -> Span
  where
    E::State: SpanCreator,
  {
    let simple_span = self.span();
    self.state().span_from(simple_span)
  }

  fn create_spanned<V>(&mut self, inner: V) -> Spanned<V, Span>
  where
    E::State: SpanCreator,
  {
    let simple_span = self.span();
    Spanned {
      inner,
      span: self.state().span_from(simple_span),
    }
  }
}