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
2 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