From 91795923b569502ebcdbd56e0d89c7159040c87d Mon Sep 17 00:00:00 2001 From: Erin Date: Thu, 30 Nov 2023 16:59:17 +0100 Subject: [PATCH] Deleted AbleScript. --- Cargo.lock | 682 ------------------------- Cargo.toml | 6 - LICENSE | 2 +- ablescript/Cargo.toml | 14 - ablescript/src/ast.rs | 216 -------- ablescript/src/base_55.rs | 23 - ablescript/src/brian.rs | 438 ---------------- ablescript/src/consts.rs | 43 -- ablescript/src/error.rs | 93 ---- ablescript/src/host_interface.rs | 53 -- ablescript/src/interpret.rs | 812 ------------------------------ ablescript/src/lexer.rs | 144 ------ ablescript/src/lib.rs | 18 - ablescript/src/parser.rs | 812 ------------------------------ ablescript/src/rickroll | 67 --- ablescript/src/value/coercions.rs | 279 ---------- ablescript/src/value/functio.rs | 113 ----- ablescript/src/value/mod.rs | 202 -------- ablescript/src/value/ops.rs | 455 ----------------- ablescript_cli/Cargo.toml | 15 - ablescript_cli/src/main.rs | 55 -- ablescript_cli/src/repl.rs | 66 --- examples/by-arity-chain.able | 44 -- examples/carts.able | 8 - examples/functio.able | 4 - examples/hello-world.able | 2 - examples/iotest.able | 5 - examples/melo-hello.able | 3 - examples/pass-by-reference.able | 23 - 29 files changed, 1 insertion(+), 4696 deletions(-) delete mode 100644 Cargo.lock delete mode 100644 Cargo.toml delete mode 100644 ablescript/Cargo.toml delete mode 100644 ablescript/src/ast.rs delete mode 100644 ablescript/src/base_55.rs delete mode 100644 ablescript/src/brian.rs delete mode 100644 ablescript/src/consts.rs delete mode 100644 ablescript/src/error.rs delete mode 100644 ablescript/src/host_interface.rs delete mode 100644 ablescript/src/interpret.rs delete mode 100644 ablescript/src/lexer.rs delete mode 100644 ablescript/src/lib.rs delete mode 100644 ablescript/src/parser.rs delete mode 100644 ablescript/src/rickroll delete mode 100644 ablescript/src/value/coercions.rs delete mode 100644 ablescript/src/value/functio.rs delete mode 100644 ablescript/src/value/mod.rs delete mode 100644 ablescript/src/value/ops.rs delete mode 100644 ablescript_cli/Cargo.toml delete mode 100644 ablescript_cli/src/main.rs delete mode 100644 ablescript_cli/src/repl.rs delete mode 100644 examples/by-arity-chain.able delete mode 100644 examples/carts.able delete mode 100644 examples/functio.able delete mode 100644 examples/hello-world.able delete mode 100644 examples/iotest.able delete mode 100644 examples/melo-hello.able delete mode 100644 examples/pass-by-reference.able diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 3d09f55b..00000000 --- a/Cargo.lock +++ /dev/null @@ -1,682 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ablescript" -version = "0.5.4" -dependencies = [ - "logos", - "rand", -] - -[[package]] -name = "ablescript_cli" -version = "0.5.4" -dependencies = [ - "ablescript", - "clap", - "rustyline", -] - -[[package]] -name = "anstream" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" - -[[package]] -name = "anstyle-parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" -dependencies = [ - "anstyle", - "windows-sys 0.48.0", -] - -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clap" -version = "4.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" - -[[package]] -name = "clipboard-win" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" -dependencies = [ - "error-code", - "str-buf", - "winapi", -] - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "error-code" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" -dependencies = [ - "libc", - "str-buf", -] - -[[package]] -name = "fd-lock" -version = "3.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" -dependencies = [ - "cfg-if", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "getrandom" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "libc" -version = "0.2.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" - -[[package]] -name = "libredox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" -dependencies = [ - "bitflags 2.4.1", - "libc", - "redox_syscall", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "logos" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" -dependencies = [ - "logos-derive", -] - -[[package]] -name = "logos-codegen" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" -dependencies = [ - "beef", - "fnv", - "proc-macro2", - "quote", - "regex-syntax", - "syn", -] - -[[package]] -name = "logos-derive" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" -dependencies = [ - "logos-codegen", -] - -[[package]] -name = "memchr" -version = "2.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" - -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "1.0.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_users" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "rustix" -version = "0.38.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" -dependencies = [ - "bitflags 2.4.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustyline" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfc8644681285d1fb67a467fb3021bfea306b99b4146b166a1fe3ada965eece" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "clipboard-win", - "dirs-next", - "fd-lock", - "libc", - "log", - "memchr", - "nix", - "radix_trie", - "scopeguard", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "winapi", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "smallvec" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" - -[[package]] -name = "str-buf" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "2.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index b791aaec..00000000 --- a/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[workspace] -members = ["ablescript", "ablescript_cli"] - -[profile.release] -lto = true -strip = true diff --git a/LICENSE b/LICENSE index 010c404f..6cbc7bc6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 AbleCorp +Copyright (c) 2023 AbleCorp Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/ablescript/Cargo.toml b/ablescript/Cargo.toml deleted file mode 100644 index aa879628..00000000 --- a/ablescript/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "ablescript" -version = "0.5.4" -authors = ["AbleScript Developers"] -edition = "2021" - -description = "The best programming language" -license = "MIT" -documentation = "https://ablecorp.us/able-script-the-book/" -repository = "https://git.ablecorp.us/AbleScript/able-script" - -[dependencies] -logos = "0.13" -rand = "0.8" diff --git a/ablescript/src/ast.rs b/ablescript/src/ast.rs deleted file mode 100644 index 5b564f1d..00000000 --- a/ablescript/src/ast.rs +++ /dev/null @@ -1,216 +0,0 @@ -//! AbleScript's Abstract Syntax tree -//! -//! Statements are the type which is AST made of, as they -//! express an effect. -//! -//! Expressions are just operations and they cannot be -//! used as statements. Functions in AbleScript are in fact -//! just plain subroutines and they do not return any value, -//! so their calls are statements. - -use crate::{base_55::char2num, value::Value}; -use std::{fmt::Debug, hash::Hash}; - -type Span = std::ops::Range; - -#[derive(Clone)] -pub struct Spanned { - pub item: T, - pub span: Span, -} - -impl Spanned { - pub fn new(item: T, span: Span) -> Self { - Self { item, span } - } -} - -impl Debug for Spanned { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if f.alternate() { - write!(f, "{:#?} @ {:?}", self.item, self.span) - } else { - write!(f, "{:?} @ {:?}", self.item, self.span) - } - } -} - -impl PartialEq for Spanned { - fn eq(&self, other: &Self) -> bool { - self.item == other.item - } -} - -impl Hash for Spanned { - fn hash(&self, state: &mut H) { - self.item.hash(state); - } -} - -#[derive(Debug, PartialEq, Clone, Hash)] -pub struct Assignable { - pub ident: Spanned, - pub kind: AssignableKind, -} - -#[derive(Debug, PartialEq, Clone, Hash)] -pub enum AssignableKind { - Variable, - Index { indices: Vec> }, -} - -pub struct InvalidAssignable; - -impl Assignable { - pub fn from_expr(expr: Spanned) -> Result { - match expr.item { - Expr::Variable(ident) => Ok(Assignable { - ident: Spanned::new(ident, expr.span), - kind: AssignableKind::Variable, - }), - Expr::Index { expr, index } => Self::from_index(*expr, *index), - _ => Err(InvalidAssignable), - } - } - - fn from_index( - mut buf: Spanned, - index: Spanned, - ) -> Result { - let mut indices = vec![index]; - let ident = loop { - match buf.item { - Expr::Variable(ident) => break ident, - Expr::Index { expr, index } => { - indices.push(*index); - buf = *expr; - } - _ => return Err(InvalidAssignable), - } - }; - - indices.reverse(); - Ok(Assignable { - ident: Spanned::new(ident, buf.span), - kind: AssignableKind::Index { indices }, - }) - } -} - -pub type Block = Vec>; - -/// A syntactic unit expressing an effect. -#[derive(Debug, PartialEq, Clone, Hash)] -pub enum Stmt { - // Control flow - Unless { - cond: Spanned, - body: Block, - }, - Loop { - body: Block, - }, - Enough, - AndAgain, - - Dim { - ident: Spanned, - init: Option>, - }, - Assign { - assignable: Assignable, - value: Spanned, - }, - - Functio { - ident: Spanned, - params: Vec>, - body: Block, - }, - BfFunctio { - ident: Spanned, - tape_len: Option>, - code: Vec, - }, - Call { - expr: Spanned, - args: Vec>, - }, - Print { - expr: Spanned, - newline: bool, - }, - Read(Assignable), - Melo(Spanned), - Finally(Block), - Rlyeh, - Rickroll, -} - -/// Expression is parse unit which do not cause any effect, -/// like math and logical operations or values. -#[derive(Debug, PartialEq, Clone, Hash)] -pub enum Expr { - BinOp { - lhs: Box>, - rhs: Box>, - kind: BinOpKind, - }, - Aint(Box>), - Literal(Literal), - Cart(Vec<(Spanned, Spanned)>), - Index { - expr: Box>, - index: Box>, - }, - Len(Box>), - Keys(Box>), - Variable(String), -} - -#[derive(Debug, PartialEq, Eq, Clone, Hash)] -pub enum Literal { - Char(char), - Int(isize), - Str(String), -} - -impl From for Value { - fn from(lit: Literal) -> Self { - match lit { - Literal::Char(c) => Self::Int(char2num(c)), - Literal::Int(i) => Self::Int(i), - Literal::Str(s) => Self::Str(s), - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Hash)] -pub enum BinOpKind { - Add, - Subtract, - Multiply, - Divide, - Greater, - Less, - Equal, - NotEqual, -} - -impl BinOpKind { - pub fn from_token(t: crate::lexer::Token) -> Result { - use crate::lexer::Token; - - match t { - Token::Plus => Ok(Self::Add), - Token::Minus => Ok(Self::Subtract), - Token::Star => Ok(Self::Multiply), - Token::FwdSlash => Ok(Self::Divide), - Token::GreaterThan => Ok(Self::Greater), - Token::LessThan => Ok(Self::Less), - Token::Equals => Ok(Self::Equal), - Token::Aint => Ok(Self::NotEqual), - t => Err(crate::error::ErrorKind::UnexpectedToken(t)), - } - } -} diff --git a/ablescript/src/base_55.rs b/ablescript/src/base_55.rs deleted file mode 100644 index 2e5a5a3e..00000000 --- a/ablescript/src/base_55.rs +++ /dev/null @@ -1,23 +0,0 @@ -pub const fn char2num(c: char) -> isize { - match c { - ' ' => 0, - // NOTE(Able): Why does it jump to 53 here? MY REASONS ARE BEYOND YOUR UNDERSTANDING MORTAL - '/' => 53, - '\\' => 54, - '.' => 55, - 'U' => -210, // Backwards compatibility - 'A'..='Z' => -(c as isize) + 64, - 'a'..='z' => (c as isize) - 96, - _ => 0, - } -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn str_to_base55() { - let chrs: Vec = "AbleScript".chars().map(char2num).collect(); - assert_eq!(chrs, &[-1, 2, 12, 5, -19, 3, 18, 9, 16, 20]); - } -} diff --git a/ablescript/src/brian.rs b/ablescript/src/brian.rs deleted file mode 100644 index 65c5a0e1..00000000 --- a/ablescript/src/brian.rs +++ /dev/null @@ -1,438 +0,0 @@ -//! A brainfuck interpreter capable of executing arbitrary code, with arbitrary inputs and outputs. -//! -//! If you just want to execute some simple brainfuck, check the [`interpret_with_io`] function. -//! -//! To construct the interpreter, use the [`from_ascii`] or [`from_ascii_with_input_buffer`] methods -//! (or their variants that take a maximum tape size). The latter grants access to -//! the method [`add_input`], which allows for the addition of input while the interpreter is running. -//! -//! [`from_ascii`]: Interpreter::from_ascii -//! [`from_ascii_with_input_buffer`]: Interpreter::from_ascii_with_input_buffer -//! [`add_input`]: Interpreter::add_input -//! -//! Finally, to run the interpreter, you can use the [`advance`], [`advance_until_io`], or [`interpret_with_output`] methods. -//! -//! [`advance`]: Interpreter::advance -//! [`advance_until_io`]: Interpreter::advance_until_io -//! [`interpret_with_output`]: Interpreter::interpret_with_output - -#![deny(missing_docs)] -// Putting this here because we still don't use the entire capabilities of this module. ~~Alex -#![allow(dead_code)] - -use std::{ - collections::VecDeque, - error::Error, - fmt::Display, - io::{Read, Write}, -}; - -// NOTE(Able): This is the brain fuck interface - -/// The default limit for the tape size. This is the value used by methods that don't take it as a parameter -pub const DEFAULT_TAPE_SIZE_LIMIT: usize = 30_000; - -/// Mappings from integers to BF instructions -pub const INSTRUCTION_MAPPINGS: &[u8] = b"[]+-,.<>"; -#[derive(Debug, Clone, PartialEq, Eq)] -/// A brainfuck interpreter. Read the [module level documentation](self) for more -pub struct Interpreter<'a, I> { - code: &'a [u8], - instr_ptr: usize, - tape: Vec, - data_ptr: usize, - tape_size_limit: usize, - input: I, -} - -impl<'a> Interpreter<'a, InputBuffer> { - /// Construct an `Interpreter` from an ASCII string of code with an empty input buffer - /// This methods sets the tape size limit to [its default value](DEFAULT_TAPE_SIZE_LIMIT) - pub fn from_ascii_with_input_buffer(code: &'a [u8]) -> Self { - Self::from_ascii_with_input_buffer_and_tape_limit(code, DEFAULT_TAPE_SIZE_LIMIT) - } - - /// Construct an `Interpreter` from an ASCII string of code with an empty input buffer, - /// setting the tape size limit to the specified value - pub fn from_ascii_with_input_buffer_and_tape_limit( - code: &'a [u8], - tape_size_limit: usize, - ) -> Self { - Self { - code, - instr_ptr: 0, - tape: Vec::new(), - data_ptr: 0, - tape_size_limit, - input: InputBuffer(VecDeque::new()), - } - } - - /// Add a byte to the input buffer of this interpreter - pub fn add_input(&mut self, input: i8) { - self.input.0.push_back(input); - } -} - -impl<'a, I: BootlegRead> Interpreter<'a, I> { - /// Construct an interpreter from an ASCII string of code, a source of input bytes, and a tape size limit - pub fn from_ascii_with_tape_limit(code: &'a [u8], input: I, tape_size_limit: usize) -> Self { - Self { - code, - instr_ptr: 0, - tape: Vec::new(), - data_ptr: 0, - tape_size_limit, - input, - } - } - - /// Constructs an interpreter from an ASCII string of code, a source of input bytes, and [the default tape size limit](DEFAULT_TAPE_SIZE_LIMIT) - pub fn from_ascii(code: &'a [u8], input: I) -> Self { - Self::from_ascii_with_tape_limit(code, input, DEFAULT_TAPE_SIZE_LIMIT) - } - - /// Advance the interpreter by one instruction. - /// A return value of Ok(None) indicates succesful termination of the interpreter - pub fn advance(&mut self) -> Result, ProgramError> { - let &opcode = match self.code.get(self.instr_ptr) { - Some(opcode) => opcode, - None => return Ok(None), - }; - - match opcode { - b'>' => self.data_ptr += 1, - - b'<' => { - self.data_ptr = self - .data_ptr - .checked_sub(1) - .ok_or(ProgramError::DataPointerUnderflow)?; - } - - b'+' => { - let val = self - .get_or_resize_tape_mut() - .ok_or(ProgramError::TapeSizeExceededLimit)?; - *val = val.wrapping_add(1) - } - - b'-' => { - let val = self - .get_or_resize_tape_mut() - .ok_or(ProgramError::TapeSizeExceededLimit)?; - *val = val.wrapping_sub(1) - } - - b'.' => { - self.instr_ptr += 1; - return Ok(Some(Status::Output(self.get_at_data_ptr()))); - } - - b',' => match self.input.bootleg_read() { - Ok(Some(num)) => { - let cell = self - .get_or_resize_tape_mut() - .ok_or(ProgramError::TapeSizeExceededLimit)?; - *cell = num; - } - Ok(None) => return Ok(Some(Status::NeedsInput)), - Err(_) => return Err(ProgramError::InputReadError), - }, - - b'[' => { - if self.get_at_data_ptr() == 0 { - self.instr_ptr = self - .get_matching_closing_bracket(self.instr_ptr) - .ok_or(ProgramError::UnmatchedOpeningBracket)? - //Instruction pointer will be incremented by 1 after the match - } - } - - b']' => { - if self.get_at_data_ptr() != 0 { - self.instr_ptr = self - .get_matching_opening_bracket(self.instr_ptr) - .ok_or(ProgramError::UnmatchedClosingBracket)? - //Instruction pointer will be incremented by 1 after the match - } - } - - _ => {} //brainfuck treats all characters it doesn't understand as comments - } - - self.instr_ptr += 1; - - Ok(Some(Status::Continue)) - } - - /// Advances the interpreter until the next IO operation. See [`advance`](Interpreter::advance) - pub fn advance_until_io(&mut self) -> Result, ProgramError> { - while let Some(status) = self.advance()? { - match status { - Status::NeedsInput => return Ok(Some(IoStatus::NeedsInput)), - Status::Output(out) => return Ok(Some(IoStatus::Output(out))), - Status::Continue => continue, - } - } - Ok(None) - } - - /// Executes the interpreter until it halts, writing all return values to the provided `Write` type. - /// For more granular control, use [`advance`](Interpreter::advance) - pub fn interpret_with_output(&mut self, mut output: O) -> Result<(), InterpretError> { - while let Some(status) = self.advance_until_io()? { - match status { - IoStatus::NeedsInput => return Err(InterpretError::EndOfInput), - IoStatus::Output(out) => match output.write(&[out as u8]) { - Ok(0) => return Err(InterpretError::OutputBufferFull), - Ok(_) => continue, - Err(_) => return Err(InterpretError::OutputWriteError), - }, - } - } - Ok(()) - } - - fn get_or_resize_tape_mut(&mut self) -> Option<&mut i8> { - if self.data_ptr > self.tape_size_limit { - return None; - } - if self.data_ptr >= self.tape.len() { - self.tape.resize(self.data_ptr + 1, 0); - } - Some(&mut self.tape[self.data_ptr]) - } - - fn get_at_data_ptr(&self) -> i8 { - //No need to resize the tape to read: if the tape doesn't extend that far already, it holds a value of 0 - self.tape.get(self.data_ptr).copied().unwrap_or(0) - } - - fn get_matching_closing_bracket(&mut self, opening: usize) -> Option { - self.code[opening..] - .iter() - .zip(opening..) - .scan(0, |counter, (char, index)| { - match char { - b'[' => *counter += 1, - b']' => *counter -= 1, - _ => {} - }; - Some((*counter, index)) - }) - .find_map(|(counter, index)| (counter == 0).then_some(index)) - } - - fn get_matching_opening_bracket(&mut self, closing: usize) -> Option { - self.code[..closing + 1] - .iter() - .zip(0..closing + 1) - .rev() - .scan(0, |counter, (char, index)| { - match char { - b']' => *counter += 1, - b'[' => *counter -= 1, - _ => {} - }; - Some((*counter, index)) - }) - .find_map(|(counter, index)| (counter == 0).then_some(index)) - } -} - -/// A convenience function for interpreting brainfuck code with a given input and output source. -/// For more information, consult [the module level documentation](self) -pub fn interpret_with_io( - code: &[u8], - input: I, - output: O, -) -> Result<(), InterpretError> { - Interpreter::from_ascii(code, input).interpret_with_output(output) -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -///The result of advancing the interpreter by one step, assuming it didn't terminate -pub enum Status { - NeedsInput, - Output(i8), - Continue, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -/// The result of advancing the interpreter until the next IO operation, assuming it didn't terminate -pub enum IoStatus { - NeedsInput, - Output(i8), -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -/// An error that occurred while the interpreter was advancing -pub enum ProgramError { - DataPointerUnderflow, - InputReadError, - UnmatchedOpeningBracket, - UnmatchedClosingBracket, - TapeSizeExceededLimit, -} - -impl Display for ProgramError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - ProgramError::DataPointerUnderflow => "data pointer underflow", - ProgramError::InputReadError => "input read error", - ProgramError::UnmatchedOpeningBracket => "unmatched `[`", - ProgramError::UnmatchedClosingBracket => "unmatched `]`", - ProgramError::TapeSizeExceededLimit => "tape size exceeded", - } - ) - } -} -impl Error for ProgramError {} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -/// An error that occurred while the interpreter was being run start-to-end all in one go -pub enum InterpretError { - ProgramError(ProgramError), - EndOfInput, - OutputBufferFull, - OutputWriteError, -} - -impl Display for InterpretError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - InterpretError::ProgramError(e) => write!(f, "program error: {}", e), - InterpretError::EndOfInput => write!(f, "unexpected end of input"), - InterpretError::OutputBufferFull => write!(f, "output buffer full"), - InterpretError::OutputWriteError => write!(f, "output write error"), - } - } -} -impl Error for InterpretError {} - -impl From for InterpretError { - fn from(e: ProgramError) -> Self { - InterpretError::ProgramError(e) - } -} - -/// A bootlegged version of the standard library's read trait, so as to allow the interpreter to be generic over any `Read` -/// type, as well as over an input buffer. -pub trait BootlegRead { - type Error; - fn bootleg_read(&mut self) -> Result, Self::Error>; -} - -impl BootlegRead for T { - type Error = std::io::Error; - fn bootleg_read(&mut self) -> Result, Self::Error> { - let mut buffer = [0]; - match self.read(&mut buffer) { - Ok(0) => Ok(None), - Ok(_) => Ok(Some(buffer[0] as i8)), - Err(e) => Err(e), - } - } -} - -/// A wrapper around a `VecDeque`, to be able to implement `BootlegRead` for it -struct InputBuffer(VecDeque); -impl BootlegRead for InputBuffer { - type Error = std::convert::Infallible; - fn bootleg_read(&mut self) -> Result, Self::Error> { - Ok(self.0.pop_front()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn adder() { - let mut interpreter = Interpreter { - code: b"[->+<]", //Source: https://en.wikipedia.org/wiki/Brainfuck - instr_ptr: 0, - tape: vec![10, 5], - data_ptr: 0, - tape_size_limit: DEFAULT_TAPE_SIZE_LIMIT, - input: std::io::empty(), - }; - - while let Some(status) = interpreter.advance_until_io().expect("Unexpected error") { - match status { - IoStatus::NeedsInput => panic!("Requested input in an IO-less program"), - IoStatus::Output(_) => panic!("Produced output in an IO-less program"), - } - } - - assert_eq!(interpreter.tape, vec![0, 15]); - } - - #[test] - fn hello_world() { - let mut interpreter = Interpreter::from_ascii( - b"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.", - std::io::empty(), - ); - - let mut string = Vec::new(); - interpreter - .interpret_with_output(&mut string) - .expect("Failed to write to output buffer"); - assert_eq!(string, b"Hello World!\n"); - } - - #[test] - fn with_input_buffer() { - let mut interpreter = Interpreter::from_ascii_with_input_buffer(b"+++++.>,[-<->]."); - let output = match interpreter - .advance_until_io() - .expect("Unexpected error") - .expect("Unexpected termination") - { - IoStatus::NeedsInput => panic!("Unexpected input request"), - IoStatus::Output(out) => out, - }; - - assert_eq!( - interpreter.advance_until_io(), - Ok(Some(IoStatus::NeedsInput)) - ); - - interpreter.add_input(output); - - assert_eq!( - interpreter.advance_until_io(), - Ok(Some(IoStatus::Output(0))) - ); - assert_eq!(interpreter.advance_until_io(), Ok(None)); - } - - #[test] - fn hit_tape_size_limit() { - let mut interpreter = - Interpreter::from_ascii_with_tape_limit(b"+>+>+>+>+>", std::io::empty(), 1); - let result = interpreter.interpret_with_output(std::io::sink()); - assert_eq!( - result, - Err(InterpretError::ProgramError( - ProgramError::TapeSizeExceededLimit - )) - ); - } - - #[test] - fn positive_integer_overflow() { - interpret_with_io(b"+[+]", std::io::empty(), std::io::sink()).unwrap(); - } - - #[test] - fn negative_integer_overflow() { - interpret_with_io(b"-", std::io::empty(), std::io::sink()).unwrap(); - } -} diff --git a/ablescript/src/consts.rs b/ablescript/src/consts.rs deleted file mode 100644 index 151c6b0f..00000000 --- a/ablescript/src/consts.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! Number constants. - -use crate::value::{Value, Variable}; -use std::collections::HashMap; - -pub const ANSWER: isize = 42; - -/// Initialize a HashMap between the constant names and values -/// accessible from within AbleScript. -pub fn ablescript_consts() -> HashMap { - use Value::*; - - [ - ("TAU", Int(6)), // Circumference / radius - ("PI", Int(3)), // Deprecated, do not use - ("EULER", Int(3)), // Mathematical constant e - ("MASS", Int(70)), // @Kev#6900's weight in kilograms - ("PHI", Int(2)), // Golden ratio - ("WUA", Int(1)), // 1 - ("EULERS_CONSTANT", Int(0)), // ??? - ("GRAVITY", Int(10)), // Earth surface gravity, m/s - ("RNG", Int(12)), // Kixiron#5289 Randomly rolled dice - ("STD_RNG", Int(4)), // The standard random number is 4 (https://xkcd.com/221/) - ("INF", Int(isize::max_value())), // The biggest number - ("INTERESSANT", Int(114514)), // HTGAzureX1212.#5959 intéressant number - ("FUNNY", Int(69)), // HTGAzureX1212.#5959 funny number - ( - // Never gonna let you down - "NEVERGONNAGIVEYOUUP", - Str("1452251871514141792252515212116".to_owned()), - ), - ("OCTOTHORPE", Str("#".to_owned())), // It's an octothorpe - ("AMOGUS", Str("ඞ".to_owned())), // Amogus - ("ANSWER", Int(ANSWER)), - ("nul", Nul), - ("always", Abool(crate::value::Abool::Always)), - ("sometimes", Abool(crate::value::Abool::Sometimes)), - ("never", Abool(crate::value::Abool::Never)), - ] - .into_iter() - .map(|(name, value)| (name.to_owned(), Variable::from_value(value))) - .collect() -} diff --git a/ablescript/src/error.rs b/ablescript/src/error.rs deleted file mode 100644 index 46f9d869..00000000 --- a/ablescript/src/error.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::{brian::InterpretError, lexer::Token}; -use std::{fmt::Display, io, ops::Range}; - -#[derive(Debug)] -pub struct Error { - pub kind: ErrorKind, - pub span: Range, -} - -#[derive(Debug)] -pub enum ErrorKind { - /// Parser expected token, but none was available - UnexpectedEoi, - - /// Parser encountered unknown token - InvalidToken, - - /// Parser expected certain token, but other one appeared - UnexpectedToken(Token), - - /// Attempted to assign to undefined variable - UnknownVariable(String), - - /// Attempted to access banned variable - MeloVariable(String), - - /// Breaking / re-starting loop outside loop - LoopOpOutsideLoop, - - /// Rlyeh was executed but host interface's exit - /// doesn't exit the program - NonExitingRlyeh(i32), - - /// Missing left-hand side expression in binary expression - MissingLhs, - - /// Error when executing BF code - Brian(InterpretError), - - /// IO Error - Io(io::Error), -} - -impl Error { - pub fn new(kind: ErrorKind, span: Range) -> Self { - Self { kind, span } - } - - /// Create an UnexpectedEoi error, where the EOI occurs at the - /// given index in the input. - pub fn unexpected_eoi(index: usize) -> Self { - Self::new(ErrorKind::UnexpectedEoi, index..index) - } -} - -impl Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Error at range {}-{}: {}", - self.span.start, self.span.end, self.kind - ) - } -} -impl std::error::Error for Error {} - -impl Display for ErrorKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ErrorKind::UnexpectedEoi => write!(f, "unexpected end of input"), - ErrorKind::InvalidToken => write!(f, "invalid token"), - ErrorKind::UnexpectedToken(Token::Melo) => write!(f, "unexpected marten"), - ErrorKind::UnexpectedToken(token) => write!(f, "unexpected token {:?}", token), - ErrorKind::UnknownVariable(name) => write!(f, "unknown identifier \"{}\"", name), - ErrorKind::MeloVariable(name) => write!(f, "banned variable \"{}\"", name), - ErrorKind::LoopOpOutsideLoop => write!( - f, - "unable to perform loop operation (enough or and enough) outside a loop" - ), - &ErrorKind::NonExitingRlyeh(code) => write!(f, "program exited with code {code}"), - ErrorKind::Brian(err) => write!(f, "brainfuck error: {}", err), - // TODO: give concrete numbers here. - ErrorKind::MissingLhs => write!(f, "missing expression before binary operation"), - ErrorKind::Io(err) => write!(f, "I/O error: {}", err), - } - } -} - -impl From for ErrorKind { - fn from(e: io::Error) -> Self { - Self::Io(e) - } -} diff --git a/ablescript/src/host_interface.rs b/ablescript/src/host_interface.rs deleted file mode 100644 index 0808aab9..00000000 --- a/ablescript/src/host_interface.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::value::Variable; -use std::collections::HashMap; - -/// Host Environment Interface -pub trait HostInterface { - /// Initial variables for a stack frame - fn initial_vars(&mut self) -> HashMap; - - /// Print a string - fn print(&mut self, string: &str, new_line: bool) -> std::io::Result<()>; - - /// Read a byte - fn read_byte(&mut self) -> std::io::Result; - - /// This function should exit the program with specified code. - /// - /// For cases where exit is not desired, just let the function return - /// and interpreter will terminate with an error. - fn exit(&mut self, code: i32); -} - -/// Standard [HostInterface] implementation -#[derive(Clone, Copy, Default)] -pub struct Standard; -impl HostInterface for Standard { - fn initial_vars(&mut self) -> HashMap { - HashMap::default() - } - - fn print(&mut self, string: &str, new_line: bool) -> std::io::Result<()> { - use std::io::Write; - - let mut stdout = std::io::stdout(); - stdout.write_all(string.as_bytes())?; - if new_line { - stdout.write_all(b"\n")?; - } - - Ok(()) - } - - fn read_byte(&mut self) -> std::io::Result { - use std::io::Read; - - let mut buf = [0]; - std::io::stdin().read_exact(&mut buf)?; - Ok(buf[0]) - } - - fn exit(&mut self, code: i32) { - std::process::exit(code); - } -} diff --git a/ablescript/src/interpret.rs b/ablescript/src/interpret.rs deleted file mode 100644 index 13c6fb37..00000000 --- a/ablescript/src/interpret.rs +++ /dev/null @@ -1,812 +0,0 @@ -//! Expression evaluator and statement interpreter. -//! -//! To interpret a piece of AbleScript code, you first need to -//! construct an [ExecEnv], which is responsible for storing the stack -//! of local variable and function definitions accessible from an -//! AbleScript snippet. You can then call [ExecEnv::eval_stmts] to -//! evaluate or execute any number of expressions or statements. - -#![deny(missing_docs)] - -use crate::{ - ast::{Assignable, AssignableKind, Block, Expr, Spanned, Stmt}, - consts::ablescript_consts, - error::{Error, ErrorKind}, - host_interface::HostInterface, - value::{Functio, Value, ValueRef, Variable}, -}; -use rand::random; -use std::{ - cmp::Ordering, - collections::{HashMap, VecDeque}, - mem::take, - ops::Range, -}; - -/// An environment for executing AbleScript code. -pub struct ExecEnv { - /// The stack, ordered such that `stack[stack.len() - 1]` is the - /// top-most (newest) stack frame, and `stack[0]` is the - /// bottom-most (oldest) stack frame. - stack: Vec, - - /// The `read` statement maintains a buffer of up to 7 bits, - /// because input comes from the operating system 8 bits at a time - /// (via stdin) but gets delivered to AbleScript 3 bits at a time - /// (via the `read` statement). We store each of those bits as - /// booleans to facilitate easy manipulation. - read_buf: VecDeque, - - /// Interface to interact with the host interface - host_interface: H, - - /// Vector of blocks to be executed at the end of the program - finalisers: Vec, -} - -/// A set of visible variable and function definitions in a single -/// stack frame. -struct Scope { - /// The mapping from variable names to values. - variables: HashMap, -} - -impl Default for ExecEnv -where - H: Default + HostInterface, -{ - fn default() -> Self { - Self::with_host_interface(H::default()) - } -} - -impl Default for Scope { - fn default() -> Self { - Self { - variables: ablescript_consts(), - } - } -} - -/// The reason a successful series of statements halted. -enum HaltStatus { - /// We ran out of statements to execute. - Finished, - - /// An `enough` statement occurred at the given span, and was not - /// caught by a `loop` statement up to this point. - Enough(Range), - - /// A `and again` statement occurred at the given span, and was not - /// caught by a `loop` statement up to this point. - AndAgain(Range), -} - -/// The number of bits the `read` statement reads at once from -/// standard input. -pub const READ_BITS: u8 = 3; - -impl ExecEnv { - /// Create a new Scope with no predefined variable definitions or - /// other information. - pub fn with_host_interface(mut host_interface: H) -> Self { - Self { - stack: vec![ - Default::default(), - Scope { - variables: host_interface.initial_vars(), - }, - ], - read_buf: Default::default(), - finalisers: vec![], - host_interface, - } - } - - /// Create a new Scope with predefined variables - pub fn new_with_vars(mut host_interface: H, vars: I) -> Self - where - I: IntoIterator, - { - Self { - stack: vec![ - Scope { - variables: ablescript_consts().into_iter().chain(vars).collect(), - }, - Scope { - variables: host_interface.initial_vars(), - }, - ], - read_buf: Default::default(), - finalisers: vec![], - host_interface, - } - } - - /// Execute a set of Statements in the root stack frame. Return an - /// error if one or more of the Stmts failed to evaluate, or if a - /// `enough` or `and again` statement occurred at the top level. - pub fn eval_stmts(&mut self, stmts: &[Spanned]) -> Result<(), Error> { - match self.eval_stmts_hs(stmts, false)? { - HaltStatus::Finished => Ok(()), - HaltStatus::Enough(span) | HaltStatus::AndAgain(span) => Err(Error { - // It's an error to issue a `enough` outside of a - // `loop` statement. - kind: ErrorKind::LoopOpOutsideLoop, - span, - }), - }?; - - while !self.finalisers.is_empty() { - for block in std::mem::take(&mut self.finalisers) { - self.eval_stmts_hs(&block, true)?; - } - } - - Ok(()) - } - - /// The same as `eval_stmts`, but report "enough" and "and again" - /// exit codes as normal conditions in a HaltStatus enum, and - /// create a new stack frame if `stackframe` is true. - /// - /// `interpret`-internal code should typically prefer this - /// function over `eval_stmts`. - fn eval_stmts_hs( - &mut self, - stmts: &[Spanned], - stackframe: bool, - ) -> Result { - let init_depth = self.stack.len(); - - if stackframe { - self.stack.push(Default::default()); - } - - let mut final_result = Ok(HaltStatus::Finished); - for stmt in stmts { - final_result = self.eval_stmt(stmt); - if !matches!(final_result, Ok(HaltStatus::Finished)) { - break; - } - } - - if stackframe { - self.stack.pop(); - } - - // Invariant: stack size must have net 0 change. - debug_assert_eq!(self.stack.len(), init_depth); - final_result - } - - /// Evaluate an Expr, returning its value or an error. - fn eval_expr(&self, expr: &Spanned) -> Result { - use crate::ast::BinOpKind::*; - use crate::ast::Expr::*; - - Ok(match &expr.item { - BinOp { lhs, rhs, kind } => { - let lhs = self.eval_expr(lhs)?; - let rhs = self.eval_expr(rhs)?; - match kind { - Add => lhs + rhs, - Subtract => lhs - rhs, - Multiply => lhs * rhs, - Divide => lhs / rhs, - Greater => Value::Abool((lhs > rhs).into()), - Less => Value::Abool((lhs < rhs).into()), - Equal => Value::Abool((lhs == rhs).into()), - NotEqual => Value::Abool((lhs != rhs).into()), - } - } - Aint(expr) => !self.eval_expr(expr)?, - Literal(lit) => lit.clone().into(), - Expr::Cart(members) => Value::Cart( - members - .iter() - .map(|(value, key)| { - self.eval_expr(value).and_then(|value| { - self.eval_expr(key).map(|key| (key, ValueRef::new(value))) - }) - }) - .collect::, _>>()?, - ), - Index { expr, index } => { - let value = self.eval_expr(expr)?; - let index = self.eval_expr(index)?; - - value - .into_cart() - .get(&index) - .map(|x| x.borrow().clone()) - .unwrap_or(Value::Nul) - } - Len(expr) => Value::Int(self.eval_expr(expr)?.length()), - Keys(expr) => Value::Cart( - self.eval_expr(expr)? - .into_cart() - .into_keys() - .enumerate() - .map(|(i, k)| (Value::Int(i as isize + 1), ValueRef::new(k))) - .collect(), - ), - // TODO: not too happy with constructing an artificial - // Ident here. - Variable(name) => { - self.get_var_value(&Spanned::new(name.to_owned(), expr.span.clone()))? - } - }) - } - - /// Perform the action indicated by a statement. - fn eval_stmt(&mut self, stmt: &Spanned) -> Result { - match &stmt.item { - Stmt::Print { expr, newline } => { - let value = self.eval_expr(expr)?; - self.host_interface - .print(&value.to_string(), *newline) - .map_err(|e| Error::new(e.into(), stmt.span.clone()))?; - } - Stmt::Dim { ident, init } => { - let init = match init { - Some(e) => self.eval_expr(e)?, - None => Value::Nul, - }; - - self.decl_var(&ident.item, init); - } - Stmt::Functio { - ident, - params, - body, - } => { - self.decl_var( - &ident.item, - Value::Functio(Functio::Able { - params: params.iter().map(|ident| ident.item.to_owned()).collect(), - body: body.to_owned(), - }), - ); - } - Stmt::BfFunctio { - ident, - tape_len, - code, - } => { - self.decl_var( - &ident.item, - Value::Functio(Functio::Bf { - instructions: code.to_owned(), - tape_len: tape_len - .as_ref() - .map(|tape_len| { - self.eval_expr(tape_len).map(|v| v.into_isize() as usize) - }) - .unwrap_or(Ok(crate::brian::DEFAULT_TAPE_SIZE_LIMIT))?, - }), - ); - } - Stmt::Unless { cond, body } => { - if !self.eval_expr(cond)?.into_abool().to_bool() { - return self.eval_stmts_hs(body, true); - } - } - Stmt::Call { expr, args } => { - let func = self.eval_expr(expr)?.into_functio(); - return self.fn_call(func, args, &stmt.span); - } - Stmt::Loop { body } => loop { - let res = self.eval_stmts_hs(body, true)?; - match res { - HaltStatus::Finished => (), - HaltStatus::Enough(_) => break, - HaltStatus::AndAgain(_) => continue, - } - }, - Stmt::Assign { assignable, value } => { - self.assign(assignable, self.eval_expr(value)?)?; - } - Stmt::Enough => { - return Ok(HaltStatus::Enough(stmt.span.clone())); - } - Stmt::AndAgain => { - return Ok(HaltStatus::AndAgain(stmt.span.clone())); - } - Stmt::Melo(ident) => match self.get_var_mut(ident)? { - var @ Variable::Ref(_) => *var = Variable::Melo, - Variable::Melo => { - for s in &mut self.stack { - if s.variables.remove(&ident.item).is_some() { - break; - } - } - } - }, - Stmt::Finally(block) => self.finalisers.push(block.clone()), - Stmt::Rlyeh => { - // Maybe print a creepy error message or something - // here at some point. ~~Alex - let code = random(); - self.host_interface.exit(code); - return Err(Error::new( - ErrorKind::NonExitingRlyeh(code), - stmt.span.clone(), - )); - } - Stmt::Rickroll => { - self.host_interface - .print(include_str!("rickroll"), false) - .map_err(|e| Error::new(e.into(), stmt.span.clone()))?; - } - Stmt::Read(assignable) => { - let mut value = 0; - for _ in 0..READ_BITS { - value <<= 1; - value += self - .get_bit() - .map_err(|e| Error::new(e, stmt.span.clone()))? - as isize; - } - - self.assign(assignable, Value::Int(value))?; - } - } - - Ok(HaltStatus::Finished) - } - - /// Assign a value to an Assignable. - fn assign(&mut self, dest: &Assignable, value: Value) -> Result<(), Error> { - match dest.kind { - AssignableKind::Variable => { - self.get_var_rc_mut(&dest.ident)?.replace(value); - } - AssignableKind::Index { ref indices } => { - let mut cell = self.get_var_rc_mut(&dest.ident)?.clone(); - for index in indices { - let index = self.eval_expr(index)?; - - let next_cell = match &mut *cell.borrow_mut() { - Value::Cart(c) => { - // cell is a cart, so we can do simple - // indexing. - if let Some(x) = c.get(&index) { - // cell[index] exists, get a shared - // reference to it. - ValueRef::clone(x) - } else { - // cell[index] does not exist, so we - // insert an empty cart by default - // instead. - let next_cell = ValueRef::new(Value::Cart(Default::default())); - c.insert(index, ValueRef::clone(&next_cell)); - next_cell - } - } - x => { - // cell is not a cart; `take` it, convert - // it into a cart, and write the result - // back into it. - let mut cart = take(x).into_cart(); - let next_cell = ValueRef::new(Value::Cart(Default::default())); - cart.insert(index, ValueRef::clone(&next_cell)); - *x = Value::Cart(cart); - next_cell - } - }; - cell = next_cell; - } - cell.replace(value); - } - } - - Ok(()) - } - - /// Call a function with the given arguments (i.e., actual - /// parameters). If the function invocation fails for some reason, - /// report the error at `span`. - fn fn_call( - &mut self, - func: Functio, - args: &[Spanned], - span: &Range, - ) -> Result { - // Arguments that are ExprKind::Variable are pass by - // reference; all other expressions are pass by value. - let args = args - .iter() - .map(|arg| { - if let Expr::Variable(name) = &arg.item { - self.get_var_rc_mut(&Spanned::new(name.to_owned(), arg.span.clone())) - .cloned() - } else { - self.eval_expr(arg).map(ValueRef::new) - } - }) - .collect::, Error>>()?; - - self.fn_call_with_values(func, &args, span) - } - - fn fn_call_with_values( - &mut self, - func: Functio, - args: &[ValueRef], - span: &Range, - ) -> Result { - match func { - Functio::Bf { - instructions, - tape_len, - } => { - let mut input: Vec = vec![]; - for arg in args { - arg.borrow().bf_write(&mut input); - } - - let mut output = vec![]; - - crate::brian::Interpreter::from_ascii_with_tape_limit( - &instructions, - &input as &[_], - tape_len, - ) - .interpret_with_output(&mut output) - .map_err(|e| Error { - kind: ErrorKind::Brian(e), - span: span.to_owned(), - })?; - - match String::from_utf8(output) { - Ok(string) => self.host_interface.print(&string, false), - Err(e) => self - .host_interface - .print(&format!("{:?}", e.as_bytes()), true), - } - .map_err(|e| Error::new(e.into(), span.clone()))?; - - Ok(HaltStatus::Finished) - } - Functio::Able { params, body } => { - self.stack.push(Default::default()); - - for (param, arg) in params.iter().zip(args.iter()) { - self.decl_var_shared(param, arg.to_owned()); - } - - let res = self.eval_stmts_hs(&body, false); - - self.stack.pop(); - res - } - Functio::Builtin(b) => { - b.call(args).map_err(|e| Error::new(e, span.clone()))?; - Ok(HaltStatus::Finished) - } - Functio::Chain { functios, kind } => { - use crate::value::functio::FunctioChainKind; - let (left_functio, right_functio) = *functios; - Ok( - match match kind { - FunctioChainKind::Equal => { - let (l, r) = args.split_at(args.len() / 2); - - ( - self.fn_call_with_values(left_functio, l, span)?, - self.fn_call_with_values(right_functio, r, span)?, - ) - } - FunctioChainKind::ByArity => { - let (l, r) = Self::deinterlace( - args, - (left_functio.arity(), right_functio.arity()), - ); - - ( - self.fn_call_with_values(left_functio, &l, span)?, - self.fn_call_with_values(right_functio, &r, span)?, - ) - } - } { - (s, HaltStatus::Finished) => s, - (HaltStatus::Finished, s) => s, - (_, r) => r, - }, - ) - } - Functio::Eval(code) => self.eval_stmts_hs(&crate::parser::parse(&code)?, false), - } - } - - fn deinterlace(args: &[ValueRef], arities: (usize, usize)) -> (Vec, Vec) { - let n_alternations = usize::min(arities.0, arities.1); - let (extra_l, extra_r) = match Ord::cmp(&arities.0, &arities.1) { - Ordering::Less => (0, arities.1 - arities.0), - Ordering::Equal => (0, 0), - Ordering::Greater => (arities.0 - arities.1, 0), - }; - - ( - args.chunks(2) - .take(n_alternations) - .map(|chunk| ValueRef::clone(&chunk[0])) - .chain( - args.get(2 * n_alternations..) - .iter() - .copied() - .flatten() - .map(ValueRef::clone) - .take(extra_l), - ) - .collect(), - args.chunks(2) - .take(n_alternations) - .flat_map(|chunk| chunk.get(1)) - .map(ValueRef::clone) - .chain( - args.get(2 * n_alternations..) - .iter() - .copied() - .flatten() - .map(ValueRef::clone) - .take(extra_r), - ) - .collect(), - ) - } - - /// Get a single bit from the bit buffer, or refill it from - /// standard input if it is empty. - fn get_bit(&mut self) -> Result { - const BITS_PER_BYTE: u8 = 8; - - if self.read_buf.is_empty() { - let byte = self.host_interface.read_byte()?; - - for n in (0..BITS_PER_BYTE).rev() { - self.read_buf.push_back(((byte >> n) & 1) != 0); - } - } - - Ok(self - .read_buf - .pop_front() - .expect("We just pushed to the buffer if it was empty")) - } - - /// Get the value of a variable. Throw an error if the variable is - /// inaccessible or banned. - fn get_var_value(&self, name: &Spanned) -> Result { - // Search for the name in the stack from top to bottom. - match self - .stack - .iter() - .rev() - .find_map(|scope| scope.variables.get(&name.item)) - { - Some(Variable::Ref(r)) => Ok(r.borrow().clone()), - Some(Variable::Melo) => Err(Error { - kind: ErrorKind::MeloVariable(name.item.to_owned()), - span: name.span.clone(), - }), - None => Ok(Value::Undefined), - } - } - - /// Get a mutable reference to a variable. - fn get_var_mut(&mut self, name: &Spanned) -> Result<&mut Variable, Error> { - // This function has a lot of duplicated code with `get_var`, - // which I feel like is a bad sign... - match self - .stack - .iter_mut() - .rev() - .find_map(|scope| scope.variables.get_mut(&name.item)) - { - Some(var) => Ok(var), - None => Err(Error { - kind: ErrorKind::UnknownVariable(name.item.to_owned()), - span: name.span.clone(), - }), - } - } - - /// Get an reference to an Rc'd pointer to the value of a variable. Throw an error - /// if the variable is inaccessible or banned. - fn get_var_rc_mut(&mut self, name: &Spanned) -> Result<&mut ValueRef, Error> { - match self.get_var_mut(name)? { - Variable::Ref(r) => Ok(r), - Variable::Melo => Err(Error { - kind: ErrorKind::MeloVariable(name.item.to_owned()), - span: name.span.clone(), - }), - } - } - - /// Declare a new variable, with the given initial value. - fn decl_var(&mut self, name: &str, value: Value) { - self.decl_var_shared(name, ValueRef::new(value)); - } - - /// Declare a new variable, with the given shared initial value. - fn decl_var_shared(&mut self, name: &str, value: ValueRef) { - self.stack - .iter_mut() - .last() - .expect("Declaring variable on empty stack") - .variables - .insert(name.to_owned(), Variable::Ref(value)); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - ast::{Expr, Literal}, - host_interface::Standard, - }; - - #[test] - fn basic_expression_test() { - // Check that 2 + 2 = 4. - let env = ExecEnv::::default(); - assert_eq!( - env.eval_expr(&Spanned { - item: Expr::BinOp { - lhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(2)), - span: 1..1, - }), - rhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(2)), - span: 1..1, - }), - kind: crate::ast::BinOpKind::Add, - }, - span: 1..1 - }) - .unwrap(), - Value::Int(4) - ) - } - - #[test] - fn type_coercions() { - // The sum of an integer and an aboolean causes an aboolean - // coercion. - - let env = ExecEnv::::default(); - assert_eq!( - env.eval_expr(&Spanned { - item: Expr::BinOp { - lhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(2)), - span: 1..1, - }), - rhs: Box::new(Spanned { - item: Expr::Variable("always".to_owned()), - span: 1..1, - }), - kind: crate::ast::BinOpKind::Add, - }, - span: 1..1 - }) - .unwrap(), - Value::Int(3) - ); - } - - #[test] - fn overflow_should_not_panic() { - // Integer overflow should throw a recoverable error instead - // of panicking. - let env = ExecEnv::::default(); - assert_eq!( - env.eval_expr(&Spanned { - item: Expr::BinOp { - lhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(isize::MAX)), - span: 1..1, - }), - rhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(1)), - span: 1..1, - }), - kind: crate::ast::BinOpKind::Add, - }, - span: 1..1 - }) - .unwrap(), - Value::Int(-9223372036854775808) - ); - - // And the same for divide by zero. - assert_eq!( - env.eval_expr(&Spanned { - item: Expr::BinOp { - lhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(84)), - span: 1..1, - }), - rhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(0)), - span: 1..1, - }), - kind: crate::ast::BinOpKind::Divide, - }, - span: 1..1 - }) - .unwrap(), - Value::Int(2) - ); - } - - // From here on out, I'll use this function to parse and run - // expressions, because writing out abstract syntax trees by hand - // takes forever and is error-prone. - fn eval(env: &mut ExecEnv, src: &str) -> Result { - // We can assume there won't be any syntax errors in the - // interpreter tests. - let ast = crate::parser::parse(src).unwrap(); - env.eval_stmts(&ast).map(|()| Value::Nul) - } - - #[test] - fn variable_decl_and_assignment() { - // Functions have no return values, so use some - // pass-by-reference hacks to detect the correct - // functionality. - let mut env = ExecEnv::::default(); - - // Declaring and reading from a variable. - eval(&mut env, "foo dim 32; bar dim foo + 1;").unwrap(); - assert_eq!( - env.get_var_value(&Spanned { - item: "bar".to_owned(), - span: 1..1, - }) - .unwrap(), - Value::Int(33) - ); - - // Assigning an existing variable. - eval(&mut env, "/*hi*/ =: foo;").unwrap(); - assert_eq!( - env.get_var_value(&Spanned { - item: "foo".to_owned(), - span: 1..1, - }) - .unwrap(), - Value::Str("hi".to_owned()) - ); - - // But variable assignment should be illegal when the variable - // hasn't been declared in advance. - eval(&mut env, "bar + 1 =: invalid;").unwrap_err(); - } - - #[test] - fn scope_visibility_rules() { - // Declaration and assignment of variables declared in an `if` - // statement should have no effect on those declared outside - // of it. - let mut env = ExecEnv::::default(); - eval( - &mut env, - "foo dim 1; 2 =: foo; unless (never) { foo dim 3; 4 =: foo; }", - ) - .unwrap(); - - assert_eq!( - env.get_var_value(&Spanned { - item: "foo".to_owned(), - span: 1..1, - }) - .unwrap(), - Value::Int(2) - ); - } -} diff --git a/ablescript/src/lexer.rs b/ablescript/src/lexer.rs deleted file mode 100644 index 2c018615..00000000 --- a/ablescript/src/lexer.rs +++ /dev/null @@ -1,144 +0,0 @@ -use logos::{Lexer, Logos}; - -#[derive(Logos, Debug, PartialEq, Eq, Clone)] -#[logos(skip r"[ \t\n\f]+")] -#[logos(skip r"owo .*")] -#[rustfmt::skip] -pub enum Token { - // Symbols - #[token("(")] LeftParen, - #[token(")")] RightParen, - #[token("[")] LeftBracket, - #[token("]")] RightBracket, - #[token("{")] LeftCurly, - #[token("}")] RightCurly, - #[token(";")] Semicolon, - #[token(",")] Comma, - - // Operators - #[token("+")] Plus, - #[token("-")] Minus, - #[token("*")] Star, - #[token("/")] FwdSlash, - #[token("=:")] Assign, - #[token("<=")] Arrow, - - // Logical operators - #[token("<")] LessThan, - #[token(">")] GreaterThan, - #[token("=")] Equals, - #[token("ain't")] Aint, - - // Keywords - #[token("functio")] Functio, - #[token("bff")] Bff, - #[token("dim")] Dim, - #[token("print")] Print, - #[token("read")] Read, - #[token("melo")] Melo, - #[token("T-Dark")] TDark, - - // Control flow keywords - #[token("unless")] Unless, - #[token("loop")] Loop, - #[token("enough")] Enough, - #[token("and again")] AndAgain, - #[token("finally")] Finally, - #[token("rlyeh")] Rlyeh, - - #[token("rickroll")] Rickroll, - - // Literals - #[token("/*", get_string)] String(String), - #[regex(r"-?[0-9]+", get_value)] Integer(isize), - #[regex(r"\p{XID_Start}", get_value)] Char(char), - #[regex(r"\p{XID_Start}[\p{XID_Continue}]+", get_ident)] - #[token("and ", |_| "and".to_owned())] - Identifier(String), -} - -fn get_value(lexer: &mut Lexer) -> Option { - lexer.slice().parse().ok() -} - -fn get_string(lexer: &mut Lexer) -> Option { - lexer.bump(lexer.remainder().find("*/")?); - - let mut string = String::new(); - let mut slice = &lexer.slice()[2..]; - while let Some(escape_start) = slice.find('"') { - // Push predeceasing string - string.push_str(slice.get(..escape_start)?); - - // Move slice behind escape start delimiter - slice = slice.get(escape_start + 1..)?; - - // Get escape end delimiter position and parse string before it to - // a character from it's unicode value (base-12) and push it to string - let escape_end = slice.find('"')?; - string.push( - u32::from_str_radix(slice.get(..escape_end)?, 12) - .ok() - .and_then(char::from_u32)?, - ); - - // Move slice behind escape end delimiter - slice = slice.get(escape_end + 1..)?; - } - - // Push remaining string - string.push_str(slice); - lexer.bump(2); - - Some(string) -} - -fn get_ident(lexer: &mut Lexer) -> String { - lexer.slice().to_owned() -} - -#[cfg(test)] -mod tests { - use super::Token; - use super::Token::*; - use logos::Logos; - - #[test] - fn simple_fn() { - let code = "functio test() { dim var 3; unless (var ain't 3) { var print } }"; - let expected = &[ - Functio, - Identifier("test".to_owned()), - LeftParen, - RightParen, - LeftCurly, - Dim, - Identifier("var".to_owned()), - Integer(3), - Semicolon, - Unless, - LeftParen, - Identifier("var".to_owned()), - Aint, - Integer(3), - RightParen, - LeftCurly, - Identifier("var".to_owned()), - Print, - RightCurly, - RightCurly, - ]; - - let result: Vec<_> = Token::lexer(code).collect::>().unwrap(); - assert_eq!(result, expected); - } - - #[test] - fn escapes() { - let code = r#"/*»"720B""722B""7195"«*/"#; - let expected = &[Token::String("»にゃぁ«".to_owned())]; - - let result: Vec<_> = Token::lexer(code).collect::>().unwrap(); - assert_eq!(result, expected); - } -} diff --git a/ablescript/src/lib.rs b/ablescript/src/lib.rs deleted file mode 100644 index d2282119..00000000 --- a/ablescript/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! The AbleScript language reference implementation. See -//! for more -//! information. - -#![forbid(unsafe_code)] -#![cfg_attr(not(test), forbid(clippy::unwrap_used))] - -pub mod ast; -pub mod error; -pub mod host_interface; -pub mod interpret; -pub mod parser; -pub mod value; - -mod base_55; -mod brian; -mod consts; -mod lexer; diff --git a/ablescript/src/parser.rs b/ablescript/src/parser.rs deleted file mode 100644 index c829bf9f..00000000 --- a/ablescript/src/parser.rs +++ /dev/null @@ -1,812 +0,0 @@ -//! AbleScript Parser -//! -//! Type of this parser is recursive descent - -use crate::ast::*; -use crate::error::{Error, ErrorKind}; -use crate::lexer::Token; -use logos::{Lexer, Logos}; - -/// Parser structure which holds lexer and metadata -/// -/// Make one using [`Parser::new`] function -struct Parser<'source> { - lexer: Lexer<'source, Token>, - tdark: bool, -} - -impl<'source> Parser<'source> { - /// Create a new parser from source code - fn new(source: &'source str) -> Self { - Self { - lexer: Token::lexer(source), - tdark: false, - } - } - - /// Start parsing tokens - /// - /// Loops trough lexer, parses statements, returns AST - fn parse(&mut self) -> Result { - let mut ast = vec![]; - while let Some(token) = self.lexer.next() { - match token { - // T-Dark block (replace `lang` with `script`) - Ok(Token::TDark) => ast.extend(self.tdark_flow()?), - Ok(token) => ast.push(self.parse_stmt(token)?), - - // Invalid token - Err(()) => return Err(Error::new(ErrorKind::InvalidToken, self.lexer.span())), - } - } - Ok(ast) - } - - /// Get next item - /// - /// If EOF, return Error instead of None - fn checked_next(&mut self) -> Result { - match self.lexer.next() { - Some(Ok(t)) => Ok(t), - Some(Err(())) => Err(Error::new(ErrorKind::InvalidToken, self.lexer.span())), - None => Err(Error::unexpected_eoi(self.lexer.span().start)), - } - } - - /// Parse a token - /// - /// This function will route to corresponding flow functions - /// which may advance the lexer iterator - fn parse_stmt(&mut self, token: Token) -> Result, Error> { - let start = self.lexer.span().start; - - match token { - Token::Unless => self.unless_flow(), - Token::Functio => self.functio_flow(), - Token::Bff => self.bff_flow(), - Token::Melo => self.melo_flow(), - Token::Loop => self.get_block().map(|body| Stmt::Loop { body }), - Token::Enough => self.semicolon_terminated(Stmt::Enough), - Token::AndAgain => self.semicolon_terminated(Stmt::AndAgain), - Token::Finally => self.get_block().map(Stmt::Finally), - Token::Rlyeh => self.semicolon_terminated(Stmt::Rlyeh), - Token::Rickroll => self.semicolon_terminated(Stmt::Rickroll), - - Token::Identifier(_) - | Token::String(_) - | Token::Integer(_) - | Token::Char(_) - | Token::Aint - | Token::LeftBracket - | Token::LeftParen => self.value_flow(token), - - t => Err(Error { - kind: ErrorKind::UnexpectedToken(t), - span: start..self.lexer.span().end, - }), - } - .map(|stmt| Spanned::new(stmt, start..self.lexer.span().end)) - } - - /// Require statement to be semicolon terminated - /// - /// Utility function for short statements - fn semicolon_terminated(&mut self, stmt_kind: Stmt) -> Result { - self.require(Token::Semicolon)?; - Ok(stmt_kind) - } - - /// Require next item to be equal with expected one - fn require(&mut self, required: Token) -> Result<(), Error> { - match self.checked_next()? { - t if t == required => Ok(()), - t => Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())), - } - } - - /// Get an Identifier - fn get_ident(&mut self) -> Result, Error> { - match self.checked_next()? { - Token::Identifier(ident) => { - Ok(Spanned::new(self.tdark_subst(ident), self.lexer.span())) - } - t => Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())), - } - } - - /// Parse an expression - /// - /// AbleScript strongly separates expressions from statements. - /// Expressions do not have any side effects and the are - /// only mathematial and logical operations or values. - fn parse_expr( - &mut self, - token: Token, - buf: &mut Option>, - ) -> Result, Error> { - let start = match buf { - Some(e) => e.span.start, - None => self.lexer.span().start, - }; - - match token { - // Values - Token::Identifier(i) => Ok(Expr::Variable(self.tdark_subst(i))), - Token::Integer(i) => Ok(Expr::Literal(Literal::Int(i))), - Token::String(s) => Ok(Expr::Literal(Literal::Str(self.tdark_subst(s)))), - Token::Char(c) => Ok(Expr::Literal(Literal::Char(c))), - Token::LeftBracket => match buf.take() { - Some(buf) => self.index_flow(buf), - None => self.cart_flow(), - }, - - // Operations - Token::Aint if buf.is_none() => { - let next = self.checked_next()?; - Ok(Expr::Aint(Box::new(self.parse_expr(next, buf)?))) - } - - Token::Plus - | Token::Minus - | Token::Star - | Token::FwdSlash - | Token::Equals - | Token::LessThan - | Token::GreaterThan - | Token::Aint => self.binop_flow( - BinOpKind::from_token(token).map_err(|e| Error::new(e, self.lexer.span()))?, - buf, - ), - - Token::LeftParen => return self.expr_flow(Token::RightParen), - t => Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())), - } - .map(|expr| Spanned::new(expr, start..self.lexer.span().end)) - } - - /// Flow for creating carts - fn cart_flow(&mut self) -> Result { - let mut cart = vec![]; - let mut buf = None; - - match self.checked_next()? { - Token::RightBracket => (), - t => { - buf = Some(self.parse_expr(t, &mut buf)?); - 'cart: loop { - let value = loop { - match self.checked_next()? { - Token::Arrow => break buf.take(), - t => buf = Some(self.parse_expr(t, &mut buf)?), - } - } - .ok_or_else(|| { - Error::new(ErrorKind::UnexpectedToken(Token::Arrow), self.lexer.span()) - })?; - - let key = loop { - match self.checked_next()? { - Token::RightBracket => { - cart.push(( - value, - buf.take().ok_or_else(|| { - Error::unexpected_eoi(self.lexer.span().start) - })?, - )); - - break 'cart; - } - Token::Comma => break buf.take(), - t => buf = Some(self.parse_expr(t, &mut buf)?), - } - } - .ok_or_else(|| Error::unexpected_eoi(self.lexer.span().start))?; - - cart.push((value, key)); - } - } - } - - Ok(Expr::Cart(cart)) - } - - /// Flow for indexing operations - /// - /// Indexing with empty index resolves to length of expression, else it indexes - fn index_flow(&mut self, expr: Spanned) -> Result { - let mut buf = None; - Ok(loop { - match self.checked_next()? { - Token::RightBracket => match buf { - Some(index) => { - break Expr::Index { - expr: Box::new(expr), - index: Box::new(index), - } - } - None => break Expr::Len(Box::new(expr)), - }, - Token::GreaterThan if buf.is_none() => { - self.require(Token::RightBracket)?; - break Expr::Keys(Box::new(expr)); - } - token => buf = Some(self.parse_expr(token, &mut buf)?), - } - }) - } - - /// Flow for operators - /// - /// Generates operation from LHS buffer and next expression as RHS - /// - /// This is unaware of precedence, as AbleScript do not have it - fn binop_flow( - &mut self, - kind: BinOpKind, - lhs: &mut Option>, - ) -> Result { - Ok(Expr::BinOp { - lhs: Box::new( - lhs.take() - .ok_or_else(|| Error::new(ErrorKind::MissingLhs, self.lexer.span()))?, - ), - rhs: { - let next = self.checked_next()?; - Box::new(self.parse_expr(next, &mut None)?) - }, - kind, - }) - } - - /// Parse expressions until terminate token - fn expr_flow(&mut self, terminate: Token) -> Result, Error> { - let mut buf = None; - Ok(loop { - match self.checked_next()? { - t if t == terminate => { - break buf.take().ok_or_else(|| { - Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span()) - })? - } - t => buf = Some(self.parse_expr(t, &mut buf)?), - } - }) - } - - /// Parse a list of statements between curly braces - fn get_block(&mut self) -> Result { - self.require(Token::LeftCurly)?; - let mut block = vec![]; - - loop { - match self.checked_next()? { - Token::RightCurly => break, - Token::TDark => block.extend(self.tdark_flow()?), - t => block.push(self.parse_stmt(t)?), - } - } - Ok(block) - } - - /// Parse T-Dark block - fn tdark_flow(&mut self) -> Result { - self.tdark = true; - let block = self.get_block(); - self.tdark = false; - block - } - - /// If Statement parser gets any kind of value (Identifier or Literal) - /// It cannot parse it as it do not parse expressions. Instead of it it - /// will parse it to function call or print statement. - fn value_flow(&mut self, init: Token) -> Result { - let mut buf = Some(self.parse_expr(init, &mut None)?); - Ok(loop { - match self.checked_next()? { - // Print to stdout - Token::Print => { - break Stmt::Print { - expr: buf.take().ok_or_else(|| { - Error::new(ErrorKind::UnexpectedToken(Token::Print), self.lexer.span()) - })?, - newline: match self.checked_next()? { - Token::Semicolon => true, - Token::Minus => { - self.require(Token::Semicolon)?; - false - } - token => { - return Err(Error::new( - ErrorKind::UnexpectedToken(token), - self.lexer.span(), - )); - } - }, - }; - } - - // Functio call - Token::LeftParen => { - break self.functio_call_flow(buf.take().ok_or_else(|| { - Error::new( - ErrorKind::UnexpectedToken(Token::LeftParen), - self.lexer.span(), - ) - })?)?; - } - - // Variable declaration - Token::Dim => { - return match buf.take() { - Some(Spanned { - item: Expr::Variable(ident), - span, - }) => Ok(Stmt::Dim { - ident: Spanned::new(ident, span), - init: { - let mut init = None; - loop { - match self.checked_next()? { - Token::Semicolon => break init, - token => init = Some(self.parse_expr(token, &mut init)?), - } - } - }, - }), - _ => Err(Error::new( - ErrorKind::UnexpectedToken(Token::Dim), - self.lexer.span(), - )), - } - } - - // Variable assignment - Token::Assign => { - return match buf.take() { - Some(expr) => self.assignment_flow(expr), - None => Err(Error::new( - ErrorKind::UnexpectedToken(Token::Assign), - self.lexer.span(), - )), - } - } - - // Read input - Token::Read => { - if let Some(Ok(assignable)) = buf.take().map(Assignable::from_expr) { - self.require(Token::Semicolon)?; - break Stmt::Read(assignable); - } else { - return Err(Error::new( - ErrorKind::UnexpectedToken(Token::Read), - self.lexer.span(), - )); - } - } - - t => buf = Some(self.parse_expr(t, &mut buf)?), - } - }) - } - - /// Parse Unless flow - /// - /// Consists of condition and block, there is no else - fn unless_flow(&mut self) -> Result { - self.require(Token::LeftParen)?; - Ok(Stmt::Unless { - cond: self.expr_flow(Token::RightParen)?, - body: self.get_block()?, - }) - } - - /// Parse functio flow - /// - /// functio $ident (a, b, c) { ... } - fn functio_flow(&mut self) -> Result { - let ident = self.get_ident()?; - self.require(Token::LeftParen)?; - - let mut params = vec![]; - loop { - match self.checked_next()? { - Token::RightParen => break, - Token::Identifier(i) => { - params.push(Spanned::new(i, self.lexer.span())); - - // Require comma (next) or right paren (end) after identifier - match self.checked_next()? { - Token::Comma => continue, - Token::RightParen => break, - t => { - return Err(Error::new( - ErrorKind::UnexpectedToken(t), - self.lexer.span(), - )) - } - } - } - t => return Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())), - } - } - - let body = self.get_block()?; - - Ok(Stmt::Functio { - ident, - params, - body, - }) - } - - /// Parse BF function declaration - /// - /// `bff $ident ([tapelen]) { ... }` - fn bff_flow(&mut self) -> Result { - let ident = self.get_ident()?; - - let tape_len = match self.checked_next()? { - Token::LeftParen => { - let len = Some(self.expr_flow(Token::RightParen)?); - self.require(Token::LeftCurly)?; - len - } - Token::LeftCurly => None, - token => { - return Err(Error::new( - ErrorKind::UnexpectedToken(token), - self.lexer.span(), - )) - } - }; - - let mut code: Vec = vec![]; - loop { - match self.lexer.next() { - Some(Ok(Token::RightCurly)) => break, - Some(_) => code.push(self.lexer.slice().as_bytes()[0]), - None => return Err(Error::unexpected_eoi(self.lexer.span().start)), - } - } - - Ok(Stmt::BfFunctio { - ident, - tape_len, - code, - }) - } - - /// Parse functio call flow - fn functio_call_flow(&mut self, expr: Spanned) -> Result { - let mut args = vec![]; - let mut buf = None; - loop { - match self.checked_next()? { - // End of argument list - Token::RightParen => { - if let Some(expr) = buf.take() { - args.push(expr) - } - break; - } - - // Next argument - Token::Comma => match buf.take() { - Some(expr) => args.push(expr), - // Comma alone - None => { - return Err(Error::new( - ErrorKind::UnexpectedToken(Token::Comma), - self.lexer.span(), - )) - } - }, - t => buf = Some(self.parse_expr(t, &mut buf)?), - } - } - - self.require(Token::Semicolon)?; - Ok(Stmt::Call { expr, args }) - } - - /// Parse assignment to assignable - fn assignment_flow(&mut self, value: Spanned) -> Result { - let ident = self.get_ident()?; - let kind = match self.checked_next()? { - Token::Semicolon => AssignableKind::Variable, - Token::LeftBracket => { - let mut indices = vec![]; - loop { - indices.push(self.expr_flow(Token::RightBracket)?); - match self.checked_next()? { - Token::Semicolon => break AssignableKind::Index { indices }, - Token::LeftBracket => (), - t => { - return Err(Error::new( - ErrorKind::UnexpectedToken(t), - self.lexer.span(), - )) - } - } - } - } - t => return Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())), - }; - - Ok(Stmt::Assign { - assignable: Assignable { ident, kind }, - value, - }) - } - - /// Parse Melo flow - fn melo_flow(&mut self) -> Result { - let ident = self.get_ident()?; - self.semicolon_terminated(Stmt::Melo(ident)) - } - - /// Perform lang -> script substitution if in T-Dark block - fn tdark_subst(&self, mut string: String) -> String { - if self.tdark { - if let Some(pos) = string.to_lowercase().find("lang") { - let range = pos..pos + 4; - let mut count_upper = 0_u8; - string.replace_range( - range.clone(), - &(string[range] - .chars() - .zip("scri".chars()) - .map(|(lc, sc)| { - if lc.is_uppercase() { - count_upper += 1; - sc.to_ascii_uppercase() - } else { - sc.to_ascii_lowercase() - } - }) - .collect::() - + match count_upper { - 0 | 1 => "pt", - 2 if rand::random() => "Pt", - 2 => "pT", - _ => "PT", - }), - ) - } - } - - string - } -} - -/// Parse AbleScript code into AST -pub fn parse(source: &str) -> Result { - Parser::new(source).parse() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn simple_math() { - let code = "1 * (num + 3) / 666 print;"; - let expected = &[Spanned { - item: Stmt::Print { - expr: Spanned { - item: Expr::BinOp { - lhs: Box::new(Spanned { - item: Expr::BinOp { - lhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(1)), - span: 0..1, - }), - rhs: Box::new(Spanned { - item: Expr::BinOp { - lhs: Box::new(Spanned { - item: Expr::Variable("num".to_owned()), - span: 5..6, - }), - rhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(3)), - span: 9..10, - }), - kind: BinOpKind::Add, - }, - span: 5..10, - }), - kind: BinOpKind::Multiply, - }, - span: 0..11, - }), - rhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(666)), - span: 14..17, - }), - kind: BinOpKind::Divide, - }, - span: 0..17, - }, - newline: true, - }, - span: 0..24, - }]; - - let ast = Parser::new(code).parse().unwrap(); - assert_eq!(ast, expected); - } - - #[test] - fn variable_declaration() { - let code = "var dim 42;"; - let expected = &[Spanned { - item: Stmt::Dim { - ident: Spanned { - item: "var".to_owned(), - span: 0..3, - }, - init: Some(Spanned { - item: Expr::Literal(Literal::Int(42)), - span: 4..6, - }), - }, - span: 0..11, - }]; - - let ast = Parser::new(code).parse().unwrap(); - assert_eq!(ast, expected); - } - - #[test] - fn unless_flow() { - let code = "unless (never + never) { /*Buy Able products!*/ print; }"; - let expected = &[Spanned { - item: Stmt::Unless { - cond: Spanned { - item: Expr::BinOp { - lhs: Box::new(Spanned { - item: Expr::Variable("never".to_owned()), - span: 8..13, - }), - rhs: Box::new(Spanned { - item: Expr::Variable("never".to_owned()), - span: 16..21, - }), - kind: BinOpKind::Add, - }, - span: 8..21, - }, - body: vec![Spanned { - item: Stmt::Print { - expr: Spanned { - item: Expr::Literal(Literal::Str("Buy Able products!".to_owned())), - span: 25..47, - }, - newline: true, - }, - span: 25..54, - }], - }, - span: 0..56, - }]; - - let ast = Parser::new(code).parse().unwrap(); - assert_eq!(ast, expected); - } - - #[test] - fn tdark() { - let code = "T-Dark { lang dim /*lang*/ + lang; }"; - let expected = &[Spanned { - item: Stmt::Dim { - ident: Spanned { - item: "script".to_owned(), - span: 9..15, - }, - init: Some(Spanned { - item: Expr::BinOp { - lhs: Box::new(Spanned { - item: Expr::Literal(Literal::Str("script".to_owned())), - span: 20..26, - }), - rhs: Box::new(Spanned { - item: Expr::Variable("script".to_owned()), - span: 29..33, - }), - kind: BinOpKind::Add, - }, - span: 20..33, - }), - }, - span: 9..34, - }]; - - let ast = Parser::new(code).parse().unwrap(); - assert_eq!(ast, expected); - } - - #[test] - fn cart_construction() { - let code = "[/*able*/ <= 1, /*script*/ <= 3 - 1] print;"; - let expected = &[Spanned { - item: Stmt::Print { - expr: Spanned { - item: Expr::Cart(vec![ - ( - Spanned { - item: Expr::Literal(Literal::Str("able".to_owned())), - span: 1..7, - }, - Spanned { - item: Expr::Literal(Literal::Int(1)), - span: 11..12, - }, - ), - ( - Spanned { - item: Expr::Literal(Literal::Str("script".to_owned())), - span: 14..22, - }, - Spanned { - item: Expr::BinOp { - kind: BinOpKind::Subtract, - lhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(3)), - span: 26..27, - }), - rhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(1)), - span: 30..31, - }), - }, - span: 26..31, - }, - ), - ]), - span: 0..32, - }, - newline: true, - }, - span: 0..39, - }]; - - let ast = Parser::new(code).parse().unwrap(); - assert_eq!(ast, expected); - } - - #[test] - fn cart_index() { - let code = "[/*able*/ <= /*ablecorp*/][/*ablecorp*/] print;"; - let expected = &[Spanned { - item: Stmt::Print { - expr: Spanned { - item: Expr::Index { - expr: Box::new(Spanned { - item: Expr::Cart(vec![( - Spanned { - item: Expr::Literal(Literal::Str("able".to_owned())), - span: 1..7, - }, - Spanned { - item: Expr::Literal(Literal::Str("ablecorp".to_owned())), - span: 11..21, - }, - )]), - span: 0..22, - }), - index: Box::new(Spanned { - item: Expr::Literal(Literal::Str("ablecorp".to_owned())), - span: 23..33, - }), - }, - span: 0..34, - }, - newline: true, - }, - span: 0..41, - }]; - - let ast = Parser::new(code).parse().unwrap(); - assert_eq!(ast, expected); - } -} diff --git a/ablescript/src/rickroll b/ablescript/src/rickroll deleted file mode 100644 index 3c6c6d21..00000000 --- a/ablescript/src/rickroll +++ /dev/null @@ -1,67 +0,0 @@ -We're no strangers to love -You know the rules and so do I -A full commitments what I'm thinking of -You wouldn't get this from another guy - -I just wanna tell you how I'm feeling -Gotta make you understand - -Never gonna give you up -Never gonna let you down -Never gonna run around and desert you -Never gonna make you cry -Never gonna say goodbye -Never gonna tell a lie and hurt you - -We've known each other for so long -Your heart's been aching but you're too shy to say it -Inside we both know what's been going on -We know the game and we're gonna play it - -And if you ask me how I'm feeling -Don't tell me you're too blind to see - -Never gonna give you up -Never gonna let you down -Never gonna run around and desert you -Never gonna make you cry -Never gonna say goodbye -Never gonna tell a lie and hurt you - -Never gonna give you up -Never gonna let you down -Never gonna run around and desert you -Never gonna make you cry -Never gonna say goodbye -Never gonna tell a lie and hurt you - -Never gonna give, never gonna give -(Give you up) - -We've known each other for so long -Your heart's been aching but you're too shy to say it -Inside we both know what's been going on -We know the game and we're gonna play it - -I just wanna tell you how I'm feeling -Gotta make you understand - -Never gonna give you up -Never gonna let you down -Never gonna run around and desert you -Never gonna make you cry -Never gonna say goodbye -Never gonna tell a lie and hurt you - -Never gonna give you up -Never gonna let you down -Never gonna run around and desert you -Never gonna make you cry -Never gonna say goodbye -Never gonna tell a lie and hurt you - -Never gonna give you up -Never gonna let you down -Never gonna run around and desert you -Never gonna make you cry -Never gonna say goodbye diff --git a/ablescript/src/value/coercions.rs b/ablescript/src/value/coercions.rs deleted file mode 100644 index a4f1c578..00000000 --- a/ablescript/src/value/coercions.rs +++ /dev/null @@ -1,279 +0,0 @@ -use super::{functio::FunctioChainKind, Abool, Cart, Functio, Value, ValueRef}; -use crate::{brian::INSTRUCTION_MAPPINGS, consts}; -use std::collections::HashMap; - -impl Value { - /// Coerce a value to an integer. - pub fn into_isize(self) -> isize { - match self { - Value::Nul => consts::ANSWER, - Value::Undefined => rand::random(), - Value::Str(text) => text - .parse() - .unwrap_or_else(|_| text.chars().map(|cr| cr as isize).sum()), - Value::Int(i) => i, - Value::Abool(a) => a as _, - Value::Functio(f) => match f { - Functio::Bf { - instructions, - tape_len, - } => { - instructions.into_iter().map(|x| x as isize).sum::() * tape_len as isize - } - Functio::Able { params, body } => { - params - .into_iter() - .map(|x| x.bytes().map(|x| x as isize).sum::()) - .sum::() - + body.len() as isize - } - Functio::Builtin(b) => (b.fn_addr() + b.arity) as _, - Functio::Chain { functios, kind } => { - let (lf, rf) = *functios; - Value::Functio(lf).into_isize() - + Value::Functio(rf).into_isize() - * match kind { - FunctioChainKind::Equal => -1, - FunctioChainKind::ByArity => 1, - } - } - Functio::Eval(code) => code.bytes().map(|x| x as isize).sum(), - }, - Value::Cart(c) => c - .into_iter() - .map(|(i, v)| i.into_isize() * v.borrow().clone().into_isize()) - .sum(), - } - } - - /// Coerce a value to an aboolean. - pub fn into_abool(self) -> Abool { - match self { - Value::Nul => Abool::Never, - Value::Undefined => Abool::Sometimes, - Value::Str(s) => match s.to_lowercase().as_str() { - "never" | "no" | "🇳🇴" => Abool::Never, - "sometimes" => Abool::Sometimes, - "always" | "yes" => Abool::Always, - s => (!s.is_empty()).into(), - }, - Value::Int(x) => match x.cmp(&0) { - std::cmp::Ordering::Less => Abool::Never, - std::cmp::Ordering::Equal => Abool::Sometimes, - std::cmp::Ordering::Greater => Abool::Always, - }, - Value::Abool(a) => a, - Value::Functio(f) => match f { - Functio::Bf { - instructions, - tape_len, - } => Value::Int( - (instructions.iter().map(|x| *x as usize).sum::() * tape_len) as _, - ) - .into_abool(), - Functio::Able { params, body } => { - let str_to_isize = - |x: String| -> isize { x.as_bytes().iter().map(|x| *x as isize).sum() }; - - let params: isize = params.into_iter().map(str_to_isize).sum(); - let body: isize = body - .into_iter() - .map(|x| format!("{:?}", x)) - .map(str_to_isize) - .sum(); - - Value::Int((params + body) % 3 - 1).into_abool() - } - Functio::Builtin(b) => (b.fn_addr() % b.arity == 0).into(), - Functio::Chain { functios, kind } => { - let (lhs, rhs) = *functios; - match kind { - FunctioChainKind::Equal => { - Value::Abool(Value::Functio(lhs).into_abool()) - + Value::Abool(Value::Functio(rhs).into_abool()) - } - FunctioChainKind::ByArity => { - Value::Abool(Value::Functio(lhs).into_abool()) - * Value::Abool(Value::Functio(rhs).into_abool()) - } - } - .into_abool() - } - Functio::Eval(code) => Value::Str(code).into_abool(), - }, - Value::Cart(c) => { - if c.is_empty() { - Abool::Never - } else { - Abool::Always - } - } - } - } - - /// Coerce a value to a functio. - pub fn into_functio(self) -> Functio { - match self { - Value::Nul | Value::Undefined => Functio::Able { - body: vec![], - params: vec![], - }, - Value::Str(s) => Functio::Eval(s), - Value::Int(i) => Functio::Bf { - instructions: { - std::iter::successors(Some(i as usize), |i| { - Some(i / INSTRUCTION_MAPPINGS.len()) - }) - .take_while(|&i| i != 0) - .map(|i| INSTRUCTION_MAPPINGS[i % INSTRUCTION_MAPPINGS.len()]) - .collect() - }, - tape_len: crate::brian::DEFAULT_TAPE_SIZE_LIMIT, - }, - Value::Abool(a) => Functio::Eval(match a { - Abool::Never => "".to_owned(), - Abool::Sometimes => { - use rand::seq::SliceRandom; - let mut str_chars: Vec<_> = "Buy Able Products!".chars().collect(); - str_chars.shuffle(&mut rand::thread_rng()); - - format!(r#""{}"print;"#, str_chars.iter().collect::()) - } - Abool::Always => r#"loop{"Buy Able products!"print;}"#.to_owned(), - }), - Value::Functio(f) => f, - Value::Cart(c) => { - let kind = if let Some(114514) = c - .get(&Value::Str("1452251871514141792252515212116".to_owned())) - .map(|x| x.borrow().to_owned().into_isize()) - { - FunctioChainKind::Equal - } else { - FunctioChainKind::ByArity - }; - - let mut cart_vec = c.iter().collect::>(); - cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less)); - - cart_vec - .into_iter() - .map(|(_, x)| x.borrow().to_owned().into_functio()) - .reduce(|acc, x| Functio::Chain { - functios: Box::new((acc, x)), - kind, - }) - .unwrap_or_else(|| Functio::Eval(r#""Buy Able Products!"print;"#.to_owned())) - } - } - } - - /// Coerce a value into a cart. - pub fn into_cart(self) -> Cart { - match self { - Value::Nul => HashMap::new(), - Value::Undefined => [(Value::Undefined, ValueRef::new(Value::Undefined))] - .into_iter() - .collect(), - Value::Str(s) => s - .chars() - .enumerate() - .map(|(i, x)| { - ( - Value::Int(i as isize + 1), - ValueRef::new(Value::Str(x.to_string())), - ) - }) - .collect(), - Value::Int(i) => Value::Str(i.to_string()).into_cart(), - Value::Abool(a) => Value::Str(a.to_string()).into_cart(), - Value::Functio(f) => match f { - Functio::Able { params, body } => { - let params: Cart = params - .into_iter() - .enumerate() - .map(|(i, x)| (Value::Int(i as isize + 1), ValueRef::new(Value::Str(x)))) - .collect(); - - let body: Cart = body - .into_iter() - .enumerate() - .map(|(i, x)| { - ( - Value::Int(i as isize + 1), - ValueRef::new(Value::Str(format!("{:?}", x))), - ) - }) - .collect(); - - let mut cart = HashMap::new(); - cart.insert( - Value::Str("params".to_owned()), - ValueRef::new(Value::Cart(params)), - ); - - cart.insert( - Value::Str("body".to_owned()), - ValueRef::new(Value::Cart(body)), - ); - - cart - } - Functio::Bf { - instructions, - tape_len, - } => { - let mut cart: Cart = instructions - .into_iter() - .enumerate() - .map(|(i, x)| { - ( - Value::Int(i as isize + 1), - ValueRef::new( - char::from_u32(x as u32) - .map(|x| Value::Str(x.to_string())) - .unwrap_or(Value::Nul), - ), - ) - }) - .collect(); - - cart.insert( - Value::Str("tapelen".to_owned()), - ValueRef::new(Value::Int(tape_len as _)), - ); - cart - } - Functio::Builtin(b) => { - let mut cart = HashMap::new(); - cart.insert( - Value::Str("addr".to_owned()), - ValueRef::new(Value::Cart(Value::Int(b.fn_addr() as _).into_cart())), - ); - - cart.insert( - Value::Str("arity".to_owned()), - ValueRef::new(Value::Int(b.arity as _)), - ); - - cart - } - Functio::Chain { functios, kind } => { - let (lhs, rhs) = *functios; - match kind { - FunctioChainKind::Equal => { - Value::Cart(Value::Functio(lhs).into_cart()) - + Value::Cart(Value::Functio(rhs).into_cart()) - } - FunctioChainKind::ByArity => { - Value::Cart(Value::Functio(lhs).into_cart()) - * Value::Cart(Value::Functio(rhs).into_cart()) - } - } - .into_cart() - } - Functio::Eval(s) => Value::Str(s).into_cart(), - }, - Value::Cart(c) => c, - } - } -} diff --git a/ablescript/src/value/functio.rs b/ablescript/src/value/functio.rs deleted file mode 100644 index 950c559f..00000000 --- a/ablescript/src/value/functio.rs +++ /dev/null @@ -1,113 +0,0 @@ -use super::ValueRef; -use crate::ast::Block; -use std::{hash::Hash, rc::Rc}; - -type BuiltinRc = Rc Result<(), crate::error::ErrorKind>>; - -/// AbleScript Function -#[derive(Debug, PartialEq, Clone, Hash)] -pub enum Functio { - /// BF instructions and a length of the type - /// - /// Takes input bytes as parameters - Bf { - instructions: Vec, - tape_len: usize, - }, - - /// Regular AbleScript functio - /// - /// Consists of parameter name mapping and AST - Able { params: Vec, body: Block }, - - /// Builtin Rust functio - Builtin(BuiltinFunctio), - - /// Chained functio pair - Chain { - functios: Box<(Functio, Functio)>, - kind: FunctioChainKind, - }, - - /// Code to be parsed and then executed in current scope - Eval(String), -} - -impl Functio { - pub fn arity(&self) -> usize { - match self { - Functio::Bf { - instructions: _, - tape_len: _, - } => 0, - Functio::Able { params, body: _ } => params.len(), - Functio::Builtin(b) => b.arity, - Functio::Chain { functios, kind: _ } => functios.0.arity() + functios.1.arity(), - Functio::Eval(_) => 0, - } - } -} - -/// Built-in Rust functio -#[derive(Clone)] -pub struct BuiltinFunctio { - pub(super) function: BuiltinRc, - pub(super) arity: usize, -} - -impl BuiltinFunctio { - /// Wrap a Rust function into AbleScript's built-in functio - /// - /// Arity used for functio chaining, recommend value for variadic - /// functions is the accepted minimum. - pub fn new(f: F, arity: usize) -> Self - where - F: Fn(&[ValueRef]) -> Result<(), crate::error::ErrorKind> + 'static, - { - Self { - function: Rc::new(f), - arity, - } - } - - pub fn call(&self, args: &[ValueRef]) -> Result<(), crate::error::ErrorKind> { - (self.function)(args) - } - - pub fn fn_addr(&self) -> usize { - Rc::as_ptr(&self.function) as *const () as _ - } -} - -impl std::fmt::Debug for BuiltinFunctio { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("BuiltinFunctio") - .field("function", &"built-in") - .field("arity", &self.arity) - .finish() - } -} - -impl PartialEq for BuiltinFunctio { - fn eq(&self, other: &Self) -> bool { - self.fn_addr() == other.fn_addr() && self.arity == other.arity - } -} - -impl Hash for BuiltinFunctio { - fn hash(&self, state: &mut H) { - self.fn_addr().hash(state); - self.arity.hash(state); - } -} - -/// A method of distributting parameters across functio chain -#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] -pub enum FunctioChainKind { - /// All parameters are equally distributed - Equal, - - /// Parameters are distributed to members of chain - /// by their arity - ByArity, -} diff --git a/ablescript/src/value/mod.rs b/ablescript/src/value/mod.rs deleted file mode 100644 index 2fc89984..00000000 --- a/ablescript/src/value/mod.rs +++ /dev/null @@ -1,202 +0,0 @@ -pub mod functio; - -mod coercions; -mod ops; - -pub use functio::Functio; - -use std::{ - cell::{Ref, RefCell, RefMut}, - collections::HashMap, - fmt::Display, - hash::Hash, - io::Write, - mem::discriminant, - rc::Rc, -}; - -pub type Cart = HashMap; - -/// AbleScript Value -#[derive(Debug, Default, Clone)] -pub enum Value { - #[default] - Nul, - Undefined, - Str(String), - Int(isize), - Abool(Abool), - Functio(Functio), - Cart(Cart), -} - -impl Hash for Value { - fn hash(&self, state: &mut H) { - discriminant(self).hash(state); - match self { - Value::Nul | Value::Undefined => (), - Value::Str(v) => v.hash(state), - Value::Int(v) => v.hash(state), - Value::Abool(v) => v.to_string().hash(state), - Value::Functio(statements) => statements.hash(state), - Value::Cart(_) => self.to_string().hash(state), - } - } -} - -impl Value { - /// Write an AbleScript value to a Brainfuck input stream by - /// coercing the value to an integer, then truncating that integer - /// to a single byte, then writing that byte. This should only be - /// called on `Write`rs that cannot fail, e.g., `Vec`, because - /// any IO errors will cause a panic. - pub fn bf_write(&self, stream: &mut impl Write) { - stream - .write_all(&[self.clone().into_isize() as u8]) - .expect("Failed to write to Brainfuck input"); - } -} - -/// Three-state logic value -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)] -pub enum Abool { - Never = -1, - Sometimes = 0, - Always = 1, -} - -impl Abool { - pub fn to_bool(&self) -> bool { - match self { - Self::Always => true, - Self::Sometimes if rand::random() => true, - _ => false, - } - } -} - -impl Display for Abool { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Abool::Never => write!(f, "never"), - Abool::Sometimes => write!(f, "sometimes"), - Abool::Always => write!(f, "always"), - } - } -} - -impl From for Abool { - fn from(b: bool) -> Self { - if b { - Abool::Always - } else { - Abool::Never - } - } -} - -impl Display for Value { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Value::Nul => write!(f, "nul"), - Value::Undefined => write!(f, "undefined"), - Value::Str(v) => write!(f, "{}", v), - Value::Int(v) => write!(f, "{}", v), - Value::Abool(v) => write!(f, "{}", v), - Value::Functio(v) => match v { - Functio::Bf { - instructions, - tape_len, - } => { - write!( - f, - "({}) {}", - tape_len, - String::from_utf8(instructions.to_owned()) - .expect("Brainfuck functio source should be UTF-8") - ) - } - Functio::Able { params, body } => { - write!( - f, - "({}) -> {:?}", - params.join(", "), - // Maybe we should have a pretty-printer for - // statement blocks at some point? - body, - ) - } - Functio::Builtin(b) => write!(f, "builtin @ {}", b.fn_addr()), - Functio::Chain { functios, kind } => { - let (a, b) = *functios.clone(); - write!( - f, - "{} {} {} ", - Value::Functio(a), - match kind { - functio::FunctioChainKind::Equal => '+', - functio::FunctioChainKind::ByArity => '*', - }, - Value::Functio(b) - ) - } - Functio::Eval(s) => write!(f, "{}", s), - }, - Value::Cart(c) => { - write!(f, "[")?; - let mut cart_vec = c.iter().collect::>(); - cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less)); - - for (idx, (key, value)) in cart_vec.into_iter().enumerate() { - write!(f, "{}", if idx != 0 { ", " } else { "" },)?; - match &*value.borrow() { - x if std::ptr::eq(x, self) => write!(f, ""), - x => write!(f, "{x}"), - }?; - write!(f, " <= {key}")?; - } - - write!(f, "]") - } - } - } -} - -/// Runtime borrow-checked, counted reference to a [Value] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ValueRef(Rc>); - -impl ValueRef { - pub fn new(v: Value) -> Self { - Self(Rc::new(RefCell::new(v))) - } - - pub fn borrow(&self) -> Ref { - self.0.borrow() - } - - pub fn borrow_mut(&self) -> RefMut { - self.0.borrow_mut() - } - - pub fn replace(&self, v: Value) -> Value { - self.0.replace(v) - } -} - -/// AbleScript variable either holding a reference -/// or being banned -#[derive(Debug)] -pub enum Variable { - /// Reference to a value - Ref(ValueRef), - - /// Banned variable - Melo, -} - -impl Variable { - pub fn from_value(value: Value) -> Self { - Self::Ref(ValueRef::new(value)) - } -} diff --git a/ablescript/src/value/ops.rs b/ablescript/src/value/ops.rs deleted file mode 100644 index 29426f1d..00000000 --- a/ablescript/src/value/ops.rs +++ /dev/null @@ -1,455 +0,0 @@ -use super::{ - functio::{BuiltinFunctio, FunctioChainKind}, - Abool, Functio, Value, ValueRef, -}; -use crate::consts; -use rand::Rng; -use std::{ - collections::HashMap, - ops::{Add, Div, Mul, Not, Sub}, -}; - -impl Add for Value { - type Output = Value; - - fn add(self, rhs: Self) -> Self::Output { - match self { - Value::Nul | Value::Undefined => match rhs { - Value::Nul => Value::Nul, - Value::Undefined => Value::Undefined, - Value::Str(_) => Value::Str(self.to_string()) + rhs, - Value::Int(_) => Value::Int(self.into_isize()) + rhs, - Value::Abool(_) => Value::Abool(self.into_abool()) + rhs, - Value::Functio(_) => Value::Functio(self.into_functio()) + rhs, - Value::Cart(_) => Value::Cart(self.into_cart()) + rhs, - }, - Value::Str(s) => Value::Str(format!("{s}{rhs}")), - Value::Int(i) => Value::Int(i.wrapping_add(rhs.into_isize())), - Value::Abool(_) => { - Value::Abool(Value::Int(self.into_isize().max(rhs.into_isize())).into_abool()) - } - Value::Functio(f) => Value::Functio(Functio::Chain { - functios: Box::new((f, rhs.into_functio())), - kind: FunctioChainKind::Equal, - }), - Value::Cart(c) => { - Value::Cart(c.into_iter().chain(rhs.into_cart().into_iter()).collect()) - } - } - } -} - -impl Sub for Value { - type Output = Value; - - fn sub(self, rhs: Self) -> Self::Output { - match self { - Value::Nul | Value::Undefined => match rhs { - Value::Nul => Value::Nul, - Value::Undefined => Value::Undefined, - Value::Str(_) => Value::Str(self.to_string()) - rhs, - Value::Int(_) => Value::Int(self.into_isize()) - rhs, - Value::Abool(_) => Value::Abool(self.into_abool()) - rhs, - Value::Functio(_) => Value::Functio(self.into_functio()) - rhs, - Value::Cart(_) => Value::Cart(self.into_cart()) - rhs, - }, - Value::Str(s) => Value::Str(s.replace(&rhs.to_string(), "")), - Value::Int(i) => Value::Int(i.wrapping_sub(rhs.into_isize())), - Value::Abool(_) => (self.clone() + rhs.clone()) * !(self * rhs), - Value::Functio(f) => Value::Functio(match f { - Functio::Bf { - instructions: lhs_ins, - tape_len: lhs_tl, - } => match rhs.into_functio() { - Functio::Bf { - instructions: rhs_ins, - tape_len: rhs_tl, - } => Functio::Bf { - instructions: lhs_ins - .into_iter() - .zip(rhs_ins.into_iter()) - .filter_map(|(l, r)| if l != r { Some(l) } else { None }) - .collect(), - tape_len: lhs_tl - rhs_tl, - }, - rhs => Functio::Bf { - instructions: lhs_ins - .into_iter() - .zip(Value::Functio(rhs).to_string().bytes()) - .filter_map(|(l, r)| if l != r { Some(l) } else { None }) - .collect(), - tape_len: lhs_tl, - }, - }, - Functio::Able { - params: lhs_params, - body: lhs_body, - } => match rhs.into_functio() { - Functio::Able { - params: rhs_params, - body: rhs_body, - } => Functio::Able { - params: lhs_params - .into_iter() - .zip(rhs_params.into_iter()) - .filter_map(|(l, r)| if l != r { Some(l) } else { None }) - .collect(), - body: lhs_body - .into_iter() - .zip(rhs_body.into_iter()) - .filter_map(|(l, r)| if l != r { Some(l) } else { None }) - .collect(), - }, - rhs => Value::Int( - Value::Functio(Functio::Able { - params: lhs_params, - body: lhs_body, - }) - .into_isize() - - Value::Functio(rhs).into_isize(), - ) - .into_functio(), - }, - Functio::Builtin(b) => { - let arity = b.arity; - let resulting_arity = arity.saturating_sub(rhs.into_isize() as usize); - - Functio::Builtin(BuiltinFunctio::new( - move |args| { - b.call( - &args - .iter() - .take(resulting_arity) - .cloned() - .chain(std::iter::repeat_with(|| ValueRef::new(Value::Nul))) - .take(arity) - .collect::>(), - ) - }, - resulting_arity, - )) - } - Functio::Chain { functios, .. } => { - let rhs = rhs.into_functio(); - let (a, b) = *functios; - - match (a == rhs, b == rhs) { - (_, true) => a, - (true, _) => b, - (_, _) => (Value::Functio(a) - Value::Functio(rhs)).into_functio(), - } - } - Functio::Eval(lhs_code) => Functio::Eval(lhs_code.replace( - &match rhs.into_functio() { - Functio::Eval(code) => code, - rhs => Value::Functio(rhs).to_string(), - }, - "", - )), - }), - Value::Cart(c) => Value::Cart({ - let rhs_cart = rhs.into_cart(); - c.into_iter() - .filter(|(k, v)| rhs_cart.get(k) != Some(v)) - .collect() - }), - } - } -} - -impl Mul for Value { - type Output = Value; - - fn mul(self, rhs: Self) -> Self::Output { - match self { - Value::Nul | Value::Undefined => match rhs { - Value::Nul => Value::Nul, - Value::Undefined => Value::Undefined, - Value::Str(_) => Value::Str(self.to_string()) * rhs, - Value::Int(_) => Value::Int(self.into_isize()) * rhs, - Value::Abool(_) => Value::Abool(self.into_abool()) * rhs, - Value::Functio(_) => Value::Functio(self.into_functio()) * rhs, - Value::Cart(_) => Value::Cart(self.into_cart()) * rhs, - }, - Value::Str(s) => Value::Str(s.repeat(rhs.into_isize() as usize)), - Value::Int(i) => Value::Int(i.wrapping_mul(rhs.into_isize())), - Value::Abool(_) => { - Value::Abool(Value::Int(self.into_isize().min(rhs.into_isize())).into_abool()) - } - Value::Functio(f) => Value::Functio(Functio::Chain { - functios: Box::new((f, rhs.into_functio())), - kind: FunctioChainKind::ByArity, - }), - Value::Cart(c) => { - let rhsc = rhs.into_cart(); - - Value::Cart( - c.into_iter() - .map(|(k, v)| { - if let Some(k) = rhsc.get(&k) { - (k.borrow().clone(), v) - } else { - (k, v) - } - }) - .collect(), - ) - } - } - } -} - -impl Div for Value { - type Output = Value; - - fn div(self, rhs: Self) -> Self::Output { - match self { - Value::Nul | Value::Undefined => match rhs { - Value::Nul => Value::Nul, - Value::Undefined => Value::Undefined, - Value::Str(_) => Value::Str(self.to_string()) / rhs, - Value::Int(_) => Value::Int(self.into_isize()) / rhs, - Value::Abool(_) => Value::Abool(self.into_abool()) / rhs, - Value::Functio(_) => Value::Functio(self.into_functio()) / rhs, - Value::Cart(_) => Value::Cart(self.into_cart()) / rhs, - }, - Value::Str(s) => Value::Cart( - s.split(&rhs.to_string()) - .enumerate() - .map(|(i, x)| { - ( - Value::Int(i as isize + 1), - ValueRef::new(Value::Str(x.to_owned())), - ) - }) - .collect(), - ), - Value::Int(i) => Value::Int(i.wrapping_div(match rhs.into_isize() { - 0 => consts::ANSWER, - x => x, - })), - Value::Abool(_) => !self + rhs, - Value::Functio(f) => Value::Functio(match f { - Functio::Bf { - instructions, - tape_len, - } => { - let fraction = 1.0 / rhs.into_isize() as f64; - let len = instructions.len(); - Functio::Bf { - instructions: instructions - .into_iter() - .take((len as f64 * fraction) as usize) - .collect(), - tape_len, - } - } - Functio::Able { params, body } => { - let fraction = 1.0 / rhs.into_isize() as f64; - let len = body.len(); - Functio::Able { - params, - body: body - .into_iter() - .take((len as f64 * fraction) as usize) - .collect(), - } - } - Functio::Builtin(b) => Functio::Builtin(BuiltinFunctio { - arity: b.arity + rhs.into_isize() as usize, - ..b - }), - Functio::Chain { functios, kind } => { - let functios = *functios; - Functio::Chain { - functios: Box::new(( - (Value::Functio(functios.0) / rhs.clone()).into_functio(), - (Value::Functio(functios.1) / rhs).into_functio(), - )), - kind, - } - } - Functio::Eval(s) => { - let fraction = 1.0 / rhs.into_isize() as f64; - let len = s.len(); - Functio::Eval(s.chars().take((len as f64 * fraction) as usize).collect()) - } - }), - Value::Cart(c) => { - let cart_len = match c.len() { - 0 => return Value::Cart(HashMap::new()), - l => l, - }; - - let chunk_len = match rhs.into_isize() as usize { - 0 => rand::thread_rng().gen_range(1..=cart_len), - l => l, - }; - - Value::Cart( - c.into_iter() - .collect::>() - .chunks(cart_len / chunk_len + (cart_len % chunk_len != 0) as usize) - .enumerate() - .map(|(k, v)| { - ( - Value::Int(k as isize + 1), - ValueRef::new(Value::Cart(v.iter().cloned().collect())), - ) - }) - .collect(), - ) - } - } - } -} - -impl Not for Value { - type Output = Value; - - fn not(self) -> Self::Output { - match self { - Value::Nul => Value::Undefined, - Value::Undefined => Value::Nul, - Value::Str(s) => Value::Str(s.chars().rev().collect()), - Value::Int(i) => Value::Int(i.swap_bytes()), - Value::Abool(a) => Value::Abool(match a { - Abool::Never => Abool::Always, - Abool::Sometimes => Abool::Sometimes, - Abool::Always => Abool::Never, - }), - Value::Functio(f) => Value::Functio(match f { - Functio::Bf { - mut instructions, - tape_len, - } => { - instructions.reverse(); - - Functio::Bf { - instructions, - tape_len, - } - } - Functio::Able { - mut params, - mut body, - } => { - params.reverse(); - body.reverse(); - - Functio::Able { params, body } - } - Functio::Builtin(b) => { - let arity = b.arity; - Functio::Builtin(BuiltinFunctio::new( - move |args| b.call(&args.iter().cloned().rev().collect::>()), - arity, - )) - } - Functio::Chain { functios, kind } => { - let (a, b) = *functios; - Functio::Chain { - functios: Box::new(( - (!Value::Functio(b)).into_functio(), - (!Value::Functio(a)).into_functio(), - )), - kind, - } - } - Functio::Eval(code) => Functio::Eval(code.chars().rev().collect()), - }), - Value::Cart(c) => Value::Cart( - c.into_iter() - .map(|(k, v)| (v.borrow().clone(), ValueRef::new(k))) - .collect(), - ), - } - } -} - -impl PartialEq for Value { - fn eq(&self, other: &Self) -> bool { - let other = other.clone(); - - match self { - Value::Nul => matches!(other, Value::Nul), - Value::Undefined => matches!(other, Value::Undefined), - Value::Str(s) => *s == other.to_string(), - Value::Int(i) => *i == other.into_isize(), - Value::Abool(a) => *a == other.into_abool(), - Value::Functio(f) => *f == other.into_functio(), - Value::Cart(c) => *c == other.into_cart(), - } - } -} - -impl Eq for Value {} - -impl PartialOrd for Value { - fn partial_cmp(&self, other: &Self) -> Option { - use std::cmp::Ordering::*; - let other = other.clone(); - - match self { - Value::Nul if other == Value::Nul => Some(Equal), - Value::Undefined if other == Value::Undefined => Some(Equal), - Value::Str(s) => Some(s.cmp(&other.to_string())), - Value::Int(i) => Some(i.cmp(&other.into_isize())), - Value::Abool(a) => a.partial_cmp(&other.into_abool()), - Value::Functio(_) => self.clone().into_isize().partial_cmp(&other.into_isize()), - Value::Cart(c) => Some(c.len().cmp(&other.into_cart().len())), - Value::Nul | Value::Undefined => None, - } - } -} - -impl Value { - /// Get a length of a value - pub fn length(&self) -> isize { - match self { - Value::Nul => 0, - Value::Undefined => -1, - Value::Str(s) => s.len() as _, - Value::Int(i) => i.count_zeros() as _, - Value::Abool(a) => match a { - Abool::Never => -3, - Abool::Sometimes => { - if rand::thread_rng().gen() { - 3 - } else { - -3 - } - } - Abool::Always => 3, - }, - Value::Functio(f) => match f { - // Compares lengths of functions: - // BfFunctio - Sum of lengths of instructions and length of tape - // AbleFunctio - Sum of argument count and body length - // Eval - Length of input code - Functio::Bf { - instructions, - tape_len, - } => (instructions.len() + tape_len) as _, - Functio::Able { params, body } => (params.len() + format!("{:?}", body).len()) as _, - Functio::Builtin(b) => (std::mem::size_of_val(b.function.as_ref()) + b.arity) as _, - Functio::Chain { functios, kind } => { - let (lhs, rhs) = *functios.clone(); - match kind { - FunctioChainKind::Equal => { - Value::Int(Value::Functio(lhs).into_isize()) - + Value::Int(Value::Functio(rhs).into_isize()) - } - FunctioChainKind::ByArity => { - Value::Int(Value::Functio(lhs).into_isize()) - * Value::Int(Value::Functio(rhs).into_isize()) - } - } - .into_isize() - } - Functio::Eval(s) => s.len() as _, - }, - Value::Cart(c) => c.len() as _, - } - } -} diff --git a/ablescript_cli/Cargo.toml b/ablescript_cli/Cargo.toml deleted file mode 100644 index ba860df9..00000000 --- a/ablescript_cli/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "ablescript_cli" -version = "0.5.4" -authors = ["AbleScript Developers"] -edition = "2021" - -description = "The best programming language" -license = "MIT" -documentation = "https://gblecorp.github.io/able-script-the-book" -repository = "https://git.ablecorp.us/AbleScript/able-script" - -[dependencies] -ablescript = { version = "0.5.3", path = "../ablescript" } -clap = { version = "4.2", features = ["derive"] } -rustyline = "11.0" diff --git a/ablescript_cli/src/main.rs b/ablescript_cli/src/main.rs deleted file mode 100644 index ead6e57c..00000000 --- a/ablescript_cli/src/main.rs +++ /dev/null @@ -1,55 +0,0 @@ -#![forbid(unsafe_code)] - -mod repl; - -use ablescript::{interpret::ExecEnv, parser::parse}; -use clap::Parser; -use std::{path::PathBuf, process::exit}; - -fn main() { - // variables::test(); // NOTE(Able): Add this as a test case - let args = Args::parse(); - match args.file { - Some(file_path) => { - // Read file - let source = match std::fs::read_to_string(&file_path) { - Ok(s) => s, - Err(e) => { - println!("Failed to read file \"{:?}\": {}", file_path, e); - exit(1) - } - }; - - // Parse & evaluate - if let Err(e) = parse(&source).and_then(|ast| { - if args.debug { - eprintln!("{:#?}", ast); - } - ExecEnv::::default().eval_stmts(&ast) - }) { - println!( - "Error `{:?}` occurred at span: {:?} = `{:?}`", - e.kind, - e.span.clone(), - &source[e.span] - ); - } - } - None => { - println!("Hi [AbleScript {}]", env!("CARGO_PKG_VERSION")); - repl::repl(args.debug); - } - } -} - -#[derive(Parser, Debug)] -#[command(name = "AbleScript", version, about)] -struct Args { - /// File to execute - #[arg(short, long)] - file: Option, - - /// Dump AST to console - #[arg(short, long)] - debug: bool, -} diff --git a/ablescript_cli/src/repl.rs b/ablescript_cli/src/repl.rs deleted file mode 100644 index 6d8504b8..00000000 --- a/ablescript_cli/src/repl.rs +++ /dev/null @@ -1,66 +0,0 @@ -use ablescript::{interpret::ExecEnv, parser::parse}; -use rustyline::DefaultEditor; - -pub fn repl(ast_print: bool) { - let mut rl = match DefaultEditor::new() { - Ok(rl) => rl, - Err(e) => { - eprintln!("Failed to create editor: {e}"); - std::process::exit(-1); - } - }; - - let mut env = ExecEnv::::default(); - - // If this is `Some`, the user has previously entered an - // incomplete statement and is now completing it; otherwise, the - // user is entering a completely new statement. - let mut partial: Option = None; - loop { - match rl.readline(if partial.is_some() { ">> " } else { ":: " }) { - Ok(readline) => { - let readline = readline.trim_end(); - let _ = rl.add_history_entry(readline); - - let partial_data = match partial { - Some(line) => line + readline, - None => readline.to_owned(), - }; - - partial = match parse(&partial_data).and_then(|ast| { - if ast_print { - eprintln!("{:#?}", &ast); - } - env.eval_stmts(&ast) - }) { - Ok(_) => None, - Err(ablescript::error::Error { - // Treat "Unexpected EOF" errors as "we need - // more data". - kind: ablescript::error::ErrorKind::UnexpectedEoi, - .. - }) => Some(partial_data), - Err(e) => { - println!("{}", e); - println!(" | {}", partial_data); - println!( - " {}{}", - " ".repeat(e.span.start), - "^".repeat((e.span.end - e.span.start).max(1)) - ); - None - } - }; - } - Err(rustyline::error::ReadlineError::Eof) => { - println!("bye"); - break; - } - Err(rustyline::error::ReadlineError::Interrupted) => (), - Err(e) => { - println!("Error: {:?}", e); - break; - } - } - } -} diff --git a/examples/by-arity-chain.able b/examples/by-arity-chain.able deleted file mode 100644 index 2647a630..00000000 --- a/examples/by-arity-chain.able +++ /dev/null @@ -1,44 +0,0 @@ -functio arity_0() { - /*this function has arity 0*/ print; -} - -functio arity_1(arg1) { - /*this function has arity 1*/ print; - arg1 print; -} - -functio arity_2(arg1, arg2) { - /*this function has arity 2*/ print; - arg1 print; - arg2 print; -} - -functio arity_3(arg1, arg2, arg3) { - /*this function has arity 3*/ print; - arg1 print; - arg2 print; - arg3 print; -} - -owo arity_0(); -owo arity_1(/*foo*/); -owo arity_2(/*foo*/, /*bar*/); -owo arity_3(/*foo*/, /*bar*/, /*baz*/); - -i1 dim arity_0 * arity_1; -i1(/*second*/); - -/*----*/ print; - -i2 dim arity_1 * arity_0; -i2(/*first*/); - -/*----*/ print; - -ifancy dim arity_3 * arity_3; -ifancy(/*left1*/, /*right1*/, /*left2*/, /*right2*/, /*left3*/, /*right3*/); - -/*---*/ print; - -another dim arity_0 * arity_3; -another(/*right1*/, /*right2*/, /*right3*/); diff --git a/examples/carts.able b/examples/carts.able deleted file mode 100644 index 65e9505e..00000000 --- a/examples/carts.able +++ /dev/null @@ -1,8 +0,0 @@ -functio helloable() { - /*Hello, Able!*/ print; -} - -cart dim [/*able*/ <= 42, helloable <= /*hello*/]; - -cart[42] print; -cart[/*hello*/](); diff --git a/examples/functio.able b/examples/functio.able deleted file mode 100644 index 5f2635b6..00000000 --- a/examples/functio.able +++ /dev/null @@ -1,4 +0,0 @@ -functio hello(words){ - words print; -} -hello(/*wonk*/); diff --git a/examples/hello-world.able b/examples/hello-world.able deleted file mode 100644 index 247f8a43..00000000 --- a/examples/hello-world.able +++ /dev/null @@ -1,2 +0,0 @@ -hello dim /*world*/; -hello print; diff --git a/examples/iotest.able b/examples/iotest.able deleted file mode 100644 index c3da0b4c..00000000 --- a/examples/iotest.able +++ /dev/null @@ -1,5 +0,0 @@ -data dim; -loop { - data read; - data print; -} diff --git a/examples/melo-hello.able b/examples/melo-hello.able deleted file mode 100644 index 9224b6b0..00000000 --- a/examples/melo-hello.able +++ /dev/null @@ -1,3 +0,0 @@ -hi dim /*wonk*/; -melo hi; -hi print; owo Should error out diff --git a/examples/pass-by-reference.able b/examples/pass-by-reference.able deleted file mode 100644 index 0d331393..00000000 --- a/examples/pass-by-reference.able +++ /dev/null @@ -1,23 +0,0 @@ -owo Pass-by-reference test - -owo Swap two variables. -functio swap(left, right) { - tmp dim left; - right =: left; - tmp =: right; -} - -foo dim /*hello*/; -bar dim /*world*/; - -swap(foo, bar); - -unless (foo = /*world*/) { - /*FAILED*/ print; - /*foo should be 'world', is actually:*/ print; - foo print; -} - -unless (foo ain't /*world*/) { - /*OK*/ print; -}