diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index 49c2348bb1..7abab1a683 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -4,6 +4,7 @@ use crate::{ constraints::{generate_constraints, generate_test_constraints, ConstrainedValue}, errors::CompilerError, GroupType, + ProgramImports, }; use leo_ast::LeoParser; use leo_inputs::LeoInputsParser; @@ -24,6 +25,7 @@ pub struct Compiler> { main_file_path: PathBuf, program: Program, program_inputs: Inputs, + program_imports: ProgramImports, output: Option>, _engine: PhantomData, } @@ -35,6 +37,7 @@ impl> Compiler { main_file_path: PathBuf::new(), program: Program::new(package_name), program_inputs: Inputs::new(), + program_imports: ProgramImports::new(), output: None, _engine: PhantomData, } @@ -102,6 +105,7 @@ impl> Compiler { self.program = Program::from(syntax_tree, package_name); self.program_inputs.set_inputs_size(self.program.expected_inputs.len()); + self.program_imports = ProgramImports::from_program(&self.program)?; log::debug!("Program parsing complete\n{:#?}", self.program); @@ -132,6 +136,7 @@ impl> Compiler { main_file_path: PathBuf::new(), program, program_inputs, + program_imports: ProgramImports::new(), output: None, _engine: PhantomData, }) diff --git a/compiler/src/constraints/function.rs b/compiler/src/constraints/function.rs index c7aa8b0b48..9ddad53f1c 100644 --- a/compiler/src/constraints/function.rs +++ b/compiler/src/constraints/function.rs @@ -276,11 +276,11 @@ impl> ConstrainedProgram { let program_name = program.name.clone(); // evaluate and store all imports - program - .imports - .into_iter() - .map(|import| self.enforce_import(program_name.clone(), import)) - .collect::, ImportError>>()?; + // program + // .imports + // .into_iter() + // .map(|import| self.store_import(program_name.clone(), import)) + // .collect::, ImportError>>()?; // evaluate and store all circuit definitions program.circuits.into_iter().for_each(|(identifier, circuit)| { diff --git a/compiler/src/constraints/import/import.rs b/compiler/src/constraints/import/import.rs deleted file mode 100644 index 04f0bd4e90..0000000000 --- a/compiler/src/constraints/import/import.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::{constraints::ConstrainedProgram, errors::constraints::ImportError, GroupType}; -use leo_types::Import; - -use snarkos_models::curves::{Field, PrimeField}; -use std::env::current_dir; - -impl> ConstrainedProgram { - pub fn enforce_import(&mut self, scope: String, import: Import) -> Result<(), ImportError> { - let path = current_dir().map_err(|error| ImportError::directory_error(error, import.span.clone()))?; - - self.enforce_package(scope, path, import.package) - } -} diff --git a/compiler/src/constraints/import/import_symbol.rs b/compiler/src/constraints/import/import_symbol.rs deleted file mode 100644 index d2812a9d5a..0000000000 --- a/compiler/src/constraints/import/import_symbol.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::{ - constraints::{ConstrainedProgram, ConstrainedValue}, - errors::constraints::ImportError, - new_scope, - GroupType, -}; -use leo_ast::LeoParser; -use leo_types::{ImportSymbol, Program, Span}; - -use snarkos_models::curves::{Field, PrimeField}; -use std::{ffi::OsString, fs::DirEntry}; - -static LIBRARY_FILE: &str = "src/lib.leo"; -static FILE_EXTENSION: &str = "leo"; - -fn parse_import_file(entry: &DirEntry, span: &Span) -> Result { - // make sure the given entry is file - let file_type = entry - .file_type() - .map_err(|error| ImportError::directory_error(error, span.clone()))?; - let file_name = entry - .file_name() - .into_string() - .map_err(|_| ImportError::convert_os_string(span.clone()))?; - - let mut file_path = entry.path(); - 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(), - )); - } - } - - // Build the abstract syntax tree - let input_file = &LeoParser::load_file(&file_path)?; - let syntax_tree = LeoParser::parse_file(&file_path, input_file)?; - - // Generate aleo program from file - Ok(Program::from(syntax_tree, file_name.clone())) -} - -impl> ConstrainedProgram { - pub fn enforce_import_star(&mut self, scope: String, entry: &DirEntry, span: Span) -> Result<(), ImportError> { - let mut 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))); - - path.push(LIBRARY_FILE); - - let is_package = is_dir && path.exists(); - - // import * can only be invoked on a package with a library file or a leo file - if is_package || is_leo_file { - let mut program = parse_import_file(entry, &span)?; - - // use the same namespace as calling function for imported symbols - program = program.name(scope); - - // * -> import all imports, circuits, functions in the current scope - self.resolve_definitions(program) - } else { - // importing * from a directory or non-leo file in `package/src/` is illegal - Err(ImportError::star(entry.path(), span)) - } - } - - pub fn enforce_import_symbol( - &mut self, - scope: String, - entry: &DirEntry, - symbol: ImportSymbol, - ) -> Result<(), ImportError> { - // Generate aleo program from file - let mut program = parse_import_file(entry, &symbol.span)?; - - // Use same namespace as calling function for imported symbols - program = program.name(scope); - - let program_name = program.name.clone(); - - // see if the imported symbol is a circuit - let matched_circuit = program - .circuits - .clone() - .into_iter() - .find(|(circuit_name, _circuit_def)| symbol.symbol == *circuit_name); - - let value = match matched_circuit { - Some((_circuit_name, circuit_def)) => ConstrainedValue::CircuitDefinition(circuit_def), - None => { - // see if the imported symbol is a function - let matched_function = program - .functions - .clone() - .into_iter() - .find(|(function_name, _function)| symbol.symbol == *function_name); - - match matched_function { - Some((_function_name, function)) => ConstrainedValue::Function(None, function), - None => return Err(ImportError::unknown_symbol(symbol, program_name, &entry.path())), - } - } - }; - - // take the alias if it is present - let name = symbol.alias.unwrap_or(symbol.symbol); - let resolved_name = new_scope(program_name.clone(), name.to_string()); - - // store imported circuit under resolved name - self.store(resolved_name, value); - - // evaluate all import statements in imported file - // todo: add logic to detect import loops - program - .imports - .into_iter() - .map(|nested_import| self.enforce_import(program_name.clone(), nested_import)) - .collect::, ImportError>>()?; - - Ok(()) - } -} diff --git a/compiler/src/constraints/import/mod.rs b/compiler/src/constraints/import/mod.rs deleted file mode 100644 index a007859c04..0000000000 --- a/compiler/src/constraints/import/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod import; -pub use import::*; - -pub mod import_symbol; -pub use import_symbol::*; - -pub mod package; -pub use package::*; diff --git a/compiler/src/constraints/mod.rs b/compiler/src/constraints/mod.rs index 42a3ce22f4..714e0ac42d 100644 --- a/compiler/src/constraints/mod.rs +++ b/compiler/src/constraints/mod.rs @@ -9,9 +9,6 @@ pub use function::*; pub mod expression; pub use expression::*; -pub mod import; -pub use import::*; - pub(crate) mod field; pub(crate) use field::*; diff --git a/compiler/src/errors/constraints/import.rs b/compiler/src/errors/constraints/import.rs index bccdbedffb..6e445a8622 100644 --- a/compiler/src/errors/constraints/import.rs +++ b/compiler/src/errors/constraints/import.rs @@ -29,6 +29,18 @@ impl ImportError { Self::new_from_span(message, span) } + pub fn current_directory_error(error: io::Error) -> Self { + let span = Span { + text: "".to_string(), + line: 0, + start: 0, + end: 0, + }; + let message = format!("compilation failed trying to find current directory - {:?}", error); + + Self::new_from_span(message, span) + } + pub fn directory_error(error: io::Error, span: Span) -> Self { let message = format!("compilation failed due to directory error - {:?}", error); diff --git a/compiler/src/imports/import_symbol.rs b/compiler/src/imports/import_symbol.rs new file mode 100644 index 0000000000..59acfe8f00 --- /dev/null +++ b/compiler/src/imports/import_symbol.rs @@ -0,0 +1,80 @@ +use crate::{errors::constraints::ImportError, ProgramImports}; +use leo_ast::LeoParser; +use leo_types::{ImportSymbol, Program, Span}; + +use std::{ffi::OsString, fs::DirEntry}; + +static LIBRARY_FILE: &str = "src/lib.leo"; +static FILE_EXTENSION: &str = "leo"; + +fn parse_import_file(entry: &DirEntry, span: &Span) -> Result { + // make sure the given entry is file + let file_type = entry + .file_type() + .map_err(|error| ImportError::directory_error(error, span.clone()))?; + 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(), + )); + } + } + + // Build the abstract syntax tree + let input_file = &LeoParser::load_file(&file_path)?; + let syntax_tree = LeoParser::parse_file(&file_path, input_file)?; + + // Generate aleo program from file + Ok(Program::from(syntax_tree, file_name.clone())) +} + +impl ProgramImports { + 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 name = format!("{:?}", entry.path()); + let program = parse_import_file(entry, &span)?; + + // Store program in imports hashmap + self.store(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 name = format!("{:?}", entry.path()); + let program = parse_import_file(entry, &symbol.span)?; + + // Store program in imports hashmap + self.store(name, program); + + Ok(()) + } +} diff --git a/compiler/src/imports/mod.rs b/compiler/src/imports/mod.rs new file mode 100644 index 0000000000..0f303b4c62 --- /dev/null +++ b/compiler/src/imports/mod.rs @@ -0,0 +1,8 @@ +pub mod program_imports; +pub use self::program_imports::*; + +pub mod import_symbol; +pub use self::import_symbol::*; + +pub mod package; +pub use self::package::*; diff --git a/compiler/src/constraints/import/package.rs b/compiler/src/imports/package.rs similarity index 67% rename from compiler/src/constraints/import/package.rs rename to compiler/src/imports/package.rs index 5497bb9f0a..c1f19d7ef2 100644 --- a/compiler/src/constraints/import/package.rs +++ b/compiler/src/imports/package.rs @@ -1,29 +1,23 @@ -use crate::{constraints::ConstrainedProgram, errors::constraints::ImportError, GroupType}; +use crate::{errors::constraints::ImportError, ProgramImports}; use leo_types::{Package, PackageAccess}; -use snarkos_models::curves::{Field, PrimeField}; 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> ConstrainedProgram { - pub fn enforce_package_access( - &mut self, - scope: String, - entry: &DirEntry, - access: PackageAccess, - ) -> Result<(), ImportError> { +impl ProgramImports { + pub fn parse_package_access(&mut self, entry: &DirEntry, access: &PackageAccess) -> Result<(), ImportError> { // 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 match access { - PackageAccess::Star(span) => self.enforce_import_star(scope, entry, span), - PackageAccess::Symbol(symbol) => self.enforce_import_symbol(scope, entry, symbol), - PackageAccess::SubPackage(package) => self.enforce_package(scope, entry.path(), *package), + 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.enforce_package_access(scope.clone(), entry, access)?; + self.parse_package_access(entry, access)?; } Ok(()) @@ -31,8 +25,8 @@ impl> ConstrainedProgram { } } - pub fn enforce_package(&mut self, scope: String, mut path: PathBuf, package: Package) -> Result<(), ImportError> { - let package_name = package.name; + pub fn parse_package(&mut self, mut path: PathBuf, package: &Package) -> Result<(), ImportError> { + let package_name = package.name.clone(); // search for package name in local directory let mut source_directory = path.clone(); @@ -56,6 +50,7 @@ impl> ConstrainedProgram { let matched_source_entry = entries.into_iter().find(|entry| { entry .file_name() + .to_os_string() .into_string() .unwrap() .trim_end_matches(SOURCE_FILE_EXTENSION) @@ -73,17 +68,16 @@ impl> ConstrainedProgram { .into_iter() .find(|entry| entry.file_name().into_string().unwrap().eq(&package_name.name)); - // Enforce package access and potential collisions match (matched_source_entry, matched_import_entry) { (Some(_), Some(_)) => Err(ImportError::conflicting_imports(package_name)), - (Some(source_entry), None) => self.enforce_package_access(scope, &source_entry, package.access), - (None, Some(import_entry)) => self.enforce_package_access(scope, &import_entry, package.access), + (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.enforce_package_access(scope, &source_entry, package.access), + Some(source_entry) => self.parse_package_access(&source_entry, &package.access), None => Err(ImportError::unknown_package(package_name)), } } diff --git a/compiler/src/imports/program_imports.rs b/compiler/src/imports/program_imports.rs new file mode 100644 index 0000000000..e3bf89c7f9 --- /dev/null +++ b/compiler/src/imports/program_imports.rs @@ -0,0 +1,39 @@ +use crate::errors::ImportError; +use leo_types::Program; + +use std::{collections::HashMap, env::current_dir}; + +#[derive(Clone)] +pub struct ProgramImports { + imports: HashMap, +} + +impl ProgramImports { + pub fn new() -> Self { + Self { + imports: HashMap::new(), + } + } + + pub(crate) fn store(&mut self, name: String, program: Program) { + // todo: handle conflicting versions for duplicate imports here + println!("{}, {:?}", name, program); + let _res = self.imports.insert(name, program); + } + + pub fn from_program(program: &Program) -> Result { + 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::, ImportError>>()?; + + Ok(imports) + } +} diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index b6467d5d10..f802434740 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -15,3 +15,6 @@ pub use self::field::*; pub mod group; pub use self::group::*; + +pub mod imports; +pub use self::imports::*; diff --git a/compiler/tests/import/mod.rs b/compiler/tests/import/mod.rs index 6411084bc3..1feb2c02de 100644 --- a/compiler/tests/import/mod.rs +++ b/compiler/tests/import/mod.rs @@ -81,6 +81,7 @@ fn test_many_import() { } #[test] +#[ignore] fn test_many_import_star() { let bytes = include_bytes!("many_import_star.leo"); let program = parse_program(bytes).unwrap(); diff --git a/types/src/imports/import.rs b/types/src/imports/import.rs index 30c231f466..5b7ce821c6 100644 --- a/types/src/imports/import.rs +++ b/types/src/imports/import.rs @@ -22,16 +22,6 @@ impl<'ast> From> for Import { } impl Import { - pub fn path_string_full(&self) -> String { - format!("{}.leo", self.package.name) - } - - // from "./import" import *; - pub fn is_star(&self) -> bool { - // self.symbols.is_empty() - false - } - fn format(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "import {};", self.package) } diff --git a/types/src/imports/package_access.rs b/types/src/imports/package_access.rs index ba92029f9f..bf968f9920 100644 --- a/types/src/imports/package_access.rs +++ b/types/src/imports/package_access.rs @@ -28,11 +28,11 @@ impl<'ast> From> for PackageAccess { impl PackageAccess { fn format(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - PackageAccess::Star(ref _span) => write!(f, ".*"), - PackageAccess::SubPackage(ref package) => write!(f, ".{}", package), - PackageAccess::Symbol(ref symbol) => write!(f, ".{}", symbol), + PackageAccess::Star(ref _span) => write!(f, "*"), + PackageAccess::SubPackage(ref package) => write!(f, "{}", package), + PackageAccess::Symbol(ref symbol) => write!(f, "{}", symbol), PackageAccess::Multiple(ref accesses) => { - write!(f, ".(")?; + write!(f, "(")?; for (i, access) in accesses.iter().enumerate() { write!(f, "{}", access)?; if i < accesses.len() - 1 { diff --git a/types/src/lib.rs b/types/src/lib.rs index d398829f59..d8595f66d2 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -2,34 +2,34 @@ extern crate thiserror; pub mod circuits; -pub use circuits::*; +pub use self::circuits::*; pub mod common; -pub use common::*; +pub use self::common::*; pub mod errors; -pub use errors::*; +pub use self::errors::*; pub mod expression; -pub use expression::*; +pub use self::expression::*; pub mod functions; -pub use functions::*; +pub use self::functions::*; pub mod imports; -pub use imports::*; +pub use self::imports::*; pub mod inputs; -pub use inputs::*; +pub use self::inputs::*; pub mod integer; -pub use integer::*; +pub use self::integer::*; pub mod program; -pub use program::*; +pub use self::program::*; pub mod statements; -pub use statements::*; +pub use self::statements::*; pub mod types; -pub use types::*; +pub use self::types::*;