diff --git a/ast/src/imports/import.rs b/ast/src/imports/import.rs deleted file mode 100644 index a3819e8621..0000000000 --- a/ast/src/imports/import.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2019-2022 Aleo Systems Inc. -// This file is part of the Leo library. - -// The Leo library is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The Leo library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with the Leo library. If not, see . - -use crate::PackageOrPackages; -use leo_span::{Span, Symbol}; - -use serde::{Deserialize, Serialize}; -use std::fmt; - -/// Represents an import statement in a Leo program. -#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)] -pub struct ImportStatement { - /// The package or packages to import. - pub package_or_packages: PackageOrPackages, - /// The span, excluding the `;`. - pub span: Span, -} - -impl ImportStatement { - /// Returns the the package file name of the self import statement. - pub fn get_file_name(&self) -> Symbol { - match self.package_or_packages { - PackageOrPackages::Package(ref package) => package.name.name, - PackageOrPackages::Packages(ref packages) => packages.name.name, - } - } -} - -impl ImportStatement { - fn format(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "import {};", self.package_or_packages) - } -} - -impl fmt::Display for ImportStatement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.format(f) - } -} - -impl fmt::Debug for ImportStatement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.format(f) - } -} diff --git a/ast/src/imports/import_symbol.rs b/ast/src/imports/import_symbol.rs deleted file mode 100644 index c0b4de7443..0000000000 --- a/ast/src/imports/import_symbol.rs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2019-2022 Aleo Systems Inc. -// This file is part of the Leo library. - -// The Leo library is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The Leo library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with the Leo library. If not, see . - -use crate::Identifier; - -use leo_span::{sym, Span}; - -use serde::{Deserialize, Serialize}; -use std::fmt; - -/// An import of `symbol` possibly renamed to `alias`, -/// e.g., `symbol` or `symbol as alias`. -/// -/// This is the leaf of an import tree. -#[derive(Clone, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct ImportSymbol { - pub symbol: Identifier, - /// The name, if any, to import `symbol` as. - /// If not specified, `symbol` is the name it is imported under. - pub alias: Option, - /// The span including `symbol` and possibly `as alias`. - pub span: Span, -} - -impl fmt::Display for ImportSymbol { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.alias.is_some() { - write!(f, "{} as {}", self.symbol, self.alias.as_ref().unwrap()) - } else { - write!(f, "{}", self.symbol) - } - } -} - -// TODO (collin): remove this -impl fmt::Debug for ImportSymbol { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.alias.is_some() { - write!(f, "{} as {}", self.symbol, self.alias.as_ref().unwrap()) - } else { - write!(f, "{}", self.symbol) - } - } -} - -impl ImportSymbol { - /// Creates a glob `*` import. - pub fn star(span: &Span) -> Self { - Self { - symbol: Identifier { - name: sym::Star, - span: span.clone(), - }, - alias: None, - span: span.clone(), - } - } - - /// Is this a glob import? - pub fn is_star(&self) -> bool { - self.symbol.name == sym::Star - } -} diff --git a/ast/src/imports/mod.rs b/ast/src/imports/mod.rs index 29f787db60..0abc99f168 100644 --- a/ast/src/imports/mod.rs +++ b/ast/src/imports/mod.rs @@ -14,20 +14,106 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -pub mod import; -pub use import::*; +use crate::Identifier; +use leo_span::{Span, Symbol}; -pub mod import_symbol; -pub use import_symbol::*; +use serde::{Deserialize, Serialize}; +use std::fmt; -pub mod package; -pub use package::*; +/// Represents an import statement in a Leo program. +#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct ImportStatement { + /// The tree specifying what items or packages to import. + pub tree: ImportTree, + /// The span, excluding the `;`. + pub span: Span, +} -pub mod packages; -pub use packages::*; +impl ImportStatement { + /// Returns the the package file name of the self import statement. + pub fn get_file_name(&self) -> Symbol { + self.tree.base.first().unwrap().name + } +} -pub mod package_or_packages; -pub use package_or_packages::*; +impl fmt::Display for ImportStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "import {};", self.tree) + } +} -pub mod package_access; -pub use package_access::*; +impl fmt::Debug for ImportStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/// An import tree specifies item(s) to import. +#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct ImportTree { + /// A path to the base item or package to import or import from. + /// The list is always non-empty. + pub base: Vec, + /// Specifies the kind of import and the meaning of `base`. + /// This includes plain imports, renames, globs (`*`), and nested imports. + pub kind: ImportTreeKind, + /// The span for the import excluding `import` and `;`. + pub span: Span, +} + +impl fmt::Display for ImportTree { + /// Formats `self` to `f`. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // Format the path. + for (i, part) in self.base.iter().enumerate() { + write!(f, "{}", part)?; + if i < self.base.len() - 1 { + write!(f, ".")?; + } + } + + // Format the kind. + match &self.kind { + ImportTreeKind::Glob { .. } => write!(f, ".*"), + ImportTreeKind::Leaf { alias: None } => Ok(()), + ImportTreeKind::Leaf { alias: Some(alias) } => write!(f, "as {}", alias), + ImportTreeKind::Nested { tree } => { + write!(f, ".(")?; + for (i, node) in tree.iter().enumerate() { + write!(f, "{}", node)?; + if i < tree.len() - 1 { + write!(f, ", ")?; + } + } + write!(f, ")") + } + } + } +} + +impl fmt::Debug for ImportTree { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/// Specifies the import kind and the meaning of `base`. +#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)] +pub enum ImportTreeKind { + /// A glob import `*`. + Glob { + /// The span for the `*`. + span: Span, + }, + /// A leaf package to import. + Leaf { + /// When specified, the package is imported under a different name. + /// Otherwise, the `base` name is used as in the `ImportTree`. + alias: Option, + }, + /// A nested import of items or sub-packages. + Nested { + /// The sub-tree specifying what to import from the `base`. + tree: Vec, + }, +} diff --git a/ast/src/imports/package.rs b/ast/src/imports/package.rs deleted file mode 100644 index ce693f604d..0000000000 --- a/ast/src/imports/package.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2019-2022 Aleo Systems Inc. -// This file is part of the Leo library. - -// The Leo library is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The Leo library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with the Leo library. If not, see . - -use crate::{common::Identifier, PackageAccess}; -use leo_span::Span; - -use serde::{Deserialize, Serialize}; -use std::fmt; - -/// A package import specification. -#[derive(Clone, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct Package { - /// The base package to import `access` from. - pub name: Identifier, - /// A specification of what to import from `name`. - pub access: PackageAccess, - /// The span including the `name` and the `access`. - pub span: Span, -} - -impl Package { - /// Formats `self` to `f`. - fn format(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}.{}", self.name, self.access) - } -} - -impl fmt::Display for Package { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.format(f) - } -} - -impl fmt::Debug for Package { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.format(f) - } -} diff --git a/ast/src/imports/package_access.rs b/ast/src/imports/package_access.rs deleted file mode 100644 index 28745f6921..0000000000 --- a/ast/src/imports/package_access.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (C) 2019-2022 Aleo Systems Inc. -// This file is part of the Leo library. - -// The Leo library is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The Leo library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with the Leo library. If not, see . - -use crate::{ImportSymbol, Node, Package, Packages}; -use leo_span::Span; - -use serde::{Deserialize, Serialize}; -use std::fmt; - -#[derive(Clone, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum PackageAccess { - /// A glob import `*`. - Star { - /// The span for the `*`. - span: Span, - }, - /// A subpackage to import. - SubPackage(Box), - /// A leaf package to import. - Symbol(ImportSymbol), - /// Several subpackages to import. - // FIXME(Centril): This structure seems convoluted and unclear. - // Refactor and simplify the types to: - // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/struct.UseTree.html. - Multiple(Packages), -} - -impl Node for PackageAccess { - fn span(&self) -> &Span { - match self { - PackageAccess::Star { span } => span, - PackageAccess::SubPackage(package) => &package.span, - PackageAccess::Symbol(package) => &package.span, - PackageAccess::Multiple(package) => &package.span, - } - } - - fn set_span(&mut self, span: Span) { - match self { - PackageAccess::Star { span } => *span = span.clone(), - PackageAccess::SubPackage(package) => package.span = span, - PackageAccess::Symbol(package) => package.span = span, - PackageAccess::Multiple(package) => package.span = span, - } - } -} - -impl PackageAccess { - /// Formats `self` to `f`. - fn format(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - PackageAccess::Star { .. } => write!(f, "*"), - PackageAccess::SubPackage(ref package) => write!(f, "{}", package), - PackageAccess::Symbol(ref symbol) => write!(f, "{}", symbol), - PackageAccess::Multiple(ref packages) => { - write!(f, "(")?; - for (i, access) in packages.accesses.iter().enumerate() { - write!(f, "{}", access)?; - if i < packages.accesses.len() - 1 { - write!(f, ", ")?; - } - } - write!(f, ")") - } - } - } -} - -impl fmt::Debug for PackageAccess { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.format(f) - } -} - -impl fmt::Display for PackageAccess { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.format(f) - } -} diff --git a/ast/src/imports/package_or_packages.rs b/ast/src/imports/package_or_packages.rs deleted file mode 100644 index 780f99f1e5..0000000000 --- a/ast/src/imports/package_or_packages.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2019-2022 Aleo Systems Inc. -// This file is part of the Leo library. - -// The Leo library is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The Leo library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with the Leo library. If not, see . - -use crate::{Node, Package, Packages}; -use leo_span::Span; - -use serde::{Deserialize, Serialize}; -use std::fmt; - -/// A specification of what packages to import. -#[derive(Clone, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum PackageOrPackages { - /// Instruction to import a single package or item. - Package(Package), - /// Instruction to import a packages or items with a common prefix. - Packages(Packages), -} - -impl PackageOrPackages { - /// Formats `self` to `f`. - fn format(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - PackageOrPackages::Package(ref package) => write!(f, "{}", package), - PackageOrPackages::Packages(ref packages) => { - write!(f, "(")?; - for (i, access) in packages.accesses.iter().enumerate() { - write!(f, "{}", access)?; - if i < packages.accesses.len() - 1 { - write!(f, ", ")?; - } - } - write!(f, ")") - } - } - } -} - -impl fmt::Debug for PackageOrPackages { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.format(f) - } -} - -impl fmt::Display for PackageOrPackages { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.format(f) - } -} - -impl Node for PackageOrPackages { - fn span(&self) -> &Span { - match self { - PackageOrPackages::Package(package) => &package.span, - PackageOrPackages::Packages(packages) => &packages.span, - } - } - - fn set_span(&mut self, span: Span) { - match self { - PackageOrPackages::Package(package) => package.span = span, - PackageOrPackages::Packages(packages) => packages.span = span, - } - } -} diff --git a/ast/src/imports/packages.rs b/ast/src/imports/packages.rs deleted file mode 100644 index dafe4cae9d..0000000000 --- a/ast/src/imports/packages.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2019-2022 Aleo Systems Inc. -// This file is part of the Leo library. - -// The Leo library is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// The Leo library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with the Leo library. If not, see . - -use crate::{common::Identifier, PackageAccess}; -use leo_span::Span; - -use serde::{Deserialize, Serialize}; -use std::fmt; - -/// Import of `name.(accesses)`, that is, several sub-packages or items within `name`. -#[derive(Clone, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct Packages { - /// The common package that `accesses` are contained within. - pub name: Identifier, - /// The packages or items to import within the package `name`. - pub accesses: Vec, - /// The entire span for `name.(accesses)`. - pub span: Span, -} - -impl Packages { - /// Formats `self` to `f`. - fn format(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}.(", self.name)?; - for (i, access) in self.accesses.iter().enumerate() { - write!(f, "{}", access)?; - if i < self.accesses.len() - 1 { - write!(f, ", ")?; - } - } - write!(f, ")") - } -} - -impl fmt::Display for Packages { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.format(f) - } -} - -impl fmt::Debug for Packages { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.format(f) - } -} diff --git a/ast/src/reducer/reconstructing_director.rs b/ast/src/reducer/reconstructing_director.rs index 963a8e84e2..ba70f7d729 100644 --- a/ast/src/reducer/reconstructing_director.rs +++ b/ast/src/reducer/reconstructing_director.rs @@ -496,27 +496,33 @@ impl ReconstructingDirector { self.reducer.reduce_function_input(input, new) } - pub fn reduce_package_or_packages(&mut self, package_or_packages: &PackageOrPackages) -> Result { - let new = match package_or_packages { - PackageOrPackages::Package(package) => PackageOrPackages::Package(Package { - name: self.reduce_identifier(&package.name)?, - access: package.access.clone(), - span: package.span.clone(), - }), - PackageOrPackages::Packages(packages) => PackageOrPackages::Packages(Packages { - name: self.reduce_identifier(&packages.name)?, - accesses: packages.accesses.clone(), - span: packages.span.clone(), - }), + pub fn reduce_import_tree(&mut self, tree: &ImportTree) -> Result { + let new = ImportTree { + base: tree + .base + .iter() + .map(|i| self.reduce_identifier(i)) + .collect::>()?, + + kind: match &tree.kind { + ImportTreeKind::Glob { .. } | ImportTreeKind::Leaf { alias: None } => tree.kind.clone(), + ImportTreeKind::Leaf { alias: Some(alias) } => { + let alias = self.reduce_identifier(alias)?; + ImportTreeKind::Leaf { alias: Some(alias) } + } + ImportTreeKind::Nested { tree } => ImportTreeKind::Nested { + tree: tree.iter().map(|n| self.reduce_import_tree(n)).collect::>()?, + }, + }, + span: tree.span.clone(), }; - self.reducer.reduce_package_or_packages(package_or_packages, new) + self.reducer.reduce_import_tree(tree, new) } pub fn reduce_import_statement(&mut self, import: &ImportStatement) -> Result { - let package_or_packages = self.reduce_package_or_packages(&import.package_or_packages)?; - - self.reducer.reduce_import_statement(import, package_or_packages) + let tree = self.reduce_import_tree(&import.tree)?; + self.reducer.reduce_import_statement(import, tree) } pub fn reduce_import(&mut self, identifier: &[Symbol], import: &Program) -> Result<(Vec, Program)> { diff --git a/ast/src/reducer/reconstructing_reducer.rs b/ast/src/reducer/reconstructing_reducer.rs index 4118ce6f8d..808f4ecd35 100644 --- a/ast/src/reducer/reconstructing_reducer.rs +++ b/ast/src/reducer/reconstructing_reducer.rs @@ -421,21 +421,13 @@ pub trait ReconstructingReducer { Ok(new) } - fn reduce_package_or_packages( - &mut self, - _package_or_packages: &PackageOrPackages, - new: PackageOrPackages, - ) -> Result { + fn reduce_import_tree(&mut self, _tree: &ImportTree, new: ImportTree) -> Result { Ok(new) } - fn reduce_import_statement( - &mut self, - import: &ImportStatement, - package_or_packages: PackageOrPackages, - ) -> Result { + fn reduce_import_statement(&mut self, import: &ImportStatement, tree: ImportTree) -> Result { Ok(ImportStatement { - package_or_packages, + tree, span: import.span.clone(), }) } diff --git a/parser/src/parser/context.rs b/parser/src/parser/context.rs index b338216cd1..4c7479311d 100644 --- a/parser/src/parser/context.rs +++ b/parser/src/parser/context.rs @@ -425,6 +425,8 @@ impl<'a> ParserContext<'a> { trailing = false; break; } + + trailing = true; } // Parse closing delimiter. diff --git a/parser/src/parser/file.rs b/parser/src/parser/file.rs index 52f8a4489c..3d6e473b2f 100644 --- a/parser/src/parser/file.rs +++ b/parser/src/parser/file.rs @@ -18,7 +18,7 @@ use super::*; use crate::KEYWORD_TOKENS; use leo_errors::{ParserError, Result}; -use leo_span::{sym, Span}; +use leo_span::sym; impl ParserContext<'_> { /// @@ -146,64 +146,6 @@ impl ParserContext<'_> { Ok(name) } - /// Returns a vector of [`PackageAccess`] AST nodes if the next tokens represent package access - /// expressions within an import statement. - pub fn parse_package_accesses(&mut self, start: &Span) -> Result<(Vec, Span)> { - let (out, _, end) = self.parse_paren_comma_list(|p| p.parse_package_access().map(Some))?; - - if out.is_empty() { - self.emit_err(ParserError::invalid_import_list(&end)); - } - - Ok((out, start + &end)) - } - - /// - /// Returns a [`PackageAccess`] AST node if the next tokens represent a package access expression - /// within an import statement. - /// - pub fn parse_package_access(&mut self) -> Result { - if let Some(SpannedToken { span, .. }) = self.eat(Token::Mul) { - Ok(PackageAccess::Star { span }) - } else { - let mut name = self.expect_ident()?; - - // Allow dashes in the accessed members (should only be used for directories). - // If imported member does not exist, code will fail on ASG level. - if let Token::Minus = self.peek_token().as_ref() { - let span = self.expect(Token::Minus)?; - name.span = name.span + span; - let next = self.expect_ident()?; - name.span = name.span + next.span; - name.name = Symbol::intern(&format!("{}-{}", name.name, next.name)); - } - - if self.peek_token().as_ref() == &Token::Dot { - self.backtrack(SpannedToken { - token: Token::Ident(name.name), - span: name.span, - }); - Ok(match self.parse_package_path()? { - PackageOrPackages::Package(p) => PackageAccess::SubPackage(Box::new(p)), - PackageOrPackages::Packages(p) => PackageAccess::Multiple(p), - }) - } else if self.eat(Token::As).is_some() { - let alias = self.expect_ident()?; - Ok(PackageAccess::Symbol(ImportSymbol { - span: &name.span + &alias.span, - symbol: name, - alias: Some(alias), - })) - } else { - Ok(PackageAccess::Symbol(ImportSymbol { - span: name.span.clone(), - symbol: name, - alias: None, - })) - } - } - } - /// Returns an [`Identifier`] AST node if the next tokens represent a valid package name. pub fn parse_package_name(&mut self) -> Result { // Build the package name, starting with valid characters up to a dash `-` (Token::Minus). @@ -257,33 +199,57 @@ impl ParserContext<'_> { Ok(base) } - /// - /// Returns a [`PackageOrPackages`] AST node if the next tokens represent a valid package import + /// Returns an [`ImportTree`] AST node if the next tokens represent a valid package import /// with accesses. - /// - pub fn parse_package_path(&mut self) -> Result { - let name = self.parse_package_name()?; - self.expect(Token::Dot)?; - if self.peek_is_left_par() { - let (accesses, span) = self.parse_package_accesses(&name.span)?; - Ok(PackageOrPackages::Packages(Packages { span, name, accesses })) - } else { - let access = self.parse_package_access()?; - let span = &name.span + access.span(); - Ok(PackageOrPackages::Package(Package { span, name, access })) + fn parse_import_tree(&mut self) -> Result { + // Parse the first part of the path. + let first_name = self.parse_package_name()?; + let start = first_name.span.clone(); + let mut base = vec![first_name]; + + let make = |base, end, kind| { + let span = start + end; + ImportTree { base, span, kind } + }; + + // Paths are separated by `.`s. + while self.eat(Token::Dot).is_some() { + if self.peek_is_left_par() { + // Encountered `.(`, so we have a nested import. Recurse! + let (tree, _, end) = self.parse_paren_comma_list(|p| p.parse_import_tree().map(Some))?; + + if tree.is_empty() { + self.emit_err(ParserError::invalid_import_list(&end)); + } + + return Ok(make(base, end, ImportTreeKind::Nested { tree })); + } else if let Some(SpannedToken { span, .. }) = self.eat(Token::Mul) { + // Encountered `.*`, so we have a glob import. + return Ok(make(base, span.clone(), ImportTreeKind::Glob { span })); + } + + // Parse another path segment. + base.push(self.parse_package_name()?); } + + let (end, alias) = if self.eat(Token::As).is_some() { + // Encountered `as`, so interpret as `path as rename`. + let alias = self.expect_ident()?; + (alias.span.clone(), Some(alias)) + } else { + (base.last().unwrap().span.clone(), None) + }; + Ok(make(base, end, ImportTreeKind::Leaf { alias })) } - /// /// Returns a [`ImportStatement`] AST node if the next tokens represent an import statement. - /// pub fn parse_import_statement(&mut self) -> Result { self.expect(Token::Import)?; - let package_or_packages = self.parse_package_path()?; + let tree = self.parse_import_tree()?; self.expect(Token::Semicolon)?; Ok(ImportStatement { - span: package_or_packages.span().clone(), - package_or_packages, + span: tree.span.clone(), + tree, }) }