diff --git a/boa_ast/src/declaration/export.rs b/boa_ast/src/declaration/export.rs index 518e7f36ac0..2c92d64eafb 100644 --- a/boa_ast/src/declaration/export.rs +++ b/boa_ast/src/declaration/export.rs @@ -3,12 +3,17 @@ //! This module contains `export` declaration AST nodes. //! //! More information: -//! - [MDN documentation][mdn] +//! - [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 super::FromClause; +use crate::{ + function::{AsyncFunction, AsyncGenerator, Class, Function, Generator}, + Declaration, Expression, Statement, +}; use boa_interner::Sym; /// An export declaration AST node. @@ -17,10 +22,38 @@ use boa_interner::Sym; /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/ecma262/#prod-ExportDeclaration -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub enum ExportDeclaration { + /// Re-export all exports. + ReExportAll { + /// Alias for the module export. + alias: Option, + /// From clause. + from: FromClause, + }, /// List of exports. - ExportList, + List { + /// List of exports. + list: Box<[ExportSpecifier]>, + /// From clause. + from: Option, + }, + /// Variable statement export. + VarStatement(Statement), + /// 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), } /// Export specifier diff --git a/boa_ast/src/declaration/import.rs b/boa_ast/src/declaration/import.rs index ed175c11d97..f90f1bde2e4 100644 --- a/boa_ast/src/declaration/import.rs +++ b/boa_ast/src/declaration/import.rs @@ -12,6 +12,8 @@ use crate::expression::Identifier; use boa_interner::Sym; +use super::FromClause; + /// An import declaration AST node. /// /// More information: @@ -34,7 +36,7 @@ pub enum ImportDeclaration { }, /// Import list (`import { export1, export2 as alias2} from "module-name"`), with an optional /// default export binding. - ImportList { + List { /// Optional default export for the import list. default_export: Option, /// List of imports. @@ -64,16 +66,12 @@ impl ImportDeclaration { /// Creates a new namespace import declaration. #[inline] - pub fn import_list( - default_export: Option, - import_list: L, - from_clause: F, - ) -> Self + pub fn list(default_export: Option, import_list: L, from_clause: F) -> Self where L: Into>, F: Into, { - Self::ImportList { + Self::List { default_export, import_list: import_list.into(), from_clause: from_clause.into(), @@ -115,30 +113,3 @@ impl ImportSpecifier { self.alias } } - -/// From clause AST node. -/// -/// More information: -/// - [ECMAScript specification][spec] -/// -/// [spec]: https://tc39.es/ecma262/#prod-FromClause -#[derive(Debug, Clone, Copy)] -pub struct FromClause { - module: Sym, -} - -impl FromClause { - /// Gets the module specifier for the from clause. - #[inline] - #[must_use] - pub const fn module(self) -> Sym { - self.module - } -} - -impl From for FromClause { - #[inline] - fn from(s: Sym) -> Self { - Self { module: s } - } -} diff --git a/boa_ast/src/declaration/mod.rs b/boa_ast/src/declaration/mod.rs index 0379fe32f8c..5642ba11b6d 100644 --- a/boa_ast/src/declaration/mod.rs +++ b/boa_ast/src/declaration/mod.rs @@ -15,7 +15,7 @@ //! [diff]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements#difference_between_statements_and_declarations use super::function::{AsyncFunction, AsyncGenerator, Class, Function, Generator}; -use boa_interner::{Interner, ToIndentedString, ToInternedString}; +use boa_interner::{Interner, Sym, ToIndentedString, ToInternedString}; use core::ops::ControlFlow; mod export; @@ -99,3 +99,30 @@ impl VisitWith for Declaration { } } } + +/// From clause AST node. +/// +/// More information: +/// - [ECMAScript specification][spec] +/// +/// [spec]: https://tc39.es/ecma262/#prod-FromClause +#[derive(Debug, Clone, Copy)] +pub struct FromClause { + module: Sym, +} + +impl FromClause { + /// Gets the module specifier for the from clause. + #[inline] + #[must_use] + pub const fn module(self) -> Sym { + self.module + } +} + +impl From for FromClause { + #[inline] + fn from(s: Sym) -> Self { + Self { module: s } + } +} diff --git a/boa_parser/src/parser/statement/declaration/export.rs b/boa_parser/src/parser/statement/declaration/export.rs index be66599458c..935ecfe1c27 100644 --- a/boa_parser/src/parser/statement/declaration/export.rs +++ b/boa_parser/src/parser/statement/declaration/export.rs @@ -22,6 +22,8 @@ use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; +use super::FromClause; + /// Parses an export declaration. /// /// More information: @@ -39,19 +41,47 @@ where fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { 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 = match tok.kind() { + 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) => todo!("parse alias, then from"), - TokenKind::IdentifierName(Sym::FROM) => todo!("parse from"), + 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 from = FromClause::new("export declaration").parse(cursor, interner)?; + + boa_ast::declaration::ExportDeclaration::ReExportAll { + alias: Some(alias), + from, + } + } + TokenKind::IdentifierName(Sym::FROM) => { + let from = FromClause::new("export declaration").parse(cursor, interner)?; + + boa_ast::declaration::ExportDeclaration::ReExportAll { alias: None, from } + } _ => { return Err(Error::expected( ["as".to_owned(), "from".to_owned()], @@ -64,11 +94,22 @@ where } TokenKind::Punctuator(Punctuator::OpenBlock) => { let list = NamedExports.parse(cursor, interner)?; - todo!("check if there is a from clause") + + let next = cursor.peek(0, interner).or_abrupt()?; + + if let TokenKind::IdentifierName(Sym::FROM) = next.kind() { + let from = FromClause::new("export declaration").parse(cursor, interner)?; + boa_ast::declaration::ExportDeclaration::List { + list, + from: Some(from), + } + } else { + boa_ast::declaration::ExportDeclaration::List { list, from: None } + } } TokenKind::Keyword((Keyword::Var, _)) => VariableStatement::new(false, true) .parse(cursor, interner) - .map(|s| todo!("variable declaration export"))?, + .map(boa_ast::declaration::ExportDeclaration::VarStatement)?, TokenKind::Keyword((Keyword::Default, _)) => { cursor.advance(interner); @@ -76,16 +117,19 @@ where match tok.kind() { TokenKind::Keyword((Keyword::Class, _)) => { - let _ = ClassDeclaration::new(false, true, true).parse(cursor, interner)?; + let class_declaration = + ClassDeclaration::new(false, true, true).parse(cursor, interner)?; todo!("Class") } _ => todo!("default export parsing"), } } - _ => {} + _ => { + todo!() + } }; - todo!("ExportDeclaration parsing") + Ok(export_clause) } } diff --git a/boa_parser/src/parser/statement/declaration/import.rs b/boa_parser/src/parser/statement/declaration/import.rs index 0e2991b059d..ced19eda463 100644 --- a/boa_parser/src/parser/statement/declaration/import.rs +++ b/boa_parser/src/parser/statement/declaration/import.rs @@ -12,7 +12,9 @@ use crate::{ lexer::TokenKind, parser::{ - cursor::Cursor, statement::BindingIdentifier, Error, OrAbrupt, ParseResult, TokenParser, + cursor::Cursor, + statement::{declaration::FromClause, BindingIdentifier}, + Error, OrAbrupt, ParseResult, TokenParser, }, }; use boa_ast::{expression::Identifier, Keyword, Punctuator}; @@ -112,29 +114,9 @@ where } }; - let from_tok = cursor.next(interner).or_abrupt()?; - if from_tok.kind() != &TokenKind::IdentifierName(Sym::FROM) { - return Err(Error::expected( - ["from".to_owned()], - from_tok.to_string(interner), - from_tok.span(), - "import declaration", - )); - } + let module_identifier = FromClause::new("import declaration").parse(cursor, interner)?; - let module_identifier_tok = cursor.next(interner).or_abrupt()?; - match module_identifier_tok.kind() { - TokenKind::StringLiteral(module_identifier) => { - cursor.expect_semicolon("import declaration", interner)?; - Ok(import_clause.add_from(*module_identifier)) - } - _ => Err(Error::expected( - ["string literal".to_owned()], - module_identifier_tok.to_string(interner), - module_identifier_tok.span(), - "import declaration", - )), - } + Ok(import_clause.add_from(module_identifier)) } } @@ -237,13 +219,16 @@ enum ImportClause { impl ImportClause { #[inline] - fn add_from(self, from_clause: Sym) -> boa_ast::declaration::ImportDeclaration { + fn add_from( + self, + from_clause: boa_ast::declaration::FromClause, + ) -> boa_ast::declaration::ImportDeclaration { match self { Self::Namespace(default, alias) => { boa_ast::declaration::ImportDeclaration::namespace(default, alias, from_clause) } Self::ImportList(default, list) => { - boa_ast::declaration::ImportDeclaration::import_list(default, list, from_clause) + boa_ast::declaration::ImportDeclaration::list(default, list, from_clause) } } } diff --git a/boa_parser/src/parser/statement/declaration/mod.rs b/boa_parser/src/parser/statement/declaration/mod.rs index 75f6b47e9ce..4f7a037ec07 100644 --- a/boa_parser/src/parser/statement/declaration/mod.rs +++ b/boa_parser/src/parser/statement/declaration/mod.rs @@ -26,9 +26,10 @@ pub(in crate::parser) use self::{ use crate::{ lexer::TokenKind, parser::{AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser}, + Error, }; use boa_ast::{self as ast, Keyword}; -use boa_interner::Interner; +use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use std::io::Read; @@ -45,6 +46,8 @@ pub(super) struct Declaration { } impl Declaration { + /// Creates a new declaration parser. + #[inline] pub(super) fn new(allow_yield: Y, allow_await: A) -> Self where Y: Into, @@ -81,3 +84,48 @@ where } } } + +/// Parses a `from` clause. +/// +/// More information: +/// - [ECMAScript specification][spec] +/// +/// [spec]: https://tc39.es/ecma262/#prod-FromClause +#[derive(Debug, Clone, Copy)] +struct FromClause { + context: &'static str, +} + +impl FromClause { + /// Creates a new `from` clause parser + #[inline] + const fn new(context: &'static str) -> Self { + Self { context } + } +} + +impl TokenParser for FromClause +where + R: Read, +{ + type Output = ast::declaration::FromClause; + + fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { + let _timer = Profiler::global().start_event("FromClause", "Parsing"); + + cursor.expect(TokenKind::IdentifierName(Sym::FROM), self.context, interner)?; + + let tok = cursor.next(interner).or_abrupt()?; + + let TokenKind::StringLiteral(from) = tok.kind() else { + return Err(Error::expected( + ["string literal".to_owned()], + tok.to_string(interner), + tok.span(), + self.context, + )) + }; + + Ok((*from).into()) + } +}