mirror of https://github.com/boa-dev/boa.git
Browse Source
This Pull Request fixes/closes #337. It changes the following: - Implement class declaration parsing. - Implement class expression parsing. - Implement class execution. There are still some features like `super` missing and there are some early errors that are not implemented yet. But I think it makes sense to merge this, as we can branch out the missing features from here.pull/2014/head
raskad
3 years ago
45 changed files with 3752 additions and 373 deletions
@ -0,0 +1,367 @@
|
||||
#[cfg(test)] |
||||
mod tests; |
||||
|
||||
use crate::syntax::ast::node::{ |
||||
declaration::{block_to_string, FunctionExpr}, |
||||
join_nodes, |
||||
object::{MethodDefinition, PropertyName}, |
||||
Node, StatementList, |
||||
}; |
||||
use boa_gc::{Finalize, Trace}; |
||||
use boa_interner::{Interner, Sym, ToInternedString}; |
||||
|
||||
#[cfg(feature = "deser")] |
||||
use serde::{Deserialize, Serialize}; |
||||
|
||||
/// The `class` declaration defines a class with the specified methods, fields, and optional constructor.
|
||||
///
|
||||
/// Classes can be used to create objects, which can also be created through literals (using `{}`).
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-class-definitions
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
|
||||
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] |
||||
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] |
||||
pub struct Class { |
||||
name: Sym, |
||||
super_ref: Option<Box<Node>>, |
||||
constructor: Option<FunctionExpr>, |
||||
elements: Box<[ClassElement]>, |
||||
} |
||||
|
||||
impl Class { |
||||
/// Creates a new class declaration.
|
||||
pub(in crate::syntax) fn new<S, C, E>( |
||||
name: Sym, |
||||
super_ref: S, |
||||
constructor: C, |
||||
elements: E, |
||||
) -> Self |
||||
where |
||||
S: Into<Option<Box<Node>>>, |
||||
C: Into<Option<FunctionExpr>>, |
||||
E: Into<Box<[ClassElement]>>, |
||||
{ |
||||
Self { |
||||
name, |
||||
super_ref: super_ref.into(), |
||||
constructor: constructor.into(), |
||||
elements: elements.into(), |
||||
} |
||||
} |
||||
|
||||
/// Returns the name of the class.
|
||||
pub(crate) fn name(&self) -> Sym { |
||||
self.name |
||||
} |
||||
|
||||
/// Returns the super class ref of the class.
|
||||
pub(crate) fn super_ref(&self) -> &Option<Box<Node>> { |
||||
&self.super_ref |
||||
} |
||||
|
||||
/// Returns the constructor of the class.
|
||||
pub(crate) fn constructor(&self) -> &Option<FunctionExpr> { |
||||
&self.constructor |
||||
} |
||||
|
||||
/// Gets the list of all fields defined on the class.
|
||||
pub(crate) fn elements(&self) -> &[ClassElement] { |
||||
&self.elements |
||||
} |
||||
|
||||
/// Implements the display formatting with indentation.
|
||||
pub(in crate::syntax::ast::node) fn to_indented_string( |
||||
&self, |
||||
interner: &Interner, |
||||
indent_n: usize, |
||||
) -> String { |
||||
if self.elements.is_empty() && self.constructor().is_none() { |
||||
return format!( |
||||
"class {}{} {{}}", |
||||
interner.resolve_expect(self.name), |
||||
if let Some(node) = &self.super_ref { |
||||
format!(" extends {}", node.to_interned_string(interner)) |
||||
} else { |
||||
"".to_string() |
||||
} |
||||
); |
||||
} |
||||
let indentation = " ".repeat(indent_n + 1); |
||||
let mut buf = format!( |
||||
"class {}{} {{\n", |
||||
interner.resolve_expect(self.name), |
||||
if let Some(node) = &self.super_ref { |
||||
format!("extends {}", node.to_interned_string(interner)) |
||||
} else { |
||||
"".to_string() |
||||
} |
||||
); |
||||
if let Some(expr) = &self.constructor { |
||||
buf.push_str(&format!( |
||||
"{indentation}constructor({}) {}\n", |
||||
join_nodes(interner, &expr.parameters().parameters), |
||||
block_to_string(expr.body(), interner, indent_n + 1) |
||||
)); |
||||
} |
||||
for element in self.elements.iter() { |
||||
buf.push_str(&match element { |
||||
ClassElement::MethodDefinition(name, method) => { |
||||
format!( |
||||
"{indentation}{}{}({}) {}\n", |
||||
match &method { |
||||
MethodDefinition::Get(_) => "get ", |
||||
MethodDefinition::Set(_) => "set ", |
||||
_ => "", |
||||
}, |
||||
name.to_interned_string(interner), |
||||
match &method { |
||||
MethodDefinition::Get(node) |
||||
| MethodDefinition::Set(node) |
||||
| MethodDefinition::Ordinary(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
MethodDefinition::Generator(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
MethodDefinition::AsyncGenerator(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
MethodDefinition::Async(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
}, |
||||
match &method { |
||||
MethodDefinition::Get(node) |
||||
| MethodDefinition::Set(node) |
||||
| MethodDefinition::Ordinary(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
MethodDefinition::Generator(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
MethodDefinition::AsyncGenerator(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
MethodDefinition::Async(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
}, |
||||
) |
||||
} |
||||
ClassElement::StaticMethodDefinition(name, method) => { |
||||
format!( |
||||
"{indentation}static {}{}({}) {}\n", |
||||
match &method { |
||||
MethodDefinition::Get(_) => "get ", |
||||
MethodDefinition::Set(_) => "set ", |
||||
_ => "", |
||||
}, |
||||
name.to_interned_string(interner), |
||||
match &method { |
||||
MethodDefinition::Get(node) |
||||
| MethodDefinition::Set(node) |
||||
| MethodDefinition::Ordinary(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
MethodDefinition::Generator(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
MethodDefinition::AsyncGenerator(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
MethodDefinition::Async(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
}, |
||||
match &method { |
||||
MethodDefinition::Get(node) |
||||
| MethodDefinition::Set(node) |
||||
| MethodDefinition::Ordinary(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
MethodDefinition::Generator(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
MethodDefinition::AsyncGenerator(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
MethodDefinition::Async(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
}, |
||||
) |
||||
} |
||||
ClassElement::FieldDefinition(name, field) => match field { |
||||
Some(node) => { |
||||
format!( |
||||
"{indentation}{} = {};\n", |
||||
name.to_interned_string(interner), |
||||
node.to_no_indent_string(interner, indent_n + 1) |
||||
) |
||||
} |
||||
None => { |
||||
format!("{indentation}{};\n", name.to_interned_string(interner),) |
||||
} |
||||
}, |
||||
ClassElement::StaticFieldDefinition(name, field) => match field { |
||||
Some(node) => { |
||||
format!( |
||||
"{indentation}static {} = {};\n", |
||||
name.to_interned_string(interner), |
||||
node.to_no_indent_string(interner, indent_n + 1) |
||||
) |
||||
} |
||||
None => { |
||||
format!( |
||||
"{indentation}static {};\n", |
||||
name.to_interned_string(interner), |
||||
) |
||||
} |
||||
}, |
||||
ClassElement::PrivateMethodDefinition(name, method) => { |
||||
format!( |
||||
"{indentation}{}#{}({}) {}\n", |
||||
match &method { |
||||
MethodDefinition::Get(_) => "get ", |
||||
MethodDefinition::Set(_) => "set ", |
||||
_ => "", |
||||
}, |
||||
interner.resolve_expect(*name), |
||||
match &method { |
||||
MethodDefinition::Get(node) |
||||
| MethodDefinition::Set(node) |
||||
| MethodDefinition::Ordinary(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
MethodDefinition::Generator(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
MethodDefinition::AsyncGenerator(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
MethodDefinition::Async(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
}, |
||||
match &method { |
||||
MethodDefinition::Get(node) |
||||
| MethodDefinition::Set(node) |
||||
| MethodDefinition::Ordinary(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
MethodDefinition::Generator(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
MethodDefinition::AsyncGenerator(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
MethodDefinition::Async(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
}, |
||||
) |
||||
} |
||||
ClassElement::PrivateStaticMethodDefinition(name, method) => { |
||||
format!( |
||||
"{indentation}static {}#{}({}) {}\n", |
||||
match &method { |
||||
MethodDefinition::Get(_) => "get ", |
||||
MethodDefinition::Set(_) => "set ", |
||||
_ => "", |
||||
}, |
||||
interner.resolve_expect(*name), |
||||
match &method { |
||||
MethodDefinition::Get(node) |
||||
| MethodDefinition::Set(node) |
||||
| MethodDefinition::Ordinary(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
MethodDefinition::Generator(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
MethodDefinition::AsyncGenerator(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
MethodDefinition::Async(node) => { |
||||
join_nodes(interner, &node.parameters().parameters) |
||||
} |
||||
}, |
||||
match &method { |
||||
MethodDefinition::Get(node) |
||||
| MethodDefinition::Set(node) |
||||
| MethodDefinition::Ordinary(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
MethodDefinition::Generator(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
MethodDefinition::AsyncGenerator(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
MethodDefinition::Async(node) => { |
||||
block_to_string(node.body(), interner, indent_n + 1) |
||||
} |
||||
}, |
||||
) |
||||
} |
||||
ClassElement::PrivateFieldDefinition(name, field) => match field { |
||||
Some(node) => { |
||||
format!( |
||||
"{indentation}#{} = {};\n", |
||||
interner.resolve_expect(*name), |
||||
node.to_no_indent_string(interner, indent_n + 1) |
||||
) |
||||
} |
||||
None => { |
||||
format!("{indentation}#{};\n", interner.resolve_expect(*name),) |
||||
} |
||||
}, |
||||
ClassElement::PrivateStaticFieldDefinition(name, field) => match field { |
||||
Some(node) => { |
||||
format!( |
||||
"{indentation}static #{} = {};\n", |
||||
interner.resolve_expect(*name), |
||||
node.to_no_indent_string(interner, indent_n + 1) |
||||
) |
||||
} |
||||
None => { |
||||
format!("{indentation}static #{};\n", interner.resolve_expect(*name),) |
||||
} |
||||
}, |
||||
ClassElement::StaticBlock(statement_list) => { |
||||
format!( |
||||
"{indentation}static {}\n", |
||||
block_to_string(statement_list, interner, indent_n + 1) |
||||
) |
||||
} |
||||
}); |
||||
} |
||||
buf.push('}'); |
||||
buf |
||||
} |
||||
} |
||||
|
||||
impl ToInternedString for Class { |
||||
fn to_interned_string(&self, interner: &Interner) -> String { |
||||
self.to_indented_string(interner, 0) |
||||
} |
||||
} |
||||
|
||||
/// Class element types.
|
||||
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] |
||||
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] |
||||
pub enum ClassElement { |
||||
MethodDefinition(PropertyName, MethodDefinition), |
||||
StaticMethodDefinition(PropertyName, MethodDefinition), |
||||
FieldDefinition(PropertyName, Option<Node>), |
||||
StaticFieldDefinition(PropertyName, Option<Node>), |
||||
PrivateMethodDefinition(Sym, MethodDefinition), |
||||
PrivateStaticMethodDefinition(Sym, MethodDefinition), |
||||
PrivateFieldDefinition(Sym, Option<Node>), |
||||
PrivateStaticFieldDefinition(Sym, Option<Node>), |
||||
StaticBlock(StatementList), |
||||
} |
@ -0,0 +1,104 @@
|
||||
use crate::syntax::ast::node::test_formatting; |
||||
|
||||
#[test] |
||||
fn class_declaration_empty() { |
||||
test_formatting( |
||||
r#" |
||||
class A {}; |
||||
"#, |
||||
); |
||||
} |
||||
|
||||
#[test] |
||||
fn class_declaration_empty_extends() { |
||||
test_formatting( |
||||
r#" |
||||
class A extends Object {}; |
||||
"#, |
||||
); |
||||
} |
||||
|
||||
#[test] |
||||
fn class_declaration_constructor() { |
||||
test_formatting( |
||||
r#" |
||||
class A { |
||||
constructor(a, b, c) { |
||||
this.value = a + b + c; |
||||
} |
||||
}; |
||||
"#, |
||||
); |
||||
} |
||||
|
||||
#[test] |
||||
fn class_declaration_elements() { |
||||
test_formatting( |
||||
r#" |
||||
class A { |
||||
a; |
||||
b = 1; |
||||
c() {} |
||||
d(a, b, c) { |
||||
return a + b + c; |
||||
} |
||||
set e(value) {} |
||||
get e() {} |
||||
}; |
||||
"#, |
||||
); |
||||
} |
||||
|
||||
#[test] |
||||
fn class_declaration_elements_private() { |
||||
test_formatting( |
||||
r#" |
||||
class A { |
||||
#a; |
||||
#b = 1; |
||||
#c() {} |
||||
#d(a, b, c) { |
||||
return a + b + c; |
||||
} |
||||
set #e(value) {} |
||||
get #e() {} |
||||
}; |
||||
"#, |
||||
); |
||||
} |
||||
|
||||
#[test] |
||||
fn class_declaration_elements_static() { |
||||
test_formatting( |
||||
r#" |
||||
class A { |
||||
static a; |
||||
static b = 1; |
||||
static c() {} |
||||
static d(a, b, c) { |
||||
return a + b + c; |
||||
} |
||||
static set e(value) {} |
||||
static get e() {} |
||||
}; |
||||
"#, |
||||
); |
||||
} |
||||
|
||||
#[test] |
||||
fn class_declaration_elements_private_static() { |
||||
test_formatting( |
||||
r#" |
||||
class A { |
||||
static #a; |
||||
static #b = 1; |
||||
static #c() {} |
||||
static #d(a, b, c) { |
||||
return a + b + c; |
||||
} |
||||
static set #e(value) {} |
||||
static get #e() {} |
||||
}; |
||||
"#, |
||||
); |
||||
} |
@ -0,0 +1,63 @@
|
||||
use crate::syntax::ast::node::Node; |
||||
use boa_gc::{Finalize, Trace}; |
||||
use boa_interner::{Interner, Sym, ToInternedString}; |
||||
|
||||
#[cfg(feature = "deser")] |
||||
use serde::{Deserialize, Serialize}; |
||||
|
||||
/// This property accessor provides access to an class object's private fields.
|
||||
///
|
||||
/// This expression can be described as ` MemberExpression.PrivateIdentifier`
|
||||
/// Example: `this.#a`
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
/// - [MDN documentation][mdn]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-MemberExpression
|
||||
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields
|
||||
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] |
||||
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] |
||||
pub struct GetPrivateField { |
||||
obj: Box<Node>, |
||||
field: Sym, |
||||
} |
||||
|
||||
impl GetPrivateField { |
||||
/// Creates a `GetPrivateField` AST node.
|
||||
pub fn new<V>(value: V, field: Sym) -> Self |
||||
where |
||||
V: Into<Node>, |
||||
{ |
||||
Self { |
||||
obj: Box::new(value.into()), |
||||
field, |
||||
} |
||||
} |
||||
|
||||
/// Gets the original object from where to get the field from.
|
||||
pub fn obj(&self) -> &Node { |
||||
&self.obj |
||||
} |
||||
|
||||
/// Gets the name of the field to retrieve.
|
||||
pub fn field(&self) -> Sym { |
||||
self.field |
||||
} |
||||
} |
||||
|
||||
impl ToInternedString for GetPrivateField { |
||||
fn to_interned_string(&self, interner: &Interner) -> String { |
||||
format!( |
||||
"{}.#{}", |
||||
self.obj.to_interned_string(interner), |
||||
interner.resolve_expect(self.field) |
||||
) |
||||
} |
||||
} |
||||
|
||||
impl From<GetPrivateField> for Node { |
||||
fn from(get_private_field: GetPrivateField) -> Self { |
||||
Self::GetPrivateField(get_private_field) |
||||
} |
||||
} |
@ -0,0 +1,79 @@
|
||||
//! This module implements lexing for private identifiers (#foo, #myvar, etc.) used in the JavaScript programing language.
|
||||
|
||||
use super::{identifier::Identifier, Cursor, Error, Tokenizer}; |
||||
use crate::syntax::{ |
||||
ast::{Position, Span}, |
||||
lexer::{Token, TokenKind}, |
||||
}; |
||||
use boa_interner::Interner; |
||||
use boa_profiler::Profiler; |
||||
use std::io::Read; |
||||
|
||||
/// Private Identifier lexing.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript reference][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-PrivateIdentifier
|
||||
#[derive(Debug, Clone, Copy)] |
||||
pub(super) struct PrivateIdentifier; |
||||
|
||||
impl PrivateIdentifier { |
||||
/// Creates a new private identifier lexer.
|
||||
pub(super) fn new() -> Self { |
||||
Self |
||||
} |
||||
} |
||||
|
||||
impl<R> Tokenizer<R> for PrivateIdentifier { |
||||
fn lex( |
||||
&mut self, |
||||
cursor: &mut Cursor<R>, |
||||
start_pos: Position, |
||||
interner: &mut Interner, |
||||
) -> Result<Token, Error> |
||||
where |
||||
R: Read, |
||||
{ |
||||
let _timer = Profiler::global().start_event("PrivateIdentifier", "Lexing"); |
||||
|
||||
if let Some(next_ch) = cursor.next_char()? { |
||||
if let Ok(c) = char::try_from(next_ch) { |
||||
match c { |
||||
'\\' if cursor.peek()? == Some(b'u') => { |
||||
let (name, _) = Identifier::take_identifier_name(cursor, start_pos, c)?; |
||||
Ok(Token::new( |
||||
TokenKind::PrivateIdentifier(interner.get_or_intern(&name)), |
||||
Span::new(start_pos, cursor.pos()), |
||||
)) |
||||
} |
||||
_ if Identifier::is_identifier_start(c as u32) => { |
||||
let (name, _) = Identifier::take_identifier_name(cursor, start_pos, c)?; |
||||
Ok(Token::new( |
||||
TokenKind::PrivateIdentifier(interner.get_or_intern(&name)), |
||||
Span::new(start_pos, cursor.pos()), |
||||
)) |
||||
} |
||||
_ => Err(Error::syntax( |
||||
"Abrupt end: Expecting private identifier", |
||||
start_pos, |
||||
)), |
||||
} |
||||
} else { |
||||
Err(Error::syntax( |
||||
format!( |
||||
"unexpected utf-8 char '\\u{next_ch}' at line {}, column {}", |
||||
start_pos.line_number(), |
||||
start_pos.column_number() |
||||
), |
||||
start_pos, |
||||
)) |
||||
} |
||||
} else { |
||||
Err(Error::syntax( |
||||
"Abrupt end: Expecting private identifier", |
||||
start_pos, |
||||
)) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,81 @@
|
||||
use crate::syntax::{ |
||||
ast::{Keyword, Node}, |
||||
lexer::TokenKind, |
||||
parser::{ |
||||
statement::{BindingIdentifier, ClassTail}, |
||||
AllowAwait, AllowYield, Cursor, ParseError, TokenParser, |
||||
}, |
||||
}; |
||||
use boa_interner::{Interner, Sym}; |
||||
use boa_profiler::Profiler; |
||||
use std::io::Read; |
||||
|
||||
/// Class expression parsing.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ClassExpression
|
||||
#[derive(Debug, Clone, Copy)] |
||||
pub(super) struct ClassExpression { |
||||
name: Option<Sym>, |
||||
allow_yield: AllowYield, |
||||
allow_await: AllowAwait, |
||||
} |
||||
|
||||
impl ClassExpression { |
||||
/// Creates a new `ClassExpression` parser.
|
||||
pub(in crate::syntax::parser) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self |
||||
where |
||||
N: Into<Option<Sym>>, |
||||
Y: Into<AllowYield>, |
||||
A: Into<AllowAwait>, |
||||
{ |
||||
Self { |
||||
name: name.into(), |
||||
allow_yield: allow_yield.into(), |
||||
allow_await: allow_await.into(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl<R> TokenParser<R> for ClassExpression |
||||
where |
||||
R: Read, |
||||
{ |
||||
type Output = Node; |
||||
|
||||
fn parse( |
||||
self, |
||||
cursor: &mut Cursor<R>, |
||||
interner: &mut Interner, |
||||
) -> Result<Self::Output, ParseError> { |
||||
let _timer = Profiler::global().start_event("ClassExpression", "Parsing"); |
||||
let strict = cursor.strict_mode(); |
||||
cursor.set_strict_mode(true); |
||||
|
||||
let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?; |
||||
let name = match token.kind() { |
||||
TokenKind::Identifier(_) | TokenKind::Keyword(Keyword::Yield | Keyword::Await) => { |
||||
BindingIdentifier::new(self.allow_yield, self.allow_await) |
||||
.parse(cursor, interner)? |
||||
} |
||||
_ => { |
||||
if let Some(name) = self.name { |
||||
name |
||||
} else { |
||||
return Err(ParseError::unexpected( |
||||
token.to_string(interner), |
||||
token.span(), |
||||
"expected class identifier", |
||||
)); |
||||
} |
||||
} |
||||
}; |
||||
cursor.set_strict_mode(strict); |
||||
|
||||
Ok(Node::ClassExpr( |
||||
ClassTail::new(name, self.allow_yield, self.allow_await).parse(cursor, interner)?, |
||||
)) |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue