Merge branch 'feature/lib-and-imports' of github.com:AleoHQ/leo into feature/comparator

This commit is contained in:
collin 2020-06-29 19:52:47 -07:00
commit 45a2664fd9
81 changed files with 1762 additions and 547 deletions

152
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,152 @@
name: CI
on:
pull_request:
push:
branches:
- master
env:
RUST_BACKTRACE: 1
jobs:
style:
name: Check Style
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Load snarkOS
run: |
mkdir ~/.ssh
echo "${{ secrets.SNARKOS_DEPLOY_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
eval $(ssh-agent -s)
ssh-add -k ~/.ssh/id_rsa
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
components: rustfmt
- name: cargo fmt --check
uses: actions-rs/cargo@v1
env:
CARGO_NET_GIT_FETCH_WITH_CLI: true
with:
command: fmt
args: --all -- --check
test:
name: Test
runs-on: ubuntu-latest
env:
RUSTFLAGS: -Dwarnings
strategy:
matrix:
rust:
- stable
- nightly
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Load snarkOS
run: |
mkdir ~/.ssh
echo "${{ secrets.SNARKOS_DEPLOY_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
eval $(ssh-agent -s)
ssh-add -k ~/.ssh/id_rsa
- name: Install Rust (${{ matrix.rust }})
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- name: Check examples
uses: actions-rs/cargo@v1
env:
CARGO_NET_GIT_FETCH_WITH_CLI: true
with:
command: check
args: --examples --all
- name: Check examples with all features on stable
uses: actions-rs/cargo@v1
with:
command: check
args: --examples --all-features --all
if: matrix.rust == 'stable'
- name: Check benchmarks on nightly
uses: actions-rs/cargo@v1
with:
command: check
args: --all-features --examples --all --benches
if: matrix.rust == 'nightly'
- name: Test
uses: actions-rs/cargo@v1
with:
command: test
args: --release --all --no-fail-fast
codecov:
name: Code Coverage
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Load snarkOS
run: |
mkdir ~/.ssh
echo "${{ secrets.SNARKOS_DEPLOY_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
eval $(ssh-agent -s)
ssh-add -k ~/.ssh/id_rsa
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
components: rustfmt
- name: Test
uses: actions-rs/cargo@v1
with:
command: test
args: --all
env:
CARGO_NET_GIT_FETCH_WITH_CLI: true
CARGO_INCREMENTAL: "0"
- name: Install dependencies for code coverage
run: |
sudo apt-get update
sudo apt-get -y install binutils-dev libcurl4-openssl-dev zlib1g-dev libdw-dev libiberty-dev
- name: Generate coverage report
run: |
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz
tar xzf master.tar.gz
cd kcov-master
mkdir build && cd build
cmake .. && make
make install DESTDIR=../../kcov-build
cd ../..
rm -rf kcov-master
for file in target/debug/deps/*-*; do if [[ "$file" != *\.* ]]; then mkdir -p "target/cov/$(basename $file)"; ./kcov-build/usr/local/bin/kcov --exclude-pattern=/.cargo,/usr/lib --verify "target/cov/$(basename $file)" "$file"; fi done
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.CODECOV_TOKEN }}

137
Cargo.lock generated
View File

@ -78,9 +78,9 @@ checksum = "58946044516aa9dc922182e0d6e9d124a31aafe6b421614654eb27cf90cec09c"
[[package]]
name = "bincode"
version = "1.2.1"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf"
checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d"
dependencies = [
"byteorder",
"serde",
@ -149,6 +149,33 @@ version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "bzip2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b"
dependencies = [
"bzip2-sys",
"libc",
]
[[package]]
name = "bzip2-sys"
version = "0.1.9+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad3b39a260062fca31f7b0b12f207e8f2590a67d32ec7d59c20484b07ea7285e"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "cc"
version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1be3409f94d7bdceeb5f5fac551039d9b3f00e25da7a74fc4d33400a0d96368"
[[package]]
name = "cfg-if"
version = "0.1.10"
@ -190,6 +217,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "crc32fast"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-deque"
version = "0.7.3"
@ -334,6 +370,18 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "flate2"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]]
name = "from-pest"
version = "0.3.1"
@ -492,6 +540,8 @@ dependencies = [
"snarkos-utilities",
"thiserror",
"toml",
"walkdir",
"zip",
]
[[package]]
@ -519,7 +569,7 @@ dependencies = [
"log",
"pest",
"rand",
"sha2 0.8.2",
"sha2",
"snarkos-curves",
"snarkos-errors",
"snarkos-gadgets",
@ -691,6 +741,18 @@ dependencies = [
"sha-1",
]
[[package]]
name = "pkg-config"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
[[package]]
name = "podio"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b18befed8bc2b61abc79a457295e7e838417326da1586050b919414073977f19"
[[package]]
name = "ppv-lite86"
version = "0.2.8"
@ -840,9 +902,9 @@ checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
[[package]]
name = "rusty-hook"
version = "0.11.1"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27138b73a8ce63ae918707a5e3b57f9b0c0842a57b82f0e43474cf4e3aaf0ff4"
checksum = "96cee9be61be7e1cbadd851e58ed7449c29c620f00b23df937cb9cbc04ac21a3"
dependencies = [
"ci_info",
"getopts",
@ -856,6 +918,15 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -905,18 +976,6 @@ dependencies = [
"opaque-debug",
]
[[package]]
name = "sha2"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
dependencies = [
"block-buffer 0.7.3",
"digest 0.8.1",
"fake-simd",
"opaque-debug",
]
[[package]]
name = "sha2"
version = "0.9.0"
@ -947,6 +1006,7 @@ checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
[[package]]
name = "snarkos-algorithms"
version = "0.8.0"
source = "git+ssh://git@github.com/AleoHQ/snarkOS.git?rev=c7a56d9#c7a56d97a76dd6e7841aa6bb86e086f7128e5620"
dependencies = [
"blake2",
"derivative",
@ -954,7 +1014,7 @@ dependencies = [
"rand",
"rand_chacha",
"rayon",
"sha2 0.9.0",
"sha2",
"smallvec",
"snarkos-errors",
"snarkos-models",
@ -965,6 +1025,7 @@ dependencies = [
[[package]]
name = "snarkos-curves"
version = "0.8.0"
source = "git+ssh://git@github.com/AleoHQ/snarkOS.git?rev=c7a56d9#c7a56d97a76dd6e7841aa6bb86e086f7128e5620"
dependencies = [
"derivative",
"rand",
@ -978,6 +1039,7 @@ dependencies = [
[[package]]
name = "snarkos-derives"
version = "0.1.0"
source = "git+ssh://git@github.com/AleoHQ/snarkOS.git?rev=c7a56d9#c7a56d97a76dd6e7841aa6bb86e086f7128e5620"
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
@ -987,6 +1049,7 @@ dependencies = [
[[package]]
name = "snarkos-errors"
version = "0.8.0"
source = "git+ssh://git@github.com/AleoHQ/snarkOS.git?rev=c7a56d9#c7a56d97a76dd6e7841aa6bb86e086f7128e5620"
dependencies = [
"base58",
"bech32",
@ -999,6 +1062,7 @@ dependencies = [
[[package]]
name = "snarkos-gadgets"
version = "0.8.0"
source = "git+ssh://git@github.com/AleoHQ/snarkOS.git?rev=c7a56d9#c7a56d97a76dd6e7841aa6bb86e086f7128e5620"
dependencies = [
"derivative",
"digest 0.8.1",
@ -1012,6 +1076,7 @@ dependencies = [
[[package]]
name = "snarkos-models"
version = "0.8.0"
source = "git+ssh://git@github.com/AleoHQ/snarkOS.git?rev=c7a56d9#c7a56d97a76dd6e7841aa6bb86e086f7128e5620"
dependencies = [
"bincode",
"derivative",
@ -1026,10 +1091,12 @@ dependencies = [
[[package]]
name = "snarkos-profiler"
version = "0.8.0"
source = "git+ssh://git@github.com/AleoHQ/snarkOS.git?rev=c7a56d9#c7a56d97a76dd6e7841aa6bb86e086f7128e5620"
[[package]]
name = "snarkos-utilities"
version = "0.8.0"
source = "git+ssh://git@github.com/AleoHQ/snarkOS.git?rev=c7a56d9#c7a56d97a76dd6e7841aa6bb86e086f7128e5620"
dependencies = [
"bincode",
"rand",
@ -1130,6 +1197,16 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "time"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "toml"
version = "0.5.6"
@ -1187,6 +1264,17 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "walkdir"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
@ -1223,3 +1311,16 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "zip"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58287c28d78507f5f91f2a4cf1e8310e2c76fd4c6932f93ac60fd1ceb402db7d"
dependencies = [
"bzip2",
"crc32fast",
"flate2",
"podio",
"time",
]

View File

@ -19,12 +19,12 @@ members = [ "ast", "compiler", "leo-inputs", "types" ]
leo-compiler = { path = "compiler", version = "0.1.0" }
leo-inputs = { path = "leo-inputs", version = "0.1.0"}
snarkos-algorithms = { path = "../snarkOS/algorithms", version = "0.8.0", default-features = false }
snarkos-curves = { path = "../snarkOS/curves", version = "0.8.0", default-features = false }
snarkos-errors = { path = "../snarkOS/errors", version = "0.8.0", default-features = false }
snarkos-gadgets = { path = "../snarkOS/gadgets", version = "0.8.0", default-features = false }
snarkos-models = { path = "../snarkOS/models", version = "0.8.0", default-features = false }
snarkos-utilities = { path = "../snarkOS/utilities", version = "0.8.0" }
snarkos-algorithms = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-curves = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-errors = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-gadgets = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-models = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-utilities = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9" }
clap = { version = "2.33.0" }
colored = { version = "1.9" }
@ -37,9 +37,11 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
toml = { version = "0.5" }
thiserror = { version = "1.0" }
walkdir = { version = "2" }
zip = { version = "0.5" }
[dev-dependencies]
rusty-hook = { version = "0.11.1" }
rusty-hook = { version = "0.11.2" }
[profile.release]
opt-level = 3

144
README.md
View File

@ -356,40 +356,111 @@ function main() -> Circ {
```
## Imports
Both struct and function imports are supported.
Leo supports importing functions and circuits by name into the current file with the following syntax:
```leo
import all: `*`
import alias: `symbol as alias`
```rust
import [package].[name];
```
`src/simple_import.leo`
#### Import Aliases
To import a name using an alias:
```rust
circuit Point {
x: u32
y: u32
import [package].[name] as [alias];
```
#### Import Multiple
To import multiple names from the same package:
```rust
import [package].(
[name_1],
[name_2] as [alias],
);
```
#### Import Star
To import all symbols from a package:
```rust
import [package].*;
```
### Local
You can import from a local file in the `src/` directory by using its `[file].leo` as the `[package]` name.
```rust
import [file].[name];
```
#### Example:
`src/bar.leo`
```rust
circuit Bar {
b: u32
}
function test() -> (u32, u32[2]) {
return 1, [2, 3]
function baz() -> u32 {
return 1u32
}
```
`src/simple.leo`
`src/main.leo`
```rust
from "./simple_import" import {
Point as Foo,
test
};
import bar.(
Bar,
baz
);
// from "./simple_import" import *
function main() {
const bar = Bar { b: 1u32};
const z = baz();
}
```
function main() -> (u32[3]) {
let p = Foo { x: 1, y: 2};
### Foreign
You can import from a foreign package in the `imports/` directory using its `[package]` name.
```rust
import [package].[name];
```
let (a, b) = test();
#### Example:
`imports/bar/src/lib.leo`
```rust
circuit Bar {
b: u32
}
```
return [a, ...b]
`src/main.leo`
```rust
import bar.Bar;
function main() {
const bar = Bar { b: 1u32 };
}
```
### Package Paths
Leo treats directories as package names when importing.
```rust
import [package].[directory].[file].[name]
```
#### Example:
We wish to import the `Baz` circuit from the `baz.leo` file in the `bar` directory in the `foo` package
`imports/foo/src/bar/baz.leo`
```rust
circuit Baz {
b: u32
}
```
`src/main.leo`
```rust
import foo.bar.baz.Baz;
function main() {
const baz = Baz { b: 1u32 };
}
```
@ -400,11 +471,11 @@ This will enforce that the two values are equal in the constraint system.
```rust
function main() {
assert_eq(45, 45);
assert_eq!(45, 45);
assert_eq(2fe, 2fe);
assert_eq!(2fe, 2fe);
assert_eq(true, true);
assert_eq!(true, true);
}
```
@ -490,13 +561,28 @@ This will create a new directory with a given package name. The new package will
- inputs.leo # Your program inputs for main.leo
- outputs # Your program outputs
- src
- lib.leo # Your program library
- main.leo # Your program
- tests
- test.leo # Your program tests
- Leo.toml # Your program manifest
```
#### Flags
```rust
leo new {$Name} --bin
```
This will create a new directory with a given package name. The new package will have a directory structure as above.
```rust
leo new {$Name} --lib
```
This will create a new directory with a given package name. The new package will have a directory structure as follows:
```
- src
- lib.leo # Your program library
- Leo.toml # Your program manifest
```
### `leo init`
To initialize an existing directory, run:
@ -505,6 +591,16 @@ leo init
```
This will initialize the current directory with the same package directory setup.
#### Flags
`leo init` supports the same flags as `leo new`
```rust
leo init --bin
```
```rust
leo init --lib
```
### `leo build`
To compile your program and verify that it builds properly, run:

View File

@ -1,8 +1,4 @@
use crate::{
ast::Rule,
common::LineEnd,
imports::{ImportSource, ImportSymbol},
};
use crate::{ast::Rule, common::LineEnd, imports::Package};
use pest::Span;
use pest_ast::FromPest;
@ -10,8 +6,7 @@ use pest_ast::FromPest;
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::import))]
pub struct Import<'ast> {
pub source: ImportSource<'ast>,
pub symbols: Vec<ImportSymbol<'ast>>,
pub package: Package<'ast>,
pub line_end: LineEnd,
#[pest_ast(outer())]
pub span: Span<'ast>,

View File

@ -1,13 +0,0 @@
use crate::ast::{span_into_string, Rule};
use pest::Span;
use pest_ast::FromPest;
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::import_source))]
pub struct ImportSource<'ast> {
#[pest_ast(outer(with(span_into_string)))]
pub value: String,
#[pest_ast(outer())]
pub span: Span<'ast>,
}

View File

@ -1,8 +1,14 @@
pub mod import;
pub use import::*;
pub mod import_source;
pub use import_source::*;
pub mod import_symbol;
pub use import_symbol::*;
pub mod package;
pub use package::*;
pub mod package_access;
pub use package_access::*;
pub mod star;
pub use star::*;

View File

@ -0,0 +1,13 @@
use crate::{ast::Rule, common::Identifier, imports::PackageAccess};
use pest::Span;
use pest_ast::FromPest;
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::package))]
pub struct Package<'ast> {
pub name: Identifier<'ast>,
pub access: PackageAccess<'ast>,
#[pest_ast(outer())]
pub span: Span<'ast>,
}

View File

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

11
ast/src/imports/star.rs Normal file
View File

@ -0,0 +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<'ast> {
#[pest_ast(outer())]
pub span: Span<'ast>,
}

View File

@ -18,7 +18,7 @@ protected_name = {
LINE_END = { ";" ~ NEWLINE* }
// Declared in common/mutable.rs
mutable = { "mut" }
mutable = { "mut " }
// Declared in common/range.rs
range = { expression? ~ ".." ~ expression? }
@ -33,15 +33,15 @@ spread = { "..." ~ expression }
spread_or_expression = { spread | expression }
// Declared in common/static_.rs
static_ = { "static" }
static_ = { "static " }
// Declared in common/variable.rs
variable = { mutable? ~ identifier ~ (":" ~ type_)? }
// Declared in common/declare.rs
declare = { let_ | const_ }
const_ = { "const" }
let_ = { "let" }
const_ = { "const " }
let_ = { "let " }
/// Operations
@ -175,23 +175,23 @@ access_static_member = { "::" ~ identifier }
/// Circuits
// Declared in circuits/circuit_definition.rs
circuit = { "circuit" ~ identifier ~ "{" ~ NEWLINE* ~ circuit_member* ~ NEWLINE* ~ "}" ~ NEWLINE* }
circuit = { "circuit " ~ identifier ~ "{" ~ NEWLINE* ~ circuit_member* ~ NEWLINE* ~ "}" ~ NEWLINE* }
// Declared in circuits/circuit_field.rs
circuit_field = { identifier ~ ":" ~ expression }
// Declared in circuits/circuit_field_definition.rs
circuit_field_definition = { identifier ~ ":" ~ type_ ~ NEWLINE* }
circuit_field_definition = { identifier ~ ":" ~ type_ ~ ","?}
// Declared in circuits/circuit_function.rs
circuit_function = { static_? ~ function_definition }
// Declared in circuits/circuit_member.rs
circuit_member = { circuit_function | circuit_field_definition }
circuit_member = { circuit_function | circuit_field_definition ~ NEWLINE*}
/// Conditionals
expression_conditional = { "if" ~ expression ~ "?" ~ expression ~ ":" ~ expression}
expression_conditional = { "if " ~ expression ~ "? " ~ expression ~ ": " ~ expression}
/// Expressions
@ -252,7 +252,7 @@ assert_eq = {"assert_eq!" ~ "(" ~ NEWLINE* ~ expression ~ "," ~ NEWLINE* ~ expre
statement_assign = { assignee ~ operation_assign ~ expression ~ LINE_END }
// Declared in statements/conditional_statement.rs
statement_conditional = {"if" ~ (expression | "(" ~ expression ~ ")") ~ "{" ~ NEWLINE* ~ statement+ ~ "}" ~ ("else" ~ conditional_nested_or_end_statement)?}
statement_conditional = {"if " ~ (expression | "(" ~ expression ~ ")") ~ "{" ~ NEWLINE* ~ statement+ ~ "}" ~ ("else" ~ conditional_nested_or_end_statement)?}
conditional_nested_or_end_statement = { statement_conditional | "{" ~ NEWLINE* ~ statement+ ~ "}"}
// Declared in statements/definition_statement.rs
@ -262,40 +262,52 @@ statement_definition = { declare ~ variable ~ "=" ~ expression ~ LINE_END}
statement_expression = { expression ~ LINE_END }
// Declared in statements/for_statement.rs
statement_for = { "for" ~ identifier ~ "in" ~ expression ~ ".." ~ expression ~ "{" ~ NEWLINE* ~ statement+ ~ "}"}
statement_for = { "for " ~ identifier ~ "in " ~ expression ~ ".." ~ expression ~ "{" ~ NEWLINE* ~ statement+ ~ "}"}
// Declared in statements/multiple_assignment_statement.rs
statement_multiple_assignment = { declare ~ "(" ~ variable_tuple ~ ")" ~ "=" ~ identifier ~ "(" ~ expression_tuple ~ ")" ~ LINE_END}
variable_tuple = _{ variable ~ ("," ~ variable)* }
// Declared in statements/return_statement.rs
statement_return = { "return" ~ expression_tuple }
statement_return = { "return " ~ expression_tuple }
/// Functions
// Declared in functions/function.rs
function_definition = { "function" ~ identifier ~ "(" ~ input_model_list ~ ")" ~ ("->" ~ (type_ | "(" ~ type_list ~ ")"))? ~ "{" ~ NEWLINE* ~ statement* ~ NEWLINE* ~ "}" ~ NEWLINE* }
function_definition = { "function " ~ identifier ~ "(" ~ input_model_list ~ ")" ~ ("->" ~ (type_ | "(" ~ type_list ~ ")"))? ~ "{" ~ NEWLINE* ~ statement* ~ NEWLINE* ~ "}" ~ NEWLINE* }
// Declared in functions/function_input.rs
function_input = { mutable? ~ identifier ~ ":" ~ type_ }
input_model_list = _{ (function_input ~ ("," ~ function_input)*)? }
// Declared in functions/test_function.rs
test_function = { "test" ~ function_definition }
test_function = { "test " ~ function_definition }
/// Imports
// Declared in imports/import.rs
import = { "from" ~ "\"" ~ import_source ~ "\"" ~ "import" ~ ("*" | ("{" ~ NEWLINE* ~ import_symbol_tuple ~ NEWLINE* ~ "}") | import_symbol) ~ LINE_END}
import = { "import" ~ package ~ LINE_END}
// Declared in imports/import_source.rs
import_source = @{ (!"\"" ~ ANY)* }
// Declared in imports/package.rs
package = { identifier ~ "." ~ package_access }
// Declared in imports/package_access
package_access = {
star
| package // subpackage
| 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_symbol_tuple = _{ import_symbol ~ ("," ~ NEWLINE* ~ import_symbol)* }
/// Utilities
COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") | ("//" ~ (!NEWLINE ~ ANY)*) }
WHITESPACE = _{ " " | "\t" ~ (NEWLINE)* }
WHITESPACE = _{ " " | "\t" ~ (NEWLINE)* } // pest implicit whitespace keyword

View File

@ -9,18 +9,18 @@ leo-ast = { path = "../ast", version = "0.1.0" }
leo-types = { path = "../types", version = "0.1.0" }
leo-inputs = { path = "../leo-inputs", version = "0.1.0" }
snarkos-curves = { path = "../../snarkOS/curves", version = "0.8.0", default-features = false }
snarkos-errors = { path = "../../snarkOS/errors", version = "0.8.0", default-features = false }
snarkos-gadgets = { path = "../../snarkOS/gadgets", version = "0.8.0", default-features = false }
snarkos-models = { path = "../../snarkOS/models", version = "0.8.0", default-features = false }
snarkos-curves = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-errors = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-gadgets = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-models = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
bincode = { version = "1.0" }
hex = { version = "0.4.2" }
log = { version = "0.4" }
pest = { version = "2.0" }
rand = { version = "0.7" }
sha2 = { version = "0.8" }
sha2 = { version = "0.9" }
thiserror = { version = "1.0" }
[dev-dependencies]
snarkos-utilities = { path = "../../snarkOS/utilities", version = "0.8.0" }
snarkos-utilities = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9" }

View File

@ -66,8 +66,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
// Hash the file contents
let mut hasher = Sha256::new();
hasher.input(unparsed_file.as_bytes());
let hash = hasher.result();
hasher.update(unparsed_file.as_bytes());
let hash = hasher.finalize();
Ok(hex::encode(hash))
}

View File

@ -1,91 +0,0 @@
use crate::{
constraints::{ConstrainedProgram, ConstrainedValue},
errors::constraints::ImportError,
new_scope,
GroupType,
};
use leo_ast::LeoParser;
use leo_types::{Import, Program};
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()))?;
// 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
program
.imports
.into_iter()
.map(|nested_import| self.enforce_import(program_name.clone(), nested_import))
.collect::<Result<Vec<_>, ImportError>>()?;
Ok(())
}
}
}

View File

@ -0,0 +1,13 @@
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)
}
}

View File

@ -0,0 +1,136 @@
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::{read_dir, DirEntry},
};
static LIBRARY_FILE: &str = "src/lib.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> {
// import star from a file
if entry.path().is_file() {
// only parse `.leo` files
if let Some(extension) = entry.path().extension() {
if extension.eq(&OsString::from("leo")) {
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
self.resolve_definitions(program)
} else {
Ok(())
}
} else {
Ok(())
}
} else {
// import star for every file in the directory
for entry in read_dir(entry.path()).map_err(|error| ImportError::directory_error(error, span.clone()))? {
match entry {
Ok(entry) => self.enforce_import_star(scope.clone(), &entry, span.clone())?,
Err(error) => return Err(ImportError::directory_error(error, span.clone())),
}
}
Ok(())
}
}
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(())
}
}

View File

@ -0,0 +1,8 @@
pub mod import;
pub use import::*;
pub mod import_symbol;
pub use import_symbol::*;
pub mod package;
pub use package::*;

View File

@ -0,0 +1,91 @@
use crate::{constraints::ConstrainedProgram, errors::constraints::ImportError, GroupType};
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> {
// 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::Multiple(accesses) => {
for access in accesses {
self.enforce_package_access(scope.clone(), entry, access)?;
}
Ok(())
}
}
}
pub fn enforce_package(&mut self, scope: String, mut path: PathBuf, package: Package) -> Result<(), ImportError> {
let package_name = package.name;
// 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()))?
.into_iter()
.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()
.trim_end_matches(SOURCE_FILE_EXTENSION)
.eq(&package_name.name)
});
if imports_directory.exists() {
let entries = fs::read_dir(imports_directory)
.map_err(|error| ImportError::directory_error(error, package_name.span.clone()))?
.into_iter()
.collect::<Result<Vec<_>, std::io::Error>>()
.map_err(|error| ImportError::directory_error(error, package_name.span.clone()))?;
let matched_import_entry = entries
.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),
(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),
None => Err(ImportError::unknown_package(package_name)),
}
}
}
}

View File

@ -59,7 +59,6 @@ pub fn generate_constraints<F: Field + PrimeField, G: GroupType<F>, CS: Constrai
match main.clone() {
ConstrainedValue::Function(_circuit_identifier, function) => {
let result = resolved_program.enforce_main_function(cs, program_name, function, parameters)?;
log::debug!("{}", result);
Ok(result)
}
_ => Err(CompilerError::NoMainFunction),

View File

@ -2,7 +2,7 @@
use crate::{
constraints::{ConstrainedProgram, ConstrainedValue},
errors::StatementError,
errors::{StatementError, ValueError},
new_scope,
GroupType,
Integer,
@ -21,7 +21,6 @@ use leo_types::{
Variable,
};
use crate::errors::ValueError;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::{

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};
@ -17,12 +17,42 @@ impl ImportError {
ImportError::Error(FormattedError::new_from_span(message, span))
}
pub fn directory_error(error: io::Error, span: Span) -> Self {
let message = format!("attempt to access current directory failed - {:?}", error);
pub fn conflicting_imports(identifier: Identifier) -> Self {
let message = format!("conflicting imports found for `{}`", identifier.name);
Self::new_from_span(message, identifier.span)
}
pub fn convert_os_string(span: Span) -> Self {
let message = format!("failed to convert file string name, maybe an illegal character?");
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);
Self::new_from_span(message, span)
}
pub fn expected_lib_file(entry: String, span: Span) -> Self {
let message = format!(
"expected library file`{}` when looking for symbol `{}`",
entry, span.text
);
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 {
let message = format!("cannot find imported symbol `{}` in imported file `{}`", symbol, file);
let mut error = FormattedError::new_from_span(message, symbol.span);

View File

@ -1,5 +1,5 @@
circuit Circ {
x: u32
x: u32,
}
function main() -> u32 {

View File

@ -1,5 +1,5 @@
circuit Foo {
foo: u32
foo: u32,
static function bar() -> u32 {
return 0

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

@ -0,0 +1 @@
outputs/

View File

@ -0,0 +1,3 @@
[package]
name = "bar"
version = "0.1.0"

View File

@ -0,0 +1,3 @@
circuit Bat {
t: u32
}

View File

@ -0,0 +1,3 @@
circuit Baz {
z: u32
}

View File

@ -0,0 +1,3 @@
circuit Bar {
r: u32
}

View File

@ -0,0 +1 @@
outputs/

View File

@ -0,0 +1,3 @@
[package]
name = "car"
version = "0.1.0"

View File

@ -0,0 +1,3 @@
circuit Car {
c: u32
}

View File

@ -0,0 +1,25 @@
import test_import.( // local import
Point,
foo,
);
import bar.( // imports directory import
Bar,
baz.Baz,
bat.bat.Bat,
);
import car.Car; // imports directory import
function main() -> u32 {
// const point = Point { x: 1u32, y: 1u32 };
// const foo = foo();
const bar = Bar { r: 1u32 };
const bat = Bat { t: 1u32 };
const baz = Baz { z: 1u32 };
const car = Car { c: 1u32 };
return car.c
}

View File

@ -0,0 +1,16 @@
import test_import.*; // local import
import bar.*; // imports directory import
import car.*; // imports directory import
function main() -> u32 {
const point = Point { x: 1u32, y: 1u32 };
const foo = foo();
const bar = Bar { r: 1u32 };
const bat = Bat { t: 1u32 };
const baz = Baz { z: 1u32 };
const car = Car { c: 1u32 };
return car.c
}

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,30 @@ fn test_alias() {
let bytes = include_bytes!("alias.leo");
let program = parse_program(bytes).unwrap();
set_local_dir();
output_one(program);
}
// more complex tests
#[test]
#[ignore]
fn test_many_import() {
let bytes = include_bytes!("many_import.leo");
let program = parse_program(bytes).unwrap();
set_local_dir();
output_one(program);
}
#[test]
#[ignore]
fn test_many_import_star() {
let bytes = include_bytes!("many_import_star.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

@ -15,8 +15,8 @@ circuit PedersenHash {
// The 'pedersen_hash' main function.
function main() -> u32 {
let parameters = [0u32; 512];
let pedersen = PedersenHash::new(parameters);
const parameters = [0u32; 512];
const pedersen = PedersenHash::new(parameters);
let input: bool[512] = [true; 512];
return pedersen.hash(input)
}

View File

@ -5,11 +5,11 @@ authors = ["The Aleo Team <hello@aleo.org>"]
edition = "2018"
[dependencies]
snarkos-algorithms = { path = "../../snarkOS/algorithms", version = "0.8.0", default-features = false }
snarkos-curves = { path = "../../snarkOS/curves", version = "0.8.0", default-features = false }
snarkos-errors = { path = "../../snarkOS/errors", version = "0.8.0", default-features = false }
snarkos-gadgets = { path = "../../snarkOS/gadgets", version = "0.8.0", default-features = false }
snarkos-models = { path = "../../snarkOS/models", version = "0.8.0", default-features = false }
snarkos-algorithms = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-curves = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-errors = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-gadgets = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-models = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
from-pest = { version = "0.3.1" }
pest = { version = "2.0" }

View File

@ -1,4 +1,4 @@
use crate::{cli_types::*, errors::CLIError};
use crate::{cli_types::*, errors::CLIError, logger};
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
@ -66,6 +66,19 @@ pub trait CLI {
.subcommands(subcommands)
}
#[cfg_attr(tarpaulin, skip)]
fn process(arguments: &ArgMatches) -> Result<(), CLIError> {
// Set logging environment
match arguments.is_present("debug") {
true => logger::init_logger("leo", 2),
false => logger::init_logger("leo", 1),
}
let options = Self::parse(arguments)?;
let _output = Self::output(options)?;
Ok(())
}
#[cfg_attr(tarpaulin, skip)]
fn parse(arguments: &ArgMatches) -> Result<Self::Options, CLIError>;

View File

@ -2,15 +2,15 @@ use crate::{
cli::*,
cli_types::*,
directories::{source::SOURCE_DIRECTORY_NAME, OutputsDirectory},
errors::{BuildError, CLIError},
files::{ChecksumFile, MainFile, Manifest, MAIN_FILE_NAME},
errors::CLIError,
files::{ChecksumFile, LibFile, MainFile, Manifest, LIB_FILE_NAME, MAIN_FILE_NAME},
};
use leo_compiler::{compiler::Compiler, group::edwards_bls12::EdwardsGroupType};
use snarkos_algorithms::snark::KeypairAssembly;
use snarkos_curves::{bls12_377::Bls12_377, edwards_bls12::Fq};
use snarkos_models::gadgets::r1cs::ConstraintSystem;
use crate::files::BytesFile;
use clap::ArgMatches;
use std::{convert::TryFrom, env::current_dir};
@ -19,7 +19,7 @@ pub struct BuildCommand;
impl CLI for BuildCommand {
type Options = ();
type Output = (Compiler<Fq, EdwardsGroupType>, bool);
type Output = Option<(Compiler<Fq, EdwardsGroupType>, bool)>;
const ABOUT: AboutType = "Compile the current package as a program";
const ARGUMENTS: &'static [ArgumentType] = &[];
@ -47,80 +47,75 @@ impl CLI for BuildCommand {
package_path.pop();
}
// Verify the main file exists
if !MainFile::exists_at(&package_path) {
return Err(BuildError::MainFileDoesNotExist(package_path.as_os_str().to_owned()).into());
}
// Compile the package starting with the lib.leo file
if LibFile::exists_at(&package_path) {
// Construct the path to the library file in the source directory
let mut lib_file_path = package_path.clone();
lib_file_path.push(SOURCE_DIRECTORY_NAME);
lib_file_path.push(LIB_FILE_NAME);
// Create the outputs directory
OutputsDirectory::create(&package_path)?;
// Compile the library file but do not output
let _program = Compiler::<Fq, EdwardsGroupType>::new_from_path(package_name.clone(), lib_file_path)?;
};
// Construct the path to the main file in the source directory
let mut main_file_path = package_path.clone();
main_file_path.push(SOURCE_DIRECTORY_NAME);
main_file_path.push(MAIN_FILE_NAME);
// Compile the main.leo file along with constraints
if MainFile::exists_at(&package_path) {
// Create the outputs directory
OutputsDirectory::create(&package_path)?;
// Check if the program bytes exist
let existing_bytes = BytesFile::new(&package_name).exists_at(&path);
// Construct the path to the main file in the source directory
let mut main_file_path = package_path.clone();
main_file_path.push(SOURCE_DIRECTORY_NAME);
main_file_path.push(MAIN_FILE_NAME);
let program = if existing_bytes {
// Load the program ast from stored bytes
let bytes = BytesFile::new(&package_name).read_from(&path)?;
let mut program = Compiler::<Fq, EdwardsGroupType>::from_bytes(bytes.as_slice())?;
program.set_path(main_file_path.clone());
program
} else {
// Load the program at `main_file_path`
let program =
Compiler::<Fq, EdwardsGroupType>::new_from_path(package_name.clone(), main_file_path.clone())?;
// Store the program ast as bytes
let bytes = program.to_bytes()?;
// Compute the current program checksum
let program_checksum = program.checksum()?;
BytesFile::new(&package_name).write_to(&path, bytes)?;
// Generate the program on the constraint system and verify correctness
{
let mut cs = KeypairAssembly::<Bls12_377> {
num_inputs: 0,
num_aux: 0,
num_constraints: 0,
at: vec![],
bt: vec![],
ct: vec![],
};
let temporary_program = program.clone();
let output = temporary_program.compile_constraints(&mut cs)?;
log::debug!("Compiled constraints - {:#?}", output);
log::debug!("Number of constraints - {:#?}", cs.num_constraints());
}
program
};
// Compute the current program checksum
let program_checksum = program.checksum()?;
// Generate the program on the constraint system and verify correctness
{
let mut cs = KeypairAssembly::<Bls12_377> {
num_inputs: 0,
num_aux: 0,
num_constraints: 0,
at: vec![],
bt: vec![],
ct: vec![],
// If a checksum file exists, check if it differs from the new checksum
let checksum_file = ChecksumFile::new(&package_name);
let checksum_differs = if checksum_file.exists_at(&package_path) {
let previous_checksum = checksum_file.read_from(&package_path)?;
program_checksum != previous_checksum
} else {
// By default, the checksum differs if there is no checksum to compare against
true
};
let temporary_program = program.clone();
let output = temporary_program.compile_constraints(&mut cs)?;
log::debug!("Compiled constraints - {:#?}", output);
// If checksum differs, compile the program
if checksum_differs {
// Write the new checksum to the outputs directory
checksum_file.write_to(&path, program_checksum)?;
log::debug!("Checksum saved ({:?})", path);
}
log::info!("Compiled program in {:?}", main_file_path);
return Ok(Some((program, checksum_differs)));
}
// If a checksum file exists, check if it differs from the new checksum
let checksum_file = ChecksumFile::new(&package_name);
let checksum_differs = if checksum_file.exists_at(&package_path) {
let previous_checksum = checksum_file.read_from(&package_path)?;
program_checksum != previous_checksum
} else {
// By default, the checksum differs if there is no checksum to compare against
true
};
// If checksum differs, compile the program
if checksum_differs {
// Write the new checksum to the outputs directory
checksum_file.write_to(&path, program_checksum)?;
}
log::info!("Compiled program in {:?}", main_file_path);
Ok((program, checksum_differs))
// Return None when compiling a package for publishing
// The published package does not need to have a main.leo
Ok(None)
}
}

50
leo/commands/clean.rs Normal file
View File

@ -0,0 +1,50 @@
use crate::{
cli::*,
cli_types::*,
errors::CLIError,
files::{ChecksumFile, Manifest, ProofFile, ProvingKeyFile, VerificationKeyFile},
};
use clap::ArgMatches;
use std::{convert::TryFrom, env::current_dir};
#[derive(Debug)]
pub struct CleanCommand;
impl CLI for CleanCommand {
type Options = ();
type Output = ();
const ABOUT: AboutType = "Clean the outputs directory";
const ARGUMENTS: &'static [ArgumentType] = &[];
const FLAGS: &'static [FlagType] = &[];
const NAME: NameType = "clean";
const OPTIONS: &'static [OptionType] = &[];
const SUBCOMMANDS: &'static [SubCommandType] = &[];
#[cfg_attr(tarpaulin, skip)]
fn parse(_arguments: &ArgMatches) -> Result<Self::Options, CLIError> {
Ok(())
}
#[cfg_attr(tarpaulin, skip)]
fn output(_options: Self::Options) -> Result<Self::Output, CLIError> {
// Get the package name
let path = current_dir()?;
let package_name = Manifest::try_from(&path)?.get_package_name();
// Remove the checksum from the outputs directory
ChecksumFile::new(&package_name).remove(&path)?;
// Remove the proving key from the outputs directory
ProvingKeyFile::new(&package_name).remove(&path)?;
// Remove the verification key from the outputs directory
VerificationKeyFile::new(&package_name).remove(&path)?;
// Remove the proof from the outputs directory
ProofFile::new(&package_name).remove(&path)?;
Ok(())
}
}

View File

@ -1,4 +1,11 @@
use crate::{cli::*, cli_types::*, commands::BuildCommand, errors::CLIError, files::Manifest};
use crate::{
cli::*,
cli_types::*,
commands::BuildCommand,
directories::SOURCE_DIRECTORY_NAME,
errors::{CLIError, RunError},
files::{Manifest, MAIN_FILE_NAME},
};
use clap::ArgMatches;
use std::{convert::TryFrom, env::current_dir};
@ -24,14 +31,26 @@ impl CLI for DeployCommand {
#[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
let (_program, _checksum_differs) = BuildCommand::output(options)?;
// Get the package name
let path = current_dir()?;
let _package_name = Manifest::try_from(&path)?.get_package_name();
log::info!("Unimplemented - `leo deploy`");
match BuildCommand::output(options)? {
Some((_program, _checksum_differs)) => {
// Get the package name
let _package_name = Manifest::try_from(&path)?.get_package_name();
Ok(())
log::info!("Unimplemented - `leo deploy`");
Ok(())
}
None => {
let mut main_file_path = path.clone();
main_file_path.push(SOURCE_DIRECTORY_NAME);
main_file_path.push(MAIN_FILE_NAME);
Err(CLIError::RunError(RunError::MainFileDoesNotExist(
main_file_path.into_os_string(),
)))
}
}
}
}

View File

@ -3,7 +3,7 @@ use crate::{
cli_types::*,
directories::{InputsDirectory, SourceDirectory},
errors::{CLIError, InitError},
files::{Gitignore, InputsFile, MainFile, Manifest},
files::{Gitignore, InputsFile, LibFile, MainFile, Manifest},
};
use clap::ArgMatches;
@ -13,35 +13,31 @@ use std::env::current_dir;
pub struct InitCommand;
impl CLI for InitCommand {
type Options = Option<String>;
type Options = bool;
type Output = ();
const ABOUT: AboutType = "Create a new Leo package in an existing directory";
const ARGUMENTS: &'static [ArgumentType] = &[];
const FLAGS: &'static [FlagType] = &[];
const FLAGS: &'static [FlagType] = &[("--lib"), ("--bin")];
const NAME: NameType = "init";
const OPTIONS: &'static [OptionType] = &[];
const SUBCOMMANDS: &'static [SubCommandType] = &[];
#[cfg_attr(tarpaulin, skip)]
fn parse(_arguments: &ArgMatches) -> Result<Self::Options, CLIError> {
Ok(None)
fn parse(arguments: &ArgMatches) -> Result<Self::Options, CLIError> {
Ok(arguments.is_present("lib"))
}
#[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
let name = options;
let path = current_dir()?;
// Derive the package name
let package_name = match name {
Some(name) => name,
None => path
.file_stem()
.ok_or_else(|| InitError::ProjectNameInvalid(path.as_os_str().to_owned()))?
.to_string_lossy()
.to_string(),
};
let package_name = path
.file_stem()
.ok_or_else(|| InitError::ProjectNameInvalid(path.as_os_str().to_owned()))?
.to_string_lossy()
.to_string();
// Verify the directory exists
if !path.exists() {
@ -62,20 +58,30 @@ impl CLI for InitCommand {
// Create the source directory
SourceDirectory::create(&path)?;
// Create the inputs directory
InputsDirectory::create(&path)?;
// Create a new library or binary file
// Verify the inputs file does not exist
let inputs_file = InputsFile::new(&package_name);
if !inputs_file.exists_at(&path) {
// Create the inputs file in the inputs directory
inputs_file.write_to(&path)?;
}
if options {
// Verify the library file does not exist
if !LibFile::exists_at(&path) {
// Create the library file in the source directory
LibFile::new(&package_name).write_to(&path)?;
}
} else {
// Create the inputs directory
InputsDirectory::create(&path)?;
// Verify the main file does not exist
if !MainFile::exists_at(&path) {
// Create the main file in the source directory
MainFile::new(&package_name).write_to(&path)?;
// Verify the inputs file does not exist
let inputs_file = InputsFile::new(&package_name);
if !inputs_file.exists_at(&path) {
// Create the inputs file in the inputs directory
inputs_file.write_to(&path)?;
}
// Verify the main file does not exist
if !MainFile::exists_at(&path) {
// Create the main file in the source directory
MainFile::new(&package_name).write_to(&path)?;
}
}
Ok(())

View File

@ -1,4 +1,11 @@
use crate::{cli::*, cli_types::*, commands::BuildCommand, errors::CLIError, files::Manifest};
use crate::{
cli::*,
cli_types::*,
commands::BuildCommand,
directories::SOURCE_DIRECTORY_NAME,
errors::{CLIError, RunError},
files::{Manifest, MAIN_FILE_NAME},
};
use clap::ArgMatches;
use std::{convert::TryFrom, env::current_dir};
@ -24,14 +31,25 @@ impl CLI for LoadCommand {
#[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
let (_program, _checksum_differs) = BuildCommand::output(options)?;
// Get the package name
let path = current_dir()?;
let _package_name = Manifest::try_from(&path)?.get_package_name();
match BuildCommand::output(options)? {
Some((_program, _checksum_differs)) => {
// Get the package name
let _package_name = Manifest::try_from(&path)?.get_package_name();
log::info!("Unimplemented - `leo deploy`");
log::info!("Unimplemented - `leo load`");
Ok(())
Ok(())
}
None => {
let mut main_file_path = path.clone();
main_file_path.push(SOURCE_DIRECTORY_NAME);
main_file_path.push(MAIN_FILE_NAME);
Err(CLIError::RunError(RunError::MainFileDoesNotExist(
main_file_path.into_os_string(),
)))
}
}
}
}

View File

@ -1,6 +1,9 @@
pub mod build;
pub use self::build::*;
pub mod clean;
pub use self::clean::*;
pub mod deploy;
pub use self::deploy::*;

View File

@ -3,7 +3,7 @@ use crate::{
cli_types::*,
directories::{InputsDirectory, SourceDirectory},
errors::{CLIError, NewError},
files::{Gitignore, InputsFile, MainFile, Manifest},
files::{Gitignore, InputsFile, LibFile, MainFile, Manifest},
};
use clap::ArgMatches;
@ -13,7 +13,7 @@ use std::{env::current_dir, fs};
pub struct NewCommand;
impl CLI for NewCommand {
type Options = Option<String>;
type Options = (Option<String>, bool);
type Output = ();
const ABOUT: AboutType = "Create a new Leo package in a new directory";
@ -26,16 +26,21 @@ impl CLI for NewCommand {
1u64,
),
];
const FLAGS: &'static [FlagType] = &[];
const FLAGS: &'static [FlagType] = &[("--lib"), ("--bin")];
const NAME: NameType = "new";
const OPTIONS: &'static [OptionType] = &[];
const SUBCOMMANDS: &'static [SubCommandType] = &[];
#[cfg_attr(tarpaulin, skip)]
fn parse(arguments: &ArgMatches) -> Result<Self::Options, CLIError> {
let mut is_lib = false;
if arguments.is_present("lib") {
is_lib = true;
}
match arguments.value_of("NAME") {
Some(name) => Ok(Some(name.to_string())),
None => Ok(None),
Some(name) => Ok((Some(name.to_string()), is_lib)),
None => Ok((None, is_lib)),
}
}
@ -44,7 +49,7 @@ impl CLI for NewCommand {
let mut path = current_dir()?;
// Derive the package name
let package_name = match options {
let package_name = match options.0 {
Some(name) => name,
None => path
.file_stem()
@ -74,14 +79,20 @@ impl CLI for NewCommand {
// Create the source directory
SourceDirectory::create(&path)?;
// Create the inputs directory
InputsDirectory::create(&path)?;
// Create a new library or binary file
if options.1 {
// Create the library file in the source directory
LibFile::new(&package_name).write_to(&path)?;
} else {
// Create the inputs directory
InputsDirectory::create(&path)?;
// Create the inputs file in the inputs directory
InputsFile::new(&package_name).write_to(&path)?;
// Create the inputs file in the inputs directory
InputsFile::new(&package_name).write_to(&path)?;
// Create the main file in the source directory
MainFile::new(&package_name).write_to(&path)?;
// Create the main file in the source directory
MainFile::new(&package_name).write_to(&path)?;
}
Ok(())
}

View File

@ -40,6 +40,8 @@ impl CLI for ProveCommand {
let path = current_dir()?;
let package_name = Manifest::try_from(&path)?.get_package_name();
log::info!("Proving...");
// Fetch program inputs here
let inputs_string = InputsFile::new(&package_name).read_from(&path)?;
program.parse_inputs(&inputs_string)?;

View File

@ -1,4 +1,11 @@
use crate::{cli::*, cli_types::*, commands::BuildCommand, errors::CLIError, files::Manifest};
use crate::{
cli::*,
cli_types::*,
commands::BuildCommand,
directories::outputs::OutputsDirectory,
errors::CLIError,
files::{Manifest, ZipFile},
};
use clap::ArgMatches;
use std::{convert::TryFrom, env::current_dir};
@ -24,13 +31,24 @@ impl CLI for PublishCommand {
#[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
let (_program, _checksum_differs) = BuildCommand::output(options)?;
// Build all program files.
// It's okay if there's just a lib.leo file here
let _output = BuildCommand::output(options)?;
// Get the package name
let path = current_dir()?;
let _package_name = Manifest::try_from(&path)?.get_package_name();
let package_name = Manifest::try_from(&path)?.get_package_name();
log::info!("Unimplemented - `leo publish`");
// Create the outputs directory
OutputsDirectory::create(&path)?;
// Create zip file
let zip_file = ZipFile::new(&package_name);
if zip_file.exists_at(&path) {
log::info!("Existing package zip file found. Skipping compression.")
} else {
zip_file.write(&path)?;
}
Ok(())
}

View File

@ -2,8 +2,9 @@ use crate::{
cli::*,
cli_types::*,
commands::BuildCommand,
errors::{CLIError, VerificationKeyFileError},
files::{Manifest, ProvingKeyFile, VerificationKeyFile},
directories::SOURCE_DIRECTORY_NAME,
errors::{CLIError, RunError, VerificationKeyFileError},
files::{Manifest, ProvingKeyFile, VerificationKeyFile, MAIN_FILE_NAME},
};
use leo_compiler::{compiler::Compiler, group::edwards_bls12::EdwardsGroupType};
@ -40,66 +41,87 @@ impl CLI for SetupCommand {
#[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
let (program, checksum_differs) = BuildCommand::output(options)?;
// Get the package name
let path = current_dir()?;
let package_name = Manifest::try_from(&path)?.get_package_name();
// Check if a proving key and verification key already exists
let keys_exist = ProvingKeyFile::new(&package_name).exists_at(&path)
&& VerificationKeyFile::new(&package_name).exists_at(&path);
match BuildCommand::output(options)? {
Some((program, checksum_differs)) => {
// Check if a proving key and verification key already exists
let keys_exist = ProvingKeyFile::new(&package_name).exists_at(&path)
&& VerificationKeyFile::new(&package_name).exists_at(&path);
// If keys do not exist or the checksum differs, run the program setup
if !keys_exist || checksum_differs {
// Start the timer
let start = Instant::now();
// If keys do not exist or the checksum differs, run the program setup
// If keys do not exist or the checksum differs, run the program setup
if !keys_exist || checksum_differs {
log::info!("Setup starting...");
// Run the program setup operation
let rng = &mut thread_rng();
let parameters = generate_random_parameters::<Bls12_377, _, _>(program.clone(), rng).unwrap();
let prepared_verifying_key = prepare_verifying_key::<Bls12_377>(&parameters.vk);
// Start the timer
let start = Instant::now();
// End the timer
log::info!("Setup completed in {:?} milliseconds", start.elapsed().as_millis());
// Run the program setup operation
let rng = &mut thread_rng();
let parameters = generate_random_parameters::<Bls12_377, _, _>(program.clone(), rng).unwrap();
let prepared_verifying_key = prepare_verifying_key::<Bls12_377>(&parameters.vk);
// TODO (howardwu): Convert parameters to a 'proving key' struct for serialization.
// Write the proving key file to the outputs directory
let mut proving_key = vec![];
parameters.write(&mut proving_key)?;
ProvingKeyFile::new(&package_name).write_to(&path, &proving_key)?;
// End the timer
let end = start.elapsed().as_millis();
// Write the proving key file to the outputs directory
let mut verification_key = vec![];
prepared_verifying_key.write(&mut verification_key)?;
VerificationKeyFile::new(&package_name).write_to(&path, &verification_key)?;
}
// TODO (howardwu): Convert parameters to a 'proving key' struct for serialization.
// Write the proving key file to the outputs directory
let mut proving_key = vec![];
parameters.write(&mut proving_key)?;
ProvingKeyFile::new(&package_name).write_to(&path, &proving_key)?;
log::info!("Saving proving key ({:?})", path);
// Read the proving key file from the outputs directory
let proving_key = ProvingKeyFile::new(&package_name).read_from(&path)?;
let parameters = Parameters::<Bls12_377>::read(proving_key.as_slice(), true)?;
// Write the verification key file to the outputs directory
let mut verification_key = vec![];
prepared_verifying_key.write(&mut verification_key)?;
VerificationKeyFile::new(&package_name).write_to(&path, &verification_key)?;
log::info!("Saving verification key ({:?})", path);
// Read the proving key file from the outputs directory
let prepared_verifying_key = prepare_verifying_key::<Bls12_377>(&parameters.vk);
{
// Load the stored verification key from the outputs directory
let stored_vk = VerificationKeyFile::new(&package_name).read_from(&path)?;
// Convert the prepared_verifying_key to a buffer
let mut verification_key = vec![];
prepared_verifying_key.write(&mut verification_key)?;
// Check that the constructed prepared verification key matches the stored verification key
let compare: Vec<(u8, u8)> = verification_key.into_iter().zip(stored_vk.into_iter()).collect();
for (a, b) in compare {
if a != b {
return Err(VerificationKeyFileError::IncorrectVerificationKey.into());
// Output the setup time
log::info!("Setup completed in {:?} milliseconds", end);
} else {
log::info!("Setup complete");
}
// Read the proving key file from the outputs directory
let proving_key = ProvingKeyFile::new(&package_name).read_from(&path)?;
let parameters = Parameters::<Bls12_377>::read(proving_key.as_slice(), true)?;
// Read the proving key file from the outputs directory
let prepared_verifying_key = prepare_verifying_key::<Bls12_377>(&parameters.vk);
{
// Load the stored verification key from the outputs directory
let stored_vk = VerificationKeyFile::new(&package_name).read_from(&path)?;
// Convert the prepared_verifying_key to a buffer
let mut verification_key = vec![];
prepared_verifying_key.write(&mut verification_key)?;
// Check that the constructed prepared verification key matches the stored verification key
let compare: Vec<(u8, u8)> = verification_key.into_iter().zip(stored_vk.into_iter()).collect();
for (a, b) in compare {
if a != b {
return Err(VerificationKeyFileError::IncorrectVerificationKey.into());
}
}
}
log::info!("Program setup complete");
Ok((program, parameters, prepared_verifying_key))
}
None => {
let mut main_file_path = path.clone();
main_file_path.push(SOURCE_DIRECTORY_NAME);
main_file_path.push(MAIN_FILE_NAME);
Err(CLIError::RunError(RunError::MainFileDoesNotExist(
main_file_path.into_os_string(),
)))
}
}
log::info!("Completed program setup");
Ok((program, parameters, prepared_verifying_key))
}
}

View File

@ -1,4 +1,11 @@
use crate::{cli::*, cli_types::*, commands::BuildCommand, errors::CLIError, files::Manifest};
use crate::{
cli::*,
cli_types::*,
commands::BuildCommand,
directories::SOURCE_DIRECTORY_NAME,
errors::{CLIError, RunError},
files::{Manifest, MAIN_FILE_NAME},
};
use clap::ArgMatches;
use std::{convert::TryFrom, env::current_dir};
@ -24,14 +31,26 @@ impl CLI for UnloadCommand {
#[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
let (_program, _checksum_differs) = BuildCommand::output(options)?;
// Get the package name
let path = current_dir()?;
let _package_name = Manifest::try_from(&path)?.get_package_name();
log::info!("Unimplemented - `leo deploy`");
match BuildCommand::output(options)? {
Some((_program, _checksum_differs)) => {
// Get the package name
let _package_name = Manifest::try_from(&path)?.get_package_name();
Ok(())
log::info!("Unimplemented - `leo load`");
Ok(())
}
None => {
let mut main_file_path = path.clone();
main_file_path.push(SOURCE_DIRECTORY_NAME);
main_file_path.push(MAIN_FILE_NAME);
Err(CLIError::RunError(RunError::MainFileDoesNotExist(
main_file_path.into_os_string(),
)))
}
}
}
}

View File

@ -0,0 +1,33 @@
use crate::errors::ImportsDirectoryError;
use std::{fs, path::PathBuf};
pub(crate) static IMPORTS_DIRECTORY_NAME: &str = "imports/";
pub struct ImportsDirectory;
impl ImportsDirectory {
/// Creates a directory at the provided path with the default directory name.
pub fn create(path: &PathBuf) -> Result<(), ImportsDirectoryError> {
let mut path = path.to_owned();
if path.is_dir() && !path.ends_with(IMPORTS_DIRECTORY_NAME) {
path.push(PathBuf::from(IMPORTS_DIRECTORY_NAME));
}
fs::create_dir_all(&path).map_err(ImportsDirectoryError::Creating)
}
/// Removes the directory at the provided path.
pub fn remove(path: &PathBuf) -> Result<(), ImportsDirectoryError> {
let mut path = path.to_owned();
if path.is_dir() && !path.ends_with(IMPORTS_DIRECTORY_NAME) {
path.push(PathBuf::from(IMPORTS_DIRECTORY_NAME));
}
if path.exists() {
fs::remove_dir_all(&path).map_err(ImportsDirectoryError::Removing)?;
}
Ok(())
}
}

View File

@ -1,3 +1,6 @@
pub mod imports;
pub use self::imports::*;
pub mod inputs;
pub use self::inputs::*;

View File

@ -6,7 +6,7 @@ pub enum CLIError {
BuildError(BuildError),
#[error("{}", _0)]
BytesFileError(BytesFileError),
BytesFileError(ZipFileError),
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
@ -26,6 +26,9 @@ pub enum CLIError {
#[error("{}", _0)]
InputsFileError(InputsFileError),
#[error("{}", _0)]
LibFileError(LibFileError),
#[error("{}", _0)]
MainFileError(MainFileError),
@ -57,8 +60,8 @@ pub enum CLIError {
VerificationKeyFileError(VerificationKeyFileError),
}
impl From<BytesFileError> for CLIError {
fn from(error: BytesFileError) -> Self {
impl From<ZipFileError> for CLIError {
fn from(error: ZipFileError) -> Self {
log::error!("{}\n", error);
CLIError::BytesFileError(error)
}
@ -106,6 +109,13 @@ impl From<InputsFileError> for CLIError {
}
}
impl From<LibFileError> for CLIError {
fn from(error: LibFileError) -> Self {
log::error!("{}\n", error);
CLIError::LibFileError(error)
}
}
impl From<MainFileError> for CLIError {
fn from(error: MainFileError) -> Self {
log::error!("{}\n", error);

View File

@ -0,0 +1,28 @@
use std::{ffi::OsString, fs::FileType, io};
#[derive(Debug, Error)]
pub enum ImportsDirectoryError {
#[error("creating: {}", _0)]
Creating(io::Error),
#[error("file entry getting: {}", _0)]
GettingFileEntry(io::Error),
#[error("file {:?} extension getting", _0)]
GettingFileExtension(OsString),
#[error("file {:?} type getting: {}", _0, _1)]
GettingFileType(OsString, io::Error),
#[error("invalid file {:?} extension: {:?}", _0, _1)]
InvalidFileExtension(OsString, OsString),
#[error("invalid file {:?} type: {:?}", _0, _1)]
InvalidFileType(OsString, FileType),
#[error("reading: {}", _0)]
Reading(io::Error),
#[error("removing: {}", _0)]
Removing(io::Error),
}

View File

@ -1,3 +1,6 @@
pub mod imports;
pub use self::imports::*;
pub mod inputs;
pub use self::inputs::*;

View File

@ -11,6 +11,9 @@ pub enum ChecksumFileError {
#[error("Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf),
#[error("Cannot remove the provided file - {:?}", _0)]
FileRemovalError(PathBuf),
#[error("writing: {}", _0)]
Writing(io::Error),
}

19
leo/errors/files/lib.rs Normal file
View File

@ -0,0 +1,19 @@
use std::io;
#[derive(Debug, Error)]
pub enum LibFileError {
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
#[error("creating: {}", _0)]
Creating(io::Error),
#[error("writing: {}", _0)]
Writing(io::Error),
}
impl From<std::io::Error> for LibFileError {
fn from(error: std::io::Error) -> Self {
LibFileError::Crate("std::io", format!("{}", error))
}
}

View File

@ -1,5 +1,5 @@
pub mod bytes;
pub use self::bytes::*;
pub mod zip;
pub use self::zip::*;
pub mod checksum;
pub use self::checksum::*;
@ -10,6 +10,9 @@ pub use self::gitignore::*;
pub mod inputs;
pub use self::inputs::*;
pub mod lib;
pub use self::lib::*;
pub mod main;
pub use self::main::*;

View File

@ -11,6 +11,9 @@ pub enum ProofFileError {
#[error("Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf),
#[error("Cannot remove the provided file - {:?}", _0)]
FileRemovalError(PathBuf),
#[error("writing: {}", _0)]
Writing(io::Error),
}

View File

@ -11,6 +11,9 @@ pub enum ProvingKeyFileError {
#[error("Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf),
#[error("Cannot remove the provided file - {:?}", _0)]
FileRemovalError(PathBuf),
#[error("writing: {}", _0)]
Writing(io::Error),
}

View File

@ -11,6 +11,9 @@ pub enum VerificationKeyFileError {
#[error("Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf),
#[error("Cannot remove the provided file - {:?}", _0)]
FileRemovalError(PathBuf),
#[error("Verification key file was corrupted")]
IncorrectVerificationKey,

View File

@ -1,7 +1,9 @@
use std::{io, path::PathBuf};
use walkdir::Error as WalkDirError;
use zip::result::ZipError;
#[derive(Debug, Error)]
pub enum BytesFileError {
pub enum ZipFileError {
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
@ -13,10 +15,16 @@ pub enum BytesFileError {
#[error("writing: {}", _0)]
Writing(io::Error),
#[error("{}", _0)]
WalkDirError(#[from] WalkDirError),
#[error("{}", _0)]
ZipError(#[from] ZipError),
}
impl From<std::io::Error> for BytesFileError {
impl From<std::io::Error> for ZipFileError {
fn from(error: std::io::Error) -> Self {
BytesFileError::Crate("std::io", format!("{}", error))
ZipFileError::Crate("std::io", format!("{}", error))
}
}

View File

@ -1,60 +0,0 @@
//! The program bytes file.
use crate::{directories::outputs::OUTPUTS_DIRECTORY_NAME, errors::BytesFileError};
use serde::Deserialize;
use std::{
fs::{self, File},
io::Write,
path::PathBuf,
};
pub static BYTES_FILE_EXTENSION: &str = ".bytes";
#[derive(Deserialize)]
pub struct BytesFile {
pub package_name: String,
}
impl BytesFile {
pub fn new(package_name: &str) -> Self {
Self {
package_name: package_name.to_string(),
}
}
pub fn exists_at(&self, path: &PathBuf) -> bool {
let path = self.setup_file_path(path);
path.exists()
}
/// Reads the program bytes from the given file path if it exists.
pub fn read_from(&self, path: &PathBuf) -> Result<Vec<u8>, BytesFileError> {
let path = self.setup_file_path(path);
Ok(fs::read(&path).map_err(|_| BytesFileError::FileReadError(path.clone()))?)
}
/// Writes the given program bytes to a file.
pub fn write_to(&self, path: &PathBuf, bytes: Vec<u8>) -> Result<(), BytesFileError> {
let path = self.setup_file_path(path);
let mut file = File::create(&path)?;
file.write_all(bytes.as_slice())?;
log::info!("program bytes stored to {:?}", path);
Ok(())
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned();
if path.is_dir() {
if !path.ends_with(OUTPUTS_DIRECTORY_NAME) {
path.push(PathBuf::from(OUTPUTS_DIRECTORY_NAME));
}
path.push(PathBuf::from(format!("{}{}", self.package_name, BYTES_FILE_EXTENSION)));
}
path
}
}

View File

@ -42,11 +42,21 @@ impl ChecksumFile {
let mut file = File::create(&path)?;
file.write_all(checksum.as_bytes())?;
log::info!("Checksum stored to {:?}", path);
Ok(())
}
/// Removes the checksum at the given path if it exists. Returns `true` on success,
/// `false` if the file doesn't exist, and `Error` if the file system fails during operation.
pub fn remove(&self, path: &PathBuf) -> Result<bool, ChecksumFileError> {
let path = self.setup_file_path(path);
if !path.exists() {
return Ok(false);
}
fs::remove_file(&path).map_err(|_| ChecksumFileError::FileRemovalError(path.clone()))?;
Ok(true)
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned();
if path.is_dir() {

56
leo/files/lib.rs Normal file
View File

@ -0,0 +1,56 @@
//! The `lib.leo` file.
use crate::{directories::source::SOURCE_DIRECTORY_NAME, errors::LibFileError};
use serde::Deserialize;
use std::{fs::File, io::Write, path::PathBuf};
pub static LIB_FILE_NAME: &str = "lib.leo";
#[derive(Deserialize)]
pub struct LibFile {
pub package_name: String,
}
impl LibFile {
pub fn new(package_name: &str) -> Self {
Self {
package_name: package_name.to_string(),
}
}
pub fn exists_at(path: &PathBuf) -> bool {
let mut path = path.to_owned();
if path.is_dir() {
if !path.ends_with(SOURCE_DIRECTORY_NAME) {
path.push(PathBuf::from(SOURCE_DIRECTORY_NAME));
}
path.push(PathBuf::from(LIB_FILE_NAME));
}
path.exists()
}
pub fn write_to(self, path: &PathBuf) -> Result<(), LibFileError> {
let mut path = path.to_owned();
if path.is_dir() {
if !path.ends_with(SOURCE_DIRECTORY_NAME) {
path.push(PathBuf::from(SOURCE_DIRECTORY_NAME));
}
path.push(PathBuf::from(LIB_FILE_NAME));
}
let mut file = File::create(&path)?;
Ok(file.write_all(self.template().as_bytes())?)
}
fn template(&self) -> String {
format!(
r#"// The '{}' library circuit.
circuit Foo {{
a: field
}}
"#,
self.package_name
)
}
}

View File

@ -1,5 +1,5 @@
pub mod bytes;
pub use self::bytes::*;
pub mod zip;
pub use self::zip::*;
pub mod checksum;
pub use self::checksum::*;
@ -10,6 +10,9 @@ pub use self::inputs::*;
pub mod gitignore;
pub use self::gitignore::*;
pub mod lib;
pub use self::lib::*;
pub mod main;
pub use self::main::*;

View File

@ -43,11 +43,23 @@ impl ProofFile {
let mut file = File::create(&path)?;
file.write_all(proof)?;
log::info!("Proof stored to {:?}", path);
log::info!("Proof stored ({:?})", path);
Ok(())
}
/// Removes the proof at the given path if it exists. Returns `true` on success,
/// `false` if the file doesn't exist, and `Error` if the file system fails during operation.
pub fn remove(&self, path: &PathBuf) -> Result<bool, ProofFileError> {
let path = self.setup_file_path(path);
if !path.exists() {
return Ok(false);
}
fs::remove_file(&path).map_err(|_| ProofFileError::FileRemovalError(path.clone()))?;
Ok(true)
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned();
if path.is_dir() {

View File

@ -42,11 +42,21 @@ impl ProvingKeyFile {
let mut file = File::create(&path)?;
file.write_all(proving_key)?;
log::info!("Proving key stored to {:?}", path);
Ok(())
}
/// Removes the proving key at the given path if it exists. Returns `true` on success,
/// `false` if the file doesn't exist, and `Error` if the file system fails during operation.
pub fn remove(&self, path: &PathBuf) -> Result<bool, ProvingKeyFileError> {
let path = self.setup_file_path(path);
if !path.exists() {
return Ok(false);
}
fs::remove_file(&path).map_err(|_| ProvingKeyFileError::FileRemovalError(path.clone()))?;
Ok(true)
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned();
if path.is_dir() {

View File

@ -42,11 +42,21 @@ impl VerificationKeyFile {
let mut file = File::create(&path)?;
file.write_all(verification_key)?;
log::info!("Verification key stored to {:?}", path);
Ok(())
}
/// Removes the verification key at the given path if it exists. Returns `true` on success,
/// `false` if the file doesn't exist, and `Error` if the file system fails during operation.
pub fn remove(&self, path: &PathBuf) -> Result<bool, VerificationKeyFileError> {
let path = self.setup_file_path(path);
if !path.exists() {
return Ok(false);
}
fs::remove_file(&path).map_err(|_| VerificationKeyFileError::FileRemovalError(path.clone()))?;
Ok(true)
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned();
if path.is_dir() {

137
leo/files/zip.rs Normal file
View File

@ -0,0 +1,137 @@
//! The program package zip file.
use crate::{
directories::{IMPORTS_DIRECTORY_NAME, INPUTS_DIRECTORY_NAME, OUTPUTS_DIRECTORY_NAME},
errors::ZipFileError,
files::{
CHECKSUM_FILE_EXTENSION,
INPUTS_FILE_EXTENSION,
PROOF_FILE_EXTENSION,
PROVING_KEY_FILE_EXTENSION,
VERIFICATION_KEY_FILE_EXTENSION,
},
};
use serde::Deserialize;
use std::{
fs::File,
io::{Read, Write},
path::{Path, PathBuf},
};
use walkdir::WalkDir;
use zip::write::{FileOptions, ZipWriter};
pub static ZIP_FILE_EXTENSION: &str = ".zip";
#[derive(Deserialize)]
pub struct ZipFile {
pub package_name: String,
}
impl ZipFile {
pub fn new(package_name: &str) -> Self {
Self {
package_name: package_name.to_string(),
}
}
pub fn exists_at(&self, path: &PathBuf) -> bool {
let path = self.setup_file_path(path);
path.exists()
}
// /// Reads the program bytes from the given file path if it exists.
// pub fn read_from(&self, path: &PathBuf) -> Result<Vec<u8>, ZipFileError> {
// let path = self.setup_file_path(path);
//
// Ok(fs::read(&path).map_err(|_| ZipFileError::FileReadError(path.clone()))?)
// }
/// Writes the current package contents to a zip file.
pub fn write(&self, src_dir: &PathBuf) -> Result<(), ZipFileError> {
// Build walkdir iterator from current package
let walkdir = WalkDir::new(src_dir.clone());
// Create zip file
let path = self.setup_file_path(src_dir);
let file = &mut File::create(&path)?;
let mut zip = ZipWriter::new(file);
let options = FileOptions::default()
.compression_method(zip::CompressionMethod::Stored)
.unix_permissions(0o755);
// Walk through files in directory and write desired ones to the zip file
let mut buffer = Vec::new();
for entry in walkdir.into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
let name = path.strip_prefix(src_dir.as_path()).unwrap();
// filter excluded paths
if is_excluded(name) {
continue;
}
// write file or directory
if path.is_file() {
log::info!("\tadding file {:?} as {:?}", path, name);
zip.start_file_from_path(name, options)?;
let mut f = File::open(path)?;
f.read_to_end(&mut buffer)?;
zip.write_all(&*buffer)?;
buffer.clear();
} else if name.as_os_str().len() != 0 {
// Only if not root Avoids path spec / warning
// and mapname conversion failed error on unzip
log::info!("\tadding dir {:?} as {:?}", path, name);
zip.add_directory_from_path(name, options)?;
}
}
zip.finish()?;
log::info!("Package zip file created successfully {:?}", path);
Ok(())
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned();
if path.is_dir() {
if !path.ends_with(OUTPUTS_DIRECTORY_NAME) {
path.push(PathBuf::from(OUTPUTS_DIRECTORY_NAME));
}
path.push(PathBuf::from(format!("{}{}", self.package_name, ZIP_FILE_EXTENSION)));
}
path
}
}
fn is_excluded(path: &Path) -> bool {
// excluded directories: `inputs`, `outputs`, `imports`
if path.ends_with(INPUTS_DIRECTORY_NAME.trim_end_matches("/"))
| path.ends_with(OUTPUTS_DIRECTORY_NAME.trim_end_matches("/"))
| path.ends_with(IMPORTS_DIRECTORY_NAME.trim_end_matches("/"))
{
return true;
}
// excluded extensions: `.in`, `.bytes`, `lpk`, `lvk`, `.proof`, `.sum`
path.extension()
.map(|ext| {
if ext.eq(INPUTS_FILE_EXTENSION.trim_start_matches("."))
| ext.eq(ZIP_FILE_EXTENSION.trim_start_matches("."))
| ext.eq(PROVING_KEY_FILE_EXTENSION.trim_start_matches("."))
| ext.eq(VERIFICATION_KEY_FILE_EXTENSION.trim_start_matches("."))
| ext.eq(PROOF_FILE_EXTENSION.trim_start_matches("."))
| ext.eq(CHECKSUM_FILE_EXTENSION.trim_start_matches("."))
| ext.eq(ZIP_FILE_EXTENSION.trim_start_matches("."))
{
true
} else {
false
}
})
.unwrap_or(false)
}

View File

@ -3,33 +3,18 @@ use std::io::Write;
const LEVEL_NAME_LENGTH: usize = 10;
#[allow(dead_code)]
fn level_string(level: log::Level) -> colored::ColoredString {
match level {
log::Level::Error => "ERROR".bold().red(),
log::Level::Warn => "WARN".bold().yellow(),
log::Level::Info => "INFO".bold().blue(),
log::Level::Debug => "DEBUG".bold().magenta(),
log::Level::Trace => "TRACE".bold(),
}
}
#[allow(dead_code)]
fn colored_string(level: log::Level, message: &str) -> colored::ColoredString {
match level {
log::Level::Error => message.bold().red(),
log::Level::Warn => message.bold().yellow(),
log::Level::Info => message.bold().blue(),
log::Level::Info => message.bold().cyan(),
log::Level::Debug => message.bold().magenta(),
log::Level::Trace => message.bold(),
}
}
/// Initialize logger with custom format and verbosity.
///
/// # Arguments
///
/// * `verbosity` - Verbosity level. 0 for `Warn`, 1 for `Info`, 2 for `Debug`, more for `Trace`
pub fn init_logger(app_name: &'static str, verbosity: usize) {
env_logger::builder()
.filter_level(match verbosity {
@ -46,8 +31,7 @@ pub fn init_logger(app_name: &'static str, verbosity: usize) {
writeln!(
buf,
"{:>5}{:>5} {}",
level_string(record.level()),
"{:>5} {}",
colored_string(record.level(), app_name),
record.args().to_string().replace("\n", &padding)
)

View File

@ -1,11 +1,9 @@
use leo::{cli::*, commands::*, errors::CLIError, logger};
use leo::{cli::*, commands::*, errors::CLIError};
use clap::{App, AppSettings};
use clap::{App, AppSettings, Arg};
#[cfg_attr(tarpaulin, skip)]
fn main() -> Result<(), CLIError> {
logger::init_logger("leo", 1);
let arguments = App::new("leo")
.version("v0.1.0")
.about("Leo compiler and package manager")
@ -16,46 +14,41 @@ fn main() -> Result<(), CLIError> {
AppSettings::DisableVersion,
AppSettings::SubcommandRequiredElseHelp,
])
.args(&[Arg::with_name("debug")
.short("d")
.long("debug")
.help("Enables debugging mode")
.global(true)])
.subcommands(vec![
NewCommand::new().display_order(0),
InitCommand::new().display_order(1),
BuildCommand::new().display_order(2),
LoadCommand::new().display_order(3),
UnloadCommand::new().display_order(4),
SetupCommand::new().display_order(5),
ProveCommand::new().display_order(6),
RunCommand::new().display_order(7),
PublishCommand::new().display_order(8),
DeployCommand::new().display_order(9),
TestCommand::new().display_order(10),
TestCommand::new().display_order(3),
LoadCommand::new().display_order(4),
UnloadCommand::new().display_order(5),
SetupCommand::new().display_order(6),
ProveCommand::new().display_order(7),
RunCommand::new().display_order(8),
PublishCommand::new().display_order(9),
DeployCommand::new().display_order(10),
CleanCommand::new().display_order(11),
])
.set_term_width(0)
.get_matches();
match arguments.subcommand() {
("new", Some(arguments)) => NewCommand::output(NewCommand::parse(arguments)?),
("init", Some(arguments)) => InitCommand::output(InitCommand::parse(arguments)?),
("build", Some(arguments)) => {
BuildCommand::output(BuildCommand::parse(arguments)?)?;
Ok(())
}
("load", Some(arguments)) => LoadCommand::output(LoadCommand::parse(arguments)?),
("unload", Some(arguments)) => UnloadCommand::output(UnloadCommand::parse(arguments)?),
("setup", Some(arguments)) => {
SetupCommand::output(SetupCommand::parse(arguments)?)?;
Ok(())
}
("prove", Some(arguments)) => {
ProveCommand::output(ProveCommand::parse(arguments)?)?;
Ok(())
}
("run", Some(arguments)) => RunCommand::output(RunCommand::parse(arguments)?),
("publish", Some(arguments)) => PublishCommand::output(PublishCommand::parse(arguments)?),
("deploy", Some(arguments)) => DeployCommand::output(DeployCommand::parse(arguments)?),
("test", Some(arguments)) => {
TestCommand::output(TestCommand::parse(arguments)?)?;
Ok(())
}
("new", Some(arguments)) => NewCommand::process(arguments),
("init", Some(arguments)) => InitCommand::process(arguments),
("build", Some(arguments)) => BuildCommand::process(arguments),
("test", Some(arguments)) => TestCommand::process(arguments),
("load", Some(arguments)) => LoadCommand::process(arguments),
("unload", Some(arguments)) => UnloadCommand::process(arguments),
("setup", Some(arguments)) => SetupCommand::process(arguments),
("prove", Some(arguments)) => ProveCommand::process(arguments),
("run", Some(arguments)) => RunCommand::process(arguments),
("publish", Some(arguments)) => PublishCommand::process(arguments),
("deploy", Some(arguments)) => DeployCommand::process(arguments),
("clean", Some(arguments)) => CleanCommand::process(arguments),
_ => unreachable!(),
}
}

View File

@ -8,8 +8,8 @@ edition = "2018"
leo-ast = { path = "../ast", version = "0.1.0" }
leo-inputs = { path = "../leo-inputs", version = "0.1.0" }
snarkos-errors = { path = "../../snarkOS/errors", version = "0.8.0", default-features = false }
snarkos-models = { path = "../../snarkOS/models", version = "0.8.0", default-features = false }
snarkos-errors = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
snarkos-models = { git = "ssh://git@github.com/AleoHQ/snarkOS.git", rev = "c7a56d9", default-features = false }
pest = { version = "2.0" }
serde = { version = "1.0" }

View File

@ -1,6 +1,6 @@
//! The import type for a Leo program.
use crate::{ImportSymbol, Span};
use crate::{Package, Span};
use leo_ast::imports::Import as AstImport;
use serde::{Deserialize, Serialize};
@ -8,20 +8,14 @@ use std::fmt;
#[derive(Clone, Serialize, Deserialize)]
pub struct Import {
pub path_string: String,
pub symbols: Vec<ImportSymbol>,
pub package: Package,
pub span: Span,
}
impl<'ast> From<AstImport<'ast>> for Import {
fn from(import: AstImport<'ast>) -> Self {
Import {
path_string: import.source.value,
symbols: import
.symbols
.into_iter()
.map(|symbol| ImportSymbol::from(symbol))
.collect(),
package: Package::from(import.package),
span: Span::from(import.span),
}
}
@ -29,28 +23,17 @@ impl<'ast> From<AstImport<'ast>> for Import {
impl Import {
pub fn path_string_full(&self) -> String {
format!("{}.leo", self.path_string)
format!("{}.leo", self.package.name)
}
// from "./import" import *;
pub fn is_star(&self) -> bool {
self.symbols.is_empty()
// self.symbols.is_empty()
false
}
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "from {} import ", self.path_string)?;
if self.symbols.is_empty() {
write!(f, "*")
} else {
write!(f, "{{\n")?;
for (i, symbol) in self.symbols.iter().enumerate() {
write!(f, "{}", symbol)?;
if i < self.symbols.len() - 1 {
write!(f, ",\n")?;
}
}
write!(f, "\n}}")
}
write!(f, "import {};", self.package)
}
}

View File

@ -24,9 +24,9 @@ impl<'ast> From<AstImportSymbol<'ast>> for ImportSymbol {
impl fmt::Display for ImportSymbol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.alias.is_some() {
write!(f, "\t{} as {}", self.symbol, self.alias.as_ref().unwrap())
write!(f, "{} as {}", self.symbol, self.alias.as_ref().unwrap())
} else {
write!(f, "\t{}", self.symbol)
write!(f, "{}", self.symbol)
}
}
}

View File

@ -3,3 +3,9 @@ pub use import::*;
pub mod import_symbol;
pub use import_symbol::*;
pub mod package;
pub use package::*;
pub mod package_access;
pub use package_access::*;

View File

@ -0,0 +1,40 @@
use crate::{common::Identifier, PackageAccess, Span};
use leo_ast::imports::Package as AstPackage;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Serialize, Deserialize)]
pub struct Package {
pub name: Identifier,
pub access: PackageAccess,
pub span: Span,
}
impl<'ast> From<AstPackage<'ast>> for Package {
fn from(package: AstPackage<'ast>) -> Self {
Package {
name: Identifier::from(package.name),
access: PackageAccess::from(package.access),
span: Span::from(package.span),
}
}
}
impl Package {
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}.{}", self.name, self.access)
}
}
impl fmt::Display for Package {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format(f)
}
}
impl fmt::Debug for Package {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format(f)
}
}

View File

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