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:
parent
ec15d215c1
commit
253cdf1ae5
@ -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
456
impls/rust/Cargo.lock
generated
@ -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"
|
@ -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"
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)),
|
||||
|
@ -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"),
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
})
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
#!/bin/bash
|
||||
exec $(dirname $0)/${STEP:-stepA_mal} "${@}"
|
||||
exec $(dirname $0)/target/release/${STEP:-stepA_mal} "${@}"
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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)),
|
||||
|
@ -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)),
|
||||
|
@ -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)),
|
||||
|
@ -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)),
|
||||
|
@ -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)),
|
||||
|
@ -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)),
|
||||
|
@ -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)),
|
||||
|
@ -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)),
|
||||
|
@ -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)),
|
||||
|
@ -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"),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user