1
0
Fork 0
mirror of synced 2025-09-23 20:28:36 +00:00

Merge pull request #565 from Zokrates/fix-import-context

Fix import context
This commit is contained in:
Thibaut Schaeffer 2020-02-17 23:39:32 +01:00 committed by GitHub
commit cf8600e608
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 607 additions and 510 deletions

View file

@ -13,7 +13,7 @@ use std::io::{stdin, BufReader, BufWriter, Read, Write};
use std::path::{Path, PathBuf};
use std::string::String;
use zokrates_abi::Encode;
use zokrates_core::compile::{compile, CompilationArtifacts};
use zokrates_core::compile::{compile, CompilationArtifacts, CompileError};
use zokrates_core::ir;
use zokrates_core::proof_system::*;
use zokrates_core::typed_absy::abi::Abi;
@ -271,14 +271,6 @@ fn cli() -> Result<(), String> {
let path = PathBuf::from(sub_matches.value_of("input").unwrap());
let location = path
.parent()
.unwrap()
.to_path_buf()
.into_os_string()
.into_string()
.unwrap();
let light = sub_matches.occurrences_of("light") > 0;
let bin_output_path = Path::new(sub_matches.value_of("output").unwrap());
@ -287,17 +279,36 @@ fn cli() -> Result<(), String> {
let hr_output_path = bin_output_path.to_path_buf().with_extension("ztf");
let file = File::open(path.clone()).unwrap();
let file = File::open(path.clone())
.map_err(|why| format!("Couldn't open input file {}: {}", path.display(), why))?;
let mut reader = BufReader::new(file);
let mut source = String::new();
reader
.read_to_string(&mut source)
.map_err(|why| format!("couldn't open input file {}: {}", path.display(), why))?;
reader.read_to_string(&mut source).unwrap();
let fmt_error = |e: &CompileError| {
format!(
"{}:{}",
e.file()
.canonicalize()
.unwrap()
.strip_prefix(std::env::current_dir().unwrap())
.unwrap()
.display(),
e.value()
)
};
let artifacts: CompilationArtifacts<FieldPrime> =
compile(source, location, Some(&fs_resolve))
.map_err(|e| format!("Compilation failed:\n\n {}", e))?;
compile(source, path, Some(&fs_resolve)).map_err(|e| {
format!(
"Compilation failed:\n\n{}",
e.0.iter()
.map(|e| fmt_error(e))
.collect::<Vec<_>>()
.join("\n\n")
)
})?;
let program_flattened = artifacts.prog();
@ -306,7 +317,7 @@ fn cli() -> Result<(), String> {
// serialize flattened program and write to binary file
let bin_output_file = File::create(&bin_output_path)
.map_err(|why| format!("couldn't create {}: {}", bin_output_path.display(), why))?;
.map_err(|why| format!("Couldn't create {}: {}", bin_output_path.display(), why))?;
let mut writer = BufWriter::new(bin_output_file);
@ -315,7 +326,7 @@ fn cli() -> Result<(), String> {
// serialize ABI spec and write to JSON file
let abi_spec_file = File::create(&abi_spec_path)
.map_err(|why| format!("couldn't create {}: {}", abi_spec_path.display(), why))?;
.map_err(|why| format!("Couldn't create {}: {}", abi_spec_path.display(), why))?;
let abi = artifacts.abi();
@ -666,19 +677,12 @@ mod tests {
let file = File::open(path.clone()).unwrap();
let mut reader = BufReader::new(file);
let location = path
.parent()
.unwrap()
.to_path_buf()
.into_os_string()
.into_string()
.unwrap();
let mut source = String::new();
reader.read_to_string(&mut source).unwrap();
let _: CompilationArtifacts<FieldPrime> =
compile(source, location, Some(&fs_resolve)).unwrap();
compile(source, path, Some(&fs_resolve)).unwrap();
}
}
@ -694,20 +698,12 @@ mod tests {
let file = File::open(path.clone()).unwrap();
let location = path
.parent()
.unwrap()
.to_path_buf()
.into_os_string()
.into_string()
.unwrap();
let mut reader = BufReader::new(file);
let mut source = String::new();
reader.read_to_string(&mut source).unwrap();
let artifacts: CompilationArtifacts<FieldPrime> =
compile(source, location, Some(&fs_resolve)).unwrap();
compile(source, path, Some(&fs_resolve)).unwrap();
let _ = artifacts
.prog()
@ -729,20 +725,12 @@ mod tests {
let file = File::open(path.clone()).unwrap();
let location = path
.parent()
.unwrap()
.to_path_buf()
.into_os_string()
.into_string()
.unwrap();
let mut reader = BufReader::new(file);
let mut source = String::new();
reader.read_to_string(&mut source).unwrap();
let artifacts: CompilationArtifacts<FieldPrime> =
compile(source, location, Some(&fs_resolve)).unwrap();
compile(source, path, Some(&fs_resolve)).unwrap();
let _ = artifacts
.prog()

View file

@ -25,13 +25,13 @@ impl<'ast> From<pest::ImportDirective<'ast>> for absy::ImportNode<'ast> {
match import {
pest::ImportDirective::Main(import) => {
imports::Import::new(None, import.source.span.as_str())
imports::Import::new(None, std::path::Path::new(import.source.span.as_str()))
.alias(import.alias.map(|a| a.span.as_str()))
.span(import.span)
}
pest::ImportDirective::From(import) => imports::Import::new(
Some(import.symbol.span.as_str()),
import.source.span.as_str(),
std::path::Path::new(import.source.span.as_str()),
)
.alias(import.alias.map(|a| a.span.as_str()))
.span(import.span),

View file

@ -16,6 +16,7 @@ pub use crate::absy::parameter::{Parameter, ParameterNode};
use crate::absy::types::{FunctionIdentifier, UnresolvedSignature, UnresolvedType, UserTypeId};
pub use crate::absy::variable::{Variable, VariableNode};
use embed::FlatEmbed;
use std::path::PathBuf;
use crate::imports::ImportNode;
use std::fmt;
@ -27,7 +28,7 @@ use std::collections::HashMap;
pub type Identifier<'ast> = &'ast str;
/// The identifier of a `Module`, typically a path or uri
pub type ModuleId = String;
pub type ModuleId = PathBuf;
/// A collection of `Module`s
pub type Modules<'ast, T> = HashMap<ModuleId, Module<'ast, T>>;
@ -160,7 +161,12 @@ impl<'ast> SymbolImport<'ast> {
impl<'ast> fmt::Display for SymbolImport<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} from {}", self.symbol_id, self.module_id)
write!(
f,
"{} from {}",
self.symbol_id,
self.module_id.display().to_string()
)
}
}

View file

@ -13,6 +13,7 @@ use static_analysis::Analyse;
use std::collections::HashMap;
use std::fmt;
use std::io;
use std::path::PathBuf;
use typed_absy::abi::Abi;
use typed_arena::Arena;
use zokrates_field::field::Field;
@ -35,7 +36,7 @@ impl<T: Field> CompilationArtifacts<T> {
}
#[derive(Debug)]
pub struct CompileErrors(Vec<CompileError>);
pub struct CompileErrors(pub Vec<CompileError>);
impl From<CompileError> for CompileErrors {
fn from(e: CompileError) -> CompileErrors {
@ -43,50 +44,46 @@ impl From<CompileError> for CompileErrors {
}
}
impl fmt::Display for CompileErrors {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
self.0
.iter()
.map(|e| format!("{}", e))
.collect::<Vec<_>>()
.join("\n\n")
)
}
}
#[derive(Debug)]
pub enum CompileErrorInner {
ParserError(pest::Error),
ImportError(imports::Error),
SemanticError(semantics::Error),
SemanticError(semantics::ErrorInner),
ReadError(io::Error),
}
impl CompileErrorInner {
pub fn with_context(self, context: &String) -> CompileError {
pub fn in_file(self, context: &PathBuf) -> CompileError {
CompileError {
value: self,
context: context.clone(),
file: context.clone(),
}
}
}
#[derive(Debug)]
pub struct CompileError {
context: String,
file: PathBuf,
value: CompileErrorInner,
}
impl CompileError {
pub fn file(&self) -> &PathBuf {
&self.file
}
pub fn value(&self) -> &CompileErrorInner {
&self.value
}
}
impl CompileErrors {
pub fn with_context(self, context: String) -> Self {
pub fn with_context(self, file: PathBuf) -> Self {
CompileErrors(
self.0
.into_iter()
.map(|e| CompileError {
context: context.clone(),
file: file.clone(),
..e
})
.collect(),
@ -94,12 +91,6 @@ impl CompileErrors {
}
}
impl fmt::Display for CompileError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}:{}", self.context, self.value)
}
}
impl From<pest::Error> for CompileErrorInner {
fn from(error: pest::Error) -> Self {
CompileErrorInner::ParserError(error)
@ -118,29 +109,34 @@ impl From<io::Error> for CompileErrorInner {
}
}
impl From<semantics::Error> for CompileErrorInner {
impl From<semantics::Error> for CompileError {
fn from(error: semantics::Error) -> Self {
CompileErrorInner::SemanticError(error)
CompileError {
value: CompileErrorInner::SemanticError(error.inner),
file: error.module_id,
}
}
}
impl fmt::Display for CompileErrorInner {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let res = match *self {
CompileErrorInner::ParserError(ref e) => format!("{}", e),
CompileErrorInner::SemanticError(ref e) => format!("{}", e),
CompileErrorInner::ReadError(ref e) => format!("{}", e),
CompileErrorInner::ImportError(ref e) => format!("{}", e),
};
write!(f, "{}", res)
match *self {
CompileErrorInner::ParserError(ref e) => write!(f, "{}", e),
CompileErrorInner::SemanticError(ref e) => write!(f, "{}", e),
CompileErrorInner::ReadError(ref e) => write!(f, "{}", e),
CompileErrorInner::ImportError(ref e) => write!(f, "{}", e),
}
}
}
pub type Resolve<'a, E> = &'a dyn Fn(String, String) -> Result<(String, String), E>;
// See zokrates_fs_resolver for the spec
pub type Resolve<'a, E> = &'a dyn Fn(PathBuf, PathBuf) -> Result<(String, PathBuf), E>;
type FilePath = PathBuf;
pub fn compile<T: Field, E: Into<imports::Error>>(
source: String,
location: String,
location: FilePath,
resolve_option: Option<Resolve<E>>,
) -> Result<CompilationArtifacts<T>, CompileErrors> {
let arena = Arena::new();
@ -150,12 +146,7 @@ pub fn compile<T: Field, E: Into<imports::Error>>(
// check semantics
let typed_ast = Checker::check(compiled).map_err(|errors| {
CompileErrors(
errors
.into_iter()
.map(|e| CompileErrorInner::from(e).with_context(&location))
.collect(),
)
CompileErrors(errors.into_iter().map(|e| CompileError::from(e)).collect())
})?;
let abi = typed_ast.abi();
@ -183,7 +174,7 @@ pub fn compile<T: Field, E: Into<imports::Error>>(
pub fn compile_program<'ast, T: Field, E: Into<imports::Error>>(
source: &'ast str,
location: String,
location: FilePath,
resolve_option: Option<Resolve<E>>,
arena: &'ast Arena<String>,
) -> Result<Program<'ast, T>, CompileErrors> {
@ -207,13 +198,13 @@ pub fn compile_program<'ast, T: Field, E: Into<imports::Error>>(
pub fn compile_module<'ast, T: Field, E: Into<imports::Error>>(
source: &'ast str,
location: String,
location: FilePath,
resolve_option: Option<Resolve<E>>,
modules: &mut HashMap<ModuleId, Module<'ast, T>>,
arena: &'ast Arena<String>,
) -> Result<Module<'ast, T>, CompileErrors> {
let ast = pest::generate_ast(&source)
.map_err(|e| CompileErrors::from(CompileErrorInner::from(e).with_context(&location)))?;
.map_err(|e| CompileErrors::from(CompileErrorInner::from(e).in_file(&location)))?;
let module_without_imports: Module<T> = Module::from(ast);
Importer::new().apply_imports(
@ -238,13 +229,10 @@ mod test {
return foo()
"#
.to_string();
let res: Result<CompilationArtifacts<FieldPrime>, CompileErrors> = compile(
source,
String::from("./path/to/file"),
None::<Resolve<io::Error>>,
);
assert!(res
.unwrap_err()
let res: Result<CompilationArtifacts<FieldPrime>, CompileErrors> =
compile(source, "./path/to/file".into(), None::<Resolve<io::Error>>);
assert!(res.unwrap_err().0[0]
.value()
.to_string()
.contains(&"Can't resolve import without a resolver"));
}
@ -256,11 +244,8 @@ mod test {
return 1
"#
.to_string();
let res: Result<CompilationArtifacts<FieldPrime>, CompileErrors> = compile(
source,
String::from("./path/to/file"),
None::<Resolve<io::Error>>,
);
let res: Result<CompilationArtifacts<FieldPrime>, CompileErrors> =
compile(source, "./path/to/file".into(), None::<Resolve<io::Error>>);
assert!(res.is_ok());
}
}

View file

@ -12,6 +12,7 @@ use crate::parser::Position;
use std::collections::HashMap;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};
use typed_arena::Arena;
use zokrates_field::field::Field;
@ -54,9 +55,11 @@ impl From<io::Error> for Error {
}
}
type ImportPath<'ast> = &'ast Path;
#[derive(PartialEq, Clone)]
pub struct Import<'ast> {
source: Identifier<'ast>,
source: ImportPath<'ast>,
symbol: Option<Identifier<'ast>>,
alias: Option<Identifier<'ast>>,
}
@ -64,7 +67,7 @@ pub struct Import<'ast> {
pub type ImportNode<'ast> = Node<Import<'ast>>;
impl<'ast> Import<'ast> {
pub fn new(symbol: Option<Identifier<'ast>>, source: Identifier<'ast>) -> Import<'ast> {
pub fn new(symbol: Option<Identifier<'ast>>, source: ImportPath<'ast>) -> Import<'ast> {
Import {
symbol,
source,
@ -78,7 +81,7 @@ impl<'ast> Import<'ast> {
pub fn new_with_alias(
symbol: Option<Identifier<'ast>>,
source: Identifier<'ast>,
source: ImportPath<'ast>,
alias: Identifier<'ast>,
) -> Import<'ast> {
Import {
@ -93,7 +96,7 @@ impl<'ast> Import<'ast> {
self
}
pub fn get_source(&self) -> &Identifier<'ast> {
pub fn get_source(&self) -> &ImportPath<'ast> {
&self.source
}
}
@ -101,8 +104,8 @@ impl<'ast> Import<'ast> {
impl<'ast> fmt::Display for Import<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.alias {
Some(ref alias) => write!(f, "import {} as {}", self.source, alias),
None => write!(f, "import {}", self.source),
Some(ref alias) => write!(f, "import {} as {}", self.source.display(), alias),
None => write!(f, "import {}", self.source.display()),
}
}
}
@ -110,8 +113,13 @@ impl<'ast> fmt::Display for Import<'ast> {
impl<'ast> fmt::Debug for Import<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.alias {
Some(ref alias) => write!(f, "import(source: {}, alias: {})", self.source, alias),
None => write!(f, "import(source: {})", self.source),
Some(ref alias) => write!(
f,
"import(source: {}, alias: {})",
self.source.display(),
alias
),
None => write!(f, "import(source: {})", self.source.display()),
}
}
}
@ -126,7 +134,7 @@ impl Importer {
pub fn apply_imports<'ast, T: Field, E: Into<Error>>(
&self,
destination: Module<'ast, T>,
location: String,
location: PathBuf,
resolve_option: Option<Resolve<E>>,
modules: &mut HashMap<ModuleId, Module<'ast, T>>,
arena: &'ast Arena<String>,
@ -139,7 +147,7 @@ impl Importer {
let alias = import.alias;
// handle the case of special bellman and packing imports
if import.source.starts_with("EMBED") {
match import.source.as_ref() {
match import.source.to_str().unwrap() {
"EMBED/sha256round" => {
let alias = alias.unwrap_or("sha256round");
@ -166,17 +174,15 @@ impl Importer {
return Err(CompileErrorInner::ImportError(
Error::new(format!("Embed {} not found. Options are \"EMBED/sha256round\", \"EMBED/unpack\"", s)).with_pos(Some(pos)),
)
.with_context(&location)
.in_file(&location)
.into());
}
}
} else {
// to resolve imports, we need a resolver
match resolve_option {
Some(resolve) => match resolve(location.clone(), import.source.to_string()) {
Ok((source, location)) => {
let source = arena.alloc(source);
Some(resolve) => match resolve(location.clone(), import.source.to_path_buf()) {
Ok((source, new_location)) => {
// generate an alias from the imported path if none was given explicitely
let alias = import.alias.unwrap_or(
std::path::Path::new(import.source)
@ -184,19 +190,32 @@ impl Importer {
.ok_or(CompileErrors::from(
CompileErrorInner::ImportError(Error::new(format!(
"Could not determine alias for import {}",
import.source
import.source.display()
)))
.with_context(&location),
.in_file(&location),
))?
.to_str()
.unwrap(),
);
let compiled =
compile_module(source, location, resolve_option, modules, &arena)
.map_err(|e| e.with_context(import.source.to_string()))?;
match modules.get(&new_location) {
Some(_) => {}
None => {
let source = arena.alloc(source);
modules.insert(import.source.to_string(), compiled);
let compiled = compile_module(
source,
new_location.clone(),
resolve_option,
modules,
&arena,
)?;
assert!(modules
.insert(new_location.clone(), compiled)
.is_none());
}
};
symbols.push(
SymbolDeclaration {
@ -204,7 +223,7 @@ impl Importer {
symbol: Symbol::There(
SymbolImport::with_id_in_module(
import.symbol.unwrap_or("main"),
import.source.clone(),
new_location.display().to_string(),
)
.start_end(pos.0, pos.1),
),
@ -216,7 +235,7 @@ impl Importer {
return Err(CompileErrorInner::ImportError(
err.into().with_pos(Some(pos)),
)
.with_context(&location)
.in_file(&location)
.into());
}
},
@ -224,7 +243,7 @@ impl Importer {
return Err(CompileErrorInner::from(Error::new(
"Can't resolve import without a resolver",
))
.with_context(&location)
.in_file(&location)
.into());
}
}
@ -249,10 +268,10 @@ mod tests {
#[test]
fn create_with_no_alias() {
assert_eq!(
Import::new(None, "./foo/bar/baz.zok"),
Import::new(None, Path::new("./foo/bar/baz.zok")),
Import {
symbol: None,
source: "./foo/bar/baz.zok",
source: Path::new("./foo/bar/baz.zok"),
alias: None,
}
);
@ -261,10 +280,10 @@ mod tests {
#[test]
fn create_with_alias() {
assert_eq!(
Import::new_with_alias(None, "./foo/bar/baz.zok", &"myalias"),
Import::new_with_alias(None, Path::new("./foo/bar/baz.zok"), &"myalias"),
Import {
symbol: None,
source: "./foo/bar/baz.zok",
source: Path::new("./foo/bar/baz.zok"),
alias: Some("myalias"),
}
);

File diff suppressed because it is too large Load diff

View file

@ -27,8 +27,8 @@ pub struct Inliner<'ast, T: Field> {
modules: TypedModules<'ast, T>, // the modules in which to look for functions when inlining
module_id: TypedModuleId, // the current module we're visiting
statement_buffer: Vec<TypedStatement<'ast, T>>, // a buffer of statements to be added to the inlined statements
stack: Vec<(String, FunctionKey<'ast>, usize)>, // the current call stack
call_count: HashMap<(String, FunctionKey<'ast>), usize>, // the call count for each function
stack: Vec<(TypedModuleId, FunctionKey<'ast>, usize)>, // the current call stack
call_count: HashMap<(TypedModuleId, FunctionKey<'ast>), usize>, // the call count for each function
}
impl<'ast, T: Field> Inliner<'ast, T> {
@ -74,9 +74,9 @@ impl<'ast, T: Field> Inliner<'ast, T> {
// return a program with a single module containing `main`, `_UNPACK`, and `_SHA256_ROUND
TypedProgram {
main: String::from("main"),
main: "main".into(),
modules: vec![(
String::from("main"),
"main".into(),
TypedModule {
functions: vec![
(unpack_key, TypedFunctionSymbol::Flat(unpack)),
@ -311,6 +311,7 @@ impl<'ast, T: Field> Folder<'ast, T> for Inliner<'ast, T> {
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
use typed_absy::types::{FunctionKey, Signature, Type};
use zokrates_field::field::FieldPrime;
@ -354,7 +355,7 @@ mod tests {
TypedFunctionSymbol::There(
FunctionKey::with_id("foo")
.signature(Signature::new().outputs(vec![Type::FieldElement])),
String::from("foo"),
"foo".into(),
),
),
]
@ -378,12 +379,12 @@ mod tests {
.collect(),
};
let modules: HashMap<_, _> = vec![(String::from("main"), main), (String::from("foo"), foo)]
let modules: HashMap<_, _> = vec![("main".into(), main), ("foo".into(), foo)]
.into_iter()
.collect();
let program = TypedProgram {
main: String::from("main"),
main: "main".into(),
modules,
};
@ -393,7 +394,7 @@ mod tests {
assert_eq!(
program
.modules
.get(&String::from("main"))
.get(&PathBuf::from("main"))
.unwrap()
.functions
.get(
@ -469,7 +470,7 @@ mod tests {
.inputs(vec![Type::FieldElement])
.outputs(vec![Type::FieldElement]),
),
String::from("foo"),
"foo".into(),
),
),
]
@ -500,12 +501,12 @@ mod tests {
.collect(),
};
let modules: HashMap<_, _> = vec![(String::from("main"), main), (String::from("foo"), foo)]
let modules: HashMap<_, _> = vec![("main".into(), main), ("foo".into(), foo)]
.into_iter()
.collect();
let program: TypedProgram<FieldPrime> = TypedProgram {
main: String::from("main"),
main: "main".into(),
modules,
};
@ -514,7 +515,7 @@ mod tests {
assert_eq!(program.modules.len(), 1);
let stack = vec![(
String::from("foo"),
"foo".into(),
FunctionKey::with_id("foo").signature(
Signature::new()
.inputs(vec![Type::FieldElement])
@ -526,7 +527,7 @@ mod tests {
assert_eq!(
program
.modules
.get(&String::from("main"))
.get(&PathBuf::from("main"))
.unwrap()
.functions
.get(
@ -614,7 +615,7 @@ mod tests {
TypedFunctionSymbol::There(
FunctionKey::with_id("foo")
.signature(Signature::new().outputs(vec![Type::FieldElement])),
String::from("foo"),
"foo".into(),
),
),
]
@ -638,12 +639,12 @@ mod tests {
.collect(),
};
let modules: HashMap<_, _> = vec![(String::from("main"), main), (String::from("foo"), foo)]
let modules: HashMap<_, _> = vec![("main".into(), main), ("foo".into(), foo)]
.into_iter()
.collect();
let program = TypedProgram {
main: String::from("main"),
main: "main".into(),
modules,
};
@ -653,7 +654,7 @@ mod tests {
assert_eq!(
program
.modules
.get(&String::from("main"))
.get(&PathBuf::from("main"))
.unwrap()
.functions
.get(
@ -733,10 +734,10 @@ mod tests {
.collect(),
};
let modules: HashMap<_, _> = vec![(String::from("main"), main)].into_iter().collect();
let modules: HashMap<_, _> = vec![("main".into(), main)].into_iter().collect();
let program = TypedProgram {
main: String::from("main"),
main: "main".into(),
modules,
};
@ -746,7 +747,7 @@ mod tests {
assert_eq!(
program
.modules
.get(&String::from("main"))
.get(&PathBuf::from("main"))
.unwrap()
.functions
.get(
@ -832,7 +833,7 @@ mod tests {
.inputs(vec![Type::FieldElement])
.outputs(vec![Type::FieldElement]),
),
String::from("id"),
"id".into(),
),
),
]
@ -861,19 +862,19 @@ mod tests {
.collect(),
};
let modules = vec![(String::from("main"), main), (String::from("id"), id)]
let modules = vec![("main".into(), main), ("id".into(), id)]
.into_iter()
.collect();
let program: TypedProgram<FieldPrime> = TypedProgram {
main: String::from("main"),
main: "main".into(),
modules,
};
let program = Inliner::inline(program);
let stack0 = vec![(
String::from("id"),
"id".into(),
FunctionKey::with_id("main").signature(
Signature::new()
.inputs(vec![Type::FieldElement])
@ -882,7 +883,7 @@ mod tests {
1,
)];
let stack1 = vec![(
String::from("id"),
"id".into(),
FunctionKey::with_id("main").signature(
Signature::new()
.inputs(vec![Type::FieldElement])
@ -895,7 +896,7 @@ mod tests {
assert_eq!(
program
.modules
.get(&String::from("main"))
.get(&PathBuf::from("main"))
.unwrap()
.functions
.get(

View file

@ -78,7 +78,7 @@ mod tests {
let p = TypedProgram {
modules: vec![(
"main".to_string(),
"main".into(),
TypedModule {
functions: vec![(
FunctionKey::with_id("main"),
@ -94,7 +94,7 @@ mod tests {
)]
.into_iter()
.collect(),
main: "main".to_string(),
main: "main".into(),
};
assert!(PropagatedUnroller::unroll(p).is_err());
@ -203,7 +203,7 @@ mod tests {
let p = TypedProgram {
modules: vec![(
"main".to_string(),
"main".into(),
TypedModule {
functions: vec![(
FunctionKey::with_id("main"),
@ -219,11 +219,12 @@ mod tests {
)]
.into_iter()
.collect(),
main: "main".to_string(),
main: "main".into(),
};
let statements = match PropagatedUnroller::unroll(p).unwrap().modules["main"].functions
[&FunctionKey::with_id("main")]
let statements = match PropagatedUnroller::unroll(p).unwrap().modules
[std::path::Path::new("main")]
.functions[&FunctionKey::with_id("main")]
.clone()
{
TypedFunctionSymbol::Here(f) => f.statements,

View file

@ -69,10 +69,10 @@ mod tests {
);
let mut modules = HashMap::new();
modules.insert(String::from("main"), TypedModule { functions });
modules.insert("main".into(), TypedModule { functions });
let typed_ast: TypedProgram<FieldPrime> = TypedProgram {
main: String::from("main"),
main: "main".into(),
modules,
};

View file

@ -14,6 +14,7 @@ mod variable;
pub use crate::typed_absy::parameter::Parameter;
pub use crate::typed_absy::types::{Signature, Type};
pub use crate::typed_absy::variable::Variable;
use std::path::PathBuf;
use crate::typed_absy::types::{FunctionKey, MemberId};
use embed::FlatEmbed;
@ -38,7 +39,7 @@ pub struct Identifier<'ast> {
}
/// An identifier for a `TypedModule`. Typically a path or uri.
pub type TypedModuleId = String;
pub type TypedModuleId = PathBuf;
/// A collection of `TypedModule`s
pub type TypedModules<'ast, T> = HashMap<TypedModuleId, TypedModule<'ast, T>>;
@ -90,7 +91,7 @@ impl<'ast, T: Field> fmt::Display for TypedProgram<'ast, T> {
writeln!(
f,
"| {}: |{}",
module_id,
module_id.display(),
if *module_id == self.main {
"<---- main"
} else {
@ -123,7 +124,12 @@ impl<'ast> fmt::Display for Identifier<'ast> {
"{}_{}_{}",
self.stack
.iter()
.map(|(name, sig, count)| format!("{}_{}_{}", name, sig.to_slug(), count))
.map(|(name, sig, count)| format!(
"{}_{}_{}",
name.display(),
sig.to_slug(),
count
))
.collect::<Vec<_>>()
.join("_"),
self.id,
@ -189,7 +195,10 @@ impl<'ast, T: Field> fmt::Display for TypedModule<'ast, T> {
TypedFunctionSymbol::Here(ref function) => format!("def {}{}", key.id, function),
TypedFunctionSymbol::There(ref fun_key, ref module_id) => format!(
"import {} from \"{}\" as {} // with signature {}",
fun_key.id, module_id, key.id, key.signature
fun_key.id,
module_id.display(),
key.id,
key.signature
),
TypedFunctionSymbol::Flat(ref flat_fun) => {
format!("def {}{}:\n\t// hidden", key.id, flat_fun.signature::<T>())

View file

@ -0,0 +1,2 @@
def foo() -> (field):
return 1

View file

@ -0,0 +1,4 @@
from "./dep/foo" import foo as bar
def foo() -> (field):
return 2 + bar()

View file

@ -0,0 +1,15 @@
{
"entry_point": "./tests/tests/import/import.zok",
"tests": [
{
"input": {
"values": []
},
"output": {
"Ok": {
"values": ["3"]
}
}
}
]
}

View file

@ -0,0 +1,4 @@
from "./dep/foo" import foo
def main() -> (field):
return foo()

View file

@ -6,43 +6,72 @@ use std::path::{Component, PathBuf};
const ZOKRATES_HOME: &str = &"ZOKRATES_HOME";
type CurrentLocation = String;
type ImportLocation<'a> = String;
// path to the current file we're importing into
type CurrentLocation = PathBuf;
// path we're importing from
type ImportLocation<'a> = PathBuf;
type SourceCode = String;
/// Returns the source code and the new location from a file path and import path
///
/// # Arguments
///
/// * `current_location` - Path to the file we're importing into
/// * `import_location` - Path to the file we're importing from
///
/// # Returns
/// * The content of the file we're importing from
/// * The path to the file we're importing from
///
/// # Remarks
///
/// * `current_location* must point to a file
/// * `import_location` and the returned path are both relative to the directory in which `current_location` is, unless it's an absolute
/// path, in which case they are relative to the root of the ZoKrates stdlib at `$ZOKRATES_HOME`
///
pub fn resolve<'a>(
current_location: CurrentLocation,
import_location: ImportLocation<'a>,
) -> Result<(SourceCode, CurrentLocation), io::Error> {
let source = Path::new(&import_location);
if !current_location.is_file() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("{} was expected to be a file", current_location.display()),
));
}
// paths starting with `./` or `../` are interpreted relative to the current file
// other paths `abc/def` are interpreted relative to $ZOKRATES_HOME
let base = match source.components().next() {
Some(Component::CurDir) | Some(Component::ParentDir) => PathBuf::from(current_location),
_ => PathBuf::from(
std::env::var(ZOKRATES_HOME).expect("$ZOKRATES_HOME is not set, please set it"),
),
};
Some(Component::CurDir) | Some(Component::ParentDir) => {
Ok(PathBuf::from(current_location).parent().unwrap().into())
}
_ => std::env::var(ZOKRATES_HOME)
.map_err(|_| {
io::Error::new(
io::ErrorKind::Other,
"$ZOKRATES_HOME is not set, please set it",
)
})
.map(PathBuf::from),
}?;
let path_owned = base
.join(PathBuf::from(import_location))
.join(PathBuf::from(import_location.clone()))
.with_extension("zok");
if path_owned.is_dir() {
return Err(io::Error::new(io::ErrorKind::Other, "Not a file"));
if !path_owned.is_file() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("No file found at {}", import_location.display()),
));
}
let next_location = generate_next_location(&path_owned)?;
let source = read_to_string(path_owned)?;
let source = read_to_string(&path_owned)?;
Ok((source, next_location))
}
fn generate_next_location<'a>(path: &'a PathBuf) -> Result<String, io::Error> {
path.parent()
.ok_or(io::Error::new(io::ErrorKind::Other, "Invalid path"))
.map(|v| v.to_path_buf().into_os_string().into_string().unwrap())
Ok((source, path_owned))
}
#[cfg(test)]
@ -52,39 +81,44 @@ mod tests {
#[test]
fn valid_path() {
use std::io::Write;
// create a source folder with a zok file
let folder = tempfile::tempdir().unwrap();
let file_path = folder.path().join("bar.zok");
let mut file = File::create(file_path).unwrap();
writeln!(file, "some code").unwrap();
let (_, next_location) =
resolve(folder.path().to_str().unwrap().to_string(), "./bar".into()).unwrap();
assert_eq!(next_location, folder.path().to_str().unwrap().to_string());
File::create(file_path.clone()).unwrap();
let (_, next_location) = resolve(file_path.clone(), "./bar.zok".into()).unwrap();
assert_eq!(next_location, file_path);
}
#[test]
fn non_existing_file() {
let res = resolve(String::from("./src"), "./rubbish".into());
let res = resolve("./source.zok".into(), "./rubbish".into());
assert!(res.is_err());
}
#[test]
fn invalid_location() {
let res = resolve(String::from(",8!-$2abc"), "./foo".into());
let res = resolve(",8!-$2abc".into(), "./foo".into());
assert!(res.is_err());
}
#[test]
fn not_a_file() {
let res = resolve(String::from("."), "./src/".into());
// create a source folder with a zok file
let folder = tempfile::tempdir().unwrap();
let dir_path = folder.path().join("dir");
std::fs::create_dir(dir_path.clone()).unwrap();
let res = resolve(".".into(), "./dir/".into());
assert!(res.is_err());
}
#[test]
fn no_parent() {
let res = resolve(String::from("."), ".".into());
// create a source folder with a zok file
let folder = tempfile::tempdir().unwrap();
let file_path = folder.path().join("foo.zok");
File::create(file_path.clone()).unwrap();
let res = resolve(file_path, ".".into());
assert!(res.is_err());
}
@ -101,20 +135,13 @@ mod tests {
// create a user folder with a code file
let source_folder = tempfile::tempdir().unwrap();
let file_path = source_folder.path().join("bar.zok");
let mut file = File::create(file_path).unwrap();
let mut file = File::create(file_path.clone()).unwrap();
writeln!(file, "<user code>").unwrap();
// assign HOME folder to ZOKRATES_HOME
std::env::set_var(ZOKRATES_HOME, zokrates_home_folder.path());
let result = resolve(
source_folder
.path()
.to_path_buf()
.to_string_lossy()
.to_string(),
"./bar.zok".into(),
);
let result = resolve(file_path, "./bar.zok".into());
assert!(result.is_ok());
// the imported file should be the user's
assert_eq!(result.unwrap().0, String::from("<user code>\n"));
@ -133,20 +160,13 @@ mod tests {
// create a user folder with a code file
let source_folder = tempfile::tempdir().unwrap();
let file_path = source_folder.path().join("bar.zok");
let mut file = File::create(file_path).unwrap();
let mut file = File::create(file_path.clone()).unwrap();
writeln!(file, "<user code>").unwrap();
// assign HOME folder to ZOKRATES_HOME
std::env::set_var(ZOKRATES_HOME, zokrates_home_folder.path());
let result = resolve(
source_folder
.path()
.to_path_buf()
.to_string_lossy()
.to_string(),
"bar.zok".into(),
);
let result = resolve(file_path.clone(), "bar.zok".into());
assert!(result.is_ok());
// the imported file should be the user's
assert_eq!(result.unwrap().0, String::from("<stdlib code>\n"));
@ -162,13 +182,11 @@ mod tests {
let file_path = source_folder.path().join("bar.zok");
let mut file = File::create(file_path).unwrap();
writeln!(file, "<user code>").unwrap();
let origin_path = source_subfolder.path().join("foo.zok");
File::create(origin_path).unwrap();
let result = resolve(
source_subfolder
.path()
.to_path_buf()
.to_string_lossy()
.to_string(),
source_subfolder.path().to_path_buf().join("foo.zok"),
"../bar.zok".into(),
);
assert!(result.is_ok());
@ -189,21 +207,21 @@ mod tests {
// assign HOME folder to ZOKRATES_HOME
std::env::set_var(ZOKRATES_HOME, zokrates_home_folder.path());
let result = resolve("/path/to/user/folder".to_string(), "./bar.zok".into());
let result = resolve("/path/to/source.zok".into(), "./bar.zok".into());
assert!(result.is_err());
}
#[test]
fn fail_if_not_found_in_std() {
std::env::set_var(ZOKRATES_HOME, "");
let result = resolve("/path/to/source".to_string(), "bar.zok".into());
let result = resolve("/path/to/source.zok".into(), "bar.zok".into());
assert!(result.is_err());
}
#[test]
#[should_panic]
fn panic_if_home_not_set() {
std::env::remove_var(ZOKRATES_HOME);
let _ = resolve("/path/to/source".to_string(), "bar.zok".into());
let result = resolve("/path/to/source.zok".into(), "bar.zok".into());
assert!(result.is_err());
}
}

View file

@ -1,9 +1,10 @@
use bincode::{deserialize, serialize};
use serde::{Deserialize, Serialize};
use serde_json::to_string_pretty;
use std::path::PathBuf;
use wasm_bindgen::prelude::*;
use zokrates_abi::{parse_strict, Encode, Decode, Inputs};
use zokrates_core::compile::{compile as core_compile, CompilationArtifacts};
use zokrates_core::compile::{compile as core_compile, CompilationArtifacts, CompileError};
use zokrates_core::imports::Error;
use zokrates_core::ir;
use zokrates_core::proof_system::{self, ProofSystem};
@ -30,8 +31,8 @@ pub struct ComputationResult {
}
impl ResolverResult {
fn into_tuple(self) -> (String, String) {
(self.source, self.location)
fn into_tuple(self) -> (String, PathBuf) {
(self.source, PathBuf::from(self.location))
}
}
@ -53,30 +54,38 @@ pub fn compile(
location: JsValue,
resolve: &js_sys::Function,
) -> Result<JsValue, JsValue> {
let closure = |l: String, p: String| {
let closure = |l: PathBuf, p: PathBuf| {
let value = resolve
.call2(&JsValue::UNDEFINED, &l.into(), &p.clone().into())
.call2(&JsValue::UNDEFINED, &l.display().to_string().into(), &p.clone().display().to_string().into())
.map_err(|_| {
Error::new(format!(
"Error thrown in callback: Could not resolve `{}`",
p
p.display()
))
})?;
if value.is_null() || value.is_undefined() {
Err(Error::new(format!("Could not resolve `{}`", p)))
Err(Error::new(format!("Could not resolve `{}`", p.display())))
} else {
let result: ResolverResult = value.into_serde().unwrap();
Ok(result.into_tuple())
}
};
let fmt_error = |e: &CompileError| {
format!(
"{}:{}",
e.file().display(),
e.value()
)
};
let artifacts: CompilationArtifacts<FieldPrime> = core_compile(
source.as_string().unwrap(),
location.as_string().unwrap(),
PathBuf::from(location.as_string().unwrap()),
Some(&closure),
)
.map_err(|ce| JsValue::from_str(&format!("{}", ce)))?;
.map_err(|ce| JsValue::from_str(&format!("{}", ce.0.iter().map(|e| fmt_error(e)).collect::<Vec<_>>().join("\n"))))?;
let result = CompilationResult {
program: serialize_program(artifacts.prog())?,

View file

@ -77,17 +77,7 @@ pub fn test_inner(test_path: &str) {
let code = std::fs::read_to_string(&t.entry_point).unwrap();
let artifacts = compile(
code,
t.entry_point
.parent()
.unwrap()
.to_str()
.unwrap()
.to_string(),
Some(&resolve),
)
.unwrap();
let artifacts = compile(code, t.entry_point.clone(), Some(&resolve)).unwrap();
let bin = artifacts.prog();