Browse Source

Refactor `IdentifierReference` parsing (#2055)

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
parent
commit
e42e2f64be
  1. 32
      boa_engine/src/syntax/ast/node/operator/assign/mod.rs
  2. 2
      boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs
  3. 2
      boa_engine/src/syntax/parser/expression/assignment/mod.rs
  4. 238
      boa_engine/src/syntax/parser/expression/identifiers.rs
  5. 17
      boa_engine/src/syntax/parser/expression/mod.rs
  6. 2
      boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs
  7. 2
      boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs
  8. 4
      boa_engine/src/syntax/parser/expression/primary/class_expression/mod.rs
  9. 2
      boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs
  10. 2
      boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs
  11. 112
      boa_engine/src/syntax/parser/expression/primary/mod.rs
  12. 63
      boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs
  13. 4
      boa_engine/src/syntax/parser/function/mod.rs
  14. 18
      boa_engine/src/syntax/parser/mod.rs
  15. 2
      boa_engine/src/syntax/parser/statement/break_stm/mod.rs
  16. 2
      boa_engine/src/syntax/parser/statement/continue_stm/mod.rs
  17. 6
      boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs
  18. 3
      boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs
  19. 11
      boa_engine/src/syntax/parser/statement/iteration/for_statement.rs
  20. 4
      boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs
  21. 122
      boa_engine/src/syntax/parser/statement/mod.rs

32
boa_engine/src/syntax/ast/node/operator/assign/mod.rs

@ -8,7 +8,7 @@ use crate::syntax::ast::node::{
ArrayDecl, DeclarationPattern, GetConstField, GetField, Identifier, Node, Object, ArrayDecl, DeclarationPattern, GetConstField, GetField, Identifier, Node, Object,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
use boa_interner::{Interner, ToInternedString}; use boa_interner::{Interner, Sym, ToInternedString};
#[cfg(feature = "deser")] #[cfg(feature = "deser")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -90,18 +90,18 @@ pub enum AssignTarget {
impl AssignTarget { impl AssignTarget {
/// Converts the left-hand-side node of an assignment expression into it's an [`AssignTarget`]. /// Converts the left-hand-side node of an assignment expression into it's an [`AssignTarget`].
/// Returns `None` if the given node is an invalid left-hand-side for a assignment expression. /// Returns `None` if the given node is an invalid left-hand-side for a assignment expression.
pub(crate) fn from_node(node: &Node) -> Option<Self> { pub(crate) fn from_node(node: &Node, strict: bool) -> Option<Self> {
match node { match node {
Node::Identifier(target) => Some(Self::Identifier(*target)), Node::Identifier(target) => Some(Self::Identifier(*target)),
Node::GetPrivateField(target) => Some(Self::GetPrivateField(target.clone())), Node::GetPrivateField(target) => Some(Self::GetPrivateField(target.clone())),
Node::GetConstField(target) => Some(Self::GetConstField(target.clone())), Node::GetConstField(target) => Some(Self::GetConstField(target.clone())),
Node::GetField(target) => Some(Self::GetField(target.clone())), Node::GetField(target) => Some(Self::GetField(target.clone())),
Node::Object(object) => { Node::Object(object) => {
let pattern = object_decl_to_declaration_pattern(object)?; let pattern = object_decl_to_declaration_pattern(object, strict)?;
Some(Self::DeclarationPattern(pattern)) Some(Self::DeclarationPattern(pattern))
} }
Node::ArrayDecl(array) => { Node::ArrayDecl(array) => {
let pattern = array_decl_to_declaration_pattern(array)?; let pattern = array_decl_to_declaration_pattern(array, strict)?;
Some(Self::DeclarationPattern(pattern)) Some(Self::DeclarationPattern(pattern))
} }
_ => None, _ => None,
@ -140,11 +140,17 @@ impl From<GetField> for AssignTarget {
} }
/// Converts an object literal into an object declaration pattern. /// Converts an object literal into an object declaration pattern.
pub(crate) fn object_decl_to_declaration_pattern(object: &Object) -> Option<DeclarationPattern> { pub(crate) fn object_decl_to_declaration_pattern(
object: &Object,
strict: bool,
) -> Option<DeclarationPattern> {
let mut bindings = Vec::new(); let mut bindings = Vec::new();
let mut excluded_keys = Vec::new(); let mut excluded_keys = Vec::new();
for (i, property) in object.properties().iter().enumerate() { for (i, property) in object.properties().iter().enumerate() {
match property { match property {
PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => {
return None
}
PropertyDefinition::IdentifierReference(ident) => { PropertyDefinition::IdentifierReference(ident) => {
excluded_keys.push(*ident); excluded_keys.push(*ident);
bindings.push(BindingPatternTypeObject::SingleName { bindings.push(BindingPatternTypeObject::SingleName {
@ -155,6 +161,9 @@ pub(crate) fn object_decl_to_declaration_pattern(object: &Object) -> Option<Decl
} }
PropertyDefinition::Property(name, node) => match (name, node) { PropertyDefinition::Property(name, node) => match (name, node) {
(PropertyName::Literal(name), Node::Identifier(ident)) if *name == ident.sym() => { (PropertyName::Literal(name), Node::Identifier(ident)) if *name == ident.sym() => {
if strict && *name == Sym::EVAL {
return None;
}
excluded_keys.push(*name); excluded_keys.push(*name);
bindings.push(BindingPatternTypeObject::SingleName { bindings.push(BindingPatternTypeObject::SingleName {
ident: *name, ident: *name,
@ -196,7 +205,10 @@ pub(crate) fn object_decl_to_declaration_pattern(object: &Object) -> Option<Decl
} }
/// Converts an array declaration into an array declaration pattern. /// Converts an array declaration into an array declaration pattern.
pub(crate) fn array_decl_to_declaration_pattern(array: &ArrayDecl) -> Option<DeclarationPattern> { pub(crate) fn array_decl_to_declaration_pattern(
array: &ArrayDecl,
strict: bool,
) -> Option<DeclarationPattern> {
if array.has_trailing_comma_spread() { if array.has_trailing_comma_spread() {
return None; return None;
} }
@ -227,11 +239,11 @@ pub(crate) fn array_decl_to_declaration_pattern(array: &ArrayDecl) -> Option<Dec
}); });
} }
Node::ArrayDecl(array) => { Node::ArrayDecl(array) => {
let pattern = array_decl_to_declaration_pattern(array)?; let pattern = array_decl_to_declaration_pattern(array, strict)?;
bindings.push(BindingPatternTypeArray::BindingPatternRest { pattern }); bindings.push(BindingPatternTypeArray::BindingPatternRest { pattern });
} }
Node::Object(object) => { Node::Object(object) => {
let pattern = object_decl_to_declaration_pattern(object)?; let pattern = object_decl_to_declaration_pattern(object, strict)?;
bindings.push(BindingPatternTypeArray::BindingPatternRest { pattern }); bindings.push(BindingPatternTypeArray::BindingPatternRest { pattern });
} }
_ => return None, _ => return None,
@ -268,11 +280,11 @@ pub(crate) fn array_decl_to_declaration_pattern(array: &ArrayDecl) -> Option<Dec
AssignTarget::GetPrivateField(_) => return None, AssignTarget::GetPrivateField(_) => return None,
}, },
Node::ArrayDecl(array) => { Node::ArrayDecl(array) => {
let pattern = array_decl_to_declaration_pattern(array)?; let pattern = array_decl_to_declaration_pattern(array, strict)?;
bindings.push(BindingPatternTypeArray::BindingPattern { pattern }); bindings.push(BindingPatternTypeArray::BindingPattern { pattern });
} }
Node::Object(object) => { Node::Object(object) => {
let pattern = object_decl_to_declaration_pattern(object)?; let pattern = object_decl_to_declaration_pattern(object, strict)?;
bindings.push(BindingPatternTypeArray::BindingPattern { pattern }); bindings.push(BindingPatternTypeArray::BindingPattern { pattern });
} }
Node::GetField(get_field) => { Node::GetField(get_field) => {

2
boa_engine/src/syntax/parser/expression/assignment/arrow_function.rs

@ -19,8 +19,8 @@ use crate::syntax::{
lexer::{Error as LexError, TokenKind}, lexer::{Error as LexError, TokenKind},
parser::{ parser::{
error::{ErrorContext, ParseError, ParseResult}, error::{ErrorContext, ParseError, ParseResult},
expression::BindingIdentifier,
function::{FormalParameters, FunctionBody}, function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
AllowAwait, AllowIn, AllowYield, Cursor, TokenParser, AllowAwait, AllowIn, AllowYield, Cursor, TokenParser,
}, },
}; };

2
boa_engine/src/syntax/parser/expression/assignment/mod.rs

@ -229,7 +229,7 @@ where
} }
cursor.next(interner)?.expect("= token vanished"); cursor.next(interner)?.expect("= token vanished");
if let Some(target) = AssignTarget::from_node(&lhs) { if let Some(target) = AssignTarget::from_node(&lhs, cursor.strict_mode()) {
if let AssignTarget::Identifier(ident) = target { if let AssignTarget::Identifier(ident) = target {
self.name = Some(ident.sym()); self.name = Some(ident.sym());
} }

238
boa_engine/src/syntax/parser/expression/identifiers.rs

@ -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;

17
boa_engine/src/syntax/parser/expression/mod.rs

@ -8,15 +8,17 @@
//! [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-expressions //! [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-expressions
mod assignment; mod assignment;
mod identifiers;
mod left_hand_side; mod left_hand_side;
mod primary; mod primary;
#[cfg(test)]
mod tests;
mod unary; mod unary;
mod update; mod update;
use self::assignment::ExponentiationExpression; pub(in crate::syntax::parser) mod await_expr;
use super::{AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser};
#[cfg(test)]
mod tests;
use crate::syntax::{ use crate::syntax::{
ast::op::LogOp, ast::op::LogOp,
ast::{ ast::{
@ -24,15 +26,18 @@ use crate::syntax::{
Keyword, Punctuator, Keyword, Punctuator,
}, },
lexer::{InputElement, TokenKind}, lexer::{InputElement, TokenKind},
parser::ParseError, parser::{
expression::assignment::ExponentiationExpression, AllowAwait, AllowIn, AllowYield, Cursor,
ParseError, ParseResult, TokenParser,
},
}; };
use boa_interner::{Interner, Sym}; use boa_interner::{Interner, Sym};
use boa_profiler::Profiler; use boa_profiler::Profiler;
use std::io::Read; use std::io::Read;
pub(super) use self::{assignment::AssignmentExpression, primary::Initializer}; pub(super) use self::{assignment::AssignmentExpression, primary::Initializer};
pub(in crate::syntax::parser) mod await_expr;
pub(in crate::syntax::parser) use { pub(in crate::syntax::parser) use {
identifiers::{BindingIdentifier, LabelIdentifier},
left_hand_side::LeftHandSideExpression, left_hand_side::LeftHandSideExpression,
primary::object_initializer::{ primary::object_initializer::{
AsyncGeneratorMethod, AsyncMethod, GeneratorMethod, PropertyName, AsyncGeneratorMethod, AsyncMethod, GeneratorMethod, PropertyName,

2
boa_engine/src/syntax/parser/expression/primary/async_function_expression/mod.rs

@ -5,8 +5,8 @@ use crate::syntax::{
ast::{node::AsyncFunctionExpr, Keyword, Position, Punctuator}, ast::{node::AsyncFunctionExpr, Keyword, Position, Punctuator},
lexer::{Error as LexError, TokenKind}, lexer::{Error as LexError, TokenKind},
parser::{ parser::{
expression::BindingIdentifier,
function::{FormalParameters, FunctionBody}, function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
AllowYield, Cursor, ParseError, TokenParser, AllowYield, Cursor, ParseError, TokenParser,
}, },
}; };

2
boa_engine/src/syntax/parser/expression/primary/async_generator_expression/mod.rs

@ -14,8 +14,8 @@ use crate::syntax::{
ast::{node::AsyncGeneratorExpr, Keyword, Position, Punctuator}, ast::{node::AsyncGeneratorExpr, Keyword, Position, Punctuator},
lexer::{Error as LexError, TokenKind}, lexer::{Error as LexError, TokenKind},
parser::{ parser::{
expression::BindingIdentifier,
function::{FormalParameters, FunctionBody}, function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
Cursor, ParseError, TokenParser, Cursor, ParseError, TokenParser,
}, },
}; };

4
boa_engine/src/syntax/parser/expression/primary/class_expression/mod.rs

@ -2,8 +2,8 @@ use crate::syntax::{
ast::{Keyword, Node}, ast::{Keyword, Node},
lexer::TokenKind, lexer::TokenKind,
parser::{ parser::{
statement::{BindingIdentifier, ClassTail}, expression::BindingIdentifier, statement::ClassTail, AllowAwait, AllowYield, Cursor,
AllowAwait, AllowYield, Cursor, ParseError, TokenParser, ParseError, TokenParser,
}, },
}; };
use boa_interner::{Interner, Sym}; use boa_interner::{Interner, Sym};

2
boa_engine/src/syntax/parser/expression/primary/function_expression/mod.rs

@ -14,8 +14,8 @@ use crate::syntax::{
ast::{node::FunctionExpr, Keyword, Position, Punctuator}, ast::{node::FunctionExpr, Keyword, Position, Punctuator},
lexer::{Error as LexError, TokenKind}, lexer::{Error as LexError, TokenKind},
parser::{ parser::{
expression::BindingIdentifier,
function::{FormalParameters, FunctionBody}, function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
Cursor, ParseError, TokenParser, Cursor, ParseError, TokenParser,
}, },
}; };

2
boa_engine/src/syntax/parser/expression/primary/generator_expression/mod.rs

@ -14,8 +14,8 @@ use crate::syntax::{
ast::{node::GeneratorExpr, Position, Punctuator}, ast::{node::GeneratorExpr, Position, Punctuator},
lexer::{Error as LexError, TokenKind}, lexer::{Error as LexError, TokenKind},
parser::{ parser::{
expression::BindingIdentifier,
function::{FormalParameters, FunctionBody}, function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
Cursor, ParseError, TokenParser, Cursor, ParseError, TokenParser,
}, },
}; };

112
boa_engine/src/syntax/parser/expression/primary/mod.rs

@ -33,7 +33,9 @@ use crate::syntax::{
}, },
lexer::{token::Numeric, InputElement, TokenKind}, lexer::{token::Numeric, InputElement, TokenKind},
parser::{ parser::{
expression::{primary::template::TemplateLiteral, Expression}, expression::{
identifiers::IdentifierReference, primary::template::TemplateLiteral, Expression,
},
AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
}, },
}; };
@ -85,15 +87,19 @@ where
// TODO: tok currently consumes the token instead of peeking, so the token // TODO: tok currently consumes the token instead of peeking, so the token
// isn't passed and consumed by parsers according to spec (EX: GeneratorExpression) // isn't passed and consumed by parsers according to spec (EX: GeneratorExpression)
let tok = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; let tok = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
match tok.kind() { match tok.kind() {
TokenKind::Keyword((Keyword::This | Keyword::Async, true)) => Err(ParseError::general( TokenKind::Keyword((Keyword::This | Keyword::Async, true)) => Err(ParseError::general(
"Keyword must not contain escaped characters", "Keyword must not contain escaped characters",
tok.span().start(), tok.span().start(),
)), )),
TokenKind::Keyword((Keyword::This, false)) => Ok(Node::This), TokenKind::Keyword((Keyword::This, false)) => {
cursor.next(interner).expect("token disappeared");
Ok(Node::This)
}
TokenKind::Keyword((Keyword::Function, _)) => { TokenKind::Keyword((Keyword::Function, _)) => {
cursor.next(interner).expect("token disappeared");
let next_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; let next_token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
if next_token.kind() == &TokenKind::Punctuator(Punctuator::Mul) { if next_token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {
GeneratorExpression::new(self.name) GeneratorExpression::new(self.name)
@ -106,9 +112,11 @@ where
} }
} }
TokenKind::Keyword((Keyword::Class, _)) => { TokenKind::Keyword((Keyword::Class, _)) => {
cursor.next(interner).expect("token disappeared");
ClassExpression::new(self.name, false, false).parse(cursor, interner) ClassExpression::new(self.name, false, false).parse(cursor, interner)
} }
TokenKind::Keyword((Keyword::Async, false)) => { TokenKind::Keyword((Keyword::Async, false)) => {
cursor.next(interner).expect("token disappeared");
let mul_peek = cursor.peek(1, interner)?.ok_or(ParseError::AbruptEnd)?; let mul_peek = cursor.peek(1, interner)?.ok_or(ParseError::AbruptEnd)?;
if mul_peek.kind() == &TokenKind::Punctuator(Punctuator::Mul) { if mul_peek.kind() == &TokenKind::Punctuator(Punctuator::Mul) {
AsyncGeneratorExpression::new(self.name) AsyncGeneratorExpression::new(self.name)
@ -121,6 +129,7 @@ where
} }
} }
TokenKind::Punctuator(Punctuator::OpenParen) => { TokenKind::Punctuator(Punctuator::OpenParen) => {
cursor.next(interner).expect("token disappeared");
cursor.set_goal(InputElement::RegExp); cursor.set_goal(InputElement::RegExp);
let expr = Expression::new(self.name, true, self.allow_yield, self.allow_await) let expr = Expression::new(self.name, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?; .parse(cursor, interner)?;
@ -128,71 +137,76 @@ where
Ok(expr) Ok(expr)
} }
TokenKind::Punctuator(Punctuator::OpenBracket) => { TokenKind::Punctuator(Punctuator::OpenBracket) => {
cursor.next(interner).expect("token disappeared");
cursor.set_goal(InputElement::RegExp); cursor.set_goal(InputElement::RegExp);
ArrayLiteral::new(self.allow_yield, self.allow_await) ArrayLiteral::new(self.allow_yield, self.allow_await)
.parse(cursor, interner) .parse(cursor, interner)
.map(Node::ArrayDecl) .map(Node::ArrayDecl)
} }
TokenKind::Punctuator(Punctuator::OpenBlock) => { TokenKind::Punctuator(Punctuator::OpenBlock) => {
cursor.next(interner).expect("token disappeared");
cursor.set_goal(InputElement::RegExp); cursor.set_goal(InputElement::RegExp);
Ok(ObjectLiteral::new(self.allow_yield, self.allow_await) Ok(ObjectLiteral::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)? .parse(cursor, interner)?
.into()) .into())
} }
TokenKind::BooleanLiteral(boolean) => Ok(Const::from(*boolean).into()), TokenKind::BooleanLiteral(boolean) => {
TokenKind::NullLiteral => Ok(Const::Null.into()), let node = Const::from(*boolean).into();
TokenKind::Identifier(ident) => Ok(Identifier::new(*ident).into()), cursor.next(interner).expect("token disappeared");
TokenKind::Keyword((Keyword::Let, _)) => Ok(Identifier::new(Sym::LET).into()), Ok(node)
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",
tok.span().start(),
))
}
TokenKind::Keyword((Keyword::Yield, _)) if !self.allow_yield.0 => {
if cursor.strict_mode() {
return Err(ParseError::general(
"Unexpected strict mode reserved word",
tok.span().start(),
));
} }
Ok(Identifier::new(Sym::YIELD).into()) TokenKind::NullLiteral => {
cursor.next(interner).expect("token disappeared");
Ok(Const::Null.into())
} }
TokenKind::Keyword((Keyword::Await, _)) if self.allow_await.0 => { TokenKind::Identifier(_)
// Early Error: It is a Syntax Error if this production has an [Await] parameter and StringValue of Identifier is "await". | TokenKind::Keyword((Keyword::Let | Keyword::Yield | Keyword::Await, _)) => {
Err(ParseError::general( IdentifierReference::new(self.allow_yield, self.allow_await)
"Unexpected identifier", .parse(cursor, interner)
tok.span().start(), .map(Node::from)
))
}
TokenKind::Keyword((Keyword::Await, _)) if !self.allow_await.0 => {
if cursor.strict_mode() {
return Err(ParseError::general(
"Unexpected strict mode reserved word",
tok.span().start(),
));
} }
Ok(Identifier::new(Sym::AWAIT).into()) TokenKind::StringLiteral(lit) => {
let node = Const::from(*lit).into();
cursor.next(interner).expect("token disappeared");
Ok(node)
} }
TokenKind::StringLiteral(lit) => Ok(Const::from(*lit).into()), TokenKind::TemplateNoSubstitution(template_string) => {
TokenKind::TemplateNoSubstitution(template_string) => Ok(Const::from( let node = Const::from(
template_string template_string
.to_owned_cooked(interner) .to_owned_cooked(interner)
.map_err(ParseError::lex)?, .map_err(ParseError::lex)?,
) )
.into()), .into();
TokenKind::NumericLiteral(Numeric::Integer(num)) => Ok(Const::from(*num).into()), cursor.next(interner).expect("token disappeared");
TokenKind::NumericLiteral(Numeric::Rational(num)) => Ok(Const::from(*num).into()), Ok(node)
TokenKind::NumericLiteral(Numeric::BigInt(num)) => Ok(Const::from(num.clone()).into()), }
TokenKind::NumericLiteral(Numeric::Integer(num)) => {
let node = Const::from(*num).into();
cursor.next(interner).expect("token disappeared");
Ok(node)
}
TokenKind::NumericLiteral(Numeric::Rational(num)) => {
let node = Const::from(*num).into();
cursor.next(interner).expect("token disappeared");
Ok(node)
}
TokenKind::NumericLiteral(Numeric::BigInt(num)) => {
let node = Const::from(num.clone()).into();
cursor.next(interner).expect("token disappeared");
Ok(node)
}
TokenKind::RegularExpressionLiteral(body, flags) => { TokenKind::RegularExpressionLiteral(body, flags) => {
Ok(Node::from(New::from(Call::new( let node = Node::from(New::from(Call::new(
Identifier::new(Sym::REGEXP), Identifier::new(Sym::REGEXP),
vec![Const::from(*body).into(), Const::from(*flags).into()], vec![Const::from(*body).into(), Const::from(*flags).into()],
)))) )));
cursor.next(interner).expect("token disappeared");
Ok(node)
} }
TokenKind::Punctuator(Punctuator::Div) => { TokenKind::Punctuator(Punctuator::Div) => {
let tok = cursor.lex_regex(tok.span().start(), interner)?; let position = tok.span().start();
cursor.next(interner).expect("token disappeared");
let tok = cursor.lex_regex(position, interner)?;
if let TokenKind::RegularExpressionLiteral(body, flags) = *tok.kind() { if let TokenKind::RegularExpressionLiteral(body, flags) = *tok.kind() {
Ok(Node::from(New::from(Call::new( Ok(Node::from(New::from(Call::new(
@ -208,16 +222,18 @@ where
)) ))
} }
} }
TokenKind::TemplateMiddle(template_string) => TemplateLiteral::new( TokenKind::TemplateMiddle(template_string) => {
let parser = TemplateLiteral::new(
self.allow_yield, self.allow_yield,
self.allow_await, self.allow_await,
tok.span().start(), tok.span().start(),
template_string template_string
.to_owned_cooked(interner) .to_owned_cooked(interner)
.map_err(ParseError::lex)?, .map_err(ParseError::lex)?,
) );
.parse(cursor, interner) cursor.next(interner).expect("token disappeared");
.map(Node::TemplateLit), parser.parse(cursor, interner).map(Node::TemplateLit)
}
_ => Err(ParseError::unexpected( _ => Err(ParseError::unexpected(
tok.to_string(interner), tok.to_string(interner),
tok.span(), tok.span(),

63
boa_engine/src/syntax/parser/expression/primary/object_initializer/mod.rs

@ -15,13 +15,13 @@ use crate::syntax::{
node::{ node::{
object::{self, MethodDefinition}, object::{self, MethodDefinition},
AsyncFunctionExpr, AsyncGeneratorExpr, FormalParameterList, FunctionExpr, AsyncFunctionExpr, AsyncGeneratorExpr, FormalParameterList, FunctionExpr,
GeneratorExpr, Identifier, Node, Object, GeneratorExpr, Node, Object,
}, },
Const, Keyword, Punctuator, Const, Keyword, Punctuator,
}, },
lexer::{token::Numeric, Error as LexError, TokenKind}, lexer::{token::Numeric, Error as LexError, TokenKind},
parser::{ parser::{
expression::AssignmentExpression, expression::{identifiers::IdentifierReference, AssignmentExpression},
function::{FormalParameter, FormalParameters, FunctionBody, UniqueFormalParameters}, function::{FormalParameter, FormalParameters, FunctionBody, UniqueFormalParameters},
AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
}, },
@ -146,63 +146,8 @@ where
next_token.kind(), next_token.kind(),
TokenKind::Punctuator(Punctuator::CloseBlock | Punctuator::Comma) TokenKind::Punctuator(Punctuator::CloseBlock | Punctuator::Comma)
) { ) {
let token = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?; let ident = IdentifierReference::new(self.allow_yield, self.allow_await)
let ident = match token.kind() { .parse(cursor, interner)?;
TokenKind::Identifier(Sym::ARGUMENTS) if cursor.strict_mode() => {
return Err(ParseError::lex(LexError::Syntax(
"unexpected identifier 'arguments' in strict mode".into(),
token.span().start(),
)));
}
TokenKind::Identifier(Sym::EVAL) if cursor.strict_mode() => {
return Err(ParseError::lex(LexError::Syntax(
"unexpected identifier 'eval' in strict mode".into(),
token.span().start(),
)));
}
TokenKind::Identifier(ident) => Identifier::new(*ident),
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".
return Err(ParseError::general(
"Unexpected identifier",
token.span().start(),
));
}
TokenKind::Keyword((Keyword::Yield, _)) if !self.allow_yield.0 => {
if cursor.strict_mode() {
// Early Error: It is a Syntax Error if the code matched by this production is contained in strict mode code.
return Err(ParseError::general(
"Unexpected strict mode reserved word",
token.span().start(),
));
}
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".
return Err(ParseError::general(
"Unexpected identifier",
token.span().start(),
));
}
TokenKind::Keyword((Keyword::Await, _)) if !self.allow_await.0 => {
if cursor.strict_mode() {
// Early Error: It is a Syntax Error if the code matched by this production is contained in strict mode code.
return Err(ParseError::general(
"Unexpected strict mode reserved word",
token.span().start(),
));
}
Identifier::new(Sym::YIELD)
}
_ => {
return Err(ParseError::unexpected(
token.to_string(interner),
token.span(),
"expected IdentifierReference",
));
}
};
return Ok(object::PropertyDefinition::property(ident.sym(), ident)); return Ok(object::PropertyDefinition::property(ident.sym(), ident));
} }
} }

4
boa_engine/src/syntax/parser/function/mod.rs

@ -18,8 +18,8 @@ use crate::syntax::{
}, },
lexer::{Error as LexError, InputElement, TokenKind}, lexer::{Error as LexError, InputElement, TokenKind},
parser::{ parser::{
expression::Initializer, expression::{BindingIdentifier, Initializer},
statement::{ArrayBindingPattern, BindingIdentifier, ObjectBindingPattern, StatementList}, statement::{ArrayBindingPattern, ObjectBindingPattern, StatementList},
AllowAwait, AllowYield, Cursor, ParseError, TokenParser, AllowAwait, AllowYield, Cursor, ParseError, TokenParser,
}, },
}; };

18
boa_engine/src/syntax/parser/mod.rs

@ -1,25 +1,29 @@
//! Boa parser implementation. //! Boa parser implementation.
mod cursor; mod cursor;
pub mod error;
mod expression; mod expression;
pub(crate) mod function;
mod statement; mod statement;
pub(crate) mod function;
pub mod error;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
pub use self::error::{ParseError, ParseResult};
use self::cursor::Cursor;
use crate::{ use crate::{
syntax::{ast::node::StatementList, lexer::TokenKind}, syntax::{
ast::{node::StatementList, Position},
lexer::TokenKind,
parser::cursor::Cursor,
},
Context, Context,
}; };
use boa_interner::{Interner, Sym}; use boa_interner::{Interner, Sym};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use std::io::Read; use std::io::Read;
use super::ast::Position; pub use self::error::{ParseError, ParseResult};
/// Trait implemented by parsers. /// Trait implemented by parsers.
/// ///

2
boa_engine/src/syntax/parser/statement/break_stm/mod.rs

@ -10,12 +10,12 @@
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use super::LabelIdentifier;
use crate::syntax::{ use crate::syntax::{
ast::{node::Break, Keyword, Punctuator}, ast::{node::Break, Keyword, Punctuator},
lexer::TokenKind, lexer::TokenKind,
parser::{ parser::{
cursor::{Cursor, SemicolonResult}, cursor::{Cursor, SemicolonResult},
expression::LabelIdentifier,
AllowAwait, AllowYield, ParseError, TokenParser, AllowAwait, AllowYield, ParseError, TokenParser,
}, },
}; };

2
boa_engine/src/syntax/parser/statement/continue_stm/mod.rs

@ -15,7 +15,7 @@ use crate::syntax::{
lexer::TokenKind, lexer::TokenKind,
parser::{ parser::{
cursor::{Cursor, SemicolonResult}, cursor::{Cursor, SemicolonResult},
statement::LabelIdentifier, expression::LabelIdentifier,
AllowAwait, AllowYield, ParseError, TokenParser, AllowAwait, AllowYield, ParseError, TokenParser,
}, },
}; };

6
boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs

@ -11,14 +11,14 @@ use crate::syntax::{
lexer::{Error as LexError, TokenKind}, lexer::{Error as LexError, TokenKind},
parser::{ parser::{
expression::{ expression::{
AssignmentExpression, AsyncGeneratorMethod, AsyncMethod, GeneratorMethod, AssignmentExpression, AsyncGeneratorMethod, AsyncMethod, BindingIdentifier,
LeftHandSideExpression, PropertyName, GeneratorMethod, LeftHandSideExpression, PropertyName,
}, },
function::{ function::{
FormalParameter, FormalParameters, FunctionBody, UniqueFormalParameters, FormalParameter, FormalParameters, FunctionBody, UniqueFormalParameters,
FUNCTION_BREAK_TOKENS, FUNCTION_BREAK_TOKENS,
}, },
statement::{BindingIdentifier, StatementList}, statement::StatementList,
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser, AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser,
}, },
}; };

3
boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs

@ -24,8 +24,9 @@ use crate::syntax::{
ast::{Keyword, Node, Position, Punctuator}, ast::{Keyword, Node, Position, Punctuator},
lexer::TokenKind, lexer::TokenKind,
parser::{ parser::{
expression::BindingIdentifier,
function::{FormalParameters, FunctionBody}, function::{FormalParameters, FunctionBody},
statement::{BindingIdentifier, LexError}, statement::LexError,
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser, AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
}, },
}; };

11
boa_engine/src/syntax/parser/statement/iteration/for_statement.rs

@ -118,7 +118,8 @@ where
} }
(Some(init), TokenKind::Keyword((Keyword::In, false))) => { (Some(init), TokenKind::Keyword((Keyword::In, false))) => {
let init_position = token.span().start(); let init_position = token.span().start();
let init = node_to_iterable_loop_initializer(init, init_position)?; let init =
node_to_iterable_loop_initializer(init, init_position, cursor.strict_mode())?;
let _next = cursor.next(interner)?; let _next = cursor.next(interner)?;
let expr = Expression::new(None, true, self.allow_yield, self.allow_await) let expr = Expression::new(None, true, self.allow_yield, self.allow_await)
@ -167,7 +168,8 @@ where
return Ok(ForInLoop::new(init, expr, body).into()); return Ok(ForInLoop::new(init, expr, body).into());
} }
(Some(init), TokenKind::Keyword((Keyword::Of, false))) => { (Some(init), TokenKind::Keyword((Keyword::Of, false))) => {
let init = node_to_iterable_loop_initializer(init, init_position)?; let init =
node_to_iterable_loop_initializer(init, init_position, cursor.strict_mode())?;
let _next = cursor.next(interner)?; let _next = cursor.next(interner)?;
let iterable = Expression::new(None, true, self.allow_yield, self.allow_await) let iterable = Expression::new(None, true, self.allow_yield, self.allow_await)
@ -277,6 +279,7 @@ where
fn node_to_iterable_loop_initializer( fn node_to_iterable_loop_initializer(
node: &Node, node: &Node,
position: Position, position: Position,
strict: bool,
) -> Result<IterableLoopInitializer, ParseError> { ) -> Result<IterableLoopInitializer, ParseError> {
match node { match node {
Node::Identifier(name) => Ok(IterableLoopInitializer::Identifier(*name)), Node::Identifier(name) => Ok(IterableLoopInitializer::Identifier(*name)),
@ -333,7 +336,7 @@ fn node_to_iterable_loop_initializer(
position, position,
))), ))),
Node::Object(object) => { Node::Object(object) => {
if let Some(pattern) = object_decl_to_declaration_pattern(object) { if let Some(pattern) = object_decl_to_declaration_pattern(object, strict) {
Ok(IterableLoopInitializer::DeclarationPattern(pattern)) Ok(IterableLoopInitializer::DeclarationPattern(pattern))
} else { } else {
Err(ParseError::lex(LexError::Syntax( Err(ParseError::lex(LexError::Syntax(
@ -343,7 +346,7 @@ fn node_to_iterable_loop_initializer(
} }
} }
Node::ArrayDecl(array) => { Node::ArrayDecl(array) => {
if let Some(pattern) = array_decl_to_declaration_pattern(array) { if let Some(pattern) = array_decl_to_declaration_pattern(array, strict) {
Ok(IterableLoopInitializer::DeclarationPattern(pattern)) Ok(IterableLoopInitializer::DeclarationPattern(pattern))
} else { } else {
Err(ParseError::lex(LexError::Syntax( Err(ParseError::lex(LexError::Syntax(

4
boa_engine/src/syntax/parser/statement/labelled_stm/mod.rs

@ -4,9 +4,9 @@ use crate::syntax::{
parser::{ parser::{
cursor::Cursor, cursor::Cursor,
error::ParseError, error::ParseError,
expression::LabelIdentifier,
statement::{ statement::{
declaration::hoistable::FunctionDeclaration, AllowAwait, AllowReturn, LabelIdentifier, declaration::hoistable::FunctionDeclaration, AllowAwait, AllowReturn, Statement,
Statement,
}, },
AllowYield, TokenParser, AllowYield, TokenParser,
}, },

122
boa_engine/src/syntax/parser/statement/mod.rs

@ -52,9 +52,9 @@ use crate::syntax::{
Keyword, Node, Punctuator, Keyword, Node, Punctuator,
}, },
lexer::{Error as LexError, InputElement, Token, TokenKind}, lexer::{Error as LexError, InputElement, Token, TokenKind},
parser::expression::{await_expr::AwaitExpression, Initializer}, parser::expression::{await_expr::AwaitExpression, BindingIdentifier, Initializer},
}; };
use boa_interner::{Interner, Sym}; use boa_interner::Interner;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use std::{io::Read, vec}; use std::{io::Read, vec};
@ -387,124 +387,6 @@ where
} }
} }
/// 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(super) type LabelIdentifier = BindingIdentifier;
/// Binding identifier parsing.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-BindingIdentifier
#[derive(Debug, Clone, Copy)]
pub(super) struct BindingIdentifier {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl BindingIdentifier {
/// Creates a new `BindingIdentifier` parser.
pub(super) 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::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::Identifier(ref s) => Ok(*s),
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 => {
if cursor.strict_mode() {
Err(ParseError::general(
"await keyword in binding identifier not allowed in strict mode",
next_token.span().start(),
))
} else {
Ok(Sym::AWAIT)
}
}
_ => Err(ParseError::expected(
["identifier".to_owned()],
next_token.to_string(interner),
next_token.span(),
"binding identifier",
)),
}
}
}
/// `ObjectBindingPattern` pattern parsing. /// `ObjectBindingPattern` pattern parsing.
/// ///
/// More information: /// More information:

Loading…
Cancel
Save