mirror of https://github.com/boa-dev/boa.git
Browse Source
I'm creating this draft PR, since I wanted to have some early feedback, and because I though I would have time to finish it last week, but I got caught up with other stuff. Feel free to contribute :) The main thing here is that I have divided `eval()`, `parse()` and similar functions so that they can decide if they are parsing scripts or modules. Let me know your thoughts. Then, I was checking the import & export parsing, and I noticed we are using `TokenKind::Identifier` for `IdentifierName`, so I changed that name. An `Identifier` is an `IdentifierName` that isn't a `ReservedWord`. This means we should probably also adapt all `IdentifierReference`, `BindingIdentifier` and so on parsing. I already created an `Identifier` parser. Something interesting there is that `await` is not a valid `Identifier` if the goal symbol is `Module`, as you can see in the [spec](https://tc39.es/ecma262/#prod-LabelIdentifier), but currently we don't have that information in the `InputElement` enumeration, we only have `Div`, `RegExp` and `TemplateTail`. How could we approach this? Co-authored-by: jedel1043 <jedel0124@gmail.com>pull/2588/head
Iban Eguia Moraza
2 years ago
84 changed files with 2350 additions and 340 deletions
@ -0,0 +1,215 @@
|
||||
//! Export declaration AST nodes.
|
||||
//!
|
||||
//! This module contains `export` declaration AST nodes.
|
||||
//!
|
||||
//! More information:
|
||||
//! - [MDN documentation][mdn]
|
||||
//! - [ECMAScript specification][spec]
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#sec-exports
|
||||
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
|
||||
|
||||
use std::ops::ControlFlow; |
||||
|
||||
use super::{ModuleSpecifier, VarDeclaration}; |
||||
use crate::{ |
||||
function::{AsyncFunction, AsyncGenerator, Class, Function, Generator}, |
||||
try_break, |
||||
visitor::{VisitWith, Visitor, VisitorMut}, |
||||
Declaration, Expression, |
||||
}; |
||||
use boa_interner::Sym; |
||||
|
||||
/// The kind of re-export in an [`ExportDeclaration`].
|
||||
#[derive(Debug, Clone)] |
||||
pub enum ReExportKind { |
||||
/// Namespaced Re-export (`export * as name from "module-name"`).
|
||||
Namespaced { |
||||
/// Reexported name for the imported module.
|
||||
name: Option<Sym>, |
||||
}, |
||||
/// Re-export list (`export { export1, export2 as alias2 } from "module-name"`).
|
||||
Named { |
||||
/// List of the required re-exports of the re-exported module.
|
||||
names: Box<[ExportSpecifier]>, |
||||
}, |
||||
} |
||||
|
||||
impl VisitWith for ReExportKind { |
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: Visitor<'a>, |
||||
{ |
||||
match self { |
||||
Self::Namespaced { name: Some(name) } => visitor.visit_sym(name), |
||||
Self::Namespaced { name: None } => ControlFlow::Continue(()), |
||||
Self::Named { names } => { |
||||
for name in &**names { |
||||
try_break!(visitor.visit_export_specifier(name)); |
||||
} |
||||
ControlFlow::Continue(()) |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: VisitorMut<'a>, |
||||
{ |
||||
match self { |
||||
Self::Namespaced { name: Some(name) } => visitor.visit_sym_mut(name), |
||||
Self::Namespaced { name: None } => ControlFlow::Continue(()), |
||||
Self::Named { names } => { |
||||
for name in &mut **names { |
||||
try_break!(visitor.visit_export_specifier_mut(name)); |
||||
} |
||||
ControlFlow::Continue(()) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// An export declaration AST node.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ExportDeclaration
|
||||
#[derive(Debug, Clone)] |
||||
pub enum ExportDeclaration { |
||||
/// Re-export.
|
||||
ReExport { |
||||
/// The kind of reexport declared.
|
||||
kind: ReExportKind, |
||||
/// Reexported module specifier.
|
||||
specifier: ModuleSpecifier, |
||||
}, |
||||
/// List of exports.
|
||||
List(Box<[ExportSpecifier]>), |
||||
/// Variable statement export.
|
||||
VarStatement(VarDeclaration), |
||||
/// Declaration export.
|
||||
Declaration(Declaration), |
||||
/// Default function export.
|
||||
DefaultFunction(Function), |
||||
/// Default generator export.
|
||||
DefaultGenerator(Generator), |
||||
/// Default async function export.
|
||||
DefaultAsyncFunction(AsyncFunction), |
||||
/// Default async generator export.
|
||||
DefaultAsyncGenerator(AsyncGenerator), |
||||
/// Default class declaration export.
|
||||
DefaultClassDeclaration(Class), |
||||
/// Default assignment expression export.
|
||||
DefaultAssignmentExpression(Expression), |
||||
} |
||||
|
||||
impl VisitWith for ExportDeclaration { |
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: Visitor<'a>, |
||||
{ |
||||
match self { |
||||
Self::ReExport { specifier, kind } => { |
||||
try_break!(visitor.visit_module_specifier(specifier)); |
||||
visitor.visit_re_export_kind(kind) |
||||
} |
||||
Self::List(list) => { |
||||
for item in &**list { |
||||
try_break!(visitor.visit_export_specifier(item)); |
||||
} |
||||
ControlFlow::Continue(()) |
||||
} |
||||
Self::VarStatement(var) => visitor.visit_var_declaration(var), |
||||
Self::Declaration(decl) => visitor.visit_declaration(decl), |
||||
Self::DefaultFunction(f) => visitor.visit_function(f), |
||||
Self::DefaultGenerator(g) => visitor.visit_generator(g), |
||||
Self::DefaultAsyncFunction(af) => visitor.visit_async_function(af), |
||||
Self::DefaultAsyncGenerator(ag) => visitor.visit_async_generator(ag), |
||||
Self::DefaultClassDeclaration(c) => visitor.visit_class(c), |
||||
Self::DefaultAssignmentExpression(expr) => visitor.visit_expression(expr), |
||||
} |
||||
} |
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: VisitorMut<'a>, |
||||
{ |
||||
match self { |
||||
Self::ReExport { specifier, kind } => { |
||||
try_break!(visitor.visit_module_specifier_mut(specifier)); |
||||
visitor.visit_re_export_kind_mut(kind) |
||||
} |
||||
Self::List(list) => { |
||||
for item in &mut **list { |
||||
try_break!(visitor.visit_export_specifier_mut(item)); |
||||
} |
||||
ControlFlow::Continue(()) |
||||
} |
||||
Self::VarStatement(var) => visitor.visit_var_declaration_mut(var), |
||||
Self::Declaration(decl) => visitor.visit_declaration_mut(decl), |
||||
Self::DefaultFunction(f) => visitor.visit_function_mut(f), |
||||
Self::DefaultGenerator(g) => visitor.visit_generator_mut(g), |
||||
Self::DefaultAsyncFunction(af) => visitor.visit_async_function_mut(af), |
||||
Self::DefaultAsyncGenerator(ag) => visitor.visit_async_generator_mut(ag), |
||||
Self::DefaultClassDeclaration(c) => visitor.visit_class_mut(c), |
||||
Self::DefaultAssignmentExpression(expr) => visitor.visit_expression_mut(expr), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Export specifier
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ExportSpecifier
|
||||
#[derive(Debug, Clone, Copy)] |
||||
pub struct ExportSpecifier { |
||||
alias: Sym, |
||||
private_name: Sym, |
||||
} |
||||
|
||||
impl ExportSpecifier { |
||||
/// Creates a new [`ExportSpecifier`].
|
||||
#[inline] |
||||
#[must_use] |
||||
pub const fn new(alias: Sym, private_name: Sym) -> Self { |
||||
Self { |
||||
alias, |
||||
private_name, |
||||
} |
||||
} |
||||
|
||||
/// Gets the original alias.
|
||||
#[inline] |
||||
#[must_use] |
||||
pub const fn alias(self) -> Sym { |
||||
self.alias |
||||
} |
||||
|
||||
/// Gets the private name of the export inside the module.
|
||||
#[inline] |
||||
#[must_use] |
||||
pub const fn private_name(self) -> Sym { |
||||
self.private_name |
||||
} |
||||
} |
||||
|
||||
impl VisitWith for ExportSpecifier { |
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: Visitor<'a>, |
||||
{ |
||||
try_break!(visitor.visit_sym(&self.alias)); |
||||
visitor.visit_sym(&self.private_name) |
||||
} |
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: VisitorMut<'a>, |
||||
{ |
||||
try_break!(visitor.visit_sym_mut(&mut self.alias)); |
||||
visitor.visit_sym_mut(&mut self.private_name) |
||||
} |
||||
} |
@ -0,0 +1,206 @@
|
||||
//! Import declaration AST nodes.
|
||||
//!
|
||||
//! This module contains `import` declaration AST nodes.
|
||||
//!
|
||||
//! More information:
|
||||
//! - [MDN documentation][mdn]
|
||||
//! - [ECMAScript specification][spec]
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#sec-imports
|
||||
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
|
||||
|
||||
use std::ops::ControlFlow; |
||||
|
||||
use crate::{ |
||||
expression::Identifier, |
||||
try_break, |
||||
visitor::{VisitWith, Visitor, VisitorMut}, |
||||
}; |
||||
use boa_interner::Sym; |
||||
|
||||
use super::ModuleSpecifier; |
||||
|
||||
/// The kind of import in an [`ImportDeclaration`].
|
||||
#[derive(Debug, Clone)] |
||||
pub enum ImportKind { |
||||
/// Default (`import defaultName from "module-name"`) or unnamed (`import "module-name").
|
||||
DefaultOrUnnamed, |
||||
/// Namespaced import (`import * as name from "module-name"`).
|
||||
Namespaced { |
||||
/// Binding for the namespace created from the exports of the imported module.
|
||||
binding: Identifier, |
||||
}, |
||||
/// Import list (`import { export1, export2 as alias2 } from "module-name"`).
|
||||
Named { |
||||
/// List of the required exports of the imported module.
|
||||
names: Box<[ImportSpecifier]>, |
||||
}, |
||||
} |
||||
|
||||
impl VisitWith for ImportKind { |
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: Visitor<'a>, |
||||
{ |
||||
match self { |
||||
Self::DefaultOrUnnamed => ControlFlow::Continue(()), |
||||
Self::Namespaced { binding } => visitor.visit_identifier(binding), |
||||
Self::Named { names } => { |
||||
for name in &**names { |
||||
try_break!(visitor.visit_import_specifier(name)); |
||||
} |
||||
ControlFlow::Continue(()) |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: VisitorMut<'a>, |
||||
{ |
||||
match self { |
||||
Self::DefaultOrUnnamed => ControlFlow::Continue(()), |
||||
Self::Namespaced { binding } => visitor.visit_identifier_mut(binding), |
||||
Self::Named { names } => { |
||||
for name in &mut **names { |
||||
try_break!(visitor.visit_import_specifier_mut(name)); |
||||
} |
||||
ControlFlow::Continue(()) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// An import declaration AST node.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ImportDeclaration
|
||||
#[derive(Debug, Clone)] |
||||
pub struct ImportDeclaration { |
||||
/// Binding for the default export of `specifier`.
|
||||
default: Option<Identifier>, |
||||
/// See [`ImportKind`].
|
||||
kind: ImportKind, |
||||
/// Module specifier.
|
||||
specifier: ModuleSpecifier, |
||||
} |
||||
|
||||
impl ImportDeclaration { |
||||
/// Creates a new import declaration.
|
||||
#[inline] |
||||
#[must_use] |
||||
pub const fn new( |
||||
default: Option<Identifier>, |
||||
kind: ImportKind, |
||||
specifier: ModuleSpecifier, |
||||
) -> Self { |
||||
Self { |
||||
default, |
||||
kind, |
||||
specifier, |
||||
} |
||||
} |
||||
|
||||
/// Gets the binding for the default export of the module.
|
||||
#[inline] |
||||
#[must_use] |
||||
pub const fn default(&self) -> Option<Identifier> { |
||||
self.default |
||||
} |
||||
|
||||
/// Gets the module specifier of the import declaration.
|
||||
#[inline] |
||||
#[must_use] |
||||
pub const fn specifier(&self) -> ModuleSpecifier { |
||||
self.specifier |
||||
} |
||||
|
||||
/// Gets the import kind of the import declaration
|
||||
#[inline] |
||||
#[must_use] |
||||
pub const fn kind(&self) -> &ImportKind { |
||||
&self.kind |
||||
} |
||||
} |
||||
|
||||
impl VisitWith for ImportDeclaration { |
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: Visitor<'a>, |
||||
{ |
||||
if let Some(default) = &self.default { |
||||
try_break!(visitor.visit_identifier(default)); |
||||
} |
||||
try_break!(visitor.visit_import_kind(&self.kind)); |
||||
visitor.visit_module_specifier(&self.specifier) |
||||
} |
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: VisitorMut<'a>, |
||||
{ |
||||
if let Some(default) = &mut self.default { |
||||
try_break!(visitor.visit_identifier_mut(default)); |
||||
} |
||||
try_break!(visitor.visit_import_kind_mut(&mut self.kind)); |
||||
visitor.visit_module_specifier_mut(&mut self.specifier) |
||||
} |
||||
} |
||||
|
||||
/// Import specifier
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ImportSpecifier
|
||||
#[derive(Debug, Clone, Copy)] |
||||
pub struct ImportSpecifier { |
||||
binding: Identifier, |
||||
export_name: Sym, |
||||
} |
||||
|
||||
impl ImportSpecifier { |
||||
/// Creates a new [`ImportSpecifier`].
|
||||
#[inline] |
||||
#[must_use] |
||||
pub const fn new(binding: Identifier, export_name: Sym) -> Self { |
||||
Self { |
||||
binding, |
||||
export_name, |
||||
} |
||||
} |
||||
|
||||
/// Gets the binding of the import specifier.
|
||||
#[inline] |
||||
#[must_use] |
||||
pub const fn binding(self) -> Identifier { |
||||
self.binding |
||||
} |
||||
|
||||
/// Gets the optional export name of the import.
|
||||
#[inline] |
||||
#[must_use] |
||||
pub const fn export_name(self) -> Sym { |
||||
self.export_name |
||||
} |
||||
} |
||||
|
||||
impl VisitWith for ImportSpecifier { |
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: Visitor<'a>, |
||||
{ |
||||
try_break!(visitor.visit_identifier(&self.binding)); |
||||
visitor.visit_sym(&self.export_name) |
||||
} |
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: VisitorMut<'a>, |
||||
{ |
||||
try_break!(visitor.visit_identifier_mut(&mut self.binding)); |
||||
visitor.visit_sym_mut(&mut self.export_name) |
||||
} |
||||
} |
@ -0,0 +1,275 @@
|
||||
//! Module item list AST nodes.
|
||||
//!
|
||||
//! More information:
|
||||
//! - [ECMAScript specification][spec]
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#sec-modules
|
||||
|
||||
use std::{convert::Infallible, ops::ControlFlow}; |
||||
|
||||
use boa_interner::Sym; |
||||
use rustc_hash::FxHashSet; |
||||
|
||||
use crate::{ |
||||
declaration::{ExportDeclaration, ExportSpecifier, ImportDeclaration, ReExportKind}, |
||||
expression::Identifier, |
||||
operations::BoundNamesVisitor, |
||||
try_break, |
||||
visitor::{VisitWith, Visitor, VisitorMut}, |
||||
StatementListItem, |
||||
}; |
||||
|
||||
/// Module item list AST node.
|
||||
///
|
||||
/// It contains a list of
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ModuleItemList
|
||||
#[derive(Debug, Clone)] |
||||
pub struct ModuleItemList { |
||||
items: Box<[ModuleItem]>, |
||||
} |
||||
|
||||
impl ModuleItemList { |
||||
/// Gets the list of module items.
|
||||
#[inline] |
||||
#[must_use] |
||||
pub const fn items(&self) -> &[ModuleItem] { |
||||
&self.items |
||||
} |
||||
|
||||
/// Abstract operation [`ExportedNames`][spec].
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-exportednames
|
||||
#[inline] |
||||
#[must_use] |
||||
pub fn exported_names(&self) -> Vec<Sym> { |
||||
#[derive(Debug)] |
||||
struct ExportedItemsVisitor<'vec>(&'vec mut Vec<Sym>); |
||||
|
||||
impl<'ast> Visitor<'ast> for ExportedItemsVisitor<'_> { |
||||
type BreakTy = Infallible; |
||||
|
||||
fn visit_import_declaration( |
||||
&mut self, |
||||
_: &'ast ImportDeclaration, |
||||
) -> ControlFlow<Self::BreakTy> { |
||||
ControlFlow::Continue(()) |
||||
} |
||||
fn visit_statement_list_item( |
||||
&mut self, |
||||
_: &'ast StatementListItem, |
||||
) -> ControlFlow<Self::BreakTy> { |
||||
ControlFlow::Continue(()) |
||||
} |
||||
fn visit_export_specifier( |
||||
&mut self, |
||||
node: &'ast ExportSpecifier, |
||||
) -> ControlFlow<Self::BreakTy> { |
||||
self.0.push(node.alias()); |
||||
ControlFlow::Continue(()) |
||||
} |
||||
fn visit_export_declaration( |
||||
&mut self, |
||||
node: &'ast ExportDeclaration, |
||||
) -> ControlFlow<Self::BreakTy> { |
||||
match node { |
||||
ExportDeclaration::ReExport { kind, .. } => { |
||||
match kind { |
||||
ReExportKind::Namespaced { name: Some(name) } => self.0.push(*name), |
||||
ReExportKind::Namespaced { name: None } => {} |
||||
ReExportKind::Named { names } => { |
||||
for specifier in &**names { |
||||
try_break!(self.visit_export_specifier(specifier)); |
||||
} |
||||
} |
||||
} |
||||
ControlFlow::Continue(()) |
||||
} |
||||
ExportDeclaration::List(list) => { |
||||
for specifier in &**list { |
||||
try_break!(self.visit_export_specifier(specifier)); |
||||
} |
||||
ControlFlow::Continue(()) |
||||
} |
||||
ExportDeclaration::VarStatement(var) => { |
||||
BoundNamesVisitor(self.0).visit_var_declaration(var) |
||||
} |
||||
ExportDeclaration::Declaration(decl) => { |
||||
BoundNamesVisitor(self.0).visit_declaration(decl) |
||||
} |
||||
ExportDeclaration::DefaultFunction(_) |
||||
| ExportDeclaration::DefaultGenerator(_) |
||||
| ExportDeclaration::DefaultAsyncFunction(_) |
||||
| ExportDeclaration::DefaultAsyncGenerator(_) |
||||
| ExportDeclaration::DefaultClassDeclaration(_) |
||||
| ExportDeclaration::DefaultAssignmentExpression(_) => { |
||||
self.0.push(Sym::DEFAULT); |
||||
ControlFlow::Continue(()) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
let mut names = Vec::new(); |
||||
|
||||
ExportedItemsVisitor(&mut names).visit_module_item_list(self); |
||||
|
||||
names |
||||
} |
||||
|
||||
/// Abstract operation [`ExportedBindings`][spec].
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-exportedbindings
|
||||
#[inline] |
||||
#[must_use] |
||||
pub fn exported_bindings(&self) -> FxHashSet<Identifier> { |
||||
#[derive(Debug)] |
||||
struct ExportedBindingsVisitor<'vec>(&'vec mut FxHashSet<Identifier>); |
||||
|
||||
impl<'ast> Visitor<'ast> for ExportedBindingsVisitor<'_> { |
||||
type BreakTy = Infallible; |
||||
|
||||
fn visit_import_declaration( |
||||
&mut self, |
||||
_: &'ast ImportDeclaration, |
||||
) -> ControlFlow<Self::BreakTy> { |
||||
ControlFlow::Continue(()) |
||||
} |
||||
fn visit_statement_list_item( |
||||
&mut self, |
||||
_: &'ast StatementListItem, |
||||
) -> ControlFlow<Self::BreakTy> { |
||||
ControlFlow::Continue(()) |
||||
} |
||||
fn visit_export_specifier( |
||||
&mut self, |
||||
node: &'ast ExportSpecifier, |
||||
) -> ControlFlow<Self::BreakTy> { |
||||
self.0.insert(Identifier::new(node.private_name())); |
||||
ControlFlow::Continue(()) |
||||
} |
||||
fn visit_export_declaration( |
||||
&mut self, |
||||
node: &'ast ExportDeclaration, |
||||
) -> ControlFlow<Self::BreakTy> { |
||||
let name = match node { |
||||
ExportDeclaration::ReExport { .. } => return ControlFlow::Continue(()), |
||||
ExportDeclaration::List(list) => { |
||||
for specifier in &**list { |
||||
try_break!(self.visit_export_specifier(specifier)); |
||||
} |
||||
return ControlFlow::Continue(()); |
||||
} |
||||
ExportDeclaration::DefaultAssignmentExpression(expr) => { |
||||
return BoundNamesVisitor(self.0).visit_expression(expr); |
||||
} |
||||
ExportDeclaration::VarStatement(var) => { |
||||
return BoundNamesVisitor(self.0).visit_var_declaration(var); |
||||
} |
||||
ExportDeclaration::Declaration(decl) => { |
||||
return BoundNamesVisitor(self.0).visit_declaration(decl); |
||||
} |
||||
ExportDeclaration::DefaultFunction(f) => f.name(), |
||||
ExportDeclaration::DefaultGenerator(g) => g.name(), |
||||
ExportDeclaration::DefaultAsyncFunction(af) => af.name(), |
||||
ExportDeclaration::DefaultAsyncGenerator(ag) => ag.name(), |
||||
ExportDeclaration::DefaultClassDeclaration(cl) => cl.name(), |
||||
}; |
||||
|
||||
self.0 |
||||
.insert(name.unwrap_or_else(|| Identifier::new(Sym::DEFAULT_EXPORT))); |
||||
|
||||
ControlFlow::Continue(()) |
||||
} |
||||
} |
||||
|
||||
let mut names = FxHashSet::default(); |
||||
|
||||
ExportedBindingsVisitor(&mut names).visit_module_item_list(self); |
||||
|
||||
names |
||||
} |
||||
} |
||||
|
||||
impl<T> From<T> for ModuleItemList |
||||
where |
||||
T: Into<Box<[ModuleItem]>>, |
||||
{ |
||||
#[inline] |
||||
fn from(items: T) -> Self { |
||||
Self { |
||||
items: items.into(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl VisitWith for ModuleItemList { |
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: Visitor<'a>, |
||||
{ |
||||
for item in &*self.items { |
||||
try_break!(visitor.visit_module_item(item)); |
||||
} |
||||
|
||||
ControlFlow::Continue(()) |
||||
} |
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: VisitorMut<'a>, |
||||
{ |
||||
for item in &mut *self.items { |
||||
try_break!(visitor.visit_module_item_mut(item)); |
||||
} |
||||
|
||||
ControlFlow::Continue(()) |
||||
} |
||||
} |
||||
|
||||
/// Module item AST node.
|
||||
///
|
||||
/// This is an extension over a [`StatementList`](crate::StatementList), which can also include
|
||||
/// multiple [`ImportDeclaration`] and [`ExportDeclaration`] nodes, along with
|
||||
/// [`StatementListItem`] nodes.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ModuleItem
|
||||
#[derive(Debug, Clone)] |
||||
pub enum ModuleItem { |
||||
/// See [`ImportDeclaration`].
|
||||
ImportDeclaration(ImportDeclaration), |
||||
/// See [`ExportDeclaration`].
|
||||
ExportDeclaration(ExportDeclaration), |
||||
/// See [`StatementListItem`].
|
||||
StatementListItem(StatementListItem), |
||||
} |
||||
|
||||
impl VisitWith for ModuleItem { |
||||
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: Visitor<'a>, |
||||
{ |
||||
match self { |
||||
Self::ImportDeclaration(i) => visitor.visit_import_declaration(i), |
||||
Self::ExportDeclaration(e) => visitor.visit_export_declaration(e), |
||||
Self::StatementListItem(s) => visitor.visit_statement_list_item(s), |
||||
} |
||||
} |
||||
|
||||
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy> |
||||
where |
||||
V: VisitorMut<'a>, |
||||
{ |
||||
match self { |
||||
Self::ImportDeclaration(i) => visitor.visit_import_declaration_mut(i), |
||||
Self::ExportDeclaration(e) => visitor.visit_export_declaration_mut(e), |
||||
Self::StatementListItem(s) => visitor.visit_statement_list_item_mut(s), |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,65 @@
|
||||
use boa_ast::{ModuleItem, ModuleItemList}; |
||||
|
||||
use crate::JsResult; |
||||
|
||||
use super::ByteCompiler; |
||||
|
||||
impl ByteCompiler<'_, '_> { |
||||
/// Compiles a [`ModuleItemList`].
|
||||
#[inline] |
||||
pub fn compile_module_item_list( |
||||
&mut self, |
||||
list: &ModuleItemList, |
||||
configurable_globals: bool, |
||||
) -> JsResult<()> { |
||||
for node in list.items() { |
||||
self.compile_module_item(node, configurable_globals)?; |
||||
} |
||||
Ok(()) |
||||
} |
||||
|
||||
/// Compiles a [`ModuleItem`].
|
||||
#[inline] |
||||
#[allow(unused_variables, clippy::missing_panics_doc)] // Unimplemented
|
||||
pub fn compile_module_item( |
||||
&mut self, |
||||
item: &ModuleItem, |
||||
configurable_globals: bool, |
||||
) -> JsResult<()> { |
||||
match item { |
||||
ModuleItem::ImportDeclaration(import) => todo!("import declaration compilation"), |
||||
ModuleItem::ExportDeclaration(export) => todo!("export declaration compilation"), |
||||
ModuleItem::StatementListItem(stmt) => { |
||||
self.compile_stmt_list_item(stmt, false, configurable_globals) |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Creates the declarations for a module.
|
||||
pub(crate) fn create_module_decls( |
||||
&mut self, |
||||
stmt_list: &ModuleItemList, |
||||
configurable_globals: bool, |
||||
) { |
||||
for node in stmt_list.items() { |
||||
self.create_decls_from_module_item(node, configurable_globals); |
||||
} |
||||
} |
||||
|
||||
/// Creates the declarations from a [`ModuleItem`].
|
||||
#[inline] |
||||
#[allow(unused_variables)] // Unimplemented
|
||||
pub(crate) fn create_decls_from_module_item( |
||||
&mut self, |
||||
item: &ModuleItem, |
||||
configurable_globals: bool, |
||||
) -> bool { |
||||
match item { |
||||
ModuleItem::ImportDeclaration(import) => todo!("import declaration generation"), |
||||
ModuleItem::ExportDeclaration(export) => todo!("export declaration generation"), |
||||
ModuleItem::StatementListItem(stmt) => { |
||||
self.create_decls_from_stmt_list_item(stmt, configurable_globals) |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,316 @@
|
||||
//! Export declaration parsing
|
||||
//!
|
||||
//! This parses `export` declarations.
|
||||
//!
|
||||
//! More information:
|
||||
//! - [MDN documentation][mdn]
|
||||
//! - [ECMAScript specification][spec]
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#sec-exports
|
||||
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
|
||||
|
||||
use crate::{ |
||||
lexer::{token::ContainsEscapeSequence, TokenKind}, |
||||
parser::{ |
||||
cursor::Cursor, |
||||
expression::AssignmentExpression, |
||||
statement::{declaration::ClassDeclaration, variable::VariableStatement}, |
||||
Error, OrAbrupt, ParseResult, TokenParser, |
||||
}, |
||||
}; |
||||
use boa_ast::{ |
||||
declaration::{ExportDeclaration as AstExportDeclaration, ReExportKind}, |
||||
Keyword, Punctuator, |
||||
}; |
||||
use boa_interner::{Interner, Sym}; |
||||
use boa_profiler::Profiler; |
||||
use std::io::Read; |
||||
|
||||
use super::{ |
||||
hoistable::{AsyncFunctionDeclaration, AsyncGeneratorDeclaration, GeneratorDeclaration}, |
||||
Declaration, FromClause, FunctionDeclaration, |
||||
}; |
||||
|
||||
/// Parses an export declaration.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ExportDeclaration
|
||||
#[derive(Debug, Clone, Copy)] |
||||
pub(in crate::parser) struct ExportDeclaration; |
||||
|
||||
impl<R> TokenParser<R> for ExportDeclaration |
||||
where |
||||
R: Read, |
||||
{ |
||||
type Output = AstExportDeclaration; |
||||
|
||||
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> { |
||||
let _timer = Profiler::global().start_event("ExportDeclaration", "Parsing"); |
||||
|
||||
cursor.expect((Keyword::Export, false), "export declaration", interner)?; |
||||
|
||||
let tok = cursor.peek(0, interner).or_abrupt()?; |
||||
|
||||
let export_clause: Self::Output = match tok.kind() { |
||||
TokenKind::Punctuator(Punctuator::Mul) => { |
||||
cursor.advance(interner); |
||||
|
||||
let next = cursor.peek(0, interner).or_abrupt()?; |
||||
|
||||
match next.kind() { |
||||
TokenKind::IdentifierName((Sym::AS, _)) => { |
||||
cursor.advance(interner); |
||||
let tok = cursor.next(interner).or_abrupt()?; |
||||
|
||||
let alias = match tok.kind() { |
||||
TokenKind::StringLiteral((export_name, _)) |
||||
| TokenKind::IdentifierName((export_name, _)) => *export_name, |
||||
_ => { |
||||
return Err(Error::expected( |
||||
["identifier".to_owned(), "string literal".to_owned()], |
||||
tok.to_string(interner), |
||||
tok.span(), |
||||
"export declaration", |
||||
)) |
||||
} |
||||
}; |
||||
|
||||
let specifier = |
||||
FromClause::new("export declaration").parse(cursor, interner)?; |
||||
|
||||
AstExportDeclaration::ReExport { |
||||
kind: ReExportKind::Namespaced { name: Some(alias) }, |
||||
specifier, |
||||
} |
||||
} |
||||
TokenKind::IdentifierName((Sym::FROM, _)) => { |
||||
let specifier = |
||||
FromClause::new("export declaration").parse(cursor, interner)?; |
||||
|
||||
AstExportDeclaration::ReExport { |
||||
kind: ReExportKind::Namespaced { name: None }, |
||||
specifier, |
||||
} |
||||
} |
||||
_ => { |
||||
return Err(Error::expected( |
||||
["as".to_owned(), "from".to_owned()], |
||||
next.to_string(interner), |
||||
next.span(), |
||||
"export declaration", |
||||
)) |
||||
} |
||||
} |
||||
} |
||||
TokenKind::Punctuator(Punctuator::OpenBlock) => { |
||||
let names = NamedExports.parse(cursor, interner)?; |
||||
|
||||
let next = cursor.peek(0, interner).or_abrupt()?; |
||||
|
||||
if matches!( |
||||
next.kind(), |
||||
TokenKind::IdentifierName((Sym::FROM, ContainsEscapeSequence(false))) |
||||
) { |
||||
let specifier = |
||||
FromClause::new("export declaration").parse(cursor, interner)?; |
||||
AstExportDeclaration::ReExport { |
||||
kind: ReExportKind::Named { names }, |
||||
specifier, |
||||
} |
||||
} else { |
||||
AstExportDeclaration::List(names) |
||||
} |
||||
} |
||||
TokenKind::Keyword((Keyword::Var, false)) => VariableStatement::new(false, true) |
||||
.parse(cursor, interner) |
||||
.map(AstExportDeclaration::VarStatement)?, |
||||
TokenKind::Keyword((Keyword::Default, _)) => { |
||||
cursor.advance(interner); |
||||
|
||||
let tok = cursor.peek(0, interner).or_abrupt()?; |
||||
|
||||
match tok.kind() { |
||||
TokenKind::Keyword((Keyword::Function, false)) => { |
||||
let next_token = cursor.peek(1, interner).or_abrupt()?; |
||||
if next_token.kind() == &TokenKind::Punctuator(Punctuator::Mul) { |
||||
AstExportDeclaration::DefaultGenerator( |
||||
GeneratorDeclaration::new(false, true, true) |
||||
.parse(cursor, interner)?, |
||||
) |
||||
} else { |
||||
AstExportDeclaration::DefaultFunction( |
||||
FunctionDeclaration::new(false, true, true) |
||||
.parse(cursor, interner)?, |
||||
) |
||||
} |
||||
} |
||||
TokenKind::Keyword((Keyword::Async, false)) => { |
||||
let next_token = cursor.peek(2, interner).or_abrupt()?; |
||||
if next_token.kind() == &TokenKind::Punctuator(Punctuator::Mul) { |
||||
AstExportDeclaration::DefaultAsyncGenerator( |
||||
AsyncGeneratorDeclaration::new(false, true, true) |
||||
.parse(cursor, interner)?, |
||||
) |
||||
} else { |
||||
AstExportDeclaration::DefaultAsyncFunction( |
||||
AsyncFunctionDeclaration::new(false, true, true) |
||||
.parse(cursor, interner)?, |
||||
) |
||||
} |
||||
} |
||||
TokenKind::Keyword((Keyword::Class, false)) => { |
||||
AstExportDeclaration::DefaultClassDeclaration( |
||||
ClassDeclaration::new(false, true, true).parse(cursor, interner)?, |
||||
) |
||||
} |
||||
_ => AstExportDeclaration::DefaultAssignmentExpression( |
||||
AssignmentExpression::new(None, true, false, true) |
||||
.parse(cursor, interner)?, |
||||
), |
||||
} |
||||
} |
||||
_ => AstExportDeclaration::Declaration( |
||||
Declaration::new(false, true).parse(cursor, interner)?, |
||||
), |
||||
}; |
||||
|
||||
Ok(export_clause) |
||||
} |
||||
} |
||||
|
||||
/// Parses a named export list.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-NamedExports
|
||||
#[derive(Debug, Clone, Copy)] |
||||
struct NamedExports; |
||||
|
||||
impl<R> TokenParser<R> for NamedExports |
||||
where |
||||
R: Read, |
||||
{ |
||||
type Output = Box<[boa_ast::declaration::ExportSpecifier]>; |
||||
|
||||
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> { |
||||
cursor.expect(Punctuator::OpenBlock, "export declaration", interner)?; |
||||
|
||||
let mut list = Vec::new(); |
||||
|
||||
loop { |
||||
let tok = cursor.next(interner).or_abrupt()?; |
||||
match tok.kind() { |
||||
TokenKind::Punctuator(Punctuator::CloseBlock) => { |
||||
break; |
||||
} |
||||
TokenKind::Punctuator(Punctuator::Comma) => { |
||||
if list.is_empty() { |
||||
return Err(Error::expected( |
||||
[ |
||||
Punctuator::CloseBlock.to_string(), |
||||
"string literal".to_owned(), |
||||
"identifier".to_owned(), |
||||
], |
||||
tok.to_string(interner), |
||||
tok.span(), |
||||
"export declaration", |
||||
)); |
||||
} |
||||
} |
||||
TokenKind::StringLiteral(_) | TokenKind::IdentifierName(_) => { |
||||
list.push(ExportSpecifier.parse(cursor, interner)?); |
||||
} |
||||
_ => { |
||||
return Err(Error::expected( |
||||
[ |
||||
Punctuator::CloseBlock.to_string(), |
||||
Punctuator::Comma.to_string(), |
||||
], |
||||
tok.to_string(interner), |
||||
tok.span(), |
||||
"export declaration", |
||||
)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Ok(list.into_boxed_slice()) |
||||
} |
||||
} |
||||
|
||||
/// Parses a module export name.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ModuleExportName
|
||||
#[derive(Debug, Clone, Copy)] |
||||
pub(super) struct ModuleExportName; |
||||
|
||||
impl<R> TokenParser<R> for ModuleExportName |
||||
where |
||||
R: Read, |
||||
{ |
||||
type Output = Sym; |
||||
|
||||
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> { |
||||
let tok = cursor.next(interner).or_abrupt()?; |
||||
|
||||
match tok.kind() { |
||||
TokenKind::StringLiteral((ident, _)) => { |
||||
if interner.resolve_expect(*ident).utf8().is_none() { |
||||
return Err(Error::general( |
||||
"import specifiers don't allow unpaired surrogates", |
||||
tok.span().end(), |
||||
)); |
||||
} |
||||
Ok(*ident) |
||||
} |
||||
TokenKind::IdentifierName((ident, _)) => Ok(*ident), |
||||
_ => Err(Error::expected( |
||||
["identifier".to_owned(), "string literal".to_owned()], |
||||
tok.to_string(interner), |
||||
tok.span(), |
||||
"export specifier parsing", |
||||
)), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Parses an export specifier.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ExportSpecifier
|
||||
#[derive(Debug, Clone, Copy)] |
||||
struct ExportSpecifier; |
||||
|
||||
impl<R> TokenParser<R> for ExportSpecifier |
||||
where |
||||
R: Read, |
||||
{ |
||||
type Output = boa_ast::declaration::ExportSpecifier; |
||||
|
||||
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> { |
||||
let inner_name = ModuleExportName.parse(cursor, interner)?; |
||||
|
||||
if cursor |
||||
.next_if(TokenKind::identifier(Sym::AS), interner)? |
||||
.is_some() |
||||
{ |
||||
let export_name = ModuleExportName.parse(cursor, interner)?; |
||||
Ok(boa_ast::declaration::ExportSpecifier::new( |
||||
export_name, |
||||
inner_name, |
||||
)) |
||||
} else { |
||||
Ok(boa_ast::declaration::ExportSpecifier::new( |
||||
inner_name, inner_name, |
||||
)) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,327 @@
|
||||
//! Import declaration parsing
|
||||
//!
|
||||
//! This parses `import` declarations.
|
||||
//!
|
||||
//! More information:
|
||||
//! - [MDN documentation][mdn]
|
||||
//! - [ECMAScript specification][spec]
|
||||
//!
|
||||
//! [spec]: https://tc39.es/ecma262/#sec-imports
|
||||
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
|
||||
|
||||
use crate::{ |
||||
lexer::TokenKind, |
||||
parser::{ |
||||
cursor::Cursor, |
||||
statement::{declaration::FromClause, BindingIdentifier}, |
||||
Error, OrAbrupt, ParseResult, TokenParser, |
||||
}, |
||||
}; |
||||
use boa_ast::{ |
||||
declaration::{ |
||||
ImportDeclaration as AstImportDeclaration, ImportKind, |
||||
ImportSpecifier as AstImportSpecifier, ModuleSpecifier, |
||||
}, |
||||
expression::Identifier, |
||||
Keyword, Punctuator, |
||||
}; |
||||
use boa_interner::{Interner, Sym}; |
||||
use boa_profiler::Profiler; |
||||
use std::io::Read; |
||||
|
||||
/// Parses an import declaration.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ImportDeclaration
|
||||
#[derive(Debug, Clone, Copy)] |
||||
pub(in crate::parser) struct ImportDeclaration; |
||||
|
||||
impl<R> TokenParser<R> for ImportDeclaration |
||||
where |
||||
R: Read, |
||||
{ |
||||
type Output = AstImportDeclaration; |
||||
|
||||
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> { |
||||
let _timer = Profiler::global().start_event("ImportDeclaration", "Parsing"); |
||||
|
||||
cursor.expect((Keyword::Import, false), "import declaration", interner)?; |
||||
|
||||
let tok = cursor.peek(0, interner).or_abrupt()?; |
||||
|
||||
let import_clause = match tok.kind() { |
||||
TokenKind::StringLiteral((module_identifier, _)) => { |
||||
let module_identifier = *module_identifier; |
||||
|
||||
cursor.advance(interner); |
||||
cursor.expect_semicolon("import declaration", interner)?; |
||||
|
||||
return Ok(AstImportDeclaration::new( |
||||
None, |
||||
ImportKind::DefaultOrUnnamed, |
||||
ModuleSpecifier::new(module_identifier), |
||||
)); |
||||
} |
||||
TokenKind::Punctuator(Punctuator::OpenBlock) => { |
||||
let list = NamedImports.parse(cursor, interner)?; |
||||
ImportClause::ImportList(None, list) |
||||
} |
||||
TokenKind::Punctuator(Punctuator::Mul) => { |
||||
let alias = NameSpaceImport.parse(cursor, interner)?; |
||||
ImportClause::Namespace(None, alias) |
||||
} |
||||
TokenKind::IdentifierName(_) |
||||
| TokenKind::Keyword((Keyword::Await | Keyword::Yield, _)) => { |
||||
let imported_binding = ImportedBinding.parse(cursor, interner)?; |
||||
|
||||
let tok = cursor.peek(0, interner).or_abrupt()?; |
||||
|
||||
match tok.kind() { |
||||
TokenKind::Punctuator(Punctuator::Comma) => { |
||||
cursor.advance(interner); |
||||
let tok = cursor.peek(0, interner).or_abrupt()?; |
||||
|
||||
match tok.kind() { |
||||
TokenKind::Punctuator(Punctuator::OpenBlock) => { |
||||
let list = NamedImports.parse(cursor, interner)?; |
||||
ImportClause::ImportList(Some(imported_binding), list) |
||||
} |
||||
TokenKind::Punctuator(Punctuator::Mul) => { |
||||
let alias = NameSpaceImport.parse(cursor, interner)?; |
||||
ImportClause::Namespace(Some(imported_binding), alias) |
||||
} |
||||
_ => { |
||||
return Err(Error::expected( |
||||
[ |
||||
Punctuator::OpenBlock.to_string(), |
||||
Punctuator::Mul.to_string(), |
||||
], |
||||
tok.to_string(interner), |
||||
tok.span(), |
||||
"import declaration", |
||||
)) |
||||
} |
||||
} |
||||
} |
||||
_ => ImportClause::ImportList(Some(imported_binding), Box::default()), |
||||
} |
||||
} |
||||
_ => { |
||||
return Err(Error::expected( |
||||
[ |
||||
Punctuator::OpenBlock.to_string(), |
||||
Punctuator::Mul.to_string(), |
||||
"identifier".to_owned(), |
||||
"string literal".to_owned(), |
||||
], |
||||
tok.to_string(interner), |
||||
tok.span(), |
||||
"import declaration", |
||||
)) |
||||
} |
||||
}; |
||||
|
||||
let module_identifier = FromClause::new("import declaration").parse(cursor, interner)?; |
||||
|
||||
Ok(import_clause.with_specifier(module_identifier)) |
||||
} |
||||
} |
||||
|
||||
/// Parses an imported binding
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ImportedBinding
|
||||
#[derive(Debug, Clone, Copy)] |
||||
struct ImportedBinding; |
||||
|
||||
impl<R> TokenParser<R> for ImportedBinding |
||||
where |
||||
R: Read, |
||||
{ |
||||
type Output = Identifier; |
||||
|
||||
#[inline] |
||||
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> { |
||||
BindingIdentifier::new(false, true).parse(cursor, interner) |
||||
} |
||||
} |
||||
|
||||
/// Parses a named import list.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-NamedImports
|
||||
#[derive(Debug, Clone, Copy)] |
||||
struct NamedImports; |
||||
|
||||
impl<R> TokenParser<R> for NamedImports |
||||
where |
||||
R: Read, |
||||
{ |
||||
type Output = Box<[AstImportSpecifier]>; |
||||
|
||||
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> { |
||||
cursor.expect(Punctuator::OpenBlock, "import declaration", interner)?; |
||||
|
||||
let mut list = Vec::new(); |
||||
|
||||
loop { |
||||
let tok = cursor.next(interner).or_abrupt()?; |
||||
match tok.kind() { |
||||
TokenKind::Punctuator(Punctuator::CloseBlock) => { |
||||
break; |
||||
} |
||||
TokenKind::Punctuator(Punctuator::Comma) => { |
||||
if list.is_empty() { |
||||
return Err(Error::expected( |
||||
[ |
||||
Punctuator::CloseBlock.to_string(), |
||||
"string literal".to_owned(), |
||||
"identifier".to_owned(), |
||||
], |
||||
tok.to_string(interner), |
||||
tok.span(), |
||||
"import declaration", |
||||
)); |
||||
} |
||||
} |
||||
TokenKind::StringLiteral(_) | TokenKind::IdentifierName(_) => { |
||||
list.push(ImportSpecifier.parse(cursor, interner)?); |
||||
} |
||||
_ => { |
||||
return Err(Error::expected( |
||||
[ |
||||
Punctuator::CloseBlock.to_string(), |
||||
Punctuator::Comma.to_string(), |
||||
], |
||||
tok.to_string(interner), |
||||
tok.span(), |
||||
"import declaration", |
||||
)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Ok(list.into_boxed_slice()) |
||||
} |
||||
} |
||||
|
||||
/// Parses an import clause.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ImportClause
|
||||
#[derive(Debug, Clone)] |
||||
enum ImportClause { |
||||
Namespace(Option<Identifier>, Identifier), |
||||
ImportList(Option<Identifier>, Box<[AstImportSpecifier]>), |
||||
} |
||||
|
||||
impl ImportClause { |
||||
#[inline] |
||||
#[allow(clippy::missing_const_for_fn)] |
||||
fn with_specifier(self, specifier: ModuleSpecifier) -> AstImportDeclaration { |
||||
match self { |
||||
Self::Namespace(default, binding) => { |
||||
AstImportDeclaration::new(default, ImportKind::Namespaced { binding }, specifier) |
||||
} |
||||
Self::ImportList(default, names) => { |
||||
if names.is_empty() { |
||||
AstImportDeclaration::new(default, ImportKind::DefaultOrUnnamed, specifier) |
||||
} else { |
||||
AstImportDeclaration::new(default, ImportKind::Named { names }, specifier) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Parses an import specifier.
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-ImportSpecifier
|
||||
#[derive(Debug, Clone, Copy)] |
||||
struct ImportSpecifier; |
||||
|
||||
impl<R> TokenParser<R> for ImportSpecifier |
||||
where |
||||
R: Read, |
||||
{ |
||||
type Output = AstImportSpecifier; |
||||
|
||||
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> { |
||||
let tok = cursor.next(interner).or_abrupt()?; |
||||
|
||||
match tok.kind() { |
||||
TokenKind::StringLiteral((name, _)) => { |
||||
if interner.resolve_expect(*name).utf8().is_none() { |
||||
return Err(Error::general( |
||||
"import specifiers don't allow unpaired surrogates", |
||||
tok.span().end(), |
||||
)); |
||||
} |
||||
cursor.expect( |
||||
TokenKind::identifier(Sym::AS), |
||||
"import declaration", |
||||
interner, |
||||
)?; |
||||
|
||||
let binding = ImportedBinding.parse(cursor, interner)?; |
||||
|
||||
Ok(AstImportSpecifier::new(binding, *name)) |
||||
} |
||||
TokenKind::IdentifierName((name, _)) => { |
||||
if cursor |
||||
.next_if(TokenKind::identifier(Sym::AS), interner)? |
||||
.is_some() |
||||
{ |
||||
let binding = ImportedBinding.parse(cursor, interner)?; |
||||
Ok(AstImportSpecifier::new(binding, *name)) |
||||
} else { |
||||
Ok(AstImportSpecifier::new(Identifier::new(*name), *name)) |
||||
} |
||||
} |
||||
_ => Err(Error::expected( |
||||
["string literal".to_owned(), "identifier".to_owned()], |
||||
tok.to_string(interner), |
||||
tok.span(), |
||||
"import declaration", |
||||
)), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Parses a namespace import
|
||||
///
|
||||
/// More information:
|
||||
/// - [ECMAScript specification][spec]
|
||||
///
|
||||
/// [spec]: https://tc39.es/ecma262/#prod-NameSpaceImport
|
||||
#[derive(Debug, Clone, Copy)] |
||||
struct NameSpaceImport; |
||||
|
||||
impl<R> TokenParser<R> for NameSpaceImport |
||||
where |
||||
R: Read, |
||||
{ |
||||
type Output = Identifier; |
||||
|
||||
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> { |
||||
cursor.expect(Punctuator::Mul, "import declaration", interner)?; |
||||
cursor.expect( |
||||
TokenKind::identifier(Sym::AS), |
||||
"import declaration", |
||||
interner, |
||||
)?; |
||||
|
||||
ImportedBinding.parse(cursor, interner) |
||||
} |
||||
} |
Loading…
Reference in new issue