#![deny(rustdoc::broken_intra_doc_links)]
#![doc = include_str!("../CONTRIBUTING.md")]
use crate::diagnostic::{expected_token, ParseDiagnostic, ToDiagnostic};
use crate::event::Event;
use crate::event::Event::Token;
use crate::token_source::{BumpWithContext, NthToken, TokenSource, TokenSourceWithBufferedLexer};
use biome_console::fmt::Display;
use biome_diagnostics::location::AsSpan;
use biome_rowan::{AstNode, Language, SendNode, SyntaxKind, SyntaxNode, TextRange, TextSize};
use std::any::type_name;
pub mod diagnostic;
pub mod event;
pub mod lexer;
mod marker;
pub mod parse_lists;
pub mod parse_recovery;
pub mod parsed_syntax;
pub mod prelude;
pub mod token_set;
pub mod token_source;
pub mod tree_sink;
use crate::lexer::LexerWithCheckpoint;
use crate::parsed_syntax::ParsedSyntax;
use crate::parsed_syntax::ParsedSyntax::{Absent, Present};
use biome_diagnostics::serde::Diagnostic;
pub use marker::{CompletedMarker, Marker};
pub use token_set::TokenSet;
pub struct ParserContext<K: SyntaxKind> {
events: Vec<Event<K>>,
skipping: bool,
diagnostics: Vec<ParseDiagnostic>,
}
impl<K: SyntaxKind> Default for ParserContext<K> {
fn default() -> Self {
Self::new()
}
}
impl<K: SyntaxKind> ParserContext<K> {
pub fn new() -> Self {
Self {
skipping: false,
events: Vec::new(),
diagnostics: Vec::new(),
}
}
pub fn events(&self) -> &[Event<K>] {
&self.events
}
pub fn diagnostics(&self) -> &[ParseDiagnostic] {
&self.diagnostics
}
pub fn truncate_diagnostics(&mut self, at: usize) {
self.diagnostics.truncate(at);
}
pub fn push_token(&mut self, kind: K, end: TextSize) {
self.push_event(Token { kind, end });
}
pub fn push_event(&mut self, event: Event<K>) {
self.events.push(event)
}
pub fn is_skipping(&self) -> bool {
self.skipping
}
pub unsafe fn split_off_events(&mut self, position: usize) -> Vec<Event<K>> {
self.events.split_off(position)
}
fn cur_event_pos(&self) -> usize {
self.events.len().saturating_sub(1)
}
fn drain_events(&mut self, amount: usize) {
self.events.truncate(self.events.len() - amount);
}
pub fn rewind(&mut self, checkpoint: ParserContextCheckpoint) {
let ParserContextCheckpoint {
event_pos,
errors_pos,
} = checkpoint;
self.drain_events(self.cur_event_pos() - event_pos);
self.diagnostics.truncate(errors_pos as usize);
}
#[must_use]
pub fn checkpoint(&self) -> ParserContextCheckpoint {
ParserContextCheckpoint {
event_pos: self.cur_event_pos(),
errors_pos: self.diagnostics.len() as u32,
}
}
pub fn finish(self) -> (Vec<Event<K>>, Vec<ParseDiagnostic>) {
(self.events, self.diagnostics)
}
}
#[derive(Debug)]
pub struct ParserContextCheckpoint {
event_pos: usize,
errors_pos: u32,
}
impl ParserContextCheckpoint {
pub fn event_position(&self) -> usize {
self.event_pos
}
}
pub trait Parser: Sized {
type Kind: SyntaxKind;
type Source: TokenSource<Kind = Self::Kind>;
fn context(&self) -> &ParserContext<Self::Kind>;
fn context_mut(&mut self) -> &mut ParserContext<Self::Kind>;
fn source(&self) -> &Self::Source;
fn source_mut(&mut self) -> &mut Self::Source;
fn is_speculative_parsing(&self) -> bool {
false
}
fn text(&self, span: TextRange) -> &str {
&self.source().text()[span]
}
fn cur(&self) -> Self::Kind {
self.source().current()
}
fn cur_range(&self) -> TextRange {
self.source().current_range()
}
fn has_preceding_line_break(&self) -> bool {
self.source().has_preceding_line_break()
}
fn cur_text(&self) -> &str {
&self.source().text()[self.cur_range()]
}
fn at(&self, kind: Self::Kind) -> bool {
self.cur() == kind
}
fn at_ts(&self, kinds: TokenSet<Self::Kind>) -> bool {
kinds.contains(self.cur())
}
fn nth<'l, Lex>(&mut self, n: usize) -> Self::Kind
where
Lex: LexerWithCheckpoint<'l, Kind = Self::Kind>,
Self::Source: NthToken<Lex> + TokenSourceWithBufferedLexer<Lex>,
{
self.source_mut().nth(n)
}
fn nth_at<'l, Lex>(&mut self, n: usize, kind: Self::Kind) -> bool
where
Lex: LexerWithCheckpoint<'l, Kind = Self::Kind>,
Self::Source: NthToken<Lex> + TokenSourceWithBufferedLexer<Lex>,
{
self.nth(n) == kind
}
fn nth_at_ts<'l, Lex>(&mut self, n: usize, kinds: TokenSet<Self::Kind>) -> bool
where
Lex: LexerWithCheckpoint<'l, Kind = Self::Kind>,
Self::Source: NthToken<Lex> + TokenSourceWithBufferedLexer<Lex>,
{
kinds.contains(self.nth(n))
}
#[inline]
fn has_nth_preceding_line_break<'l, Lex>(&mut self, n: usize) -> bool
where
Lex: LexerWithCheckpoint<'l, Kind = Self::Kind>,
Self::Source: NthToken<Lex> + TokenSourceWithBufferedLexer<Lex>,
{
self.source_mut().has_nth_preceding_line_break(n)
}
fn bump(&mut self, kind: Self::Kind) {
assert_eq!(
kind,
self.cur(),
"expected {:?} but at {:?}",
kind,
self.cur()
);
self.do_bump(kind)
}
fn bump_ts(&mut self, kinds: TokenSet<Self::Kind>) {
assert!(
kinds.contains(self.cur()),
"expected {:?} but at {:?}",
kinds,
self.cur()
);
self.bump_any()
}
fn bump_remap_with_context(
&mut self,
kind: Self::Kind,
context: <Self::Source as BumpWithContext>::Context,
) where
Self::Source: BumpWithContext,
{
self.do_bump_with_context(kind, context);
}
fn bump_remap(&mut self, kind: Self::Kind) {
self.do_bump(kind);
}
fn bump_any(&mut self) {
let kind = self.cur();
assert_ne!(kind, Self::Kind::EOF);
self.do_bump(kind);
}
fn bump_with_context(
&mut self,
kind: Self::Kind,
context: <Self::Source as BumpWithContext>::Context,
) where
Self::Source: BumpWithContext,
{
assert_eq!(
kind,
self.cur(),
"expected {:?} but at {:?}",
kind,
self.cur()
);
self.do_bump_with_context(kind, context);
}
#[doc(hidden)]
fn do_bump_with_context(
&mut self,
kind: Self::Kind,
context: <Self::Source as BumpWithContext>::Context,
) where
Self::Source: BumpWithContext,
{
let end = self.cur_range().end();
self.context_mut().push_token(kind, end);
if self.context().skipping {
self.source_mut().skip_as_trivia_with_context(context);
} else {
self.source_mut().bump_with_context(context);
}
}
#[doc(hidden)]
fn do_bump(&mut self, kind: Self::Kind) {
let end = self.cur_range().end();
self.context_mut().push_token(kind, end);
if self.context().skipping {
self.source_mut().skip_as_trivia();
} else {
self.source_mut().bump();
}
}
fn eat_with_context(
&mut self,
kind: Self::Kind,
context: <Self::Source as BumpWithContext>::Context,
) -> bool
where
Self::Source: BumpWithContext,
{
if !self.at(kind) {
return false;
}
self.do_bump_with_context(kind, context);
true
}
fn eat(&mut self, kind: Self::Kind) -> bool {
if !self.at(kind) {
return false;
}
self.do_bump(kind);
true
}
fn eat_ts(&mut self, kinds: TokenSet<Self::Kind>) -> bool {
if !self.at_ts(kinds) {
return false;
}
self.do_bump(self.cur());
true
}
fn eat_ts_with_context(
&mut self,
kinds: TokenSet<Self::Kind>,
context: <Self::Source as BumpWithContext>::Context,
) -> bool
where
Self::Source: BumpWithContext,
{
if !self.at_ts(kinds) {
return false;
}
self.do_bump_with_context(self.cur(), context);
true
}
fn expect_with_context(
&mut self,
kind: Self::Kind,
context: <Self::Source as BumpWithContext>::Context,
) -> bool
where
Self::Source: BumpWithContext,
{
if self.eat_with_context(kind, context) {
true
} else {
self.error(expected_token(kind));
false
}
}
fn expect(&mut self, kind: Self::Kind) -> bool {
if self.eat(kind) {
true
} else {
self.error(expected_token(kind));
false
}
}
fn parse_as_skipped_trivia_tokens<P>(&mut self, parse: P)
where
P: FnOnce(&mut Self),
{
let events_pos = self.context().events.len();
self.context_mut().skipping = true;
parse(self);
self.context_mut().skipping = false;
self.context_mut().events.truncate(events_pos);
}
fn error(&mut self, err: impl ToDiagnostic<Self>) {
let err = err.into_diagnostic(self);
if let Some(previous) = self.context().diagnostics.last() {
match (&err.diagnostic_range(), &previous.diagnostic_range()) {
(Some(err_range), Some(previous_range))
if err_range.start() == previous_range.start() =>
{
return;
}
_ => {}
}
}
self.context_mut().diagnostics.push(err)
}
#[must_use]
fn err_builder(&self, message: impl Display, span: impl AsSpan) -> ParseDiagnostic {
ParseDiagnostic::new(message, span)
}
fn err_and_bump(&mut self, err: impl ToDiagnostic<Self>, unknown_syntax_kind: Self::Kind) {
let m = self.start();
self.bump_any();
m.complete(self, unknown_syntax_kind);
self.error(err);
}
fn last(&self) -> Option<Self::Kind> {
self.context()
.events
.iter()
.rev()
.find_map(|event| match event {
Token { kind, .. } => Some(*kind),
_ => None,
})
}
fn last_end(&self) -> Option<TextSize> {
self.context()
.events
.iter()
.rev()
.find_map(|event| match event {
Token { end, .. } => Some(*end),
_ => None,
})
}
fn start(&mut self) -> Marker {
let pos = self.context().events.len() as u32;
let start = self.source().position();
self.context_mut().push_event(Event::tombstone());
Marker::new(pos, start)
}
}
#[derive(Debug, Eq, Ord, PartialOrd, PartialEq, Hash, Default)]
pub struct ParserProgress(Option<TextSize>);
impl ParserProgress {
#[inline]
pub fn has_progressed<P>(&self, p: &P) -> bool
where
P: Parser,
{
match self.0 {
None => true,
Some(pos) => pos < p.source().position(),
}
}
#[inline]
pub fn assert_progressing<P>(&mut self, p: &P)
where
P: Parser,
{
assert!(
self.has_progressed(p),
"The parser is no longer progressing. Stuck at '{}' {:?}:{:?}",
p.cur_text(),
p.cur(),
p.cur_range(),
);
self.0 = Some(p.source().position());
}
}
pub trait SyntaxFeature: Sized {
type Parser<'source>: Parser;
fn is_supported(&self, p: &Self::Parser<'_>) -> bool;
fn is_unsupported(&self, p: &Self::Parser<'_>) -> bool {
!self.is_supported(p)
}
fn exclusive_syntax<'source, S, E, D>(
&self,
p: &mut Self::Parser<'source>,
syntax: S,
error_builder: E,
) -> ParsedSyntax
where
S: Into<ParsedSyntax>,
E: FnOnce(&Self::Parser<'source>, &CompletedMarker) -> D,
D: ToDiagnostic<Self::Parser<'source>>,
{
syntax.into().map(|mut syntax| {
if self.is_unsupported(p) {
let error = error_builder(p, &syntax);
p.error(error);
syntax.change_to_bogus(p);
syntax
} else {
syntax
}
})
}
fn parse_exclusive_syntax<'source, P, E>(
&self,
p: &mut Self::Parser<'source>,
parse: P,
error_builder: E,
) -> ParsedSyntax
where
P: FnOnce(&mut Self::Parser<'source>) -> ParsedSyntax,
E: FnOnce(&Self::Parser<'source>, &CompletedMarker) -> ParseDiagnostic,
{
if self.is_supported(p) {
parse(p)
} else {
let diagnostics_checkpoint = p.context().diagnostics().len();
let syntax = parse(p);
p.context_mut().truncate_diagnostics(diagnostics_checkpoint);
match syntax {
Present(mut syntax) => {
let diagnostic = error_builder(p, &syntax);
p.error(diagnostic);
syntax.change_to_bogus(p);
Present(syntax)
}
_ => Absent,
}
}
}
fn excluding_syntax<'source, S, E>(
&self,
p: &mut Self::Parser<'source>,
syntax: S,
error_builder: E,
) -> ParsedSyntax
where
S: Into<ParsedSyntax>,
E: FnOnce(&Self::Parser<'source>, &CompletedMarker) -> ParseDiagnostic,
{
syntax.into().map(|mut syntax| {
if self.is_unsupported(p) {
syntax
} else {
let error = error_builder(p, &syntax);
p.error(error);
syntax.change_to_bogus(p);
syntax
}
})
}
}
#[derive(Clone, Debug)]
pub struct AnyParse {
pub(crate) root: SendNode,
pub(crate) diagnostics: Vec<ParseDiagnostic>,
}
impl AnyParse {
pub fn new(root: SendNode, diagnostics: Vec<ParseDiagnostic>) -> AnyParse {
AnyParse { root, diagnostics }
}
pub fn syntax<L>(&self) -> SyntaxNode<L>
where
L: Language + 'static,
{
self.root.clone().into_node().unwrap_or_else(|| {
panic!(
"could not downcast root node to language {}",
type_name::<L>()
)
})
}
pub fn tree<N>(&self) -> N
where
N: AstNode,
N::Language: 'static,
{
N::unwrap_cast(self.syntax::<N::Language>())
}
pub fn into_diagnostics(self) -> Vec<Diagnostic> {
self.diagnostics.into_iter().map(Diagnostic::new).collect()
}
pub fn diagnostics(&self) -> &[ParseDiagnostic] {
&self.diagnostics
}
pub fn has_errors(&self) -> bool {
self.diagnostics.iter().any(|diag| diag.is_error())
}
}