diff --git a/.circleci/config.yml b/.circleci/config.yml index 774136c6..1c8419a5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -42,9 +42,9 @@ jobs: - restore_cache: keys: - v4-cargo-cache-{{ arch }}-{{ checksum "Cargo.lock" }} - - run: - name: Check format - command: rustup component add rustfmt-preview; cargo fmt --all -- --check + # - run: + # name: Check format + # command: rustup component add rustfmt; cargo fmt --all -- --check - run: name: Install libsnark prerequisites command: ./scripts/install_libsnark_prerequisites.sh diff --git a/Cargo.lock b/Cargo.lock index a232d754..85fe2dd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,7 +2,7 @@ # It is not intended for manual editing. [[package]] name = "adler32" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -51,7 +51,7 @@ dependencies = [ "difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "environment 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "skeptic 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -71,11 +71,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.37" +version = "0.3.38" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -104,7 +104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -120,7 +120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -130,7 +130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bitflags" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -170,7 +170,7 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -194,7 +194,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -214,9 +214,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -230,7 +230,7 @@ dependencies = [ [[package]] name = "cfg-if" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -250,7 +250,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -262,7 +262,7 @@ name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -306,8 +306,8 @@ dependencies = [ "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "publicsuffix 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -332,7 +332,7 @@ name = "crc32fast" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -340,7 +340,7 @@ name = "crossbeam" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -371,7 +371,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -391,7 +391,7 @@ name = "crossbeam-utils" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -409,7 +409,7 @@ dependencies = [ "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -422,13 +422,13 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -467,15 +467,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "either" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "encoding_rs" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -488,7 +488,7 @@ name = "error-chain" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -496,7 +496,7 @@ name = "error-chain" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -505,7 +505,7 @@ name = "failure" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -551,12 +551,13 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -601,7 +602,7 @@ name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -650,7 +651,7 @@ name = "getrandom" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -660,12 +661,12 @@ name = "git2" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "libgit2-sys 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -729,7 +730,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hyper" -version = "0.12.34" +version = "0.12.35" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -749,9 +750,9 @@ dependencies = [ "tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -763,7 +764,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.34 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -807,7 +808,7 @@ name = "itertools" version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -850,23 +851,23 @@ version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "curl-sys 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)", + "curl-sys 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "libssh2-sys 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libssh2-sys 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libssh2-sys" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -884,11 +885,10 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.1.5" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -896,7 +896,7 @@ name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -946,10 +946,10 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -992,7 +992,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1003,10 +1003,10 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1017,7 +1017,7 @@ name = "net2" version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1054,7 +1054,7 @@ dependencies = [ "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1107,15 +1107,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl" -version = "0.10.24" +version = "0.10.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1125,7 +1125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.49" +version = "0.9.50" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1135,14 +1135,6 @@ dependencies = [ "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "owning_ref" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "pairing_ce" version = "0.18.0" @@ -1171,20 +1163,23 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parking_lot_core" -version = "0.4.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1236,7 +1231,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "pest_meta 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1271,7 +1266,7 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1294,7 +1289,7 @@ name = "pulldown-cmark" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1310,7 +1305,7 @@ name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1366,7 +1361,7 @@ dependencies = [ [[package]] name = "rand" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1551,31 +1546,31 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.9.20" +version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "cookie_store 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "encoding_rs 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.34 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1646,25 +1641,20 @@ dependencies = [ "digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "schannel" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "scopeguard" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "scopeguard" version = "1.0.0" @@ -1695,7 +1685,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1705,10 +1695,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1716,27 +1706,27 @@ name = "serde_bytes" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1746,7 +1736,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1779,7 +1769,7 @@ dependencies = [ "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1794,11 +1784,6 @@ name = "smallvec" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "stable_deref_trait" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "string" version = "0.2.1" @@ -1837,7 +1822,7 @@ name = "syn" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1867,9 +1852,9 @@ name = "tempfile" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1921,9 +1906,9 @@ dependencies = [ "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1933,7 +1918,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1967,7 +1952,7 @@ dependencies = [ [[package]] name = "tokio-reactor" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1976,7 +1961,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2002,21 +1987,21 @@ dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-threadpool" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2042,12 +2027,12 @@ name = "try_from" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "typed-arena" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2265,6 +2250,17 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "zokrates_abi" +version = "0.1.0" +dependencies = [ + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "zokrates_core 0.3.14", + "zokrates_field 0.3.3", +] + [[package]] name = "zokrates_cli" version = "0.4.11" @@ -2275,8 +2271,9 @@ dependencies = [ "fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "zokrates_abi 0.1.0", "zokrates_core 0.3.14", "zokrates_field 0.3.3", "zokrates_fs_resolver 0.4.1", @@ -2306,11 +2303,11 @@ dependencies = [ "reduce 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", - "typed-arena 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "typed-arena 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "zokrates_embed 0.1.0", "zokrates_field 0.3.3", @@ -2345,9 +2342,9 @@ dependencies = [ "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "pairing_ce 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2362,7 +2359,7 @@ name = "zokrates_github_resolver" version = "0.1.0" dependencies = [ "mockito 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.21 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2402,16 +2399,16 @@ version = "0.1.0" dependencies = [ "fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "zokrates_core 0.3.14", "zokrates_field 0.3.3", "zokrates_fs_resolver 0.4.1", ] [metadata] -"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" @@ -2420,13 +2417,13 @@ dependencies = [ "checksum assert_cli 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72342c21057a3cb5f7c2d849bf7999a83795434dd36d74fa8c24680581bd1930" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" -"checksum backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "5180c5a20655b14a819b652fd2378fa5f1697b6c9ddad3e695c2f9cedf6df4e2" +"checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5" "checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum bellman_ce 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "938ec0feff00f9dfda0e7cbfe8db8b717966a84f6a12e63ed0943c4a90d6a5de" "checksum bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e103c8b299b28a9c6990458b7013dc4a8356a9b854c51b9883241f5866fac36e" "checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" -"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" +"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2" "checksum blake2-rfc_bellman_edition 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdc60350286c7c3db13b98e91dbe5c8b6830a6821bc20af5b0c310ce94d74915" "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" "checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" @@ -2438,7 +2435,7 @@ dependencies = [ "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" "checksum cargo_metadata 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e5d1b4d380e1bab994591a24c2bdd1b054f64b60bef483a8c598c7c345bc3bbe" "checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64a4b57c8f4e3a2e9ac07e0f6abc9c24b6fc9e1b54c3478cfb598f3d0023e51c" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" @@ -2459,14 +2456,14 @@ dependencies = [ "checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" "checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" "checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" -"checksum curl-sys 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)" = "520594da9914c1dc77ce3be450fc1c74fde67c82966d80f8e93c6d460eb0e9ae" +"checksum curl-sys 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9a9a4e417722876332136a00cacf92c2ceb331fab4b52b6a1ad16c6cd79255" "checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" -"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" -"checksum encoding_rs 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)" = "79906e1ad1f7f8bc48864fcc6ffd58336fb5992e627bf61928099cb25fdf4314" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +"checksum encoding_rs 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)" = "87240518927716f79692c2ed85bfe6e98196d18c6401ec75355760233a7e12e9" "checksum environment 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4b14e20978669064c33b4c1e0fb4083412e40fe56cbea2eae80fd7591503ee" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" "checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" @@ -2475,7 +2472,7 @@ dependencies = [ "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum ff_ce 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "18af1ea1b80a4b474fae13af4c58cf0a5a2bc33832d5fa70f68a4b286178fdb5" "checksum ff_derive_ce 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d245b4e76c5b36bb7721ea15b7fbc61bebf0c5d2890eaf49fe1e2a3eed36db9" -"checksum flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "2adaffba6388640136149e18ed080b77a78611c1e1d6de75aedcdf78df5d4682" +"checksum flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ad3c5233c9a940c8719031b423d7e6c16af66e031cb0420b0896f5245bf181d3" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" @@ -2498,7 +2495,7 @@ dependencies = [ "checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" "checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" -"checksum hyper 0.12.34 (registry+https://github.com/rust-lang/crates.io-index)" = "898a87371a3999b2f731b9af636cd76aa20de10e69c2daf3e71388326b619fe0" +"checksum hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)" = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" "checksum hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" @@ -2511,9 +2508,9 @@ dependencies = [ "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum libgit2-sys 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "48441cb35dc255da8ae72825689a95368bf510659ae1ad55dc4aa88cb1789bf1" -"checksum libssh2-sys 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "126a1f4078368b163bfdee65fbab072af08a1b374a5551b21e87ade27b1fbf9d" +"checksum libssh2-sys 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "8914d10b159fc288f2b6f253c94bd0c15a777fd5a297691141d89674b87e66fd" "checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" -"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" +"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" @@ -2522,7 +2519,7 @@ dependencies = [ "checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" "checksum mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "dd1d63acd1b78403cc0c325605908475dd9b9a3acbf65ed8bcab97e27014afcf" "checksum mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0ed03949aef72dbdf3116a383d7b38b4768e6f960528cd6a6044aa9ed68599" -"checksum miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7108aff85b876d06f22503dcce091e29f76733b2bfdd91eebce81f5e68203a10" +"checksum miniz_oxide 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "304f66c19be2afa56530fa7c39796192eef38618da8d19df725ad7c6d6b2aaae" "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum mockito 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d303c060b18db22e0a9ac12133c32f927c3f980b6f70004bc0cb2f8accc94704" @@ -2538,15 +2535,14 @@ dependencies = [ "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)" = "8152bb5a9b5b721538462336e3bef9a539f892715e5037fda0f984577311af15" +"checksum openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2f372b2b53ce10fb823a337aaa674e3a7d072b957c6264d0f4ff0bd86e657449" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)" = "f4fad9e54bd23bd4cbbe48fdc08a1b8091707ac869ef8508edea2fec77dcc884" -"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" +"checksum openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)" = "2c42dcccb832556b5926bc9ae61e8775f2a61e725ab07ab3d1e7fcf8ae62c3b6" "checksum pairing_ce 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f075a9c570e2026111cb6dddf6a320e5163c42aa32500b315ec34acbcf7c9b36" "checksum parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)" = "511379a8194230c2395d2f5fa627a5a7e108a9f976656ce723ae68fca4097bfc" "checksum parity-wasm 0.35.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3e1e076c4e01399b6cd0793a8df42f90bba3ae424671ef421d1608a943155d93" -"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" -"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e4fb201c5c22a55d8b24fef95f78be52738e5e1361129be1b5e862ecdb6894a" @@ -2557,7 +2553,7 @@ dependencies = [ "checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" +"checksum proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90cf5f418035b98e655e9cdb225047638296b862b42411c4e45bb88d700f7fc0" "checksum publicsuffix 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9bf259a81de2b2eb9850ec990ec78e6a25319715584fd7652b9b26f96fcb1510" "checksum pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eef52fac62d0ea7b9b4dc7da092aa64ea7ec3d90af6679422d3d7e0e14b6ee15" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" @@ -2566,7 +2562,7 @@ dependencies = [ "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" +"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" @@ -2588,7 +2584,7 @@ dependencies = [ "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum reqwest 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)" = "0f6d896143a583047512e59ac54a215cb203c29cc941917343edea3be8df9c78" +"checksum reqwest 0.9.21 (registry+https://github.com/rust-lang/crates.io-index)" = "02b7e953e14c6f3102b7e8d1f1ee3abf5ecee80b427f5565c9389835cecae95c" "checksum rgb 0.8.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2089e4031214d129e201f8c3c8c2fe97cd7322478a0d1cdf78e7029b0042efdb" "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" @@ -2598,24 +2594,22 @@ dependencies = [ "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" "checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" "checksum sapling-crypto_ce 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d385cebab4241e694ca5979b0cbf9b353ba12fe6a17d66643a958b397362d56d" -"checksum schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" -"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eee63d0f4a9ec776eeb30e220f0bc1e092c3ad744b2a379e3993070364d3adc2" "checksum security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "f4473e8506b213730ff2061073b48fa51dcc66349219e2e7c5608f0296a1d95a" +"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" "checksum serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)" = "defbb8a83d7f34cc8380751eeb892b825944222888aff18996ea7901f24aec88" -"checksum serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "11e410fde43e157d789fc290d26bc940778ad0fdd47836426fbac36573710dbb" -"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" +"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" +"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" "checksum serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" "checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" "checksum single 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5add732a1ab689845591a1b50339cf5310b563e08dc5813c65991f30369ea2" "checksum skeptic 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fb8ed853fdc19ce09752d63f3a2e5b5158aeb261520cd75eb618bd60305165" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" -"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" @@ -2633,14 +2627,14 @@ dependencies = [ "checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" "checksum tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f27ee0e6db01c5f0b2973824547ce7e637b2ed79b891a9677b0de9bd532b6ac" "checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" -"checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce" +"checksum tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c56391be9805bc80163151c0b9e5164ee64f4b0200962c346fea12773158f22d" "checksum tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2162248ff317e2bc713b261f242b69dbb838b85248ed20bb21df56d60ea4cae7" "checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" -"checksum tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "90ca01319dea1e376a001e8dc192d42ebde6dd532532a5bad988ac37db365b19" +"checksum tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2bd2c6a3885302581f4401c82af70d792bb9df1700e7437b0aeb4ada94d5388c" "checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "checksum try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b" -"checksum typed-arena 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f70f5c346cc11bc044ae427ab2feae213350dca9e2d637047797d5ff316a646" +"checksum typed-arena 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9c0704a799d314795d3d847d519b284bae681ef9b1f3da99f7ebc7b47ba2e607" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum ucd-trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2" "checksum ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa9b3b49edd3468c0e6565d85783f51af95212b6fa3986a5500954f00b460874" diff --git a/Cargo.toml b/Cargo.toml index bf1ca2ac..b4be12d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "zokrates_github_resolver", "zokrates_stdlib", "zokrates_embed", + "zokrates_abi", "zokrates_test", "zokrates_core_test", ] \ No newline at end of file diff --git a/zokrates_abi/Cargo.toml b/zokrates_abi/Cargo.toml new file mode 100644 index 00000000..d4710737 --- /dev/null +++ b/zokrates_abi/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "zokrates_abi" +version = "0.1.0" +authors = ["Thibaut Schaeffer "] +edition = "2018" + +[dependencies] +zokrates_field = { version = "0.3", path = "../zokrates_field" } +zokrates_core = { version = "0.3", path = "../zokrates_core" } +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" diff --git a/zokrates_abi/src/lib.rs b/zokrates_abi/src/lib.rs new file mode 100644 index 00000000..9020a543 --- /dev/null +++ b/zokrates_abi/src/lib.rs @@ -0,0 +1,491 @@ +#![feature(box_patterns, box_syntax)] + +pub enum Inputs { + Raw(Vec), + Abi(CheckedValues), +} + +impl> Encode for Inputs { + fn encode(self) -> Vec { + match self { + Inputs::Raw(v) => v, + Inputs::Abi(v) => v.encode(), + } + } +} + +use std::collections::BTreeMap; +use std::convert::TryFrom; +use std::fmt; +use zokrates_core::typed_absy::Type; + +use zokrates_field::field::Field; + +type Map = BTreeMap; + +#[derive(Debug, PartialEq)] +pub enum Error { + Json(String), + Conversion(String), + Type(String), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Json(e) => write!(f, "Invalid JSON: {}", e), + Error::Conversion(e) => write!(f, "Invalid ZoKrates values: {}", e), + Error::Type(e) => write!(f, "Type error: {}", e), + } + } +} + +#[derive(PartialEq, Debug)] +enum Value { + Field(T), + Boolean(bool), + Array(Vec>), + Struct(Map>), +} + +#[derive(PartialEq, Debug)] +enum CheckedValue { + Field(T), + Boolean(bool), + Array(Vec>), + Struct(Vec<(String, CheckedValue)>), +} + +#[derive(PartialEq, Debug)] +pub struct CheckedValues(Vec>); + +impl fmt::Display for Value { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Value::Field(v) => write!(f, "{}", v), + Value::Boolean(v) => write!(f, "{}", v), + Value::Array(v) => write!( + f, + "[{}]", + v.iter() + .map(|v| format!("{}", v)) + .collect::>() + .join(", ") + ), + Value::Struct(v) => write!( + f, + "{{{}}}", + v.iter() + .map(|(k, v)| format!("{}: {}", k, v)) + .collect::>() + .join(", ") + ), + } + } +} + +impl Value { + fn check(self, ty: Type) -> Result, String> { + match (self, ty) { + (Value::Field(f), Type::FieldElement) => Ok(CheckedValue::Field(f)), + (Value::Boolean(b), Type::Boolean) => Ok(CheckedValue::Boolean(b)), + (Value::Array(a), Type::Array(box inner_ty, size)) => { + if a.len() != size { + Err(format!( + "Expected array of size {}, found array of size {}", + size, + a.len() + )) + } else { + let a = a + .into_iter() + .map(|val| val.check(inner_ty.clone())) + .collect::, _>>()?; + Ok(CheckedValue::Array(a)) + } + } + (Value::Struct(mut s), Type::Struct(members)) => { + if s.len() != members.len() { + Err(format!( + "Expected {} member(s), found {}", + members.len(), + s.len() + )) + } else { + let s = members + .into_iter() + .map(|(id, ty)| { + s.remove(&id) + .ok_or_else(|| format!("Member with id `{}` not found", id)) + .map(|v| v.check(ty).map(|v| (id, v))) + }) + .collect::, _>>()? + .into_iter() + .collect::>()?; + Ok(CheckedValue::Struct(s)) + } + } + (v, t) => Err(format!("Value `{}` doesn't match expected type `{}`", v, t)), + } + } +} + +pub trait Encode { + fn encode(self) -> Vec; +} + +pub trait Decode { + type Expected; + + fn decode(raw: Vec, expected: Self::Expected) -> Self; +} + +impl> Encode for CheckedValue { + fn encode(self) -> Vec { + match self { + CheckedValue::Field(t) => vec![t], + CheckedValue::Boolean(b) => vec![if b { 1.into() } else { 0.into() }], + CheckedValue::Array(a) => a.into_iter().flat_map(|v| v.encode()).collect(), + CheckedValue::Struct(s) => s.into_iter().flat_map(|(_, v)| v.encode()).collect(), + } + } +} + +impl + PartialEq> Decode for CheckedValues { + type Expected = Vec; + + fn decode(raw: Vec, expected: Self::Expected) -> Self { + CheckedValues( + expected + .into_iter() + .scan(0, |state, e| { + let new_state = *state + e.get_primitive_count(); + let res = CheckedValue::decode(raw[*state..new_state].to_vec(), e); + *state = new_state; + Some(res) + }) + .collect(), + ) + } +} + +impl + PartialEq + Clone> Decode for CheckedValue { + type Expected = Type; + + fn decode(raw: Vec, expected: Self::Expected) -> Self { + let mut raw = raw; + + match expected { + Type::FieldElement => CheckedValue::Field(raw.pop().unwrap()), + Type::Boolean => { + let v = raw.pop().unwrap(); + CheckedValue::Boolean(if v == 0.into() { + false + } else if v == 1.into() { + true + } else { + unreachable!() + }) + } + Type::Array(box inner_ty, _) => CheckedValue::Array( + raw.chunks(inner_ty.get_primitive_count()) + .map(|c| CheckedValue::decode(c.to_vec(), inner_ty.clone())) + .collect(), + ), + Type::Struct(members) => CheckedValue::Struct( + members + .into_iter() + .scan(0, |state, (id, ty)| { + let new_state = *state + ty.get_primitive_count(); + let res = CheckedValue::decode(raw[*state..new_state].to_vec(), ty); + *state = new_state; + Some((id, res)) + }) + .collect(), + ), + } + } +} + +impl> Encode for CheckedValues { + fn encode(self) -> Vec { + self.0.into_iter().flat_map(|v| v.encode()).collect() + } +} + +#[derive(PartialEq, Debug)] +struct Values(Vec>); + +impl TryFrom for Values { + type Error = String; + + fn try_from(v: serde_json::Value) -> Result, Self::Error> { + match v { + serde_json::Value::Array(a) => a + .into_iter() + .map(|v| Value::try_from(v)) + .collect::>() + .map(|v| Values(v)), + v => Err(format!("Expected an array of values, found `{}`", v)), + } + } +} + +impl TryFrom for Value { + type Error = String; + fn try_from(v: serde_json::Value) -> Result, Self::Error> { + match v { + serde_json::Value::String(s) => T::try_from_dec_str(&s) + .map(|v| Value::Field(v)) + .map_err(|_| format!("Could not parse `{}` as field element", s)), + serde_json::Value::Bool(b) => Ok(Value::Boolean(b)), + serde_json::Value::Number(n) => Err(format!( + "Value `{}` isn't allowed, did you mean `\"{}\"`?", + n, n + )), + serde_json::Value::Array(a) => a + .into_iter() + .map(|v| Value::try_from(v)) + .collect::>() + .map(|v| Value::Array(v)), + serde_json::Value::Object(o) => o + .into_iter() + .map(|(k, v)| Value::try_from(v).map(|v| (k, v))) + .collect::, _>>() + .map(|v| Value::Struct(v)), + v => Err(format!("Value `{}` isn't allowed", v)), + } + } +} + +impl Into for CheckedValue { + fn into(self) -> serde_json::Value { + match self { + CheckedValue::Field(f) => serde_json::Value::String(f.to_dec_string()), + CheckedValue::Boolean(b) => serde_json::Value::Bool(b), + CheckedValue::Array(a) => { + serde_json::Value::Array(a.into_iter().map(|e| e.into()).collect()) + } + CheckedValue::Struct(s) => { + serde_json::Value::Object(s.into_iter().map(|(k, v)| (k, v.into())).collect()) + } + } + } +} + +impl Into for CheckedValues { + fn into(self) -> serde_json::Value { + serde_json::Value::Array(self.0.into_iter().map(|e| e.into()).collect()) + } +} + +fn parse(s: &str) -> Result, Error> { + let json_values: serde_json::Value = + serde_json::from_str(s).map_err(|e| Error::Json(e.to_string()))?; + Values::try_from(json_values).map_err(|e| Error::Conversion(e)) +} + +pub fn parse_strict(s: &str, types: Vec) -> Result, Error> { + let parsed = parse(s)?; + if parsed.0.len() != types.len() { + return Err(Error::Type(format!( + "Expected {} inputs, found {}", + types.len(), + parsed.0.len() + ))); + } + let checked = parsed + .0 + .into_iter() + .zip(types.into_iter()) + .map(|(v, ty)| v.check(ty)) + .collect::, _>>() + .map_err(|e| Error::Type(e))?; + Ok(CheckedValues(checked)) +} + +#[cfg(test)] +mod tests { + use super::*; + use zokrates_field::field::FieldPrime; + + #[test] + fn numbers() { + let s = "[1, 2]"; + assert_eq!( + parse::(s).unwrap_err(), + Error::Conversion(String::from( + "Value `1` isn't allowed, did you mean `\"1\"`?" + )) + ); + } + + #[test] + fn fields() { + let s = r#"["1", "2"]"#; + assert_eq!( + parse::(s).unwrap(), + Values(vec![Value::Field(1.into()), Value::Field(2.into())]) + ); + } + + #[test] + fn bools() { + let s = "[true, false]"; + assert_eq!( + parse::(s).unwrap(), + Values(vec![Value::Boolean(true), Value::Boolean(false)]) + ); + } + + #[test] + fn array() { + let s = "[[true, false]]"; + assert_eq!( + parse::(s).unwrap(), + Values(vec![Value::Array(vec![ + Value::Boolean(true), + Value::Boolean(false) + ])]) + ); + } + + #[test] + fn struc() { + let s = r#"[{"a": "42"}]"#; + assert_eq!( + parse::(s).unwrap(), + Values(vec![Value::Struct( + vec![("a".to_string(), Value::Field(42.into()))] + .into_iter() + .collect() + )]) + ); + } + + mod strict { + use super::*; + + #[test] + fn fields() { + let s = r#"["1", "2"]"#; + assert_eq!( + parse_strict::(s, vec![Type::FieldElement, Type::FieldElement]) + .unwrap(), + CheckedValues(vec![ + CheckedValue::Field(1.into()), + CheckedValue::Field(2.into()) + ]) + ); + } + + #[test] + fn bools() { + let s = "[true, false]"; + assert_eq!( + parse_strict::(s, vec![Type::Boolean, Type::Boolean]).unwrap(), + CheckedValues(vec![ + CheckedValue::Boolean(true), + CheckedValue::Boolean(false) + ]) + ); + } + + #[test] + fn array() { + let s = "[[true, false]]"; + assert_eq!( + parse_strict::(s, vec![Type::array(Type::Boolean, 2)]).unwrap(), + CheckedValues(vec![CheckedValue::Array(vec![ + CheckedValue::Boolean(true), + CheckedValue::Boolean(false) + ])]) + ); + } + + #[test] + fn struc() { + let s = r#"[{"a": "42"}]"#; + assert_eq!( + parse_strict::( + s, + vec![Type::Struct(vec![("a".into(), Type::FieldElement)])] + ) + .unwrap(), + CheckedValues(vec![CheckedValue::Struct( + vec![("a".to_string(), CheckedValue::Field(42.into()))] + .into_iter() + .collect() + )]) + ); + + let s = r#"[{"b": "42"}]"#; + assert_eq!( + parse_strict::( + s, + vec![Type::Struct(vec![("a".into(), Type::FieldElement)])] + ) + .unwrap_err(), + Error::Type("Member with id `a` not found".into()) + ); + + let s = r#"[{}]"#; + assert_eq!( + parse_strict::( + s, + vec![Type::Struct(vec![("a".into(), Type::FieldElement)])] + ) + .unwrap_err(), + Error::Type("Expected 1 member(s), found 0".into()) + ); + + let s = r#"[{"a": false}]"#; + assert_eq!( + parse_strict::( + s, + vec![Type::Struct(vec![("a".into(), Type::FieldElement)])] + ) + .unwrap_err(), + Error::Type("Value `false` doesn't match expected type `field`".into()) + ); + } + } + + mod encode { + use super::*; + + #[test] + fn fields() { + let v = CheckedValues(vec![CheckedValue::Field(1), CheckedValue::Field(2)]); + assert_eq!(v.encode(), vec![1, 2]); + } + + #[test] + fn bools() { + let v: CheckedValues = CheckedValues(vec![ + CheckedValue::Boolean(true), + CheckedValue::Boolean(false), + ]); + assert_eq!(v.encode(), vec![1, 0]); + } + + #[test] + fn array() { + let v: CheckedValues = CheckedValues(vec![CheckedValue::Array(vec![ + CheckedValue::Boolean(true), + CheckedValue::Boolean(false), + ])]); + assert_eq!(v.encode(), vec![1, 0]); + } + + #[test] + fn struc() { + let v: CheckedValues = CheckedValues(vec![CheckedValue::Struct( + vec![("a".to_string(), CheckedValue::Field(42))] + .into_iter() + .collect(), + )]); + assert_eq!(v.encode(), vec![42]); + } + } +} diff --git a/zokrates_book/src/concepts/imports.md b/zokrates_book/src/concepts/imports.md index 25d3dcb4..2472030a 100644 --- a/zokrates_book/src/concepts/imports.md +++ b/zokrates_book/src/concepts/imports.md @@ -1,27 +1,66 @@ ## Imports -You can separate your code into multiple ZoKrates files using `import` statements, ignoring the `.zok` extension of the imported file: +You can separate your code into multiple ZoKrates files using `import` statements to import symbols, ignoring the `.zok` extension of the imported file. + +### Import syntax + +#### Symbol selection + +The preferred way to import a symbol is by module and name: +```zokrates +from "./path/to/my/module" import MySymbol + +// `MySymbol` is now in scope. +``` + +#### Aliasing + +The `as` keyword enables renaming symbols: + +```zokrates +from "./path/to/my/module" import MySymbol as MyAlias + +// `MySymbol` is now in scope under the alias MyAlias. +``` +#### Legacy + +The legacy way to import a symbol is by only specifying a module: +``` +import "./path/to/my/module" +``` +In this case, the name of the symbol is assumed to be `main` and the alias is assumed to be the module's filename so that the above is equivalent to +```zokrates +from "./path/to/my/module" import main as module + +// `main` is now in scope under the alias `module`. +``` + +Note that this legacy method is likely to be become deprecated, so it is recommended to use the preferred way instead. +### Symbols + +Two type of symbols can be imported + +#### Functions +Functions are imported by name. If many functions have the same name but different signatures, all of them get imported, and which one to use in a particular call is infered. + +#### User-defined types +User-defined types declared with the `struct` keyword are imported by name. ### Relative Imports You can import a resource in the same folder directly, like this: ```zokrates -import "./mycode" +from "./mycode" import foo ``` There also is a handy syntax to import from the parent directory: ```zokrates -import "../mycode" +from "../mycode" import foo ``` Also imports further up the file-system are supported: ```zokrates -import "../../../mycode" -``` - -You can also choose to rename the imported resource, like so: -```zokrates -import "./mycode" as abc +from "../../../mycode" import foo ``` ### Absolute Imports diff --git a/zokrates_book/src/concepts/types.md b/zokrates_book/src/concepts/types.md index d75698de..67ad89f9 100644 --- a/zokrates_book/src/concepts/types.md +++ b/zokrates_book/src/concepts/types.md @@ -1,10 +1,10 @@ -## Types +# Types -ZoKrates currently exposes two primitive types and a complex array type: +ZoKrates currently exposes two primitive types and two complex types: -### Primitive Types +## Primitive Types -#### `field` +### `field` This is the most basic type in ZoKrates, and it represents a positive integer in `[0, p - 1]` where `p` is a (large) prime number. @@ -16,7 +16,7 @@ While `field` values mostly behave like unsigned integers, one should keep in mi {{#include ../../../zokrates_cli/examples/book/field_overflow.zok}} ``` -#### `bool` +### `bool` ZoKrates has limited support for booleans, to the extent that they can only be used as the condition in `if ... else ... endif` expressions. @@ -24,9 +24,11 @@ You can use them for equality checks, inequality checks and inequality checks be Note that while equality checks are cheap, inequality checks should be use wisely as they are orders of magnitude more expensive. -### Complex Types +## Complex Types -#### Arrays +ZoKrates provides two complex types, Arrays and Structs. + +### Arrays ZoKrates supports static arrays, i.e., their length needs to be known at compile time. Arrays can contain elements of any type and have arbitrary dimensions. @@ -37,10 +39,10 @@ The following examples code shows examples of how to use arrays: {{#include ../../../zokrates_cli/examples/book/array.zok}} ``` -##### Declaration and Initialization +#### Declaration and Initialization An array is defined by appending `[]` to a type literal representing the type of the array's elements. -Initialization always needs to happen in the same statement than declaration, unless the array is declared within a function's signature. +Initialization always needs to happen in the same statement as declaration, unless the array is declared within a function's signature. For initialization, a list of comma-separated values is provided within brackets `[]`. @@ -54,7 +56,7 @@ The following code provides examples for declaration and initialization: bool[13] b = [false; 13] // initialize a bool array with value false ``` -##### Multidimensional Arrays +#### Multidimensional Arrays As an array can contain any type of elements, it can contain arrays again. There is a special syntax to declare such multi-dimensional arrays, i.e., arrays of arrays. @@ -67,21 +69,59 @@ Consider the following example: {{#include ../../../zokrates_cli/examples/book/multidim_array.zok}} ``` -##### Spreads and Slices +#### Spreads and Slices ZoKrates provides some syntactic sugar to retrieve subsets of arrays. -###### Spreads -The spread operator `...` applied to an copies the elements of an existing array. +##### Spreads +The spread operator `...` applied to an array copies the elements of the existing array. This can be used to conveniently compose new arrays, as shown in the following example: ``` field[3] = [1, 2, 3] field[4] c = [...a, 4] // initialize an array copying values from `a`, followed by 4 ``` -###### Slices +##### Slices An array can also be assigned to by creating a copy of a subset of an existing array. This operation is called slicing, and the following example shows how to slice in ZoKrates: ``` field[3] a = [1, 2, 3] field[2] b = a[1..3] // initialize an array copying a slice from `a` ``` + +### Structs +A struct is a composite datatype representing a named collection of variables. +The contained variables can be of any type. + +The following code shows an example of how to use structs. + +```zokrates +{{#include ../../../zokrates_cli/examples/book/structs.code}} +``` + +#### Definition +Before a struct data type can be used, it needs to be defined. +A struct definition starts with the `struct` keyword followed by a name. Afterwards, a new-line separated list of variables is declared in curly braces `{}`. For example: + +```zokrates +struct Point { + field x + field y +} +``` + +#### Declaration and Initialization + +Initialization of a variable of a struct type always needs to happen in the same statement as declaration, unless the struct-typed variable is declared within a function's signature. + +The following example shows declaration and initialization of a variable of the `Point` struct type: + +```zokrates +{{#include ../../../zokrates_cli/examples/book/struct_init.code}} +``` + +#### Assignment +The variables within a struct instance, the so called members, can be accessed through the `.` operator as shown in the following extended example: + +```zokrates +{{#include ../../../zokrates_cli/examples/book/struct_assign.code}} +``` diff --git a/zokrates_cli/Cargo.lock b/zokrates_cli/Cargo.lock deleted file mode 100644 index 8e9c95ed..00000000 --- a/zokrates_cli/Cargo.lock +++ /dev/null @@ -1,670 +0,0 @@ -[[package]] -name = "aho-corasick" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "assert_cli" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "environment 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", - "skeptic 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "atty" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bincode" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitflags" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bytecount" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cargo_metadata" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cc" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "clap" -version = "2.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cmake" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "colored" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "difference" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "dtoa" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "environment" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "error-chain" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "gcc" -version = "0.3.54" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "glob" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "itoa" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lazy_static" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lazy_static" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lazy_static" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.42" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memchr" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", - "num-complex 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-bigint" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-complex" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-integer" -version = "0.1.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-iter" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-rational" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pulldown-cmark" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "redox_syscall" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_termios" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "reduce" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "regex" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aho-corasick 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex-syntax" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "remove_dir_all" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc-serialize" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "same-file" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde" -version = "1.0.68" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde_derive" -version = "1.0.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_json" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "skeptic" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytecount 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cargo_metadata 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "strsim" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syn" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tempdir" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termion" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "textwrap" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "thread_local" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ucd-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-width" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unreachable" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "utf8-ranges" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "vec_map" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "walkdir" -version = "2.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "zokrates" -version = "0.1.0" -dependencies = [ - "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cmake 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", - "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", - "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "reduce 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zokrates-cli" -version = "0.1.0" -dependencies = [ - "assert_cli 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "zokrates 0.1.0", -] - -[metadata] -"checksum aho-corasick 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f0ba20154ea1f47ce2793322f049c5646cc6d0fa9759d5f333f286e507bf8080" -"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum assert_cli 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72342c21057a3cb5f7c2d849bf7999a83795434dd36d74fa8c24680581bd1930" -"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" -"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" -"checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e" -"checksum bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e103c8b299b28a9c6990458b7013dc4a8356a9b854c51b9883241f5866fac36e" -"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" -"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" -"checksum bytecount 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "882585cd7ec84e902472df34a5e01891202db3bf62614e1f0afe459c1afcf744" -"checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" -"checksum cargo_metadata 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "682476b87b3e22cd3820d86b26cd8603cd84ab76dce7547b2631858347aa8967" -"checksum cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "49ec142f5768efb5b7622aebc3fdbdbb8950a4b9ba996393cb76ef7466e8747d" -"checksum cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efe5c877e17a9c717a0bf3613b2709f723202c4e4675cc8f12926ded29bcb17e" -"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" -"checksum cmake 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "95470235c31c726d72bf2e1f421adc1e65b9d561bf5529612cbe1a72da1467b3" -"checksum colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc" -"checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" -"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" -"checksum environment 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4b14e20978669064c33b4c1e0fb4083412e40fe56cbea2eae80fd7591503ee" -"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" -"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" -"checksum lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417" -"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" -"checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739" -"checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" -"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" -"checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" -"checksum num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" -"checksum num-complex 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "b288631d7878aaf59442cffd36910ea604ecd7745c36054328595114001c9656" -"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" -"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" -"checksum num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" -"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" -"checksum proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "effdb53b25cdad54f8f48843d67398f7ef2e14f12c1b4cb4effc549a6462a4d6" -"checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" -"checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" -"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" -"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum reduce 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f77b717415291f4d7929a111402316b272c566ae9d4b75a61507dba88ecbd89" -"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" -"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" -"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" -"checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649" -"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" -"checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)" = "429fcc4efa8a11341b5422c2ace724daba276c1748467e869478f53c0ba4562e" -"checksum serde_derive 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)" = "6a25ad0bf818ed2d180c89addbe29198d1de6c89ed08a48aa6a4d3d16a63cbfe" -"checksum serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "84b8035cabe9b35878adec8ac5fe03d5f6bc97ff6edd7ccb96b44c1276ba390e" -"checksum skeptic 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c4474d6da9593171bcb086890fc344a3a12783cb24e5b141f8a5d0e43561f4b6" -"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" -"checksum syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c67da57e61ebc7b7b6fff56bb34440ca3a83db037320b0507af4c10368deda7d" -"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" -"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" -"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" -"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" -"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" -"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "63636bd0eb3d00ccb8b9036381b526efac53caf112b7783b730ab3f8e44da369" -"checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/zokrates_cli/Cargo.toml b/zokrates_cli/Cargo.toml index 0e5bbdb0..03701014 100644 --- a/zokrates_cli/Cargo.toml +++ b/zokrates_cli/Cargo.toml @@ -16,6 +16,7 @@ clap = "2.26.2" bincode = "0.8.0" regex = "0.2" zokrates_field = { version = "0.3", path = "../zokrates_field" } +zokrates_abi = { version = "0.1", path = "../zokrates_abi" } zokrates_core = { version = "0.3", path = "../zokrates_core" } zokrates_fs_resolver = { version = "0.4", path = "../zokrates_fs_resolver"} zokrates_github_resolver = { version = "0.1", path = "../zokrates_github_resolver", optional = true} diff --git a/zokrates_cli/examples/book/struct_assign.code b/zokrates_cli/examples/book/struct_assign.code new file mode 100644 index 00000000..c52794d1 --- /dev/null +++ b/zokrates_cli/examples/book/struct_assign.code @@ -0,0 +1,10 @@ +struct Point { + field x + field y +} + +def main(field a) -> (Point): + Point p = Point {x: 1, y: 0} + p.x = a + p.y = p.x + return p diff --git a/zokrates_cli/examples/book/struct_init.code b/zokrates_cli/examples/book/struct_init.code new file mode 100644 index 00000000..837afc84 --- /dev/null +++ b/zokrates_cli/examples/book/struct_init.code @@ -0,0 +1,8 @@ +struct Point { + field x + field y +} + +def main() -> (Point): + Point p = Point {x: 1, y: 0} + return p diff --git a/zokrates_cli/examples/book/structs.code b/zokrates_cli/examples/book/structs.code new file mode 100644 index 00000000..a08399ba --- /dev/null +++ b/zokrates_cli/examples/book/structs.code @@ -0,0 +1,14 @@ +struct Bar { + field[2] c + bool d +} + +struct Foo { + Bar a + bool b +} + +def main() -> (Foo): + Foo[2] f = [Foo { a: Bar { c: [0, 0], d: false }, b: true}, Foo { a: Bar {c: [0, 0], d: false}, b: true}] + f[0].a.c = [42, 43] + return f[0] diff --git a/zokrates_cli/examples/structs/add.code b/zokrates_cli/examples/structs/add.code new file mode 100644 index 00000000..388ce158 --- /dev/null +++ b/zokrates_cli/examples/structs/add.code @@ -0,0 +1,16 @@ +struct Point { + field x + field y +} + +def main(Point p, Point q) -> (Point): + + field a = 42 + field d = 21 + + field dpxpyqxqy = d * p.x * p.y * q.x * q.y + + return Point { + x: (p.x * q.y + q.x * p.y) / (1 + dpxpyqxqy), + y: (q.x * q.y - a * p.x * p.y) / (1 - dpxpyqxqy) + } diff --git a/zokrates_cli/examples/structs/set_member.code b/zokrates_cli/examples/structs/set_member.code new file mode 100644 index 00000000..be326580 --- /dev/null +++ b/zokrates_cli/examples/structs/set_member.code @@ -0,0 +1,29 @@ +struct Bar { + field[2] c + bool d +} + +struct Foo { + Bar a + bool b +} + +def main() -> (Foo): + Foo[2] f = [ + Foo { + a: Bar { + c: [0, 0], + d: false + }, + b: true + }, + Foo { + a: Bar { + c: [0, 0], + d: false + }, + b: true + } + ] + f[0].a.c = [42, 43] + return f[0] diff --git a/zokrates_cli/src/bin.rs b/zokrates_cli/src/bin.rs index 511a5ecb..9def3e13 100644 --- a/zokrates_cli/src/bin.rs +++ b/zokrates_cli/src/bin.rs @@ -12,6 +12,7 @@ use std::io::{stdin, BufReader, BufWriter, Read, Write}; use std::path::{Path, PathBuf}; use std::string::String; use std::{env, io}; +use zokrates_abi::Encode; use zokrates_core::compile::compile; use zokrates_core::ir; use zokrates_core::proof_system::*; @@ -176,10 +177,19 @@ fn cli() -> Result<(), String> { ).arg(Arg::with_name("arguments") .short("a") .long("arguments") - .help("Arguments for the program's main method as a space separated list") + .help("Arguments for the program's main function") .takes_value(true) .multiple(true) // allows multiple values .required(false) + ).arg(Arg::with_name("abi") + .long("abi") + .help("Use the ABI") + .required(false) + ).arg(Arg::with_name("stdin") + .long("stdin") + .help("Read arguments from stdin") + .conflicts_with("arguments") + .required(false) ).arg(Arg::with_name("light") .long("light") .help("Skip logs and human readable output") @@ -329,60 +339,89 @@ fn cli() -> Result<(), String> { let mut reader = BufReader::new(file); - let program_ast: ir::Prog = + let ir_prog: ir::Prog = deserialize_from(&mut reader, Infinite).map_err(|why| why.to_string())?; // print deserialized flattened program if !sub_matches.is_present("light") { - println!("{}", program_ast); + println!("{}", ir_prog); } - let expected_cli_args_count = - program_ast.public_arguments_count() + program_ast.private_arguments_count(); + let signature = ir_prog.signature.clone(); + + let is_stdin = sub_matches.is_present("stdin"); + let is_abi = sub_matches.is_present("abi"); + + if !is_stdin && is_abi { + return Err( + "ABI input as inline argument is not supported. Please use `--stdin`.".into(), + ); + } + + use zokrates_abi::Inputs; // get arguments - let arguments: Vec<_> = match sub_matches.values_of("arguments") { + let arguments = match is_stdin { // take inline arguments - Some(p) => p - .map(|x| FieldPrime::try_from_dec_str(x).map_err(|_| x.to_string())) - .collect(), + false => { + let arguments = sub_matches.values_of("arguments"); + arguments + .map(|a| { + a.map(|x| FieldPrime::try_from_dec_str(x).map_err(|_| x.to_string())) + .collect::, _>>() + }) + .unwrap_or(Ok(vec![])) + .map(|v| Inputs::Raw(v)) + } // take stdin arguments - None => { - if expected_cli_args_count > 0 { - let mut stdin = stdin(); - let mut input = String::new(); - match stdin.read_to_string(&mut input) { + true => { + let mut stdin = stdin(); + let mut input = String::new(); + + match is_abi { + true => match stdin.read_to_string(&mut input) { Ok(_) => { - input.retain(|x| x != '\n'); - input - .split(" ") - .map(|x| { - FieldPrime::try_from_dec_str(x).map_err(|_| x.to_string()) - }) - .collect() + use zokrates_abi::parse_strict; + + parse_strict(&input, signature.inputs) + .map(|parsed| Inputs::Abi(parsed)) + .map_err(|why| why.to_string()) } Err(_) => Err(String::from("???")), - } - } else { - Ok(vec![]) + }, + false => match ir_prog.arguments_count() { + 0 => Ok(Inputs::Raw(vec![])), + _ => match stdin.read_to_string(&mut input) { + Ok(_) => { + input.retain(|x| x != '\n'); + input + .split(" ") + .map(|x| { + FieldPrime::try_from_dec_str(x) + .map_err(|_| x.to_string()) + }) + .collect::, _>>() + .map(|v| Inputs::Raw(v)) + } + Err(_) => Err(String::from("???")), + }, + }, } } } .map_err(|e| format!("Could not parse argument: {}", e))?; - if arguments.len() != expected_cli_args_count { - Err(format!( - "Wrong number of arguments. Given: {}, Required: {}.", - arguments.len(), - expected_cli_args_count - ))? - } - - let witness = program_ast - .execute(&arguments) + let witness = ir_prog + .execute(&arguments.encode()) .map_err(|e| format!("Execution failed: {}", e))?; - println!("\nWitness: \n\n{}", witness.format_outputs()); + use zokrates_abi::Decode; + + let results_json_value: serde_json::Value = + zokrates_abi::CheckedValues::decode(witness.return_values(), signature.outputs) + .into(); + + println!("\nWitness: \n\n{}", results_json_value.to_string()); // write witness to file let output_path = Path::new(sub_matches.value_of("output").unwrap()); diff --git a/zokrates_cli/tests/code/arithmetics.arguments.json b/zokrates_cli/tests/code/arithmetics.arguments.json index c57d705b..70faac20 100644 --- a/zokrates_cli/tests/code/arithmetics.arguments.json +++ b/zokrates_cli/tests/code/arithmetics.arguments.json @@ -1,4 +1,4 @@ [ - 1, - 2 + "1", + "2" ] \ No newline at end of file diff --git a/zokrates_cli/tests/code/if_else_false.arguments.json b/zokrates_cli/tests/code/if_else_false.arguments.json index 6e7ea636..45abfef6 100644 --- a/zokrates_cli/tests/code/if_else_false.arguments.json +++ b/zokrates_cli/tests/code/if_else_false.arguments.json @@ -1 +1,3 @@ -[0] \ No newline at end of file +[ + "0" +] \ No newline at end of file diff --git a/zokrates_cli/tests/code/if_else_true.arguments.json b/zokrates_cli/tests/code/if_else_true.arguments.json index bace2a0b..51ba4f97 100644 --- a/zokrates_cli/tests/code/if_else_true.arguments.json +++ b/zokrates_cli/tests/code/if_else_true.arguments.json @@ -1 +1,3 @@ -[1] \ No newline at end of file +[ + "1" +] \ No newline at end of file diff --git a/zokrates_cli/tests/code/multidim_update.arguments.json b/zokrates_cli/tests/code/multidim_update.arguments.json index 2f4b5615..607a017d 100644 --- a/zokrates_cli/tests/code/multidim_update.arguments.json +++ b/zokrates_cli/tests/code/multidim_update.arguments.json @@ -1,6 +1,12 @@ [ - 0, - 0, - 0, - 0 + [ + [ + "0", + "0" + ], + [ + "0", + "0" + ] + ] ] \ No newline at end of file diff --git a/zokrates_cli/tests/code/n_choose_k.arguments.json b/zokrates_cli/tests/code/n_choose_k.arguments.json index 93317aa2..2ef9360d 100644 --- a/zokrates_cli/tests/code/n_choose_k.arguments.json +++ b/zokrates_cli/tests/code/n_choose_k.arguments.json @@ -1 +1,4 @@ -[5, 1] \ No newline at end of file +[ + "5", + "1" +] \ No newline at end of file diff --git a/zokrates_cli/tests/code/no_return.arguments.json b/zokrates_cli/tests/code/no_return.arguments.json index 9b3abfd7..6d113a65 100644 --- a/zokrates_cli/tests/code/no_return.arguments.json +++ b/zokrates_cli/tests/code/no_return.arguments.json @@ -1,4 +1,4 @@ [ - 1, - 1 + "1", + "1" ] \ No newline at end of file diff --git a/zokrates_cli/tests/code/return_array.arguments.json b/zokrates_cli/tests/code/return_array.arguments.json index d0360628..70ac3a6c 100644 --- a/zokrates_cli/tests/code/return_array.arguments.json +++ b/zokrates_cli/tests/code/return_array.arguments.json @@ -1 +1,14 @@ -[1, 1, 1, 2, 3, 3, 3, 3] \ No newline at end of file +[ + [ + "1", + "1", + "1" + ], + "2", + [ + "3", + "3", + "3", + "3" + ] +] \ No newline at end of file diff --git a/zokrates_cli/tests/code/sha_round.arguments.json b/zokrates_cli/tests/code/sha_round.arguments.json index 5e2441a0..b0eb4bb3 100644 --- a/zokrates_cli/tests/code/sha_round.arguments.json +++ b/zokrates_cli/tests/code/sha_round.arguments.json @@ -1 +1,260 @@ -[0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1] \ No newline at end of file +[ + [ + "0", + "0", + "0", + "1", + "1", + "1", + "1", + "1", + "0", + "0", + "1", + "1", + "1", + "0", + "1", + "1", + "1", + "0", + "0", + "0", + "1", + "0", + "1", + "1", + "1", + "0", + "0", + "1", + "1", + "0", + "0", + "0", + "1", + "1", + "0", + "0", + "0", + "0", + "1", + "0", + "0", + "0", + "0", + "1", + "1", + "1", + "1", + "0", + "1", + "0", + "1", + "1", + "1", + "0", + "0", + "0", + "1", + "0", + "0", + "1", + "0", + "1", + "0", + "0", + "0", + "0", + "1", + "1", + "1", + "1", + "0", + "0", + "1", + "0", + "0", + "0", + "1", + "1", + "1", + "0", + "1", + "1", + "1", + "0", + "0", + "0", + "1", + "1", + "0", + "0", + "1", + "1", + "0", + "0", + "1", + "0", + "0", + "0", + "1", + "0", + "1", + "1", + "0", + "0", + "0", + "0", + "0", + "1", + "0", + "1", + "0", + "0", + "0", + "0", + "0", + "1", + "0", + "1", + "0", + "0", + "1", + "0", + "1", + "1", + "0", + "1", + "1", + "0", + "0", + "0", + "0", + "1", + "0", + "0", + "0", + "0", + "0", + "1", + "0", + "1", + "0", + "1", + "0", + "1", + "0", + "1", + "1", + "0", + "0", + "0", + "1", + "0", + "0", + "1", + "1", + "0", + "0", + "0", + "0", + "1", + "0", + "1", + "0", + "0", + "1", + "1", + "1", + "0", + "0", + "1", + "1", + "1", + "0", + "0", + "1", + "1", + "1", + "0", + "0", + "0", + "1", + "1", + "1", + "1", + "0", + "0", + "1", + "1", + "0", + "1", + "0", + "1", + "1", + "1", + "1", + "0", + "1", + "1", + "1", + "1", + "0", + "0", + "0", + "1", + "0", + "0", + "1", + "1", + "1", + "0", + "1", + "0", + "0", + "0", + "0", + "0", + "0", + "1", + "1", + "1", + "1", + "0", + "1", + "1", + "1", + "1", + "1", + "0", + "1", + "0", + "1", + "0", + "1", + "1", + "0", + "0", + "1", + "1", + "0", + "0", + "0", + "0", + "1", + "1", + "1", + "1", + "0", + "1", + "0", + "0", + "1", + "0", + "1", + "1", + "0", + "1" + ] +] \ No newline at end of file diff --git a/zokrates_cli/tests/code/simple_add.arguments.json b/zokrates_cli/tests/code/simple_add.arguments.json index fd8ef095..70faac20 100644 --- a/zokrates_cli/tests/code/simple_add.arguments.json +++ b/zokrates_cli/tests/code/simple_add.arguments.json @@ -1 +1,4 @@ -[1, 2] \ No newline at end of file +[ + "1", + "2" +] \ No newline at end of file diff --git a/zokrates_cli/tests/code/simple_mul.arguments.json b/zokrates_cli/tests/code/simple_mul.arguments.json index f26b6970..f9ca65e0 100644 --- a/zokrates_cli/tests/code/simple_mul.arguments.json +++ b/zokrates_cli/tests/code/simple_mul.arguments.json @@ -1 +1,5 @@ -[2, 3, 4] \ No newline at end of file +[ + "2", + "3", + "4" +] \ No newline at end of file diff --git a/zokrates_cli/tests/code/taxation.arguments.json b/zokrates_cli/tests/code/taxation.arguments.json index ecb0a5bd..030cb431 100644 --- a/zokrates_cli/tests/code/taxation.arguments.json +++ b/zokrates_cli/tests/code/taxation.arguments.json @@ -1 +1,4 @@ -[15, 12] \ No newline at end of file +[ + "15", + "12" +] \ No newline at end of file diff --git a/zokrates_cli/tests/integration.rs b/zokrates_cli/tests/integration.rs index 972685ad..63d70a52 100644 --- a/zokrates_cli/tests/integration.rs +++ b/zokrates_cli/tests/integration.rs @@ -4,14 +4,16 @@ extern crate serde_json; #[cfg(test)] mod integration { use assert_cli; - use serde_json; - use serde_json::Value; + use bincode::{deserialize_from, Infinite}; use std::fs; use std::fs::File; - use std::io::prelude::*; + use std::io::{BufReader, Read}; use std::panic; use std::path::Path; use tempdir::TempDir; + use zokrates_abi::{parse_strict, Encode}; + use zokrates_core::ir; + use zokrates_field::field::FieldPrime; #[test] #[ignore] @@ -29,8 +31,13 @@ mod integration { Path::new(Path::new(path.file_stem().unwrap()).file_stem().unwrap()); let prog = dir.join(program_name).with_extension("zok"); let witness = dir.join(program_name).with_extension("expected.witness"); - let args = dir.join(program_name).with_extension("arguments.json"); - test_compile_and_witness(program_name.to_str().unwrap(), &prog, &args, &witness); + let json_input = dir.join(program_name).with_extension("arguments.json"); + test_compile_and_witness( + program_name.to_str().unwrap(), + &prog, + &json_input, + &witness, + ); } } } @@ -47,7 +54,7 @@ mod integration { fn test_compile_and_witness( program_name: &str, program_path: &Path, - arguments_path: &Path, + inputs_path: &Path, expected_witness_path: &Path, ) { let tmp_dir = TempDir::new(".tmp").unwrap(); @@ -88,24 +95,51 @@ mod integration { assert_cli::Assert::command(&compile).succeeds().unwrap(); // COMPUTE_WITNESS - let arguments: Value = - serde_json::from_reader(File::open(arguments_path).unwrap()).unwrap(); - let arguments_str_list: Vec = arguments - .as_array() - .unwrap() - .iter() - .map(|i| match *i { - Value::Number(ref n) => n.to_string(), - _ => panic!(format!( - "Cannot read arguments. Check {}", - arguments_path.to_str().unwrap() - )), - }) + // derive program signature from IR program representation + let file = File::open(&flattened_path) + .map_err(|why| format!("couldn't open {}: {}", flattened_path.display(), why)) + .unwrap(); + + let mut reader = BufReader::new(file); + + let ir_prog: ir::Prog = deserialize_from(&mut reader, Infinite) + .map_err(|why| why.to_string()) + .unwrap(); + + let signature = ir_prog.signature.clone(); + + // run witness-computation for ABI-encoded inputs through stdin + let json_input_str = fs::read_to_string(inputs_path).unwrap(); + + let compute = vec![ + "../target/release/zokrates", + "compute-witness", + "-i", + flattened_path.to_str().unwrap(), + "-o", + witness_path.to_str().unwrap(), + "--stdin", + "--abi", + ]; + + assert_cli::Assert::command(&compute) + .stdin(&json_input_str) + .succeeds() + .unwrap(); + + // run witness-computation for raw-encoded inputs (converted) with `-a ` + let inputs_abi: zokrates_abi::Inputs = + parse_strict(&json_input_str, signature.inputs) + .map(|parsed| zokrates_abi::Inputs::Abi(parsed)) + .map_err(|why| why.to_string()) + .unwrap(); + let inputs_raw: Vec<_> = inputs_abi + .encode() + .into_iter() + .map(|v| v.to_string()) .collect(); - // WITH `-a ` - let mut compute_inline = vec![ "../target/release/zokrates", "compute-witness", @@ -116,7 +150,7 @@ mod integration { "-a", ]; - for arg in arguments_str_list.iter() { + for arg in &inputs_raw { compute_inline.push(arg); } @@ -124,22 +158,6 @@ mod integration { .succeeds() .unwrap(); - // WITH stdin ARGUMENTS - - let compute = vec![ - "../target/release/zokrates", - "compute-witness", - "-i", - flattened_path.to_str().unwrap(), - "-o", - witness_path.to_str().unwrap(), - ]; - - assert_cli::Assert::command(&compute) - .stdin(&arguments_str_list.join(" ")) - .succeeds() - .unwrap(); - // load the expected witness let mut expected_witness_file = File::open(&expected_witness_path).unwrap(); let mut expected_witness = String::new(); diff --git a/zokrates_core/src/absy/from_ast.rs b/zokrates_core/src/absy/from_ast.rs index 45868143..44532609 100644 --- a/zokrates_core/src/absy/from_ast.rs +++ b/zokrates_core/src/absy/from_ast.rs @@ -1,48 +1,96 @@ use absy; use imports; -use types::Type; use zokrates_field::field::Field; use zokrates_pest_ast as pest; impl<'ast, T: Field> From> for absy::Module<'ast, T> { fn from(prog: pest::File<'ast>) -> absy::Module { - absy::Module { - functions: prog - .functions + absy::Module::with_symbols( + prog.structs .into_iter() - .map(|f| absy::FunctionDeclarationNode::from(f)) - .collect(), - imports: prog - .imports - .into_iter() - .map(|i| absy::ImportNode::from(i)) - .collect(), - } + .map(|t| absy::SymbolDeclarationNode::from(t)) + .chain( + prog.functions + .into_iter() + .map(|f| absy::SymbolDeclarationNode::from(f)), + ), + ) + .imports(prog.imports.into_iter().map(|i| absy::ImportNode::from(i))) } } impl<'ast> From> for absy::ImportNode<'ast> { fn from(import: pest::ImportDirective<'ast>) -> absy::ImportNode { use absy::NodeValue; - imports::Import::new(import.source.span.as_str()) + + match import { + pest::ImportDirective::Main(import) => { + imports::Import::new(None, 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(), + ) .alias(import.alias.map(|a| a.span.as_str())) - .span(import.span) + .span(import.span), + } } } -impl<'ast, T: Field> From> for absy::FunctionDeclarationNode<'ast, T> { - fn from(function: pest::Function<'ast>) -> absy::FunctionDeclarationNode { +impl<'ast, T: Field> From> for absy::SymbolDeclarationNode<'ast, T> { + fn from(definition: pest::StructDefinition<'ast>) -> absy::SymbolDeclarationNode<'ast, T> { + use absy::NodeValue; + + let span = definition.span; + + let id = definition.id.span.as_str(); + + let ty = absy::StructType { + fields: definition + .fields + .into_iter() + .map(|f| absy::StructFieldNode::from(f)) + .collect(), + } + .span(span.clone()); + + absy::SymbolDeclaration { + id, + symbol: absy::Symbol::HereType(ty), + } + .span(span) + } +} + +impl<'ast> From> for absy::StructFieldNode<'ast> { + fn from(field: pest::StructField<'ast>) -> absy::StructFieldNode { + use absy::NodeValue; + + let span = field.span; + + let id = field.id.span.as_str(); + + let ty = absy::UnresolvedTypeNode::from(field.ty); + + absy::StructField { id, ty }.span(span) + } +} + +impl<'ast, T: Field> From> for absy::SymbolDeclarationNode<'ast, T> { + fn from(function: pest::Function<'ast>) -> absy::SymbolDeclarationNode { use absy::NodeValue; let span = function.span; - let signature = absy::Signature::new() + let signature = absy::UnresolvedSignature::new() .inputs( function .parameters .clone() .into_iter() - .map(|p| absy::ParameterNode::from(p).value.id.value.get_type()) + .map(|p| absy::UnresolvedTypeNode::from(p.ty)) .collect(), ) .outputs( @@ -50,7 +98,7 @@ impl<'ast, T: Field> From> for absy::FunctionDeclarationNod .returns .clone() .into_iter() - .map(|r| Type::from(r)) + .map(|r| absy::UnresolvedTypeNode::from(r)) .collect(), ); @@ -71,9 +119,9 @@ impl<'ast, T: Field> From> for absy::FunctionDeclarationNod } .span(span.clone()); - absy::FunctionDeclaration { + absy::SymbolDeclaration { id, - symbol: absy::FunctionSymbol::Here(function), + symbol: absy::Symbol::HereFunction(function), } .span(span) } @@ -91,8 +139,11 @@ impl<'ast> From> for absy::ParameterNode<'ast> { }) .unwrap_or(false); - let variable = - absy::Variable::new(param.id.span.as_str(), Type::from(param.ty)).span(param.id.span); + let variable = absy::Variable::new( + param.id.span.as_str(), + absy::UnresolvedTypeNode::from(param.ty), + ) + .span(param.id.span); absy::Parameter::new(variable, private).span(param.span) } @@ -123,7 +174,11 @@ fn statements_from_multi_assignment<'ast, T: Field>( .filter(|i| i.ty.is_some()) .map(|i| { absy::Statement::Declaration( - absy::Variable::new(i.id.span.as_str(), Type::from(i.ty.unwrap())).span(i.id.span), + absy::Variable::new( + i.id.span.as_str(), + absy::UnresolvedTypeNode::from(i.ty.unwrap()), + ) + .span(i.id.span), ) .span(i.span) }); @@ -158,8 +213,11 @@ fn statements_from_definition<'ast, T: Field>( vec![ absy::Statement::Declaration( - absy::Variable::new(definition.id.span.as_str(), Type::from(definition.ty)) - .span(definition.id.span.clone()), + absy::Variable::new( + definition.id.span.as_str(), + absy::UnresolvedTypeNode::from(definition.ty), + ) + .span(definition.id.span.clone()), ) .span(definition.span.clone()), absy::Statement::Definition( @@ -218,7 +276,7 @@ impl<'ast, T: Field> From> for absy::StatementNod let from = absy::ExpressionNode::from(statement.from); let to = absy::ExpressionNode::from(statement.to); let index = statement.index.span.as_str(); - let ty = Type::from(statement.ty); + let ty = absy::UnresolvedTypeNode::from(statement.ty); let statements: Vec> = statement .statements .into_iter() @@ -262,6 +320,7 @@ impl<'ast, T: Field> From> for absy::ExpressionNode<'ast, pest::Expression::Identifier(e) => absy::ExpressionNode::from(e), pest::Expression::Postfix(e) => absy::ExpressionNode::from(e), pest::Expression::InlineArray(e) => absy::ExpressionNode::from(e), + pest::Expression::InlineStruct(e) => absy::ExpressionNode::from(e), pest::Expression::ArrayInitializer(e) => absy::ExpressionNode::from(e), pest::Expression::Unary(e) => absy::ExpressionNode::from(e), } @@ -414,6 +473,25 @@ impl<'ast, T: Field> From> for absy::Expressio } } +impl<'ast, T: Field> From> for absy::ExpressionNode<'ast, T> { + fn from(s: pest::InlineStructExpression<'ast>) -> absy::ExpressionNode<'ast, T> { + use absy::NodeValue; + absy::Expression::InlineStruct( + s.ty.span.as_str().to_string(), + s.members + .into_iter() + .map(|member| { + ( + member.id.span.as_str(), + absy::ExpressionNode::from(member.expression), + ) + }) + .collect(), + ) + .span(s.span) + } +} + impl<'ast, T: Field> From> for absy::ExpressionNode<'ast, T> { @@ -471,6 +549,9 @@ impl<'ast, T: Field> From> for absy::ExpressionNod absy::Expression::Select(box acc, box absy::RangeOrExpression::from(a.expression)) .span(a.span) } + pest::Access::Member(m) => { + absy::Expression::Member(box acc, box m.id.span.as_str()).span(m.span) + } }) } } @@ -511,29 +592,44 @@ impl<'ast, T: Field> From> for absy::AssigneeNode<'ast, T> let a = absy::AssigneeNode::from(assignee.id); let span = assignee.span; - assignee - .indices - .into_iter() - .map(|i| absy::RangeOrExpression::from(i)) - .fold(a, |acc, s| { - absy::Assignee::Select(box acc, box s).span(span.clone()) - }) + assignee.accesses.into_iter().fold(a, |acc, s| { + match s { + pest::AssigneeAccess::Select(s) => { + absy::Assignee::Select(box acc, box absy::RangeOrExpression::from(s.expression)) + } + pest::AssigneeAccess::Member(m) => { + absy::Assignee::Member(box acc, box m.id.span.as_str()) + } + } + .span(span.clone()) + }) } } -impl<'ast> From> for Type { - fn from(t: pest::Type<'ast>) -> Type { +impl<'ast> From> for absy::UnresolvedTypeNode { + fn from(t: pest::Type<'ast>) -> absy::UnresolvedTypeNode { + use absy::NodeValue; + match t { pest::Type::Basic(t) => match t { - pest::BasicType::Field(_) => Type::FieldElement, - pest::BasicType::Boolean(_) => Type::Boolean, + pest::BasicType::Field(t) => absy::UnresolvedType::FieldElement.span(t.span), + pest::BasicType::Boolean(t) => absy::UnresolvedType::Boolean.span(t.span), }, pest::Type::Array(t) => { let inner_type = match t.ty { - pest::BasicType::Field(_) => Type::FieldElement, - pest::BasicType::Boolean(_) => Type::Boolean, + pest::BasicOrStructType::Basic(t) => match t { + pest::BasicType::Field(t) => { + absy::UnresolvedType::FieldElement.span(t.span) + } + pest::BasicType::Boolean(t) => absy::UnresolvedType::Boolean.span(t.span), + }, + pest::BasicOrStructType::Struct(t) => { + absy::UnresolvedType::User(t.span.as_str().to_string()).span(t.span) + } }; + let span = t.span; + t.dimensions .into_iter() .map(|s| match s { @@ -553,10 +649,14 @@ impl<'ast> From> for Type { }) .rev() .fold(None, |acc, s| match acc { - None => Some(Type::array(inner_type.clone(), s)), - Some(acc) => Some(Type::array(acc, s)), + None => Some(absy::UnresolvedType::array(inner_type.clone(), s)), + Some(acc) => Some(absy::UnresolvedType::array(acc.span(span.clone()), s)), }) .unwrap() + .span(span.clone()) + } + pest::Type::Struct(s) => { + absy::UnresolvedType::User(s.id.span.as_str().to_string()).span(s.span) } } } @@ -565,6 +665,7 @@ impl<'ast> From> for Type { #[cfg(test)] mod tests { use super::*; + use absy::NodeValue; use zokrates_field::field::FieldPrime; #[test] @@ -572,9 +673,9 @@ mod tests { let source = "def main() -> (field): return 42"; let ast = pest::generate_ast(&source).unwrap(); let expected: absy::Module = absy::Module { - functions: vec![absy::FunctionDeclaration { + symbols: vec![absy::SymbolDeclaration { id: &source[4..8], - symbol: absy::FunctionSymbol::Here( + symbol: absy::Symbol::HereFunction( absy::Function { arguments: vec![], statements: vec![absy::Statement::Return( @@ -587,9 +688,9 @@ mod tests { .into(), ) .into()], - signature: absy::Signature::new() + signature: absy::UnresolvedSignature::new() .inputs(vec![]) - .outputs(vec![Type::FieldElement]), + .outputs(vec![absy::UnresolvedType::FieldElement.mock()]), } .into(), ), @@ -605,9 +706,9 @@ mod tests { let source = "def main() -> (bool): return true"; let ast = pest::generate_ast(&source).unwrap(); let expected: absy::Module = absy::Module { - functions: vec![absy::FunctionDeclaration { + symbols: vec![absy::SymbolDeclaration { id: &source[4..8], - symbol: absy::FunctionSymbol::Here( + symbol: absy::Symbol::HereFunction( absy::Function { arguments: vec![], statements: vec![absy::Statement::Return( @@ -617,9 +718,9 @@ mod tests { .into(), ) .into()], - signature: absy::Signature::new() + signature: absy::UnresolvedSignature::new() .inputs(vec![]) - .outputs(vec![Type::Boolean]), + .outputs(vec![absy::UnresolvedType::Boolean.mock()]), } .into(), ), @@ -636,17 +737,25 @@ mod tests { let ast = pest::generate_ast(&source).unwrap(); let expected: absy::Module = absy::Module { - functions: vec![absy::FunctionDeclaration { + symbols: vec![absy::SymbolDeclaration { id: &source[4..8], - symbol: absy::FunctionSymbol::Here( + symbol: absy::Symbol::HereFunction( absy::Function { arguments: vec![ absy::Parameter::private( - absy::Variable::field_element(&source[23..24]).into(), + absy::Variable::new( + &source[23..24], + absy::UnresolvedType::FieldElement.mock(), + ) + .into(), ) .into(), absy::Parameter::public( - absy::Variable::boolean(&source[31..32]).into(), + absy::Variable::new( + &source[31..32], + absy::UnresolvedType::Boolean.mock(), + ) + .into(), ) .into(), ], @@ -660,9 +769,12 @@ mod tests { .into(), ) .into()], - signature: absy::Signature::new() - .inputs(vec![Type::FieldElement, Type::Boolean]) - .outputs(vec![Type::FieldElement]), + signature: absy::UnresolvedSignature::new() + .inputs(vec![ + absy::UnresolvedType::FieldElement.mock(), + absy::UnresolvedType::Boolean.mock(), + ]) + .outputs(vec![absy::UnresolvedType::FieldElement.mock()]), } .into(), ), @@ -678,14 +790,14 @@ mod tests { use super::*; /// Helper method to generate the ast for `def main(private {ty} a) -> (): return` which we use to check ty - fn wrap(ty: types::Type) -> absy::Module<'static, FieldPrime> { + fn wrap(ty: absy::UnresolvedType) -> absy::Module<'static, FieldPrime> { absy::Module { - functions: vec![absy::FunctionDeclaration { + symbols: vec![absy::SymbolDeclaration { id: "main", - symbol: absy::FunctionSymbol::Here( + symbol: absy::Symbol::HereFunction( absy::Function { arguments: vec![absy::Parameter::private( - absy::Variable::new("a", ty.clone()).into(), + absy::Variable::new("a", ty.clone().mock()).into(), ) .into()], statements: vec![absy::Statement::Return( @@ -695,7 +807,7 @@ mod tests { .into(), ) .into()], - signature: absy::Signature::new().inputs(vec![ty]), + signature: absy::UnresolvedSignature::new().inputs(vec![ty.mock()]), } .into(), ), @@ -708,19 +820,33 @@ mod tests { #[test] fn array() { let vectors = vec![ - ("field", types::Type::FieldElement), - ("bool", types::Type::Boolean), + ("field", absy::UnresolvedType::FieldElement), + ("bool", absy::UnresolvedType::Boolean), ( "field[2]", - types::Type::Array(box types::Type::FieldElement, 2), + absy::UnresolvedType::Array(box absy::UnresolvedType::FieldElement.mock(), 2), ), ( "field[2][3]", - types::Type::Array(box Type::Array(box types::Type::FieldElement, 3), 2), + absy::UnresolvedType::Array( + box absy::UnresolvedType::Array( + box absy::UnresolvedType::FieldElement.mock(), + 3, + ) + .mock(), + 2, + ), ), ( "bool[2][3]", - types::Type::Array(box Type::Array(box types::Type::Boolean, 3), 2), + absy::UnresolvedType::Array( + box absy::UnresolvedType::Array( + box absy::UnresolvedType::Boolean.mock(), + 3, + ) + .mock(), + 2, + ), ), ]; @@ -737,9 +863,9 @@ mod tests { use super::*; fn wrap(expression: absy::Expression<'static, FieldPrime>) -> absy::Module { absy::Module { - functions: vec![absy::FunctionDeclaration { + symbols: vec![absy::SymbolDeclaration { id: "main", - symbol: absy::FunctionSymbol::Here( + symbol: absy::Symbol::HereFunction( absy::Function { arguments: vec![], statements: vec![absy::Statement::Return( @@ -749,7 +875,7 @@ mod tests { .into(), ) .into()], - signature: absy::Signature::new(), + signature: absy::UnresolvedSignature::new(), } .into(), ), diff --git a/zokrates_core/src/absy/mod.rs b/zokrates_core/src/absy/mod.rs index 0b884cb0..c2e1e283 100644 --- a/zokrates_core/src/absy/mod.rs +++ b/zokrates_core/src/absy/mod.rs @@ -8,12 +8,13 @@ mod from_ast; mod node; pub mod parameter; +pub mod types; pub mod variable; pub use crate::absy::node::{Node, NodeValue}; pub use crate::absy::parameter::{Parameter, ParameterNode}; +use crate::absy::types::{FunctionIdentifier, UnresolvedSignature, UnresolvedType, UserTypeId}; pub use crate::absy::variable::{Variable, VariableNode}; -use crate::types::{FunctionIdentifier, Signature}; use embed::FlatEmbed; use crate::imports::ImportNode; @@ -31,8 +32,8 @@ pub type ModuleId = String; /// A collection of `Module`s pub type Modules<'ast, T> = HashMap>; -/// A collection of `FunctionDeclaration`. Duplicates are allowed here as they are fine syntatically. -pub type FunctionDeclarations<'ast, T> = Vec>; +/// A collection of `SymbolDeclaration`. Duplicates are allowed here as they are fine syntactically. +pub type Declarations<'ast, T> = Vec>; /// A `Program` is a collection of `Module`s and an id of the main `Module` pub struct Program<'ast, T: Field> { @@ -42,17 +43,26 @@ pub struct Program<'ast, T: Field> { /// A declaration of a `FunctionSymbol`, be it from an import or a function definition #[derive(PartialEq, Debug, Clone)] -pub struct FunctionDeclaration<'ast, T: Field> { +pub struct SymbolDeclaration<'ast, T: Field> { pub id: Identifier<'ast>, - pub symbol: FunctionSymbol<'ast, T>, + pub symbol: Symbol<'ast, T>, } -impl<'ast, T: Field> fmt::Display for FunctionDeclaration<'ast, T> { +#[derive(PartialEq, Debug, Clone)] +pub enum Symbol<'ast, T: Field> { + HereType(StructTypeNode<'ast>), + HereFunction(FunctionNode<'ast, T>), + There(SymbolImportNode<'ast>), + Flat(FlatEmbed), +} + +impl<'ast, T: Field> fmt::Display for SymbolDeclaration<'ast, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.symbol { - FunctionSymbol::Here(ref fun) => write!(f, "def {}{}", self.id, fun), - FunctionSymbol::There(ref import) => write!(f, "import {} as {}", import, self.id), - FunctionSymbol::Flat(ref flat_fun) => write!( + Symbol::HereType(ref t) => write!(f, "struct {} {}", self.id, t), + Symbol::HereFunction(ref fun) => write!(f, "def {}{}", self.id, fun), + Symbol::There(ref import) => write!(f, "import {} as {}", import, self.id), + Symbol::Flat(ref flat_fun) => write!( f, "def {}{}:\n\t// hidden", self.id, @@ -62,50 +72,95 @@ impl<'ast, T: Field> fmt::Display for FunctionDeclaration<'ast, T> { } } -type FunctionDeclarationNode<'ast, T> = Node>; +pub type SymbolDeclarationNode<'ast, T> = Node>; /// A module as a collection of `FunctionDeclaration`s #[derive(Clone, PartialEq)] pub struct Module<'ast, T: Field> { - /// Functions of the module - pub functions: FunctionDeclarations<'ast, T>, + /// Symbols of the module + pub symbols: Declarations<'ast, T>, pub imports: Vec>, // we still use `imports` as they are not directly converted into `FunctionDeclaration`s after the importer is done, `imports` is empty } -/// A function, be it defined in this module, imported from another module or a flat embed -#[derive(Debug, Clone, PartialEq)] -pub enum FunctionSymbol<'ast, T: Field> { - Here(FunctionNode<'ast, T>), - There(FunctionImportNode<'ast>), - Flat(FlatEmbed), +impl<'ast, T: Field> Module<'ast, T> { + pub fn with_symbols>>(i: I) -> Self { + Module { + symbols: i.into_iter().collect(), + imports: vec![], + } + } + + pub fn imports>>(mut self, i: I) -> Self { + self.imports = i.into_iter().collect(); + self + } } -/// A function import +pub type UnresolvedTypeNode = Node; + +/// A struct type definition #[derive(Debug, Clone, PartialEq)] -pub struct FunctionImport<'ast> { - /// the id of the function in the target module. Note: there may be many candidates as imports statements do not specify the signature - pub function_id: Identifier<'ast>, +pub struct StructType<'ast> { + pub fields: Vec>, +} + +impl<'ast> fmt::Display for StructType<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + self.fields + .iter() + .map(|fi| fi.to_string()) + .collect::>() + .join("\n") + ) + } +} + +pub type StructTypeNode<'ast> = Node>; + +/// A struct type definition +#[derive(Debug, Clone, PartialEq)] +pub struct StructField<'ast> { + pub id: Identifier<'ast>, + pub ty: UnresolvedTypeNode, +} + +impl<'ast> fmt::Display for StructField<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}: {},", self.id, self.ty) + } +} + +type StructFieldNode<'ast> = Node>; + +/// An import +#[derive(Debug, Clone, PartialEq)] +pub struct SymbolImport<'ast> { + /// the id of the symbol in the target module. Note: there may be many candidates as imports statements do not specify the signature. In that case they must all be functions however. + pub symbol_id: Identifier<'ast>, /// the id of the module to import from pub module_id: ModuleId, } -type FunctionImportNode<'ast> = Node>; +type SymbolImportNode<'ast> = Node>; -impl<'ast> FunctionImport<'ast> { +impl<'ast> SymbolImport<'ast> { pub fn with_id_in_module>, U: Into>( - function_id: S, + symbol_id: S, module_id: U, ) -> Self { - FunctionImport { - function_id: function_id.into(), + SymbolImport { + symbol_id: symbol_id.into(), module_id: module_id.into(), } } } -impl<'ast> fmt::Display for FunctionImport<'ast> { +impl<'ast> fmt::Display for SymbolImport<'ast> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} from {}", self.function_id, self.module_id) + write!(f, "{} from {}", self.symbol_id, self.module_id) } } @@ -119,7 +174,7 @@ impl<'ast, T: Field> fmt::Display for Module<'ast, T> { .collect::>(), ); res.extend( - self.functions + self.symbols .iter() .map(|x| format!("{}", x)) .collect::>(), @@ -132,13 +187,13 @@ impl<'ast, T: Field> fmt::Debug for Module<'ast, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "module(\n\timports:\n\t\t{}\n\tfunctions:\n\t\t{}\n)", + "module(\n\timports:\n\t\t{}\n\tsymbols:\n\t\t{}\n)", self.imports .iter() .map(|x| format!("{:?}", x)) .collect::>() .join("\n\t\t"), - self.functions + self.symbols .iter() .map(|x| format!("{:?}", x)) .collect::>() @@ -155,7 +210,7 @@ pub struct Function<'ast, T: Field> { /// Vector of statements that are executed when running the function pub statements: Vec>, /// function signature - pub signature: Signature, + pub signature: UnresolvedSignature, } pub type FunctionNode<'ast, T> = Node>; @@ -199,6 +254,7 @@ impl<'ast, T: Field> fmt::Debug for Function<'ast, T> { pub enum Assignee<'ast, T: Field> { Identifier(Identifier<'ast>), Select(Box>, Box>), + Member(Box>, Box>), } pub type AssigneeNode<'ast, T> = Node>; @@ -208,6 +264,7 @@ impl<'ast, T: Field> fmt::Debug for Assignee<'ast, T> { match *self { Assignee::Identifier(ref s) => write!(f, "Identifier({:?})", s), Assignee::Select(ref a, ref e) => write!(f, "Select({:?}[{:?}])", a, e), + Assignee::Member(ref s, ref m) => write!(f, "Member({:?}.{:?})", s, m), } } } @@ -414,10 +471,12 @@ pub enum Expression<'ast, T: Field> { And(Box>, Box>), Not(Box>), InlineArray(Vec>), + InlineStruct(UserTypeId, Vec<(Identifier<'ast>, ExpressionNode<'ast, T>)>), Select( Box>, Box>, ), + Member(Box>, Box>), Or(Box>, Box>), } @@ -466,7 +525,18 @@ impl<'ast, T: Field> fmt::Display for Expression<'ast, T> { } write!(f, "]") } + Expression::InlineStruct(ref id, ref members) => { + write!(f, "{} {{", id)?; + for (i, (member_id, e)) in members.iter().enumerate() { + write!(f, "{}: {}", member_id, e)?; + if i < members.len() - 1 { + write!(f, ", ")?; + } + } + write!(f, "}}") + } Expression::Select(ref array, ref index) => write!(f, "{}[{}]", array, index), + Expression::Member(ref struc, ref id) => write!(f, "{}.{}", struc, id), Expression::Or(ref lhs, ref rhs) => write!(f, "{} || {}", lhs, rhs), } } @@ -505,9 +575,15 @@ impl<'ast, T: Field> fmt::Debug for Expression<'ast, T> { f.debug_list().entries(exprs.iter()).finish()?; write!(f, "]") } + Expression::InlineStruct(ref id, ref members) => { + write!(f, "InlineStruct({:?}, [", id)?; + f.debug_list().entries(members.iter()).finish()?; + write!(f, "]") + } Expression::Select(ref array, ref index) => { write!(f, "Select({:?}, {:?})", array, index) } + Expression::Member(ref struc, ref id) => write!(f, "{}.{}", struc, id), Expression::Or(ref lhs, ref rhs) => write!(f, "{} || {}", lhs, rhs), } } diff --git a/zokrates_core/src/absy/node.rs b/zokrates_core/src/absy/node.rs index e9cf423c..f553f89f 100644 --- a/zokrates_core/src/absy/node.rs +++ b/zokrates_core/src/absy/node.rs @@ -74,10 +74,13 @@ impl<'ast, T: Field> NodeValue for Expression<'ast, T> {} impl<'ast, T: Field> NodeValue for ExpressionList<'ast, T> {} impl<'ast, T: Field> NodeValue for Assignee<'ast, T> {} impl<'ast, T: Field> NodeValue for Statement<'ast, T> {} -impl<'ast, T: Field> NodeValue for FunctionDeclaration<'ast, T> {} +impl<'ast, T: Field> NodeValue for SymbolDeclaration<'ast, T> {} +impl NodeValue for UnresolvedType {} +impl<'ast> NodeValue for StructType<'ast> {} +impl<'ast> NodeValue for StructField<'ast> {} impl<'ast, T: Field> NodeValue for Function<'ast, T> {} impl<'ast, T: Field> NodeValue for Module<'ast, T> {} -impl<'ast> NodeValue for FunctionImport<'ast> {} +impl<'ast> NodeValue for SymbolImport<'ast> {} impl<'ast> NodeValue for Variable<'ast> {} impl<'ast> NodeValue for Parameter<'ast> {} impl<'ast> NodeValue for Import<'ast> {} diff --git a/zokrates_core/src/absy/types.rs b/zokrates_core/src/absy/types.rs new file mode 100644 index 00000000..1dbbc718 --- /dev/null +++ b/zokrates_core/src/absy/types.rs @@ -0,0 +1,98 @@ +use absy::UnresolvedTypeNode; +use std::fmt; + +pub type Identifier<'ast> = &'ast str; + +pub type MemberId = String; + +pub type UserTypeId = String; + +#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] +pub enum UnresolvedType { + FieldElement, + Boolean, + Array(Box, usize), + User(UserTypeId), +} + +impl fmt::Display for UnresolvedType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + UnresolvedType::FieldElement => write!(f, "field"), + UnresolvedType::Boolean => write!(f, "bool"), + UnresolvedType::Array(ref ty, ref size) => write!(f, "{}[{}]", ty, size), + UnresolvedType::User(i) => write!(f, "{}", i), + } + } +} + +impl UnresolvedType { + pub fn array(ty: UnresolvedTypeNode, size: usize) -> Self { + UnresolvedType::Array(box ty, size) + } +} + +pub type FunctionIdentifier<'ast> = &'ast str; + +pub use self::signature::UnresolvedSignature; + +mod signature { + use std::fmt; + + use absy::UnresolvedTypeNode; + + #[derive(Clone, PartialEq, Serialize, Deserialize)] + pub struct UnresolvedSignature { + pub inputs: Vec, + pub outputs: Vec, + } + + impl fmt::Debug for UnresolvedSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "Signature(inputs: {:?}, outputs: {:?})", + self.inputs, self.outputs + ) + } + } + + impl fmt::Display for UnresolvedSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "(")?; + for (i, t) in self.inputs.iter().enumerate() { + write!(f, "{}", t)?; + if i < self.inputs.len() - 1 { + write!(f, ", ")?; + } + } + write!(f, ") -> (")?; + for (i, t) in self.outputs.iter().enumerate() { + write!(f, "{}", t)?; + if i < self.outputs.len() - 1 { + write!(f, ", ")?; + } + } + write!(f, ")") + } + } + + impl UnresolvedSignature { + pub fn new() -> UnresolvedSignature { + UnresolvedSignature { + inputs: vec![], + outputs: vec![], + } + } + + pub fn inputs(mut self, inputs: Vec) -> Self { + self.inputs = inputs; + self + } + + pub fn outputs(mut self, outputs: Vec) -> Self { + self.outputs = outputs; + self + } + } +} diff --git a/zokrates_core/src/absy/variable.rs b/zokrates_core/src/absy/variable.rs index 8eefd9cb..f03b3f0d 100644 --- a/zokrates_core/src/absy/variable.rs +++ b/zokrates_core/src/absy/variable.rs @@ -1,55 +1,27 @@ -use crate::absy::Node; +use crate::absy::types::UnresolvedType; +use crate::absy::{Node, UnresolvedTypeNode}; use std::fmt; -use types::Type; use crate::absy::Identifier; -#[derive(Clone, PartialEq, Hash, Eq)] +#[derive(Clone, PartialEq)] pub struct Variable<'ast> { pub id: Identifier<'ast>, - pub _type: Type, + pub _type: UnresolvedTypeNode, } pub type VariableNode<'ast> = Node>; impl<'ast> Variable<'ast> { - pub fn new>(id: S, t: Type) -> Variable<'ast> { + pub fn new>(id: S, t: UnresolvedTypeNode) -> Variable<'ast> { Variable { id: id.into(), _type: t, } } - pub fn field_element>(id: S) -> Variable<'ast> { - Variable { - id: id.into(), - _type: Type::FieldElement, - } - } - - pub fn boolean>(id: S) -> Variable<'ast> { - Variable { - id: id.into(), - _type: Type::Boolean, - } - } - - pub fn field_array>(id: S, size: usize) -> Variable<'ast> { - Variable { - id: id.into(), - _type: Type::array(Type::FieldElement, size), - } - } - - pub fn array>(id: S, inner_ty: Type, size: usize) -> Variable<'ast> { - Variable { - id: id.into(), - _type: Type::array(inner_ty, size), - } - } - - pub fn get_type(&self) -> Type { - self._type.clone() + pub fn get_type(&self) -> UnresolvedType { + self._type.value.clone() } } diff --git a/zokrates_core/src/embed.rs b/zokrates_core/src/embed.rs index ef8226ff..6ced72be 100644 --- a/zokrates_core/src/embed.rs +++ b/zokrates_core/src/embed.rs @@ -5,7 +5,7 @@ use flat_absy::{ }; use reduce::Reduce; use std::collections::HashMap; -use types::{FunctionKey, Signature, Type}; +use typed_absy::types::{FunctionKey, Signature, Type}; use zokrates_embed::{generate_sha256_round_constraints, BellmanConstraint}; use zokrates_field::field::Field; @@ -419,9 +419,15 @@ mod tests { let prog = crate::ir::Prog { main: f, private: vec![true; 768], + signature: Signature::new() + .inputs(vec![Type::FieldElement; 768]) + .outputs(vec![Type::FieldElement; 256]), }; - let input = (0..512).map(|_| 0).chain((0..256).map(|_| 1)).collect(); + let input = (0..512) + .map(|_| FieldPrime::from(0)) + .chain((0..256).map(|_| FieldPrime::from(1))) + .collect(); prog.execute(&input).unwrap(); } diff --git a/zokrates_core/src/flat_absy/mod.rs b/zokrates_core/src/flat_absy/mod.rs index 9e5015ef..0762476e 100644 --- a/zokrates_core/src/flat_absy/mod.rs +++ b/zokrates_core/src/flat_absy/mod.rs @@ -12,7 +12,7 @@ pub use self::flat_parameter::FlatParameter; pub use self::flat_variable::FlatVariable; use crate::helpers::DirectiveStatement; -use crate::types::Signature; +use crate::typed_absy::types::Signature; use std::collections::HashMap; use std::fmt; use zokrates_field::field::Field; diff --git a/zokrates_core/src/flatten/mod.rs b/zokrates_core/src/flatten/mod.rs index 720bef0e..d42e5a9b 100644 --- a/zokrates_core/src/flatten/mod.rs +++ b/zokrates_core/src/flatten/mod.rs @@ -7,12 +7,10 @@ use crate::flat_absy::*; use crate::helpers::{DirectiveStatement, Helper, RustHelper}; +use crate::typed_absy::types::{FunctionIdentifier, FunctionKey, MemberId, Signature, Type}; use crate::typed_absy::*; -use crate::types::Type; -use crate::types::{FunctionKey, Signature}; use std::collections::HashMap; use std::convert::TryFrom; -use types::FunctionIdentifier; use zokrates_field::field::Field; /// Flattener, computes flattened program. @@ -29,7 +27,7 @@ pub struct Flattener<'ast, T: Field> { // We introduce a trait in order to make it possible to make flattening `e` generic over the type of `e` trait Flatten<'ast, T: Field>: - TryFrom, Error = ()> + IfElse<'ast, T> + Select<'ast, T> + TryFrom, Error = ()> + IfElse<'ast, T> + Select<'ast, T> + Member<'ast, T> { fn flatten( self, @@ -61,6 +59,17 @@ impl<'ast, T: Field> Flatten<'ast, T> for BooleanExpression<'ast, T> { } } +impl<'ast, T: Field> Flatten<'ast, T> for StructExpression<'ast, T> { + fn flatten( + self, + flattener: &mut Flattener<'ast, T>, + symbols: &TypedFunctionSymbols<'ast, T>, + statements_flattened: &mut Vec>, + ) -> Vec> { + flattener.flatten_struct_expression(symbols, statements_flattened, self) + } +} + impl<'ast, T: Field> Flatten<'ast, T> for ArrayExpression<'ast, T> { fn flatten( self, @@ -85,6 +94,11 @@ impl<'ast, T: Field> Flatten<'ast, T> for ArrayExpression<'ast, T> { statements_flattened, self, ), + Type::Struct(..) => flattener.flatten_array_expression::>( + symbols, + statements_flattened, + self, + ), } } } @@ -194,6 +208,163 @@ impl<'ast, T: Field> Flattener<'ast, T> { res.into_iter().map(|r| r.into()).collect() } + fn flatten_member_expression( + &mut self, + symbols: &TypedFunctionSymbols<'ast, T>, + statements_flattened: &mut Vec>, + s: StructExpression<'ast, T>, + member_id: MemberId, + ) -> Vec> { + let members = s.ty().clone(); + let expected_output_size = members + .iter() + .find(|(id, _)| *id == member_id) + .unwrap() + .1 + .get_primitive_count(); + + let res = + match s.into_inner() { + StructExpressionInner::Value(values) => { + // If the struct has an explicit value, we get the value at the given member + assert_eq!(values.len(), members.len()); + values + .into_iter() + .zip(members.into_iter()) + .filter(|(_, (id, _))| *id == member_id) + .flat_map(|(v, (_, t))| match t { + Type::FieldElement => FieldElementExpression::try_from(v) + .unwrap() + .flatten(self, symbols, statements_flattened), + Type::Boolean => BooleanExpression::try_from(v).unwrap().flatten( + self, + symbols, + statements_flattened, + ), + Type::Array(..) => ArrayExpression::try_from(v).unwrap().flatten( + self, + symbols, + statements_flattened, + ), + Type::Struct(..) => StructExpression::try_from(v).unwrap().flatten( + self, + symbols, + statements_flattened, + ), + }) + .collect() + } + StructExpressionInner::Identifier(id) => { + // If the struct is an identifier, we allocated variables in the layout for that identifier. We need to access a subset of these values. + // the struct is encoded as a sequence, so we need to identify the offset at which this member starts + let offset = members + .iter() + .take_while(|(id, _)| *id != member_id) + .map(|(_, ty)| ty.get_primitive_count()) + .sum(); + + // we also need the size of this member + let size = members + .iter() + .find(|(id, _)| *id == member_id) + .unwrap() + .1 + .get_primitive_count(); + self.layout.get(&id).unwrap()[offset..(offset + size)] + .into_iter() + .map(|i| i.clone().into()) + .collect() + } + StructExpressionInner::Select(box array, box index) => { + let offset = members + .iter() + .take_while(|(id, _)| *id != member_id) + .map(|(_, ty)| ty.get_primitive_count()) + .sum(); + + // we also need the size of this member + let size = members + .iter() + .find(|(id, _)| *id == member_id) + .unwrap() + .1 + .get_primitive_count(); + + self.flatten_select_expression::>( + symbols, + statements_flattened, + array, + index, + )[offset..offset + size] + .to_vec() + } + StructExpressionInner::FunctionCall(..) => unreachable!(), + StructExpressionInner::IfElse(box condition, box consequence, box alternative) => { + // if the struct is `(if c then a else b)`, we want to access `(if c then a else b).member` + // we reduce to `if c then a.member else b.member` + let ty = members + .clone() + .into_iter() + .find(|(id, _)| *id == member_id) + .unwrap() + .1; + + match ty { + Type::FieldElement => self.flatten_if_else_expression( + symbols, + statements_flattened, + condition.clone(), + FieldElementExpression::member(consequence.clone(), member_id.clone()), + FieldElementExpression::member(alternative.clone(), member_id), + ), + Type::Boolean => self.flatten_if_else_expression( + symbols, + statements_flattened, + condition.clone(), + BooleanExpression::member(consequence.clone(), member_id.clone()), + BooleanExpression::member(alternative.clone(), member_id), + ), + Type::Struct(..) => self.flatten_if_else_expression( + symbols, + statements_flattened, + condition.clone(), + StructExpression::member(consequence.clone(), member_id.clone()), + StructExpression::member(alternative.clone(), member_id), + ), + Type::Array(..) => self.flatten_if_else_expression( + symbols, + statements_flattened, + condition.clone(), + ArrayExpression::member(consequence.clone(), member_id.clone()), + ArrayExpression::member(alternative.clone(), member_id), + ), + } + } + StructExpressionInner::Member(box s0, m_id) => { + let e = self.flatten_member_expression(symbols, statements_flattened, s0, m_id); + + let offset = members + .iter() + .take_while(|(id, _)| *id != member_id) + .map(|(_, ty)| ty.get_primitive_count()) + .sum(); + + // we also need the size of this member + let size = members + .iter() + .find(|(id, _)| *id == member_id) + .unwrap() + .1 + .get_primitive_count(); + + e[offset..(offset + size)].into() + } + }; + + assert_eq!(res.len(), expected_output_size); + res + } + /// Flatten an array selection expression /// /// # Arguments @@ -244,6 +415,13 @@ impl<'ast, T: Field> Flattener<'ast, T> { ) .flatten(self, symbols, statements_flattened) } + ArrayExpressionInner::Member(box s, id) => { + assert!(n < T::from(size)); + let n = n.to_dec_string().parse::().unwrap(); + self.flatten_member_expression(symbols, statements_flattened, s, id) + [n * ty.get_primitive_count()..(n + 1) * ty.get_primitive_count()] + .to_vec() + } ArrayExpressionInner::Select(box array, box index) => { assert!(n < T::from(size)); let n = n.to_dec_string().parse::().unwrap(); @@ -270,6 +448,13 @@ impl<'ast, T: Field> Flattener<'ast, T> { array, index, ), + Type::Struct(..) => self + .flatten_select_expression::>( + symbols, + statements_flattened, + array, + index, + ), }; e[n * element_size..(n + 1) * element_size] @@ -333,6 +518,7 @@ impl<'ast, T: Field> Flattener<'ast, T> { ), ) } + ArrayExpressionInner::Member(box s, id) => U::member(s, id), ArrayExpressionInner::Select(box array, box index) => U::select( ArrayExpressionInner::Select(box array, box index) .annotate(ty.clone(), size), @@ -671,6 +857,9 @@ impl<'ast, T: Field> Flattener<'ast, T> { alternative, )[0] .clone(), + BooleanExpression::Member(box s, id) => { + self.flatten_member_expression(symbols, statements_flattened, s, id)[0].clone() + } BooleanExpression::Select(box array, box index) => self .flatten_select_expression::>( symbols, @@ -826,7 +1015,15 @@ impl<'ast, T: Field> Flattener<'ast, T> { statements_flattened, e, ), + Type::Struct(..) => self.flatten_array_expression::>( + symbols, + statements_flattened, + e, + ), }, + TypedExpression::Struct(e) => { + self.flatten_struct_expression(symbols, statements_flattened, e) + } } } @@ -1059,6 +1256,9 @@ impl<'ast, T: Field> Flattener<'ast, T> { assert!(exprs_flattened.expressions.len() == 1); // outside of MultipleDefinition, FunctionCalls must return a single value exprs_flattened.expressions[0].clone() } + FieldElementExpression::Member(box s, id) => { + self.flatten_member_expression(symbols, statements_flattened, s, id)[0].clone() + } FieldElementExpression::Select(box array, box index) => self .flatten_select_expression::>( symbols, @@ -1070,6 +1270,92 @@ impl<'ast, T: Field> Flattener<'ast, T> { } } + /// Flattens an array expression + /// + /// # Arguments + /// + /// * `symbols` - Available functions in in this context + /// * `statements_flattened` - Vector where new flattened statements can be added. + /// * `expr` - `StructExpression` that will be flattened. + fn flatten_struct_expression( + &mut self, + symbols: &TypedFunctionSymbols<'ast, T>, + statements_flattened: &mut Vec>, + expr: StructExpression<'ast, T>, + ) -> Vec> { + let ty = expr.get_type(); + let expected_output_size = expr.get_type().get_primitive_count(); + let members = expr.ty().clone(); + + let res = match expr.into_inner() { + StructExpressionInner::Identifier(x) => self + .layout + .get(&x) + .unwrap() + .iter() + .map(|v| FlatExpression::Identifier(v.clone())) + .collect(), + StructExpressionInner::Value(values) => values + .into_iter() + .flat_map(|v| self.flatten_expression(symbols, statements_flattened, v)) + .collect(), + StructExpressionInner::FunctionCall(key, param_expressions) => { + let exprs_flattened = self.flatten_function_call( + symbols, + statements_flattened, + key.id, + vec![ty], + param_expressions, + ); + exprs_flattened.expressions + } + StructExpressionInner::IfElse(box condition, box consequence, box alternative) => { + members + .into_iter() + .flat_map(|(id, ty)| match ty { + Type::FieldElement => FieldElementExpression::if_else( + condition.clone(), + FieldElementExpression::member(consequence.clone(), id.clone()), + FieldElementExpression::member(alternative.clone(), id.clone()), + ) + .flatten(self, symbols, statements_flattened), + Type::Boolean => BooleanExpression::if_else( + condition.clone(), + BooleanExpression::member(consequence.clone(), id.clone()), + BooleanExpression::member(alternative.clone(), id.clone()), + ) + .flatten(self, symbols, statements_flattened), + Type::Struct(..) => StructExpression::if_else( + condition.clone(), + StructExpression::member(consequence.clone(), id.clone()), + StructExpression::member(alternative.clone(), id.clone()), + ) + .flatten(self, symbols, statements_flattened), + Type::Array(..) => ArrayExpression::if_else( + condition.clone(), + ArrayExpression::member(consequence.clone(), id.clone()), + ArrayExpression::member(alternative.clone(), id.clone()), + ) + .flatten(self, symbols, statements_flattened), + }) + .collect() + } + StructExpressionInner::Member(box s, id) => { + self.flatten_member_expression(symbols, statements_flattened, s, id) + } + StructExpressionInner::Select(box array, box index) => self + .flatten_select_expression::>( + symbols, + statements_flattened, + array, + index, + ), + }; + + assert_eq!(res.len(), expected_output_size); + res + } + /// Flattens an array expression /// /// # Arguments @@ -1136,6 +1422,9 @@ impl<'ast, T: Field> Flattener<'ast, T> { .flatten(self, symbols, statements_flattened) }) .collect(), + ArrayExpressionInner::Member(box s, id) => { + self.flatten_member_expression(symbols, statements_flattened, s, id) + } ArrayExpressionInner::Select(box array, box index) => self .flatten_select_expression::>( symbols, @@ -1194,6 +1483,9 @@ impl<'ast, T: Field> Flattener<'ast, T> { TypedAssignee::Select(..) => unreachable!( "array element redefs should have been replaced by array redefs in unroll" ), + TypedAssignee::Member(..) => unreachable!( + "struct member redefs should have been replaced by struct redef in unroll" + ), } } TypedStatement::Condition(lhs, rhs) => { @@ -1280,7 +1572,7 @@ impl<'ast, T: Field> Flattener<'ast, T> { let arguments_flattened = funct .arguments .into_iter() - .flat_map(|p| self.use_parameter(&p, &mut statements_flattened)) + .flat_map(|p| self.use_parameter(&p)) .collect(); // flatten statements in functions and apply substitution @@ -1328,29 +1620,14 @@ impl<'ast, T: Field> Flattener<'ast, T> { /// /// * `name` - a String that holds the name of the variable fn use_variable(&mut self, variable: &Variable<'ast>) -> Vec { - let vars = match variable.get_type() { - Type::FieldElement => self.issue_new_variables(1), - Type::Boolean => self.issue_new_variables(1), - Type::Array(ty, size) => self.issue_new_variables(ty.get_primitive_count() * size), - }; + let vars = self.issue_new_variables(variable.get_type().get_primitive_count()); self.layout.insert(variable.id.clone(), vars.clone()); vars } - fn use_parameter( - &mut self, - parameter: &Parameter<'ast>, - statements: &mut Vec>, - ) -> Vec { + fn use_parameter(&mut self, parameter: &Parameter<'ast>) -> Vec { let variables = self.use_variable(¶meter.id); - match parameter.id.get_type() { - Type::Boolean => statements.extend(Self::boolean_constraint(&variables)), - Type::Array(box Type::Boolean, _) => { - statements.extend(Self::boolean_constraint(&variables)) - } - _ => {} - }; variables .into_iter() @@ -1371,21 +1648,6 @@ impl<'ast, T: Field> Flattener<'ast, T> { (0..count).map(|_| self.issue_new_variable()).collect() } - fn boolean_constraint(variables: &Vec) -> Vec> { - variables - .iter() - .map(|v| { - FlatStatement::Condition( - FlatExpression::Identifier(*v), - FlatExpression::Mult( - box FlatExpression::Identifier(*v), - box FlatExpression::Identifier(*v), - ), - ) - }) - .collect() - } - // create an internal variable. We do not register it in the layout fn use_sym(&mut self) -> FlatVariable { self.issue_new_variable() @@ -1410,62 +1672,10 @@ impl<'ast, T: Field> Flattener<'ast, T> { #[cfg(test)] mod tests { use super::*; - use crate::types::Signature; - use crate::types::Type; + use crate::typed_absy::types::Signature; + use crate::typed_absy::types::Type; use zokrates_field::field::FieldPrime; - mod boolean_checks { - use super::*; - - #[test] - fn boolean_arg() { - // def main(bool a): - // return a - // - // -> should flatten to - // - // def main(_0) -> (1): - // _0 * _0 == _0 - // return _0 - - let function: TypedFunction = TypedFunction { - arguments: vec![Parameter::private(Variable::boolean("a".into()))], - statements: vec![TypedStatement::Return(vec![BooleanExpression::Identifier( - "a".into(), - ) - .into()])], - signature: Signature::new() - .inputs(vec![Type::Boolean]) - .outputs(vec![Type::Boolean]), - }; - - let expected = FlatFunction { - arguments: vec![FlatParameter::private(FlatVariable::new(0))], - statements: vec![ - FlatStatement::Condition( - FlatExpression::Identifier(FlatVariable::new(0)), - FlatExpression::Mult( - box FlatExpression::Identifier(FlatVariable::new(0)), - box FlatExpression::Identifier(FlatVariable::new(0)), - ), - ), - FlatStatement::Return(FlatExpressionList { - expressions: vec![FlatExpression::Identifier(FlatVariable::new(0))], - }), - ], - signature: Signature::new() - .inputs(vec![Type::Boolean]) - .outputs(vec![Type::Boolean]), - }; - - let mut flattener = Flattener::new(); - - let flat_function = flattener.flatten_function(&mut HashMap::new(), function); - - assert_eq!(flat_function, expected); - } - } - #[test] fn powers_zero() { // def main(): diff --git a/zokrates_core/src/imports.rs b/zokrates_core/src/imports.rs index f9818eca..589db42d 100644 --- a/zokrates_core/src/imports.rs +++ b/zokrates_core/src/imports.rs @@ -57,15 +57,17 @@ impl From for Error { #[derive(PartialEq, Clone)] pub struct Import<'ast> { source: Identifier<'ast>, + symbol: Option>, alias: Option>, } pub type ImportNode<'ast> = Node>; impl<'ast> Import<'ast> { - pub fn new(source: Identifier<'ast>) -> Import<'ast> { + pub fn new(symbol: Option>, source: Identifier<'ast>) -> Import<'ast> { Import { - source: source, + symbol, + source, alias: None, } } @@ -74,9 +76,14 @@ impl<'ast> Import<'ast> { &self.alias } - pub fn new_with_alias(source: Identifier<'ast>, alias: Identifier<'ast>) -> Import<'ast> { + pub fn new_with_alias( + symbol: Option>, + source: Identifier<'ast>, + alias: Identifier<'ast>, + ) -> Import<'ast> { Import { - source: source, + symbol, + source, alias: Some(alias), } } @@ -124,7 +131,7 @@ impl Importer { modules: &mut HashMap>, arena: &'ast Arena, ) -> Result, CompileErrors> { - let mut functions: Vec<_> = vec![]; + let mut symbols: Vec<_> = vec![]; for import in destination.imports { let pos = import.pos(); @@ -136,10 +143,10 @@ impl Importer { "EMBED/sha256round" => { let alias = alias.unwrap_or("sha256round"); - functions.push( - FunctionDeclaration { + symbols.push( + SymbolDeclaration { id: &alias, - symbol: FunctionSymbol::Flat(FlatEmbed::Sha256Round), + symbol: Symbol::Flat(FlatEmbed::Sha256Round), } .start_end(pos.0, pos.1), ); @@ -147,10 +154,10 @@ impl Importer { "EMBED/unpack" => { let alias = alias.unwrap_or("unpack"); - functions.push( - FunctionDeclaration { + symbols.push( + SymbolDeclaration { id: &alias, - symbol: FunctionSymbol::Flat(FlatEmbed::Unpack), + symbol: Symbol::Flat(FlatEmbed::Unpack), } .start_end(pos.0, pos.1), ); @@ -185,12 +192,12 @@ impl Importer { modules.insert(import.source.to_string(), compiled); - functions.push( - FunctionDeclaration { + symbols.push( + SymbolDeclaration { id: &alias, - symbol: FunctionSymbol::There( - FunctionImport::with_id_in_module( - "main", + symbol: Symbol::There( + SymbolImport::with_id_in_module( + import.symbol.unwrap_or("main"), import.source.clone(), ) .start_end(pos.0, pos.1), @@ -218,11 +225,12 @@ impl Importer { } } - functions.extend(destination.functions); + symbols.extend(destination.symbols); Ok(Module { imports: vec![], - functions: functions, + symbols, + ..destination }) } } @@ -235,8 +243,9 @@ mod tests { #[test] fn create_with_no_alias() { assert_eq!( - Import::new("./foo/bar/baz.zok"), + Import::new(None, "./foo/bar/baz.zok"), Import { + symbol: None, source: "./foo/bar/baz.zok", alias: None, } @@ -246,8 +255,9 @@ mod tests { #[test] fn create_with_alias() { assert_eq!( - Import::new_with_alias("./foo/bar/baz.zok", &"myalias"), + Import::new_with_alias(None, "./foo/bar/baz.zok", &"myalias"), Import { + symbol: None, source: "./foo/bar/baz.zok", alias: Some("myalias"), } diff --git a/zokrates_core/src/ir/folder.rs b/zokrates_core/src/ir/folder.rs index fea45ab3..38cfb620 100644 --- a/zokrates_core/src/ir/folder.rs +++ b/zokrates_core/src/ir/folder.rs @@ -41,7 +41,7 @@ pub trait Folder: Sized { pub fn fold_module>(f: &mut F, p: Prog) -> Prog { Prog { main: f.fold_function(p.main), - private: p.private, + ..p } } diff --git a/zokrates_core/src/ir/from_flat.rs b/zokrates_core/src/ir/from_flat.rs index b4865b7a..13268a02 100644 --- a/zokrates_core/src/ir/from_flat.rs +++ b/zokrates_core/src/ir/from_flat.rs @@ -66,12 +66,19 @@ impl From> for Prog { // get the main function let main = flat_prog.main; + // get the signature to keep high level information in the low level representation + let signature = main.signature.clone(); + // get the interface of the program, ie which inputs are private and public let private = main.arguments.iter().map(|p| p.private).collect(); let main = main.into(); - Prog { private, main } + Prog { + private, + main, + signature, + } } } diff --git a/zokrates_core/src/ir/interpreter.rs b/zokrates_core/src/ir/interpreter.rs index 2546ec0e..65495aea 100644 --- a/zokrates_core/src/ir/interpreter.rs +++ b/zokrates_core/src/ir/interpreter.rs @@ -8,7 +8,7 @@ use zokrates_field::field::Field; pub type ExecutionResult = Result, Error>; impl Prog { - pub fn execute + Clone>(&self, inputs: &Vec) -> ExecutionResult { + pub fn execute(&self, inputs: &Vec) -> ExecutionResult { let main = &self.main; self.check_inputs(&inputs)?; let mut witness = BTreeMap::new(); diff --git a/zokrates_core/src/ir/mod.rs b/zokrates_core/src/ir/mod.rs index ed78e6de..c483c646 100644 --- a/zokrates_core/src/ir/mod.rs +++ b/zokrates_core/src/ir/mod.rs @@ -2,6 +2,7 @@ use crate::flat_absy::flat_parameter::FlatParameter; use crate::flat_absy::FlatVariable; use crate::helpers::Helper; use std::fmt; +use typed_absy::types::signature::Signature; use zokrates_field::field::Field; mod expression; @@ -104,6 +105,7 @@ impl fmt::Display for Function { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct Prog { + pub signature: Signature, pub main: Function, pub private: Vec, } @@ -120,12 +122,8 @@ impl Prog { .count() } - pub fn public_arguments_count(&self) -> usize { - self.private.iter().filter(|b| !**b).count() - } - - pub fn private_arguments_count(&self) -> usize { - self.private.iter().filter(|b| **b).count() + pub fn arguments_count(&self) -> usize { + self.private.len() } pub fn parameters(&self) -> Vec { diff --git a/zokrates_core/src/lib.rs b/zokrates_core/src/lib.rs index 3b2894f2..a387e875 100644 --- a/zokrates_core/src/lib.rs +++ b/zokrates_core/src/lib.rs @@ -34,11 +34,10 @@ mod optimizer; mod parser; mod semantics; mod static_analysis; -mod typed_absy; -mod types; pub mod absy; pub mod compile; pub mod flat_absy; pub mod ir; pub mod proof_system; +pub mod typed_absy; diff --git a/zokrates_core/src/optimizer/duplicate.rs b/zokrates_core/src/optimizer/duplicate.rs index 0b7e9a26..c8203a30 100644 --- a/zokrates_core/src/optimizer/duplicate.rs +++ b/zokrates_core/src/optimizer/duplicate.rs @@ -49,6 +49,7 @@ impl Folder for DuplicateOptimizer { mod tests { use super::*; use flat_absy::FlatVariable; + use typed_absy::types::Signature; use zokrates_field::field::FieldPrime; #[test] @@ -78,6 +79,7 @@ mod tests { returns: vec![], arguments: vec![], }, + signature: Signature::new(), }; let expected = p.clone(); @@ -117,6 +119,7 @@ mod tests { returns: vec![], arguments: vec![], }, + signature: Signature::new(), }; let expected = Prog { @@ -136,6 +139,7 @@ mod tests { returns: vec![], arguments: vec![], }, + signature: Signature::new(), }; assert_eq!(DuplicateOptimizer::optimize(p), expected); diff --git a/zokrates_core/src/proof_system/bn128/g16.rs b/zokrates_core/src/proof_system/bn128/g16.rs index a5f26020..ab692636 100644 --- a/zokrates_core/src/proof_system/bn128/g16.rs +++ b/zokrates_core/src/proof_system/bn128/g16.rs @@ -338,6 +338,7 @@ mod tests { use crate::flat_absy::FlatVariable; use crate::ir::*; use crate::proof_system::bn128::g16::serialize::serialize_proof; + use typed_absy::types::{Signature, Type}; #[allow(dead_code)] #[derive(Deserialize)] @@ -367,11 +368,14 @@ mod tests { )], }, private: vec![false], + signature: Signature::new() + .inputs(vec![Type::FieldElement]) + .outputs(vec![Type::FieldElement]), }; let witness = program .clone() - .execute::(&vec![FieldPrime::from(42)]) + .execute(&vec![FieldPrime::from(42)]) .unwrap(); let computation = Computation::with_witness(program, witness); diff --git a/zokrates_core/src/proof_system/bn128/utils/bellman.rs b/zokrates_core/src/proof_system/bn128/utils/bellman.rs index c9fec557..4d06afae 100644 --- a/zokrates_core/src/proof_system/bn128/utils/bellman.rs +++ b/zokrates_core/src/proof_system/bn128/utils/bellman.rs @@ -294,6 +294,7 @@ mod parse { mod tests { use super::*; use crate::ir::{Function, LinComb}; + use typed_absy::types::{Signature, Type}; use zokrates_field::field::FieldPrime; mod prove { @@ -309,9 +310,10 @@ mod tests { statements: vec![], }, private: vec![], + signature: Signature::new(), }; - let witness = program.clone().execute::(&vec![]).unwrap(); + let witness = program.clone().execute(&vec![]).unwrap(); let computation = Computation::with_witness(program, witness); let params = computation.clone().setup(); @@ -331,12 +333,12 @@ mod tests { )], }, private: vec![true], + signature: Signature::new() + .inputs(vec![Type::FieldElement]) + .outputs(vec![Type::FieldElement]), }; - let witness = program - .clone() - .execute::(&vec![FieldPrime::from(0)]) - .unwrap(); + let witness = program.clone().execute(&vec![FieldPrime::from(0)]).unwrap(); let computation = Computation::with_witness(program, witness); let params = computation.clone().setup(); @@ -356,12 +358,12 @@ mod tests { )], }, private: vec![false], + signature: Signature::new() + .inputs(vec![Type::FieldElement]) + .outputs(vec![Type::FieldElement]), }; - let witness = program - .clone() - .execute::(&vec![FieldPrime::from(0)]) - .unwrap(); + let witness = program.clone().execute(&vec![FieldPrime::from(0)]).unwrap(); let computation = Computation::with_witness(program, witness); let params = computation.clone().setup(); @@ -381,9 +383,10 @@ mod tests { )], }, private: vec![], + signature: Signature::new().outputs(vec![Type::FieldElement]), }; - let witness = program.clone().execute::(&vec![]).unwrap(); + let witness = program.clone().execute(&vec![]).unwrap(); let computation = Computation::with_witness(program, witness); let params = computation.clone().setup(); @@ -415,11 +418,14 @@ mod tests { ], }, private: vec![true, false], + signature: Signature::new() + .inputs(vec![Type::FieldElement, Type::FieldElement]) + .outputs(vec![Type::FieldElement, Type::FieldElement]), }; let witness = program .clone() - .execute::(&vec![FieldPrime::from(3), FieldPrime::from(4)]) + .execute(&vec![FieldPrime::from(3), FieldPrime::from(4)]) .unwrap(); let computation = Computation::with_witness(program, witness); @@ -440,12 +446,12 @@ mod tests { )], }, private: vec![false], + signature: Signature::new() + .inputs(vec![Type::FieldElement]) + .outputs(vec![Type::FieldElement]), }; - let witness = program - .clone() - .execute::(&vec![FieldPrime::from(3)]) - .unwrap(); + let witness = program.clone().execute(&vec![FieldPrime::from(3)]).unwrap(); let computation = Computation::with_witness(program, witness); let params = computation.clone().setup(); @@ -467,11 +473,14 @@ mod tests { )], }, private: vec![true, false], + signature: Signature::new() + .inputs(vec![Type::FieldElement, Type::FieldElement]) + .outputs(vec![Type::FieldElement]), }; let witness = program .clone() - .execute::(&vec![FieldPrime::from(3), FieldPrime::from(4)]) + .execute(&vec![FieldPrime::from(3), FieldPrime::from(4)]) .unwrap(); let computation = Computation::with_witness(program, witness); diff --git a/zokrates_core/src/semantics.rs b/zokrates_core/src/semantics.rs index 473b5464..58e02eac 100644 --- a/zokrates_core/src/semantics.rs +++ b/zokrates_core/src/semantics.rs @@ -4,17 +4,18 @@ //! @author Thibaut Schaeffer //! @date 2017 -use crate::absy::variable::Variable; use crate::absy::Identifier; use crate::absy::*; use crate::typed_absy::*; -use std::collections::{HashMap, HashSet}; +use crate::typed_absy::{Parameter, Variable}; +use std::collections::{hash_map::Entry, BTreeSet, HashMap, HashSet}; use std::fmt; use zokrates_field::field::Field; use crate::parser::Position; -use crate::types::{FunctionKey, Type}; +use crate::absy::types::{UnresolvedSignature, UnresolvedType, UserTypeId}; +use crate::typed_absy::types::{FunctionKey, Signature, Type}; use std::hash::{Hash, Hasher}; @@ -24,6 +25,77 @@ pub struct Error { message: String, } +type TypeMap = HashMap>; + +/// The global state of the program during semantic checks +#[derive(Debug)] +struct State<'ast, T: Field> { + /// The modules yet to be checked, which we consume as we explore the dependency tree + modules: Modules<'ast, T>, + /// The already checked modules, which we're returning at the end + typed_modules: TypedModules<'ast, T>, + /// The user-defined types, which we keep track at this phase only. In later phases, we rely only on basic types and combinations thereof + types: TypeMap, +} + +/// A symbol for a given name: either a type, or a group of functions. Not both! +#[derive(PartialEq, Hash, Eq, Debug)] +enum SymbolType { + Type, + Functions(BTreeSet), +} + +/// A data structure to keep track of all symbols in a module +#[derive(Default)] +struct SymbolUnifier { + symbols: HashMap, +} + +impl SymbolUnifier { + fn insert_type>(&mut self, id: S) -> bool { + let s_type = self.symbols.entry(id.into()); + match s_type { + // if anything is already called `id`, we cannot introduce this type + Entry::Occupied(..) => false, + // otherwise, we can! + Entry::Vacant(v) => { + v.insert(SymbolType::Type); + true + } + } + } + + fn insert_function>(&mut self, id: S, signature: Signature) -> bool { + let s_type = self.symbols.entry(id.into()); + match s_type { + // if anything is already called `id`, it depends what it is + Entry::Occupied(mut o) => { + match o.get_mut() { + // if it's a Type, then we can't introduce a function + SymbolType::Type => false, + // if it's a Function, we can introduce a new function only if it has a different signature + SymbolType::Functions(signatures) => signatures.insert(signature), + } + } + // otherwise, we can! + Entry::Vacant(v) => { + v.insert(SymbolType::Functions(vec![signature].into_iter().collect())); + true + } + } + } +} + +impl<'ast, T: Field> State<'ast, T> { + fn new(modules: Modules<'ast, T>) -> Self { + State { + modules, + typed_modules: HashMap::new(), + types: HashMap::new(), + } + } +} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let location = self @@ -150,20 +222,12 @@ impl<'ast> Checker<'ast> { &mut self, program: Program<'ast, T>, ) -> Result, Vec> { - // start with full `modules` - let mut modules = program.modules; - // and empty `typed_modules` - let mut typed_modules = HashMap::new(); + let mut state = State::new(program.modules); let mut errors = vec![]; - match Checker::check_single_main(modules.get(&program.main).unwrap()) { - Ok(_) => {} - Err(e) => errors.push(e), - }; - // recursively type-check modules starting with `main` - match self.check_module(&program.main, &mut modules, &mut typed_modules) { + match self.check_module(&program.main, &mut state) { Ok(()) => {} Err(e) => errors.extend(e), }; @@ -172,85 +236,275 @@ impl<'ast> Checker<'ast> { return Err(errors); } + Checker::check_single_main(state.typed_modules.get(&program.main).unwrap()) + .map_err(|e| vec![e])?; + Ok(TypedProgram { main: program.main, - modules: typed_modules, + modules: state.typed_modules, }) } + fn check_struct_type_declaration( + &mut self, + s: StructTypeNode<'ast>, + module_id: &ModuleId, + types: &TypeMap, + ) -> Result> { + let pos = s.pos(); + let s = s.value; + + let mut errors = vec![]; + let mut fields: Vec<(_, _)> = vec![]; + let mut fields_set = HashSet::new(); + + for field in s.fields { + let member_id = field.value.id.to_string(); + match self + .check_type(field.value.ty, module_id, &types) + .map(|t| (member_id, t)) + { + Ok(f) => match fields_set.insert(f.0.clone()) { + true => fields.push(f), + false => errors.push(Error { + pos: Some(pos), + message: format!("Duplicate key {} in struct definition", f.0,), + }), + }, + Err(e) => { + errors.push(e); + } + } + } + + if errors.len() > 0 { + return Err(errors); + } + + Ok(Type::Struct(fields)) + } + + fn check_symbol_declaration( + &mut self, + declaration: SymbolDeclarationNode<'ast, T>, + module_id: &ModuleId, + state: &mut State<'ast, T>, + functions: &mut HashMap, TypedFunctionSymbol<'ast, T>>, + symbol_unifier: &mut SymbolUnifier, + ) -> Result<(), Vec> { + let mut errors = vec![]; + + let pos = declaration.pos(); + let declaration = declaration.value; + + match declaration.symbol { + Symbol::HereType(t) => { + match self.check_struct_type_declaration(t.clone(), module_id, &state.types) { + Ok(ty) => { + match symbol_unifier.insert_type(declaration.id) { + false => errors.push(Error { + pos: Some(pos), + message: format!( + "{} conflicts with another symbol", + declaration.id, + ), + }), + true => {} + }; + state + .types + .entry(module_id.clone()) + .or_default() + .insert(declaration.id.to_string(), ty); + } + Err(e) => errors.extend(e), + } + } + Symbol::HereFunction(f) => match self.check_function(f, module_id, &state.types) { + Ok(funct) => { + match symbol_unifier.insert_function(declaration.id, funct.signature.clone()) { + false => errors.push(Error { + pos: Some(pos), + message: format!("{} conflicts with another symbol", declaration.id,), + }), + true => {} + }; + + self.functions.insert( + FunctionKey::with_id(declaration.id.clone()) + .signature(funct.signature.clone()), + ); + functions.insert( + FunctionKey::with_id(declaration.id.clone()) + .signature(funct.signature.clone()), + TypedFunctionSymbol::Here(funct), + ); + } + Err(e) => { + errors.extend(e); + } + }, + Symbol::There(import) => { + let pos = import.pos(); + let import = import.value; + + match Checker::new().check_module(&import.module_id, state) { + Ok(()) => { + // find candidates in the checked module + let function_candidates: Vec<_> = state + .typed_modules + .get(&import.module_id) + .unwrap() + .functions + .iter() + .filter(|(k, _)| k.id == import.symbol_id) + .map(|(_, v)| FunctionKey { + id: import.symbol_id.clone(), + signature: v.signature(&state.typed_modules).clone(), + }) + .collect(); + + // find candidates in the types + let type_candidate = state + .types + .entry(import.module_id.clone()) + .or_default() + .get(import.symbol_id) + .cloned(); + + match (function_candidates.len(), type_candidate) { + (0, Some(t)) => { + // we imported a type, so the symbol it gets bound to should not already exist + match symbol_unifier.insert_type(declaration.id) { + false => { + errors.push(Error { + pos: Some(pos), + message: format!( + "{} conflicts with another symbol", + declaration.id, + ), + }); + } + true => {} + }; + state + .types + .entry(module_id.clone()) + .or_default() + .insert(import.symbol_id.to_string(), t.clone()); + } + (0, None) => { + errors.push(Error { + pos: Some(pos), + message: format!( + "Could not find symbol {} in module {}", + import.symbol_id, import.module_id, + ), + }); + } + (_, Some(_)) => unreachable!("collision in module we're importing from should have been caught when checking it"), + _ => { + for candidate in function_candidates { + + match symbol_unifier.insert_function(declaration.id, candidate.signature.clone()) { + false => { + errors.push(Error { + pos: Some(pos), + message: format!( + "{} conflicts with another symbol", + declaration.id, + ), + }); + }, + true => {} + }; + + self.functions.insert(candidate.clone().id(declaration.id)); + functions.insert( + candidate.clone().id(declaration.id), + TypedFunctionSymbol::There( + candidate, + import.module_id.clone(), + ), + ); + } + } + }; + } + Err(e) => { + errors.extend(e); + } + }; + } + Symbol::Flat(funct) => { + match symbol_unifier.insert_function(declaration.id, funct.signature::()) { + false => { + errors.push(Error { + pos: Some(pos), + message: format!("{} conflicts with another symbol", declaration.id,), + }); + } + true => {} + }; + + self.functions.insert( + FunctionKey::with_id(declaration.id.clone()) + .signature(funct.signature::().clone()), + ); + functions.insert( + FunctionKey::with_id(declaration.id.clone()) + .signature(funct.signature::().clone()), + TypedFunctionSymbol::Flat(funct), + ); + } + }; + + // return if any errors occured + if errors.len() > 0 { + return Err(errors); + } + + Ok(()) + } + fn check_module( &mut self, module_id: &ModuleId, - modules: &mut Modules<'ast, T>, - typed_modules: &mut TypedModules<'ast, T>, + state: &mut State<'ast, T>, ) -> Result<(), Vec> { let mut errors = vec![]; let mut checked_functions = HashMap::new(); // check if the module was already removed from the untyped ones - let to_insert = match modules.remove(module_id) { + let to_insert = match state.modules.remove(module_id) { // if it was, do nothing None => None, // if it was not, check it Some(module) => { assert_eq!(module.imports.len(), 0); - for declaration in module.functions { - self.enter_scope(); - let pos = declaration.pos(); - let declaration = declaration.value; + // we need to create an entry in the types map to store types for this module + state.types.entry(module_id.clone()).or_default(); - match self.check_function_symbol(declaration.symbol, modules, typed_modules) { - Ok(checked_function_symbols) => { - for funct in checked_function_symbols { - let query = FunctionQuery::new( - declaration.id.clone(), - &funct.signature(&typed_modules).inputs, - &funct - .signature(&typed_modules) - .outputs - .clone() - .into_iter() - .map(|o| Some(o)) - .collect(), - ); + // we keep track of the introduced symbols to avoid colisions between types and functions + let mut symbol_unifier = SymbolUnifier::default(); - let candidates = self.find_candidates(&query); - - match candidates.len() { - 1 => { - errors.push(Error { - pos: Some(pos), - message: format!( - "Duplicate definition for function {} with signature {}", - declaration.id, - funct.signature(&typed_modules) - ), - }); - } - 0 => {} - _ => panic!( - "duplicate function declaration should have been caught" - ), - } - self.functions.insert( - FunctionKey::with_id(declaration.id.clone()) - .signature(funct.signature(&typed_modules).clone()), - ); - checked_functions.insert( - FunctionKey::with_id(declaration.id.clone()) - .signature(funct.signature(&typed_modules).clone()), - funct, - ); - } - } + // we go through symbol declarations and check them + for declaration in module.symbols { + match self.check_symbol_declaration( + declaration, + module_id, + state, + &mut checked_functions, + &mut symbol_unifier, + ) { + Ok(()) => {} Err(e) => { errors.extend(e); } } - - self.exit_scope(); } + Some(TypedModule { functions: checked_functions, }) @@ -265,23 +519,23 @@ impl<'ast> Checker<'ast> { // insert into typed_modules if we checked anything match to_insert { Some(typed_module) => { - typed_modules.insert(module_id.clone(), typed_module); + // there should be no checked module at that key just yet, if there is we have a colision or we checked something twice + assert!(state + .typed_modules + .insert(module_id.clone(), typed_module) + .is_none()); } None => {} }; - if errors.len() > 0 { - return Err(errors); - } - Ok(()) } - fn check_single_main(module: &Module) -> Result<(), Error> { + fn check_single_main(module: &TypedModule) -> Result<(), Error> { match module .functions .iter() - .filter(|node| node.value.id == "main") + .filter(|(key, _)| key.id == "main") .count() { 1 => Ok(()), @@ -298,7 +552,7 @@ impl<'ast> Checker<'ast> { fn check_for_var(&self, var: &VariableNode) -> Result<(), Error> { match var.value.get_type() { - Type::FieldElement => Ok(()), + UnresolvedType::FieldElement => Ok(()), t => Err(Error { pos: Some(var.pos()), message: format!("Variable in for loop cannot have type {}", t), @@ -309,22 +563,129 @@ impl<'ast> Checker<'ast> { fn check_function( &mut self, funct_node: FunctionNode<'ast, T>, + module_id: &ModuleId, + types: &TypeMap, ) -> Result, Vec> { + self.enter_scope(); + let mut errors = vec![]; let funct = funct_node.value; + let mut arguments_checked = vec![]; + let mut signature = None; assert_eq!(funct.arguments.len(), funct.signature.inputs.len()); - for arg in &funct.arguments { - self.insert_into_scope(arg.value.id.value.clone()); + for arg in funct.arguments { + match self.check_parameter(arg, module_id, types) { + Ok(a) => { + self.insert_into_scope(a.id.clone()); + arguments_checked.push(a); + } + Err(e) => errors.extend(e), + } } let mut statements_checked = vec![]; - for stat in funct.statements.into_iter() { - match self.check_statement(stat, &funct.signature.outputs) { - Ok(statement) => { - statements_checked.push(statement); + match self.check_signature(funct.signature, module_id, types) { + Ok(s) => { + for stat in funct.statements.into_iter() { + let pos = stat.pos(); + + match self.check_statement(stat, module_id, types) { + Ok(statement) => { + match &statement { + TypedStatement::Return(e) => { + match e.iter().map(|e| e.get_type()).collect::>() + == s.outputs + { + true => {} + false => errors.push(Error { + pos: Some(pos), + message: format!( + "Expected ({}) in return statement, found ({})", + s.outputs + .iter() + .map(|t| t.to_string()) + .collect::>() + .join(", "), + e.iter() + .map(|e| e.get_type()) + .map(|t| t.to_string()) + .collect::>() + .join(", ") + ), + }), + } + } + _ => {} + }; + statements_checked.push(statement); + } + Err(e) => { + errors.extend(e); + } + } + } + signature = Some(s); + } + Err(e) => { + errors.extend(e); + } + }; + + if errors.len() > 0 { + return Err(errors); + } + + self.exit_scope(); + + Ok(TypedFunction { + arguments: arguments_checked, + statements: statements_checked, + signature: signature.unwrap(), + }) + } + + fn check_parameter( + &self, + p: ParameterNode<'ast>, + module_id: &ModuleId, + types: &TypeMap, + ) -> Result, Vec> { + let var = self.check_variable(p.value.id, module_id, types)?; + + Ok(Parameter { + id: var, + private: p.value.private, + }) + } + + fn check_signature( + &self, + signature: UnresolvedSignature, + module_id: &ModuleId, + types: &TypeMap, + ) -> Result> { + let mut errors = vec![]; + let mut inputs = vec![]; + let mut outputs = vec![]; + + for t in signature.inputs { + match self.check_type(t, module_id, types) { + Ok(t) => { + inputs.push(t); + } + Err(e) => { + errors.push(e); + } + } + } + + for t in signature.outputs { + match self.check_type(t, module_id, types) { + Ok(t) => { + outputs.push(t); } Err(e) => { errors.push(e); @@ -336,127 +697,83 @@ impl<'ast> Checker<'ast> { return Err(errors); } - Ok(TypedFunction { - arguments: funct - .arguments - .into_iter() - .map(|a| a.value.into()) - .collect(), - statements: statements_checked, - signature: funct.signature, - }) + Ok(Signature { inputs, outputs }) } - fn check_function_symbol( - &mut self, - funct_symbol: FunctionSymbol<'ast, T>, - modules: &mut Modules<'ast, T>, - typed_modules: &mut TypedModules<'ast, T>, - ) -> Result>, Vec> { - let mut symbols = vec![]; - let mut errors = vec![]; + fn check_type( + &self, + ty: UnresolvedTypeNode, + module_id: &ModuleId, + types: &TypeMap, + ) -> Result { + let pos = ty.pos(); + let ty = ty.value; - match funct_symbol { - FunctionSymbol::Here(funct_node) => self - .check_function(funct_node) - .map(|f| vec![TypedFunctionSymbol::Here(f)]), - FunctionSymbol::There(import_node) => { - let pos = import_node.pos(); - let import = import_node.value; - - match Checker::new().check_module(&import.module_id, modules, typed_modules) { - Ok(()) => { - // find candidates in the checked module - let candidates: Vec<_> = typed_modules - .get(&import.module_id) - .unwrap() - .functions - .iter() - .filter(|(k, _)| k.id == import.function_id) - .map(|(_, v)| FunctionKey { - id: import.function_id.clone(), - signature: v.signature(&typed_modules).clone(), - }) - .collect(); - - match candidates.len() { - 0 => errors.push(Error { - pos: Some(pos), - message: format!( - "Function {} not found in module {}", - import.function_id, import.module_id - ), - }), - _ => { - symbols.extend(candidates.into_iter().map(|f| { - TypedFunctionSymbol::There(f, import.module_id.clone()) - })) - } - } - } - Err(e) => { - errors.extend(e); - } - }; - - // return if any errors occured - if errors.len() > 0 { - return Err(errors); - } - - Ok(symbols) + match ty { + UnresolvedType::FieldElement => Ok(Type::FieldElement), + UnresolvedType::Boolean => Ok(Type::Boolean), + UnresolvedType::Array(t, size) => Ok(Type::Array( + box self.check_type(*t, module_id, types)?, + size, + )), + UnresolvedType::User(id) => { + types + .get(module_id) + .unwrap() + .get(&id) + .cloned() + .ok_or_else(|| Error { + pos: Some(pos), + message: format!("Undefined type {}", id), + }) } - FunctionSymbol::Flat(flat_fun) => Ok(vec![TypedFunctionSymbol::Flat(flat_fun)]), } } + fn check_variable( + &self, + v: crate::absy::VariableNode<'ast>, + module_id: &ModuleId, + types: &TypeMap, + ) -> Result, Vec> { + Ok(Variable::with_id_and_type( + v.value.id.into(), + self.check_type(v.value._type, module_id, types) + .map_err(|e| vec![e])?, + )) + } + fn check_statement( &mut self, stat: StatementNode<'ast, T>, - header_return_types: &Vec, - ) -> Result, Error> { + module_id: &ModuleId, + types: &TypeMap, + ) -> Result, Vec> { let pos = stat.pos(); match stat.value { Statement::Return(list) => { let mut expression_list_checked = vec![]; for e in list.value.expressions { - let e_checked = self.check_expression(e)?; + let e_checked = self + .check_expression(e, module_id, &types) + .map_err(|e| vec![e])?; expression_list_checked.push(e_checked); } - let return_statement_types: Vec = expression_list_checked - .iter() - .map(|e| e.get_type()) - .collect(); - - match return_statement_types == *header_return_types { - true => Ok(TypedStatement::Return(expression_list_checked)), + Ok(TypedStatement::Return(expression_list_checked)) + } + Statement::Declaration(var) => { + let var = self.check_variable(var, module_id, types)?; + match self.insert_into_scope(var.clone()) { + true => Ok(TypedStatement::Declaration(var)), false => Err(Error { pos: Some(pos), - message: format!( - "Expected ({}) in return statement, found ({})", - header_return_types - .iter() - .map(|t| t.to_string()) - .collect::>() - .join(", "), - return_statement_types - .iter() - .map(|t| t.to_string()) - .collect::>() - .join(", ") - ), + message: format!("Duplicate declaration for variable named {}", var.id), }), } + .map_err(|e| vec![e]) } - Statement::Declaration(var) => match self.insert_into_scope(var.clone().value) { - true => Ok(TypedStatement::Declaration(var.value.into())), - false => Err(Error { - pos: Some(pos), - message: format!("Duplicate declaration for variable named {}", var.value.id), - }), - }, Statement::Definition(assignee, expr) => { // we create multidef when rhs is a function call to benefit from inference // check rhs is not a function call here @@ -466,11 +783,15 @@ impl<'ast> Checker<'ast> { } // check the expression to be assigned - let checked_expr = self.check_expression(expr)?; + let checked_expr = self + .check_expression(expr, module_id, &types) + .map_err(|e| vec![e])?; let expression_type = checked_expr.get_type(); // check that the assignee is declared and is well formed - let var = self.check_assignee(assignee)?; + let var = self + .check_assignee(assignee, module_id, &types) + .map_err(|e| vec![e])?; let var_type = var.get_type(); @@ -485,10 +806,15 @@ impl<'ast> Checker<'ast> { ), }), } + .map_err(|e| vec![e]) } Statement::Condition(lhs, rhs) => { - let checked_lhs = self.check_expression(lhs)?; - let checked_rhs = self.check_expression(rhs)?; + let checked_lhs = self + .check_expression(lhs, module_id, &types) + .map_err(|e| vec![e])?; + let checked_rhs = self + .check_expression(rhs, module_id, &types) + .map_err(|e| vec![e])?; if checked_lhs.get_type() == checked_rhs.get_type() { Ok(TypedStatement::Condition(checked_lhs, checked_rhs)) @@ -504,28 +830,26 @@ impl<'ast> Checker<'ast> { ), }) } + .map_err(|e| vec![e]) } Statement::For(var, from, to, statements) => { self.enter_scope(); - self.check_for_var(&var)?; + self.check_for_var(&var).map_err(|e| vec![e])?; - self.insert_into_scope(var.clone().value); + let var = self.check_variable(var, module_id, types).unwrap(); + + self.insert_into_scope(var.clone()); let mut checked_statements = vec![]; for stat in statements { - let checked_stat = self.check_statement(stat, header_return_types)?; + let checked_stat = self.check_statement(stat, module_id, types)?; checked_statements.push(checked_stat); } self.exit_scope(); - Ok(TypedStatement::For( - var.value.into(), - from, - to, - checked_statements, - )) + Ok(TypedStatement::For(var, from, to, checked_statements)) } Statement::MultipleDefinition(assignees, rhs) => { match rhs.value { @@ -545,14 +869,14 @@ impl<'ast> Checker<'ast> { ref a => Err(Error { pos: Some(pos), message: format!("Left hand side of function return assignment must be a list of identifiers, found {}", a)}) - }?; + }.map_err(|e| vec![e])?; vars_types.push(t); var_names.push(name); } // find arguments types let mut arguments_checked = vec![]; for arg in arguments { - let arg_checked = self.check_expression(arg)?; + let arg_checked = self.check_expression(arg, module_id, &types).map_err(|e| vec![e])?; arguments_checked.push(arg_checked); } @@ -568,8 +892,8 @@ impl<'ast> Checker<'ast> { let f = &candidates[0]; // we can infer the left hand side to be typed as the return values - let lhs: Vec<_> = var_names.iter().zip(f.signature.outputs.iter()).map(|(name, ty)| - Variable::new(*name, ty.clone()) + let lhs: Vec = var_names.iter().zip(f.signature.outputs.iter()).map(|(name, ty)| + Variable::with_id_and_type(crate::typed_absy::Identifier::from(*name), ty.clone()) ).collect(); let assignees: Vec<_> = lhs.iter().map(|v| v.clone().into()).collect(); @@ -592,7 +916,7 @@ impl<'ast> Checker<'ast> { pos: Some(pos), message: format!("{} should be a FunctionCall", rhs), }), - } + }.map_err(|e| vec![e]) } } } @@ -600,49 +924,87 @@ impl<'ast> Checker<'ast> { fn check_assignee( &mut self, assignee: AssigneeNode<'ast, T>, + module_id: &ModuleId, + types: &TypeMap, ) -> Result, Error> { let pos = assignee.pos(); // check that the assignee is declared match assignee.value { Assignee::Identifier(variable_name) => match self.get_scope(&variable_name) { - Some(var) => Ok(TypedAssignee::Identifier( - crate::typed_absy::Variable::with_id_and_type( - variable_name.into(), - var.id.get_type(), - ), - )), + Some(var) => Ok(TypedAssignee::Identifier(Variable::with_id_and_type( + variable_name.into(), + var.id._type.clone(), + ))), None => Err(Error { pos: Some(assignee.pos()), message: format!("Undeclared variable: {:?}", variable_name), }), }, Assignee::Select(box assignee, box index) => { - let checked_assignee = self.check_assignee(assignee)?; - let checked_index = match index { - RangeOrExpression::Expression(e) => self.check_expression(e)?, - r => unimplemented!( - "Using slices in assignments is not supported yet, found {}", - r - ), - }; + let checked_assignee = self.check_assignee(assignee, module_id, &types)?; - let checked_typed_index = match checked_index { - TypedExpression::FieldElement(e) => Ok(e), - e => Err(Error { + let ty = checked_assignee.get_type(); + match ty { + Type::Array(..) => { + let checked_index = match index { + RangeOrExpression::Expression(e) => { + self.check_expression(e, module_id, &types)? + } + r => unimplemented!( + "Using slices in assignments is not supported yet, found {}", + r + ), + }; + + let checked_typed_index = match checked_index { + TypedExpression::FieldElement(e) => Ok(e), + e => Err(Error { + pos: Some(pos), + + message: format!( + "Expected array {} index to have type field, found {}", + checked_assignee, + e.get_type() + ), + }), + }?; + + Ok(TypedAssignee::Select( + box checked_assignee, + box checked_typed_index, + )) + } + ty => Err(Error { pos: Some(pos), message: format!( - "Expected array {} index to have type field, found {}", - checked_assignee, - e.get_type() + "Cannot access element at index {} on {} of type {}", + index, checked_assignee, ty, ), }), - }?; + } + } + Assignee::Member(box assignee, box member) => { + let checked_assignee = self.check_assignee(assignee, module_id, &types)?; - Ok(TypedAssignee::Select( - box checked_assignee, - box checked_typed_index, - )) + let ty = checked_assignee.get_type(); + match &ty { + Type::Struct(members) => match members.iter().find(|(id, _)| id == member) { + Some(_) => Ok(TypedAssignee::Member(box checked_assignee, member.into())), + None => Err(Error { + pos: Some(pos), + message: format!("{} doesn't have member {}", ty, member), + }), + }, + ty => Err(Error { + pos: Some(pos), + + message: format!( + "Cannot access field {} on {} as of type {}", + member, checked_assignee, ty, + ), + }), + } } } } @@ -650,12 +1012,15 @@ impl<'ast> Checker<'ast> { fn check_spread_or_expression( &mut self, spread_or_expression: SpreadOrExpression<'ast, T>, + module_id: &ModuleId, + types: &TypeMap, ) -> Result>, Error> { match spread_or_expression { SpreadOrExpression::Spread(s) => { let pos = s.pos(); - let checked_expression = self.check_expression(s.value.expression)?; + let checked_expression = + self.check_expression(s.value.expression, module_id, &types)?; match checked_expression { TypedExpression::Array(e) => { let ty = e.inner_type().clone(); @@ -685,6 +1050,12 @@ impl<'ast> Checker<'ast> { ) .annotate(ty.clone(), *s) .into(), + Type::Struct(fields) => StructExpressionInner::Select( + box e.clone().annotate(Type::Struct(fields.clone()), size), + box FieldElementExpression::Number(T::from(i)), + ) + .annotate(fields.clone()) + .into(), }) .collect()), } @@ -699,13 +1070,17 @@ impl<'ast> Checker<'ast> { }), } } - SpreadOrExpression::Expression(e) => self.check_expression(e).map(|r| vec![r]), + SpreadOrExpression::Expression(e) => { + self.check_expression(e, module_id, &types).map(|r| vec![r]) + } } } fn check_expression( &mut self, expr: ExpressionNode<'ast, T>, + module_id: &ModuleId, + types: &TypeMap, ) -> Result, Error> { let pos = expr.pos(); @@ -722,6 +1097,9 @@ impl<'ast> Checker<'ast> { Type::Array(ty, size) => Ok(ArrayExpressionInner::Identifier(name.into()) .annotate(*ty, size) .into()), + Type::Struct(members) => Ok(StructExpressionInner::Identifier(name.into()) + .annotate(members) + .into()), }, None => Err(Error { pos: Some(pos), @@ -730,8 +1108,8 @@ impl<'ast> Checker<'ast> { } } Expression::Add(box e1, box e2) => { - let e1_checked = self.check_expression(e1)?; - let e2_checked = self.check_expression(e2)?; + let e1_checked = self.check_expression(e1, module_id, &types)?; + let e2_checked = self.check_expression(e2, module_id, &types)?; match (e1_checked, e2_checked) { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { @@ -749,8 +1127,8 @@ impl<'ast> Checker<'ast> { } } Expression::Sub(box e1, box e2) => { - let e1_checked = self.check_expression(e1)?; - let e2_checked = self.check_expression(e2)?; + let e1_checked = self.check_expression(e1, module_id, &types)?; + let e2_checked = self.check_expression(e2, module_id, &types)?; match (e1_checked, e2_checked) { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { @@ -768,8 +1146,8 @@ impl<'ast> Checker<'ast> { } } Expression::Mult(box e1, box e2) => { - let e1_checked = self.check_expression(e1)?; - let e2_checked = self.check_expression(e2)?; + let e1_checked = self.check_expression(e1, module_id, &types)?; + let e2_checked = self.check_expression(e2, module_id, &types)?; match (e1_checked, e2_checked) { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { @@ -787,8 +1165,8 @@ impl<'ast> Checker<'ast> { } } Expression::Div(box e1, box e2) => { - let e1_checked = self.check_expression(e1)?; - let e2_checked = self.check_expression(e2)?; + let e1_checked = self.check_expression(e1, module_id, &types)?; + let e2_checked = self.check_expression(e2, module_id, &types)?; match (e1_checked, e2_checked) { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { @@ -806,8 +1184,8 @@ impl<'ast> Checker<'ast> { } } Expression::Pow(box e1, box e2) => { - let e1_checked = self.check_expression(e1)?; - let e2_checked = self.check_expression(e2)?; + let e1_checked = self.check_expression(e1, module_id, &types)?; + let e2_checked = self.check_expression(e2, module_id, &types)?; match (e1_checked, e2_checked) { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => Ok( @@ -825,9 +1203,9 @@ impl<'ast> Checker<'ast> { } } Expression::IfElse(box condition, box consequence, box alternative) => { - let condition_checked = self.check_expression(condition)?; - let consequence_checked = self.check_expression(consequence)?; - let alternative_checked = self.check_expression(alternative)?; + let condition_checked = self.check_expression(condition, module_id, &types)?; + let consequence_checked = self.check_expression(consequence, module_id, &types)?; + let alternative_checked = self.check_expression(alternative, module_id, &types)?; match condition_checked { TypedExpression::Boolean(condition) => { @@ -846,6 +1224,14 @@ impl<'ast> Checker<'ast> { let size = consequence.size(); Ok(ArrayExpressionInner::IfElse(box condition, box consequence, box alternative).annotate(inner_type, size).into()) }, + (TypedExpression::Struct(consequence), TypedExpression::Struct(alternative)) => { + if consequence.get_type() == alternative.get_type() { + let ty = consequence.ty().clone(); + Ok(StructExpressionInner::IfElse(box condition, box consequence, box alternative).annotate(ty).into()) + } else { + unimplemented!("handle consequence alternative inner type mismatch") + } + }, _ => unreachable!("types should match here as we checked them explicitly") } false => Err(Error { @@ -868,7 +1254,7 @@ impl<'ast> Checker<'ast> { // check the arguments let mut arguments_checked = vec![]; for arg in arguments { - let arg_checked = self.check_expression(arg)?; + let arg_checked = self.check_expression(arg, module_id, &types)?; arguments_checked.push(arg_checked); } @@ -898,6 +1284,15 @@ impl<'ast> Checker<'ast> { arguments_checked, ) .into()), + Type::Struct(members) => Ok(StructExpressionInner::FunctionCall( + FunctionKey { + id: f.id.clone(), + signature: f.signature.clone(), + }, + arguments_checked, + ) + .annotate(members.clone()) + .into()), Type::Array(box ty, size) => { Ok(ArrayExpressionInner::FunctionCall( FunctionKey { @@ -935,8 +1330,8 @@ impl<'ast> Checker<'ast> { } } Expression::Lt(box e1, box e2) => { - let e1_checked = self.check_expression(e1)?; - let e2_checked = self.check_expression(e2)?; + let e1_checked = self.check_expression(e1, module_id, &types)?; + let e2_checked = self.check_expression(e2, module_id, &types)?; match (e1_checked, e2_checked) { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { Ok(BooleanExpression::Lt(box e1, box e2).into()) @@ -954,8 +1349,8 @@ impl<'ast> Checker<'ast> { } } Expression::Le(box e1, box e2) => { - let e1_checked = self.check_expression(e1)?; - let e2_checked = self.check_expression(e2)?; + let e1_checked = self.check_expression(e1, module_id, &types)?; + let e2_checked = self.check_expression(e2, module_id, &types)?; match (e1_checked, e2_checked) { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { Ok(BooleanExpression::Le(box e1, box e2).into()) @@ -973,8 +1368,8 @@ impl<'ast> Checker<'ast> { } } Expression::Eq(box e1, box e2) => { - let e1_checked = self.check_expression(e1)?; - let e2_checked = self.check_expression(e2)?; + let e1_checked = self.check_expression(e1, module_id, &types)?; + let e2_checked = self.check_expression(e2, module_id, &types)?; match (e1_checked, e2_checked) { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { Ok(BooleanExpression::Eq(box e1, box e2).into()) @@ -995,8 +1390,8 @@ impl<'ast> Checker<'ast> { } } Expression::Ge(box e1, box e2) => { - let e1_checked = self.check_expression(e1)?; - let e2_checked = self.check_expression(e2)?; + let e1_checked = self.check_expression(e1, module_id, &types)?; + let e2_checked = self.check_expression(e2, module_id, &types)?; match (e1_checked, e2_checked) { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { Ok(BooleanExpression::Ge(box e1, box e2).into()) @@ -1014,8 +1409,8 @@ impl<'ast> Checker<'ast> { } } Expression::Gt(box e1, box e2) => { - let e1_checked = self.check_expression(e1)?; - let e2_checked = self.check_expression(e2)?; + let e1_checked = self.check_expression(e1, module_id, &types)?; + let e2_checked = self.check_expression(e2, module_id, &types)?; match (e1_checked, e2_checked) { (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { Ok(BooleanExpression::Gt(box e1, box e2).into()) @@ -1033,7 +1428,7 @@ impl<'ast> Checker<'ast> { } } Expression::Select(box array, box index) => { - let array = self.check_expression(array)?; + let array = self.check_expression(array, module_id, &types)?; match index { RangeOrExpression::Range(r) => match array { @@ -1099,36 +1494,92 @@ impl<'ast> Checker<'ast> { ), }), }, - RangeOrExpression::Expression(e) => match (array, self.check_expression(e)?) { - (TypedExpression::Array(a), TypedExpression::FieldElement(i)) => { - match a.inner_type().clone() { - Type::FieldElement => { - Ok(FieldElementExpression::Select(box a, box i).into()) - } - Type::Boolean => Ok(BooleanExpression::Select(box a, box i).into()), - Type::Array(box ty, size) => { - Ok(ArrayExpressionInner::Select(box a, box i) - .annotate(ty.clone(), size.clone()) - .into()) + RangeOrExpression::Expression(e) => { + match (array, self.check_expression(e, module_id, &types)?) { + (TypedExpression::Array(a), TypedExpression::FieldElement(i)) => { + match a.inner_type().clone() { + Type::FieldElement => { + Ok(FieldElementExpression::Select(box a, box i).into()) + } + Type::Boolean => { + Ok(BooleanExpression::Select(box a, box i).into()) + } + Type::Array(box ty, size) => { + Ok(ArrayExpressionInner::Select(box a, box i) + .annotate(ty.clone(), size.clone()) + .into()) + } + Type::Struct(members) => { + Ok(StructExpressionInner::Select(box a, box i) + .annotate(members.clone()) + .into()) + } } } + (a, e) => Err(Error { + pos: Some(pos), + message: format!( + "Cannot access element {} on expression of type {}", + e, + a.get_type() + ), + }), } - (a, e) => Err(Error { - pos: Some(pos), - message: format!( - "Cannot access element {} on expression of type {}", - e, - a.get_type() - ), - }), - }, + } + } + } + Expression::Member(box e, box id) => { + let e = self.check_expression(e, module_id, &types)?; + + match e { + TypedExpression::Struct(s) => { + // check that the struct has that field and return the type if it does + let ty = s + .ty() + .iter() + .find(|(member_id, _)| member_id == id) + .map(|(_, ty)| ty); + + match ty { + Some(ty) => match ty { + Type::FieldElement => { + Ok(FieldElementExpression::Member(box s, id.to_string()).into()) + } + Type::Boolean => { + Ok(BooleanExpression::Member(box s, id.to_string()).into()) + } + Type::Array(box ty, size) => { + Ok(ArrayExpressionInner::Member(box s.clone(), id.to_string()) + .annotate(ty.clone(), *size) + .into()) + } + Type::Struct(members) => { + Ok(StructExpressionInner::Member(box s.clone(), id.to_string()) + .annotate(members.clone()) + .into()) + } + }, + None => Err(Error { + pos: Some(pos), + message: format!("{} doesn't have member {}", s.get_type(), id,), + }), + } + } + e => Err(Error { + pos: Some(pos), + message: format!( + "Cannot access member {} on expression of type {}", + id, + e.get_type() + ), + }), } } Expression::InlineArray(expressions) => { // check each expression, getting its type let mut expressions_checked = vec![]; for e in expressions { - let e_checked = self.check_spread_or_expression(e)?; + let e_checked = self.check_spread_or_expression(e, module_id, &types)?; expressions_checked.extend(e_checked); } @@ -1228,15 +1679,133 @@ impl<'ast> Checker<'ast> { let size = unwrapped_expressions.len(); + Ok(ArrayExpressionInner::Value(unwrapped_expressions) + .annotate(ty, size) + .into()) + } + ty @ Type::Struct(..) => { + // we check all expressions have that same type + let mut unwrapped_expressions = vec![]; + + for e in expressions_checked { + let unwrapped_e = match e { + TypedExpression::Struct(e) => { + if e.get_type() == ty { + Ok(e) + } else { + Err(Error { + pos: Some(pos), + + message: format!( + "Expected {} to have type {}, but type is {}", + e, + ty, + e.get_type() + ), + }) + } + } + e => Err(Error { + pos: Some(pos), + + message: format!( + "Expected {} to have type {}, but type is {}", + e, + ty, + e.get_type() + ), + }), + }?; + unwrapped_expressions.push(unwrapped_e.into()); + } + + let size = unwrapped_expressions.len(); + Ok(ArrayExpressionInner::Value(unwrapped_expressions) .annotate(ty, size) .into()) } } } + Expression::InlineStruct(id, inline_members) => { + let ty = self.check_type( + UnresolvedType::User(id.clone()).at(42, 42, 42), + module_id, + &types, + )?; + let members = match ty { + Type::Struct(members) => members, + _ => unreachable!(), + }; + + // check that we provided the required number of values + + if members.len() != inline_members.len() { + return Err(Error { + pos: Some(pos), + message: format!( + "Inline struct {} does not match {} : {}", + Expression::InlineStruct(id.clone(), inline_members), + id, + Type::Struct(members) + ), + }); + } + + // check that the mapping of values matches the expected type + // put the value into a map, pick members from this map following declared members, and try to parse them + + let mut inline_members_map = inline_members + .clone() + .into_iter() + .map(|(id, v)| (id.to_string(), v)) + .collect::>(); + let mut result: Vec> = vec![]; + + for (member_id, ty) in &members { + match inline_members_map.remove(member_id) { + Some(value) => { + let expression_checked = + self.check_expression(value, module_id, &types)?; + let checked_type = expression_checked.get_type(); + if checked_type != *ty { + return Err(Error { + pos: Some(pos), + message: format!( + "Member {} of struct {} has type {}, found {} of type {}", + member_id, + id.clone(), + ty, + expression_checked, + checked_type, + ), + }); + } else { + result.push(expression_checked.into()); + } + } + None => { + return Err(Error { + pos: Some(pos), + message: format!( + "Member {} of struct {} : {} not found in value {}", + member_id, + id.clone(), + Type::Struct(members.clone()), + Expression::InlineStruct(id.clone(), inline_members), + ), + }) + } + } + } + + Ok(StructExpressionInner::Value(result) + .annotate(members) + .into()) + } Expression::And(box e1, box e2) => { - let e1_checked = self.check_expression(e1)?; - let e2_checked = self.check_expression(e2)?; + let e1_checked = self.check_expression(e1, module_id, &types)?; + let e2_checked = self.check_expression(e2, module_id, &types)?; match (e1_checked, e2_checked) { (TypedExpression::Boolean(e1), TypedExpression::Boolean(e2)) => { Ok(BooleanExpression::And(box e1, box e2).into()) @@ -1253,8 +1822,8 @@ impl<'ast> Checker<'ast> { } } Expression::Or(box e1, box e2) => { - let e1_checked = self.check_expression(e1)?; - let e2_checked = self.check_expression(e2)?; + let e1_checked = self.check_expression(e1, module_id, &types)?; + let e2_checked = self.check_expression(e2, module_id, &types)?; match (e1_checked, e2_checked) { (TypedExpression::Boolean(e1), TypedExpression::Boolean(e2)) => { Ok(BooleanExpression::Or(box e1, box e2).into()) @@ -1267,7 +1836,7 @@ impl<'ast> Checker<'ast> { } } Expression::Not(box e) => { - let e_checked = self.check_expression(e)?; + let e_checked = self.check_expression(e, module_id, &types)?; match e_checked { TypedExpression::Boolean(e) => Ok(BooleanExpression::Not(box e).into()), e => Err(Error { @@ -1280,9 +1849,12 @@ impl<'ast> Checker<'ast> { } } - fn get_scope(&self, variable_name: &Identifier<'ast>) -> Option<&ScopedVariable> { + fn get_scope(&self, variable_name: &'ast str) -> Option<&'ast ScopedVariable> { self.scope.get(&ScopedVariable { - id: Variable::new(*variable_name, Type::FieldElement), + id: Variable::with_id_and_type( + crate::typed_absy::Identifier::from(variable_name), + Type::FieldElement, + ), level: 0, }) } @@ -1313,22 +1885,28 @@ impl<'ast> Checker<'ast> { #[cfg(test)] mod tests { use super::*; + use absy; use typed_absy; - use types::Signature; use zokrates_field::field::FieldPrime; + const MODULE_ID: &str = ""; + mod array { use super::*; #[test] fn element_type_mismatch() { + let types = HashMap::new(); + let module_id = String::from(""); // [3, true] let a = Expression::InlineArray(vec![ Expression::FieldConstant(FieldPrime::from(3)).mock().into(), Expression::BooleanConstant(true).mock().into(), ]) .mock(); - assert!(Checker::new().check_expression(a).is_err()); + assert!(Checker::new() + .check_expression(a, &module_id, &types) + .is_err()); // [[0], [0, 0]] let a = Expression::InlineArray(vec![ @@ -1345,7 +1923,9 @@ mod tests { .into(), ]) .mock(); - assert!(Checker::new().check_expression(a).is_err()); + assert!(Checker::new() + .check_expression(a, &module_id, &types) + .is_err()); // [[0], true] let a = Expression::InlineArray(vec![ @@ -1359,19 +1939,101 @@ mod tests { .into(), ]) .mock(); - assert!(Checker::new().check_expression(a).is_err()); + assert!(Checker::new() + .check_expression(a, &module_id, &types) + .is_err()); } } mod symbols { use super::*; - use crate::types::Signature; + + /// Helper function to create (() -> (): return) + fn function0() -> FunctionNode<'static, FieldPrime> { + let statements: Vec> = vec![Statement::Return( + ExpressionList { + expressions: vec![], + } + .mock(), + ) + .mock()]; + + let arguments = vec![]; + + let signature = UnresolvedSignature::new(); + + Function { + arguments, + statements, + signature, + } + .mock() + } + + /// Helper function to create ((private field a) -> (): return) + fn function1() -> FunctionNode<'static, FieldPrime> { + let statements: Vec> = vec![Statement::Return( + ExpressionList { + expressions: vec![], + } + .mock(), + ) + .mock()]; + + let arguments = vec![absy::Parameter { + id: absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + private: true, + } + .mock()]; + + let signature = + UnresolvedSignature::new().inputs(vec![UnresolvedType::FieldElement.mock()]); + + Function { + arguments, + statements, + signature, + } + .mock() + } + + fn struct0() -> StructTypeNode<'static> { + StructType { fields: vec![] }.mock() + } + + fn struct1() -> StructTypeNode<'static> { + StructType { + fields: vec![StructField { + id: "foo".into(), + ty: UnresolvedType::FieldElement.mock(), + } + .mock()], + } + .mock() + } #[test] - fn imported_symbol() { + fn unifier() { + // the unifier should only accept either a single type or many functions of different signatures for each symbol + + let mut unifier = SymbolUnifier::default(); + + assert!(unifier.insert_type("foo")); + assert!(!unifier.insert_type("foo")); + assert!(!unifier.insert_function("foo", Signature::new())); + assert!(unifier.insert_function("bar", Signature::new())); + assert!(!unifier.insert_function("bar", Signature::new())); + assert!( + unifier.insert_function("bar", Signature::new().inputs(vec![Type::FieldElement])) + ); + assert!(!unifier.insert_type("bar")); + } + + #[test] + fn imported_function() { // foo.zok - // def main() -> (field): - // return 1 + // def main() -> (): + // return // bar.zok // from "./foo.zok" import main @@ -1379,60 +2041,42 @@ mod tests { // after semantic check, `bar` should import a checked function let foo: Module = Module { - functions: vec![FunctionDeclaration { + symbols: vec![SymbolDeclaration { id: "main", - symbol: FunctionSymbol::Here( - Function { - statements: vec![Statement::Return( - ExpressionList { - expressions: vec![Expression::FieldConstant(FieldPrime::from( - 1, - )) - .mock()], - } - .mock(), - ) - .mock()], - signature: Signature::new().outputs(vec![Type::FieldElement]), - arguments: vec![], - } - .mock(), - ), + symbol: Symbol::HereFunction(function0()), } .mock()], imports: vec![], }; let bar: Module = Module { - functions: vec![FunctionDeclaration { + symbols: vec![SymbolDeclaration { id: "main", - symbol: FunctionSymbol::There( - FunctionImport::with_id_in_module("main", "foo").mock(), - ), + symbol: Symbol::There(SymbolImport::with_id_in_module("main", "foo").mock()), } .mock()], imports: vec![], }; - let mut modules = vec![(String::from("foo"), foo), (String::from("bar"), bar)] - .into_iter() - .collect(); - let mut typed_modules = HashMap::new(); + let mut state = State::new( + vec![(String::from("foo"), foo), (String::from("bar"), bar)] + .into_iter() + .collect(), + ); let mut checker = Checker::new(); - checker - .check_module(&String::from("bar"), &mut modules, &mut typed_modules) - .unwrap(); assert_eq!( - typed_modules.get(&String::from("bar")), + checker.check_module(&String::from("bar"), &mut state), + Ok(()) + ); + assert_eq!( + state.typed_modules.get(&String::from("bar")), Some(&TypedModule { functions: vec![( - FunctionKey::with_id("main") - .signature(Signature::new().outputs(vec![Type::FieldElement])), + FunctionKey::with_id("main").signature(Signature::new()), TypedFunctionSymbol::There( - FunctionKey::with_id("main") - .signature(Signature::new().outputs(vec![Type::FieldElement])), + FunctionKey::with_id("main").signature(Signature::new()), "foo".to_string() ) )] @@ -1441,6 +2085,266 @@ mod tests { }) ); } + + #[test] + fn duplicate_function_declaration() { + // def foo(): + // return + // def foo(): + // return + // + // should fail + + let module = Module { + symbols: vec![ + SymbolDeclaration { + id: "foo", + symbol: Symbol::HereFunction(function0()), + } + .mock(), + SymbolDeclaration { + id: "foo", + symbol: Symbol::HereFunction(function0()), + } + .mock(), + ], + imports: vec![], + }; + + let mut state = State::new(vec![(MODULE_ID.to_string(), module)].into_iter().collect()); + + let mut checker = Checker::new(); + assert_eq!( + checker + .check_module(&MODULE_ID.to_string(), &mut state) + .unwrap_err()[0] + .message, + "foo conflicts with another symbol" + ); + } + + #[test] + fn overloaded_function_declaration() { + // def foo(): + // return + // def foo(a): + // return + // + // should succeed as overloading is allowed + + let module = Module { + symbols: vec![ + SymbolDeclaration { + id: "foo", + symbol: Symbol::HereFunction(function0()), + } + .mock(), + SymbolDeclaration { + id: "foo", + symbol: Symbol::HereFunction(function1()), + } + .mock(), + ], + imports: vec![], + }; + + let mut state = State::new(vec![(MODULE_ID.to_string(), module)].into_iter().collect()); + + let mut checker = Checker::new(); + assert_eq!( + checker.check_module(&MODULE_ID.to_string(), &mut state), + Ok(()) + ); + assert!(state + .typed_modules + .get(&MODULE_ID.to_string()) + .unwrap() + .functions + .contains_key(&FunctionKey::with_id("foo").signature(Signature::new()))); + assert!(state + .typed_modules + .get(&MODULE_ID.to_string()) + .unwrap() + .functions + .contains_key( + &FunctionKey::with_id("foo") + .signature(Signature::new().inputs(vec![Type::FieldElement])) + )) + } + + #[test] + fn duplicate_type_declaration() { + // struct Foo {} + // struct Foo { foo: field } + // + // should fail + + let module: Module = Module { + symbols: vec![ + SymbolDeclaration { + id: "foo", + symbol: Symbol::HereType(struct0()), + } + .mock(), + SymbolDeclaration { + id: "foo", + symbol: Symbol::HereType(struct1()), + } + .mock(), + ], + imports: vec![], + }; + + let mut state = State::new(vec![(String::from("main"), module)].into_iter().collect()); + + let mut checker = Checker::new(); + assert_eq!( + checker + .check_module(&String::from("main"), &mut state) + .unwrap_err()[0] + .message, + "foo conflicts with another symbol" + ); + } + + #[test] + fn type_function_conflict() { + // struct foo {} + // def foo(): + // return + // + // should fail + + let module = Module { + symbols: vec![ + SymbolDeclaration { + id: "foo", + symbol: Symbol::HereFunction(function0()), + } + .mock(), + SymbolDeclaration { + id: "foo", + symbol: Symbol::HereType(StructType { fields: vec![] }.mock()), + } + .mock(), + ], + imports: vec![], + }; + + let mut state = State::new(vec![(String::from("main"), module)].into_iter().collect()); + + let mut checker = Checker::new(); + assert_eq!( + checker + .check_module(&String::from("main"), &mut state) + .unwrap_err()[0] + .message, + "foo conflicts with another symbol" + ); + } + + #[test] + fn type_imported_function_conflict() { + // import first + + // // bar.code + // def main() -> (): return + // + // // main.code + // import main from "bar" as foo + // struct foo {} + // + // should fail + + let bar = Module::with_symbols(vec![SymbolDeclaration { + id: "main", + symbol: Symbol::HereFunction(function0()), + } + .mock()]); + + let main = Module { + symbols: vec![ + SymbolDeclaration { + id: "foo", + symbol: Symbol::There( + SymbolImport::with_id_in_module("main", "bar".to_string()).mock(), + ), + } + .mock(), + SymbolDeclaration { + id: "foo", + symbol: Symbol::HereType(struct0()), + } + .mock(), + ], + imports: vec![], + }; + + let mut state = State::new( + vec![(MODULE_ID.to_string(), main), ("bar".to_string(), bar)] + .into_iter() + .collect(), + ); + + let mut checker = Checker::new(); + assert_eq!( + checker + .check_module(&MODULE_ID.to_string(), &mut state) + .unwrap_err()[0] + .message, + "foo conflicts with another symbol" + ); + + // type declaration first + + // // bar.code + // def main() -> (): return + // + // // main.code + // struct foo {} + // import main from "bar" as foo + // + // should fail + + let bar = Module::with_symbols(vec![SymbolDeclaration { + id: "main", + symbol: Symbol::HereFunction(function0()), + } + .mock()]); + + let main = Module { + symbols: vec![ + SymbolDeclaration { + id: "foo", + symbol: Symbol::HereType(struct0()), + } + .mock(), + SymbolDeclaration { + id: "foo", + symbol: Symbol::There( + SymbolImport::with_id_in_module("main", "bar".to_string()).mock(), + ), + } + .mock(), + ], + imports: vec![], + }; + + let mut state = State::new( + vec![(MODULE_ID.to_string(), main), ("bar".to_string(), bar)] + .into_iter() + .collect(), + ); + + let mut checker = Checker::new(); + assert_eq!( + checker + .check_module(&MODULE_ID.to_string(), &mut state) + .unwrap_err()[0] + .message, + "foo conflicts with another symbol" + ); + } } pub fn new_with_args<'ast>( @@ -1465,13 +2369,16 @@ mod tests { ) .mock(); + let types = HashMap::new(); + let module_id = String::from(""); + let mut checker = Checker::new(); assert_eq!( - checker.check_statement(statement, &vec![]), - Err(Error { + checker.check_statement(statement, &module_id, &types), + Err(vec![Error { pos: Some((Position::mock(), Position::mock())), message: "Identifier \"b\" is undefined".to_string() - }) + }]) ); } @@ -1485,18 +2392,21 @@ mod tests { ) .mock(); + let types = HashMap::new(); + let module_id = String::from(""); + let mut scope = HashSet::new(); scope.insert(ScopedVariable { - id: Variable::field_element("a"), + id: Variable::field_element("a".into()), level: 0, }); scope.insert(ScopedVariable { - id: Variable::field_element("b"), + id: Variable::field_element("b".into()), level: 0, }); let mut checker = new_with_args(scope, 1, HashSet::new()); assert_eq!( - checker.check_statement(statement, &vec![]), + checker.check_statement(statement, &module_id, &types), Ok(TypedStatement::Definition( TypedAssignee::Identifier(typed_absy::Variable::field_element("a".into())), FieldElementExpression::Identifier("b".into()).into() @@ -1513,7 +2423,10 @@ mod tests { // should fail let foo_args = vec![]; let foo_statements = vec![ - Statement::Declaration(Variable::field_element("a").mock()).mock(), + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), Statement::Definition( Assignee::Identifier("a").mock(), Expression::FieldConstant(FieldPrime::from(1)).mock(), @@ -1523,9 +2436,9 @@ mod tests { let foo = Function { arguments: foo_args, statements: foo_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); @@ -1542,35 +2455,35 @@ mod tests { let bar = Function { arguments: bar_args, statements: bar_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); - let funcs = vec![ - FunctionDeclaration { + let symbols = vec![ + SymbolDeclaration { id: "foo", - symbol: FunctionSymbol::Here(foo), + symbol: Symbol::HereFunction(foo), } .mock(), - FunctionDeclaration { + SymbolDeclaration { id: "bar", - symbol: FunctionSymbol::Here(bar), + symbol: Symbol::HereFunction(bar), } .mock(), ]; let module = Module { - functions: funcs, + symbols, imports: vec![], }; - let mut modules = vec![(String::from("main"), module)].into_iter().collect(); + let mut state = State::new(vec![(String::from("main"), module)].into_iter().collect()); let mut checker = Checker::new(); assert_eq!( - checker.check_module(&String::from("main"), &mut modules, &mut HashMap::new()), + checker.check_module(&String::from("main"), &mut state), Err(vec![Error { pos: Some((Position::mock(), Position::mock())), message: "Identifier \"a\" is undefined".to_string() @@ -1590,7 +2503,10 @@ mod tests { // should pass let foo_args = vec![]; let foo_statements = vec![ - Statement::Declaration(Variable::field_element("a").mock()).mock(), + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), Statement::Definition( Assignee::Identifier("a").mock(), Expression::FieldConstant(FieldPrime::from(1)).mock(), @@ -1601,16 +2517,19 @@ mod tests { let foo = Function { arguments: foo_args, statements: foo_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); let bar_args = vec![]; let bar_statements = vec![ - Statement::Declaration(Variable::field_element("a").mock()).mock(), + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), Statement::Definition( Assignee::Identifier("a").mock(), Expression::FieldConstant(FieldPrime::from(2)).mock(), @@ -1627,9 +2546,9 @@ mod tests { let bar = Function { arguments: bar_args, statements: bar_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); @@ -1646,40 +2565,40 @@ mod tests { let main = Function { arguments: main_args, statements: main_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); - let funcs = vec![ - FunctionDeclaration { + let symbols = vec![ + SymbolDeclaration { id: "foo", - symbol: FunctionSymbol::Here(foo), + symbol: Symbol::HereFunction(foo), } .mock(), - FunctionDeclaration { + SymbolDeclaration { id: "bar", - symbol: FunctionSymbol::Here(bar), + symbol: Symbol::HereFunction(bar), } .mock(), - FunctionDeclaration { + SymbolDeclaration { id: "main", - symbol: FunctionSymbol::Here(main), + symbol: Symbol::HereFunction(main), } .mock(), ]; let module = Module { - functions: funcs, + symbols, imports: vec![], }; - let mut modules = vec![(String::from("main"), module)].into_iter().collect(); + let mut state = State::new(vec![(String::from("main"), module)].into_iter().collect()); let mut checker = Checker::new(); assert!(checker - .check_module(&String::from("main"), &mut modules, &mut HashMap::new()) + .check_module(&String::from("main"), &mut state) .is_ok()); } @@ -1692,7 +2611,7 @@ mod tests { // should fail let foo_statements = vec![ Statement::For( - Variable::field_element("i").mock(), + absy::Variable::new("i", UnresolvedType::FieldElement.mock()).mock(), FieldPrime::from(0), FieldPrime::from(10), vec![], @@ -1709,16 +2628,19 @@ mod tests { let foo = Function { arguments: vec![], statements: foo_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); + let types = HashMap::new(); + let module_id = String::from(""); + let mut checker = Checker::new(); assert_eq!( - checker.check_function(foo), + checker.check_function(foo, &module_id, &types), Err(vec![Error { pos: Some((Position::mock(), Position::mock())), message: "Identifier \"i\" is undefined".to_string() @@ -1735,7 +2657,10 @@ mod tests { // should pass let for_statements = vec![ - Statement::Declaration(Variable::field_element("a").mock()).mock(), + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), Statement::Definition( Assignee::Identifier("a").mock(), Expression::Identifier("i").mock(), @@ -1744,7 +2669,7 @@ mod tests { ]; let foo_statements = vec![Statement::For( - Variable::field_element("i").mock(), + absy::Variable::new("i", UnresolvedType::FieldElement.mock()).mock(), FieldPrime::from(0), FieldPrime::from(10), for_statements, @@ -1769,9 +2694,9 @@ mod tests { let foo = Function { arguments: vec![], statements: foo_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); @@ -1785,8 +2710,14 @@ mod tests { }, }; + let types = HashMap::new(); + let module_id = String::from(""); + let mut checker = Checker::new(); - assert_eq!(checker.check_function(foo), Ok(foo_checked)); + assert_eq!( + checker.check_function(foo, &module_id, &types), + Ok(foo_checked) + ); } #[test] @@ -1797,7 +2728,10 @@ mod tests { // field a = foo() // should fail let bar_statements: Vec> = vec![ - Statement::Declaration(Variable::field_element("a").mock()).mock(), + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), Statement::MultipleDefinition( vec![Assignee::Identifier("a").mock()], Expression::FunctionCall("foo", vec![]).mock(), @@ -1818,16 +2752,19 @@ mod tests { let bar = Function { arguments: vec![], statements: bar_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); + let types = HashMap::new(); + let module_id = String::from(""); + let mut checker = new_with_args(HashSet::new(), 0, functions); assert_eq!( - checker.check_function(bar), + checker.check_function(bar, &module_id, &types), Err(vec![Error { pos: Some((Position::mock(), Position::mock())), message: @@ -1863,16 +2800,19 @@ mod tests { let bar = Function { arguments: vec![], statements: bar_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); + let types = HashMap::new(); + let module_id = String::from(""); + let mut checker = new_with_args(HashSet::new(), 0, functions); assert_eq!( - checker.check_function(bar), + checker.check_function(bar, &module_id, &types), Err(vec![Error { pos: Some((Position::mock(), Position::mock())), message: "Function definition for function foo with signature () -> (_) not found." @@ -1887,7 +2827,10 @@ mod tests { // field a = foo() // should fail let bar_statements: Vec> = vec![ - Statement::Declaration(Variable::field_element("a").mock()).mock(), + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), Statement::MultipleDefinition( vec![Assignee::Identifier("a").mock()], Expression::FunctionCall("foo", vec![]).mock(), @@ -1898,16 +2841,19 @@ mod tests { let bar = Function { arguments: vec![], statements: bar_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); + let types = HashMap::new(); + let module_id = String::from(""); + let mut checker = new_with_args(HashSet::new(), 0, HashSet::new()); assert_eq!( - checker.check_function(bar), + checker.check_function(bar, &module_id, &types), Err(vec![Error { pos: Some((Position::mock(), Position::mock())), @@ -1940,21 +2886,30 @@ mod tests { let foo = Function { arguments: vec![crate::absy::Parameter { - id: Variable::field_element("x").mock(), + id: absy::Variable::new("x", UnresolvedType::FieldElement.mock()).mock(), private: false, } .mock()], statements: foo_statements, - signature: Signature { - inputs: vec![Type::FieldElement], - outputs: vec![Type::FieldElement, Type::FieldElement], + signature: UnresolvedSignature { + inputs: vec![UnresolvedType::FieldElement.mock()], + outputs: vec![ + UnresolvedType::FieldElement.mock(), + UnresolvedType::FieldElement.mock(), + ], }, } .mock(); let main_statements: Vec> = vec![ - Statement::Declaration(Variable::field_element("a").mock()).mock(), - Statement::Declaration(Variable::field_element("b").mock()).mock(), + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), + Statement::Declaration( + absy::Variable::new("b", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), Statement::MultipleDefinition( vec![ Assignee::Identifier("a").mock(), @@ -1975,34 +2930,34 @@ mod tests { let main = Function { arguments: vec![], statements: main_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); let module = Module { - functions: vec![ - FunctionDeclaration { + symbols: vec![ + SymbolDeclaration { id: "foo", - symbol: FunctionSymbol::Here(foo), + symbol: Symbol::HereFunction(foo), } .mock(), - FunctionDeclaration { + SymbolDeclaration { id: "main", - symbol: FunctionSymbol::Here(main), + symbol: Symbol::HereFunction(main), } .mock(), ], imports: vec![], }; - let mut modules = vec![(String::from("main"), module)].into_iter().collect(); + let mut state = State::new(vec![(String::from("main"), module)].into_iter().collect()); let mut checker = new_with_args(HashSet::new(), 0, HashSet::new()); assert_eq!( - checker.check_module(&String::from("main"), &mut modules, &mut HashMap::new()), + checker.check_module(&String::from("main"), &mut state), Err(vec![Error { pos: Some((Position::mock(), Position::mock())), message: "Identifier \"x\" is undefined".to_string() @@ -2024,16 +2979,19 @@ mod tests { let bar = Function { arguments: vec![], statements: bar_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); + let types = HashMap::new(); + let module_id = String::from(""); + let mut checker = new_with_args(HashSet::new(), 0, HashSet::new()); assert_eq!( - checker.check_function(bar), + checker.check_function(bar, &module_id, &types), Err(vec![Error { pos: Some((Position::mock(), Position::mock())), @@ -2062,16 +3020,22 @@ mod tests { let bar = Function { arguments: vec![], statements: bar_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement, Type::FieldElement], + outputs: vec![ + UnresolvedType::FieldElement.mock(), + UnresolvedType::FieldElement.mock(), + ], }, } .mock(); + let types = HashMap::new(); + let module_id = String::from(""); + let mut checker = new_with_args(HashSet::new(), 0, HashSet::new()); assert_eq!( - checker.check_function(bar), + checker.check_function(bar, &module_id, &types), Err(vec![Error { pos: Some((Position::mock(), Position::mock())), message: "Identifier \"a\" is undefined".to_string() @@ -2089,8 +3053,14 @@ mod tests { // // should pass let bar_statements: Vec> = vec![ - Statement::Declaration(Variable::field_element("a").mock()).mock(), - Statement::Declaration(Variable::field_element("b").mock()).mock(), + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), + Statement::Declaration( + absy::Variable::new("b", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), Statement::MultipleDefinition( vec![ Assignee::Identifier("a").mock(), @@ -2149,9 +3119,9 @@ mod tests { let bar = Function { arguments: vec![], statements: bar_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); @@ -2165,109 +3135,13 @@ mod tests { }, }; + let types = HashMap::new(); + let module_id = String::from(""); + let mut checker = new_with_args(HashSet::new(), 0, functions); - assert_eq!(checker.check_function(bar), Ok(bar_checked)); - } - - #[test] - fn duplicate_function_declaration() { - // def foo(a, b): - // return 1 - // def foo(c, d): - // return 2 - // - // should fail - - let foo1_statements: Vec> = vec![Statement::Return( - ExpressionList { - expressions: vec![Expression::FieldConstant(FieldPrime::from(1)).mock()], - } - .mock(), - ) - .mock()]; - - let foo1_arguments = vec![ - crate::absy::Parameter { - id: Variable::field_element("a").mock(), - private: true, - } - .mock(), - crate::absy::Parameter { - id: Variable::field_element("b").mock(), - private: true, - } - .mock(), - ]; - - let foo2_statements: Vec> = vec![Statement::Return( - ExpressionList { - expressions: vec![Expression::FieldConstant(FieldPrime::from(1)).mock()], - } - .mock(), - ) - .mock()]; - - let foo2_arguments = vec![ - crate::absy::Parameter { - id: Variable::field_element("c").mock(), - private: true, - } - .mock(), - crate::absy::Parameter { - id: Variable::field_element("d").mock(), - private: true, - } - .mock(), - ]; - - let foo1 = Function { - arguments: foo1_arguments, - statements: foo1_statements, - signature: Signature { - inputs: vec![Type::FieldElement, Type::FieldElement], - outputs: vec![Type::FieldElement], - }, - } - .mock(); - - let foo2 = Function { - arguments: foo2_arguments, - statements: foo2_statements, - signature: Signature { - inputs: vec![Type::FieldElement, Type::FieldElement], - outputs: vec![Type::FieldElement], - }, - } - .mock(); - - let module = Module { - functions: vec![ - FunctionDeclaration { - id: "foo", - symbol: FunctionSymbol::Here(foo1), - } - .mock(), - FunctionDeclaration { - id: "foo", - symbol: FunctionSymbol::Here(foo2), - } - .mock(), - ], - imports: vec![], - }; - - let mut modules = vec![(String::from("main"), module)].into_iter().collect(); - - let mut checker = Checker::new(); assert_eq!( - checker.check_module(&String::from("main"), &mut modules, &mut HashMap::new()), - Err(vec![Error { - pos: Some((Position::mock(), Position::mock())), - - message: - "Duplicate definition for function foo with signature (field, field) -> (field)" - .to_string() - }]) + checker.check_function(bar, &module_id, &types), + Ok(bar_checked) ); } @@ -2288,7 +3162,7 @@ mod tests { .mock()]; let main1_arguments = vec![crate::absy::Parameter { - id: Variable::field_element("a").mock(), + id: absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), private: false, } .mock()]; @@ -2306,9 +3180,9 @@ mod tests { let main1 = Function { arguments: main1_arguments, statements: main1_statements, - signature: Signature { - inputs: vec![Type::FieldElement], - outputs: vec![Type::FieldElement], + signature: UnresolvedSignature { + inputs: vec![UnresolvedType::FieldElement.mock()], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); @@ -2316,28 +3190,28 @@ mod tests { let main2 = Function { arguments: main2_arguments, statements: main2_statements, - signature: Signature { + signature: UnresolvedSignature { inputs: vec![], - outputs: vec![Type::FieldElement], + outputs: vec![UnresolvedType::FieldElement.mock()], }, } .mock(); - let functions = vec![ - FunctionDeclaration { + let symbols = vec![ + SymbolDeclaration { id: "main", - symbol: FunctionSymbol::Here(main1), + symbol: Symbol::HereFunction(main1), } .mock(), - FunctionDeclaration { + SymbolDeclaration { id: "main", - symbol: FunctionSymbol::Here(main2), + symbol: Symbol::HereFunction(main2), } .mock(), ]; let main_module = Module { - functions: functions, + symbols, imports: vec![], }; @@ -2365,21 +3239,31 @@ mod tests { // // should fail + let types = HashMap::new(); + let module_id = String::from(""); let mut checker = Checker::new(); - let _: Result, Error> = checker.check_statement( - Statement::Declaration(Variable::field_element("a").mock()).mock(), - &vec![], + let _: Result, Vec> = checker.check_statement( + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), + &module_id, + &types, ); - let s2_checked: Result, Error> = checker.check_statement( - Statement::Declaration(Variable::field_element("a").mock()).mock(), - &vec![], + let s2_checked: Result, Vec> = checker.check_statement( + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), + &module_id, + &types, ); assert_eq!( s2_checked, - Err(Error { + Err(vec![Error { pos: Some((Position::mock(), Position::mock())), message: "Duplicate declaration for variable named a".to_string() - }) + }]) ); } @@ -2390,24 +3274,856 @@ mod tests { // // should fail + let types = HashMap::new(); + let module_id = String::from(""); + let mut checker = Checker::new(); - let _: Result, Error> = checker.check_statement( - Statement::Declaration(Variable::field_element("a").mock()).mock(), - &vec![], + let _: Result, Vec> = checker.check_statement( + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), + &module_id, + &types, ); - let s2_checked: Result, Error> = checker.check_statement( - Statement::Declaration(Variable::boolean("a").mock()).mock(), - &vec![], + let s2_checked: Result, Vec> = checker.check_statement( + Statement::Declaration(absy::Variable::new("a", UnresolvedType::Boolean.mock()).mock()) + .mock(), + &module_id, + &types, ); assert_eq!( s2_checked, - Err(Error { + Err(vec![Error { pos: Some((Position::mock(), Position::mock())), message: "Duplicate declaration for variable named a".to_string() - }) + }]) ); } + mod structs { + use super::*; + + /// helper function to create a module at location "" with a single symbol `Foo { foo: field }` + fn create_module_with_foo( + s: StructType<'static>, + ) -> (Checker<'static>, State<'static, FieldPrime>) { + let module_id = "".to_string(); + + let module: Module = Module { + imports: vec![], + symbols: vec![SymbolDeclaration { + id: "Foo", + symbol: Symbol::HereType(s.mock()), + } + .mock()], + }; + + let mut state = State::new(vec![(module_id.clone(), module)].into_iter().collect()); + + let mut checker = Checker::new(); + + checker.check_module(&module_id, &mut state).unwrap(); + + (checker, state) + } + + /// tests about declaring a type + mod declaration { + use super::*; + + #[test] + fn empty_def() { + // an empty struct should be allowed to be defined + let module_id = "".to_string(); + let types = HashMap::new(); + let declaration = StructType { fields: vec![] }.mock(); + + let expected_type = Type::Struct(vec![]); + + assert_eq!( + Checker::new().check_struct_type_declaration(declaration, &module_id, &types), + Ok(expected_type) + ); + } + + #[test] + fn valid_def() { + // a valid struct should be allowed to be defined + let module_id = "".to_string(); + let types = HashMap::new(); + let declaration = StructType { + fields: vec![ + StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock(), + StructField { + id: "bar", + ty: UnresolvedType::Boolean.mock(), + } + .mock(), + ], + } + .mock(); + + let expected_type = Type::Struct(vec![ + ("foo".to_string(), Type::FieldElement), + ("bar".to_string(), Type::Boolean), + ]); + + assert_eq!( + Checker::new().check_struct_type_declaration(declaration, &module_id, &types), + Ok(expected_type) + ); + } + + #[test] + fn preserve_order() { + // two structs with inverted members are not equal + let module_id = "".to_string(); + let types = HashMap::new(); + + let declaration0 = StructType { + fields: vec![ + StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock(), + StructField { + id: "bar", + ty: UnresolvedType::Boolean.mock(), + } + .mock(), + ], + } + .mock(); + + let declaration1 = StructType { + fields: vec![ + StructField { + id: "bar", + ty: UnresolvedType::Boolean.mock(), + } + .mock(), + StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock(), + ], + } + .mock(); + + assert!( + Checker::new().check_struct_type_declaration(declaration0, &module_id, &types) + != Checker::new().check_struct_type_declaration( + declaration1, + &module_id, + &types + ) + ); + } + + #[test] + fn duplicate_member_def() { + // definition of a struct with a duplicate member should be rejected + let module_id = "".to_string(); + let types = HashMap::new(); + + let declaration = StructType { + fields: vec![ + StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock(), + StructField { + id: "foo", + ty: UnresolvedType::Boolean.mock(), + } + .mock(), + ], + } + .mock(); + + assert_eq!( + Checker::new() + .check_struct_type_declaration(declaration, &module_id, &types) + .unwrap_err()[0] + .message, + "Duplicate key foo in struct definition" + ); + } + + #[test] + fn recursive() { + // a struct wrapping another struct should be allowed to be defined + + // struct Foo = { foo: field } + // struct Bar = { foo: Foo } + + let module_id = "".to_string(); + + let module: Module = Module { + imports: vec![], + symbols: vec![ + SymbolDeclaration { + id: "Foo", + symbol: Symbol::HereType( + StructType { + fields: vec![StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock()], + } + .mock(), + ), + } + .mock(), + SymbolDeclaration { + id: "Bar", + symbol: Symbol::HereType( + StructType { + fields: vec![StructField { + id: "foo", + ty: UnresolvedType::User("Foo".to_string()).mock(), + } + .mock()], + } + .mock(), + ), + } + .mock(), + ], + }; + + let mut state = State::new(vec![(module_id.clone(), module)].into_iter().collect()); + + assert!(Checker::new().check_module(&module_id, &mut state).is_ok()); + assert_eq!( + state + .types + .get(&"".to_string()) + .unwrap() + .get(&"Bar".to_string()) + .unwrap(), + &Type::Struct(vec![( + "foo".to_string(), + Type::Struct(vec![("foo".to_string(), Type::FieldElement)]) + )]) + ); + } + + #[test] + fn recursive_undefined() { + // a struct wrapping an undefined struct should be rejected + + // struct Bar = { foo: Foo } + + let module_id = "".to_string(); + + let module: Module = Module { + imports: vec![], + symbols: vec![SymbolDeclaration { + id: "Bar", + symbol: Symbol::HereType( + StructType { + fields: vec![StructField { + id: "foo", + ty: UnresolvedType::User("Foo".to_string()).mock(), + } + .mock()], + } + .mock(), + ), + } + .mock()], + }; + + let mut state = State::new(vec![(module_id.clone(), module)].into_iter().collect()); + + assert!(Checker::new().check_module(&module_id, &mut state).is_err()); + } + + #[test] + fn self_referential() { + // a struct wrapping itself should be rejected + + // struct Foo = { foo: Foo } + + let module_id = "".to_string(); + + let module: Module = Module { + imports: vec![], + symbols: vec![SymbolDeclaration { + id: "Foo", + symbol: Symbol::HereType( + StructType { + fields: vec![StructField { + id: "foo", + ty: UnresolvedType::User("Foo".to_string()).mock(), + } + .mock()], + } + .mock(), + ), + } + .mock()], + }; + + let mut state = State::new(vec![(module_id.clone(), module)].into_iter().collect()); + + assert!(Checker::new().check_module(&module_id, &mut state).is_err()); + } + + #[test] + fn cyclic() { + // A wrapping B wrapping A should be rejected + + // struct Foo = { bar: Bar } + // struct Bar = { foo: Foo } + + let module_id = "".to_string(); + + let module: Module = Module { + imports: vec![], + symbols: vec![ + SymbolDeclaration { + id: "Foo", + symbol: Symbol::HereType( + StructType { + fields: vec![StructField { + id: "bar", + ty: UnresolvedType::User("Bar".to_string()).mock(), + } + .mock()], + } + .mock(), + ), + } + .mock(), + SymbolDeclaration { + id: "Bar", + symbol: Symbol::HereType( + StructType { + fields: vec![StructField { + id: "foo", + ty: UnresolvedType::User("Foo".to_string()).mock(), + } + .mock()], + } + .mock(), + ), + } + .mock(), + ], + }; + + let mut state = State::new(vec![(module_id.clone(), module)].into_iter().collect()); + + assert!(Checker::new().check_module(&module_id, &mut state).is_err()); + } + } + + /// tests about using the defined type identifier + mod usage { + use super::*; + + #[test] + fn ty() { + // a defined type can be checked + // Foo { foo: field } + // Foo + + // an undefined type cannot be checked + // Bar + + let (checker, state) = create_module_with_foo(StructType { + fields: vec![StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock()], + }); + + assert_eq!( + checker.check_type( + UnresolvedType::User("Foo".to_string()).mock(), + &MODULE_ID.to_string(), + &state.types + ), + Ok(Type::Struct(vec![("foo".to_string(), Type::FieldElement)])) + ); + + assert_eq!( + checker + .check_type( + UnresolvedType::User("Bar".to_string()).mock(), + &MODULE_ID.to_string(), + &state.types + ) + .unwrap_err() + .message, + "Undefined type Bar" + ); + } + + #[test] + fn parameter() { + // a defined type can be used as parameter + + // an undefined type cannot be used as parameter + + let (checker, state) = create_module_with_foo(StructType { + fields: vec![StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock()], + }); + + assert_eq!( + checker.check_parameter( + absy::Parameter { + id: absy::Variable::new( + "a", + UnresolvedType::User("Foo".to_string()).mock(), + ) + .mock(), + private: true, + } + .mock(), + &MODULE_ID.to_string(), + &state.types, + ), + Ok(Parameter { + id: Variable::with_id_and_type( + "a".into(), + Type::Struct(vec![("foo".to_string(), Type::FieldElement)]) + ), + private: true + }) + ); + + assert_eq!( + checker + .check_parameter( + absy::Parameter { + id: absy::Variable::new( + "a", + UnresolvedType::User("Bar".to_string()).mock(), + ) + .mock(), + private: true, + } + .mock(), + &MODULE_ID.to_string(), + &state.types, + ) + .unwrap_err()[0] + .message, + "Undefined type Bar" + ); + } + + #[test] + fn variable_declaration() { + // a defined type can be used in a variable declaration + + // an undefined type cannot be used in a variable declaration + + let (mut checker, state) = create_module_with_foo(StructType { + fields: vec![StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock()], + }); + + assert_eq!( + checker.check_statement::( + Statement::Declaration( + absy::Variable::new( + "a", + UnresolvedType::User("Foo".to_string()).mock(), + ) + .mock() + ) + .mock(), + &MODULE_ID.to_string(), + &state.types, + ), + Ok(TypedStatement::Declaration(Variable::with_id_and_type( + "a".into(), + Type::Struct(vec![("foo".to_string(), Type::FieldElement)]) + ))) + ); + + assert_eq!( + checker + .check_parameter( + absy::Parameter { + id: absy::Variable::new( + "a", + UnresolvedType::User("Bar".to_string()).mock(), + ) + .mock(), + private: true, + } + .mock(), + &MODULE_ID.to_string(), + &state.types, + ) + .unwrap_err()[0] + .message, + "Undefined type Bar" + ); + } + } + + /// tests about accessing members + mod member { + use super::*; + + #[test] + fn valid() { + // accessing a member on a struct should succeed and return the right type + + // struct Foo = { foo: field } + // Foo { foo: 42 }.foo + + let (mut checker, state) = create_module_with_foo(StructType { + fields: vec![StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock()], + }); + + assert_eq!( + checker.check_expression( + Expression::Member( + box Expression::InlineStruct( + "Foo".to_string(), + vec![( + "foo", + Expression::FieldConstant(FieldPrime::from(42)).mock() + )] + ) + .mock(), + "foo".into() + ) + .mock(), + &MODULE_ID.to_string(), + &state.types + ), + Ok(FieldElementExpression::Member( + box StructExpressionInner::Value(vec![FieldElementExpression::Number( + FieldPrime::from(42) + ) + .into()]) + .annotate(vec![("foo".to_string(), Type::FieldElement)]), + "foo".to_string() + ) + .into()) + ); + } + + #[test] + fn invalid() { + // accessing an undefined member on a struct should fail + + // struct Foo = { foo: field } + // Foo { foo: 42 }.bar + + let (mut checker, state) = create_module_with_foo(StructType { + fields: vec![StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock()], + }); + + assert_eq!( + checker + .check_expression( + Expression::Member( + box Expression::InlineStruct( + "Foo".to_string(), + vec![( + "foo", + Expression::FieldConstant(FieldPrime::from(42)).mock() + )] + ) + .mock(), + "bar".into() + ) + .mock(), + &MODULE_ID.to_string(), + &state.types + ) + .unwrap_err() + .message, + "{foo: field} doesn\'t have member bar" + ); + } + } + + /// tests about defining struct instance inline + mod value { + use super::*; + + #[test] + fn wrong_name() { + // a A value cannot be defined with B as id, even if A and B have the same members + + let (mut checker, state) = create_module_with_foo(StructType { + fields: vec![StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock()], + }); + + assert_eq!( + checker + .check_expression( + Expression::InlineStruct( + "Bar".to_string(), + vec![( + "foo", + Expression::FieldConstant(FieldPrime::from(42)).mock() + )] + ) + .mock(), + &MODULE_ID.to_string(), + &state.types + ) + .unwrap_err() + .message, + "Undefined type Bar" + ); + } + + #[test] + fn valid() { + // a A value can be defined with members ordered as in the declaration of A + + // struct Foo = { foo: field, bar: bool } + // Foo foo = Foo { foo: 42, bar: true } + + let (mut checker, state) = create_module_with_foo(StructType { + fields: vec![ + StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock(), + StructField { + id: "bar", + ty: UnresolvedType::Boolean.mock(), + } + .mock(), + ], + }); + + assert_eq!( + checker.check_expression( + Expression::InlineStruct( + "Foo".to_string(), + vec![ + ( + "foo", + Expression::FieldConstant(FieldPrime::from(42)).mock() + ), + ("bar", Expression::BooleanConstant(true).mock()) + ] + ) + .mock(), + &MODULE_ID.to_string(), + &state.types + ), + Ok(StructExpressionInner::Value(vec![ + FieldElementExpression::Number(FieldPrime::from(42)).into(), + BooleanExpression::Value(true).into() + ]) + .annotate(vec![ + ("foo".to_string(), Type::FieldElement), + ("bar".to_string(), Type::Boolean) + ]) + .into()) + ); + } + + #[test] + fn shuffled() { + // a A value can be defined with shuffled members compared to the declaration of A + + // struct Foo = { foo: field, bar: bool } + // Foo foo = Foo { bar: true, foo: 42 } + + let (mut checker, state) = create_module_with_foo(StructType { + fields: vec![ + StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock(), + StructField { + id: "bar", + ty: UnresolvedType::Boolean.mock(), + } + .mock(), + ], + }); + + assert_eq!( + checker.check_expression( + Expression::InlineStruct( + "Foo".to_string(), + vec![ + ("bar", Expression::BooleanConstant(true).mock()), + ( + "foo", + Expression::FieldConstant(FieldPrime::from(42)).mock() + ) + ] + ) + .mock(), + &MODULE_ID.to_string(), + &state.types + ), + Ok(StructExpressionInner::Value(vec![ + FieldElementExpression::Number(FieldPrime::from(42)).into(), + BooleanExpression::Value(true).into() + ]) + .annotate(vec![ + ("foo".to_string(), Type::FieldElement), + ("bar".to_string(), Type::Boolean) + ]) + .into()) + ); + } + + #[test] + fn subset() { + // a A value cannot be defined with A as id but members being a subset of the declaration + + // struct Foo = { foo: field, bar: bool } + // Foo foo = Foo { foo: 42 } + + let (mut checker, state) = create_module_with_foo(StructType { + fields: vec![ + StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock(), + StructField { + id: "bar", + ty: UnresolvedType::Boolean.mock(), + } + .mock(), + ], + }); + + assert_eq!( + checker + .check_expression( + Expression::InlineStruct( + "Foo".to_string(), + vec![( + "foo", + Expression::FieldConstant(FieldPrime::from(42)).mock() + )] + ) + .mock(), + &MODULE_ID.to_string(), + &state.types + ) + .unwrap_err() + .message, + "Inline struct Foo {foo: 42} does not match Foo : {foo: field, bar: bool}" + ); + } + + #[test] + fn invalid() { + // a A value cannot be defined with A as id but members being different ids than the declaration + // a A value cannot be defined with A as id but members being different types than the declaration + + // struct Foo = { foo: field, bar: bool } + // Foo { foo: 42, baz: bool } // error + // Foo { foo: 42, baz: 42 } // error + + let (mut checker, state) = create_module_with_foo(StructType { + fields: vec![ + StructField { + id: "foo", + ty: UnresolvedType::FieldElement.mock(), + } + .mock(), + StructField { + id: "bar", + ty: UnresolvedType::Boolean.mock(), + } + .mock(), + ], + }); + + assert_eq!( + checker + .check_expression( + Expression::InlineStruct( + "Foo".to_string(), + vec![( + "baz", + Expression::BooleanConstant(true).mock() + ),( + "foo", + Expression::FieldConstant(FieldPrime::from(42)).mock() + )] + ) + .mock(), + &MODULE_ID.to_string(), + &state.types + ).unwrap_err() + .message, + "Member bar of struct Foo : {foo: field, bar: bool} not found in value Foo {baz: true, foo: 42}" + ); + + assert_eq!( + checker + .check_expression( + Expression::InlineStruct( + "Foo".to_string(), + vec![ + ( + "bar", + Expression::FieldConstant(FieldPrime::from(42)).mock() + ), + ( + "foo", + Expression::FieldConstant(FieldPrime::from(42)).mock() + ) + ] + ) + .mock(), + &MODULE_ID.to_string(), + &state.types + ) + .unwrap_err() + .message, + "Member bar of struct Foo has type bool, found 42 of type field" + ); + } + } + } + mod assignee { use super::*; @@ -2416,16 +4132,22 @@ mod tests { // a = 42 let a = Assignee::Identifier::("a").mock(); + let types = HashMap::new(); + let module_id = String::from(""); let mut checker: Checker = Checker::new(); checker .check_statement::( - Statement::Declaration(Variable::field_element("a").mock()).mock(), - &vec![], + Statement::Declaration( + absy::Variable::new("a", UnresolvedType::FieldElement.mock()).mock(), + ) + .mock(), + &module_id, + &types, ) .unwrap(); assert_eq!( - checker.check_assignee(a), + checker.check_assignee(a, &module_id, &types), Ok(TypedAssignee::Identifier( typed_absy::Variable::field_element("a".into()) )) @@ -2444,16 +4166,27 @@ mod tests { ) .mock(); + let types = HashMap::new(); + let module_id = String::from(""); + let mut checker: Checker = Checker::new(); checker .check_statement::( - Statement::Declaration(Variable::field_array("a", 33).mock()).mock(), - &vec![], + Statement::Declaration( + absy::Variable::new( + "a", + UnresolvedType::array(UnresolvedType::FieldElement.mock(), 33).mock(), + ) + .mock(), + ) + .mock(), + &module_id, + &types, ) .unwrap(); assert_eq!( - checker.check_assignee(a), + checker.check_assignee(a, &module_id, &types), Ok(TypedAssignee::Select( box TypedAssignee::Identifier(typed_absy::Variable::field_array( "a".into(), @@ -2482,19 +4215,31 @@ mod tests { ) .mock(); + let types = HashMap::new(); + let module_id = String::from(""); let mut checker: Checker = Checker::new(); checker .check_statement::( Statement::Declaration( - Variable::array("a", Type::array(Type::FieldElement, 33), 42).mock(), + absy::Variable::new( + "a", + UnresolvedType::array( + UnresolvedType::array(UnresolvedType::FieldElement.mock(), 33) + .mock(), + 42, + ) + .mock(), + ) + .mock(), ) .mock(), - &vec![], + &module_id, + &types, ) .unwrap(); assert_eq!( - checker.check_assignee(a), + checker.check_assignee(a, &module_id, &types), Ok(TypedAssignee::Select( box TypedAssignee::Select( box TypedAssignee::Identifier(typed_absy::Variable::array( diff --git a/zokrates_core/src/static_analysis/constrain_inputs.rs b/zokrates_core/src/static_analysis/constrain_inputs.rs new file mode 100644 index 00000000..0fa6dfbd --- /dev/null +++ b/zokrates_core/src/static_analysis/constrain_inputs.rs @@ -0,0 +1,134 @@ +//! Add runtime boolean checks on user inputs +//! +//! Example: +//! ```zokrates +//! struct Foo { +//! bar: bool +//! } +//! +//! def main(Foo f) -> (): +//! f.bar == f.bar && f.bar +//! return +//! ``` +//! +//! Becomes +//! +//! ```zokrates +//! struct Foo { +//! bar: bool +//! } +//! +//! def main(Foo f) -> (): +//! f.bar == f.bar && f.bar +//! return +//! ``` +//! +//! @file constrain_inputs.rs +//! @author Thibaut Schaeffer +//! @date 2019 + +use crate::typed_absy::folder::Folder; +use crate::typed_absy::types::Type; +use crate::typed_absy::*; +use zokrates_field::field::Field; + +pub struct InputConstrainer<'ast, T: Field> { + constraints: Vec>, +} + +impl<'ast, T: Field> InputConstrainer<'ast, T> { + fn new() -> Self { + InputConstrainer { + constraints: vec![], + } + } + + pub fn constrain(p: TypedProgram) -> TypedProgram { + InputConstrainer::new().fold_program(p) + } + + fn constrain_expression(&mut self, e: TypedExpression<'ast, T>) { + match e { + TypedExpression::FieldElement(_) => {} + TypedExpression::Boolean(b) => self.constraints.push(TypedStatement::Condition( + b.clone().into(), + BooleanExpression::And(box b.clone(), box b).into(), + )), + TypedExpression::Array(a) => { + for i in 0..a.size() { + let e = match a.inner_type() { + Type::FieldElement => FieldElementExpression::select( + a.clone(), + FieldElementExpression::Number(T::from(i)), + ) + .into(), + Type::Boolean => BooleanExpression::select( + a.clone(), + FieldElementExpression::Number(T::from(i)), + ) + .into(), + Type::Array(..) => ArrayExpression::select( + a.clone(), + FieldElementExpression::Number(T::from(i)), + ) + .into(), + Type::Struct(..) => StructExpression::select( + a.clone(), + FieldElementExpression::Number(T::from(i)), + ) + .into(), + }; + + self.constrain_expression(e); + } + } + TypedExpression::Struct(s) => { + for (id, ty) in s.ty() { + let e = match ty { + Type::FieldElement => { + FieldElementExpression::member(s.clone(), id.clone()).into() + } + Type::Boolean => BooleanExpression::member(s.clone(), id.clone()).into(), + Type::Array(..) => ArrayExpression::member(s.clone(), id.clone()).into(), + Type::Struct(..) => StructExpression::member(s.clone(), id.clone()).into(), + }; + + self.constrain_expression(e); + } + } + } + } +} + +impl<'ast, T: Field> Folder<'ast, T> for InputConstrainer<'ast, T> { + fn fold_parameter(&mut self, p: Parameter<'ast>) -> Parameter<'ast> { + let v = p.id.clone(); + + let e = match v.get_type() { + Type::FieldElement => FieldElementExpression::Identifier(v.id).into(), + Type::Boolean => BooleanExpression::Identifier(v.id).into(), + Type::Struct(members) => StructExpressionInner::Identifier(v.id) + .annotate(members) + .into(), + Type::Array(box ty, size) => ArrayExpressionInner::Identifier(v.id) + .annotate(ty, size) + .into(), + }; + + self.constrain_expression(e); + + p + } + + fn fold_function(&mut self, f: TypedFunction<'ast, T>) -> TypedFunction<'ast, T> { + TypedFunction { + arguments: f + .arguments + .into_iter() + .map(|a| self.fold_parameter(a)) + .collect(), + statements: self.constraints.drain(..).chain(f.statements).collect(), + ..f + } + } +} diff --git a/zokrates_core/src/static_analysis/inline.rs b/zokrates_core/src/static_analysis/inline.rs index 3ee75cae..c079201a 100644 --- a/zokrates_core/src/static_analysis/inline.rs +++ b/zokrates_core/src/static_analysis/inline.rs @@ -17,8 +17,8 @@ //! where any call in `main` must be to `_SHA_256_ROUND` or `_UNPACK` use std::collections::HashMap; +use typed_absy::types::{FunctionKey, MemberId, Type}; use typed_absy::{folder::*, *}; -use types::{FunctionKey, Type}; use zokrates_field::field::Field; /// An inliner @@ -260,12 +260,36 @@ impl<'ast, T: Field> Folder<'ast, T> for Inliner<'ast, T> { e => fold_array_expression_inner(self, ty, size, e), } } + + fn fold_struct_expression_inner( + &mut self, + ty: &Vec<(MemberId, Type)>, + e: StructExpressionInner<'ast, T>, + ) -> StructExpressionInner<'ast, T> { + match e { + StructExpressionInner::FunctionCall(key, exps) => { + let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect(); + + match self.try_inline_call(&key, exps) { + Ok(mut ret) => match ret.pop().unwrap() { + TypedExpression::Struct(e) => e.into_inner(), + _ => unreachable!(), + }, + Err((key, expressions)) => { + StructExpressionInner::FunctionCall(key, expressions) + } + } + } + // default + e => fold_struct_expression_inner(self, ty, e), + } + } } #[cfg(test)] mod tests { use super::*; - use types::{FunctionKey, Signature, Type}; + use typed_absy::types::{FunctionKey, Signature, Type}; use zokrates_field::field::FieldPrime; #[test] diff --git a/zokrates_core/src/static_analysis/mod.rs b/zokrates_core/src/static_analysis/mod.rs index 879dc65c..e2c2e10d 100644 --- a/zokrates_core/src/static_analysis/mod.rs +++ b/zokrates_core/src/static_analysis/mod.rs @@ -4,11 +4,13 @@ //! @author Thibaut Schaeffer //! @date 2018 +mod constrain_inputs; mod flat_propagation; mod inline; mod propagation; mod unroll; +use self::constrain_inputs::InputConstrainer; use self::inline::Inliner; use self::propagation::Propagator; use self::unroll::Unroller; @@ -28,6 +30,8 @@ impl<'ast, T: Field> Analyse for TypedProgram<'ast, T> { let r = Inliner::inline(r); // propagate let r = Propagator::propagate(r); + // constrain inputs + let r = InputConstrainer::constrain(r); r } } diff --git a/zokrates_core/src/static_analysis/propagation.rs b/zokrates_core/src/static_analysis/propagation.rs index 19a43dbe..2f056cff 100644 --- a/zokrates_core/src/static_analysis/propagation.rs +++ b/zokrates_core/src/static_analysis/propagation.rs @@ -8,7 +8,7 @@ use crate::typed_absy::folder::*; use crate::typed_absy::*; use std::collections::HashMap; use std::convert::TryFrom; -use types::Type; +use typed_absy::types::{MemberId, Type}; use zokrates_field::field::Field; pub struct Propagator<'ast, T: Field> { @@ -35,6 +35,10 @@ fn is_constant<'ast, T: Field>(e: &TypedExpression<'ast, T>) -> bool { ArrayExpressionInner::Value(v) => v.iter().all(|e| is_constant(e)), _ => false, }, + TypedExpression::Struct(a) => match a.as_inner() { + StructExpressionInner::Value(v) => v.iter().all(|e| is_constant(e)), + _ => false, + }, _ => false, } } @@ -71,6 +75,9 @@ impl<'ast, T: Field> Folder<'ast, T> for Propagator<'ast, T> { TypedStatement::Definition(TypedAssignee::Select(..), _) => { unreachable!("array updates should have been replaced with full array redef") } + TypedStatement::Definition(TypedAssignee::Member(..), _) => { + unreachable!("struct update should have been replaced with full struct redef") + } // propagate lhs and rhs for conditions TypedStatement::Condition(e1, e2) => { // could stop execution here if condition is known to fail @@ -224,6 +231,24 @@ impl<'ast, T: Field> Folder<'ast, T> for Propagator<'ast, T> { } } } + FieldElementExpression::Member(box s, m) => { + let s = self.fold_struct_expression(s); + + let members = match s.get_type() { + Type::Struct(members) => members, + _ => unreachable!(), + }; + + match s.into_inner() { + StructExpressionInner::Value(v) => { + match members.iter().zip(v).find(|(id, _)| id.0 == m).unwrap().1 { + TypedExpression::FieldElement(s) => s, + _ => unreachable!(), + } + } + inner => FieldElementExpression::Member(box inner.annotate(members), m), + } + } e => fold_field_expression(self, e), } } @@ -302,10 +327,124 @@ impl<'ast, T: Field> Folder<'ast, T> for Propagator<'ast, T> { c => ArrayExpressionInner::IfElse(box c, box consequence, box alternative), } } + ArrayExpressionInner::Member(box s, m) => { + let s = self.fold_struct_expression(s); + + let members = match s.get_type() { + Type::Struct(members) => members, + _ => unreachable!(), + }; + + match s.into_inner() { + StructExpressionInner::Value(v) => { + match members.iter().zip(v).find(|(id, _)| id.0 == m).unwrap().1 { + TypedExpression::Array(a) => a.into_inner(), + _ => unreachable!(), + } + } + inner => ArrayExpressionInner::Member(box inner.annotate(members), m), + } + } e => fold_array_expression_inner(self, ty, size, e), } } + fn fold_struct_expression_inner( + &mut self, + ty: &Vec<(MemberId, Type)>, + e: StructExpressionInner<'ast, T>, + ) -> StructExpressionInner<'ast, T> { + match e { + StructExpressionInner::Identifier(id) => { + match self + .constants + .get(&TypedAssignee::Identifier(Variable::struc( + id.clone(), + ty.clone(), + ))) { + Some(e) => match e { + TypedExpression::Struct(e) => e.as_inner().clone(), + _ => panic!("constant stored for an array should be an array"), + }, + None => StructExpressionInner::Identifier(id), + } + } + StructExpressionInner::Select(box array, box index) => { + let array = self.fold_array_expression(array); + let index = self.fold_field_expression(index); + + let inner_type = array.inner_type().clone(); + let size = array.size(); + + match (array.into_inner(), index) { + (ArrayExpressionInner::Value(v), FieldElementExpression::Number(n)) => { + let n_as_usize = n.to_dec_string().parse::().unwrap(); + if n_as_usize < size { + StructExpression::try_from(v[n_as_usize].clone()) + .unwrap() + .into_inner() + } else { + unreachable!( + "out of bounds index ({} >= {}) found during static analysis", + n_as_usize, size + ); + } + } + (ArrayExpressionInner::Identifier(id), FieldElementExpression::Number(n)) => { + match self.constants.get(&TypedAssignee::Select( + box TypedAssignee::Identifier(Variable::array( + id.clone(), + inner_type.clone(), + size, + )), + box FieldElementExpression::Number(n.clone()).into(), + )) { + Some(e) => match e { + TypedExpression::Struct(e) => e.clone().into_inner(), + _ => unreachable!(""), + }, + None => StructExpressionInner::Select( + box ArrayExpressionInner::Identifier(id).annotate(inner_type, size), + box FieldElementExpression::Number(n), + ), + } + } + (a, i) => { + StructExpressionInner::Select(box a.annotate(inner_type, size), box i) + } + } + } + StructExpressionInner::IfElse(box condition, box consequence, box alternative) => { + let consequence = self.fold_struct_expression(consequence); + let alternative = self.fold_struct_expression(alternative); + match self.fold_boolean_expression(condition) { + BooleanExpression::Value(true) => consequence.into_inner(), + BooleanExpression::Value(false) => alternative.into_inner(), + c => StructExpressionInner::IfElse(box c, box consequence, box alternative), + } + } + StructExpressionInner::Member(box s, m) => { + let s = self.fold_struct_expression(s); + + let members = match s.get_type() { + Type::Struct(members) => members, + _ => unreachable!(), + }; + + match s.into_inner() { + StructExpressionInner::Value(v) => { + match members.iter().zip(v).find(|(id, _)| id.0 == m).unwrap().1 { + TypedExpression::Struct(s) => s.into_inner(), + _ => unreachable!(), + } + } + inner => StructExpressionInner::Member(box inner.annotate(members), m), + } + } + e => fold_struct_expression_inner(self, ty, e), + } + } + fn fold_boolean_expression( &mut self, e: BooleanExpression<'ast, T>, @@ -430,6 +569,24 @@ impl<'ast, T: Field> Folder<'ast, T> for Propagator<'ast, T> { c => BooleanExpression::IfElse(box c, box consequence, box alternative), } } + BooleanExpression::Member(box s, m) => { + let s = self.fold_struct_expression(s); + + let members = match s.get_type() { + Type::Struct(members) => members, + _ => unreachable!(), + }; + + match s.into_inner() { + StructExpressionInner::Value(v) => { + match members.iter().zip(v).find(|(id, _)| id.0 == m).unwrap().1 { + TypedExpression::Boolean(s) => s, + _ => unreachable!(), + } + } + inner => BooleanExpression::Member(box inner.annotate(members), m), + } + } e => fold_boolean_expression(self, e), } } diff --git a/zokrates_core/src/static_analysis/unroll.rs b/zokrates_core/src/static_analysis/unroll.rs index f95928eb..5180045c 100644 --- a/zokrates_core/src/static_analysis/unroll.rs +++ b/zokrates_core/src/static_analysis/unroll.rs @@ -5,8 +5,8 @@ //! @date 2018 use crate::typed_absy::folder::*; +use crate::typed_absy::types::{MemberId, Type}; use crate::typed_absy::*; -use crate::types::Type; use std::collections::HashMap; use std::collections::HashSet; use zokrates_field::field::Field; @@ -47,7 +47,7 @@ impl<'ast> Unroller<'ast> { fn choose_many( base: TypedExpression<'ast, T>, - indices: Vec>, + indices: Vec>, new_expression: TypedExpression<'ast, T>, statements: &mut HashSet>, ) -> TypedExpression<'ast, T> { @@ -55,131 +55,256 @@ impl<'ast> Unroller<'ast> { match indices.len() { 0 => new_expression, - _ => { - let base = match base { - TypedExpression::Array(e) => e, - e => unreachable!("can't take an element on a {}", e.get_type()), - }; + _ => match base { + TypedExpression::Array(base) => { + let inner_ty = base.inner_type(); + let size = base.size(); - let inner_ty = base.inner_type(); - let size = base.size(); + let head = indices.remove(0); + let tail = indices; - let head = indices.pop().unwrap(); - let tail = indices; - - statements.insert(TypedStatement::Condition( - BooleanExpression::Lt( - box head.clone(), - box FieldElementExpression::Number(T::from(size)), - ) - .into(), - BooleanExpression::Value(true).into(), - )); - - ArrayExpressionInner::Value( - (0..size) - .map(|i| match inner_ty { - Type::Array(..) => ArrayExpression::if_else( - BooleanExpression::Eq( - box FieldElementExpression::Number(T::from(i)), + match head { + Access::Select(head) => { + statements.insert(TypedStatement::Condition( + BooleanExpression::Lt( box head.clone(), - ), - match Self::choose_many( - ArrayExpression::select( - base.clone(), - FieldElementExpression::Number(T::from(i)), - ) - .into(), - tail.clone(), - new_expression.clone(), - statements, - ) { - TypedExpression::Array(e) => e, - e => unreachable!( - "the interior was expected to be an array, was {}", - e.get_type() - ), - }, - ArrayExpression::select( - base.clone(), - FieldElementExpression::Number(T::from(i)), - ), + box FieldElementExpression::Number(T::from(size)), + ) + .into(), + BooleanExpression::Value(true).into(), + )); + + ArrayExpressionInner::Value( + (0..size) + .map(|i| match inner_ty { + Type::Array(..) => ArrayExpression::if_else( + BooleanExpression::Eq( + box FieldElementExpression::Number(T::from(i)), + box head.clone(), + ), + match Self::choose_many( + ArrayExpression::select( + base.clone(), + FieldElementExpression::Number(T::from(i)), + ) + .into(), + tail.clone(), + new_expression.clone(), + statements, + ) { + TypedExpression::Array(e) => e, + e => unreachable!( + "the interior was expected to be an array, was {}", + e.get_type() + ), + }, + ArrayExpression::select( + base.clone(), + FieldElementExpression::Number(T::from(i)), + ), + ) + .into(), + Type::Struct(..) => StructExpression::if_else( + BooleanExpression::Eq( + box FieldElementExpression::Number(T::from(i)), + box head.clone(), + ), + match Self::choose_many( + StructExpression::select( + base.clone(), + FieldElementExpression::Number(T::from(i)), + ) + .into(), + tail.clone(), + new_expression.clone(), + statements, + ) { + TypedExpression::Struct(e) => e, + e => unreachable!( + "the interior was expected to be a struct, was {}", + e.get_type() + ), + }, + StructExpression::select( + base.clone(), + FieldElementExpression::Number(T::from(i)), + ), + ) + .into(), + Type::FieldElement => FieldElementExpression::if_else( + BooleanExpression::Eq( + box FieldElementExpression::Number(T::from(i)), + box head.clone(), + ), + match Self::choose_many( + FieldElementExpression::select( + base.clone(), + FieldElementExpression::Number(T::from(i)), + ) + .into(), + tail.clone(), + new_expression.clone(), + statements, + ) { + TypedExpression::FieldElement(e) => e, + e => unreachable!( + "the interior was expected to be a field, was {}", + e.get_type() + ), + }, + FieldElementExpression::select( + base.clone(), + FieldElementExpression::Number(T::from(i)), + ), + ) + .into(), + Type::Boolean => BooleanExpression::if_else( + BooleanExpression::Eq( + box FieldElementExpression::Number(T::from(i)), + box head.clone(), + ), + match Self::choose_many( + BooleanExpression::select( + base.clone(), + FieldElementExpression::Number(T::from(i)), + ) + .into(), + tail.clone(), + new_expression.clone(), + statements, + ) { + TypedExpression::Boolean(e) => e, + e => unreachable!( + "the interior was expected to be a boolean, was {}", + e.get_type() + ), + }, + BooleanExpression::select( + base.clone(), + FieldElementExpression::Number(T::from(i)), + ), + ) + .into(), + }) + .collect(), ) - .into(), - Type::FieldElement => FieldElementExpression::if_else( - BooleanExpression::Eq( - box FieldElementExpression::Number(T::from(i)), - box head.clone(), - ), - match Self::choose_many( - FieldElementExpression::select( - base.clone(), - FieldElementExpression::Number(T::from(i)), - ) - .into(), - tail.clone(), - new_expression.clone(), - statements, - ) { - TypedExpression::FieldElement(e) => e, - e => unreachable!( - "the interior was expected to be a field, was {}", - e.get_type() - ), - }, - FieldElementExpression::select( - base.clone(), - FieldElementExpression::Number(T::from(i)), - ), - ) - .into(), - Type::Boolean => BooleanExpression::if_else( - BooleanExpression::Eq( - box FieldElementExpression::Number(T::from(i)), - box head.clone(), - ), - match Self::choose_many( - BooleanExpression::select( - base.clone(), - FieldElementExpression::Number(T::from(i)), - ) - .into(), - tail.clone(), - new_expression.clone(), - statements, - ) { - TypedExpression::Boolean(e) => e, - e => unreachable!( - "the interior was expected to be a boolean, was {}", - e.get_type() - ), - }, - BooleanExpression::select( - base.clone(), - FieldElementExpression::Number(T::from(i)), - ), - ) - .into(), - }) - .collect(), - ) - .annotate(inner_ty.clone(), size) - .into() - } + .annotate(inner_ty.clone(), size) + .into() + } + Access::Member(..) => unreachable!("can't get a member from an array"), + } + } + TypedExpression::Struct(base) => { + let members = match base.get_type() { + Type::Struct(members) => members.clone(), + _ => unreachable!(), + }; + + let head = indices.remove(0); + let tail = indices; + + match head { + Access::Member(head) => StructExpressionInner::Value( + members + .clone() + .into_iter() + .map(|(id, t)| match t { + Type::FieldElement => { + if id == head { + Self::choose_many( + FieldElementExpression::member( + base.clone(), + head.clone(), + ) + .into(), + tail.clone(), + new_expression.clone(), + statements, + ) + } else { + FieldElementExpression::member(base.clone(), id.clone()) + .into() + } + } + Type::Boolean => { + if id == head { + Self::choose_many( + BooleanExpression::member( + base.clone(), + head.clone(), + ) + .into(), + tail.clone(), + new_expression.clone(), + statements, + ) + } else { + BooleanExpression::member(base.clone(), id.clone()) + .into() + } + } + Type::Array(..) => { + if id == head { + Self::choose_many( + ArrayExpression::member(base.clone(), head.clone()) + .into(), + tail.clone(), + new_expression.clone(), + statements, + ) + } else { + ArrayExpression::member(base.clone(), id.clone()).into() + } + } + Type::Struct(..) => { + if id == head { + Self::choose_many( + StructExpression::member( + base.clone(), + head.clone(), + ) + .into(), + tail.clone(), + new_expression.clone(), + statements, + ) + } else { + StructExpression::member(base.clone(), id.clone()) + .into() + } + } + }) + .collect(), + ) + .annotate(members) + .into(), + Access::Select(..) => unreachable!("can't get a element from a struct"), + } + } + e => unreachable!("can't make an access on a {}", e.get_type()), + }, } } } -/// Turn an assignee into its representation as a base variable and a list of indices +#[derive(Clone, Debug)] +enum Access<'ast, T: Field> { + Select(FieldElementExpression<'ast, T>), + Member(MemberId), +} +/// Turn an assignee into its representation as a base variable and a list accesses /// a[2][3][4] -> (a, [2, 3, 4]) -fn linear<'ast, T: Field>( - a: TypedAssignee<'ast, T>, -) -> (Variable, Vec>) { +fn linear<'ast, T: Field>(a: TypedAssignee<'ast, T>) -> (Variable, Vec>) { match a { TypedAssignee::Identifier(v) => (v, vec![]), TypedAssignee::Select(box array, box index) => { let (v, mut indices) = linear(array); - indices.push(index); + indices.push(Access::Select(index)); + (v, indices) + } + TypedAssignee::Member(box s, m) => { + let (v, mut indices) = linear(s); + indices.push(Access::Member(m)); (v, indices) } } @@ -206,12 +331,20 @@ impl<'ast, T: Field> Folder<'ast, T> for Unroller<'ast> { .annotate(ty, size) .into() } + Type::Struct(members) => { + StructExpressionInner::Identifier(variable.id.clone().into()) + .annotate(members) + .into() + } }; let base = self.fold_expression(base); let indices = indices .into_iter() - .map(|i| self.fold_field_expression(i)) + .map(|a| match a { + Access::Select(i) => Access::Select(self.fold_field_expression(i)), + a => a, + }) .collect(); let mut range_checks = HashSet::new(); @@ -298,7 +431,12 @@ mod tests { let index = FieldElementExpression::Number(FieldPrime::from(1)); - let a1 = Unroller::choose_many(a0.clone().into(), vec![index], e, &mut HashSet::new()); + let a1 = Unroller::choose_many( + a0.clone().into(), + vec![Access::Select(index)], + e, + &mut HashSet::new(), + ); // a[1] = 42 // -> a = [0 == 1 ? 42 : a[0], 1 == 1 ? 42 : a[1], 2 == 1 ? 42 : a[2]] @@ -356,7 +494,7 @@ mod tests { let a1 = Unroller::choose_many( a0.clone().into(), - vec![index], + vec![Access::Select(index)], e.clone().into(), &mut HashSet::new(), ); @@ -414,8 +552,8 @@ mod tests { let e = FieldElementExpression::Number(FieldPrime::from(42)); let indices = vec![ - FieldElementExpression::Number(FieldPrime::from(0)), - FieldElementExpression::Number(FieldPrime::from(0)), + Access::Select(FieldElementExpression::Number(FieldPrime::from(0))), + Access::Select(FieldElementExpression::Number(FieldPrime::from(0))), ]; let a1 = Unroller::choose_many( @@ -528,7 +666,7 @@ mod tests { #[cfg(test)] mod statement { use super::*; - use crate::types::{FunctionKey, Signature}; + use crate::typed_absy::types::{FunctionKey, Signature}; #[test] fn for_loop() { @@ -709,7 +847,7 @@ mod tests { #[test] fn incremental_multiple_definition() { - use crate::types::Type; + use crate::typed_absy::types::Type; // field a // a = 2 diff --git a/zokrates_core/src/typed_absy/folder.rs b/zokrates_core/src/typed_absy/folder.rs index 1293eeea..d25069f5 100644 --- a/zokrates_core/src/typed_absy/folder.rs +++ b/zokrates_core/src/typed_absy/folder.rs @@ -48,6 +48,7 @@ pub trait Folder<'ast, T: Field>: Sized { box self.fold_assignee(a), box self.fold_field_expression(index), ), + TypedAssignee::Member(box s, m) => TypedAssignee::Member(box self.fold_assignee(s), m), } } @@ -60,6 +61,7 @@ pub trait Folder<'ast, T: Field>: Sized { TypedExpression::FieldElement(e) => self.fold_field_expression(e).into(), TypedExpression::Boolean(e) => self.fold_boolean_expression(e).into(), TypedExpression::Array(e) => self.fold_array_expression(e).into(), + TypedExpression::Struct(e) => self.fold_struct_expression(e).into(), } } @@ -67,6 +69,13 @@ pub trait Folder<'ast, T: Field>: Sized { fold_array_expression(self, e) } + fn fold_struct_expression( + &mut self, + e: StructExpression<'ast, T>, + ) -> StructExpression<'ast, T> { + fold_struct_expression(self, e) + } + fn fold_expression_list( &mut self, es: TypedExpressionList<'ast, T>, @@ -105,6 +114,13 @@ pub trait Folder<'ast, T: Field>: Sized { ) -> ArrayExpressionInner<'ast, T> { fold_array_expression_inner(self, ty, size, e) } + fn fold_struct_expression_inner( + &mut self, + ty: &Vec<(MemberId, Type)>, + e: StructExpressionInner<'ast, T>, + ) -> StructExpressionInner<'ast, T> { + fold_struct_expression_inner(self, ty, e) + } } pub fn fold_module<'ast, T: Field, F: Folder<'ast, T>>( @@ -178,6 +194,10 @@ pub fn fold_array_expression_inner<'ast, T: Field, F: Folder<'ast, T>>( box f.fold_array_expression(alternative), ) } + ArrayExpressionInner::Member(box s, id) => { + let s = f.fold_struct_expression(s); + ArrayExpressionInner::Member(box s, id) + } ArrayExpressionInner::Select(box array, box index) => { let array = f.fold_array_expression(array); let index = f.fold_field_expression(index); @@ -186,6 +206,39 @@ pub fn fold_array_expression_inner<'ast, T: Field, F: Folder<'ast, T>>( } } +pub fn fold_struct_expression_inner<'ast, T: Field, F: Folder<'ast, T>>( + f: &mut F, + _: &Vec<(MemberId, Type)>, + e: StructExpressionInner<'ast, T>, +) -> StructExpressionInner<'ast, T> { + match e { + StructExpressionInner::Identifier(id) => StructExpressionInner::Identifier(f.fold_name(id)), + StructExpressionInner::Value(exprs) => { + StructExpressionInner::Value(exprs.into_iter().map(|e| f.fold_expression(e)).collect()) + } + StructExpressionInner::FunctionCall(id, exps) => { + let exps = exps.into_iter().map(|e| f.fold_expression(e)).collect(); + StructExpressionInner::FunctionCall(id, exps) + } + StructExpressionInner::IfElse(box condition, box consequence, box alternative) => { + StructExpressionInner::IfElse( + box f.fold_boolean_expression(condition), + box f.fold_struct_expression(consequence), + box f.fold_struct_expression(alternative), + ) + } + StructExpressionInner::Member(box s, id) => { + let s = f.fold_struct_expression(s); + StructExpressionInner::Member(box s, id) + } + StructExpressionInner::Select(box array, box index) => { + let array = f.fold_array_expression(array); + let index = f.fold_field_expression(index); + StructExpressionInner::Select(box array, box index) + } + } +} + pub fn fold_field_expression<'ast, T: Field, F: Folder<'ast, T>>( f: &mut F, e: FieldElementExpression<'ast, T>, @@ -230,6 +283,10 @@ pub fn fold_field_expression<'ast, T: Field, F: Folder<'ast, T>>( let exps = exps.into_iter().map(|e| f.fold_expression(e)).collect(); FieldElementExpression::FunctionCall(key, exps) } + FieldElementExpression::Member(box s, id) => { + let s = f.fold_struct_expression(s); + FieldElementExpression::Member(box s, id) + } FieldElementExpression::Select(box array, box index) => { let array = f.fold_array_expression(array); let index = f.fold_field_expression(index); @@ -295,6 +352,10 @@ pub fn fold_boolean_expression<'ast, T: Field, F: Folder<'ast, T>>( let alt = f.fold_boolean_expression(alt); BooleanExpression::IfElse(box cond, box cons, box alt) } + BooleanExpression::Member(box s, id) => { + let s = f.fold_struct_expression(s); + BooleanExpression::Member(box s, id) + } BooleanExpression::Select(box array, box index) => { let array = f.fold_array_expression(array); let index = f.fold_field_expression(index); @@ -332,6 +393,16 @@ pub fn fold_array_expression<'ast, T: Field, F: Folder<'ast, T>>( } } +pub fn fold_struct_expression<'ast, T: Field, F: Folder<'ast, T>>( + f: &mut F, + e: StructExpression<'ast, T>, +) -> StructExpression<'ast, T> { + StructExpression { + inner: f.fold_struct_expression_inner(&e.ty, e.inner), + ..e + } +} + pub fn fold_function_symbol<'ast, T: Field, F: Folder<'ast, T>>( f: &mut F, s: TypedFunctionSymbol<'ast, T>, diff --git a/zokrates_core/src/typed_absy/mod.rs b/zokrates_core/src/typed_absy/mod.rs index c2dc66dd..31f51267 100644 --- a/zokrates_core/src/typed_absy/mod.rs +++ b/zokrates_core/src/typed_absy/mod.rs @@ -7,11 +7,14 @@ pub mod folder; mod parameter; +pub mod types; mod variable; pub use crate::typed_absy::parameter::Parameter; +pub use crate::typed_absy::types::Type; pub use crate::typed_absy::variable::Variable; -use crate::types::{FunctionKey, Signature, Type}; + +use crate::typed_absy::types::{FunctionKey, MemberId, Signature}; use embed::FlatEmbed; use std::collections::HashMap; use std::convert::TryFrom; @@ -72,7 +75,7 @@ impl<'ast, T: Field> fmt::Display for TypedProgram<'ast, T> { } } -/// A +/// A typed program as a collection of functions. Types have been resolved during semantic checking. #[derive(PartialEq, Clone)] pub struct TypedModule<'ast, T: Field> { /// Functions of the program @@ -239,6 +242,7 @@ pub enum TypedAssignee<'ast, T: Field> { Box>, Box>, ), + Member(Box>, MemberId), } impl<'ast, T: Field> Typed for TypedAssignee<'ast, T> { @@ -252,6 +256,15 @@ impl<'ast, T: Field> Typed for TypedAssignee<'ast, T> { _ => unreachable!("an array element should only be defined over arrays"), } } + TypedAssignee::Member(ref s, ref m) => { + let s_type = s.get_type(); + match s_type { + Type::Struct(members) => { + members.iter().find(|(id, _)| id == m).unwrap().1.clone() + } + _ => unreachable!("a struct access should only be defined over structs"), + } + } } } } @@ -261,6 +274,7 @@ impl<'ast, T: Field> fmt::Debug for TypedAssignee<'ast, T> { match *self { TypedAssignee::Identifier(ref s) => write!(f, "{}", s.id), TypedAssignee::Select(ref a, ref e) => write!(f, "{}[{}]", a, e), + TypedAssignee::Member(ref s, ref m) => write!(f, "{}.{}", s, m), } } } @@ -362,6 +376,7 @@ pub enum TypedExpression<'ast, T: Field> { Boolean(BooleanExpression<'ast, T>), FieldElement(FieldElementExpression<'ast, T>), Array(ArrayExpression<'ast, T>), + Struct(StructExpression<'ast, T>), } impl<'ast, T: Field> From> for TypedExpression<'ast, T> { @@ -382,12 +397,19 @@ impl<'ast, T: Field> From> for TypedExpression<'ast, T> } } +impl<'ast, T: Field> From> for TypedExpression<'ast, T> { + fn from(e: StructExpression<'ast, T>) -> TypedExpression { + TypedExpression::Struct(e) + } +} + impl<'ast, T: Field> fmt::Display for TypedExpression<'ast, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { TypedExpression::Boolean(ref e) => write!(f, "{}", e), TypedExpression::FieldElement(ref e) => write!(f, "{}", e), - TypedExpression::Array(ref e) => write!(f, "{}", e.inner), + TypedExpression::Array(ref e) => write!(f, "{}", e), + TypedExpression::Struct(ref s) => write!(f, "{}", s), } } } @@ -398,6 +420,7 @@ impl<'ast, T: Field> fmt::Debug for TypedExpression<'ast, T> { TypedExpression::Boolean(ref e) => write!(f, "{:?}", e), TypedExpression::FieldElement(ref e) => write!(f, "{:?}", e), TypedExpression::Array(ref e) => write!(f, "{:?}", e), + TypedExpression::Struct(ref s) => write!(f, "{}", s), } } } @@ -414,12 +437,57 @@ impl<'ast, T: Field> fmt::Debug for ArrayExpression<'ast, T> { } } +impl<'ast, T: Field> fmt::Display for StructExpression<'ast, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.inner { + StructExpressionInner::Identifier(ref var) => write!(f, "{}", var), + StructExpressionInner::Value(ref values) => write!( + f, + "{{{}}}", + self.ty + .iter() + .map(|(id, _)| id) + .zip(values.iter()) + .map(|(id, o)| format!("{}: {}", id, o.to_string())) + .collect::>() + .join(", ") + ), + StructExpressionInner::FunctionCall(ref key, ref p) => { + write!(f, "{}(", key.id,)?; + for (i, param) in p.iter().enumerate() { + write!(f, "{}", param)?; + if i < p.len() - 1 { + write!(f, ", ")?; + } + } + write!(f, ")") + } + StructExpressionInner::IfElse(ref condition, ref consequent, ref alternative) => { + write!( + f, + "if {} then {} else {} fi", + condition, consequent, alternative + ) + } + StructExpressionInner::Member(ref struc, ref id) => write!(f, "{}.{}", struc, id), + StructExpressionInner::Select(ref id, ref index) => write!(f, "{}[{}]", id, index), + } + } +} + +impl<'ast, T: Field> fmt::Debug for StructExpression<'ast, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.inner) + } +} + impl<'ast, T: Field> Typed for TypedExpression<'ast, T> { fn get_type(&self) -> Type { match *self { TypedExpression::Boolean(ref e) => e.get_type(), TypedExpression::FieldElement(ref e) => e.get_type(), TypedExpression::Array(ref e) => e.get_type(), + TypedExpression::Struct(ref s) => s.get_type(), } } } @@ -430,6 +498,12 @@ impl<'ast, T: Field> Typed for ArrayExpression<'ast, T> { } } +impl<'ast, T: Field> Typed for StructExpression<'ast, T> { + fn get_type(&self) -> Type { + Type::Struct(self.ty.clone()) + } +} + impl<'ast, T: Field> Typed for FieldElementExpression<'ast, T> { fn get_type(&self) -> Type { Type::FieldElement @@ -490,6 +564,7 @@ pub enum FieldElementExpression<'ast, T: Field> { Box>, ), FunctionCall(FunctionKey<'ast>, Vec>), + Member(Box>, MemberId), Select( Box>, Box>, @@ -539,6 +614,7 @@ pub enum BooleanExpression<'ast, T: Field> { Box>, Box>, ), + Member(Box>, MemberId), Select( Box>, Box>, @@ -567,6 +643,7 @@ pub enum ArrayExpressionInner<'ast, T: Field> { Box>, Box>, ), + Member(Box>, MemberId), Select( Box>, Box>, @@ -601,6 +678,49 @@ impl<'ast, T: Field> ArrayExpression<'ast, T> { } } +#[derive(Clone, PartialEq, Hash, Eq)] +pub struct StructExpression<'ast, T: Field> { + ty: Vec<(MemberId, Type)>, + inner: StructExpressionInner<'ast, T>, +} + +impl<'ast, T: Field> StructExpression<'ast, T> { + pub fn ty(&self) -> &Vec<(MemberId, Type)> { + &self.ty + } + + pub fn as_inner(&self) -> &StructExpressionInner<'ast, T> { + &self.inner + } + + pub fn into_inner(self) -> StructExpressionInner<'ast, T> { + self.inner + } +} + +#[derive(Clone, PartialEq, Hash, Eq)] +pub enum StructExpressionInner<'ast, T: Field> { + Identifier(Identifier<'ast>), + Value(Vec>), + FunctionCall(FunctionKey<'ast>, Vec>), + IfElse( + Box>, + Box>, + Box>, + ), + Member(Box>, MemberId), + Select( + Box>, + Box>, + ), +} + +impl<'ast, T: Field> StructExpressionInner<'ast, T> { + pub fn annotate(self, ty: Vec<(MemberId, Type)>) -> StructExpression<'ast, T> { + StructExpression { ty, inner: self } + } +} + // Downcasts // Due to the fact that we keep TypedExpression simple, we end up with ArrayExpressionInner::Value whose elements are any TypedExpression, but we enforce by // construction that these elements are of the type declared in the corresponding ArrayExpression. As we know this by construction, we can downcast the TypedExpression to the correct type @@ -640,6 +760,17 @@ impl<'ast, T: Field> TryFrom> for ArrayExpression<'ast, } } +impl<'ast, T: Field> TryFrom> for StructExpression<'ast, T> { + type Error = (); + + fn try_from(te: TypedExpression<'ast, T>) -> Result, Self::Error> { + match te { + TypedExpression::Struct(e) => Ok(e), + _ => Err(()), + } + } +} + impl<'ast, T: Field> fmt::Display for FieldElementExpression<'ast, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -667,6 +798,7 @@ impl<'ast, T: Field> fmt::Display for FieldElementExpression<'ast, T> { } write!(f, ")") } + FieldElementExpression::Member(ref struc, ref id) => write!(f, "{}.{}", struc, id), FieldElementExpression::Select(ref id, ref index) => write!(f, "{}[{}]", id, index), } } @@ -691,6 +823,7 @@ impl<'ast, T: Field> fmt::Display for BooleanExpression<'ast, T> { "if {} then {} else {} fi", condition, consequent, alternative ), + BooleanExpression::Member(ref struc, ref id) => write!(f, "{}.{}", struc, id), BooleanExpression::Select(ref id, ref index) => write!(f, "{}[{}]", id, index), } } @@ -724,6 +857,7 @@ impl<'ast, T: Field> fmt::Display for ArrayExpressionInner<'ast, T> { "if {} then {} else {} fi", condition, consequent, alternative ), + ArrayExpressionInner::Member(ref s, ref id) => write!(f, "{}.{}", s, id), ArrayExpressionInner::Select(ref id, ref index) => write!(f, "{}[{}]", id, index), } } @@ -759,6 +893,9 @@ impl<'ast, T: Field> fmt::Debug for FieldElementExpression<'ast, T> { f.debug_list().entries(p.iter()).finish()?; write!(f, ")") } + FieldElementExpression::Member(ref struc, ref id) => { + write!(f, "Member({:?}, {:?})", struc, id) + } FieldElementExpression::Select(ref id, ref index) => { write!(f, "Select({:?}, {:?})", id, index) } @@ -781,6 +918,9 @@ impl<'ast, T: Field> fmt::Debug for ArrayExpressionInner<'ast, T> { "IfElse({:?}, {:?}, {:?})", condition, consequent, alternative ), + ArrayExpressionInner::Member(ref struc, ref id) => { + write!(f, "Member({:?}, {:?})", struc, id) + } ArrayExpressionInner::Select(ref id, ref index) => { write!(f, "Select({:?}, {:?})", id, index) } @@ -788,6 +928,33 @@ impl<'ast, T: Field> fmt::Debug for ArrayExpressionInner<'ast, T> { } } +impl<'ast, T: Field> fmt::Debug for StructExpressionInner<'ast, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + StructExpressionInner::Identifier(ref var) => write!(f, "{:?}", var), + StructExpressionInner::Value(ref values) => write!(f, "{:?}", values), + StructExpressionInner::FunctionCall(ref i, ref p) => { + write!(f, "FunctionCall({:?}, (", i)?; + f.debug_list().entries(p.iter()).finish()?; + write!(f, ")") + } + StructExpressionInner::IfElse(ref condition, ref consequent, ref alternative) => { + write!( + f, + "IfElse({:?}, {:?}, {:?})", + condition, consequent, alternative + ) + } + StructExpressionInner::Member(ref struc, ref id) => { + write!(f, "Member({:?}, {:?})", struc, id) + } + StructExpressionInner::Select(ref id, ref index) => { + write!(f, "Select({:?}, {:?})", id, index) + } + } + } +} + impl<'ast, T: Field> fmt::Display for TypedExpressionList<'ast, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -857,6 +1024,17 @@ impl<'ast, T: Field> IfElse<'ast, T> for ArrayExpression<'ast, T> { } } +impl<'ast, T: Field> IfElse<'ast, T> for StructExpression<'ast, T> { + fn if_else( + condition: BooleanExpression<'ast, T>, + consequence: Self, + alternative: Self, + ) -> Self { + let ty = consequence.ty().clone(); + StructExpressionInner::IfElse(box condition, box consequence, box alternative).annotate(ty) + } +} + pub trait Select<'ast, T: Field> { fn select(array: ArrayExpression<'ast, T>, index: FieldElementExpression<'ast, T>) -> Self; } @@ -883,3 +1061,68 @@ impl<'ast, T: Field> Select<'ast, T> for ArrayExpression<'ast, T> { ArrayExpressionInner::Select(box array, box index).annotate(*ty, size) } } + +impl<'ast, T: Field> Select<'ast, T> for StructExpression<'ast, T> { + fn select(array: ArrayExpression<'ast, T>, index: FieldElementExpression<'ast, T>) -> Self { + let members = match array.inner_type().clone() { + Type::Struct(members) => members, + _ => unreachable!(), + }; + + StructExpressionInner::Select(box array, box index).annotate(members) + } +} + +pub trait Member<'ast, T: Field> { + fn member(s: StructExpression<'ast, T>, member_id: MemberId) -> Self; +} + +impl<'ast, T: Field> Member<'ast, T> for FieldElementExpression<'ast, T> { + fn member(s: StructExpression<'ast, T>, member_id: MemberId) -> Self { + FieldElementExpression::Member(box s, member_id) + } +} + +impl<'ast, T: Field> Member<'ast, T> for BooleanExpression<'ast, T> { + fn member(s: StructExpression<'ast, T>, member_id: MemberId) -> Self { + BooleanExpression::Member(box s, member_id) + } +} + +impl<'ast, T: Field> Member<'ast, T> for ArrayExpression<'ast, T> { + fn member(s: StructExpression<'ast, T>, member_id: MemberId) -> Self { + let members = s.ty().clone(); + + let ty = members + .into_iter() + .find(|(id, _)| *id == member_id) + .unwrap() + .1; + + let (ty, size) = match ty { + Type::Array(box ty, size) => (ty, size), + _ => unreachable!(), + }; + + ArrayExpressionInner::Member(box s, member_id).annotate(ty, size) + } +} + +impl<'ast, T: Field> Member<'ast, T> for StructExpression<'ast, T> { + fn member(s: StructExpression<'ast, T>, member_id: MemberId) -> Self { + let members = s.ty().clone(); + + let ty = members + .into_iter() + .find(|(id, _)| *id == member_id) + .unwrap() + .1; + + let members = match ty { + Type::Struct(members) => members, + _ => unreachable!(), + }; + + StructExpressionInner::Member(box s, member_id).annotate(members) + } +} diff --git a/zokrates_core/src/typed_absy/parameter.rs b/zokrates_core/src/typed_absy/parameter.rs index 7437a31e..277ac537 100644 --- a/zokrates_core/src/typed_absy/parameter.rs +++ b/zokrates_core/src/typed_absy/parameter.rs @@ -1,4 +1,3 @@ -use crate::absy; use crate::typed_absy::Variable; use std::fmt; @@ -30,12 +29,3 @@ impl<'ast> fmt::Debug for Parameter<'ast> { write!(f, "Parameter(variable: {:?})", self.id) } } - -impl<'ast> From> for Parameter<'ast> { - fn from(p: absy::Parameter<'ast>) -> Parameter { - Parameter { - private: p.private, - id: p.id.value.into(), - } - } -} diff --git a/zokrates_core/src/typed_absy/types.rs b/zokrates_core/src/typed_absy/types.rs new file mode 100644 index 00000000..f4c4a7c0 --- /dev/null +++ b/zokrates_core/src/typed_absy/types.rs @@ -0,0 +1,290 @@ +use std::fmt; + +pub type Identifier<'ast> = &'ast str; + +pub type MemberId = String; + +#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)] +pub enum Type { + FieldElement, + Boolean, + Array(Box, usize), + Struct(Vec<(MemberId, Type)>), +} + +impl fmt::Display for Type { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Type::FieldElement => write!(f, "field"), + Type::Boolean => write!(f, "bool"), + Type::Array(ref ty, ref size) => write!(f, "{}[{}]", ty, size), + Type::Struct(ref members) => write!( + f, + "{{{}}}", + members + .iter() + .map(|(id, t)| format!("{}: {}", id, t)) + .collect::>() + .join(", ") + ), + } + } +} + +impl fmt::Debug for Type { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Type::FieldElement => write!(f, "field"), + Type::Boolean => write!(f, "bool"), + Type::Array(ref ty, ref size) => write!(f, "{}[{}]", ty, size), + Type::Struct(ref members) => write!( + f, + "{{{}}}", + members + .iter() + .map(|(id, t)| format!("{}: {}", id, t)) + .collect::>() + .join(", ") + ), + } + } +} + +impl Type { + pub fn array(ty: Type, size: usize) -> Self { + Type::Array(box ty, size) + } + + fn to_slug(&self) -> String { + match self { + Type::FieldElement => String::from("f"), + Type::Boolean => String::from("b"), + Type::Array(box ty, size) => format!("{}[{}]", ty.to_slug(), size), + Type::Struct(members) => format!( + "{{{}}}", + members + .iter() + .map(|(id, ty)| format!("{}:{}", id, ty)) + .collect::>() + .join(",") + ), + } + } + + // the number of field elements the type maps to + pub fn get_primitive_count(&self) -> usize { + match self { + Type::FieldElement => 1, + Type::Boolean => 1, + Type::Array(ty, size) => size * ty.get_primitive_count(), + Type::Struct(members) => members.iter().map(|(_, t)| t.get_primitive_count()).sum(), + } + } +} + +pub type FunctionIdentifier<'ast> = &'ast str; + +#[derive(PartialEq, Eq, Hash, Debug, Clone)] +pub struct FunctionKey<'ast> { + pub id: FunctionIdentifier<'ast>, + pub signature: Signature, +} + +impl<'ast> FunctionKey<'ast> { + pub fn with_id>>(id: S) -> Self { + FunctionKey { + id: id.into(), + signature: Signature::new(), + } + } + + pub fn signature(mut self, signature: Signature) -> Self { + self.signature = signature; + self + } + + pub fn id>>(mut self, id: S) -> Self { + self.id = id.into(); + self + } + + pub fn to_slug(&self) -> String { + format!("{}_{}", self.id, self.signature.to_slug()) + } +} + +pub use self::signature::Signature; + +pub mod signature { + use super::*; + use std::fmt; + + #[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Ord, PartialOrd)] + pub struct Signature { + pub inputs: Vec, + pub outputs: Vec, + } + + impl fmt::Debug for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "Signature(inputs: {:?}, outputs: {:?})", + self.inputs, self.outputs + ) + } + } + + impl fmt::Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "(")?; + for (i, t) in self.inputs.iter().enumerate() { + write!(f, "{}", t)?; + if i < self.inputs.len() - 1 { + write!(f, ", ")?; + } + } + write!(f, ") -> (")?; + for (i, t) in self.outputs.iter().enumerate() { + write!(f, "{}", t)?; + if i < self.outputs.len() - 1 { + write!(f, ", ")?; + } + } + write!(f, ")") + } + } + + impl Signature { + /// Returns a slug for a signature, with the following encoding: + /// i{inputs}o{outputs} where {inputs} and {outputs} each encode a list of types. + /// A list of types is encoded by compressing sequences of the same type like so: + /// + /// [field, field, field] -> 3f + /// [field] -> f + /// [field, bool, field] -> fbf + /// [field, field, bool, field] -> 2fbf + /// + pub fn to_slug(&self) -> String { + let to_slug = |types| { + let mut res = vec![]; + for t in types { + let len = res.len(); + if len == 0 { + res.push((1, t)) + } else { + if res[len - 1].1 == t { + res[len - 1].0 += 1; + } else { + res.push((1, t)) + } + } + } + res.into_iter() + .map(|(n, t): (usize, &Type)| { + let mut r = String::new(); + + if n > 1 { + r.push_str(&format!("{}", n)); + } + r.push_str(&t.to_slug()); + r + }) + .fold(String::new(), |mut acc, e| { + acc.push_str(&e); + acc + }) + }; + + format!("i{}o{}", to_slug(&self.inputs), to_slug(&self.outputs)) + } + + pub fn new() -> Signature { + Signature { + inputs: vec![], + outputs: vec![], + } + } + + pub fn inputs(mut self, inputs: Vec) -> Self { + self.inputs = inputs; + self + } + + pub fn outputs(mut self, outputs: Vec) -> Self { + self.outputs = outputs; + self + } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn signature() { + let s = Signature::new() + .inputs(vec![Type::FieldElement, Type::Boolean]) + .outputs(vec![Type::Boolean]); + + assert_eq!(s.to_string(), String::from("(field, bool) -> (bool)")); + } + + #[test] + fn slug_0() { + let s = Signature::new().inputs(vec![]).outputs(vec![]); + + assert_eq!(s.to_slug(), String::from("io")); + } + + #[test] + fn slug_1() { + let s = Signature::new() + .inputs(vec![Type::FieldElement, Type::Boolean]) + .outputs(vec![ + Type::FieldElement, + Type::FieldElement, + Type::Boolean, + Type::FieldElement, + ]); + + assert_eq!(s.to_slug(), String::from("ifbo2fbf")); + } + + #[test] + fn slug_2() { + let s = Signature::new() + .inputs(vec![ + Type::FieldElement, + Type::FieldElement, + Type::FieldElement, + ]) + .outputs(vec![Type::FieldElement, Type::Boolean, Type::FieldElement]); + + assert_eq!(s.to_slug(), String::from("i3fofbf")); + } + + #[test] + fn array_slug() { + let s = Signature::new() + .inputs(vec![ + Type::array(Type::FieldElement, 42), + Type::array(Type::FieldElement, 21), + ]) + .outputs(vec![]); + + assert_eq!(s.to_slug(), String::from("if[42]f[21]o")); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn array() { + let t = Type::Array(box Type::FieldElement, 42); + assert_eq!(t.get_primitive_count(), 42); + } +} diff --git a/zokrates_core/src/typed_absy/variable.rs b/zokrates_core/src/typed_absy/variable.rs index 1fbc2154..52814062 100644 --- a/zokrates_core/src/typed_absy/variable.rs +++ b/zokrates_core/src/typed_absy/variable.rs @@ -1,6 +1,5 @@ -use crate::absy; +use crate::typed_absy::types::{MemberId, Type}; use crate::typed_absy::Identifier; -use crate::types::Type; use std::fmt; #[derive(Clone, PartialEq, Hash, Eq)] @@ -27,6 +26,10 @@ impl<'ast> Variable<'ast> { Self::with_id_and_type(id, Type::array(ty, size)) } + pub fn struc(id: Identifier<'ast>, ty: Vec<(MemberId, Type)>) -> Variable<'ast> { + Self::with_id_and_type(id, Type::Struct(ty)) + } + pub fn with_id_and_type(id: Identifier<'ast>, _type: Type) -> Variable<'ast> { Variable { id, _type } } @@ -48,15 +51,15 @@ impl<'ast> fmt::Debug for Variable<'ast> { } } -impl<'ast> From> for Variable<'ast> { - fn from(v: absy::Variable) -> Variable { - Variable::with_id_and_type( - Identifier { - id: v.id, - version: 0, - stack: vec![], - }, - v._type, - ) - } -} +// impl<'ast> From> for Variable<'ast> { +// fn from(v: absy::Variable) -> Variable { +// Variable::with_id_and_type( +// Identifier { +// id: v.id, +// version: 0, +// stack: vec![], +// }, +// v._type, +// ) +// } +// } diff --git a/zokrates_core/src/types/mod.rs b/zokrates_core/src/types/mod.rs deleted file mode 100644 index 390f2243..00000000 --- a/zokrates_core/src/types/mod.rs +++ /dev/null @@ -1,111 +0,0 @@ -pub use crate::types::signature::Signature; -use std::fmt; - -pub type Identifier<'ast> = &'ast str; - -mod signature; - -#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum Type { - FieldElement, - Boolean, - Array(Box, usize), -} - -impl fmt::Display for Type { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Type::FieldElement => write!(f, "field"), - Type::Boolean => write!(f, "bool"), - Type::Array(ref ty, ref size) => write!(f, "{}[{}]", ty, size), - } - } -} - -impl fmt::Debug for Type { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Type::FieldElement => write!(f, "field"), - Type::Boolean => write!(f, "bool"), - Type::Array(ref ty, ref size) => write!(f, "{}[{}]", ty, size), - } - } -} - -impl Type { - pub fn array(ty: Type, size: usize) -> Self { - Type::Array(box ty, size) - } - - fn to_slug(&self) -> String { - match self { - Type::FieldElement => String::from("f"), - Type::Boolean => String::from("b"), - Type::Array(box ty, size) => format!("{}[{}]", ty.to_slug(), size), - } - } - - // the number of field elements the type maps to - pub fn get_primitive_count(&self) -> usize { - match self { - Type::FieldElement => 1, - Type::Boolean => 1, - Type::Array(ty, size) => size * ty.get_primitive_count(), - } - } -} - -#[derive(Clone, PartialEq, Hash, Eq)] -pub struct Variable<'ast> { - pub id: Identifier<'ast>, - pub _type: Type, -} - -impl<'ast> fmt::Display for Variable<'ast> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {}", self._type, self.id,) - } -} - -impl<'ast> fmt::Debug for Variable<'ast> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Variable(type: {:?}, id: {:?})", self._type, self.id,) - } -} - -pub type FunctionIdentifier<'ast> = &'ast str; - -#[derive(PartialEq, Eq, Hash, Debug, Clone)] -pub struct FunctionKey<'ast> { - pub id: FunctionIdentifier<'ast>, - pub signature: Signature, -} - -impl<'ast> FunctionKey<'ast> { - pub fn with_id>>(id: S) -> Self { - FunctionKey { - id: id.into(), - signature: Signature::new(), - } - } - - pub fn signature(mut self, signature: Signature) -> Self { - self.signature = signature; - self - } - - pub fn to_slug(&self) -> String { - format!("{}_{}", self.id, self.signature.to_slug()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn array() { - let t = Type::Array(box Type::FieldElement, 42); - assert_eq!(t.get_primitive_count(), 42); - } -} diff --git a/zokrates_core/src/types/signature.rs b/zokrates_core/src/types/signature.rs deleted file mode 100644 index b8306323..00000000 --- a/zokrates_core/src/types/signature.rs +++ /dev/null @@ -1,160 +0,0 @@ -use crate::types::Type; -use std::fmt; - -#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct Signature { - pub inputs: Vec, - pub outputs: Vec, -} - -impl fmt::Debug for Signature { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Signature(inputs: {:?}, outputs: {:?})", - self.inputs, self.outputs - ) - } -} - -impl fmt::Display for Signature { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "(")?; - for (i, t) in self.inputs.iter().enumerate() { - write!(f, "{}", t)?; - if i < self.inputs.len() - 1 { - write!(f, ", ")?; - } - } - write!(f, ") -> (")?; - for (i, t) in self.outputs.iter().enumerate() { - write!(f, "{}", t)?; - if i < self.outputs.len() - 1 { - write!(f, ", ")?; - } - } - write!(f, ")") - } -} - -impl Signature { - /// Returns a slug for a signature, with the following encoding: - /// i{inputs}o{outputs} where {inputs} and {outputs} each encode a list of types. - /// A list of types is encoded by compressing sequences of the same type like so: - /// - /// [field, field, field] -> 3f - /// [field] -> f - /// [field, bool, field] -> fbf - /// [field, field, bool, field] -> 2fbf - /// - pub fn to_slug(&self) -> String { - let to_slug = |types| { - let mut res = vec![]; - for t in types { - let len = res.len(); - if len == 0 { - res.push((1, t)) - } else { - if res[len - 1].1 == t { - res[len - 1].0 += 1; - } else { - res.push((1, t)) - } - } - } - res.into_iter() - .map(|(n, t): (usize, &Type)| { - let mut r = String::new(); - - if n > 1 { - r.push_str(&format!("{}", n)); - } - r.push_str(&t.to_slug()); - r - }) - .fold(String::new(), |mut acc, e| { - acc.push_str(&e); - acc - }) - }; - - format!("i{}o{}", to_slug(&self.inputs), to_slug(&self.outputs)) - } - - pub fn new() -> Signature { - Signature { - inputs: vec![], - outputs: vec![], - } - } - - pub fn inputs(mut self, inputs: Vec) -> Self { - self.inputs = inputs; - self - } - - pub fn outputs(mut self, outputs: Vec) -> Self { - self.outputs = outputs; - self - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn signature() { - let s = Signature::new() - .inputs(vec![Type::FieldElement, Type::Boolean]) - .outputs(vec![Type::Boolean]); - - assert_eq!(s.to_string(), String::from("(field, bool) -> (bool)")); - } - - #[test] - fn slug_0() { - let s = Signature::new().inputs(vec![]).outputs(vec![]); - - assert_eq!(s.to_slug(), String::from("io")); - } - - #[test] - fn slug_1() { - let s = Signature::new() - .inputs(vec![Type::FieldElement, Type::Boolean]) - .outputs(vec![ - Type::FieldElement, - Type::FieldElement, - Type::Boolean, - Type::FieldElement, - ]); - - assert_eq!(s.to_slug(), String::from("ifbo2fbf")); - } - - #[test] - fn slug_2() { - let s = Signature::new() - .inputs(vec![ - Type::FieldElement, - Type::FieldElement, - Type::FieldElement, - ]) - .outputs(vec![Type::FieldElement, Type::Boolean, Type::FieldElement]); - - assert_eq!(s.to_slug(), String::from("i3fofbf")); - } - - #[test] - fn array_slug() { - let s = Signature::new() - .inputs(vec![ - Type::array(Type::FieldElement, 42), - Type::array(Type::FieldElement, 21), - ]) - .outputs(vec![]); - - assert_eq!(s.to_slug(), String::from("if[42]f[21]o")); - } -} diff --git a/zokrates_core_test/tests/tests/arrays/identity.code b/zokrates_core_test/tests/tests/arrays/identity.code new file mode 100644 index 00000000..8c97930b --- /dev/null +++ b/zokrates_core_test/tests/tests/arrays/identity.code @@ -0,0 +1,2 @@ +def main(bool[3] a) -> (bool[3]): + return a \ No newline at end of file diff --git a/zokrates_core_test/tests/tests/arrays/identity.json b/zokrates_core_test/tests/tests/arrays/identity.json new file mode 100644 index 00000000..e6a36236 --- /dev/null +++ b/zokrates_core_test/tests/tests/arrays/identity.json @@ -0,0 +1,38 @@ +{ + "entry_point": "./tests/tests/arrays/identity.code", + "tests": [ + { + "input": { + "values": ["0", "0", "0"] + }, + "output": { + "Ok": { + "values": ["0", "0", "0"] + } + } + }, + { + "input": { + "values": ["1", "0", "1"] + }, + "output": { + "Ok": { + "values": ["1", "0", "1"] + } + } + }, + { + "input": { + "values": ["2", "1", "1"] + }, + "output": { + "Err": { + "UnsatisfiedConstraint": { + "left": "4", + "right": "2" + } + } + } + } + ] +} \ No newline at end of file diff --git a/zokrates_core_test/tests/tests/precedence.json b/zokrates_core_test/tests/tests/precedence.json new file mode 100644 index 00000000..6d9a678e --- /dev/null +++ b/zokrates_core_test/tests/tests/precedence.json @@ -0,0 +1,19 @@ +{ + "entry_point": "./tests/tests/precedence.zok", + "tests": [ + { + "input": { + "values": [ + "12" + ] + }, + "output": { + "Ok": { + "values": [ + "12" + ] + } + } + } + ] +} \ No newline at end of file diff --git a/zokrates_core_test/tests/tests/precedence.zok b/zokrates_core_test/tests/tests/precedence.zok new file mode 100644 index 00000000..48356e0d --- /dev/null +++ b/zokrates_core_test/tests/tests/precedence.zok @@ -0,0 +1,16 @@ +def main(field g) -> (field): + 9 == 1 + 2 * 2 ** 2 // Checks precedence of arithmetic operators (expecting transitiv behaviour) + 9 == 2 ** 2 * 2 + 1 + 7 == 2 ** 2 * 2 - 1 + 3 == 2 ** 2 / 2 + 1 + + field a = if 3 == 2 ** 2 / 2 + 1 && true then 1 else 0 fi // combines arithmetic with boolean operators + field b = if 3 == 3 && 4 < 5 then 1 else 0 fi // checks precedence of boolean operators + field c = if 4 < 5 && 3 == 3 then 1 else 0 fi + field d = if 4 > 5 && 2 >= 1 || 1 == 1 then 1 else 0 fi + field e = if 2 >= 1 && 4 > 5 || 1 == 1 then 1 else 0 fi + field f = if 1 < 2 && false || 4 < 5 && 2 >= 1 then 1 else 0 fi + + //check if all statements have evalutated to true + a * b * c * d * e * f == 1 + return g diff --git a/zokrates_core_test/tests/tests/structs/identity.code b/zokrates_core_test/tests/tests/structs/identity.code new file mode 100644 index 00000000..e5441761 --- /dev/null +++ b/zokrates_core_test/tests/tests/structs/identity.code @@ -0,0 +1,7 @@ +struct A { + field a + bool b +} + +def main(A a) -> (A): + return a \ No newline at end of file diff --git a/zokrates_core_test/tests/tests/structs/identity.json b/zokrates_core_test/tests/tests/structs/identity.json new file mode 100644 index 00000000..db4b136e --- /dev/null +++ b/zokrates_core_test/tests/tests/structs/identity.json @@ -0,0 +1,38 @@ +{ + "entry_point": "./tests/tests/structs/identity.code", + "tests": [ + { + "input": { + "values": ["42", "0"] + }, + "output": { + "Ok": { + "values": ["42", "0"] + } + } + }, + { + "input": { + "values": ["42", "1"] + }, + "output": { + "Ok": { + "values": ["42", "1"] + } + } + }, + { + "input": { + "values": ["42", "3"] + }, + "output": { + "Err": { + "UnsatisfiedConstraint": { + "left": "9", + "right": "3" + } + } + } + } + ] +} \ No newline at end of file diff --git a/zokrates_parser/src/lib.rs b/zokrates_parser/src/lib.rs index 96112e5d..a135024e 100644 --- a/zokrates_parser/src/lib.rs +++ b/zokrates_parser/src/lib.rs @@ -113,6 +113,26 @@ mod tests { }; } + #[test] + fn parse_single_def_to_multi() { + parses_to! { + parser: ZoKratesParser, + input: r#"a = foo() + "#, + rule: Rule::statement, + tokens: [ + statement(0, 22, [ + multi_assignment_statement(0, 9, [ + optionally_typed_identifier(0, 1, [ + identifier(0, 1) + ]), + identifier(4, 7), + ]) + ]) + ] + }; + } + #[test] fn parse_invalid_identifier() { fails_with! { @@ -125,6 +145,50 @@ mod tests { }; } + #[test] + fn parse_struct_def() { + parses_to! { + parser: ZoKratesParser, + input: "struct Foo { field foo\n field[2] bar } + ", + rule: Rule::ty_struct_definition, + tokens: [ + ty_struct_definition(0, 39, [ + identifier(7, 10), + struct_field(13, 22, [ + ty(13, 18, [ + ty_basic(13, 18, [ + ty_field(13, 18) + ]) + ]), + identifier(19, 22) + ]), + struct_field(24, 36, [ + ty(24, 33, [ + ty_array(24, 33, [ + ty_basic_or_struct(24, 29, [ + ty_basic(24, 29, [ + ty_field(24, 29) + ]) + ]), + expression(30, 31, [ + term(30, 31, [ + primary_expression(30, 31, [ + constant(30, 31, [ + decimal_number(30, 31) + ]) + ]) + ]) + ]) + ]) + ]), + identifier(33, 36) + ]) + ]) + ] + }; + } + #[test] fn parse_invalid_identifier_because_keyword() { fails_with! { diff --git a/zokrates_parser/src/zokrates.pest b/zokrates_parser/src/zokrates.pest index efa9411d..7b62f2e6 100644 --- a/zokrates_parser/src/zokrates.pest +++ b/zokrates_parser/src/zokrates.pest @@ -3,8 +3,11 @@ * Author: Jacob Eberhardt, Thibaut Schaeffer */ -file = { SOI ~ NEWLINE* ~ import_directive* ~ NEWLINE* ~ function_definition* ~ EOI } -import_directive = {"import" ~ "\"" ~ import_source ~ "\"" ~ ("as" ~ identifier)? ~ NEWLINE+} +file = { SOI ~ NEWLINE* ~ import_directive* ~ NEWLINE* ~ ty_struct_definition* ~ NEWLINE* ~ function_definition* ~ EOI } + +import_directive = { main_import_directive | from_import_directive } +from_import_directive = { "from" ~ "\"" ~ import_source ~ "\"" ~ "import" ~ identifier ~ ("as" ~ identifier)? ~ NEWLINE*} +main_import_directive = {"import" ~ "\"" ~ import_source ~ "\"" ~ ("as" ~ identifier)? ~ NEWLINE+} import_source = @{(!"\"" ~ ANY)*} function_definition = {"def" ~ identifier ~ "(" ~ parameter_list ~ ")" ~ "->" ~ "(" ~ type_list ~ ")" ~ ":" ~ NEWLINE* ~ statement* } @@ -15,10 +18,16 @@ parameter = {vis? ~ ty ~ identifier} ty_field = {"field"} ty_bool = {"bool"} ty_basic = { ty_field | ty_bool } -// (unidimensional for now) arrays of (basic for now) types -ty_array = { ty_basic ~ ("[" ~ expression ~ "]")+ } -ty = { ty_array | ty_basic } +ty_basic_or_struct = { ty_basic | ty_struct } +ty_array = { ty_basic_or_struct ~ ("[" ~ expression ~ "]")+ } +ty = { ty_array | ty_basic | ty_struct } type_list = _{(ty ~ ("," ~ ty)*)?} +// structs +ty_struct = { identifier } +// type definitions +ty_struct_definition = { "struct" ~ identifier ~ "{" ~ NEWLINE* ~ struct_field_list ~ NEWLINE* ~ "}" ~ NEWLINE* } +struct_field_list = _{(struct_field ~ (NEWLINE+ ~ struct_field)*)? } +struct_field = { ty ~ identifier } vis_private = {"private"} vis_public = {"public"} @@ -42,13 +51,13 @@ assignment_statement = {assignee ~ "=" ~ expression } // TODO: Is this optimal? expression_statement = {expression} optionally_typed_identifier_list = _{ optionally_typed_identifier ~ ("," ~ optionally_typed_identifier)* } -optionally_typed_identifier = { ty? ~ identifier } +optionally_typed_identifier = { (identifier) | (ty ~ identifier) } // we don't use { ty? ~ identifier } as with a single token, it gets parsed as `ty` but we want `identifier` // Expressions expression_list = _{(expression ~ ("," ~ expression)*)?} expression = { term ~ (op_binary ~ term)* } -term = { ("(" ~ expression ~ ")") | conditional_expression | postfix_expression | primary_expression | inline_array_expression | array_initializer_expression | unary_expression } +term = { ("(" ~ expression ~ ")") | inline_struct_expression | conditional_expression | postfix_expression | primary_expression | inline_array_expression | array_initializer_expression | unary_expression } spread = { "..." ~ expression } range = { from_expression? ~ ".." ~ to_expression? } from_expression = { expression } @@ -57,16 +66,21 @@ to_expression = { expression } conditional_expression = { "if" ~ expression ~ "then" ~ expression ~ "else" ~ expression ~ "fi"} postfix_expression = { identifier ~ access+ } // we force there to be at least one access, otherwise this matches single identifiers. Not sure that's what we want. -access = { array_access | call_access } +access = { array_access | call_access | member_access } array_access = { "[" ~ range_or_expression ~ "]" } call_access = { "(" ~ expression_list ~ ")" } +member_access = { "." ~ identifier } primary_expression = { identifier | constant } -inline_array_expression = { "[" ~ inline_array_inner ~ "]" } -inline_array_inner = _{(spread_or_expression ~ ("," ~ spread_or_expression)*)?} +inline_struct_expression = { identifier ~ "{" ~ NEWLINE* ~ inline_struct_member_list ~ NEWLINE* ~ "}" } +inline_struct_member_list = _{(inline_struct_member ~ ("," ~ NEWLINE* ~ inline_struct_member)*)? ~ ","? } +inline_struct_member = { identifier ~ ":" ~ expression } + +inline_array_expression = { "[" ~ NEWLINE* ~ inline_array_inner ~ NEWLINE* ~ "]" } +inline_array_inner = _{(spread_or_expression ~ ("," ~ NEWLINE* ~ spread_or_expression)*)?} spread_or_expression = { spread | expression } range_or_expression = { range | expression } array_initializer_expression = { "[" ~ expression ~ ";" ~ constant ~ "]" } @@ -75,7 +89,8 @@ unary_expression = { op_unary ~ term } // End Expressions -assignee = { identifier ~ ("[" ~ range_or_expression ~ "]")* } +assignee = { identifier ~ assignee_access* } +assignee_access = { array_access | member_access } identifier = @{ ((!keyword ~ ASCII_ALPHA) | (keyword ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* } constant = { decimal_number | boolean_literal } decimal_number = @{ "0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* } diff --git a/zokrates_pest_ast/src/lib.rs b/zokrates_pest_ast/src/lib.rs index 401256f7..78c4f17b 100644 --- a/zokrates_pest_ast/src/lib.rs +++ b/zokrates_pest_ast/src/lib.rs @@ -9,12 +9,13 @@ extern crate lazy_static; pub use ast::{ Access, ArrayAccess, ArrayInitializerExpression, ArrayType, AssertionStatement, Assignee, - AssignmentStatement, BasicType, BinaryExpression, BinaryOperator, BooleanType, CallAccess, - ConstantExpression, DefinitionStatement, Expression, FieldType, File, FromExpression, Function, - IdentifierExpression, ImportDirective, ImportSource, InlineArrayExpression, IterationStatement, + AssigneeAccess, AssignmentStatement, BasicOrStructType, BasicType, BinaryExpression, + BinaryOperator, CallAccess, ConstantExpression, DefinitionStatement, Expression, File, + FromExpression, Function, IdentifierExpression, ImportDirective, ImportSource, + InlineArrayExpression, InlineStructExpression, InlineStructMember, IterationStatement, MultiAssignmentStatement, Parameter, PostfixExpression, Range, RangeOrExpression, - ReturnStatement, Span, Spread, SpreadOrExpression, Statement, TernaryExpression, ToExpression, - Type, UnaryExpression, UnaryOperator, Visibility, + ReturnStatement, Span, Spread, SpreadOrExpression, Statement, StructDefinition, StructField, + TernaryExpression, ToExpression, Type, UnaryExpression, UnaryOperator, Visibility, }; mod ast { @@ -121,6 +122,9 @@ mod ast { Rule::postfix_expression => Expression::Postfix( PostfixExpression::from_pest(&mut pair.into_inner()).unwrap(), ), + Rule::inline_struct_expression => Expression::InlineStruct( + InlineStructExpression::from_pest(&mut pair.into_inner()).unwrap(), + ), Rule::inline_array_expression => Expression::InlineArray( InlineArrayExpression::from_pest(&mut pair.into_inner()).unwrap(), ), @@ -155,12 +159,31 @@ mod ast { #[pest_ast(rule(Rule::file))] pub struct File<'ast> { pub imports: Vec>, + pub structs: Vec>, pub functions: Vec>, pub eoi: EOI, #[pest_ast(outer())] pub span: Span<'ast>, } + #[derive(Debug, FromPest, PartialEq, Clone)] + #[pest_ast(rule(Rule::ty_struct_definition))] + pub struct StructDefinition<'ast> { + pub id: IdentifierExpression<'ast>, + pub fields: Vec>, + #[pest_ast(outer())] + pub span: Span<'ast>, + } + + #[derive(Debug, FromPest, PartialEq, Clone)] + #[pest_ast(rule(Rule::struct_field))] + pub struct StructField<'ast> { + pub ty: Type<'ast>, + pub id: IdentifierExpression<'ast>, + #[pest_ast(outer())] + pub span: Span<'ast>, + } + #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::function_definition))] pub struct Function<'ast> { @@ -174,13 +197,30 @@ mod ast { #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::import_directive))] - pub struct ImportDirective<'ast> { + pub enum ImportDirective<'ast> { + Main(MainImportDirective<'ast>), + From(FromImportDirective<'ast>), + } + + #[derive(Debug, FromPest, PartialEq, Clone)] + #[pest_ast(rule(Rule::main_import_directive))] + pub struct MainImportDirective<'ast> { pub source: ImportSource<'ast>, pub alias: Option>, #[pest_ast(outer())] pub span: Span<'ast>, } + #[derive(Debug, FromPest, PartialEq, Clone)] + #[pest_ast(rule(Rule::from_import_directive))] + pub struct FromImportDirective<'ast> { + pub source: ImportSource<'ast>, + pub symbol: IdentifierExpression<'ast>, + pub alias: Option>, + #[pest_ast(outer())] + pub span: Span<'ast>, + } + #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::import_source))] pub struct ImportSource<'ast> { @@ -193,33 +233,55 @@ mod ast { #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::ty))] pub enum Type<'ast> { - Basic(BasicType), + Basic(BasicType<'ast>), Array(ArrayType<'ast>), + Struct(StructType<'ast>), } #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::ty_basic))] - pub enum BasicType { - Field(FieldType), - Boolean(BooleanType), + pub enum BasicType<'ast> { + Field(FieldType<'ast>), + Boolean(BooleanType<'ast>), } #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::ty_field))] - pub struct FieldType; + pub struct FieldType<'ast> { + #[pest_ast(outer())] + pub span: Span<'ast>, + } #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::ty_array))] pub struct ArrayType<'ast> { - pub ty: BasicType, + pub ty: BasicOrStructType<'ast>, pub dimensions: Vec>, #[pest_ast(outer())] pub span: Span<'ast>, } + #[derive(Debug, FromPest, PartialEq, Clone)] + #[pest_ast(rule(Rule::ty_basic_or_struct))] + pub enum BasicOrStructType<'ast> { + Struct(StructType<'ast>), + Basic(BasicType<'ast>), + } + #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::ty_bool))] - pub struct BooleanType; + pub struct BooleanType<'ast> { + #[pest_ast(outer())] + pub span: Span<'ast>, + } + + #[derive(Debug, FromPest, PartialEq, Clone)] + #[pest_ast(rule(Rule::ty_struct))] + pub struct StructType<'ast> { + pub id: IdentifierExpression<'ast>, + #[pest_ast(outer())] + pub span: Span<'ast>, + } #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::parameter))] @@ -353,6 +415,7 @@ mod ast { Identifier(IdentifierExpression<'ast>), Constant(ConstantExpression<'ast>), InlineArray(InlineArrayExpression<'ast>), + InlineStruct(InlineStructExpression<'ast>), ArrayInitializer(ArrayInitializerExpression<'ast>), Unary(UnaryExpression<'ast>), } @@ -422,6 +485,24 @@ mod ast { pub span: Span<'ast>, } + #[derive(Debug, FromPest, PartialEq, Clone)] + #[pest_ast(rule(Rule::inline_struct_expression))] + pub struct InlineStructExpression<'ast> { + pub ty: IdentifierExpression<'ast>, + pub members: Vec>, + #[pest_ast(outer())] + pub span: Span<'ast>, + } + + #[derive(Debug, FromPest, PartialEq, Clone)] + #[pest_ast(rule(Rule::inline_struct_member))] + pub struct InlineStructMember<'ast> { + pub id: IdentifierExpression<'ast>, + pub expression: Expression<'ast>, + #[pest_ast(outer())] + pub span: Span<'ast>, + } + #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::array_initializer_expression))] pub struct ArrayInitializerExpression<'ast> { @@ -445,6 +526,14 @@ mod ast { pub enum Access<'ast> { Call(CallAccess<'ast>), Select(ArrayAccess<'ast>), + Member(MemberAccess<'ast>), + } + + #[derive(Debug, FromPest, PartialEq, Clone)] + #[pest_ast(rule(Rule::assignee_access))] + pub enum AssigneeAccess<'ast> { + Select(ArrayAccess<'ast>), + Member(MemberAccess<'ast>), } #[derive(Debug, FromPest, PartialEq, Clone)] @@ -463,6 +552,14 @@ mod ast { pub span: Span<'ast>, } + #[derive(Debug, FromPest, PartialEq, Clone)] + #[pest_ast(rule(Rule::member_access))] + pub struct MemberAccess<'ast> { + pub id: IdentifierExpression<'ast>, + #[pest_ast(outer())] + pub span: Span<'ast>, + } + #[derive(Debug, PartialEq, Clone)] pub struct BinaryExpression<'ast> { pub op: BinaryOperator, @@ -518,6 +615,7 @@ mod ast { Expression::Ternary(t) => &t.span, Expression::Postfix(p) => &p.span, Expression::InlineArray(a) => &a.span, + Expression::InlineStruct(s) => &s.span, Expression::ArrayInitializer(a) => &a.span, Expression::Unary(u) => &u.span, } @@ -593,8 +691,8 @@ mod ast { #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::assignee))] pub struct Assignee<'ast> { - pub id: IdentifierExpression<'ast>, // a - pub indices: Vec>, // [42 + x][31][7] + pub id: IdentifierExpression<'ast>, // a + pub accesses: Vec>, // [42 + x].foo[7] #[pest_ast(outer())] pub span: Span<'ast>, } @@ -696,13 +794,16 @@ mod tests { assert_eq!( generate_ast(&source), Ok(File { + structs: vec![], functions: vec![Function { id: IdentifierExpression { value: String::from("main"), span: Span::new(&source, 33, 37).unwrap() }, parameters: vec![], - returns: vec![Type::Basic(BasicType::Field(FieldType {}))], + returns: vec![Type::Basic(BasicType::Field(FieldType { + span: Span::new(&source, 44, 49).unwrap() + }))], statements: vec![Statement::Return(ReturnStatement { expressions: vec![Expression::add( Expression::Constant(ConstantExpression::DecimalNumber( @@ -723,14 +824,14 @@ mod tests { })], span: Span::new(&source, 29, source.len()).unwrap(), }], - imports: vec![ImportDirective { + imports: vec![ImportDirective::Main(MainImportDirective { source: ImportSource { value: String::from("foo"), span: Span::new(&source, 8, 11).unwrap() }, alias: None, span: Span::new(&source, 0, 29).unwrap() - }], + })], eoi: EOI {}, span: Span::new(&source, 0, 65).unwrap() }) @@ -745,13 +846,16 @@ mod tests { assert_eq!( generate_ast(&source), Ok(File { + structs: vec![], functions: vec![Function { id: IdentifierExpression { value: String::from("main"), span: Span::new(&source, 33, 37).unwrap() }, parameters: vec![], - returns: vec![Type::Basic(BasicType::Field(FieldType {}))], + returns: vec![Type::Basic(BasicType::Field(FieldType { + span: Span::new(&source, 44, 49).unwrap() + }))], statements: vec![Statement::Return(ReturnStatement { expressions: vec![Expression::add( Expression::Constant(ConstantExpression::DecimalNumber( @@ -790,14 +894,14 @@ mod tests { })], span: Span::new(&source, 29, 74).unwrap(), }], - imports: vec![ImportDirective { + imports: vec![ImportDirective::Main(MainImportDirective { source: ImportSource { value: String::from("foo"), span: Span::new(&source, 8, 11).unwrap() }, alias: None, span: Span::new(&source, 0, 29).unwrap() - }], + })], eoi: EOI {}, span: Span::new(&source, 0, 74).unwrap() }) @@ -812,13 +916,16 @@ mod tests { assert_eq!( generate_ast(&source), Ok(File { + structs: vec![], functions: vec![Function { id: IdentifierExpression { value: String::from("main"), span: Span::new(&source, 33, 37).unwrap() }, parameters: vec![], - returns: vec![Type::Basic(BasicType::Field(FieldType {}))], + returns: vec![Type::Basic(BasicType::Field(FieldType { + span: Span::new(&source, 44, 49).unwrap() + }))], statements: vec![Statement::Return(ReturnStatement { expressions: vec![Expression::if_else( Expression::Constant(ConstantExpression::DecimalNumber( @@ -845,14 +952,14 @@ mod tests { })], span: Span::new(&source, 29, 81).unwrap(), }], - imports: vec![ImportDirective { + imports: vec![ImportDirective::Main(MainImportDirective { source: ImportSource { value: String::from("foo"), span: Span::new(&source, 8, 11).unwrap() }, alias: None, span: Span::new(&source, 0, 29).unwrap() - }], + })], eoi: EOI {}, span: Span::new(&source, 0, 81).unwrap() }) @@ -866,13 +973,16 @@ mod tests { assert_eq!( generate_ast(&source), Ok(File { + structs: vec![], functions: vec![Function { id: IdentifierExpression { value: String::from("main"), span: Span::new(&source, 4, 8).unwrap() }, parameters: vec![], - returns: vec![Type::Basic(BasicType::Field(FieldType {}))], + returns: vec![Type::Basic(BasicType::Field(FieldType { + span: Span::new(&source, 15, 20).unwrap() + }))], statements: vec![Statement::Return(ReturnStatement { expressions: vec![Expression::Constant(ConstantExpression::DecimalNumber( DecimalNumberExpression { @@ -898,13 +1008,16 @@ mod tests { assert_eq!( generate_ast(&source), Ok(File { + structs: vec![], functions: vec![Function { id: IdentifierExpression { value: String::from("main"), span: Span::new(&source, 4, 8).unwrap() }, parameters: vec![], - returns: vec![Type::Basic(BasicType::Field(FieldType {}))], + returns: vec![Type::Basic(BasicType::Field(FieldType { + span: Span::new(&source, 15, 20).unwrap() + }))], statements: vec![Statement::MultiAssignment(MultiAssignmentStatement { function_id: IdentifierExpression { value: String::from("foo"), @@ -912,7 +1025,9 @@ mod tests { }, lhs: vec![ OptionallyTypedIdentifier { - ty: Some(Type::Basic(BasicType::Field(FieldType {}))), + ty: Some(Type::Basic(BasicType::Field(FieldType { + span: Span::new(&source, 23, 28).unwrap() + }))), id: IdentifierExpression { value: String::from("a"), span: Span::new(&source, 29, 30).unwrap(), @@ -965,13 +1080,19 @@ mod tests { #[test] fn playground() { let source = r#"import "heyman" as yo + + struct Foo { + field[2] foo + Bar bar + } + def main(private field[23] a) -> (bool[234 + 6]): field a = 1 a[32 + x][55] = y for field i in 0..3 do a == 1 + 2 + 3+ 4+ 5+ 6+ 6+ 7+ 8 + 4+ 5+ 3+ 4+ 2+ 3 endfor - a == 1 + a.member == 1 return a "#; let res = generate_ast(&source);