impl constraints and tests for multiple imports

This commit is contained in:
collin 2020-06-26 17:12:04 -07:00
parent 5900923d9d
commit a4a9ed05ea
13 changed files with 122 additions and 148 deletions

View File

@ -8,8 +8,8 @@ use pest_ast::FromPest;
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::package_access))]
pub enum PackageAccess<'ast> {
Star(Star),
Star(Star<'ast>),
SubPackage(Box<Package<'ast>>),
Multiple(Vec<Package<'ast>>),
Symbol(ImportSymbol<'ast>),
Multiple(Vec<PackageAccess<'ast>>),
}

View File

@ -1,7 +1,11 @@
use crate::ast::Rule;
use pest::Span;
use pest_ast::FromPest;
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::star))]
pub struct Star {}
pub struct Star<'ast> {
#[pest_ast(outer())]
pub span: Span<'ast>,
}

View File

@ -290,22 +290,22 @@ import = { "import" ~ package ~ LINE_END}
// Declared in imports/package.rs
package = { identifier ~ "." ~ package_access }
package_tuple = _{ "(" ~ package ~ ("," ~ NEWLINE* ~ package)* ~ ")" }
// Declared in imports/package_access
package_access = {
star
| package // subpackage
| package_tuple
| multiple_package_access
| import_symbol
}
multiple_package_access = _{ "(" ~ NEWLINE* ~ package_access ~ ("," ~ NEWLINE* ~ package_access)* ~ NEWLINE* ~ ")"}
// Declared in imports/star.rs
star = {"*"}
// Declared in imports/import_symbol.rs
import_symbol = { identifier ~ ("as" ~ identifier)? }
// import_tuple = _{}
/// Utilities

View File

