1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-19 17:47:53 +03:00

rust: update and merge eval-ast

Remove unneeded intermediate executable files.
Remove Cargo.lock from version control system.
Update dependency versions.
Update Dockerfile (converge to update-some-dockerfiles branch).
Apply changes suggested by rustfmt and clippy, or enforced by rustc.
Remove several unneeded clone().
Merge eval and eval-ast, remove quasiquoteexpand and macroexpand.
Report more errors during startup.
This commit is contained in:
Nicolas Boulenguez 2024-08-06 05:17:06 +02:00 committed by Joel Martin
parent ec15d215c1
commit 253cdf1ae5
22 changed files with 954 additions and 1422 deletions

View File

@ -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

456
impls/rust/Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -19,6 +19,11 @@ WORKDIR /mal
# Specific implementation requirements
##########################################################
RUN apt-get -y install cargo
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

View File

@ -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

View File

@ -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<Editor<()>> = Mutex::new(Editor::<()>::new());
static ref RL: Mutex<Editor<(), rustyline::history::DefaultHistory>>
= 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::<Vec<MalVal>>();
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)),

View File

@ -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<FnvHashMap<String, MalVal>>,
pub outer: Option<Env>,
@ -21,23 +20,23 @@ pub type Env = Rc<EnvStruct>;
pub fn env_new(outer: Option<Env>) -> Env {
Rc::new(EnvStruct {
data: RefCell::new(FnvHashMap::default()),
outer: outer,
outer,
})
}
// TODO: mbinds and exprs as & types
pub fn env_bind(outer: Option<Env>, mbinds: MalVal, exprs: Vec<MalVal>) -> Result<Env, MalErr> {
pub fn env_bind(outer: Option<Env>, mbinds: &MalVal, exprs: Vec<MalVal>) -> Result<Env, MalErr> {
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<Env>, mbinds: MalVal, exprs: Vec<MalVal>) -> Resul
}
pub fn env_get(env: &Env, key: &str) -> Option<MalVal> {
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"),

View File

@ -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<MalVal> = hm
.iter()
@ -40,7 +40,7 @@ impl MalVal {
.collect();
pr_seq(&l, print_readably, "{", "}", " ")
}
Func(f, _) => format!("#<fn {:?}>", f),
Func(_, _) => String::from("#<builtin>"),
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<MalVal>,
seq: &[MalVal],
print_readably: bool,
start: &str,
end: &str,
join: &str,
join: &str
) -> String {
let strs: Vec<String> = seq.iter().map(|x| x.pr_str(print_readably)).collect();
format!("{}{}{}", start, strs.join(join), end)

View File

@ -13,18 +13,18 @@ struct Reader {
impl Reader {
fn next(&mut self) -> Result<String, MalErr> {
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<String, MalErr> {
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<String> {
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
})
}

View File

@ -1,2 +1,2 @@
#!/bin/bash
exec $(dirname $0)/${STEP:-stepA_mal} "${@}"
exec $(dirname $0)/target/release/${STEP:-stepA_mal} "${@}"

View File

@ -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);
}
}

View File

@ -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));
}

View File

@ -28,52 +28,44 @@ pub type Env = FnvHashMap<String, MalVal>;
// 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<MalArgs, MalErr> {
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<String, MalVal> = 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<String, MalErr> {
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)),

View File

@ -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<MalArgs, MalErr> {
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<String, MalVal> = 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<String, MalErr> {
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)),

View File

@ -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<MalArgs, MalErr> {
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<String, MalVal> = 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<String, MalErr> {
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)),

View File

@ -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<MalArgs, MalErr> {
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<String, MalVal> = 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<String, MalErr> {
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)),

View File

@ -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<MalArgs, MalErr> {
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<String, MalVal> = 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<String, MalErr> {
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)),

View File

@ -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<MalArgs, MalErr> {
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<String, MalVal> = 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<String, MalErr> {
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)),

View File

@ -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<MalArgs, MalErr> {
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<String, MalVal> = 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<String, MalErr> {
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)),

View File

@ -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<MalArgs, MalErr> {
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<String, MalVal> = 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<String, MalErr> {
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)),

View File

@ -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<MalArgs, MalErr> {
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<String, MalVal> = 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<String, MalErr> {
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)),

View File

@ -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<FnvHashMap<String, MalVal>>, Rc<MalVal>),
Func(fn(MalArgs) -> MalRet, Rc<MalVal>),
MalFunc {
eval: fn(ast: MalVal, env: Env) -> MalRet,
eval: fn(ast: &MalVal, env: &Env) -> MalRet,
ast: Rc<MalVal>,
env: Env,
params: Rc<MalVal>,
@ -31,7 +31,6 @@ pub enum MalVal {
Atom(Rc<RefCell<MalVal>>),
}
#[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<String, MalVal>, kvs: MalArgs) -> MalRet {
}
pub fn _dissoc(mut hm: FnvHashMap<String, MalVal>, 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"),
}