mirror of https://github.com/boa-dev/boa.git
Browse Source
This Pull Request changes the following: - Crate dedicated `IdentifierReference` parser and refactor `PrimaryExpression` and `PropertyDefinition` parsers to use it. - Move `BindingIdentifier` and `LabelIdentifier` parsers from statement parser module to expression parser module to conform with the spec. - Add and early error case while converting an `ObjectLiteral` to an `ObjectAssignmentPattern`pull/2070/head
raskad
3 years ago
21 changed files with 391 additions and 285 deletions
@ -0,0 +1,238 @@
|
||||
//! Identifiers parsing.
|
||||
//!
|
||||
//! More information:
|
||||
//! - [ECMAScript specification][spec]
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#sec-identifiers
|
||||
//!
|
||||
|
||||
use crate::syntax::{ |
||||
ast::{node::Identifier, Keyword}, |
||||
lexer::{Error as LexError, TokenKind}, |
||||
parser::{cursor::Cursor, AllowAwait, AllowYield, ParseError, TokenParser}, |
||||
}; |
||||
use boa_interner::{Interner, Sym}; |
||||
use boa_profiler::Profiler; |
||||
use std::io::Read; |
||||
|
||||
const RESERVED_IDENTIFIERS_STRICT: [Sym; 9] = [ |
||||
Sym::IMPLEMENTS, |
||||
Sym::INTERFACE, |
||||
Sym::LET, |
||||
Sym::PACKAGE, |
||||
Sym::PRIVATE, |
||||
Sym::PROTECTED, |
||||
Sym::PUBLIC, |
||||
Sym::STATIC, |
||||
Sym::YIELD, |
||||
]; |
||||
|
||||
/// Identifier reference parsing.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-IdentifierReference
|
||||
#[derive(Debug, Clone, Copy)] |
||||
pub(in crate::syntax::parser) struct IdentifierReference { |
||||
allow_yield: AllowYield, |
||||
allow_await: AllowAwait, |
||||
} |
||||
|
||||
impl IdentifierReference { |
||||
/// Creates a new `IdentifierReference` parser.
|
||||
pub(in crate::syntax::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||
where |
||||
Y: Into<AllowYield>, |
||||
A: Into<AllowAwait>, |
||||
{ |
||||
Self { |
||||
allow_yield: allow_yield.into(), |
||||
allow_await: allow_await.into(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl<R> TokenParser<R> for IdentifierReference |
||||
where |
||||
R: Read, |
||||
{ |
||||
type Output = Identifier; |
||||
|
||||
fn parse( |
||||
self, |
||||
cursor: &mut Cursor<R>, |
||||
interner: &mut Interner, |
||||
) -> Result<Self::Output, ParseError> { |
||||
let _timer = Profiler::global().start_event("IdentifierReference", "Parsing"); |
||||
|
||||
let token = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; |
||||
|
||||
match token.kind() { |
||||
TokenKind::Identifier(ident) |
||||
if cursor.strict_mode() && RESERVED_IDENTIFIERS_STRICT.contains(ident) => |
||||
{ |
||||
Err(ParseError::general( |
||||
"using future reserved keyword not allowed in strict mode IdentifierReference", |
||||
token.span().start(), |
||||
)) |
||||
} |
||||
TokenKind::Identifier(ident) => Ok(Identifier::new(*ident)), |
||||
TokenKind::Keyword((Keyword::Let, _)) if cursor.strict_mode() => { |
||||
Err(ParseError::general( |
||||
"using future reserved keyword not allowed in strict mode IdentifierReference", |
||||
token.span().start(), |
||||
)) |
||||
} |
||||
TokenKind::Keyword((Keyword::Let, _)) => Ok(Identifier::new(Sym::LET)), |
||||
TokenKind::Keyword((Keyword::Yield, _)) if self.allow_yield.0 => { |
||||
// Early Error: It is a Syntax Error if this production has a [Yield] parameter and StringValue of Identifier is "yield".
|
||||
Err(ParseError::general( |
||||
"Unexpected identifier", |
||||
token.span().start(), |
||||
)) |
||||
} |
||||
TokenKind::Keyword((Keyword::Yield, _)) if !self.allow_yield.0 => { |
||||
if cursor.strict_mode() { |
||||
return Err(ParseError::general( |
||||
"Unexpected strict mode reserved word", |
||||
token.span().start(), |
||||
)); |
||||
} |
||||
Ok(Identifier::new(Sym::YIELD)) |
||||
} |
||||
TokenKind::Keyword((Keyword::Await, _)) if self.allow_await.0 => { |
||||
// Early Error: It is a Syntax Error if this production has an [Await] parameter and StringValue of Identifier is "await".
|
||||
Err(ParseError::general( |
||||
"Unexpected identifier", |
||||
token.span().start(), |
||||
)) |
||||
} |
||||
TokenKind::Keyword((Keyword::Await, _)) if !self.allow_await.0 => { |
||||
Ok(Identifier::new(Sym::AWAIT)) |
||||
} |
||||
_ => Err(ParseError::unexpected( |
||||
token.to_string(interner), |
||||
token.span(), |
||||
"IdentifierReference", |
||||
)), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Binding identifier parsing.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-BindingIdentifier
|
||||
#[derive(Debug, Clone, Copy)] |
||||
pub(in crate::syntax::parser) struct BindingIdentifier { |
||||
allow_yield: AllowYield, |
||||
allow_await: AllowAwait, |
||||
} |
||||
|
||||
impl BindingIdentifier { |
||||
/// Creates a new `BindingIdentifier` parser.
|
||||
pub(in crate::syntax::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
||||
where |
||||
Y: Into<AllowYield>, |
||||
A: Into<AllowAwait>, |
||||
{ |
||||
Self { |
||||
allow_yield: allow_yield.into(), |
||||
allow_await: allow_await.into(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl<R> TokenParser<R> for BindingIdentifier |
||||
where |
||||
R: Read, |
||||
{ |
||||
type Output = Sym; |
||||
|
||||
/// Strict mode parsing as per <https://tc39.es/ecma262/#sec-identifiers-static-semantics-early-errors>.
|
||||
fn parse( |
||||
self, |
||||
cursor: &mut Cursor<R>, |
||||
interner: &mut Interner, |
||||
) -> Result<Self::Output, ParseError> { |
||||
let _timer = Profiler::global().start_event("BindingIdentifier", "Parsing"); |
||||
|
||||
let next_token = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; |
||||
|
||||
match next_token.kind() { |
||||
TokenKind::Identifier(Sym::ARGUMENTS) if cursor.strict_mode() => { |
||||
Err(ParseError::lex(LexError::Syntax( |
||||
"unexpected identifier 'arguments' in strict mode".into(), |
||||
next_token.span().start(), |
||||
))) |
||||
} |
||||
TokenKind::Identifier(Sym::EVAL) if cursor.strict_mode() => { |
||||
Err(ParseError::lex(LexError::Syntax( |
||||
"unexpected identifier 'eval' in strict mode".into(), |
||||
next_token.span().start(), |
||||
))) |
||||
} |
||||
TokenKind::Identifier(ident) => { |
||||
if cursor.strict_mode() && RESERVED_IDENTIFIERS_STRICT.contains(ident) { |
||||
return Err(ParseError::general( |
||||
"using future reserved keyword not allowed in strict mode", |
||||
next_token.span().start(), |
||||
)); |
||||
} |
||||
Ok(*ident) |
||||
} |
||||
TokenKind::Keyword((Keyword::Let, _)) if cursor.strict_mode() => { |
||||
Err(ParseError::lex(LexError::Syntax( |
||||
"unexpected identifier 'let' in strict mode".into(), |
||||
next_token.span().start(), |
||||
))) |
||||
} |
||||
TokenKind::Keyword((Keyword::Let, _)) => Ok(Sym::LET), |
||||
TokenKind::Keyword((Keyword::Yield, _)) if self.allow_yield.0 => { |
||||
// Early Error: It is a Syntax Error if this production has a [Yield] parameter and StringValue of Identifier is "yield".
|
||||
Err(ParseError::general( |
||||
"Unexpected identifier", |
||||
next_token.span().start(), |
||||
)) |
||||
} |
||||
TokenKind::Keyword((Keyword::Yield, _)) if !self.allow_yield.0 => { |
||||
if cursor.strict_mode() { |
||||
Err(ParseError::general( |
||||
"yield keyword in binding identifier not allowed in strict mode", |
||||
next_token.span().start(), |
||||
)) |
||||
} else { |
||||
Ok(Sym::YIELD) |
||||
} |
||||
} |
||||
TokenKind::Keyword((Keyword::Await, _)) if cursor.arrow() => Ok(Sym::AWAIT), |
||||
TokenKind::Keyword((Keyword::Await, _)) if self.allow_await.0 => { |
||||
// Early Error: It is a Syntax Error if this production has an [Await] parameter and StringValue of Identifier is "await".
|
||||
Err(ParseError::general( |
||||
"Unexpected identifier", |
||||
next_token.span().start(), |
||||
)) |
||||
} |
||||
TokenKind::Keyword((Keyword::Await, _)) if !self.allow_await.0 => Ok(Sym::AWAIT), |
||||
_ => Err(ParseError::expected( |
||||
["identifier".to_owned()], |
||||
next_token.to_string(interner), |
||||
next_token.span(), |
||||
"binding identifier", |
||||
)), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Label identifier parsing.
|
||||
///
|
||||
/// This seems to be the same as a `BindingIdentifier`.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-LabelIdentifier
|
||||
pub(in crate::syntax::parser) type LabelIdentifier = BindingIdentifier; |
Loading…
Reference in new issue