use super::diagnostic::ParseDiagnostic;
use biome_rowan::{SyntaxKind, TextRange, TextSize};
use biome_unicode_table::{lookup_byte, Dispatch::WHS};
use enumflags2::{bitflags, make_bitflags, BitFlags};
use std::collections::VecDeque;
use std::iter::FusedIterator;
use std::ops::{BitOr, BitOrAssign};
use unicode_bom::Bom;
pub trait Lexer<'src> {
const NEWLINE: Self::Kind;
const WHITESPACE: Self::Kind;
type Kind: SyntaxKind;
type LexContext: LexContext + Default;
type ReLexContext;
fn source(&self) -> &'src str;
fn current(&self) -> Self::Kind;
fn current_range(&self) -> TextRange {
TextRange::new(self.current_start(), TextSize::from(self.position() as u32))
}
fn current_start(&self) -> TextSize;
fn next_token(&mut self, context: Self::LexContext) -> Self::Kind;
fn has_preceding_line_break(&self) -> bool;
fn has_unicode_escape(&self) -> bool;
fn rewind(&mut self, checkpoint: LexerCheckpoint<Self::Kind>);
fn finish(self) -> Vec<ParseDiagnostic>;
fn current_flags(&self) -> TokenFlags {
TokenFlags::empty()
}
fn position(&self) -> usize;
fn push_diagnostic(&mut self, diagnostic: ParseDiagnostic);
fn consume_newline_or_whitespaces(&mut self) -> Self::Kind {
if self.consume_newline() {
Self::NEWLINE
} else {
self.consume_whitespaces();
Self::WHITESPACE
}
}
fn consume_whitespaces(&mut self) {
self.assert_current_char_boundary();
while let Some(byte) = self.current_byte() {
let dispatch = lookup_byte(byte);
match dispatch {
WHS => match byte {
b'\t' | b' ' => self.advance(1),
b'\r' | b'\n' => {
break;
}
_ => {
let start = self.text_position();
self.advance(1);
self.push_diagnostic(
ParseDiagnostic::new(
"The CSS standard only allows tabs, whitespace, carriage return and line feed whitespace.",
start..self.text_position(),
)
.with_hint("Use a regular whitespace character instead."),
)
}
},
_ => break,
}
}
}
fn assert_byte(&self, byte: u8) {
debug_assert_eq!(self.source().as_bytes()[self.position()], byte);
}
fn next_byte(&mut self) -> Option<u8> {
self.advance(1);
self.current_byte()
}
fn is_eof(&self) -> bool {
self.position() >= self.source().len()
}
fn advance_byte_or_char(&mut self, chr: u8) {
if chr.is_ascii() {
self.advance(1);
} else {
self.advance_char_unchecked();
}
}
#[inline]
fn assert_at_char_boundary(&self, offset: usize) {
debug_assert!(self.source().is_char_boundary(self.position() + offset));
}
fn advance_char_unchecked(&mut self);
fn consume_newline(&mut self) -> bool {
self.assert_current_char_boundary();
match self.current_byte() {
Some(b'\n') => {
self.advance(1);
true
}
Some(b'\r') => {
if self.peek_byte() == Some(b'\n') {
self.advance(2)
} else {
self.advance(1)
}
true
}
_ => false,
}
}
fn advance(&mut self, n: usize);
#[inline]
fn assert_current_char_boundary(&self) {
debug_assert!(self.source().is_char_boundary(self.position()));
}
#[inline]
fn current_byte(&self) -> Option<u8> {
if self.is_eof() {
None
} else {
Some(self.source().as_bytes()[self.position()])
}
}
#[inline]
fn peek_byte(&self) -> Option<u8> {
self.byte_at(1)
}
#[inline]
fn byte_at(&self, offset: usize) -> Option<u8> {
self.source()
.as_bytes()
.get(self.position() + offset)
.copied()
}
#[inline]
fn text_position(&self) -> TextSize {
TextSize::try_from(self.position()).expect("Input to be smaller than 4 GB")
}
#[inline]
fn consume_potential_bom<K>(&mut self, kind: K) -> Option<(K, usize)> {
if let Some(first) = self.source().get(0..3) {
let bom = Bom::from(first.as_bytes());
self.advance(bom.len());
match bom {
Bom::Null => None,
_ => Some((kind, bom.len())),
}
} else {
None
}
}
#[inline]
fn current_char_unchecked(&self) -> char {
debug_assert!(!self.is_eof());
self.assert_current_char_boundary();
unsafe {
let Some(chr) = self
.source()
.get_unchecked(self.position()..self.source().len())
.chars()
.next()
else {
core::hint::unreachable_unchecked();
};
chr
}
}
fn is_metavariable_start(&mut self) -> bool {
let current_char = self.current_char_unchecked();
if current_char == 'µ' {
let current_char_length = current_char.len_utf8();
if matches!(
self.byte_at(current_char_length),
Some(b'a'..=b'z' | b'A'..=b'Z' | b'_')
) {
return true;
}
if self.byte_at(current_char_length) == Some(b'.')
&& self.byte_at(current_char_length + 1) == Some(b'.')
&& self.byte_at(current_char_length + 2) == Some(b'.')
{
return true;
}
}
false
}
fn consume_metavariable<T>(&mut self, kind: T) -> T {
debug_assert!(self.is_metavariable_start());
let current_char = self.current_char_unchecked();
self.advance(current_char.len_utf8());
if self.current_byte() == Some(b'.') {
self.advance(3);
} else {
self.advance(1);
while let Some(chr) = self.current_byte() {
match chr {
b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' => {
self.advance(1);
}
_ => break,
}
}
}
kind
}
}
pub trait ReLexer<'src>: Lexer<'src> + LexerWithCheckpoint<'src> {
fn re_lex(&mut self, context: Self::ReLexContext) -> Self::Kind;
}
pub trait LexerWithCheckpoint<'src>: Lexer<'src> {
fn checkpoint(&self) -> LexerCheckpoint<Self::Kind>;
}
pub trait LexContext {
fn is_regular(&self) -> bool;
}
impl LexContext for () {
fn is_regular(&self) -> bool {
true
}
}
#[derive(Debug)]
pub struct BufferedLexer<Kind, Lex> {
lookahead: Lookahead<Kind>,
current: Option<LexerCheckpoint<Kind>>,
inner: Lex,
}
#[derive(Debug)]
struct Lookahead<Kind> {
all_checkpoints: VecDeque<LexerCheckpoint<Kind>>,
non_trivia_checkpoints: VecDeque<LexerCheckpoint<Kind>>,
}
impl<K: SyntaxKind> Lookahead<K> {
fn new() -> Self {
Self {
all_checkpoints: VecDeque::new(),
non_trivia_checkpoints: VecDeque::new(),
}
}
fn push_back(&mut self, checkpoint: LexerCheckpoint<K>) {
if !checkpoint.current_kind.is_trivia() {
self.non_trivia_checkpoints.push_back(checkpoint.clone());
}
self.all_checkpoints.push_back(checkpoint);
}
fn pop_front(&mut self) -> Option<LexerCheckpoint<K>> {
if let Some(lookahead) = self.all_checkpoints.pop_front() {
if !lookahead.current_kind.is_trivia() {
self.non_trivia_checkpoints.pop_front();
}
Some(lookahead)
} else {
None
}
}
fn get_checkpoint(&self, index: usize) -> Option<&LexerCheckpoint<K>> {
self.all_checkpoints.get(index)
}
fn get_non_trivia_checkpoint(&self, index: usize) -> Option<&LexerCheckpoint<K>> {
self.non_trivia_checkpoints.get(index)
}
fn is_empty(&self) -> bool {
self.all_checkpoints.is_empty()
}
fn clear(&mut self) {
self.all_checkpoints.clear();
self.non_trivia_checkpoints.clear();
}
fn all_len(&self) -> usize {
self.all_checkpoints.len()
}
fn non_trivia_len(&self) -> usize {
self.non_trivia_checkpoints.len()
}
}
impl<'l, Lex: Lexer<'l>> BufferedLexer<Lex::Kind, Lex> {
pub fn new(lexer: Lex) -> Self {
Self {
inner: lexer,
current: None,
lookahead: Lookahead::new(),
}
}
#[inline(always)]
pub fn next_token(&mut self, context: Lex::LexContext) -> Lex::Kind {
if !context.is_regular() {
self.reset_lookahead();
}
else if let Some(next) = self.lookahead.pop_front() {
let kind = next.current_kind;
if self.lookahead.is_empty() {
self.current = None;
} else {
self.current = Some(next);
}
return kind;
}
self.current = None;
self.inner.next_token(context)
}
#[inline(always)]
pub fn current(&self) -> Lex::Kind {
if let Some(current) = &self.current {
current.current_kind
} else {
self.inner.current()
}
}
#[inline(always)]
pub fn current_range(&self) -> TextRange {
if let Some(current) = &self.current {
TextRange::new(current.current_start, current.position)
} else {
self.inner.current_range()
}
}
#[inline(always)]
pub fn has_preceding_line_break(&self) -> bool {
if let Some(current) = &self.current {
current.has_preceding_line_break()
} else {
self.inner.has_preceding_line_break()
}
}
#[inline]
pub fn has_unicode_escape(&self) -> bool {
if let Some(current) = &self.current {
current.has_unicode_escape()
} else {
self.inner.has_unicode_escape()
}
}
#[inline]
pub fn source(&self) -> &'l str {
self.inner.source()
}
pub fn rewind(&mut self, checkpoint: LexerCheckpoint<Lex::Kind>) {
self.inner.rewind(checkpoint);
self.lookahead.clear();
self.current = None;
}
fn reset_lookahead(&mut self) {
if let Some(current) = self.current.take() {
self.inner.rewind(current);
self.lookahead.clear();
}
}
#[inline(always)]
pub fn lookahead_iter<'s>(&'s mut self) -> LookaheadIterator<'s, 'l, Lex> {
LookaheadIterator::new(self)
}
pub fn finish(self) -> Vec<ParseDiagnostic> {
self.inner.finish()
}
}
impl<'l, Lex> BufferedLexer<Lex::Kind, Lex>
where
Lex: ReLexer<'l>,
{
pub fn re_lex(&mut self, context: Lex::ReLexContext) -> Lex::Kind {
let current_kind = self.current();
let current_checkpoint = self.inner.checkpoint();
if let Some(current) = self.current.take() {
self.inner.rewind(current);
}
let new_kind = self.inner.re_lex(context);
if new_kind != current_kind {
self.lookahead.clear();
} else if !self.lookahead.is_empty() {
self.current = Some(self.inner.checkpoint());
self.inner.rewind(current_checkpoint);
}
new_kind
}
}
impl<'l, Lex> BufferedLexer<Lex::Kind, Lex>
where
Lex: LexerWithCheckpoint<'l>,
{
#[inline(always)]
pub fn nth_non_trivia(&mut self, n: usize) -> Option<LookaheadToken<Lex::Kind>> {
assert_ne!(n, 0);
if let Some(lookahead_token) = self
.lookahead
.get_non_trivia_checkpoint(n - 1)
.map(LookaheadToken::from)
{
return Some(lookahead_token);
}
let mut remaining = n - self.lookahead.non_trivia_len();
let current_length = self.lookahead.all_len();
let iter = self.lookahead_iter().skip(current_length);
for item in iter {
if !item.kind().is_trivia() {
remaining -= 1;
if remaining == 0 {
return Some(item);
}
}
}
None
}
pub fn checkpoint(&self) -> LexerCheckpoint<Lex::Kind> {
if let Some(current) = &self.current {
current.clone()
} else {
self.inner.checkpoint()
}
}
}
#[derive(Debug)]
pub struct LookaheadIterator<'l, 't, Lex: Lexer<'t>> {
buffered: &'l mut BufferedLexer<Lex::Kind, Lex>,
nth: usize,
}
impl<'l, 't, Lex: Lexer<'t>> LookaheadIterator<'l, 't, Lex> {
fn new(lexer: &'l mut BufferedLexer<Lex::Kind, Lex>) -> Self {
Self {
buffered: lexer,
nth: 0,
}
}
}
impl<'l, 't, Lex: LexerWithCheckpoint<'t>> Iterator for LookaheadIterator<'l, 't, Lex> {
type Item = LookaheadToken<Lex::Kind>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let lookbehind = &self.buffered.lookahead;
self.nth += 1;
if let Some(lookbehind) = lookbehind.get_checkpoint(self.nth - 1) {
let lookahead = LookaheadToken::from(lookbehind);
return Some(lookahead);
}
let lexer = &mut self.buffered.inner;
if lexer.current() == SyntaxKind::EOF {
return None;
}
if self.buffered.current.is_none() {
self.buffered.current = Some(lexer.checkpoint());
}
let kind = lexer.next_token(Lex::LexContext::default());
self.buffered.lookahead.push_back(lexer.checkpoint());
Some(LookaheadToken {
kind,
flags: lexer.current_flags(),
})
}
}
impl<'l, 't, Lex: LexerWithCheckpoint<'t>> FusedIterator for LookaheadIterator<'l, 't, Lex> {}
#[derive(Debug)]
pub struct LookaheadToken<Kind> {
kind: Kind,
flags: TokenFlags,
}
impl<Kind: SyntaxKind> LookaheadToken<Kind> {
pub fn kind(&self) -> Kind {
self.kind
}
pub fn has_preceding_line_break(&self) -> bool {
self.flags.has_preceding_line_break()
}
}
impl<Kind: SyntaxKind> From<&LexerCheckpoint<Kind>> for LookaheadToken<Kind> {
fn from(checkpoint: &LexerCheckpoint<Kind>) -> Self {
LookaheadToken {
kind: checkpoint.current_kind,
flags: checkpoint.current_flags,
}
}
}
#[derive(Debug, Clone)]
pub struct LexerCheckpoint<Kind> {
pub position: TextSize,
pub current_start: TextSize,
pub current_kind: Kind,
pub current_flags: TokenFlags,
pub after_line_break: bool,
pub unicode_bom_length: usize,
pub diagnostics_pos: u32,
}
impl<Kind: SyntaxKind> LexerCheckpoint<Kind> {
pub fn current_start(&self) -> TextSize {
self.current_start
}
pub(crate) fn has_preceding_line_break(&self) -> bool {
self.current_flags.has_preceding_line_break()
}
pub(crate) fn has_unicode_escape(&self) -> bool {
self.current_flags.has_unicode_escape()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[bitflags]
#[repr(u8)]
enum TokenFlag {
PrecedingLineBreak = 1 << 0,
UnicodeEscape = 1 << 1,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct TokenFlags(BitFlags<TokenFlag>);
impl TokenFlags {
pub const PRECEDING_LINE_BREAK: Self = Self(make_bitflags!(TokenFlag::{PrecedingLineBreak}));
pub const UNICODE_ESCAPE: Self = Self(make_bitflags!(TokenFlag::{UnicodeEscape}));
pub const fn empty() -> Self {
Self(BitFlags::EMPTY)
}
pub fn contains(&self, other: impl Into<TokenFlags>) -> bool {
self.0.contains(other.into().0)
}
pub fn set(&mut self, other: impl Into<TokenFlags>, cond: bool) {
self.0.set(other.into().0, cond)
}
pub fn has_preceding_line_break(&self) -> bool {
self.contains(TokenFlags::PRECEDING_LINE_BREAK)
}
pub fn has_unicode_escape(&self) -> bool {
self.contains(TokenFlags::UNICODE_ESCAPE)
}
}
impl BitOr for TokenFlags {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
TokenFlags(self.0 | rhs.0)
}
}
impl BitOrAssign for TokenFlags {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}