diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..7b2e393 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,816 @@ +[[package]] +name = "aho-corasick" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aho-corasick" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "argon2rs" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "arrayvec" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ascii-canvas" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bit-set" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bit-vec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cc" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "diff" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "digest" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dirs" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "docopt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "either" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ena" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fixedbitset" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "generic-array" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itertools" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lalrpop" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ascii-canvas 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ena 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lalrpop-util 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "term 0.5.2 (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 = "lalrpop-util" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.55" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.4.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)", +] + +[[package]] +name = "memchr" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "new_debug_unreachable" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "nodrop" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "opaque-debug" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ordermap" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "parser" +version = "0.1.0" +dependencies = [ + "lalrpop 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lalrpop-util 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "petgraph" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_generator" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_shared" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "0.4.30" +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 = "quote" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.54" +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.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_users" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.54 (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.10 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (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.6 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.2 (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.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "string_cache" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "string_cache_codegen" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "string_cache_shared" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strsim" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "0.15.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (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 = "synstructure" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.34 (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 = "term" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "termion" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", + "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.54 (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 = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ucd-util" +version = "0.1.3" +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 = "utf8-ranges" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.7" +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" + +[metadata] +"checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" +"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c" +"checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" +"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" +"checksum ascii-canvas 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8eb72df928aafb99fe5d37b383f2fe25bd2a765e3e5f7c365916b6f2463a29" +"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" +"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" +"checksum backtrace 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "70af6de4789ac39587f100176ac7f704531e9e534b0f8676f658b3d909ce9a94" +"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" +"checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80" +"checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +"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" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" +"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" +"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" +"checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a" +"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" +"checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +"checksum docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f525a586d310c87df72ebcd98009e57f1cc030c8c268305287a476beb653969" +"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" +"checksum ena 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3dc01d68e08ca384955a3aeba9217102ca1aa85b6e168639bf27739f1d749d87" +"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" +"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" +"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" +"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" +"checksum lalrpop 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b7f2d14eb07d819e961e5169f61d67de76a7cdbc6778b62192bcfe27c5af019" +"checksum lalrpop-util 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9768f55211206d3c17181108d8facb80bdffc1f1e674a67b1dddb2743529ca19" +"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" +"checksum libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "42914d39aad277d9e176efbdad68acb1d5443ab65afe0e0e4f0d49352a950880" +"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" +"checksum new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f40f005c60db6e03bae699e414c58bf9aa7ea02a2d0b9bfbcf19286cc4c82b30" +"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" +"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" +"checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" +"checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" +"checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" +"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" +"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" +"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" +"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828" +"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" +"checksum regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f0a0bcab2fd7d1d7c54fa9eae6f43eddeb9ce2e7352f8518a814a4f65d60c58" +"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" +"checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" +"checksum rustc-demangle 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ccc78bfd5acd7bf3e89cffcf899e5cb1a52d6fafa8dec2739ad70c9577a57288" +"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" +"checksum serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "a72e9b96fa45ce22a4bc23da3858dfccfd60acd28a25bcd328a98fdd6bea43fd" +"checksum serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "101b495b109a3e3ca8c4cbe44cf62391527cdfb6ba15821c5ce80bcd5ea23f9f" +"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" +"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" +"checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" +"checksum string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eea1eee654ef80933142157fdad9dd8bc43cf7c74e999e369263496f04ff4da" +"checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" +"checksum strsim 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "032c03039aae92b350aad2e3779c352e104d919cb192ba2fabbd7b831ce4f0f6" +"checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe" +"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" +"checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" +"checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" +"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" +"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" +"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/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..10d9811 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "parser" +version = "0.1.0" +authors = ["Lionel Sambuc "] +edition = "2018" +build = "build.rs" # LALRPOP preprocessing + +[lib] +name = "parser" +path = "src/lib.rs" + +[[bin]] +name = "parser-driver" +path = "src/main.rs" + +[dependencies] +lalrpop-util = "0.17.0" +regex = "0.2.1" + +[build-dependencies] +lalrpop = "0.17.0" + diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..23c7d3f --- /dev/null +++ b/build.rs @@ -0,0 +1,5 @@ +extern crate lalrpop; + +fn main() { + lalrpop::process_root().unwrap(); +} diff --git a/src/ast.rs b/src/ast.rs new file mode 100644 index 0000000..61c13cd --- /dev/null +++ b/src/ast.rs @@ -0,0 +1,113 @@ +trait Expression { + fn check_type(); + fn predict(); + fn execute(); +} + +/**********************************************************************/ +/* FORMATTING DATA */ +/**********************************************************************/ +#[derive(Clone, Debug)] +pub enum Projection { + Nifti(LiteralSelector, Bag), + JSON(JsonValue, Bag), +} + +// JSON FORMAT +#[derive(Clone, Debug)] +pub enum JsonValue { + String(String), + JsonNumber(LiteralNumber), + Bool(bool), + Null, + Object(Vec<(String, JsonValue)>), + Array(Vec), + Selector(LiteralSelector), + Aggregation(Aggregation), +} + +#[derive(Clone, Debug)] +pub enum Aggregation { + Count(bool, LiteralSelector), + Sum(LiteralSelector), + Min(LiteralSelector), + Max(LiteralSelector), +} + +// NIFTI +#[derive(Clone, Debug)] +struct Transform { + reference: String, + offset: Vec, + rotation: Vec>, +} + +/**********************************************************************/ +/* SELECTING / FILTERING DATA */ +/**********************************************************************/ +#[derive(Clone, Debug)] +pub enum Bag { + Distinct(Box), + Filter(Option, Option>), + Complement(Box), + Intersection(Box, Box), + Union(Box, Box), + Bag(Vec), + Inside(Shape), + Outside(Shape), +} + +/**********************************************************************/ +/* BAG OPERATORS */ +/**********************************************************************/ +#[derive(Clone, Debug)] +pub enum Predicate { + Less(Position, LiteralPosition), + Greater(Position, LiteralPosition), + Equal(Position, LiteralPosition), + Not(Box), + And(Box, Box), + Or(Box, Box), +} + +/**********************************************************************/ +/* SPATIAL OPERATORS */ +/**********************************************************************/ + +/**********************************************************************/ +/* SHAPES */ +/**********************************************************************/ +#[derive(Clone, Debug)] +pub enum Shape { + Point(LiteralPosition), + HyperRectangle(Vec), + HyperSphere(LiteralPosition, LiteralNumber), + Nifti(), +} + +/**********************************************************************/ +/* POSITIONS */ +/**********************************************************************/ +#[derive(Clone, Debug)] +pub enum Position { + StrCmpICase(LiteralSelector, String), + StrCmp(LiteralSelector, String), + Selector(LiteralSelector), + LiteralPosition(LiteralPosition), +} + +/**********************************************************************/ +/* Literals / TOKENS */ +/**********************************************************************/ + +#[derive(Clone, Debug)] +pub struct Field(pub String, pub Option); + +#[derive(Clone, Debug)] +pub enum LiteralNumber { + Int(i64), + Float(f64), +} + +pub type LiteralPosition = Vec; +pub type LiteralSelector = Vec; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0a2ef0c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,10 @@ +#[macro_use] +extern crate lalrpop_util; + +lalrpop_mod!(pub queries); // synthesized by LALRPOP + +pub mod ast; +pub use ast::*; + +#[cfg(test)] +mod tests; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..de9a830 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,28 @@ +extern crate parser; + +use parser::queries; + +use std::io; + +fn main() { + //let parser = queries::FiltersParser::new(); + let parser = queries::QueryParser::new(); + + loop { + println!("\n> Expression to parse (type `quit` to exit): "); + + let mut input = String::new(); + + match io::stdin().read_line(&mut input) { + Ok(0) => break, // Catch ^D + Ok(1) => continue, // Catch \n + Err(_) => continue, + Ok(_) => (), + } + + if input.trim().eq_ignore_ascii_case("quit") { + break; + } + println!("\n> Tree: \n{:?}", parser.parse(input.as_str())); + } +} diff --git a/src/queries.lalrpop b/src/queries.lalrpop new file mode 100644 index 0000000..29017cb --- /dev/null +++ b/src/queries.lalrpop @@ -0,0 +1,441 @@ +use std::str::FromStr; + +use crate::ast; +use crate::ast::*; + +grammar; + +//*********************************************************************/ +// FORMATTING DATA */ +//*********************************************************************/ +pub Query = { Projections? }; + +Projections: ast::Projection = { + NiftiOperator, + JsonOperator +}; + +// If selector is not provided, one (1) will be used as the values for +// each position where there is a point in bag_expression. +// +// If it is provided, it MUST resolve to a NUMBER. +NiftiOperator: ast::Projection = { + "nifti" "(" ")" => + if let Some((sel, _)) = s { + Projection::Nifti(sel, b) + } else { + Projection::Nifti(Vec::new(), b) + } +}; + +JsonOperator: ast::Projection = { + "json" "(" "," ")" => + Projection::JSON(f, b) +}; + +//*********************************************************************/ +// JSON */ +//*********************************************************************/ + +// Taken and adapted from: +// https://github.com/antlr/grammars-v4/blob/master/json/JSON.g4 +// +// Some of the parser / lexer rules are in the imported grammar as well. +JsonValues: ast::JsonValue = { + String => JsonValue::String(<>), + JsonNumber => <>, + JsonObj => <>, + JsonArray => <>, + "true" => JsonValue::Bool(true), + "false" => JsonValue::Bool(false), + "null" => JsonValue::Null, + // Support reference to values from the selected bag. + Selector => JsonValue::Selector(<>), + Aggregations => JsonValue::Aggregation(<>) +}; + +JsonObj: ast::JsonValue = { + "{" "}" => { + if let Some((elem, list)) = exp { + let mut values: Vec<(String, JsonValue)> = vec![elem]; + + for v in list { + let (_, pair) = v; + values.push(pair.clone()); + } + + JsonValue::Object(values) + } else { + JsonValue::Object(Vec::new()) + } + } +}; + +JsonPair: (String, ast::JsonValue) = { + ":" => (s, v) +}; + +JsonArray: ast::JsonValue = { + "[" "]" => { + if let Some((elem, list)) = exp { + let mut values: Vec = vec![elem]; + + for v in list.iter() { + let (_, val) = v; + values.push(val.clone()); + } + + JsonValue::Array(values) + } else { + JsonValue::Array(Vec::new()) + } + } +}; + +// The bag expression is implicit here, as this is te +// second argument to the json operator +Aggregations: ast::Aggregation = { + "count" "(" ")" => { + if let Some(_) = d { + Aggregation::Count(true, s) + } else { + Aggregation::Count(false, s) + } + }, + "sum" "(" ")" => + Aggregation::Sum(<>), + "min" "(" ")" => + Aggregation::Min(<>), + "max" "(" ")" => + Aggregation::Max(<>), +}; + +//*********************************************************************/ +// SELECTING / FILTERING DATA */ +//*********************************************************************/ +pub Filters = { Bags }; + +// All these expressions generate bags. +Bags: ast::Bag = { + // Bag Operators + Distinct, + Filter, + Complement, + Intersection, + Union, + Bag, + // Spatial Operators + Inside, + Outside, + // When used directly here, the inside() operation on the shape is + // implied. + Shapes => Bag::Inside(<>) +}; + +//*********************************************************************/ +// BAG OPERATORS */ +//*********************************************************************/ +Distinct: ast::Bag = { + "distinct" "(" ")" => + Bag::Distinct(Box::new(<>)) +}; + +// Returns all the points which are NOT part of the bag. +Complement: ast::Bag = { + "complement" "(" ")" => + Bag::Complement(Box::new(<>)) +}; + +// Returns points which are part of both left and right sets. +Intersection: ast::Bag = { + "intersection" "(" "," ")" => + Bag::Intersection(Box::new(lh), Box::new(rh)) +}; + +// Returns points which are either part of left or right sets +// (or both). +Union: ast::Bag = { + "union" "(" "," ")" => + Bag::Union(Box::new(lh), Box::new(rh)) +}; + +// Filters point so that points part of the resulting bag respect +// the predicate. +Filter: ast::Bag = { +// "filter" "(" "," ")" => + "filter" "(" ")" => + Bag::Filter(None, Some(Box::new(b))), + "filter" "(" )?> ")" => match b { + Some(b) => Bag::Filter(Some(p), Some(Box::new(b))), + None => Bag::Filter(Some(p), None), + } +}; + +Predicates: ast::Predicate = { + Less, + Greater, + Equal, + Not, + And, + Or +}; + +Less: ast::Predicate = { + "<" "(" "," ")" => { + Predicate::Less(v, literal) + } +}; + +Greater: ast::Predicate = { + ">" "(" "," ")" => { + Predicate::Greater(v, literal) + } +}; + +Equal: ast::Predicate = { + "=" "(" "," ")" => { + Predicate::Equal(v, literal) + } +}; + +Not: ast::Predicate = { + "!" "(" ")" => + Predicate::Not(Box::new(p)) +}; + +And: ast::Predicate = { + "&" "(" "," ")" => + Predicate::And(Box::new(lh), Box::new(rh)) +}; + +Or: ast::Predicate = { + "|" "(" "," ")" => + Predicate::Or(Box::new(lh), Box::new(rh)) +}; + +// Arbitrary bag of positions. +Bag: ast::Bag = { + "bag" "{" "}" => { + let mut bags = vec![elem]; + + for (_, b) in list { + bags.push(b); + } + + Bag::Bag(bags) + } +}; + +//*********************************************************************/ +// SPATIAL OPERATORS */ +//*********************************************************************/ + +// Faces | vertices are included to allow selection on a pure plane or +// boundary. +// +// For example: +// intersection(outside(hyperrectangle{[0,0], [1,1]}, +// inside(hyperrectangle{[0,0], [1,1]}) +// will be true for any point lying EXACTLY on a face, corner or edge +// of the cube [0,0], [1,1]. + +// Returns the set of points outside the shape, (face included) +Outside: ast::Bag = { + "outside" "(" ")" => + Bag::Outside(<>) +}; + +// Returns the set of points inside the shape, (face included) +Inside: ast::Bag = { + "inside" "(" ")" => + Bag::Inside(<>) +}; + +//*********************************************************************/ +// SHAPES */ +//*********************************************************************/ + +// Shapes are defined in terms of POSITION, a.k.a a LiteralPosition +// value, which is not a POSITIONS, which might be a filter for example. +Shapes: ast::Shape = { + Point, + HyperRectangle, + HyperSphere, + Nifti +}; + +// If the hyperrectangle is aligned with the axes, then two points are +// enough, if not we need all the points to be specified. +HyperRectangle: ast::Shape = { + "hyperrectangle" "{" + "," + + "}" => { + let mut pos = vec![l, h]; + for (_, lh, _, rh) in list.iter() { + pos.push(lh.clone()); + pos.push(rh.clone()); + } + Shape::HyperRectangle(pos) + } +}; + +// A hypersphere is defined by its center and a radius, independantly +// of the number of dimensions of the space. +HyperSphere: ast::Shape = { + "hypersphere" "{" "," "}" => + Shape::HyperSphere(c, r) +}; + +Point: ast::Shape = { + "point" "{" "}" => + Shape::Point(<>) +}; + +// Define a shape as the non-zero values in a NIfTI object, defined by +// nifti{ +// spaceId: string, +// lower_corner: position, // Optional, default to the origin +// rotation: [ position+ ], // Optional, no rotation by default +// bytes: uri(STRING) // uri to the NIfTI object +// } +Nifti: ast::Shape = { + "nifti" "{" + String "," + ( Position "," )? + ( "[" Position ( "," Position)* "]" "," )? + ByteProvider + "}" => { + Shape::Nifti() + } +}; + +// FIXME: STRING is assumed to be a well-formed URI, fully specify here? +// +// FIXME: Add a provider for in-line raw-byte stream. +ByteProvider = { "uri" "(" String ")" }; + +//*********************************************************************/ +// POSITIONS */ +//*********************************************************************/ + +// Always returns a vector of numbers, a.k.a a position (a scalar will +// be represented as a vector of one element) +Positions: ast::Position = { + StrCmpICase, + StrCmp, + Selector => Position::Selector(<>), + Position => Position::LiteralPosition(<>) +}; + +// Compare lexicographically two strings, and returns a `position`: +// [-1] : String is lexicographically before, +// [ 0] : is equal, +// [ 1] : is after. +StrCmp: ast::Position = { + "str_cmp" "(" "," ")" => { + Position::StrCmp(s, v) + } +}; + +// Same, but case insensitive. +StrCmpICase: ast::Position = { + "str_cmp_ignore_case" "(" "," ")" => { + Position::StrCmpICase(s, v) + } +}; + +// FIXME: FIELDS are expected to be exisiting in the data model. Root Object is assumed to be the type of the ressource on which the POST call was done. +Selector = { + ( )+ +}; + +Position: ast::LiteralPosition = { + "[" )*> "]" => { + let mut pos: LiteralPosition = vec![element]; + + for e in list.iter() { + pos.push(e.clone()); + } + + pos + } +}; + +//*********************************************************************/ +// TOKENS - STRINGS */ +//*********************************************************************/ + +// Accept field descriptor which +// 1. start with a dot ('.') +// 2. optionnally followed by a field name consisting of a letter or +// underscore, followed by letters, numbers or underscore, +// 3. optionnally followed by brakets enclosing an natural number +// denoting an offset in a list or array. +Field: Field = { + => { + if let Some(pos) = n.rfind('[') { + let name = &n[1..pos]; + let index = &n[(pos+1)..(n.len()-1)]; + let index = usize::from_str(index).unwrap(); + Field(String::from(name), Some(index)) + } else { + let name = &n[1..]; + Field(String::from(name), None) + } + } +}; + +String: String = { + r#"["]([\\](["\\/bfnrt]|u[0-9a-fA-F]{4})|[^"\\\u0000-\u001F])*["]"# => + String::from(<>) +}; + +//*********************************************************************/ +// TOKENS - NUMBERS */ +//*********************************************************************/ +// We define 3 kinds of number, to avoid ambiguities in the rules. +JsonNumber: ast::JsonValue = { + => match s { + None => JsonValue::JsonNumber(v), + Some(_) => match v { + LiteralNumber::Int(x) => JsonValue::JsonNumber(LiteralNumber::Int(-x)), + LiteralNumber::Float(x) => JsonValue::JsonNumber(LiteralNumber::Float(-x)) + } + } +}; + +PositiveNumber: ast::LiteralNumber = { "+"? => v }; + +Number: ast::LiteralNumber = { + "+" => v, + "-" => match v { + LiteralNumber::Int(x) => LiteralNumber::Int(-x), + LiteralNumber::Float(x) => LiteralNumber::Float(-x) + }, + => v + +}; + +Num: ast::LiteralNumber = { + r"0([.][0-9]+([eE][+\-]?(0|[1-9][0-9]*))?)?" + => { + if let Ok(v) = i64::from_str(<>) { + LiteralNumber::Int(v) + } else { + // Either parsing as a float succeed or we pass along + // the error + LiteralNumber::Float(f64::from_str(<>).unwrap()) + } + }, + r"[1-9][0-9]*([.][0-9]+)?([eE][+\-]?(0|[1-9][0-9]*))?" + => { + if let Ok(v) = i64::from_str(<>) { + LiteralNumber::Int(v) + } else { + // Either parsing as a float succeed or we pass along + // the error + LiteralNumber::Float(f64::from_str(<>).unwrap()) + } + } +}; diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..8e0614a --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,944 @@ +//use super::ast; + +/******************************************************************/ +/* FORMATTING DATA */ +/******************************************************************/ +#[cfg(test)] +mod query { + use crate::queries; + + fn query_parser() -> queries::QueryParser { + queries::QueryParser::new() + } + + #[test] + fn query() { + let p = query_parser(); + + let nifti = "nifti(point{[0]})"; + + // Option is Empty + assert!(p.parse("").is_ok()); + + // Option is there + assert!(p.parse(nifti).is_ok()); + + // Too many element + assert!(p.parse(format!("{} {}", nifti, nifti).as_str()).is_err()); + } + + /* Not useful to test this rule + #[test] + fn projections() { + let p = query_parser(); + + let nifti = "nifti(point{[0]})"; + let json = "json(., point{[0]})"; + + // Each alternative + assert!(p.parse(nifti).is_ok()); + assert!(p.parse(json).is_ok()); + } + */ + + #[test] + fn nifti_operator() { + let p = query_parser(); + + // Check allowed forms of the operator + assert!(p.parse("nifti(point{[0]})").is_ok()); + assert!(p.parse("nifti(.properties.id, point{[0]})").is_ok()); + + //FIXME: THIS SHOULD BE ALLOWED + assert!(p.parse("nifti(2, point{[0]})").is_ok()); + assert!(p.parse("nifti(2.23, point{[0]})").is_ok()); + + //FIXME: SYNTAX OK, TYPE NOT + assert!(p.parse("nifti(\"asd\", point{[0]})").is_err()); + } + + #[test] + fn json_operator() { + let p = query_parser(); + + assert!(p.parse("json(true, point{[0]})").is_ok()); + assert!(p.parse("json(23, point{[0]})").is_ok()); + assert!(p.parse("json([23, 24], point{[0]})").is_ok()); + assert!(p.parse("json([23, count(.)], point{[0]})").is_ok()); + + assert!(p.parse("json(true)").is_err()); + assert!(p.parse("json(true,)").is_err()); + + assert!(p.parse("json(, point{[0]})").is_err()); + assert!(p.parse("json(point{[0]})").is_err()); + + assert!(p.parse("json(true, point)").is_err()); + } + + #[test] + fn json_values() { + let p = query_parser(); + + assert!(p + .parse(format!("json({}, point{{[0]}})", "true").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "false").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "null").as_str()) + .is_ok()); + + // Incorrect capitalisation + assert!(p + .parse(format!("json({}, point{{[0]}})", "True").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "False").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "Null").as_str()) + .is_err()); + } + + #[test] + fn json_obj() { + let p = query_parser(); + + assert!(p + .parse(format!("json({}, point{{[0]}})", "{}").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "{\"field\": 0}").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "{\"field\": 0, \"field1\": 1}").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "{\"field\": [0, 1]}").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "{\"field\": {\"field1\": 0}}").as_str()) + .is_ok()); + assert!(p + .parse( + format!( + "json({}, point{{[0]}})", + "{\"field\": [{\"field1\": 0}, {\"field1\": 1}]}" + ) + .as_str() + ) + .is_ok()); + } + + #[test] + fn json_pair() { + let p = query_parser(); + + assert!(p + .parse(format!("json({}, point{{[0]}})", "{:}").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "{field: 0}").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "{0: 0}").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "{\"0\": }").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "{\"0\": 0 }").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "{\"field\": 0 }").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "{\"field\": \"0\" }").as_str()) + .is_ok()); + } + + #[test] + fn json_array() { + let p = query_parser(); + + assert!(p + .parse(format!("json({}, point{{[0]}})", "[, 0]").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "[]").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "[0]").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "[0, 1]").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "[{\"field\": 0}, {\"field\": 1}]").as_str()) + .is_ok()); + } + + #[test] + fn aggregations() { + let p = query_parser(); + + //FIXME: ADD STUFF + + // count () + assert!(p + .parse(format!("json({}, point{{[0]}})", "count()").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "count(distinct)").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "count(.)").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "count(distinct .)").as_str()) + .is_ok()); + + // sum () + assert!(p + .parse(format!("json({}, point{{[0]}})", "sum()").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "sum(.)").as_str()) + .is_ok()); + + // min () + assert!(p + .parse(format!("json({}, point{{[0]}})", "min()").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "min(.)").as_str()) + .is_ok()); + + // max () + assert!(p + .parse(format!("json({}, point{{[0]}})", "max()").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "max(.)").as_str()) + .is_ok()); + } + + #[test] + fn json_numbers() { + let p = query_parser(); + + // Integers + assert!(p + .parse(format!("json({}, point{{[0]}})", "0").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "+0").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "-0").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "1").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "+1").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "-1").as_str()) + .is_ok()); + + // Floating point values + assert!(p + .parse(format!("json({}, point{{[0]}})", "0.0").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "+0.0").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "-0.0").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "0.1").as_str()) + .is_ok()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "+0.01").as_str()) + .is_err()); + assert!(p + .parse(format!("json({}, point{{[0]}})", "-0.01").as_str()) + .is_ok()); + } +} + +#[cfg(test)] +mod filters { + use crate::queries; + + /******************************************************************/ + /* SELECTING / FILTERING DATA */ + /******************************************************************/ + fn filters_parser() -> queries::FiltersParser { + queries::FiltersParser::new() + } + + #[test] + fn filters() { + let p = filters_parser(); + + assert!(p.parse("").is_err()); + + assert!(p.parse("point{[0]}").is_ok()); + } + + /* Not useful to test this rule + #[test] + fn bags() { + let p = filters_parser(); + } */ + + #[test] + fn distinct() { + let p = filters_parser(); + + assert!(p.parse("distinct()").is_err()); + + assert!(p.parse("distinct(point{[0]})").is_ok()); + } + + #[test] + fn complement() { + let p = filters_parser(); + + assert!(p.parse("complement()").is_err()); + + assert!(p.parse("complement(point{[0]})").is_ok()); + } + + #[test] + fn intersection() { + let p = filters_parser(); + + assert!(p.parse("intersection()").is_err()); + assert!(p.parse("intersection(point{[0]})").is_err()); + assert!(p + .parse("intersection(point{[0]}, point{[0]}, point{[0]})") + .is_err()); + + assert!(p.parse("intersection(point{[0]}, point{[0]})").is_ok()); + } + + #[test] + fn union() { + let p = filters_parser(); + + assert!(p.parse("union()").is_err()); + assert!(p.parse("union(point{[0]})").is_err()); + assert!(p + .parse("union(point{[0]}, point{[0]}, point{[0]})") + .is_err()); + + assert!(p.parse("union(point{[0]}, point{[0]})").is_ok()); + } + + #[test] + fn filter() { + let p = filters_parser(); + + assert!(p.parse("filter()").is_err()); + assert!(p.parse("filter(point{[0]})").is_ok()); + assert!(p.parse("filter(=(., [0]))").is_ok()); + + assert!(p.parse("filter(=(., [0]), point{[0]})").is_ok()); + } + + /* Not useful to test this rule + #[test] + fn predicates() { + let p = filters_parser(); + }*/ + + #[test] + fn less() { + let p = filters_parser(); + + assert!(p + .parse(format!("filter({}, point{{[0]}})", "<(., [0])").as_str()) + .is_ok()); + + assert!(p + .parse(format!("filter({}, point{{[0]}})", "<(, [0])").as_str()) + .is_err()); + assert!(p + .parse(format!("filter({}, point{{[0]}})", "<(.)").as_str()) + .is_err()); + assert!(p + .parse(format!("filter({}, point{{[0]}})", "<()").as_str()) + .is_err()); + } + + #[test] + fn greater() { + let p = filters_parser(); + + assert!(p + .parse(format!("filter({}, point{{[0]}})", ">(., [0])").as_str()) + .is_ok()); + + assert!(p + .parse(format!("filter({}, point{{[0]}})", ">(, [0])").as_str()) + .is_err()); + assert!(p + .parse(format!("filter({}, point{{[0]}})", ">(.)").as_str()) + .is_err()); + assert!(p + .parse(format!("filter({}, point{{[0]}})", ">()").as_str()) + .is_err()); + } + + #[test] + fn equal() { + let p = filters_parser(); + + assert!(p + .parse(format!("filter({}, point{{[0]}})", "=(., [0])").as_str()) + .is_ok()); + + assert!(p + .parse(format!("filter({}, point{{[0]}})", "=(, [0])").as_str()) + .is_err()); + assert!(p + .parse(format!("filter({}, point{{[0]}})", "=(.)").as_str()) + .is_err()); + assert!(p + .parse(format!("filter({}, point{{[0]}})", "=()").as_str()) + .is_err()); + } + + #[test] + fn not() { + let p = filters_parser(); + + assert!(p + .parse(format!("filter({}, point{{[0]}})", "!(=(., [0]))").as_str()) + .is_ok()); + + assert!(p + .parse(format!("filter({}, point{{[0]}})", "!()").as_str()) + .is_err()); + } + + #[test] + fn and() { + let p = filters_parser(); + + assert!(p + .parse(format!("filter({}, point{{[0]}})", "&(=(., [0]), =(., [0]))").as_str()) + .is_ok()); + + assert!(p + .parse(format!("filter({}, point{{[0]}})", "&(, =(., [0]))").as_str()) + .is_err()); + assert!(p + .parse(format!("filter({}, point{{[0]}})", "&(|(=(., [0])))").as_str()) + .is_err()); + assert!(p + .parse(format!("filter({}, point{{[0]}})", "&()").as_str()) + .is_err()); + } + + #[test] + fn or() { + let p = filters_parser(); + + assert!(p + .parse(format!("filter({}, point{{[0]}})", "|(=(., [0]), =(., [0]))").as_str()) + .is_ok()); + + assert!(p + .parse(format!("filter({}, point{{[0]}})", "|(, =(., [0]))").as_str()) + .is_err()); + assert!(p + .parse(format!("filter({}, point{{[0]}})", "|(|(=(., [0])))").as_str()) + .is_err()); + assert!(p + .parse(format!("filter({}, point{{[0]}})", "|()").as_str()) + .is_err()); + } + + #[test] + fn bag() { + let p = filters_parser(); + + assert!(p.parse("bag{}").is_err()); + + assert!(p.parse("bag{point{[0]}}").is_ok()); + assert!(p.parse("bag{point{[0]}, point{[0]}}").is_ok()); + assert!(p.parse("bag{point{[0]}, point{[0]}, point{[0]}}").is_ok()); + assert!(p + .parse("bag{point{[0]}, hypersphere{[0], 1}, hyperrectangle{[0], [1]}}") + .is_ok()); + } + + #[test] + fn outside() { + let p = filters_parser(); + + assert!(p.parse("outside()").is_err()); + + assert!(p.parse("outside(point{[0]})").is_ok()); + } + + #[test] + fn inside() { + let p = filters_parser(); + + assert!(p.parse("inside()").is_err()); + + assert!(p.parse("inside(point{[0]})").is_ok()); + } + + /* Not useful to test this rule + #[test] + fn shapes() { + let p = filters_parser(); + + assert!(p.parse("point{[0]}").is_ok()); + assert!(p.parse("hyperrectangle{[0], [1]}").is_ok()); + assert!(p.parse("hypersphere{[0], 1}").is_ok()); + assert!(p.parse("nifti{\"\", uri(\"\")}").is_ok()); + }*/ + + #[test] + fn hyperrectangle() { + let p = filters_parser(); + + // At least two positions when it is aligned with the axis, otherwise an even number + // of positions, as the number of vertices follows the rule 2**k, where k is the number + // of dimensions of the space containing the hyperrectangle. + assert!(p.parse("hyperrectangle{}").is_err()); + assert!(p.parse("hyperrectangle{[]}").is_err()); + assert!(p.parse("hyperrectangle{[0]}").is_err()); + assert!(p.parse("hyperrectangle{[0], [1], [2]}").is_err()); + assert!(p.parse("hyperrectangle{[0], [1], [2], [3], [4]}").is_err()); + + assert!(p.parse("hyperrectangle{[0], [1]}").is_ok()); + assert!(p.parse("hyperrectangle{[0], [1], [2], [3]}").is_ok()); + assert!(p + .parse("hyperrectangle{[0], [1], [2], [3], [4], [5]}") + .is_ok()); + } + + #[test] + fn hyperrsphere() { + let p = filters_parser(); + + assert!(p.parse("hypersphere{}").is_err()); + assert!(p.parse("hypersphere{[]}").is_err()); + assert!(p.parse("hypersphere{[0]}").is_err()); + + assert!(p.parse("hypersphere{[0], 23}").is_ok()); + } + + #[test] + fn point() { + let p = filters_parser(); + + assert!(p.parse("point{}").is_err()); + assert!(p.parse("point{[]}").is_err()); + + assert!(p.parse("point{[0]}").is_ok()); + } + + #[test] + fn nifti() { + let p = filters_parser(); + //FIXME: ADD STUFF + assert!(false); + } + + #[test] + fn byte_provider() { + let p = filters_parser(); + //FIXME: ADD STUFF + assert!(false); + } + + /* Not useful to test this rule + #[test] + fn positions() { + let p = filters_parser(); + + assert!(p + .parse( + format!( + "filter(=({}, [1]), point{{[0]}})", + "str_cmp_ignore_case(.field, \"\")" + ) + .as_str() + ) + .is_ok()); + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", "str_cmp(.field, \"\")").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", ".field").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", "[0]").as_str()) + .is_ok()); + + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", "point{[0]}").as_str()) + .is_err()); + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", "{0}").as_str()) + .is_err()); + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", "").as_str()) + .is_err()); + }*/ + + #[test] + fn str_cmp() { + let p = filters_parser(); + + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", "str_cmp(.field, \"\")").as_str()) + .is_ok()); + + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", "str_cmp(.field)").as_str()) + .is_err()); + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", "str_cmp(\"\")").as_str()) + .is_err()); + } + + #[test] + fn str_cmp_icase() { + let p = filters_parser(); + + assert!(p + .parse( + format!( + "filter(=({}, [1]), point{{[0]}})", + "str_cmp_ignore_case(.field, \"\")" + ) + .as_str() + ) + .is_ok()); + + assert!(p + .parse( + format!( + "filter(=({}, [1]), point{{[0]}})", + "str_cmp_ignore_case(.field)" + ) + .as_str() + ) + .is_err()); + assert!(p + .parse( + format!( + "filter(=({}, [1]), point{{[0]}})", + "str_cmp_ignore_case(\"\")" + ) + .as_str() + ) + .is_err()); + } + + #[test] + fn selector() { + let p = filters_parser(); + + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", ".").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", ".field").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", ".field.field").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", ".field[1].field").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(=({}, [1]), point{{[0]}})", ".field.field[1]").as_str()) + .is_ok()); + } + + #[test] + fn position() { + let p = filters_parser(); + + // Empty + assert!(p.parse(format!("point{{{}}}", "[]").as_str()).is_err()); + + // Non-numerical coordinate: + assert!(p.parse(format!("point{{{}}}", "[aa]").as_str()).is_err()); + + assert!(p + .parse(format!("point{{{}}}", "[\"aa\"]").as_str()) + .is_err()); + + // One or more coordinates + assert!(p.parse(format!("point{{{}}}", "[0]").as_str()).is_ok()); + assert!(p.parse(format!("point{{{}}}", "[0, 0]").as_str()).is_ok()); + assert!(p + .parse(format!("point{{{}}}", "[0, 0, 0]").as_str()) + .is_ok()); + assert!(p + .parse(format!("point{{{}}}", "[0, 0, 0, 0]").as_str()) + .is_ok()); + assert!(p + .parse(format!("point{{{}}}", "[0,0,0,0]").as_str()) + .is_ok()); + } + + #[test] + fn field() { + let p = filters_parser(); + + // Single dot + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".").as_str()) + .is_ok()); + + // Check first character is within allowed characters + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".a").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", "._").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".2").as_str()) + .is_err()); + + // Check second character is within allowed characters + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".fa").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".f2").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".f_").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".f2").as_str()) + .is_ok()); + + // Check we can add subscript + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".[23]").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".f[0]").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".f[2]").as_str()) + .is_ok()); + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".f[23]").as_str()) + .is_ok()); + + // Invalid index values + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".f[2.3]").as_str()) + .is_err()); + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".f[02]").as_str()) + .is_err()); + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".f[-2]").as_str()) + .is_err()); + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".f[2e2]").as_str()) + .is_err()); + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".f[2E2]").as_str()) + .is_err()); + assert!(p + .parse(format!("filter(<({}, [1]), point{{[0]}})", ".f[+2]").as_str()) + .is_err()); + } + + #[test] + fn string() { + fn test_str_ok(p: &queries::FiltersParser, string: &str) { + let n = format!( + "{}{}{}", + "nifti{", string, ", uri(\"http://a.nifti.file\") }" + ); + let n = n.as_str(); + + assert!(p.parse(n).is_ok()); + } + + fn test_str_err(p: &queries::FiltersParser, string: &str) { + let n = format!( + "{}{}{}", + "nifti{", string, ", uri(\"http://a.nifti.file\") }" + ); + let n = n.as_str(); + + assert!(p.parse(n).is_err()); + } + + let p = &filters_parser(); + + // Empty String + test_str_ok(p, r#""""#); + + // Usual escapes + test_str_ok(p, r#""\"""#); + test_str_ok(p, r#""\\""#); + test_str_ok(p, r#""\/""#); + test_str_ok(p, r#""\b""#); + test_str_ok(p, r#""\f""#); + test_str_ok(p, r#""\n""#); + test_str_ok(p, r#""\r""#); + test_str_ok(p, r#""\t""#); + + // Unicode Escape + test_str_ok(p, r#""\u0012""#); + test_str_ok(p, r#""\u001F""#); + test_str_ok(p, r#""\u001a""#); + + // ASCI Letters & digit + test_str_ok(p, r#""abcdefghijklmnopqrstuvwxyz""#); + test_str_ok(p, r#""ABCDEFGHIJKLMNOPQRSTUVWXYZ""#); + test_str_ok(p, r#""0123456789""#); + + // Space and some non-white characters + test_str_ok(p, r#"" ,.-;:!?'^&|§°+*ç%_""#); + + // Invalid + test_str_err(p, "\"\u{0010}\""); // rust requires \u{..}, while JSON does not. + } + + #[test] + fn positive_numbers() { + let p = filters_parser(); + + // Integers + assert!(p + .parse(format!("hypersphere{{[0],{}}}", "0").as_str()) + .is_ok()); + assert!(p + .parse(format!("hypersphere{{[0],{}}}", "+0").as_str()) + .is_ok()); + assert!(p + .parse(format!("hypersphere{{[0],{}}}", "-0").as_str()) + .is_err()); + assert!(p + .parse(format!("hypersphere{{[0],{}}}", "1").as_str()) + .is_ok()); + assert!(p + .parse(format!("hypersphere{{[0],{}}}", "+1").as_str()) + .is_ok()); + assert!(p + .parse(format!("hypersphere{{[0],{}}}", "-1").as_str()) + .is_err()); + + // Floating point values + assert!(p + .parse(format!("hypersphere{{[0],{}}}", "0.0").as_str()) + .is_ok()); + assert!(p + .parse(format!("hypersphere{{[0],{}}}", "+0.0").as_str()) + .is_ok()); + assert!(p + .parse(format!("hypersphere{{[0],{}}}", "-0.0").as_str()) + .is_err()); + assert!(p + .parse(format!("hypersphere{{[0],{}}}", "0.1").as_str()) + .is_ok()); + assert!(p + .parse(format!("hypersphere{{[0],{}}}", "+0.01").as_str()) + .is_ok()); + assert!(p + .parse(format!("hypersphere{{[0],{}}}", "-0.01").as_str()) + .is_err()); + } + + #[test] + fn numbers() { + let p = filters_parser(); + + // Integers + assert!(p.parse(format!("point{{[{}]}}", "0").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "+0").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "-0").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "+1").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "-1").as_str()).is_ok()); + + // Floating point values + assert!(p.parse(format!("point{{[{}]}}", "0.0").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "+0.0").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "-0.0").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "0.1").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "+0.01").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "-0.01").as_str()).is_ok()); + } + + #[test] + fn num() { + let p = filters_parser(); + + // Integers + assert!(p.parse(format!("point{{[{}]}}", "0").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1e2").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1e+2").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1e-2").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1E2").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "100").as_str()).is_ok()); + + assert!(p.parse(format!("point{{[{}]}}", "010").as_str()).is_err()); + + // Floating point values (normalized) + assert!(p.parse(format!("point{{[{}]}}", "0.0").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "0.1").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "0.1e0").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "0.1e2").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "0.1e+2").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "0.1e-2").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "0.1E2").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "0.1E23").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "0.01").as_str()).is_ok()); + + assert!(p.parse(format!("point{{[{}]}}", "0.").as_str()).is_err()); + assert!(p + .parse(format!("point{{[{}]}}", "0.1E03").as_str()) + .is_err()); + assert!(p + .parse(format!("point{{[{}]}}", "0.1E0.3").as_str()) + .is_err()); + + // Floating point values (denormalized) + assert!(p.parse(format!("point{{[{}]}}", "1.0").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1.1").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1.1e0").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1.1e2").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1.1e+2").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1.1e-2").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1.1E2").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1.1E23").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "1.01").as_str()).is_ok()); + assert!(p.parse(format!("point{{[{}]}}", "10.1").as_str()).is_ok()); + + assert!(p.parse(format!("point{{[{}]}}", "1.").as_str()).is_err()); + assert!(p.parse(format!("point{{[{}]}}", "01.1").as_str()).is_err()); + assert!(p + .parse(format!("point{{[{}]}}", "1.1E03").as_str()) + .is_err()); + assert!(p + .parse(format!("point{{[{}]}}", "1.1E0.3").as_str()) + .is_err()); + } +}