use crate::{
profiler::BoaProfiler,
syntax::{
lexer::{InputElement, Lexer, Position, Token, TokenKind},
parser::error::ParseError,
},
};
use std::io::Read;
#[cfg(test)]
mod tests;
const MAX_PEEK_SKIP: usize = 2;
const PEEK_BUF_SIZE: usize = (MAX_PEEK_SKIP + 1) * 2;
#[derive(Debug)]
pub(super) struct BufferedLexer<R> {
lexer: Lexer<R>,
peeked: [Option<Token>; PEEK_BUF_SIZE],
read_index: usize,
write_index: usize,
}
impl<R> From<Lexer<R>> for BufferedLexer<R>
where
R: Read,
{
#[inline]
fn from(lexer: Lexer<R>) -> Self {
Self {
lexer,
peeked: [
None::<Token>,
None::<Token>,
None::<Token>,
None::<Token>,
None::<Token>,
None::<Token>,
],
read_index: 0,
write_index: 0,
}
}
}
impl<R> From<R> for BufferedLexer<R>
where
R: Read,
{
#[inline]
fn from(reader: R) -> Self {
Lexer::new(reader).into()
}
}
impl<R> BufferedLexer<R>
where
R: Read,
{
#[inline]
pub(super) fn set_goal(&mut self, elm: InputElement) {
let _timer = BoaProfiler::global().start_event("cursor::set_goal()", "Parsing");
self.lexer.set_goal(elm)
}
#[inline]
pub(super) fn lex_regex(&mut self, start: Position) -> Result<Token, ParseError> {
let _timer = BoaProfiler::global().start_event("cursor::lex_regex()", "Parsing");
self.set_goal(InputElement::RegExp);
self.lexer.lex_slash_token(start).map_err(|e| e.into())
}
fn fill(&mut self) -> Result<(), ParseError> {
debug_assert!(
self.write_index < PEEK_BUF_SIZE,
"write index went out of bounds"
);
let previous_index = self.write_index.checked_sub(1).unwrap_or(PEEK_BUF_SIZE - 1);
if let Some(ref token) = self.peeked[previous_index] {
if token.kind() == &TokenKind::LineTerminator {
let next = loop {
let next = self.lexer.next()?;
if let Some(ref token) = next {
if token.kind() != &TokenKind::LineTerminator {
break next;
}
} else {
break None;
}
};
self.peeked[self.write_index] = next;
} else {
self.peeked[self.write_index] = self.lexer.next()?;
}
} else {
self.peeked[self.write_index] = self.lexer.next()?;
}
self.write_index = (self.write_index + 1) % PEEK_BUF_SIZE;
debug_assert_ne!(
self.read_index, self.write_index,
"we reached the read index with the write index"
);
debug_assert!(
self.read_index < PEEK_BUF_SIZE,
"read index went out of bounds"
);
Ok(())
}
pub(super) fn next(
&mut self,
skip_line_terminators: bool,
) -> Result<Option<Token>, ParseError> {
if self.read_index == self.write_index {
self.fill()?;
}
if let Some(ref token) = self.peeked[self.read_index] {
let tok = if !skip_line_terminators || token.kind() != &TokenKind::LineTerminator {
self.peeked[self.read_index].take()
} else {
self.read_index = (self.read_index + 1) % PEEK_BUF_SIZE;
if self.read_index == self.write_index {
self.fill()?;
}
self.peeked[self.read_index].take()
};
self.read_index = (self.read_index + 1) % PEEK_BUF_SIZE;
Ok(tok)
} else {
Ok(None)
}
}
pub(super) fn peek(
&mut self,
skip_n: usize,
skip_line_terminators: bool,
) -> Result<Option<&Token>, ParseError> {
assert!(
skip_n <= MAX_PEEK_SKIP,
"you cannot skip more than {} elements",
MAX_PEEK_SKIP
);
let mut read_index = self.read_index;
let mut count = 0;
let res_token = loop {
if read_index == self.write_index {
self.fill()?;
}
if let Some(ref token) = self.peeked[read_index] {
if !skip_line_terminators || token.kind() != &TokenKind::LineTerminator {
if count == skip_n {
break self.peeked[read_index].as_ref();
}
} else {
read_index = (read_index + 1) % PEEK_BUF_SIZE;
if read_index == self.write_index {
self.fill()?;
}
if count == skip_n {
break self.peeked[read_index].as_ref();
}
}
} else {
break None;
}
read_index = (read_index + 1) % PEEK_BUF_SIZE;
count += 1;
};
Ok(res_token)
}
}