mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-24 07:48:04 +03:00
refactor import file parsing. constraints for imports wip
This commit is contained in:
parent
330bfe46c8
commit
6a04dd0f58
@ -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<F: Field + PrimeField, G: GroupType<F>> {
|
||||
main_file_path: PathBuf,
|
||||
program: Program,
|
||||
program_inputs: Inputs,
|
||||
program_imports: ProgramImports,
|
||||
output: Option<ConstrainedValue<F, G>>,
|
||||
_engine: PhantomData<F>,
|
||||
}
|
||||
@ -35,6 +37,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
|
||||
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<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
|
||||
|
||||
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<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
|
||||
main_file_path: PathBuf::new(),
|
||||
program,
|
||||
program_inputs,
|
||||
program_imports: ProgramImports::new(),
|
||||
output: None,
|
||||
_engine: PhantomData,
|
||||
})
|
||||
|
@ -276,11 +276,11 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
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::<Result<Vec<_>, ImportError>>()?;
|
||||
// program
|
||||
// .imports
|
||||
// .into_iter()
|
||||
// .map(|import| self.store_import(program_name.clone(), import))
|
||||
// .collect::<Result<Vec<_>, ImportError>>()?;
|
||||
|
||||
// evaluate and store all circuit definitions
|
||||
program.circuits.into_iter().for_each(|(identifier, circuit)| {
|
||||
|
@ -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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
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)
|
||||
}
|
||||
}
|
@ -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<Program, ImportError> {
|
||||
// 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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
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::<Result<Vec<_>, ImportError>>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
pub mod import;
|
||||
pub use import::*;
|
||||
|
||||
pub mod import_symbol;
|
||||
pub use import_symbol::*;
|
||||
|
||||
pub mod package;
|
||||
pub use package::*;
|
@ -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::*;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
80
compiler/src/imports/import_symbol.rs
Normal file
80
compiler/src/imports/import_symbol.rs
Normal file
@ -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<Program, ImportError> {
|
||||
// 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(())
|
||||
}
|
||||
}
|
8
compiler/src/imports/mod.rs
Normal file
8
compiler/src/imports/mod.rs
Normal file
@ -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::*;
|
@ -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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
}
|
||||
}
|
||||
|
||||
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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
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<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||
.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)),
|
||||
}
|
||||
}
|
39
compiler/src/imports/program_imports.rs
Normal file
39
compiler/src/imports/program_imports.rs
Normal file
@ -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<String, Program>,
|
||||
}
|
||||
|
||||
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<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)
|
||||
}
|
||||
}
|
@ -15,3 +15,6 @@ pub use self::field::*;
|
||||
|
||||
pub mod group;
|
||||
pub use self::group::*;
|
||||
|
||||
pub mod imports;
|
||||
pub use self::imports::*;
|
||||
|
@ -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();
|
||||
|
@ -22,16 +22,6 @@ impl<'ast> From<AstImport<'ast>> 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)
|
||||
}
|
||||
|
@ -28,11 +28,11 @@ impl<'ast> From<AstPackageAccess<'ast>> 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 {
|
||||
|
@ -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::*;
|
||||
|
Loading…
Reference in New Issue
Block a user