@ -5,49 +5,61 @@ use crate::{
GroupType,
};
use leo_ast::LeoParser;
use leo_types::{Import, ImportSymbol, Package, PackageAccess, Program};
use leo_types::{Import, ImportSymbol, Package, PackageAccess, Program, Span};
use snarkos_models::curves::{Field, PrimeField};
use std::{env::current_dir, fs, fs::DirEntry, path::PathBuf};
pub(crate) static SOURCE_DIRECTORY_NAME: &str = "src/";
static SOURCE_FILE_EXTENSION: &str = ".leo";
static SOURCE_DIRECTORY_NAME: &str = "src/";
// pub(crate) static IMPORTS_DIRECTORY_NAME: &str = "imports/";
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()))?;
if file_type.is_dir() {
return Err(ImportError::expected_file(file_name, span.clone()));
}
// Build the abstract syntax tree
let file_path = &entry.path();
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_symbol(
&mut self,
scope: String,
entry: DirEntry,
symbol: ImportSymbol,
) -> Result<(), ImportError> {
// make sure the given entry is file
let file_type = entry
.file_type()
.map_err(|error| ImportError::directory_error(error, symbol.span.clone()))?;
let file_name = entry
.file_name()
.into_string()
.map_err(|_| ImportError::convert_os_string(symbol.span.clone()))?;
if file_type.is_dir() {
return Err(ImportError::expected_file(file_name, symbol));
}
// Build the abstract syntax tree
let file_path = &entry.path();
let input_file = &LeoParser::load_file(file_path)?;
let syntax_tree = LeoParser::parse_file(file_path, input_file)?;
// Generate aleo program from file
let mut program = Program::from(syntax_tree, file_name.clone());
pub fn enforce_import_star(&mut self, scope: String, entry: &DirEntry, span: Span) -> Result<(), ImportError> {
let mut program = parse_import_file(entry, &span)?;
// Use same namespace as calling function for imported symbols
program = program.name(scope);
// * -> import all imports, circuits, functions in the current scope
// if import.is_star() {
// // recursively evaluate program statements
// self.resolve_definitions(program)
// } else {
self.resolve_definitions(program)
}
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();
// match each import symbol to a symbol in the imported file
@ -71,7 +83,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
match matched_function {
Some((_function_name, function)) => ConstrainedValue::Function(None, function),
None => return Err(ImportError::unknown_symbol(symbol, program_name, file_path)),
None => return Err(ImportError::unknown_symbol(symbol, program_name, &entry.path())),
}
}
};
@ -98,18 +110,18 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
pub fn enforce_package_access(
&mut self,
scope: String,
entry: DirEntry,
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 => unimplemented!("star not impl"),
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::Multiple(packages) => {
for package in packages {
self.enforce_package(scope.clone(), entry.path(), package)?;
PackageAccess::Multiple(accesses) => {
for access in accesses {
self.enforce_package_access(scope.clone(), entry, access)?;
}
Ok(())
@ -130,9 +142,14 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
.collect::<Result<Vec<_>, std::io::Error>>()
.map_err(|error| ImportError::directory_error(error, package_name.span.clone()))?;
let matched_source_entry = entries
.into_iter()
.find(|entry| entry.file_name().into_string().unwrap().eq(&package_name.name));
let matched_source_entry = entries.into_iter().find(|entry| {
entry
.file_name()
.into_string()
.unwrap()
.trim_end_matches(SOURCE_FILE_EXTENSION)
.eq(&package_name.name)
});
// search for package name in imports directory
// let mut source_directory = path.clone();
@ -152,90 +169,15 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
// Enforce package access
if let Some(entry) = matched_source_entry {
self.enforce_package_access(scope, entry, package.access)?;
self.enforce_package_access(scope, &entry, package.access)
} else {
Err(ImportError::unknown_package(package_name))
}
Ok(())
}
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)
//
// // Sanitize the package path to the imports directory
// let mut package_path = path.clone();
// if package_path.is_file() {
// package_path.pop();
// }
//
// // Construct the path to the import file in the import directory
// let mut main_file_path = package_path.clone();
// main_file_path.push(import.path_string_full());
//
// println!("Compiling import - {:?}", main_file_path);
//
// // Build the abstract syntax tree
// let file_path = &main_file_path;
// let input_file = &LeoParser::load_file(file_path)?;
// let syntax_tree = LeoParser::parse_file(file_path, input_file)?;
//
// // Generate aleo program from file
// let mut program = Program::from(syntax_tree, import.path_string.clone());
//
// // Use same namespace as calling function for imported symbols
// program = program.name(scope);
//
// // * -> import all imports, circuits, functions in the current scope
// if import.is_star() {
// // recursively evaluate program statements
// self.resolve_definitions(program)
// } else {
// let program_name = program.name.clone();
//
// // match each import symbol to a symbol in the imported file
// for symbol in import.symbols.into_iter() {
// // 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.name == *function_name.name);
//
// match matched_function {
// Some((_function_name, function)) => ConstrainedValue::Function(None, function),
// None => return Err(ImportError::unknown_symbol(symbol, program_name, file_path)),
// }
// }
// };
//
// // take the alias if it is present
// let resolved_name = symbol.alias.unwrap_or(symbol.symbol);
// let resolved_circuit_name = new_scope(program_name.clone(), resolved_name.to_string());
//
// // store imported circuit under resolved name
// self.store(resolved_circuit_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(())
// }
}
}

View File

