Rust编写的JavaScript引擎,该项目是一个试验性质的项目。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

176 lines
5.4 KiB

//! Cursor implementation for the parser.
mod buffered_lexer;
use super::ParseError;
use crate::syntax::{
ast::Punctuator,
lexer::{InputElement, Lexer, Position, Token, TokenKind},
};
use buffered_lexer::BufferedLexer;
use std::io::Read;
/// The result of a peek for a semicolon.
#[derive(Debug)]
pub(super) enum SemicolonResult<'s> {
Found(Option<&'s Token>),
NotFound(&'s Token),
}
/// Token cursor.
///
/// This internal structure gives basic testable operations to the parser.
#[derive(Debug)]
pub(super) struct Cursor<R> {
buffered_lexer: BufferedLexer<R>,
}
impl<R> Cursor<R>
where
R: Read,
{
/// Creates a new cursor with the given reader.
#[inline]
pub(super) fn new(reader: R) -> Self {
Self {
buffered_lexer: Lexer::new(reader).into(),
}
}
#[inline]
pub(super) fn set_goal(&mut self, elm: InputElement) {
self.buffered_lexer.set_goal(elm)
}
#[inline]
pub(super) fn lex_regex(&mut self, start: Position) -> Result<Token, ParseError> {
self.buffered_lexer.lex_regex(start)
}
#[inline]
pub(super) fn lex_template(&mut self, start: Position) -> Result<Token, ParseError> {
self.buffered_lexer.lex_template(start)
}
#[inline]
pub(super) fn next(&mut self) -> Result<Option<Token>, ParseError> {
self.buffered_lexer.next(true)
}
#[inline]
pub(super) fn peek(&mut self, skip_n: usize) -> Result<Option<&Token>, ParseError> {
self.buffered_lexer.peek(skip_n, true)
}
#[inline]
pub(super) fn strict_mode(&self) -> bool {
self.buffered_lexer.strict_mode()
}
#[inline]
pub(super) fn set_strict_mode(&mut self, strict_mode: bool) {
self.buffered_lexer.set_strict_mode(strict_mode)
}
/// Returns an error if the next token is not of kind `kind`.
#[inline]
pub(super) fn expect<K>(&mut self, kind: K, context: &'static str) -> Result<Token, ParseError>
where
K: Into<TokenKind>,
{
let next_token = self.next()?.ok_or(ParseError::AbruptEnd)?;
let kind = kind.into();
if next_token.kind() == &kind {
Ok(next_token)
} else {
Err(ParseError::expected(vec![kind], next_token, context))
}
}
/// It will peek for the next token, to see if it's a semicolon.
///
/// It will automatically insert a semicolon if needed, as specified in the [spec][spec].
///
/// [spec]: https://tc39.es/ecma262/#sec-automatic-semicolon-insertion
#[inline]
pub(super) fn peek_semicolon(&mut self) -> Result<SemicolonResult<'_>, ParseError> {
match self.buffered_lexer.peek(0, false)? {
Some(tk) => match tk.kind() {
TokenKind::Punctuator(Punctuator::Semicolon)
| TokenKind::LineTerminator
| TokenKind::Punctuator(Punctuator::CloseBlock) => {
Ok(SemicolonResult::Found(Some(tk)))
}
_ => Ok(SemicolonResult::NotFound(tk)),
},
None => Ok(SemicolonResult::Found(None)),
}
}
/// Consumes the next token if it is a semicolon, or returns a `ParseError` if it's not.
///
/// It will automatically insert a semicolon if needed, as specified in the [spec][spec].
///
/// [spec]: https://tc39.es/ecma262/#sec-automatic-semicolon-insertion
#[inline]
pub(super) fn expect_semicolon(&mut self, context: &'static str) -> Result<(), ParseError> {
match self.peek_semicolon()? {
SemicolonResult::Found(Some(tk)) => match *tk.kind() {
TokenKind::Punctuator(Punctuator::Semicolon) | TokenKind::LineTerminator => {
let _ = self.buffered_lexer.next(false)?;
Ok(())
}
_ => Ok(()),
},
SemicolonResult::Found(None) => Ok(()),
SemicolonResult::NotFound(tk) => Err(ParseError::expected(
vec![TokenKind::Punctuator(Punctuator::Semicolon)],
tk.clone(),
context,
)),
}
}
/// It will make sure that the peeked token (skipping n tokens) is not a line terminator.
///
/// It expects that the token stream does not end here.
///
/// This is just syntatic sugar for a .peek(skip_n) call followed by a check that the result is not a line terminator or None.
#[inline]
pub(super) fn peek_expect_no_lineterminator(
&mut self,
skip_n: usize,
context: &'static str,
) -> Result<&Token, ParseError> {
if let Some(t) = self.buffered_lexer.peek(skip_n, false)? {
if t.kind() == &TokenKind::LineTerminator {
Err(ParseError::unexpected(t.clone(), context))
} else {
Ok(t)
}
} else {
Err(ParseError::AbruptEnd)
}
}
/// Advance the cursor to the next token and retrieve it, only if it's of `kind` type.
///
/// When the next token is a `kind` token, get the token, otherwise return `None`.
///
/// No next token also returns None.
#[inline]
pub(super) fn next_if<K>(&mut self, kind: K) -> Result<Option<Token>, ParseError>
where
K: Into<TokenKind>,
{
Ok(if let Some(token) = self.peek(0)? {
if token.kind() == &kind.into() {
self.next()?
} else {
None
}
} else {
None
})
}
}