create separate module for imports

This commit is contained in:
collin 2020-09-18 12:04:10 -07:00
parent 733561cc93
commit 22422c1b55
10 changed files with 407 additions and 3 deletions

11
Cargo.lock generated
View File

@ -1261,7 +1261,7 @@ dependencies = [
[[package]]
name = "leo-core"
version = "1.0.1"
version = "1.0.3"
dependencies = [
"leo-gadgets",
"leo-typed",
@ -1287,6 +1287,14 @@ dependencies = [
"thiserror",
]
[[package]]
name = "leo-imports"
version = "1.0.3"
dependencies = [
"leo-typed",
"thiserror",
]
[[package]]
name = "leo-input"
version = "1.0.3"
@ -1319,6 +1327,7 @@ dependencies = [
"leo-compiler",
"leo-core",
"leo-gadgets",
"leo-imports",
"leo-input",
"leo-package",
"leo-state",

View File

@ -30,6 +30,7 @@ members = [
"compiler",
"core",
"gadgets",
"imports",
"input",
"linter",
"package",
@ -49,6 +50,10 @@ version = "1.0.1"
path = "./gadgets"
version = "1.0.3"
[dependencies.leo-imports]
path = "./imports"
version = "1.0.3"
[dependencies.leo-input]
path = "./input"
version = "1.0.3"

View File

@ -23,7 +23,7 @@ version = "1.0.3"
[dependencies.leo-core]
path = "../core"
version = "1.0.1"
version = "1.0.3"
[dependencies.leo-gadgets]
path = "../gadgets"

View File

@ -1,6 +1,6 @@
[package]
name = "leo-core"
version = "1.0.1"
version = "1.0.3"
authors = [ "The Aleo Team <hello@aleo.org>" ]
description = "Core package dependencies of the Leo programming language"
homepage = "https://aleo.org"

25
imports/Cargo.toml Normal file
View File

@ -0,0 +1,25 @@
[package]
name = "leo-imports"
version = "1.0.3"
authors = [ "The Aleo Team <hello@aleo.org>"]
description = "Import parser for Leo program package dependencies"
homepage = "https://aleo.org"
respository = "https://github.com/AleoHQ/leo"
keywords = [
"aleo",
"cryptography",
"leo",
"programming-language",
"zero-knowledge"
]
categories = [ "cryptography::cryptocurrencies", "web-programming" ]
include = ["Cargo.toml", "src", "README.md", "LICENSE.md" ]
license = "GPL-3.0"
edition = "2018"
[dependencies.leo-typed]
path = "../typed"
version = "1.0.3"
[dependencies.thiserror]
version = "1.0"

View File

@ -0,0 +1,28 @@
// Copyright (C) 2019-2020 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 <https://www.gnu.org/licenses/>.
use crate::{errors::ImportError, ImportParser};
use leo_typed::Package;
pub static CORE_PACKAGE_NAME: &str = "core";
impl ImportParser {
// import a core package into scope
pub fn parse_core_package(&mut self, package: &Package) -> Result<(), ImportError> {
self.insert_core_package(package);
Ok(())
}
}

View File

@ -0,0 +1,70 @@
// Copyright (C) 2019-2020 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 <https://www.gnu.org/licenses/>.
use crate::errors::ImportError;
use leo_typed::{Package, Program};
use std::{collections::HashMap, env::current_dir};
/// Parses all relevant import files for a program.
/// Stores compiled program structs.
#[derive(Clone)]
pub struct ImportParser {
imports: HashMap<String, Program>,
core_packages: Vec<Package>,
}
impl ImportParser {
pub fn new() -> Self {
Self {
imports: HashMap::new(),
core_packages: vec![],
}
}
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);
}
pub(crate) fn insert_core_package(&mut self, package: &Package) {
let _res = self.core_packages.push(package.clone());
}
pub fn get_import(&self, file_name: &String) -> Option<&Program> {
self.imports.get(file_name)
}
pub fn core_packages(&self) -> &Vec<Package> {
&self.core_packages
}
pub fn parse(program: &Program) -> Result<Self, ImportError> {
let mut imports = Self::new();
// Find all imports relative to current directory
let path = current_dir().map_err(|error| ImportError::current_directory_error(error))?;
// Parse each imported file
program
.imports
.iter()
.map(|import| imports.parse_package(path.clone(), &import.package))
.collect::<Result<Vec<()>, ImportError>>()?;
Ok(imports)
}
}

28
imports/src/lib.rs Normal file
View File

@ -0,0 +1,28 @@
// Copyright (C) 2019-2020 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 <https://www.gnu.org/licenses/>.
/// The import parser creates a hashmap of import program names -> import program structs
pub mod core_package;
pub use self::core_package::*;
pub mod parse_symbol;
pub use self::parse_symbol::*;
pub mod import_parser;
pub use self::import_parser::*;
pub mod parse_package;
pub use self::parse_package::*;

View File

@ -0,0 +1,115 @@
// Copyright (C) 2019-2020 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 <https://www.gnu.org/licenses/>.
use crate::{errors::ImportError, ImportParser, CORE_PACKAGE_NAME};
use leo_typed::{Package, PackageAccess};
use std::{fs, fs::DirEntry, path::PathBuf};
static SOURCE_FILE_EXTENSION: &str = ".leo";
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<(), ImportError> {
tracing::debug!("import {:?}", entry.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::Multiple(accesses) => {
for access in accesses {
self.parse_package_access(entry, access)?;
}
Ok(())
}
}
}
pub fn parse_package(&mut self, mut path: PathBuf, package: &Package) -> Result<(), ImportError> {
let error_path = path.clone();
let package_name = package.name.clone();
// Fetch a core package
let core_package = package_name.name.eq(CORE_PACKAGE_NAME);
// Trim path if importing from another file
if path.is_file() {
path.pop();
}
// Search for package name in local directory
let mut source_directory = path.clone();
source_directory.push(SOURCE_DIRECTORY_NAME);
// Search for package name in `imports` directory
let mut imports_directory = path.clone();
imports_directory.push(IMPORTS_DIRECTORY_NAME);
// Read from local `src` directory or the current path
if source_directory.exists() {
path = source_directory
}
let entries = fs::read_dir(path)
.map_err(|error| ImportError::directory_error(error, package_name.span.clone(), error_path.clone()))?
.into_iter()
.collect::<Result<Vec<_>, std::io::Error>>()
.map_err(|error| ImportError::directory_error(error, package_name.span.clone(), error_path.clone()))?;
let matched_source_entry = entries.into_iter().find(|entry| {
entry
.file_name()
.to_os_string()
.into_string()
.unwrap()
.trim_end_matches(SOURCE_FILE_EXTENSION)
.eq(&package_name.name)
});
if core_package {
// Enforce core library package access
self.parse_core_package(&package)
} else if imports_directory.exists() {
let entries = fs::read_dir(imports_directory)
.map_err(|error| ImportError::directory_error(error, package_name.span.clone(), error_path.clone()))?
.into_iter()
.collect::<Result<Vec<_>, std::io::Error>>()
.map_err(|error| ImportError::directory_error(error, package_name.span.clone(), error_path.clone()))?;
let matched_import_entry = entries
.into_iter()
.find(|entry| entry.file_name().into_string().unwrap().eq(&package_name.name));
match (matched_source_entry, matched_import_entry) {
(Some(_), Some(_)) => Err(ImportError::conflicting_imports(package_name)),
(Some(source_entry), None) => self.parse_package_access(&source_entry, &package.access),
(None, Some(import_entry)) => self.parse_package_access(&import_entry, &package.access),
(None, None) => Err(ImportError::unknown_package(package_name)),
}
} else {
// Enforce local package access with no found imports directory
match matched_source_entry {
Some(source_entry) => self.parse_package_access(&source_entry, &package.access),
None => Err(ImportError::unknown_package(package_name)),
}
}
}
}

124
imports/src/parse_symbol.rs Normal file
View File

@ -0,0 +1,124 @@
// Copyright (C) 2019-2020 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 <https://www.gnu.org/licenses/>.
use crate::{errors::ImportError, ImportParser};
use leo_ast::LeoAst;
use leo_typed::{ImportSymbol, Program, Span};
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, ImportError> {
// make sure the given entry is file
let file_type = entry
.file_type()
.map_err(|error| ImportError::directory_error(error, span.clone(), entry.path()))?;
let file_name = entry
.file_name()
.to_os_string()
.into_string()
.map_err(|_| ImportError::convert_os_string(span.clone()))?;
let mut file_path = entry.path().to_path_buf();
if file_type.is_dir() {
file_path.push(LIBRARY_FILE);
if !file_path.exists() {
return Err(ImportError::expected_lib_file(
format!("{:?}", file_path.as_path()),
span.clone(),
));
}
}
// Builds the 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.
Ok(Program::from(&file_name, ast.as_repr()))
}
impl ImportParser {
pub fn parse_import_star(&mut self, entry: &DirEntry, span: &Span) -> Result<(), ImportError> {
let path = entry.path();
let is_dir = path.is_dir();
let is_leo_file = path
.extension()
.map_or(false, |ext| ext.eq(&OsString::from(FILE_EXTENSION)));
let mut package_path = path.to_path_buf();
package_path.push(LIBRARY_FILE);
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)?;
// Store program's imports in imports hashmap
program
.imports
.iter()
.map(|import| self.parse_package(entry.path(), &import.package))
.collect::<Result<Vec<()>, ImportError>>()?;
// Store program in imports hashmap
let file_name_path = PathBuf::from(entry.file_name());
let file_name = file_name_path
.file_stem()
.unwrap()
.to_os_string()
.into_string()
.unwrap(); // the file exists so these will not fail
self.insert_import(file_name, program);
Ok(())
} else {
// importing * from a directory or non-leo file in `package/src/` is illegal
Err(ImportError::star(entry.path().to_path_buf(), span.clone()))
}
}
pub fn parse_import_symbol(&mut self, entry: &DirEntry, symbol: &ImportSymbol) -> Result<(), ImportError> {
// Generate aleo program from file
let program = parse_import_file(entry, &symbol.span)?;
// Store program's imports in imports hashmap
program
.imports
.iter()
.map(|import| self.parse_package(entry.path(), &import.package))
.collect::<Result<Vec<()>, ImportError>>()?;
// Store program in imports hashmap
let file_name_path = PathBuf::from(entry.file_name());
let file_name = file_name_path
.file_stem()
.unwrap()
.to_os_string()
.into_string()
.unwrap(); // the file exists so these will not fail
self.insert_import(file_name, program);
Ok(())
}
}