diff --git a/Makefile.impls b/Makefile.impls index 831bbcc7d2..b344c1b24c 100644 --- a/Makefile.impls +++ b/Makefile.impls @@ -176,7 +176,7 @@ rexx_STEP_TO_PROG = impls/rexx/$($(1)).rexxpp rpython_STEP_TO_PROG = impls/rpython/$($(1)) ruby_STEP_TO_PROG = impls/ruby/$($(1)).rb ruby.2_STEP_TO_PROG = impls/ruby.2/$($(1)).rb -rust_STEP_TO_PROG = impls/rust/$($(1)) +rust_STEP_TO_PROG = impls/rust/target/release/$($(1)) scala_STEP_TO_PROG = impls/scala/target/scala-2.11/classes/$($(1)).class scheme_STEP_TO_PROG = $(scheme_STEP_TO_PROG_$(scheme_MODE)) skew_STEP_TO_PROG = impls/skew/$($(1)).js diff --git a/impls/rust/Cargo.lock b/impls/rust/Cargo.lock deleted file mode 100644 index a4d1a0602b..0000000000 --- a/impls/rust/Cargo.lock +++ /dev/null @@ -1,456 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "aho-corasick" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "arrayref" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "arrayvec" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace" -version = "0.3.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace-sys 0.1.32 (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.65 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "blake2b_simd" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "byteorder" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cc" -version = "1.0.46" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.10" -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.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "constant_time_eq" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crossbeam-utils" -version = "0.6.6" -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)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "dirs" -version = "2.0.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)", - "dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "dirs-sys" -version = "0.3.4" -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)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "either" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "failure" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure_derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.6 (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)", - "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fnv" -version = "1.0.6" -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 = "itertools" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.65" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "log" -version = "0.4.8" -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)", -] - -[[package]] -name = "memchr" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nix" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.46 (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.65 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "1.0.6" -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)", -] - -[[package]] -name = "quote" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.6 (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.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "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.65 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (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.8 (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.56" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_users" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "failure 0.1.6 (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.56 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex-syntax" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rust-argon2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rust2" -version = "0.1.0" -dependencies = [ - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustyline 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustyline" -version = "5.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.6 (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)", -] - -[[package]] -name = "synstructure" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.6 (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)", - "unicode-xid 0.2.0 (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.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-segmentation" -version = "1.3.0" -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.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "utf8parse" -version = "0.1.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 = "winapi" -version = "0.3.8" -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.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" -"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -"checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" -"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" -"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)" = "0213d356d3c4ea2c18c40b037c3be23cd639825c18f25ee670ac7813beeef99c" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" -"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" -"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" -"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" -"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" -"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" -"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" -"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" -"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" -"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" -"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d" -"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" -"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" -"checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf" -"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -"checksum rustyline 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4795e277e6e57dec9df62b515cd4991371daa80e8dc8d80d596e58722b89c417" -"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" -"checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" -"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" -"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"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/impls/rust/Cargo.toml b/impls/rust/Cargo.toml index de50a703b8..40831b0533 100644 --- a/impls/rust/Cargo.toml +++ b/impls/rust/Cargo.toml @@ -4,11 +4,11 @@ version = "0.1.0" authors = ["root"] [dependencies] -rustyline = "5.0.3" +rustyline = "13.0.0" lazy_static = "1.4.0" -regex = "1.3.1" -itertools = "0.8.0" +regex = "1.7" +itertools = "0.10" fnv = "1.0.6" diff --git a/impls/rust/Dockerfile b/impls/rust/Dockerfile index 661df9e6ce..c838a1212c 100644 --- a/impls/rust/Dockerfile +++ b/impls/rust/Dockerfile @@ -1,5 +1,29 @@ -FROM rust:1.38.0 +FROM ubuntu:20.04 +MAINTAINER Joel Martin -ENV CARGO_HOME /mal +########################################################## +# General requirements for testing or common across many +# implementations +########################################################## + +RUN apt-get -y update +# Required for running tests +RUN apt-get -y install make python3 +RUN ln -fs /usr/bin/python3 /usr/local/bin/python + +RUN mkdir -p /mal WORKDIR /mal + +########################################################## +# Specific implementation requirements +########################################################## + +RUN apt-get -y install cargo \ + librust-fnv-dev \ + librust-itertools-dev \ + librust-lazy-static-dev \ + librust-rustyline-dev \ + rust-clippy rustfmt + +ENV CARGO_HOME /mal diff --git a/impls/rust/Makefile b/impls/rust/Makefile index 6cceaafa9b..8702c68623 100644 --- a/impls/rust/Makefile +++ b/impls/rust/Makefile @@ -1,31 +1,33 @@ -UPPER_STEPS = step4_if_fn_do step5_tco step6_file step7_quote step8_macros step9_try stepA_mal -STEPS = step0_repl step1_read_print step2_eval step3_env $(UPPER_STEPS) +EXEC_DIR := target/release + +UPPER_STEPS := $(EXEC_DIR)/step4_if_fn_do \ + $(EXEC_DIR)/step5_tco \ + $(EXEC_DIR)/step6_file \ + $(EXEC_DIR)/step7_quote \ + $(EXEC_DIR)/step8_macros \ + $(EXEC_DIR)/step9_try \ + $(EXEC_DIR)/stepA_mal +STEP0 := $(EXEC_DIR)/step0_repl +STEP1-2 := $(EXEC_DIR)/step1_read_print \ + $(EXEC_DIR)/step2_eval +STEP3 := $(EXEC_DIR)/step3_env +STEPS := $(STEP0) $(STEP1-2) $(STEP3) $(UPPER_STEPS) all: $(STEPS) -dist: mal - -mal: stepA_mal - cp $< $@ - -%: %.rs +$(STEPS): $(EXEC_DIR)/%: %.rs cargo build --release --bin $* - cp target/release/$* $@ -STEP0_DEPS = Cargo.toml -STEP1_DEPS = $(STEP0_DEPS) types.rs reader.rs printer.rs -STEP3_DEPS = $(STEP1_DEPS) env.rs -STEP4_DEPS = $(STEP3_DEPS) core.rs +$(STEP1-2) $(STEP3) $(UPPER_STEPS): types.rs reader.rs printer.rs +$(STEP3) $(UPPER_STEPS): env.rs +$(UPPER_STEPS): core.rs -step0_repl: $(STEP0_DEPS) -step1_read_print step2_eval: $(STEP1_DEPS) -step3_env: $(STEP3_DEPS) -$(UPPER_STEPS): $(STEP4_DEPS) +lint: + rustfmt *.rs + cargo clippy .PHONY: clean clean: cargo clean - rm -f $(STEPS) - rm -f mal diff --git a/impls/rust/core.rs b/impls/rust/core.rs index 58400f7c4e..34dd628cff 100644 --- a/impls/rust/core.rs +++ b/impls/rust/core.rs @@ -16,7 +16,7 @@ use crate::types::{MalArgs, MalRet, MalVal, _assoc, _dissoc, atom, error, func, macro_rules! fn_t_int_int { ($ret:ident, $fn:expr) => {{ - |a: MalArgs| match (a[0].clone(), a[1].clone()) { + |a: MalArgs| match (&a[0], &a[1]) { (Int(a0), Int(a1)) => Ok($ret($fn(a0, a1))), _ => error("expecting (int,int) args"), } @@ -37,8 +37,8 @@ macro_rules! fn_is_type { macro_rules! fn_str { ($fn:expr) => {{ - |a: MalArgs| match a[0].clone() { - Str(a0) => $fn(a0), + |a: MalArgs| match &a[0] { + Str(a0) => $fn(&a0), _ => error("expecting (str) arg"), } }}; @@ -53,7 +53,8 @@ fn symbol(a: MalArgs) -> MalRet { fn readline(a: MalArgs) -> MalRet { lazy_static! { - static ref RL: Mutex> = Mutex::new(Editor::<()>::new()); + static ref RL: Mutex> + = Mutex::new(Editor::<(), rustyline::history::DefaultHistory>::new().unwrap()); } //let mut rl = Editor::<()>::new(); @@ -79,7 +80,7 @@ fn readline(a: MalArgs) -> MalRet { } } -fn slurp(f: String) -> MalRet { +fn slurp(f: &str) -> MalRet { let mut s = String::new(); match File::open(f).and_then(|mut f| f.read_to_string(&mut s)) { Ok(_) => Ok(Str(s)), @@ -138,7 +139,7 @@ fn keys(a: MalArgs) -> MalRet { fn vals(a: MalArgs) -> MalRet { match a[0] { - Hash(ref hm, _) => Ok(list!(hm.values().map(|v| { v.clone() }).collect())), + Hash(ref hm, _) => Ok(list!(hm.values().cloned().collect())), _ => error("keys requires Hash Map"), } } @@ -212,7 +213,7 @@ fn apply(a: MalArgs) -> MalRet { List(ref v, _) | Vector(ref v, _) => { let f = &a[0]; let mut fargs = a[1..a.len() - 1].to_vec(); - fargs.extend_from_slice(&v); + fargs.extend_from_slice(v); f.apply(fargs) } _ => error("apply called with non-seq"), @@ -238,7 +239,7 @@ fn conj(a: MalArgs) -> MalRet { let sl = a[1..] .iter() .rev() - .map(|a| a.clone()) + .cloned() .collect::>(); Ok(list!([&sl[..], v].concat())) } @@ -251,7 +252,7 @@ fn seq(a: MalArgs) -> MalRet { match a[0] { List(ref v, _) | Vector(ref v, _) if v.len() == 0 => Ok(Nil), List(ref v, _) | Vector(ref v, _) => Ok(list!(v.to_vec())), - Str(ref s) if s.len() == 0 => Ok(Nil), + Str(ref s) if s.is_empty() => Ok(Nil), Str(ref s) if !a[0].keyword_q() => { Ok(list!(s.chars().map(|c| { Str(c.to_string()) }).collect())) } @@ -271,12 +272,12 @@ pub fn ns() -> Vec<(&'static str, MalVal)> { ("symbol?", func(fn_is_type!(Sym(_)))), ( "string?", - func(fn_is_type!(Str(ref s) if !s.starts_with("\u{29e}"))), + func(fn_is_type!(Str(ref s) if !s.starts_with('\u{29e}'))), ), ("keyword", func(|a| a[0].keyword())), ( "keyword?", - func(fn_is_type!(Str(ref s) if s.starts_with("\u{29e}"))), + func(fn_is_type!(Str(ref s) if s.starts_with('\u{29e}'))), ), ("number?", func(fn_is_type!(Int(_)))), ( @@ -303,9 +304,9 @@ pub fn ns() -> Vec<(&'static str, MalVal)> { Ok(Nil) }), ), - ("read-string", func(fn_str!(|s| { read_str(s) }))), + ("read-string", func(fn_str!(read_str))), ("readline", func(readline)), - ("slurp", func(fn_str!(|f| { slurp(f) }))), + ("slurp", func(fn_str!(slurp))), ("<", func(fn_t_int_int!(Bool, |i, j| { i < j }))), ("<=", func(fn_t_int_int!(Bool, |i, j| { i <= j }))), (">", func(fn_t_int_int!(Bool, |i, j| { i > j }))), @@ -316,11 +317,11 @@ pub fn ns() -> Vec<(&'static str, MalVal)> { ("/", func(fn_t_int_int!(Int, |i, j| { i / j }))), ("time-ms", func(time_ms)), ("sequential?", func(fn_is_type!(List(_, _), Vector(_, _)))), - ("list", func(|a| Ok(list!(a)))), + ("list", func(|a| Ok(list!(a.to_vec())))), ("list?", func(fn_is_type!(List(_, _)))), - ("vector", func(|a| Ok(vector!(a)))), + ("vector", func(|a| Ok(vector!(a.to_vec())))), ("vector?", func(fn_is_type!(Vector(_, _)))), - ("hash-map", func(|a| hash_map(a))), + ("hash-map", func(hash_map)), ("map?", func(fn_is_type!(Hash(_, _)))), ("assoc", func(assoc)), ("dissoc", func(dissoc)), diff --git a/impls/rust/env.rs b/impls/rust/env.rs index d1dfc96429..e8f0ccc56b 100644 --- a/impls/rust/env.rs +++ b/impls/rust/env.rs @@ -7,7 +7,6 @@ use crate::types::MalErr::ErrString; use crate::types::MalVal::{List, Nil, Sym, Vector}; use crate::types::{error, MalErr, MalRet, MalVal}; -#[derive(Debug)] pub struct EnvStruct { data: RefCell>, pub outer: Option, @@ -21,23 +20,23 @@ pub type Env = Rc; pub fn env_new(outer: Option) -> Env { Rc::new(EnvStruct { data: RefCell::new(FnvHashMap::default()), - outer: outer, + outer, }) } // TODO: mbinds and exprs as & types -pub fn env_bind(outer: Option, mbinds: MalVal, exprs: Vec) -> Result { +pub fn env_bind(outer: Option, mbinds: &MalVal, exprs: Vec) -> Result { let env = env_new(outer); match mbinds { List(binds, _) | Vector(binds, _) => { for (i, b) in binds.iter().enumerate() { match b { Sym(s) if s == "&" => { - env_set(&env, binds[i + 1].clone(), list!(exprs[i..].to_vec()))?; + env_set(&env, &binds[i + 1], list!(exprs[i..].to_vec()))?; break; } _ => { - env_set(&env, b.clone(), exprs[i].clone())?; + env_set(&env, b, exprs[i].clone())?; } } } @@ -48,19 +47,30 @@ pub fn env_bind(outer: Option, mbinds: MalVal, exprs: Vec) -> Resul } pub fn env_get(env: &Env, key: &str) -> Option { - match env.data.borrow().get(key) { - Some(value) => Some(value.clone()), - None => match &env.outer { - None => None, - Some(outer) => env_get(&outer, key), + let mut mut_env = env; + loop { + if let Some(value) = mut_env.data.borrow().get(key) { + return Some(value.clone()); + } else if let Some(outer) = &mut_env.outer { + mut_env = outer; + } else { + return None; } } } -pub fn env_set(env: &Env, key: MalVal, val: MalVal) -> MalRet { +pub fn env_find_repl(env: &Env) -> Env { + let mut mut_env = env; + while let Some(outer) = &mut_env.outer { + mut_env = outer; + } + mut_env.clone() +} + +pub fn env_set(env: &Env, key: &MalVal, val: MalVal) -> MalRet { match key { - Sym(ref s) => { - env.data.borrow_mut().insert(s.to_string(), val.clone()); + Sym(s) => { + env_sets(env, s, val.clone()); Ok(val) } _ => error("Env.set called with non-Str"), diff --git a/impls/rust/printer.rs b/impls/rust/printer.rs index 6cd4bc8b4d..d24ea58558 100644 --- a/impls/rust/printer.rs +++ b/impls/rust/printer.rs @@ -22,8 +22,8 @@ impl MalVal { Int(i) => format!("{}", i), //Float(f) => format!("{}", f), Str(s) => { - if s.starts_with("\u{29e}") { - format!(":{}", &s[2..]) + if let Some(keyword) = s.strip_prefix('\u{29e}') { + format!(":{}", keyword) } else if print_readably { format!("\"{}\"", escape_str(s)) } else { @@ -31,8 +31,8 @@ impl MalVal { } } Sym(s) => s.clone(), - List(l, _) => pr_seq(&**l, print_readably, "(", ")", " "), - Vector(l, _) => pr_seq(&**l, print_readably, "[", "]", " "), + List(l, _) => pr_seq(l, print_readably, "(", ")", " "), + Vector(l, _) => pr_seq(l, print_readably, "[", "]", " "), Hash(hm, _) => { let l: Vec = hm .iter() @@ -40,7 +40,7 @@ impl MalVal { .collect(); pr_seq(&l, print_readably, "{", "}", " ") } - Func(f, _) => format!("#", f), + Func(_, _) => String::from("#"), MalFunc { ast: a, params: p, .. } => format!("(fn* {} {})", p.pr_str(true), a.pr_str(true)), @@ -50,11 +50,11 @@ impl MalVal { } pub fn pr_seq( - seq: &Vec, + seq: &[MalVal], print_readably: bool, start: &str, end: &str, - join: &str, + join: &str ) -> String { let strs: Vec = seq.iter().map(|x| x.pr_str(print_readably)).collect(); format!("{}{}{}", start, strs.join(join), end) diff --git a/impls/rust/reader.rs b/impls/rust/reader.rs index 4355ff568c..5a82529a1a 100644 --- a/impls/rust/reader.rs +++ b/impls/rust/reader.rs @@ -13,18 +13,18 @@ struct Reader { impl Reader { fn next(&mut self) -> Result { - self.pos = self.pos + 1; + self.pos += 1; Ok(self .tokens .get(self.pos - 1) - .ok_or(ErrString("underflow".to_string()))? + .ok_or_else(|| ErrString("underflow".to_string()))? .to_string()) } fn peek(&self) -> Result { Ok(self .tokens .get(self.pos) - .ok_or(ErrString("underflow".to_string()))? + .ok_or_else(|| ErrString("underflow".to_string()))? .to_string()) } } @@ -39,7 +39,7 @@ fn tokenize(str: &str) -> Vec { let mut res = vec![]; for cap in RE.captures_iter(str) { - if cap[1].starts_with(";") { + if cap[1].starts_with(';') { continue; } res.push(String::from(&cap[1])); @@ -51,8 +51,8 @@ fn unescape_str(s: &str) -> String { lazy_static! { static ref RE: Regex = Regex::new(r#"\\(.)"#).unwrap(); } - RE.replace_all(&s, |caps: &Captures| { - format!("{}", if &caps[1] == "n" { "\n" } else { &caps[1] }) + RE.replace_all(s, |caps: &Captures| { + if &caps[1] == "n" { "\n" } else { &caps[1] }.to_string() }) .to_string() } @@ -72,10 +72,10 @@ fn read_atom(rdr: &mut Reader) -> MalRet { Ok(Int(token.parse().unwrap())) } else if STR_RE.is_match(&token) { Ok(Str(unescape_str(&token[1..token.len() - 1]))) - } else if token.starts_with("\"") { + } else if token.starts_with('\"') { error("expected '\"', got EOF") - } else if token.starts_with(":") { - Ok(Str(format!("\u{29e}{}", &token[1..]))) + } else if let Some(keyword) = token.strip_prefix(':') { + Ok(Str(format!("\u{29e}{}", keyword))) } else { Ok(Sym(token.to_string())) } @@ -143,14 +143,14 @@ fn read_form(rdr: &mut Reader) -> MalRet { } } -pub fn read_str(str: String) -> MalRet { - let tokens = tokenize(&str); +pub fn read_str(str: &str) -> MalRet { + let tokens = tokenize(str); //println!("tokens: {:?}", tokens); - if tokens.len() == 0 { + if tokens.is_empty() { return error("no input"); } read_form(&mut Reader { pos: 0, - tokens: tokens, + tokens }) } diff --git a/impls/rust/run b/impls/rust/run index 8ba68a5484..06764851ce 100755 --- a/impls/rust/run +++ b/impls/rust/run @@ -1,2 +1,2 @@ #!/bin/bash -exec $(dirname $0)/${STEP:-stepA_mal} "${@}" +exec $(dirname $0)/target/release/${STEP:-stepA_mal} "${@}" diff --git a/impls/rust/step0_repl.rs b/impls/rust/step0_repl.rs index 3468e791d0..b3311812b0 100644 --- a/impls/rust/step0_repl.rs +++ b/impls/rust/step0_repl.rs @@ -5,7 +5,7 @@ use rustyline::Editor; fn main() { // `()` can be used when no completer is required - let mut rl = Editor::<()>::new(); + let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new().unwrap(); if rl.load_history(".mal-history").is_err() { eprintln!("No previous history."); } @@ -14,9 +14,9 @@ fn main() { let readline = rl.readline("user> "); match readline { Ok(line) => { - rl.add_history_entry(&line); + let _ = rl.add_history_entry(&line); rl.save_history(".mal-history").unwrap(); - if line.len() > 0 { + if !line.is_empty() { println!("{}", line); } } diff --git a/impls/rust/step1_read_print.rs b/impls/rust/step1_read_print.rs index 3b5394e550..8aa9eaa38f 100644 --- a/impls/rust/step1_read_print.rs +++ b/impls/rust/step1_read_print.rs @@ -20,7 +20,7 @@ mod env; fn main() { // `()` can be used when no completer is required - let mut rl = Editor::<()>::new(); + let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new().unwrap(); if rl.load_history(".mal-history").is_err() { eprintln!("No previous history."); } @@ -29,10 +29,10 @@ fn main() { let readline = rl.readline("user> "); match readline { Ok(line) => { - rl.add_history_entry(&line); + let _ = rl.add_history_entry(&line); rl.save_history(".mal-history").unwrap(); - if line.len() > 0 { - match reader::read_str(line) { + if !line.is_empty() { + match reader::read_str(&line) { Ok(mv) => { println!("{}", mv.pr_str(true)); } diff --git a/impls/rust/step2_eval.rs b/impls/rust/step2_eval.rs index 9bd190de09..3f9556e1a5 100644 --- a/impls/rust/step2_eval.rs +++ b/impls/rust/step2_eval.rs @@ -28,52 +28,44 @@ pub type Env = FnvHashMap; // read fn read(str: &str) -> MalRet { - reader::read_str(str.to_string()) + reader::read_str(str) } // eval -fn eval_ast(v: &MalArgs, env: &Env) -> Result { - let mut lst: MalArgs = vec![]; - for a in v.iter() { - match eval(a.clone(), env.clone()) { - Ok(elt) => lst.push(elt), - Err(e) => return Err(e), - } - } - return Ok(lst); -} - -fn eval(ast: MalVal, env: Env) -> MalRet { - // println!("EVAL: {}", print(&ast)), +fn eval(ast: &MalVal, env: &Env) -> MalRet { + // println!("EVAL: {}", print(&ast)); match ast { Sym(sym) => Ok(env - .get(&sym) - .ok_or(ErrString(format!("'{}' not found", sym)))? + .get(sym) + .ok_or_else(|| ErrString(format!("'{}' not found", sym)))? .clone()), - Vector(ref v, _) => match eval_ast(&v, &env) { - Ok(lst) => Ok(vector!(lst)), - Err(e) => Err(e), + Vector(v, _) => { + let mut lst: MalArgs = vec![]; + for a in v.iter() { + lst.push(eval(a, env)?); + } + Ok(vector!(lst)) } Hash(hm, _) => { let mut new_hm: FnvHashMap = FnvHashMap::default(); for (k, v) in hm.iter() { - new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?); + new_hm.insert(k.to_string(), eval(v, env)?); } Ok(Hash(Rc::new(new_hm), Rc::new(Nil))) } - List(ref l, _) => { - if l.len() == 0 { - return Ok(ast); + List(l, _) => { + if l.is_empty() { + return Ok(ast.clone()); } - match eval_ast(&l, &env) { - Ok(el) => { - let ref f = el[0].clone(); - f.apply(el[1..].to_vec()) - } - Err(e) => return Err(e), + let a0 = &l[0]; + let f = eval(a0, env)?; + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); } + f.apply(args) } - _ => Ok(ast), + _ => Ok(ast.clone()), } } @@ -84,7 +76,7 @@ fn print(ast: &MalVal) -> String { fn rep(str: &str, env: &Env) -> Result { let ast = read(str)?; - let exp = eval(ast, env.clone())?; + let exp = eval(&ast, env)?; Ok(print(&exp)) } @@ -97,7 +89,7 @@ fn int_op(op: fn(i64, i64) -> i64, a: MalArgs) -> MalRet { fn main() { // `()` can be used when no completer is required - let mut rl = Editor::<()>::new(); + let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new().unwrap(); if rl.load_history(".mal-history").is_err() { eprintln!("No previous history."); } @@ -112,9 +104,9 @@ fn main() { let readline = rl.readline("user> "); match readline { Ok(line) => { - rl.add_history_entry(&line); + let _ = rl.add_history_entry(&line); rl.save_history(".mal-history").unwrap(); - if line.len() > 0 { + if !line.is_empty() { match rep(&line, &repl_env) { Ok(out) => println!("{}", out), Err(e) => println!("Error: {}", format_error(e)), diff --git a/impls/rust/step3_env.rs b/impls/rust/step3_env.rs index 354eabd27a..528af48e49 100644 --- a/impls/rust/step3_env.rs +++ b/impls/rust/step3_env.rs @@ -25,69 +25,51 @@ use crate::env::{env_get, env_new, env_set, env_sets, Env}; // read fn read(str: &str) -> MalRet { - reader::read_str(str.to_string()) + reader::read_str(str) } // eval -fn eval_ast(v: &MalArgs, env: &Env) -> Result { - let mut lst: MalArgs = vec![]; - for a in v.iter() { - match eval(a.clone(), env.clone()) { - Ok(elt) => lst.push(elt), - Err(e) => return Err(e), - } - } - return Ok(lst); -} - -fn eval(ast: MalVal, env: Env) -> MalRet { - match env_get(&env, "DEBUG-EVAL") { +fn eval(ast: &MalVal, env: &Env) -> MalRet { + match env_get(env, "DEBUG-EVAL") { None | Some(Bool(false)) | Some(Nil) => (), - _ => println!("EVAL: {}", print(&ast)), + _ => println!("EVAL: {}", print(ast)), } match ast { - Sym(ref s) => match env_get(&env, s) { + Sym(s) => match env_get(env, s) { Some(r) => Ok(r), None => error (&format!("'{}' not found", s)), } - Vector(ref v, _) => match eval_ast(&v, &env) { - Ok(lst) => Ok(vector!(lst)), - Err(e) => Err(e), + Vector(v, _) => { + let mut lst: MalArgs = vec![]; + for a in v.iter() { + lst.push(eval(a, env)?); + } + Ok(vector!(lst)) } Hash(hm, _) => { let mut new_hm: FnvHashMap = FnvHashMap::default(); for (k, v) in hm.iter() { - new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?); + new_hm.insert(k.to_string(), eval(v, env)?); } Ok(Hash(Rc::new(new_hm), Rc::new(Nil))) } - List(ref l, _) => { - if l.len() == 0 { - return Ok(ast); + List(l, _) => { + if l.is_empty() { + return Ok(ast.clone()); } let a0 = &l[0]; match a0 { - Sym(ref a0sym) if a0sym == "def!" => { - env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?) + Sym(a0sym) if a0sym == "def!" => { + env_set(env, &l[1], eval(&l[2], env)?) } - Sym(ref a0sym) if a0sym == "let*" => { - let let_env = env_new(Some(env.clone())); - let (a1, a2) = (l[1].clone(), l[2].clone()); + Sym(a0sym) if a0sym == "let*" => { + let let_env = &env_new(Some(env.clone())); + let (a1, a2) = (&l[1], &l[2]); match a1 { - List(ref binds, _) | Vector(ref binds, _) => { + List(binds, _) | Vector(binds, _) => { for (b, e) in binds.iter().tuples() { - match b { - Sym(_) => { - let _ = env_set( - &let_env, - b.clone(), - eval(e.clone(), let_env.clone())?, - ); - } - _ => { - return error("let* with non-Sym binding"); - } - } + let val = eval(e, let_env)?; + env_set(let_env, b, val)?; } } _ => { @@ -96,16 +78,17 @@ fn eval(ast: MalVal, env: Env) -> MalRet { }; eval(a2, let_env) } - _ => match eval_ast(&l, &env) { - Ok(el) => { - let ref f = el[0].clone(); - f.apply(el[1..].to_vec()) + _ => { + let f = eval(a0, env)?; + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); } - Err(e) => return Err(e), + f.apply(args) }, } } - _ => Ok(ast), + _ => Ok(ast.clone()), } } @@ -116,7 +99,7 @@ fn print(ast: &MalVal) -> String { fn rep(str: &str, env: &Env) -> Result { let ast = read(str)?; - let exp = eval(ast, env.clone())?; + let exp = eval(&ast, env)?; Ok(print(&exp)) } @@ -129,7 +112,7 @@ fn int_op(op: fn(i64, i64) -> i64, a: MalArgs) -> MalRet { fn main() { // `()` can be used when no completer is required - let mut rl = Editor::<()>::new(); + let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new().unwrap(); if rl.load_history(".mal-history").is_err() { eprintln!("No previous history."); } @@ -144,9 +127,9 @@ fn main() { let readline = rl.readline("user> "); match readline { Ok(line) => { - rl.add_history_entry(&line); + let _ = rl.add_history_entry(&line); rl.save_history(".mal-history").unwrap(); - if line.len() > 0 { + if !line.is_empty() { match rep(&line, &repl_env) { Ok(out) => println!("{}", out), Err(e) => println!("Error: {}", format_error(e)), diff --git a/impls/rust/step4_if_fn_do.rs b/impls/rust/step4_if_fn_do.rs index b0ddf457ac..1adfede538 100644 --- a/impls/rust/step4_if_fn_do.rs +++ b/impls/rust/step4_if_fn_do.rs @@ -26,69 +26,51 @@ mod core; // read fn read(str: &str) -> MalRet { - reader::read_str(str.to_string()) + reader::read_str(str) } // eval -fn eval_ast(v: &MalArgs, env: &Env) -> Result { - let mut lst: MalArgs = vec![]; - for a in v.iter() { - match eval(a.clone(), env.clone()) { - Ok(elt) => lst.push(elt), - Err(e) => return Err(e), - } - } - return Ok(lst); -} - -fn eval(ast: MalVal, env: Env) -> MalRet { - match env_get(&env, "DEBUG-EVAL") { +fn eval(ast: &MalVal, env: &Env) -> MalRet { + match env_get(env, "DEBUG-EVAL") { None | Some(Bool(false)) | Some(Nil) => (), - _ => println!("EVAL: {}", print(&ast)), + _ => println!("EVAL: {}", print(ast)), } match ast { - Sym(ref s) => match env_get(&env, s) { + Sym(s) => match env_get(env, s) { Some(r) => Ok(r), None => error (&format!("'{}' not found", s)), } - Vector(ref v, _) => match eval_ast(&v, &env) { - Ok(lst) => Ok(vector!(lst)), - Err(e) => Err(e), + Vector(v, _) => { + let mut lst: MalArgs = vec![]; + for a in v.iter() { + lst.push(eval(a, env)?); + } + Ok(vector!(lst)) } Hash(hm, _) => { let mut new_hm: FnvHashMap = FnvHashMap::default(); for (k, v) in hm.iter() { - new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?); + new_hm.insert(k.to_string(), eval(v, env)?); } Ok(Hash(Rc::new(new_hm), Rc::new(Nil))) } - List(ref l, _) => { - if l.len() == 0 { - return Ok(ast); + List(l, _) => { + if l.is_empty() { + return Ok(ast.clone()); } let a0 = &l[0]; match a0 { - Sym(ref a0sym) if a0sym == "def!" => { - env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?) + Sym(a0sym) if a0sym == "def!" => { + env_set(env, &l[1], eval(&l[2], env)?) } - Sym(ref a0sym) if a0sym == "let*" => { - let let_env = env_new(Some(env.clone())); - let (a1, a2) = (l[1].clone(), l[2].clone()); + Sym(a0sym) if a0sym == "let*" => { + let let_env = &env_new(Some(env.clone())); + let (a1, a2) = (&l[1], &l[2]); match a1 { - List(ref binds, _) | Vector(ref binds, _) => { + List(binds, _) | Vector(binds, _) => { for (b, e) in binds.iter().tuples() { - match b { - Sym(_) => { - let _ = env_set( - &let_env, - b.clone(), - eval(e.clone(), let_env.clone())?, - ); - } - _ => { - return error("let* with non-Sym binding"); - } - } + let val = eval(e, let_env)?; + env_set(let_env, b, val)?; } } _ => { @@ -97,42 +79,43 @@ fn eval(ast: MalVal, env: Env) -> MalRet { }; eval(a2, let_env) } - Sym(ref a0sym) if a0sym == "do" => { - match eval_ast(&l[1..l.len() - 1].to_vec(), &env) { - Ok(_) => return eval(l.last().unwrap_or(&Nil).clone(), env), - Err(e) => return Err(e), + Sym(a0sym) if a0sym == "do" => { + for i in 1..l.len() - 1 { + let _ = eval(&l[i], env)?; } + eval(l.last().unwrap_or(&Nil), env) } - Sym(ref a0sym) if a0sym == "if" => { - let cond = eval(l[1].clone(), env.clone())?; + Sym(a0sym) if a0sym == "if" => { + let cond = eval(&l[1], env)?; match cond { - Bool(false) | Nil if l.len() >= 4 => eval(l[3].clone(), env.clone()), + Bool(false) | Nil if l.len() >= 4 => eval(&l[3], env), Bool(false) | Nil => Ok(Nil), - _ if l.len() >= 3 => eval(l[2].clone(), env.clone()), + _ if l.len() >= 3 => eval(&l[2], env), _ => Ok(Nil), } } - Sym(ref a0sym) if a0sym == "fn*" => { + Sym(a0sym) if a0sym == "fn*" => { let (a1, a2) = (l[1].clone(), l[2].clone()); Ok(MalFunc { - eval: eval, + eval, ast: Rc::new(a2), - env: env, + env: env.clone(), params: Rc::new(a1), is_macro: false, meta: Rc::new(Nil), }) } - _ => match eval_ast(&l, &env) { - Ok(el) => { - let ref f = el[0].clone(); - f.apply(el[1..].to_vec()) + _ => { + let f = eval(a0, env)?; + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); } - Err(e) => return Err(e), + f.apply(args) }, } } - _ => Ok(ast), + _ => Ok(ast.clone()), } } @@ -143,13 +126,22 @@ fn print(ast: &MalVal) -> String { fn rep(str: &str, env: &Env) -> Result { let ast = read(str)?; - let exp = eval(ast, env.clone())?; + let exp = eval(&ast, env)?; Ok(print(&exp)) } +fn re(str: &str, env: &Env) { + if let Ok(ast) = read(str) { + if eval(&ast, env).is_ok() { + return; + } + } + panic!("error during startup"); +} + fn main() { // `()` can be used when no completer is required - let mut rl = Editor::<()>::new(); + let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new().unwrap(); if rl.load_history(".mal-history").is_err() { eprintln!("No previous history."); } @@ -161,16 +153,16 @@ fn main() { } // core.mal: defined using the language itself - let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env); + re("(def! not (fn* (a) (if a false true)))", &repl_env); // main repl loop loop { let readline = rl.readline("user> "); match readline { Ok(line) => { - rl.add_history_entry(&line); + let _ = rl.add_history_entry(&line); rl.save_history(".mal-history").unwrap(); - if line.len() > 0 { + if !line.is_empty() { match rep(&line, &repl_env) { Ok(out) => println!("{}", out), Err(e) => println!("Error: {}", format_error(e)), diff --git a/impls/rust/step5_tco.rs b/impls/rust/step5_tco.rs index b9cb5ede6b..f8b2735290 100644 --- a/impls/rust/step5_tco.rs +++ b/impls/rust/step5_tco.rs @@ -26,148 +26,140 @@ mod core; // read fn read(str: &str) -> MalRet { - reader::read_str(str.to_string()) + reader::read_str(str) } // eval -fn eval_ast(v: &MalArgs, env: &Env) -> Result { - let mut lst: MalArgs = vec![]; - for a in v.iter() { - match eval(a.clone(), env.clone()) { - Ok(elt) => lst.push(elt), - Err(e) => return Err(e), - } - } - return Ok(lst); -} - -fn eval(mut ast: MalVal, mut env: Env) -> MalRet { - let ret: MalRet; +fn eval(orig_ast: &MalVal, orig_env: &Env) -> MalRet { + let mut ast = orig_ast; + let mut env = orig_env; + // These variables ensure a sufficient lifetime for the data + // referenced by ast and env. + let mut live_ast; + let mut live_env; 'tco: loop { - match env_get(&env, "DEBUG-EVAL") { + match env_get(env, "DEBUG-EVAL") { None | Some(Bool(false)) | Some(Nil) => (), - _ => println!("EVAL: {}", print(&ast)), + _ => println!("EVAL: {}", print(ast)), } - ret = match ast { - Sym(ref s) => match env_get(&env, s) { - Some(r) => Ok(r), - None => error (&format!("'{}' not found", s)), + match ast { + Sym(s) => match env_get(env, s) { + Some(r) => return Ok(r), + None => return error(&format!("'{}' not found", s)), } - Vector(ref v, _) => match eval_ast(&v, &env) { - Ok(lst) => Ok(vector!(lst)), - Err(e) => Err(e), + Vector(v, _) => { + let mut lst: MalArgs = vec![]; + for a in v.iter() { + lst.push(eval(a, env)?); + } + return Ok(vector!(lst)); } Hash(hm, _) => { let mut new_hm: FnvHashMap = FnvHashMap::default(); for (k, v) in hm.iter() { - new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?); + new_hm.insert(k.to_string(), eval(v, env)?); } - Ok(Hash(Rc::new(new_hm), Rc::new(Nil))) + return Ok(Hash(Rc::new(new_hm), Rc::new(Nil))); } - List(ref l, _) => { - if l.len() == 0 { - return Ok(ast); + List(l, _) => { + if l.is_empty() { + return Ok(ast.clone()); } let a0 = &l[0]; match a0 { - Sym(ref a0sym) if a0sym == "def!" => { - env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?) + Sym(a0sym) if a0sym == "def!" => { + return env_set(env, &l[1], eval(&l[2], env)?); } - Sym(ref a0sym) if a0sym == "let*" => { - env = env_new(Some(env.clone())); - let (a1, a2) = (l[1].clone(), l[2].clone()); + Sym(a0sym) if a0sym == "let*" => { + live_env = env_new(Some(env.clone())); + env = &live_env; + let (a1, a2) = (&l[1], &l[2]); match a1 { - List(ref binds, _) | Vector(ref binds, _) => { + List(binds, _) | Vector(binds, _) => { for (b, e) in binds.iter().tuples() { - match b { - Sym(_) => { - let _ = env_set( - &env, - b.clone(), - eval(e.clone(), env.clone())?, - ); - } - _ => { - return error("let* with non-Sym binding"); - } - } + let val = eval(e, env)?; + env_set(env, b, val)?; } } _ => { return error("let* with non-List bindings"); } }; - ast = a2; + live_ast = a2.clone(); + ast = &live_ast; continue 'tco; } - Sym(ref a0sym) if a0sym == "do" => { - match eval_ast(&l[1..l.len() - 1].to_vec(), &env) { - Ok(_) => { - ast = l.last().unwrap_or(&Nil).clone(); - continue 'tco; - } - Err(e) => return Err(e), + Sym(a0sym) if a0sym == "do" => { + for i in 1..l.len() - 1 { + let _ = eval(&l[i], env)?; } + live_ast = l.last().unwrap_or(&Nil).clone(); + ast = &live_ast; + continue 'tco; } - Sym(ref a0sym) if a0sym == "if" => { - let cond = eval(l[1].clone(), env.clone())?; + Sym(a0sym) if a0sym == "if" => { + let cond = eval(&l[1], env)?; match cond { Bool(false) | Nil if l.len() >= 4 => { - ast = l[3].clone(); + live_ast = l[3].clone(); + ast = &live_ast; continue 'tco; } - Bool(false) | Nil => Ok(Nil), + Bool(false) | Nil => return Ok(Nil), _ if l.len() >= 3 => { - ast = l[2].clone(); + live_ast = l[2].clone(); + ast = &live_ast; continue 'tco; } - _ => Ok(Nil), + _ => return Ok(Nil), } } - Sym(ref a0sym) if a0sym == "fn*" => { + Sym(a0sym) if a0sym == "fn*" => { let (a1, a2) = (l[1].clone(), l[2].clone()); - Ok(MalFunc { - eval: eval, + return Ok(MalFunc { + eval, ast: Rc::new(a2), - env: env, + env: env.clone(), params: Rc::new(a1), is_macro: false, meta: Rc::new(Nil), }) } - _ => match eval_ast(&l, &env) { - Ok(el) => { - let ref f = el[0].clone(); - let args = el[1..].to_vec(); - match f { - Func(_, _) => f.apply(args), - MalFunc { + _ => match eval(a0, env) { + Ok(f @ Func(_, _)) => { + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); + } + return f.apply(args); + } + Ok(MalFunc { ast: mast, env: menv, - params, + params: mparams, .. - } => { - let a = &**mast; - let p = &**params; - env = env_bind(Some(menv.clone()), p.clone(), args)?; - ast = a.clone(); + }) => { + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); + } + live_env = env_bind(Some(menv.clone()), &mparams, args.to_vec())?; + env = &live_env; + live_ast = (*mast).clone(); + ast = &live_ast; continue 'tco; } - _ => error("attempt to call non-function"), - } - } - Err(e) => return Err(e), + Ok(_) => return error("attempt to call non-function"), + e @ Err(_) => return e, }, } } - _ => Ok(ast.clone()), + _ => return Ok(ast.clone()), }; - break; } // end 'tco loop - ret } // print @@ -177,13 +169,22 @@ fn print(ast: &MalVal) -> String { fn rep(str: &str, env: &Env) -> Result { let ast = read(str)?; - let exp = eval(ast, env.clone())?; + let exp = eval(&ast, env)?; Ok(print(&exp)) } +fn re(str: &str, env: &Env) { + if let Ok(ast) = read(str) { + if eval(&ast, env).is_ok() { + return; + } + } + panic!("error during startup"); +} + fn main() { // `()` can be used when no completer is required - let mut rl = Editor::<()>::new(); + let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new().unwrap(); if rl.load_history(".mal-history").is_err() { eprintln!("No previous history."); } @@ -195,16 +196,16 @@ fn main() { } // core.mal: defined using the language itself - let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env); + re("(def! not (fn* (a) (if a false true)))", &repl_env); // main repl loop loop { let readline = rl.readline("user> "); match readline { Ok(line) => { - rl.add_history_entry(&line); + let _ = rl.add_history_entry(&line); rl.save_history(".mal-history").unwrap(); - if line.len() > 0 { + if !line.is_empty() { match rep(&line, &repl_env) { Ok(out) => println!("{}", out), Err(e) => println!("Error: {}", format_error(e)), diff --git a/impls/rust/step6_file.rs b/impls/rust/step6_file.rs index a3e2b53c97..1447e46f14 100644 --- a/impls/rust/step6_file.rs +++ b/impls/rust/step6_file.rs @@ -20,161 +20,155 @@ use crate::types::{error, format_error, MalArgs, MalErr, MalRet, MalVal}; mod env; mod printer; mod reader; -use crate::env::{env_bind, env_get, env_new, env_set, env_sets, Env}; +use crate::env::{env_bind, env_find_repl, env_get, env_new, env_set, env_sets, Env}; #[macro_use] mod core; // read fn read(str: &str) -> MalRet { - reader::read_str(str.to_string()) + reader::read_str(str) } // eval -fn eval_ast(v: &MalArgs, env: &Env) -> Result { - let mut lst: MalArgs = vec![]; - for a in v.iter() { - match eval(a.clone(), env.clone()) { - Ok(elt) => lst.push(elt), - Err(e) => return Err(e), - } - } - return Ok(lst); -} - -fn eval(mut ast: MalVal, mut env: Env) -> MalRet { - let ret: MalRet; +fn eval(orig_ast: &MalVal, orig_env: &Env) -> MalRet { + let mut ast = orig_ast; + let mut env = orig_env; + // These variables ensure a sufficient lifetime for the data + // referenced by ast and env. + let mut live_ast; + let mut live_env; 'tco: loop { - match env_get(&env, "DEBUG-EVAL") { + match env_get(env, "DEBUG-EVAL") { None | Some(Bool(false)) | Some(Nil) => (), - _ => println!("EVAL: {}", print(&ast)), + _ => println!("EVAL: {}", print(ast)), } - ret = match ast { - Sym(ref s) => match env_get(&env, s) { - Some(r) => Ok(r), - None => error (&format!("'{}' not found", s)), + match ast { + Sym(s) => match env_get(env, s) { + Some(r) => return Ok(r), + None => return error(&format!("'{}' not found", s)), } - Vector(ref v, _) => match eval_ast(&v, &env) { - Ok(lst) => Ok(vector!(lst)), - Err(e) => Err(e), + Vector(v, _) => { + let mut lst: MalArgs = vec![]; + for a in v.iter() { + lst.push(eval(a, env)?); + } + return Ok(vector!(lst)); } Hash(hm, _) => { let mut new_hm: FnvHashMap = FnvHashMap::default(); for (k, v) in hm.iter() { - new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?); + new_hm.insert(k.to_string(), eval(v, env)?); } - Ok(Hash(Rc::new(new_hm), Rc::new(Nil))) + return Ok(Hash(Rc::new(new_hm), Rc::new(Nil))); } - List(ref l, _) => { - if l.len() == 0 { - return Ok(ast); + List(l, _) => { + if l.is_empty() { + return Ok(ast.clone()); } let a0 = &l[0]; match a0 { - Sym(ref a0sym) if a0sym == "def!" => { - env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?) + Sym(a0sym) if a0sym == "def!" => { + return env_set(env, &l[1], eval(&l[2], env)?); } - Sym(ref a0sym) if a0sym == "let*" => { - env = env_new(Some(env.clone())); - let (a1, a2) = (l[1].clone(), l[2].clone()); + Sym(a0sym) if a0sym == "let*" => { + live_env = env_new(Some(env.clone())); + env = &live_env; + let (a1, a2) = (&l[1], &l[2]); match a1 { - List(ref binds, _) | Vector(ref binds, _) => { + List(binds, _) | Vector(binds, _) => { for (b, e) in binds.iter().tuples() { - match b { - Sym(_) => { - let _ = env_set( - &env, - b.clone(), - eval(e.clone(), env.clone())?, - ); - } - _ => { - return error("let* with non-Sym binding"); - } - } + let val = eval(e, env)?; + env_set(env, b, val)?; } } _ => { return error("let* with non-List bindings"); } }; - ast = a2; + live_ast = a2.clone(); + ast = &live_ast; continue 'tco; } - Sym(ref a0sym) if a0sym == "do" => { - match eval_ast(&l[1..l.len() - 1].to_vec(), &env) { - Ok(_) => { - ast = l.last().unwrap_or(&Nil).clone(); - continue 'tco; - } - Err(e) => return Err(e), + Sym(a0sym) if a0sym == "do" => { + for i in 1..l.len() - 1 { + let _ = eval(&l[i], env)?; } + live_ast = l.last().unwrap_or(&Nil).clone(); + ast = &live_ast; + continue 'tco; } - Sym(ref a0sym) if a0sym == "if" => { - let cond = eval(l[1].clone(), env.clone())?; + Sym(a0sym) if a0sym == "if" => { + let cond = eval(&l[1], env)?; match cond { Bool(false) | Nil if l.len() >= 4 => { - ast = l[3].clone(); + live_ast = l[3].clone(); + ast = &live_ast; continue 'tco; } - Bool(false) | Nil => Ok(Nil), + Bool(false) | Nil => return Ok(Nil), _ if l.len() >= 3 => { - ast = l[2].clone(); + live_ast = l[2].clone(); + ast = &live_ast; continue 'tco; } - _ => Ok(Nil), + _ => return Ok(Nil), } } - Sym(ref a0sym) if a0sym == "fn*" => { + Sym(a0sym) if a0sym == "fn*" => { let (a1, a2) = (l[1].clone(), l[2].clone()); - Ok(MalFunc { - eval: eval, + return Ok(MalFunc { + eval, ast: Rc::new(a2), - env: env, + env: env.clone(), params: Rc::new(a1), is_macro: false, meta: Rc::new(Nil), }) } - Sym(ref a0sym) if a0sym == "eval" => { - ast = eval(l[1].clone(), env.clone())?; - while let Some(ref e) = env.clone().outer { - env = e.clone(); - } + Sym(a0sym) if a0sym == "eval" => { + // Hard to implement without global variables. + // Normal argument evaluation. + live_ast = eval(&l[1], env)?; + ast = &live_ast; + live_env = env_find_repl(env); + env = &live_env; continue 'tco; } - _ => match eval_ast(&l, &env) { - Ok(el) => { - let ref f = el[0].clone(); - let args = el[1..].to_vec(); - match f { - Func(_, _) => f.apply(args), - MalFunc { + _ => match eval(a0, env) { + Ok(f @ Func(_, _)) => { + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); + } + return f.apply(args); + } + Ok(MalFunc { ast: mast, env: menv, - params, + params: mparams, .. - } => { - let a = &**mast; - let p = &**params; - env = env_bind(Some(menv.clone()), p.clone(), args)?; - ast = a.clone(); + }) => { + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); + } + live_env = env_bind(Some(menv.clone()), &mparams, args.to_vec())?; + env = &live_env; + live_ast = (*mast).clone(); + ast = &live_ast; continue 'tco; } - _ => error("attempt to call non-function"), - } - } - Err(e) => return Err(e), + Ok(_) => return error("attempt to call non-function"), + e @ Err(_) => return e, }, } } - _ => Ok(ast.clone()), + _ => return Ok(ast.clone()), }; - break; } // end 'tco loop - ret } // print @@ -184,16 +178,25 @@ fn print(ast: &MalVal) -> String { fn rep(str: &str, env: &Env) -> Result { let ast = read(str)?; - let exp = eval(ast, env.clone())?; + let exp = eval(&ast, env)?; Ok(print(&exp)) } +fn re(str: &str, env: &Env) { + if let Ok(ast) = read(str) { + if eval(&ast, env).is_ok() { + return; + } + } + panic!("error during startup"); +} + fn main() { let mut args = std::env::args(); let arg1 = args.nth(1); // `()` can be used when no completer is required - let mut rl = Editor::<()>::new(); + let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new().unwrap(); if rl.load_history(".mal-history").is_err() { eprintln!("No previous history."); } @@ -206,21 +209,16 @@ fn main() { env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect())); // core.mal: defined using the language itself - let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env); - let _ = rep( + re("(def! not (fn* (a) (if a false true)))", &repl_env); + re( "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", &repl_env, ); - // Invoked with arguments if let Some(f) = arg1 { - match rep(&format!("(load-file \"{}\")", f), &repl_env) { - Ok(_) => std::process::exit(0), - Err(e) => { - println!("Error: {}", format_error(e)); - std::process::exit(1); - } - } + // Invoked with arguments + re(&format!("(load-file \"{}\")", f), &repl_env); + std::process::exit(0); } // main repl loop @@ -228,9 +226,9 @@ fn main() { let readline = rl.readline("user> "); match readline { Ok(line) => { - rl.add_history_entry(&line); + let _ = rl.add_history_entry(&line); rl.save_history(".mal-history").unwrap(); - if line.len() > 0 { + if !line.is_empty() { match rep(&line, &repl_env) { Ok(out) => println!("{}", out), Err(e) => println!("Error: {}", format_error(e)), diff --git a/impls/rust/step7_quote.rs b/impls/rust/step7_quote.rs index f7ac8d3081..2c598065a5 100644 --- a/impls/rust/step7_quote.rs +++ b/impls/rust/step7_quote.rs @@ -20,13 +20,13 @@ use crate::types::{error, format_error, MalArgs, MalErr, MalRet, MalVal}; mod env; mod printer; mod reader; -use crate::env::{env_bind, env_get, env_new, env_set, env_sets, Env}; +use crate::env::{env_bind, env_find_repl, env_get, env_new, env_set, env_sets, Env}; #[macro_use] mod core; // read fn read(str: &str) -> MalRet { - reader::read_str(str.to_string()) + reader::read_str(str) } // eval @@ -44,9 +44,9 @@ fn qq_iter(elts: &MalArgs) -> MalVal { } } } - acc = list![Sym("cons".to_string()), quasiquote(&elt), acc]; + acc = list![Sym("cons".to_string()), quasiquote(elt), acc]; } - return acc; + acc } fn quasiquote(ast: &MalVal) -> MalVal { @@ -59,164 +59,159 @@ fn quasiquote(ast: &MalVal) -> MalVal { } } } - return qq_iter(&v); + qq_iter(v) }, - Vector(v, _) => return list![Sym("vec".to_string()), qq_iter(&v)], - Hash(_, _) | Sym(_)=> return list![Sym("quote".to_string()), ast.clone()], + Vector(v, _) => list![Sym("vec".to_string()), qq_iter(v)], + Hash(_, _) | Sym(_)=> list![Sym("quote".to_string()), ast.clone()], _ => ast.clone(), } } -fn eval_ast(v: &MalArgs, env: &Env) -> Result { - let mut lst: MalArgs = vec![]; - for a in v.iter() { - match eval(a.clone(), env.clone()) { - Ok(elt) => lst.push(elt), - Err(e) => return Err(e), - } - } - return Ok(lst); -} - -fn eval(mut ast: MalVal, mut env: Env) -> MalRet { - let ret: MalRet; +fn eval(orig_ast: &MalVal, orig_env: &Env) -> MalRet { + let mut ast = orig_ast; + let mut env = orig_env; + // These variables ensure a sufficient lifetime for the data + // referenced by ast and env. + let mut live_ast; + let mut live_env; 'tco: loop { - match env_get(&env, "DEBUG-EVAL") { + match env_get(env, "DEBUG-EVAL") { None | Some(Bool(false)) | Some(Nil) => (), - _ => println!("EVAL: {}", print(&ast)), + _ => println!("EVAL: {}", print(ast)), } - ret = match ast { - Sym(ref s) => match env_get(&env, s) { - Some(r) => Ok(r), - None => error (&format!("'{}' not found", s)), + match ast { + Sym(s) => match env_get(env, s) { + Some(r) => return Ok(r), + None => return error(&format!("'{}' not found", s)), } - Vector(ref v, _) => match eval_ast(&v, &env) { - Ok(lst) => Ok(vector!(lst)), - Err(e) => Err(e), + Vector(v, _) => { + let mut lst: MalArgs = vec![]; + for a in v.iter() { + lst.push(eval(a, env)?); + } + return Ok(vector!(lst)); } Hash(hm, _) => { let mut new_hm: FnvHashMap = FnvHashMap::default(); for (k, v) in hm.iter() { - new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?); + new_hm.insert(k.to_string(), eval(v, env)?); } - Ok(Hash(Rc::new(new_hm), Rc::new(Nil))) + return Ok(Hash(Rc::new(new_hm), Rc::new(Nil))); } - List(ref l, _) => { - if l.len() == 0 { - return Ok(ast); + List(l, _) => { + if l.is_empty() { + return Ok(ast.clone()); } let a0 = &l[0]; match a0 { - Sym(ref a0sym) if a0sym == "def!" => { - env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?) + Sym(a0sym) if a0sym == "def!" => { + return env_set(env, &l[1], eval(&l[2], env)?); } - Sym(ref a0sym) if a0sym == "let*" => { - env = env_new(Some(env.clone())); - let (a1, a2) = (l[1].clone(), l[2].clone()); + Sym(a0sym) if a0sym == "let*" => { + live_env = env_new(Some(env.clone())); + env = &live_env; + let (a1, a2) = (&l[1], &l[2]); match a1 { - List(ref binds, _) | Vector(ref binds, _) => { + List(binds, _) | Vector(binds, _) => { for (b, e) in binds.iter().tuples() { - match b { - Sym(_) => { - let _ = env_set( - &env, - b.clone(), - eval(e.clone(), env.clone())?, - ); - } - _ => { - return error("let* with non-Sym binding"); - } - } + let val = eval(e, env)?; + env_set(env, b, val)?; } } _ => { return error("let* with non-List bindings"); } }; - ast = a2; + live_ast = a2.clone(); + ast = &live_ast; continue 'tco; } - Sym(ref a0sym) if a0sym == "quote" => Ok(l[1].clone()), - Sym(ref a0sym) if a0sym == "quasiquote" => { - ast = quasiquote(&l[1]); + Sym(a0sym) if a0sym == "quote" => return Ok(l[1].clone()), + Sym(a0sym) if a0sym == "quasiquote" => { + live_ast = quasiquote(&l[1]); + ast = &live_ast; continue 'tco; } - Sym(ref a0sym) if a0sym == "do" => { - match eval_ast(&l[1..l.len() - 1].to_vec(), &env) { - Ok(_) => { - ast = l.last().unwrap_or(&Nil).clone(); - continue 'tco; - } - Err(e) => return Err(e), + Sym(a0sym) if a0sym == "do" => { + for i in 1..l.len() - 1 { + let _ = eval(&l[i], env)?; } + live_ast = l.last().unwrap_or(&Nil).clone(); + ast = &live_ast; + continue 'tco; } - Sym(ref a0sym) if a0sym == "if" => { - let cond = eval(l[1].clone(), env.clone())?; + Sym(a0sym) if a0sym == "if" => { + let cond = eval(&l[1], env)?; match cond { Bool(false) | Nil if l.len() >= 4 => { - ast = l[3].clone(); + live_ast = l[3].clone(); + ast = &live_ast; continue 'tco; } - Bool(false) | Nil => Ok(Nil), + Bool(false) | Nil => return Ok(Nil), _ if l.len() >= 3 => { - ast = l[2].clone(); + live_ast = l[2].clone(); + ast = &live_ast; continue 'tco; } - _ => Ok(Nil), + _ => return Ok(Nil), } } - Sym(ref a0sym) if a0sym == "fn*" => { + Sym(a0sym) if a0sym == "fn*" => { let (a1, a2) = (l[1].clone(), l[2].clone()); - Ok(MalFunc { - eval: eval, + return Ok(MalFunc { + eval, ast: Rc::new(a2), - env: env, + env: env.clone(), params: Rc::new(a1), is_macro: false, meta: Rc::new(Nil), }) } - Sym(ref a0sym) if a0sym == "eval" => { - ast = eval(l[1].clone(), env.clone())?; - while let Some(ref e) = env.clone().outer { - env = e.clone(); - } + Sym(a0sym) if a0sym == "eval" => { + // Hard to implement without global variables. + // Normal argument evaluation. + live_ast = eval(&l[1], env)?; + ast = &live_ast; + live_env = env_find_repl(env); + env = &live_env; continue 'tco; } - _ => match eval_ast(&l, &env) { - Ok(el) => { - let ref f = el[0].clone(); - let args = el[1..].to_vec(); - match f { - Func(_, _) => f.apply(args), - MalFunc { + _ => match eval(a0, env) { + Ok(f @ Func(_, _)) => { + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); + } + return f.apply(args); + } + Ok(MalFunc { ast: mast, env: menv, - params, + params: mparams, .. - } => { - let a = &**mast; - let p = &**params; - env = env_bind(Some(menv.clone()), p.clone(), args)?; - ast = a.clone(); + }) => { + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); + } + live_env = env_bind(Some(menv.clone()), &mparams, args.to_vec())?; + env = &live_env; + live_ast = (*mast).clone(); + ast = &live_ast; continue 'tco; } - _ => error("attempt to call non-function"), - } - } - Err(e) => return Err(e), + Ok(_) => return error("attempt to call non-function"), + e @ Err(_) => return e, }, } } - _ => Ok(ast.clone()), + _ => return Ok(ast.clone()), }; - break; } // end 'tco loop - ret } // print @@ -226,16 +221,25 @@ fn print(ast: &MalVal) -> String { fn rep(str: &str, env: &Env) -> Result { let ast = read(str)?; - let exp = eval(ast, env.clone())?; + let exp = eval(&ast, env)?; Ok(print(&exp)) } +fn re(str: &str, env: &Env) { + if let Ok(ast) = read(str) { + if eval(&ast, env).is_ok() { + return; + } + } + panic!("error during startup"); +} + fn main() { let mut args = std::env::args(); let arg1 = args.nth(1); // `()` can be used when no completer is required - let mut rl = Editor::<()>::new(); + let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new().unwrap(); if rl.load_history(".mal-history").is_err() { eprintln!("No previous history."); } @@ -248,21 +252,16 @@ fn main() { env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect())); // core.mal: defined using the language itself - let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env); - let _ = rep( + re("(def! not (fn* (a) (if a false true)))", &repl_env); + re( "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", &repl_env, ); - // Invoked with arguments if let Some(f) = arg1 { - match rep(&format!("(load-file \"{}\")", f), &repl_env) { - Ok(_) => std::process::exit(0), - Err(e) => { - println!("Error: {}", format_error(e)); - std::process::exit(1); - } - } + // Invoked with arguments + re(&format!("(load-file \"{}\")", f), &repl_env); + std::process::exit(0); } // main repl loop @@ -270,9 +269,9 @@ fn main() { let readline = rl.readline("user> "); match readline { Ok(line) => { - rl.add_history_entry(&line); + let _ = rl.add_history_entry(&line); rl.save_history(".mal-history").unwrap(); - if line.len() > 0 { + if !line.is_empty() { match rep(&line, &repl_env) { Ok(out) => println!("{}", out), Err(e) => println!("Error: {}", format_error(e)), diff --git a/impls/rust/step8_macros.rs b/impls/rust/step8_macros.rs index 6309107617..96ff6f6e9e 100644 --- a/impls/rust/step8_macros.rs +++ b/impls/rust/step8_macros.rs @@ -20,13 +20,13 @@ use crate::types::{error, format_error, MalArgs, MalErr, MalRet, MalVal}; mod env; mod printer; mod reader; -use crate::env::{env_bind, env_get, env_new, env_set, env_sets, Env}; +use crate::env::{env_bind, env_find_repl, env_get, env_new, env_set, env_sets, Env}; #[macro_use] mod core; // read fn read(str: &str) -> MalRet { - reader::read_str(str.to_string()) + reader::read_str(str) } // eval @@ -44,9 +44,9 @@ fn qq_iter(elts: &MalArgs) -> MalVal { } } } - acc = list![Sym("cons".to_string()), quasiquote(&elt), acc]; + acc = list![Sym("cons".to_string()), quasiquote(elt), acc]; } - return acc; + acc } fn quasiquote(ast: &MalVal) -> MalVal { @@ -59,93 +59,83 @@ fn quasiquote(ast: &MalVal) -> MalVal { } } } - return qq_iter(&v); + qq_iter(v) }, - Vector(v, _) => return list![Sym("vec".to_string()), qq_iter(&v)], - Hash(_, _) | Sym(_)=> return list![Sym("quote".to_string()), ast.clone()], + Vector(v, _) => list![Sym("vec".to_string()), qq_iter(v)], + Hash(_, _) | Sym(_)=> list![Sym("quote".to_string()), ast.clone()], _ => ast.clone(), } } -fn eval_ast(v: &MalArgs, env: &Env) -> Result { - let mut lst: MalArgs = vec![]; - for a in v.iter() { - match eval(a.clone(), env.clone()) { - Ok(elt) => lst.push(elt), - Err(e) => return Err(e), - } - } - return Ok(lst); -} - -fn eval(mut ast: MalVal, mut env: Env) -> MalRet { - let ret: MalRet; +fn eval(orig_ast: &MalVal, orig_env: &Env) -> MalRet { + let mut ast = orig_ast; + let mut env = orig_env; + // These variables ensure a sufficient lifetime for the data + // referenced by ast and env. + let mut live_ast; + let mut live_env; 'tco: loop { - match env_get(&env, "DEBUG-EVAL") { + match env_get(env, "DEBUG-EVAL") { None | Some(Bool(false)) | Some(Nil) => (), - _ => println!("EVAL: {}", print(&ast)), + _ => println!("EVAL: {}", print(ast)), } - ret = match ast { - Sym(ref s) => match env_get(&env, s) { - Some(r) => Ok(r), - None => error (&format!("'{}' not found", s)), + match ast { + Sym(s) => match env_get(env, s) { + Some(r) => return Ok(r), + None => return error(&format!("'{}' not found", s)), } - Vector(ref v, _) => match eval_ast(&v, &env) { - Ok(lst) => Ok(vector!(lst)), - Err(e) => Err(e), + Vector(v, _) => { + let mut lst: MalArgs = vec![]; + for a in v.iter() { + lst.push(eval(a, env)?); + } + return Ok(vector!(lst)); } Hash(hm, _) => { let mut new_hm: FnvHashMap = FnvHashMap::default(); for (k, v) in hm.iter() { - new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?); + new_hm.insert(k.to_string(), eval(v, env)?); } - Ok(Hash(Rc::new(new_hm), Rc::new(Nil))) + return Ok(Hash(Rc::new(new_hm), Rc::new(Nil))); } - List(ref l, _) => { - if l.len() == 0 { - return Ok(ast); + List(l, _) => { + if l.is_empty() { + return Ok(ast.clone()); } let a0 = &l[0]; match a0 { - Sym(ref a0sym) if a0sym == "def!" => { - env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?) + Sym(a0sym) if a0sym == "def!" => { + return env_set(env, &l[1], eval(&l[2], env)?); } - Sym(ref a0sym) if a0sym == "let*" => { - env = env_new(Some(env.clone())); - let (a1, a2) = (l[1].clone(), l[2].clone()); + Sym(a0sym) if a0sym == "let*" => { + live_env = env_new(Some(env.clone())); + env = &live_env; + let (a1, a2) = (&l[1], &l[2]); match a1 { - List(ref binds, _) | Vector(ref binds, _) => { + List(binds, _) | Vector(binds, _) => { for (b, e) in binds.iter().tuples() { - match b { - Sym(_) => { - let _ = env_set( - &env, - b.clone(), - eval(e.clone(), env.clone())?, - ); - } - _ => { - return error("let* with non-Sym binding"); - } - } + let val = eval(e, env)?; + env_set(env, b, val)?; } } _ => { return error("let* with non-List bindings"); } }; - ast = a2; + live_ast = a2.clone(); + ast = &live_ast; continue 'tco; } - Sym(ref a0sym) if a0sym == "quote" => Ok(l[1].clone()), - Sym(ref a0sym) if a0sym == "quasiquote" => { - ast = quasiquote(&l[1]); + Sym(a0sym) if a0sym == "quote" => return Ok(l[1].clone()), + Sym(a0sym) if a0sym == "quasiquote" => { + live_ast = quasiquote(&l[1]); + ast = &live_ast; continue 'tco; } - Sym(ref a0sym) if a0sym == "defmacro!" => { - let (a1, a2) = (l[1].clone(), l[2].clone()); - let r = eval(a2, env.clone())?; + Sym(a0sym) if a0sym == "defmacro!" => { + let (a1, a2) = (&l[1], &l[2]); + let r = eval(a2, env)?; match r { MalFunc { eval, @@ -153,102 +143,106 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { env, params, .. - } => Ok(env_set( + } => return env_set( &env, - a1.clone(), + a1, MalFunc { - eval: eval, - ast: ast.clone(), + eval, + ast, env: env.clone(), - params: params.clone(), + params, is_macro: true, meta: Rc::new(Nil), }, - )?), - _ => error("set_macro on non-function"), + ), + _ => return error("set_macro on non-function"), } } - Sym(ref a0sym) if a0sym == "do" => { - match eval_ast(&l[1..l.len() - 1].to_vec(), &env) { - Ok(_) => { - ast = l.last().unwrap_or(&Nil).clone(); - continue 'tco; - } - Err(e) => return Err(e), + Sym(a0sym) if a0sym == "do" => { + for i in 1..l.len() - 1 { + let _ = eval(&l[i], env)?; } + live_ast = l.last().unwrap_or(&Nil).clone(); + ast = &live_ast; + continue 'tco; } - Sym(ref a0sym) if a0sym == "if" => { - let cond = eval(l[1].clone(), env.clone())?; + Sym(a0sym) if a0sym == "if" => { + let cond = eval(&l[1], env)?; match cond { Bool(false) | Nil if l.len() >= 4 => { - ast = l[3].clone(); + live_ast = l[3].clone(); + ast = &live_ast; continue 'tco; } - Bool(false) | Nil => Ok(Nil), + Bool(false) | Nil => return Ok(Nil), _ if l.len() >= 3 => { - ast = l[2].clone(); + live_ast = l[2].clone(); + ast = &live_ast; continue 'tco; } - _ => Ok(Nil), + _ => return Ok(Nil), } } - Sym(ref a0sym) if a0sym == "fn*" => { + Sym(a0sym) if a0sym == "fn*" => { let (a1, a2) = (l[1].clone(), l[2].clone()); - Ok(MalFunc { - eval: eval, + return Ok(MalFunc { + eval, ast: Rc::new(a2), - env: env, + env: env.clone(), params: Rc::new(a1), is_macro: false, meta: Rc::new(Nil), }) } - Sym(ref a0sym) if a0sym == "eval" => { - ast = eval(l[1].clone(), env.clone())?; - while let Some(ref e) = env.clone().outer { - env = e.clone(); - } + Sym(a0sym) if a0sym == "eval" => { + // Hard to implement without global variables. + // Normal argument evaluation. + live_ast = eval(&l[1], env)?; + ast = &live_ast; + live_env = env_find_repl(env); + env = &live_env; continue 'tco; } - _ => match eval(a0.clone(), env.clone()) { - Ok(f @ MalFunc { is_macro: true, .. }) => match f.apply(l[1..].to_vec()) { - Ok(new_ast) => { - ast = new_ast; - continue 'tco; - } - Err(e) => return Err(e), + _ => match eval(a0, env) { + Ok(f @ MalFunc { is_macro: true, .. }) => { + let new_ast = f.apply(l[1..].to_vec())?; + live_ast = new_ast; + ast = &live_ast; + continue 'tco; } - Ok(f @ Func(_, _)) => match eval_ast(&l[1..].to_vec(), &env) { - Ok(args) => f.apply(args), - Err(e) => return Err(e), + Ok(f @ Func(_, _)) => { + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); + } + return f.apply(args); } Ok(MalFunc { - ast: ref mast, - env: ref menv, - params : ref mparams, + ast: mast, + env: menv, + params: mparams, .. - }) => match eval_ast(&l[1..].to_vec(), &env) { - Ok(args) => { - let a = &**mast; - let p = &**mparams; - env = env_bind(Some(menv.clone()), p.clone(), args.to_vec())?; - ast = a.clone(); + }) => { + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); + } + live_env = env_bind(Some(menv.clone()), &mparams, args.to_vec())?; + env = &live_env; + live_ast = (*mast).clone(); + ast = &live_ast; continue 'tco; - } - Err(e) => return Err(e), } - Ok(_) => error("attempt to call non-function"), - Err(e) => return Err(e), + Ok(_) => return error("attempt to call non-function"), + e @ Err(_) => return e, }, } } - _ => Ok(ast.clone()), + _ => return Ok(ast.clone()), }; - break; } // end 'tco loop - ret } // print @@ -258,16 +252,25 @@ fn print(ast: &MalVal) -> String { fn rep(str: &str, env: &Env) -> Result { let ast = read(str)?; - let exp = eval(ast, env.clone())?; + let exp = eval(&ast, env)?; Ok(print(&exp)) } +fn re(str: &str, env: &Env) { + if let Ok(ast) = read(str) { + if eval(&ast, env).is_ok() { + return; + } + } + panic!("error during startup"); +} + fn main() { let mut args = std::env::args(); let arg1 = args.nth(1); // `()` can be used when no completer is required - let mut rl = Editor::<()>::new(); + let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new().unwrap(); if rl.load_history(".mal-history").is_err() { eprintln!("No previous history."); } @@ -280,22 +283,18 @@ fn main() { env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect())); // core.mal: defined using the language itself - let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env); - let _ = rep( + re("(def! not (fn* (a) (if a false true)))", &repl_env); + re( "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", &repl_env, ); - let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", &repl_env); + re("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", + &repl_env); - // Invoked with arguments if let Some(f) = arg1 { - match rep(&format!("(load-file \"{}\")", f), &repl_env) { - Ok(_) => std::process::exit(0), - Err(e) => { - println!("Error: {}", format_error(e)); - std::process::exit(1); - } - } + // Invoked with arguments + re(&format!("(load-file \"{}\")", f), &repl_env); + std::process::exit(0); } // main repl loop @@ -303,9 +302,9 @@ fn main() { let readline = rl.readline("user> "); match readline { Ok(line) => { - rl.add_history_entry(&line); + let _ = rl.add_history_entry(&line); rl.save_history(".mal-history").unwrap(); - if line.len() > 0 { + if !line.is_empty() { match rep(&line, &repl_env) { Ok(out) => println!("{}", out), Err(e) => println!("Error: {}", format_error(e)), diff --git a/impls/rust/step9_try.rs b/impls/rust/step9_try.rs index f71893ab53..7b56dd877e 100644 --- a/impls/rust/step9_try.rs +++ b/impls/rust/step9_try.rs @@ -21,13 +21,13 @@ use crate::types::{error, format_error, MalArgs, MalErr, MalRet, MalVal}; mod env; mod printer; mod reader; -use crate::env::{env_bind, env_get, env_new, env_set, env_sets, Env}; +use crate::env::{env_bind, env_find_repl, env_get, env_new, env_set, env_sets, Env}; #[macro_use] mod core; // read fn read(str: &str) -> MalRet { - reader::read_str(str.to_string()) + reader::read_str(str) } // eval @@ -45,9 +45,9 @@ fn qq_iter(elts: &MalArgs) -> MalVal { } } } - acc = list![Sym("cons".to_string()), quasiquote(&elt), acc]; + acc = list![Sym("cons".to_string()), quasiquote(elt), acc]; } - return acc; + acc } fn quasiquote(ast: &MalVal) -> MalVal { @@ -60,93 +60,83 @@ fn quasiquote(ast: &MalVal) -> MalVal { } } } - return qq_iter(&v); + qq_iter(v) }, - Vector(v, _) => return list![Sym("vec".to_string()), qq_iter(&v)], - Hash(_, _) | Sym(_)=> return list![Sym("quote".to_string()), ast.clone()], + Vector(v, _) => list![Sym("vec".to_string()), qq_iter(v)], + Hash(_, _) | Sym(_)=> list![Sym("quote".to_string()), ast.clone()], _ => ast.clone(), } } -fn eval_ast(v: &MalArgs, env: &Env) -> Result { - let mut lst: MalArgs = vec![]; - for a in v.iter() { - match eval(a.clone(), env.clone()) { - Ok(elt) => lst.push(elt), - Err(e) => return Err(e), - } - } - return Ok(lst); -} - -fn eval(mut ast: MalVal, mut env: Env) -> MalRet { - let ret: MalRet; +fn eval(orig_ast: &MalVal, orig_env: &Env) -> MalRet { + let mut ast = orig_ast; + let mut env = orig_env; + // These variables ensure a sufficient lifetime for the data + // referenced by ast and env. + let mut live_ast; + let mut live_env; 'tco: loop { - match env_get(&env, "DEBUG-EVAL") { + match env_get(env, "DEBUG-EVAL") { None | Some(Bool(false)) | Some(Nil) => (), - _ => println!("EVAL: {}", print(&ast)), + _ => println!("EVAL: {}", print(ast)), } - ret = match ast { - Sym(ref s) => match env_get(&env, s) { - Some(r) => Ok(r), - None => error (&format!("'{}' not found", s)), + match ast { + Sym(s) => match env_get(env, s) { + Some(r) => return Ok(r), + None => return error(&format!("'{}' not found", s)), } - Vector(ref v, _) => match eval_ast(&v, &env) { - Ok(lst) => Ok(vector!(lst)), - Err(e) => Err(e), + Vector(v, _) => { + let mut lst: MalArgs = vec![]; + for a in v.iter() { + lst.push(eval(a, env)?); + } + return Ok(vector!(lst)); } Hash(hm, _) => { let mut new_hm: FnvHashMap = FnvHashMap::default(); for (k, v) in hm.iter() { - new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?); + new_hm.insert(k.to_string(), eval(v, env)?); } - Ok(Hash(Rc::new(new_hm), Rc::new(Nil))) + return Ok(Hash(Rc::new(new_hm), Rc::new(Nil))); } - List(ref l, _) => { - if l.len() == 0 { - return Ok(ast); + List(l, _) => { + if l.is_empty() { + return Ok(ast.clone()); } let a0 = &l[0]; match a0 { - Sym(ref a0sym) if a0sym == "def!" => { - env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?) + Sym(a0sym) if a0sym == "def!" => { + return env_set(env, &l[1], eval(&l[2], env)?); } - Sym(ref a0sym) if a0sym == "let*" => { - env = env_new(Some(env.clone())); - let (a1, a2) = (l[1].clone(), l[2].clone()); + Sym(a0sym) if a0sym == "let*" => { + live_env = env_new(Some(env.clone())); + env = &live_env; + let (a1, a2) = (&l[1], &l[2]); match a1 { - List(ref binds, _) | Vector(ref binds, _) => { + List(binds, _) | Vector(binds, _) => { for (b, e) in binds.iter().tuples() { - match b { - Sym(_) => { - let _ = env_set( - &env, - b.clone(), - eval(e.clone(), env.clone())?, - ); - } - _ => { - return error("let* with non-Sym binding"); - } - } + let val = eval(e, env)?; + env_set(env, b, val)?; } } _ => { return error("let* with non-List bindings"); } }; - ast = a2; + live_ast = a2.clone(); + ast = &live_ast; continue 'tco; } - Sym(ref a0sym) if a0sym == "quote" => Ok(l[1].clone()), - Sym(ref a0sym) if a0sym == "quasiquote" => { - ast = quasiquote(&l[1]); + Sym(a0sym) if a0sym == "quote" => return Ok(l[1].clone()), + Sym(a0sym) if a0sym == "quasiquote" => { + live_ast = quasiquote(&l[1]); + ast = &live_ast; continue 'tco; } - Sym(ref a0sym) if a0sym == "defmacro!" => { - let (a1, a2) = (l[1].clone(), l[2].clone()); - let r = eval(a2, env.clone())?; + Sym(a0sym) if a0sym == "defmacro!" => { + let (a1, a2) = (&l[1], &l[2]); + let r = eval(a2, env)?; match r { MalFunc { eval, @@ -154,122 +144,133 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { env, params, .. - } => Ok(env_set( + } => return env_set( &env, - a1.clone(), + a1, MalFunc { - eval: eval, - ast: ast.clone(), + eval, + ast, env: env.clone(), - params: params.clone(), + params, is_macro: true, meta: Rc::new(Nil), }, - )?), - _ => error("set_macro on non-function"), + ), + _ => return error("set_macro on non-function"), } } - Sym(ref a0sym) if a0sym == "try*" => match eval(l[1].clone(), env.clone()) { - Err(ref e) if l.len() >= 3 => { + Sym(a0sym) if a0sym == "try*" => { + if l.len() < 3 { + live_ast = l[1].clone(); + ast = &live_ast; + continue 'tco; + } + match eval(&l[1], env) { + Err(e) => { let exc = match e { ErrMalVal(mv) => mv.clone(), ErrString(s) => Str(s.to_string()), }; - match l[2].clone() { + match &l[2] { List(c, _) => { - let catch_env = env_bind( - Some(env.clone()), - list!(vec![c[1].clone()]), - vec![exc], - )?; - eval(c[2].clone(), catch_env) + live_env = env_new(Some(env.clone())); + env = &live_env; + env_set(env, &c[1], exc)?; + live_ast = c[2].clone(); + ast = &live_ast; + continue 'tco; } - _ => error("invalid catch block"), + _ => return error("invalid catch block"), } } - res => res, + res => return res, + } }, - Sym(ref a0sym) if a0sym == "do" => { - match eval_ast(&l[1..l.len() - 1].to_vec(), &env) { - Ok(_) => { - ast = l.last().unwrap_or(&Nil).clone(); - continue 'tco; - } - Err(e) => return Err(e), + Sym(a0sym) if a0sym == "do" => { + for i in 1..l.len() - 1 { + let _ = eval(&l[i], env)?; } + live_ast = l.last().unwrap_or(&Nil).clone(); + ast = &live_ast; + continue 'tco; } - Sym(ref a0sym) if a0sym == "if" => { - let cond = eval(l[1].clone(), env.clone())?; + Sym(a0sym) if a0sym == "if" => { + let cond = eval(&l[1], env)?; match cond { Bool(false) | Nil if l.len() >= 4 => { - ast = l[3].clone(); + live_ast = l[3].clone(); + ast = &live_ast; continue 'tco; } - Bool(false) | Nil => Ok(Nil), + Bool(false) | Nil => return Ok(Nil), _ if l.len() >= 3 => { - ast = l[2].clone(); + live_ast = l[2].clone(); + ast = &live_ast; continue 'tco; } - _ => Ok(Nil), + _ => return Ok(Nil), } } - Sym(ref a0sym) if a0sym == "fn*" => { + Sym(a0sym) if a0sym == "fn*" => { let (a1, a2) = (l[1].clone(), l[2].clone()); - Ok(MalFunc { - eval: eval, + return Ok(MalFunc { + eval, ast: Rc::new(a2), - env: env, + env: env.clone(), params: Rc::new(a1), is_macro: false, meta: Rc::new(Nil), }) } - Sym(ref a0sym) if a0sym == "eval" => { - ast = eval(l[1].clone(), env.clone())?; - while let Some(ref e) = env.clone().outer { - env = e.clone(); - } + Sym(a0sym) if a0sym == "eval" => { + // Hard to implement without global variables. + // Normal argument evaluation. + live_ast = eval(&l[1], env)?; + ast = &live_ast; + live_env = env_find_repl(env); + env = &live_env; continue 'tco; } - _ => match eval(a0.clone(), env.clone()) { - Ok(f @ MalFunc { is_macro: true, .. }) => match f.apply(l[1..].to_vec()) { - Ok(new_ast) => { - ast = new_ast; - continue 'tco; - } - Err(e) => return Err(e), + _ => match eval(a0, env) { + Ok(f @ MalFunc { is_macro: true, .. }) => { + let new_ast = f.apply(l[1..].to_vec())?; + live_ast = new_ast; + ast = &live_ast; + continue 'tco; } - Ok(f @ Func(_, _)) => match eval_ast(&l[1..].to_vec(), &env) { - Ok(args) => f.apply(args), - Err(e) => return Err(e), + Ok(f @ Func(_, _)) => { + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); + } + return f.apply(args); } Ok(MalFunc { - ast: ref mast, - env: ref menv, - params : ref mparams, + ast: mast, + env: menv, + params: mparams, .. - }) => match eval_ast(&l[1..].to_vec(), &env) { - Ok(args) => { - let a = &**mast; - let p = &**mparams; - env = env_bind(Some(menv.clone()), p.clone(), args.to_vec())?; - ast = a.clone(); + }) => { + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); + } + live_env = env_bind(Some(menv.clone()), &mparams, args.to_vec())?; + env = &live_env; + live_ast = (*mast).clone(); + ast = &live_ast; continue 'tco; - } - Err(e) => return Err(e), } - Ok(_) => error("attempt to call non-function"), - Err(e) => return Err(e), + Ok(_) => return error("attempt to call non-function"), + e @ Err(_) => return e, }, } } - _ => Ok(ast.clone()), + _ => return Ok(ast.clone()), }; - break; } // end 'tco loop - ret } // print @@ -279,16 +280,25 @@ fn print(ast: &MalVal) -> String { fn rep(str: &str, env: &Env) -> Result { let ast = read(str)?; - let exp = eval(ast, env.clone())?; + let exp = eval(&ast, env)?; Ok(print(&exp)) } +fn re(str: &str, env: &Env) { + if let Ok(ast) = read(str) { + if eval(&ast, env).is_ok() { + return; + } + } + panic!("error during startup"); +} + fn main() { let mut args = std::env::args(); let arg1 = args.nth(1); // `()` can be used when no completer is required - let mut rl = Editor::<()>::new(); + let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new().unwrap(); if rl.load_history(".mal-history").is_err() { eprintln!("No previous history."); } @@ -301,22 +311,18 @@ fn main() { env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect())); // core.mal: defined using the language itself - let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env); - let _ = rep( + re("(def! not (fn* (a) (if a false true)))", &repl_env); + re( "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", &repl_env, ); - let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", &repl_env); + re("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", + &repl_env); - // Invoked with arguments if let Some(f) = arg1 { - match rep(&format!("(load-file \"{}\")", f), &repl_env) { - Ok(_) => std::process::exit(0), - Err(e) => { - println!("Error: {}", format_error(e)); - std::process::exit(1); - } - } + // Invoked with arguments + re(&format!("(load-file \"{}\")", f), &repl_env); + std::process::exit(0); } // main repl loop @@ -324,9 +330,9 @@ fn main() { let readline = rl.readline("user> "); match readline { Ok(line) => { - rl.add_history_entry(&line); + let _ = rl.add_history_entry(&line); rl.save_history(".mal-history").unwrap(); - if line.len() > 0 { + if !line.is_empty() { match rep(&line, &repl_env) { Ok(out) => println!("{}", out), Err(e) => println!("Error: {}", format_error(e)), diff --git a/impls/rust/stepA_mal.rs b/impls/rust/stepA_mal.rs index 3d1bb97d7e..ce52da7432 100644 --- a/impls/rust/stepA_mal.rs +++ b/impls/rust/stepA_mal.rs @@ -23,13 +23,13 @@ use crate::types::{error, format_error, MalArgs, MalErr, MalRet, MalVal}; mod env; mod printer; mod reader; -use crate::env::{env_bind, env_get, env_new, env_set, env_sets, Env}; +use crate::env::{env_bind, env_find_repl, env_get, env_new, env_set, env_sets, Env}; #[macro_use] mod core; // read fn read(str: &str) -> MalRet { - reader::read_str(str.to_string()) + reader::read_str(str) } // eval @@ -47,9 +47,9 @@ fn qq_iter(elts: &MalArgs) -> MalVal { } } } - acc = list![Sym("cons".to_string()), quasiquote(&elt), acc]; + acc = list![Sym("cons".to_string()), quasiquote(elt), acc]; } - return acc; + acc } fn quasiquote(ast: &MalVal) -> MalVal { @@ -62,93 +62,83 @@ fn quasiquote(ast: &MalVal) -> MalVal { } } } - return qq_iter(&v); + qq_iter(v) }, - Vector(v, _) => return list![Sym("vec".to_string()), qq_iter(&v)], - Hash(_, _) | Sym(_)=> return list![Sym("quote".to_string()), ast.clone()], + Vector(v, _) => list![Sym("vec".to_string()), qq_iter(v)], + Hash(_, _) | Sym(_)=> list![Sym("quote".to_string()), ast.clone()], _ => ast.clone(), } } -fn eval_ast(v: &MalArgs, env: &Env) -> Result { - let mut lst: MalArgs = vec![]; - for a in v.iter() { - match eval(a.clone(), env.clone()) { - Ok(elt) => lst.push(elt), - Err(e) => return Err(e), - } - } - return Ok(lst); -} - -fn eval(mut ast: MalVal, mut env: Env) -> MalRet { - let ret: MalRet; +fn eval(orig_ast: &MalVal, orig_env: &Env) -> MalRet { + let mut ast = orig_ast; + let mut env = orig_env; + // These variables ensure a sufficient lifetime for the data + // referenced by ast and env. + let mut live_ast; + let mut live_env; 'tco: loop { - match env_get(&env, "DEBUG-EVAL") { + match env_get(env, "DEBUG-EVAL") { None | Some(Bool(false)) | Some(Nil) => (), - _ => println!("EVAL: {}", print(&ast)), + _ => println!("EVAL: {}", print(ast)), } - ret = match ast { - Sym(ref s) => match env_get(&env, s) { - Some(r) => Ok(r), - None => error (&format!("'{}' not found", s)), + match ast { + Sym(s) => match env_get(env, s) { + Some(r) => return Ok(r), + None => return error(&format!("'{}' not found", s)), } - Vector(ref v, _) => match eval_ast(&v, &env) { - Ok(lst) => Ok(vector!(lst)), - Err(e) => Err(e), + Vector(v, _) => { + let mut lst: MalArgs = vec![]; + for a in v.iter() { + lst.push(eval(a, env)?); + } + return Ok(vector!(lst)); } Hash(hm, _) => { let mut new_hm: FnvHashMap = FnvHashMap::default(); for (k, v) in hm.iter() { - new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?); + new_hm.insert(k.to_string(), eval(v, env)?); } - Ok(Hash(Rc::new(new_hm), Rc::new(Nil))) + return Ok(Hash(Rc::new(new_hm), Rc::new(Nil))); } - List(ref l, _) => { - if l.len() == 0 { - return Ok(ast); + List(l, _) => { + if l.is_empty() { + return Ok(ast.clone()); } let a0 = &l[0]; match a0 { - Sym(ref a0sym) if a0sym == "def!" => { - env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?) + Sym(a0sym) if a0sym == "def!" => { + return env_set(env, &l[1], eval(&l[2], env)?); } - Sym(ref a0sym) if a0sym == "let*" => { - env = env_new(Some(env.clone())); - let (a1, a2) = (l[1].clone(), l[2].clone()); + Sym(a0sym) if a0sym == "let*" => { + live_env = env_new(Some(env.clone())); + env = &live_env; + let (a1, a2) = (&l[1], &l[2]); match a1 { - List(ref binds, _) | Vector(ref binds, _) => { + List(binds, _) | Vector(binds, _) => { for (b, e) in binds.iter().tuples() { - match b { - Sym(_) => { - let _ = env_set( - &env, - b.clone(), - eval(e.clone(), env.clone())?, - ); - } - _ => { - return error("let* with non-Sym binding"); - } - } + let val = eval(e, env)?; + env_set(env, b, val)?; } } _ => { return error("let* with non-List bindings"); } }; - ast = a2; + live_ast = a2.clone(); + ast = &live_ast; continue 'tco; } - Sym(ref a0sym) if a0sym == "quote" => Ok(l[1].clone()), - Sym(ref a0sym) if a0sym == "quasiquote" => { - ast = quasiquote(&l[1]); + Sym(a0sym) if a0sym == "quote" => return Ok(l[1].clone()), + Sym(a0sym) if a0sym == "quasiquote" => { + live_ast = quasiquote(&l[1]); + ast = &live_ast; continue 'tco; } - Sym(ref a0sym) if a0sym == "defmacro!" => { - let (a1, a2) = (l[1].clone(), l[2].clone()); - let r = eval(a2, env.clone())?; + Sym(a0sym) if a0sym == "defmacro!" => { + let (a1, a2) = (&l[1], &l[2]); + let r = eval(a2, env)?; match r { MalFunc { eval, @@ -156,122 +146,133 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet { env, params, .. - } => Ok(env_set( + } => return env_set( &env, - a1.clone(), + a1, MalFunc { - eval: eval, - ast: ast.clone(), + eval, + ast, env: env.clone(), - params: params.clone(), + params, is_macro: true, meta: Rc::new(Nil), }, - )?), - _ => error("set_macro on non-function"), + ), + _ => return error("set_macro on non-function"), } } - Sym(ref a0sym) if a0sym == "try*" => match eval(l[1].clone(), env.clone()) { - Err(ref e) if l.len() >= 3 => { + Sym(a0sym) if a0sym == "try*" => { + if l.len() < 3 { + live_ast = l[1].clone(); + ast = &live_ast; + continue 'tco; + } + match eval(&l[1], env) { + Err(e) => { let exc = match e { ErrMalVal(mv) => mv.clone(), ErrString(s) => Str(s.to_string()), }; - match l[2].clone() { + match &l[2] { List(c, _) => { - let catch_env = env_bind( - Some(env.clone()), - list!(vec![c[1].clone()]), - vec![exc], - )?; - eval(c[2].clone(), catch_env) + live_env = env_new(Some(env.clone())); + env = &live_env; + env_set(env, &c[1], exc)?; + live_ast = c[2].clone(); + ast = &live_ast; + continue 'tco; } - _ => error("invalid catch block"), + _ => return error("invalid catch block"), } } - res => res, + res => return res, + } }, - Sym(ref a0sym) if a0sym == "do" => { - match eval_ast(&l[1..l.len() - 1].to_vec(), &env) { - Ok(_) => { - ast = l.last().unwrap_or(&Nil).clone(); - continue 'tco; - } - Err(e) => return Err(e), + Sym(a0sym) if a0sym == "do" => { + for i in 1..l.len() - 1 { + let _ = eval(&l[i], env)?; } + live_ast = l.last().unwrap_or(&Nil).clone(); + ast = &live_ast; + continue 'tco; } - Sym(ref a0sym) if a0sym == "if" => { - let cond = eval(l[1].clone(), env.clone())?; + Sym(a0sym) if a0sym == "if" => { + let cond = eval(&l[1], env)?; match cond { Bool(false) | Nil if l.len() >= 4 => { - ast = l[3].clone(); + live_ast = l[3].clone(); + ast = &live_ast; continue 'tco; } - Bool(false) | Nil => Ok(Nil), + Bool(false) | Nil => return Ok(Nil), _ if l.len() >= 3 => { - ast = l[2].clone(); + live_ast = l[2].clone(); + ast = &live_ast; continue 'tco; } - _ => Ok(Nil), + _ => return Ok(Nil), } } - Sym(ref a0sym) if a0sym == "fn*" => { + Sym(a0sym) if a0sym == "fn*" => { let (a1, a2) = (l[1].clone(), l[2].clone()); - Ok(MalFunc { - eval: eval, + return Ok(MalFunc { + eval, ast: Rc::new(a2), - env: env, + env: env.clone(), params: Rc::new(a1), is_macro: false, meta: Rc::new(Nil), }) } - Sym(ref a0sym) if a0sym == "eval" => { - ast = eval(l[1].clone(), env.clone())?; - while let Some(ref e) = env.clone().outer { - env = e.clone(); - } + Sym(a0sym) if a0sym == "eval" => { + // Hard to implement without global variables. + // Normal argument evaluation. + live_ast = eval(&l[1], env)?; + ast = &live_ast; + live_env = env_find_repl(env); + env = &live_env; continue 'tco; } - _ => match eval(a0.clone(), env.clone()) { - Ok(f @ MalFunc { is_macro: true, .. }) => match f.apply(l[1..].to_vec()) { - Ok(new_ast) => { - ast = new_ast; - continue 'tco; - } - Err(e) => return Err(e), + _ => match eval(a0, env) { + Ok(f @ MalFunc { is_macro: true, .. }) => { + let new_ast = f.apply(l[1..].to_vec())?; + live_ast = new_ast; + ast = &live_ast; + continue 'tco; } - Ok(f @ Func(_, _)) => match eval_ast(&l[1..].to_vec(), &env) { - Ok(args) => f.apply(args), - Err(e) => return Err(e), + Ok(f @ Func(_, _)) => { + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); + } + return f.apply(args); } Ok(MalFunc { - ast: ref mast, - env: ref menv, - params : ref mparams, + ast: mast, + env: menv, + params: mparams, .. - }) => match eval_ast(&l[1..].to_vec(), &env) { - Ok(args) => { - let a = &**mast; - let p = &**mparams; - env = env_bind(Some(menv.clone()), p.clone(), args.to_vec())?; - ast = a.clone(); + }) => { + let mut args: MalArgs = vec![]; + for i in 1..l.len() { + args.push(eval(&l[i], env)?); + } + live_env = env_bind(Some(menv.clone()), &mparams, args.to_vec())?; + env = &live_env; + live_ast = (*mast).clone(); + ast = &live_ast; continue 'tco; - } - Err(e) => return Err(e), } - Ok(_) => error("attempt to call non-function"), - Err(e) => return Err(e), + Ok(_) => return error("attempt to call non-function"), + e @ Err(_) => return e, }, } } - _ => Ok(ast.clone()), + _ => return Ok(ast.clone()), }; - break; } // end 'tco loop - ret } // print @@ -281,16 +282,25 @@ fn print(ast: &MalVal) -> String { fn rep(str: &str, env: &Env) -> Result { let ast = read(str)?; - let exp = eval(ast, env.clone())?; + let exp = eval(&ast, env)?; Ok(print(&exp)) } +fn re(str: &str, env: &Env) { + if let Ok(ast) = read(str) { + if eval(&ast, env).is_ok() { + return; + } + } + panic!("error during startup"); +} + fn main() { let mut args = std::env::args(); let arg1 = args.nth(1); // `()` can be used when no completer is required - let mut rl = Editor::<()>::new(); + let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new().unwrap(); if rl.load_history(".mal-history").is_err() { eprintln!("No previous history."); } @@ -303,34 +313,30 @@ fn main() { env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect())); // core.mal: defined using the language itself - let _ = rep("(def! *host-language* \"rust\")", &repl_env); - let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env); - let _ = rep( + re("(def! *host-language* \"rust\")", &repl_env); + re("(def! not (fn* (a) (if a false true)))", &repl_env); + re( "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", &repl_env, ); - let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", &repl_env); + re("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", + &repl_env); - // Invoked with arguments if let Some(f) = arg1 { - match rep(&format!("(load-file \"{}\")", f), &repl_env) { - Ok(_) => std::process::exit(0), - Err(e) => { - println!("Error: {}", format_error(e)); - std::process::exit(1); - } - } + // Invoked with arguments + re(&format!("(load-file \"{}\")", f), &repl_env); + std::process::exit(0); } // main repl loop - let _ = rep("(println (str \"Mal [\" *host-language* \"]\"))", &repl_env); + re("(println (str \"Mal [\" *host-language* \"]\"))", &repl_env); loop { let readline = rl.readline("user> "); match readline { Ok(line) => { - rl.add_history_entry(&line); + let _ = rl.add_history_entry(&line); rl.save_history(".mal-history").unwrap(); - if line.len() > 0 { + if !line.is_empty() { match rep(&line, &repl_env) { Ok(out) => println!("{}", out), Err(e) => println!("Error: {}", format_error(e)), diff --git a/impls/rust/types.rs b/impls/rust/types.rs index 108a646cfd..b68c2280ae 100644 --- a/impls/rust/types.rs +++ b/impls/rust/types.rs @@ -8,7 +8,7 @@ use crate::env::{env_bind, Env}; use crate::types::MalErr::{ErrMalVal, ErrString}; use crate::types::MalVal::{Atom, Bool, Func, Hash, Int, List, MalFunc, Nil, Str, Sym, Vector}; -#[derive(Debug, Clone)] +#[derive(Clone)] pub enum MalVal { Nil, Bool(bool), @@ -21,7 +21,7 @@ pub enum MalVal { Hash(Rc>, Rc), Func(fn(MalArgs) -> MalRet, Rc), MalFunc { - eval: fn(ast: MalVal, env: Env) -> MalRet, + eval: fn(ast: &MalVal, env: &Env) -> MalRet, ast: Rc, env: Env, params: Rc, @@ -31,7 +31,6 @@ pub enum MalVal { Atom(Rc>), } -#[derive(Debug)] pub enum MalErr { ErrString(String), ErrMalVal(MalVal), @@ -70,7 +69,7 @@ pub fn error(s: &str) -> MalRet { pub fn format_error(e: MalErr) -> String { match e { - ErrString(s) => s.clone(), + ErrString(s) => s, ErrMalVal(mv) => mv.pr_str(true), } } @@ -82,7 +81,7 @@ pub fn atom(mv: &MalVal) -> MalVal { impl MalVal { pub fn keyword(&self) -> MalRet { match self { - Str(s) if s.starts_with("\u{29e}") => Ok(Str(s.to_string())), + Str(s) if s.starts_with('\u{29e}') => Ok(Str(s.to_string())), Str(s) => Ok(Str(format!("\u{29e}{}", s))), _ => error("invalid type for keyword"), } @@ -105,29 +104,24 @@ impl MalVal { } pub fn apply(&self, args: MalArgs) -> MalRet { - match *self { + match self { Func(f, _) => f(args), MalFunc { eval, ref ast, - ref env, + env, ref params, .. } => { - let a = &**ast; - let p = &**params; - let fn_env = env_bind(Some(env.clone()), p.clone(), args)?; - Ok(eval(a.clone(), fn_env)?) + let fn_env = &env_bind(Some(env.clone()), params, args)?; + eval(ast, fn_env) } _ => error("attempt to call non-function"), } } pub fn keyword_q(&self) -> bool { - match self { - Str(s) if s.starts_with("\u{29e}") => true, - _ => false, - } + matches!(self, Str(s) if s.starts_with('\u{29e}')) } pub fn deref(&self) -> MalRet { @@ -162,9 +156,9 @@ impl MalVal { pub fn get_meta(&self) -> MalRet { match self { - List(_, meta) | Vector(_, meta) | Hash(_, meta) => Ok((&**meta).clone()), - Func(_, meta) => Ok((&**meta).clone()), - MalFunc { meta, .. } => Ok((&**meta).clone()), + List(_, meta) | Vector(_, meta) | Hash(_, meta) => Ok((**meta).clone()), + Func(_, meta) => Ok((**meta).clone()), + MalFunc { meta, .. } => Ok((**meta).clone()), _ => error("meta not supported by type"), } } @@ -176,7 +170,7 @@ impl MalVal { | Hash(_, ref mut meta) | Func(_, ref mut meta) | MalFunc { ref mut meta, .. } => { - *meta = Rc::new((&*new_meta).clone()); + *meta = Rc::new(new_meta.clone()); } _ => return error("with-meta not supported by type"), }; @@ -223,10 +217,10 @@ pub fn _assoc(mut hm: FnvHashMap, kvs: MalArgs) -> MalRet { } pub fn _dissoc(mut hm: FnvHashMap, ks: MalArgs) -> MalRet { - for k in ks.iter() { + for k in ks { match k { Str(ref s) => { - hm.remove(s); + let _ = hm.remove(s); } _ => return error("key is not string"), }