mod buffered_lexer;
use crate::{
Error,
lexer::{InputElement, Lexer, Token, TokenKind},
parser::{OrAbrupt, ParseResult},
source::ReadChar,
};
use boa_ast::{LinearPosition, PositionGroup, Punctuator, Spanned};
use boa_interner::Interner;
use buffered_lexer::BufferedLexer;
#[derive(Debug)]
pub(super) enum SemicolonResult<'s> {
Found(Option<&'s Token>),
NotFound(&'s Token),
}
#[derive(Debug)]
pub(super) struct Cursor<R> {
buffered_lexer: BufferedLexer<R>,
arrow: bool,
json_parse: bool,
identifier: u32,
tagged_templates_count: u32,
}
impl<R> Cursor<R>
where
R: ReadChar,
{
pub(super) fn new(reader: R) -> Self {
Self {
buffered_lexer: Lexer::new(reader).into(),
arrow: false,
json_parse: false,
identifier: 0,
tagged_templates_count: 0,
}
}
pub(super) fn set_module(&mut self) {
self.buffered_lexer.set_module(true);
}
pub(super) const fn module(&self) -> bool {
self.buffered_lexer.module()
}
pub(super) fn set_goal(&mut self, elm: InputElement) {
self.buffered_lexer.set_goal(elm);
}
pub(super) fn lex_regex(
&mut self,
start: PositionGroup,
interner: &mut Interner,
init_with_eq: bool,
) -> ParseResult<Token> {
self.buffered_lexer.lex_regex(start, interner, init_with_eq)
}
pub(super) fn lex_template(
&mut self,
start: PositionGroup,
interner: &mut Interner,
) -> ParseResult<Token> {
self.buffered_lexer.lex_template(start, interner)
}
pub(super) fn next(&mut self, interner: &mut Interner) -> ParseResult<Option<Token>> {
self.buffered_lexer.next(true, interner)
}
#[track_caller]
pub(super) fn advance(&mut self, interner: &mut Interner) {
self.next(interner)
.expect("tried to advance cursor, but the buffer was empty");
}
pub(super) fn peek(
&mut self,
skip_n: usize,
interner: &mut Interner,
) -> ParseResult<Option<&Token>> {
self.buffered_lexer.peek(skip_n, true, interner)
}
pub(super) fn peek_no_skip_line_term(
&mut self,
skip_n: usize,
interner: &mut Interner,
) -> ParseResult<Option<&Token>> {
self.buffered_lexer.peek(skip_n, false, interner)
}
pub(super) const fn strict(&self) -> bool {
self.buffered_lexer.strict()
}
pub(super) fn set_strict(&mut self, strict: bool) {
self.buffered_lexer.set_strict(strict);
}
pub(super) const fn arrow(&self) -> bool {
self.arrow
}
pub(super) fn set_arrow(&mut self, arrow: bool) {
self.arrow = arrow;
}
pub(super) const fn json_parse(&self) -> bool {
self.json_parse
}
pub(super) fn set_json_parse(&mut self, json_parse: bool) {
self.json_parse = json_parse;
}
#[inline]
pub(super) fn set_identifier(&mut self, identifier: u32) {
self.identifier = identifier;
}
#[inline]
pub(super) fn tagged_template_identifier(&mut self) -> u64 {
self.tagged_templates_count += 1;
let identifier = u64::from(self.identifier);
let count = u64::from(self.tagged_templates_count);
(count << 32) | identifier
}
pub(super) fn expect<K>(
&mut self,
kind: K,
context: &'static str,
interner: &mut Interner,
) -> ParseResult<Token>
where
K: Into<TokenKind>,
{
let next_token = self.next(interner).or_abrupt()?;
let kind = kind.into();
if next_token.kind() == &kind {
Ok(next_token)
} else {
Err(Error::expected(
[kind.to_string(interner)],
next_token.to_string(interner),
next_token.span(),
context,
))
}
}
pub(super) fn peek_semicolon(
&mut self,
interner: &mut Interner,
) -> ParseResult<SemicolonResult<'_>> {
self.peek_no_skip_line_term(0, interner)?
.map_or(Ok(SemicolonResult::Found(None)), |tk| match tk.kind() {
TokenKind::Punctuator(Punctuator::Semicolon | Punctuator::CloseBlock)
| TokenKind::LineTerminator => Ok(SemicolonResult::Found(Some(tk))),
_ => Ok(SemicolonResult::NotFound(tk)),
})
}
pub(super) fn expect_semicolon(
&mut self,
context: &'static str,
interner: &mut Interner,
) -> ParseResult<()> {
match self.peek_semicolon(interner)? {
SemicolonResult::Found(Some(tk)) => match *tk.kind() {
TokenKind::Punctuator(Punctuator::Semicolon) | TokenKind::LineTerminator => {
let _next = self.buffered_lexer.next(false, interner)?;
Ok(())
}
_ => Ok(()),
},
SemicolonResult::Found(None) => Ok(()),
SemicolonResult::NotFound(tk) => Err(Error::expected(
[";".to_owned()],
tk.to_string(interner),
tk.span(),
context,
)),
}
}
pub(super) fn peek_expect_no_lineterminator(
&mut self,
skip_n: usize,
context: &'static str,
interner: &mut Interner,
) -> ParseResult<&Token> {
let tok = self.peek_no_skip_line_term(skip_n, interner).or_abrupt()?;
if tok.kind() == &TokenKind::LineTerminator {
Err(Error::unexpected(
tok.to_string(interner),
tok.span(),
context,
))
} else {
Ok(tok)
}
}
pub(super) fn peek_is_line_terminator(
&mut self,
skip_n: usize,
interner: &mut Interner,
) -> ParseResult<Option<bool>> {
self.peek_no_skip_line_term(skip_n, interner)?
.map_or(Ok(None), |t| {
Ok(Some(t.kind() == &TokenKind::LineTerminator))
})
}
pub(super) fn next_if<K>(
&mut self,
kind: K,
interner: &mut Interner,
) -> ParseResult<Option<Token>>
where
K: Into<TokenKind>,
{
if let Some(token) = self.peek(0, interner)?
&& token.kind() == &kind.into()
{
self.next(interner)
} else {
Ok(None)
}
}
#[inline]
pub(super) fn linear_pos(&self) -> LinearPosition {
self.buffered_lexer.linear_pos()
}
pub(super) fn take_source(&mut self) -> boa_ast::SourceText {
self.buffered_lexer.take_source()
}
}