@ -1,5 +1,5 @@
use leo_ast::ParserError;
use leo_types::{Error as FormattedError, ImportSymbol, Span};
use leo_types::{Error as FormattedError, Identifier, ImportSymbol, Span};
use std::{io, path::PathBuf};
@ -29,10 +29,19 @@ impl ImportError {
Self::new_from_span(message, span)
}
pub fn expected_file(entry: String, symbol: ImportSymbol) -> Self {
let message = format!("cannot import symbol `{}` from directory `{}`", symbol, entry);
pub fn expected_file(entry: String, span: Span) -> Self {
let message = format!("cannot import symbol `{}` from directory `{}`", span.text, entry);
Self::new_from_span(message, symbol.span)
Self::new_from_span(message, span)
}
pub fn unknown_package(identifier: Identifier) -> Self {
let message = format!(
"cannot find imported package `{}` in source files or import directory",
identifier.name
);
Self::new_from_span(message, identifier.span)
}
pub fn unknown_symbol(symbol: ImportSymbol, file: String, file_path: &PathBuf) -> Self {

View File

@ -1,4 +1,4 @@
from "tests/import/test_import" import foo as bar;
import test_import.foo as bar;
function main() -> u32 {
return bar()

View File

@ -1,4 +1,4 @@
from "tests/import/test_import" import foo;
import test_import.foo;
function main() -> u32 {
return foo()

View File

@ -1,13 +1,26 @@
use crate::{integers::u32::output_one, parse_program};
use std::env::{current_dir, set_current_dir};
static TEST_SOURCE_DIRECTORY: &str = "tests/import";
// Import tests rely on knowledge of local directories. They should be run locally only.
fn set_local_dir() {
let mut local = current_dir().unwrap();
local.push(TEST_SOURCE_DIRECTORY);
set_current_dir(local).unwrap();
}
#[test]
#[ignore]
fn test_basic() {
let bytes = include_bytes!("basic.leo");
let program = parse_program(bytes).unwrap();
set_local_dir();
output_one(program);
}
@ -17,6 +30,8 @@ fn test_multiple() {
let bytes = include_bytes!("multiple.leo");
let program = parse_program(bytes).unwrap();
set_local_dir();
output_one(program);
}
@ -26,6 +41,8 @@ fn test_star() {
let bytes = include_bytes!("star.leo");
let program = parse_program(bytes).unwrap();
set_local_dir();
output_one(program);
}
@ -35,5 +52,7 @@ fn test_alias() {
let bytes = include_bytes!("alias.leo");
let program = parse_program(bytes).unwrap();
set_local_dir();
output_one(program);
}

View File

@ -1,9 +1,9 @@
from "tests/import/test_import" import {
import test_import.(
Point,
foo
};
);
function main() -> u32 {
let p = Point { x: 1u32, y: 0u32 };
return foo()
return p.x
}

View File

@ -1,4 +1,4 @@
from "tests/import/test_import" import *;
import test_import.*;
function main() -> u32 {
let p = Point { x: 1u32, y: 0u32 };

View File

@ -1,6 +1,6 @@
//! The import type for a Leo program.
use crate::{ImportSymbol, Package, Span};
use crate::{Package, Span};
use leo_ast::imports::Import as AstImport;
use serde::{Deserialize, Serialize};

View File

@ -1,4 +1,4 @@
use crate::{ImportSymbol, Package};
use crate::{ImportSymbol, Package, Span};
use leo_ast::imports::PackageAccess as AstPackageAccess;
use serde::{Deserialize, Serialize};
@ -6,21 +6,21 @@ use std::fmt;
#[derive(Clone, Serialize, Deserialize)]
pub enum PackageAccess {
Star,
Star(Span),
SubPackage(Box<Package>),
Multiple(Vec<Package>),
Symbol(ImportSymbol),
Multiple(Vec<PackageAccess>),
}
impl<'ast> From<AstPackageAccess<'ast>> for PackageAccess {
fn from(access: AstPackageAccess<'ast>) -> Self {
match access {
AstPackageAccess::Star(_) => PackageAccess::Star,
AstPackageAccess::Star(star) => PackageAccess::Star(Span::from(star.span)),
AstPackageAccess::SubPackage(package) => PackageAccess::SubPackage(Box::new(Package::from(*package))),
AstPackageAccess::Multiple(packages) => {
PackageAccess::Multiple(packages.into_iter().map(|package| Package::from(package)).collect())
}
AstPackageAccess::Symbol(symbol) => PackageAccess::Symbol(ImportSymbol::from(symbol)),
AstPackageAccess::Multiple(accesses) => {
PackageAccess::Multiple(accesses.into_iter().map(|access| PackageAccess::from(access)).collect())
}
}
}
}
@ -28,19 +28,19 @@ impl<'ast> From<AstPackageAccess<'ast>> for PackageAccess {
impl PackageAccess {
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
PackageAccess::Star => write!(f, ".*"),
PackageAccess::Star(ref _span) => write!(f, ".*"),
PackageAccess::SubPackage(ref package) => write!(f, ".{}", package),
PackageAccess::Multiple(ref packages) => {
PackageAccess::Symbol(ref symbol) => write!(f, ".{}", symbol),
PackageAccess::Multiple(ref accesses) => {
write!(f, ".(")?;
for (i, package) in packages.iter().enumerate() {
write!(f, "{}", package)?;
if i < packages.len() - 1 {
for (i, access) in accesses.iter().enumerate() {
write!(f, "{}", access)?;
if i < accesses.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, ")")
}
PackageAccess::Symbol(ref symbol) => write!(f, ".{}", symbol),
}
}
}