rename variables and add docs

This commit is contained in:
collin 2020-09-30 14:06:56 -07:00
parent f2b261bb91
commit ce8e3f4978
6 changed files with 165 additions and 48 deletions

View File

@ -36,12 +36,33 @@ impl ImportParserError {
ImportParserError::Error(FormattedError::new_from_span_with_path(message, span, path))
}
///
/// An imported package has the same name as an imported core_package.
///
pub fn conflicting_imports(identifier: Identifier) -> Self {
let message = format!("conflicting imports found for `{}`", identifier.name);
Self::new_from_span(message, identifier.span)
}
///
/// A package name has been imported twice.
///
pub fn duplicate_import(name: String, span: Span) -> Self {
let message = format!("Duplicate imports found for `{}`", name);
Self::new_from_span(message, span)
}
///
/// A core package name has been imported twice.
///
pub fn duplicate_core_package(identifier: Identifier) -> Self {
let message = format!("Duplicate core_package import `{}`", identifier.name);
Self::new_from_span(message, identifier.span)
}
pub fn convert_os_string(span: Span) -> Self {
let message = format!("failed to convert file string name, maybe an illegal character?");

View File

@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
/// The import parser creates a hashmap of import program names -> import program structs
#[macro_use]
extern crate thiserror;

View File

@ -20,9 +20,13 @@ use leo_typed::Package;
pub static CORE_PACKAGE_NAME: &str = "core";
impl ImportParser {
// import a core package into scope
///
/// Import a core package and insert into the `ImportParser`.
///
pub fn parse_core_package(&mut self, package: &Package) -> Result<(), ImportParserError> {
self.insert_core_package(package);
// Insert a core package into the `ImportParser`.
self.insert_core_package(package)?;
Ok(())
}
}

View File

@ -15,12 +15,14 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::errors::ImportParserError;
use leo_typed::{Package, Program};
use leo_typed::{Package, Program, Span};
use std::{collections::HashMap, env::current_dir};
/// Parses all relevant import files for a program.
/// Stores compiled program structs.
/// Stores imported packages.
///
/// A program can import one or more packages. A package can be found locally in the source
/// directory, foreign in the imports directory, or part of the core package list.
#[derive(Clone)]
pub struct ImportParser {
imports: HashMap<String, Program>,
@ -28,6 +30,9 @@ pub struct ImportParser {
}
impl ImportParser {
///
/// Creates a new empty `ImportParser`.
///
pub fn new() -> Self {
Self {
imports: HashMap::new(),
@ -35,30 +40,78 @@ impl ImportParser {
}
}
pub(crate) fn insert_import(&mut self, file_name: String, program: Program) {
// todo: handle conflicting versions for duplicate imports here
let _res = self.imports.insert(file_name, program);
///
/// Inserts a (file name -> program) pair into the `ImportParser`.
///
/// If the map did not have this file name present, `Ok()` is returned.
///
/// If the map did have this file name present, a duplicate import error is thrown.
///
pub(crate) fn insert_import(
&mut self,
file_name: String,
program: Program,
span: &Span,
) -> Result<(), ImportParserError> {
// Insert the imported program.
let duplicate = self.imports.insert(file_name.clone(), program);
// Check for duplicate import name.
if duplicate.is_some() {
return Err(ImportParserError::duplicate_import(file_name, span.clone()));
}
Ok(())
}
pub(crate) fn insert_core_package(&mut self, package: &Package) {
let _res = self.core_packages.push(package.clone());
///
/// Inserts a core package into the `ImportParser`.
///
/// If the vector did not have this file_name present, `Ok()` is returned.
///
/// If the vector did have this file_name present, a duplicate import error is thrown.
///
pub(crate) fn insert_core_package(&mut self, package: &Package) -> Result<(), ImportParserError> {
// Check for duplicate core package name.
if self.core_packages.contains(package) {
return Err(ImportParserError::duplicate_core_package(package.name.clone()));
}
// Append the core package.
self.core_packages.push(package.clone());
Ok(())
}
///
/// Returns a reference to the program corresponding to the file name.
///
pub fn get_import(&self, file_name: &String) -> Option<&Program> {
self.imports.get(file_name)
}
///
/// Returns a reference to the vector of core packages.
///
pub fn core_packages(&self) -> &Vec<Package> {
&self.core_packages
}
///
/// Returns a new `ImportParser` from a given `Program`.
///
/// For every import statement in the program:
/// 1. Check if the imported package exists.
/// 2. Create the typed syntax tree for the imported package.
/// 3. Insert the typed syntax tree into the `ImportParser`
///
pub fn parse(program: &Program) -> Result<Self, ImportParserError> {
let mut imports = Self::new();
// Find all imports relative to current directory
// Find all imports relative to current directory.
let path = current_dir().map_err(|error| ImportParserError::current_directory_error(error))?;
// Parse each imported file
// Parse each import statement.
program
.imports
.iter()

View File

@ -24,18 +24,25 @@ static SOURCE_DIRECTORY_NAME: &str = "src/";
static IMPORTS_DIRECTORY_NAME: &str = "imports/";
impl ImportParser {
// bring one or more import symbols into scope for the current constrained program
// we will recursively traverse sub packages here until we find the desired symbol
pub fn parse_package_access(&mut self, entry: &DirEntry, access: &PackageAccess) -> Result<(), ImportParserError> {
tracing::debug!("import {:?}", entry.path());
///
/// Import one or more symbols from a package.
///
/// Will recursively traverse sub packages until the desired symbol is found.
///
pub fn parse_package_access(
&mut self,
package: &DirEntry,
access: &PackageAccess,
) -> Result<(), ImportParserError> {
tracing::debug!("import {:?}", package.path());
match access {
PackageAccess::Star(span) => self.parse_import_star(entry, span),
PackageAccess::Symbol(symbol) => self.parse_import_symbol(entry, symbol),
PackageAccess::SubPackage(package) => self.parse_package(entry.path(), package),
PackageAccess::Star(span) => self.parse_import_star(package, span),
PackageAccess::Symbol(symbol) => self.parse_import_symbol(package, symbol),
PackageAccess::SubPackage(package) => self.parse_package(package.path(), package),
PackageAccess::Multiple(accesses) => {
for access in accesses {
self.parse_package_access(entry, access)?;
self.parse_package_access(package, access)?;
}
Ok(())
@ -43,6 +50,11 @@ impl ImportParser {
}
}
///
/// Create the typed syntax tree for an imported package.
///
/// Inserts the typed syntax tree into the `ImportParser`.
///
pub fn parse_package(&mut self, mut path: PathBuf, package: &Package) -> Result<(), ImportParserError> {
let error_path = path.clone();
let package_name = package.name.clone();
@ -68,6 +80,7 @@ impl ImportParser {
path = source_directory
}
// Get a vector of all packages in the source directory.
let entries = fs::read_dir(path)
.map_err(|error| ImportParserError::directory_error(error, package_name.span.clone(), error_path.clone()))?
.into_iter()
@ -76,6 +89,7 @@ impl ImportParser {
ImportParserError::directory_error(error, package_name.span.clone(), error_path.clone())
})?;
// Check if the imported package name is in the source directory.
let matched_source_entry = entries.into_iter().find(|entry| {
entry
.file_name()
@ -87,9 +101,10 @@ impl ImportParser {
});
if core_package {
// Enforce core library package access
// Enforce core package access.
self.parse_core_package(&package)
} else if imports_directory.exists() {
// Get a vector of all packages in the imports directory.
let entries = fs::read_dir(imports_directory)
.map_err(|error| {
ImportParserError::directory_error(error, package_name.span.clone(), error_path.clone())
@ -100,10 +115,12 @@ impl ImportParser {
ImportParserError::directory_error(error, package_name.span.clone(), error_path.clone())
})?;
// Check if the imported package name is in the imports directory.
let matched_import_entry = entries
.into_iter()
.find(|entry| entry.file_name().into_string().unwrap().eq(&package_name.name));
// Check if the package name was found in both the source and imports directory.
match (matched_source_entry, matched_import_entry) {
(Some(_), Some(_)) => Err(ImportParserError::conflicting_imports(package_name)),
(Some(source_entry), None) => self.parse_package_access(&source_entry, &package.access),

View File

@ -23,18 +23,26 @@ use std::{ffi::OsString, fs::DirEntry, path::PathBuf};
static LIBRARY_FILE: &str = "src/lib.leo";
static FILE_EXTENSION: &str = "leo";
fn parse_import_file(entry: &DirEntry, span: &Span) -> Result<Program, ImportParserError> {
// make sure the given entry is file
let file_type = entry
///
/// Returns a typed syntax tree from a given package.
///
/// Builds an abstract syntax tree from the given file and then builds the typed syntax tree.
///
fn parse_import_file(package: &DirEntry, span: &Span) -> Result<Program, ImportParserError> {
// Get the package file type.
let file_type = package
.file_type()
.map_err(|error| ImportParserError::directory_error(error, span.clone(), entry.path()))?;
let file_name = entry
.map_err(|error| ImportParserError::directory_error(error, span.clone(), package.path()))?;
// Get the package file name.
let file_name = package
.file_name()
.to_os_string()
.into_string()
.map_err(|_| ImportParserError::convert_os_string(span.clone()))?;
let mut file_path = entry.path().to_path_buf();
// Construct the package file path.
let mut file_path = package.path().to_path_buf();
if file_type.is_dir() {
file_path.push(LIBRARY_FILE);
@ -46,41 +54,51 @@ fn parse_import_file(entry: &DirEntry, span: &Span) -> Result<Program, ImportPar
}
}
// Builds the abstract syntax tree.
// Build the package abstract syntax tree.
let program_string = &LeoAst::load_file(&file_path)?;
let ast = &LeoAst::new(&file_path, &program_string)?;
// Generates the Leo program from file.
// Build the package typed syntax tree from the package abstract syntax tree.
Ok(Program::from(&file_name, ast.as_repr()))
}
impl ImportParser {
pub fn parse_import_star(&mut self, entry: &DirEntry, span: &Span) -> Result<(), ImportParserError> {
let path = entry.path();
///
/// Import all symbols from a given package.
///
/// If the package is a Leo file, import all symbols from the file.
/// If the package is a directory, import all symbol from the library file.
///
pub fn parse_import_star(&mut self, package: &DirEntry, span: &Span) -> Result<(), ImportParserError> {
let path = package.path();
let is_dir = path.is_dir();
// Check if the package is a Leo file.
let is_leo_file = path
.extension()
.map_or(false, |ext| ext.eq(&OsString::from(FILE_EXTENSION)));
// Construct the library file path to use if the package is a directory.
let mut package_path = path.to_path_buf();
package_path.push(LIBRARY_FILE);
// Check if the package is a directory.
let is_package = is_dir && package_path.exists();
// import * can only be invoked on a package with a library file or a leo file
if is_package || is_leo_file {
// Generate aleo program from file
let program = parse_import_file(entry, &span)?;
// Get the package typed syntax tree.
let program = parse_import_file(package, span)?;
// Store program's imports in imports hashmap
// Insert the package's imports into the import parser.
program
.imports
.iter()
.map(|import| self.parse_package(entry.path(), &import.package))
.map(|import| self.parse_package(package.path(), &import.package))
.collect::<Result<Vec<()>, ImportParserError>>()?;
// Store program in imports hashmap
let file_name_path = PathBuf::from(entry.file_name());
// Get the package file name from the path.
let file_name_path = PathBuf::from(package.file_name());
let file_name = file_name_path
.file_stem()
.unwrap()
@ -88,28 +106,32 @@ impl ImportParser {
.into_string()
.unwrap(); // the file exists so these will not fail
self.insert_import(file_name, program);
// Attempt to insert the typed syntax tree for the imported package.
self.insert_import(file_name, program, span)?;
Ok(())
} else {
// importing * from a directory or non-leo file in `package/src/` is illegal
Err(ImportParserError::star(entry.path().to_path_buf(), span.clone()))
Err(ImportParserError::star(package.path().to_path_buf(), span.clone()))
}
}
pub fn parse_import_symbol(&mut self, entry: &DirEntry, symbol: &ImportSymbol) -> Result<(), ImportParserError> {
// Generate aleo program from file
let program = parse_import_file(entry, &symbol.span)?;
///
/// Import a symbol from a given package.
///
pub fn parse_import_symbol(&mut self, package: &DirEntry, symbol: &ImportSymbol) -> Result<(), ImportParserError> {
// Get the package typed syntax tree.
let program = parse_import_file(package, &symbol.span)?;
// Store program's imports in imports hashmap
// Insert the package's imports into the import parser.
program
.imports
.iter()
.map(|import| self.parse_package(entry.path(), &import.package))
.map(|import| self.parse_package(package.path(), &import.package))
.collect::<Result<Vec<()>, ImportParserError>>()?;
// Store program in imports hashmap
let file_name_path = PathBuf::from(entry.file_name());
// Get the package file name from the path.
let file_name_path = PathBuf::from(package.file_name());
let file_name = file_name_path
.file_stem()
.unwrap()
@ -117,7 +139,8 @@ impl ImportParser {
.into_string()
.unwrap(); // the file exists so these will not fail
self.insert_import(file_name, program);
// Attempt to insert the typed syntax tree for the imported package.
self.insert_import(file_name, program, &symbol.span)?;
Ok(())
}