diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..4768414 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[env] +CARGO_WORKSPACE_DIR = { value = "", relative = true } diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..8d2a520 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,20 @@ +name: Rust + +on: + push: + branches: [ "main", "dev" ] + pull_request: + branches: [ "main", "dev" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/Cargo.lock b/Cargo.lock index f0fc7a9..2ccbea0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,15 +17,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block-buffer" @@ -56,78 +56,22 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "clipboard-win" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" dependencies = [ "error-code", ] [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] -[[package]] -name = "crossbeam" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - [[package]] name = "crypto-common" version = "0.1.6" @@ -187,9 +131,9 @@ dependencies = [ [[package]] name = "error-code" -version = "3.2.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" [[package]] name = "fd-lock" @@ -234,15 +178,15 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libredox" @@ -272,9 +216,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" @@ -305,9 +249,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "option-ext" @@ -335,14 +279,14 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] name = "pest" -version = "2.7.10" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", "thiserror", @@ -351,9 +295,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.10" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" dependencies = [ "pest", "pest_generator", @@ -361,9 +305,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.10" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" dependencies = [ "pest", "pest_meta", @@ -374,9 +318,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.10" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" dependencies = [ "once_cell", "pest", @@ -385,18 +329,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -415,7 +359,6 @@ dependencies = [ name = "rat" version = "0.1.0" dependencies = [ - "crossbeam", "dirs", "pest", "pest_derive", @@ -433,18 +376,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", @@ -453,9 +396,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags", "errno", @@ -523,9 +466,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "syn" -version = "2.0.66" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -534,18 +477,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -560,27 +503,27 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "ustr" @@ -602,9 +545,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -627,7 +570,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -647,18 +590,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -669,9 +612,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -681,9 +624,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -693,15 +636,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -711,9 +654,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -723,9 +666,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -735,9 +678,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -747,24 +690,24 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", diff --git a/examples/closure.rat b/examples/closure.rat new file mode 100644 index 0000000..6bb2c0f --- /dev/null +++ b/examples/closure.rat @@ -0,0 +1,2 @@ +: make-adder = [$x add] bind ; +40 2 make-adder i say diff --git a/examples/fact.rat b/examples/fact.rat new file mode 100644 index 0000000..35f4592 --- /dev/null +++ b/examples/fact.rat @@ -0,0 +1,2 @@ +: fact = [dup zero?] [incr] [dup decr] [mul] linrec ; +5 fact say diff --git a/examples/fib.rat b/examples/fib.rat index 35271af..9dfa846 100644 --- a/examples/fib.rat +++ b/examples/fib.rat @@ -1,2 +1,2 @@ -: fib ← [3 <] [pop 1] [decr] [2 -] [+] binrec ; +: fib = [dup 3 lt?] [pop 1] [decr dup decr] [add] binrec ; 30 fib say diff --git a/examples/mergesort.rat b/examples/mergesort.rat new file mode 100644 index 0000000..dc96152 --- /dev/null +++ b/examples/mergesort.rat @@ -0,0 +1,4 @@ +: seq & rat/seq ; + +[-23714 36911 27209 -61081 4487 62986 43562 59953 -6002 -64976 6222 37230 1774 60450 35372 9313 -63141 -10099 -56599 37381 29627 17147 -56774 46348 25159 42717 -10426 -38088 -9482 -51818 -13225 -22274 -52566 -5587 -148 59711 51437 6343 50139 32437 27966 -34985 167 27161 -53411 -6759 -27066 -7772 49899 -24127 7940 61517 -14064 -4433 6018 42197 -24241 4939 23687 -27950 -60335 -483 -51116 -201 21847 -65149 46808 49486 -6132 4372 14587 60525 14700 8373 -48827 -64401 -53524 -28842 -10959 -56034 -64763 -54632 28581 -3277 5279 7684 -26031 32221 1723 59531 -34723 17671 3424 -30493 28938 -12043 -27853 -14788 -34395 -23175 39083 -3314 16725 -14123 5069 -59857 52477 -25028 9931 -27366 41663 10612 -46240 27308 7778 -12942 37028 -18502 59012 53713 60519 -49390 -15897 41667 55977 45961 39978 59934 23858 -23789 -17806 -14129 -36824 -57834 -28443 -22873 26639 -20046 -57731 -16443 -599 -2646 8985 -14817 -10395 -42440 -59983 3357 44965 -44840 36400 -10323 -39719 45791 -27538 15597 7872 -19558 54625 -10621 27469 -61915 -45654 -61333 10374 50181 5656 8559 -9822 -9340 23135 -11774 -28400 -44677 304 37783 -56801 -45383 59955 23509 55993 -61971 16262 -49607 39590 -27209 1339 -56543 -41882 64893 50150 -16153 -317 -16469 7629 19020 15578 46013 53541 -27485 60355 -28348 -63558 -45715 -43954 62904 -8753 29052 -56968 52829 31804 45589 -26416 30184 29918 -38968 19638 -24968 -24088 -9447 48799 -50660 43610 -50411 -47355 -59101 59279 34413 55334 -16776 50629 63673 -44714 -32217 -37943 -21810 -51476 33889 6583 65273 60878 -13805 59344 34343 -43042 25481 40781 -37723 -39856 -19967 -54323 -6219 -24811 -51108 4870 -20829] +seq/mergesort say diff --git a/examples/person.rat b/examples/person.rat new file mode 100644 index 0000000..f65f70a --- /dev/null +++ b/examples/person.rat @@ -0,0 +1,4 @@ +: ^new = [$first-name $last-name $age] bind ; +: ^first-name = 0 at ; +: ^last-name = 1 at ; +: ^age = 2 at ; diff --git a/examples/towards_zero.rat b/examples/towards_zero.rat new file mode 100644 index 0000000..8397684 --- /dev/null +++ b/examples/towards_zero.rat @@ -0,0 +1,13 @@ +: std & rat/std ; + +: small? = 1 lt? ; + +# with recursion +: f = std/over small? [pop] [[dup decr] dip dup i] if-else ; +10 [f] f + +# with combinators +: f = [dup small?] [] [dup decr] [] linrec ; +10 f + +show diff --git a/lib/math.rat b/lib/math.rat index f39c90b..7480c2d 100644 --- a/lib/math.rat +++ b/lib/math.rat @@ -2,5 +2,10 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -÷ abs ← negative? [neg] if ; -÷ square ← dup * ; +: std & rat/std ; + +: ^abs = dup negative? [neg] if ; +: ^halve = 1 shr ; +: ^double = 1 shl ; +: ^square = dup mul ; +: ^pow = [dup quote [mul] cat] dip std/times ; diff --git a/lib/seq.rat b/lib/seq.rat new file mode 100644 index 0000000..d3a786d --- /dev/null +++ b/lib/seq.rat @@ -0,0 +1,29 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +: std & rat/std ; +: math & rat/math ; + +: ^empty? = len zero? ; + +: ^merge = + [std/dup2 [empty?] unary2 or] + [dup empty? [pop] [std/popd] if-else] + [ + std/dup2 [first] unary2 lt? + [[[first quote] [suffix] std/bi] dip] + [[first quote] [suffix] std/bi std/swapd] + if-else + ] + [cat] + linrec +; + +: ^mergesort = + [dup len 1 le?] + [] + [dup len math/halve split] + [merge] + binrec +; diff --git a/lib/std.rat b/lib/std.rat index 88227f4..9ef9d11 100644 --- a/lib/std.rat +++ b/lib/std.rat @@ -2,11 +2,17 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -÷ popd ← [pop] dip ; -÷ dupd ← [dup] dip ; -÷ swapd ← [swap] dip ; +: ^popd = swap pop ; +: ^dupd = [dup] dip ; +: ^swapd = [swap] dip ; -÷ over ← dupd swap ; -÷ overd ← [over] dip ; +: ^over = dupd swap ; +: ^overd = [over] dip ; -÷ bi ← [[quote] dip over [cat] dip] dip cat [i] dip i ; +: ^dup2 = over over ; + +: ^bi = [[quote] dip over [cat] dip] dip cat [i] dip i ; + +: ^case = swap at i ; + +: ^times = [dup 1 lt?] [pop pop] [[dup [i] dip] dip decr] [] linrec ; diff --git a/rat-cli/Cargo.toml b/rat-cli/Cargo.toml index ecfbc04..bf4de66 100644 --- a/rat-cli/Cargo.toml +++ b/rat-cli/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["rat", "rust", "programming-language", "concatenative", "concurrency license = "MPL-2.0" name = "rat-cli" repository = "https://github.com/daddinuz/rat" -rust-version = "1.75" +rust-version = "1.80" version = "0.1.0" [build-dependencies] diff --git a/rat-cli/build.rs b/rat-cli/build.rs index 6445696..ec10bb7 100644 --- a/rat-cli/build.rs +++ b/rat-cli/build.rs @@ -2,20 +2,22 @@ use std::fs; use std::path::Path; fn main() { - let source_path = Path::new(env!("CARGO_MANIFEST_DIR")) + let crate_lib_path = Path::new(env!("CARGO_MANIFEST_DIR")) .parent() .unwrap() .join("lib"); - let destination_path = rat::home_dir().join("lib"); + println!("cargo::rerun-if-changed={}", crate_lib_path.display()); - fs::create_dir_all(&destination_path).unwrap(); + let host_lib_path = rat::home_dir().join("lib"); - for entry in fs::read_dir(source_path).unwrap() { + fs::create_dir_all(&host_lib_path).unwrap(); + + for entry in fs::read_dir(crate_lib_path).unwrap() { let entry = entry.unwrap(); if entry.file_type().unwrap().is_file() { - fs::copy(entry.path(), destination_path.join(entry.file_name())).unwrap(); + fs::copy(entry.path(), host_lib_path.join(entry.file_name())).unwrap(); } } } diff --git a/rat-cli/src/error.rs b/rat-cli/src/error.rs index 5c4c17c..9a8d625 100644 --- a/rat-cli/src/error.rs +++ b/rat-cli/src/error.rs @@ -10,7 +10,6 @@ use std::io::Error as IoError; use rustyline::error::ReadlineError; -use rat::effect::Effect; use rat::parser::ParseError; pub struct CliError(Box); @@ -27,12 +26,6 @@ impl From for CliError { } } -impl From for CliError { - fn from(effect: Effect) -> Self { - Self(format!("Unhandled effect: {effect:?}").into()) - } -} - impl From for CliError { fn from(error: FmtError) -> Self { Self(error.into()) diff --git a/rat-cli/src/main.rs b/rat-cli/src/main.rs index aa2bb34..a20c532 100644 --- a/rat-cli/src/main.rs +++ b/rat-cli/src/main.rs @@ -7,6 +7,7 @@ mod error; use std::env; +use std::fmt::Write; use std::fs::{File, OpenOptions}; use std::io::{ErrorKind, Read}; use std::path::{Path, PathBuf}; @@ -29,35 +30,17 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub fn main() -> Result<(), CliError> { let mut parser = Parser::with_prelude(); let mut evaluator = Evaluator::new(); - - let mut source = String::new(); + let mut source_paths = Vec::new(); + let mut source_buffer = String::new(); let mut interactive = false; let mut show_usage = false; let mut show_license = false; - let mut has_source_files = false; env::args() .skip(1) .try_for_each(|arg| -> Result<(), CliError> { match arg.as_str() { - path if !path.starts_with('-') => { - let mut file = File::open(path)?; - - source.clear(); - file.read_to_string(&mut source)?; - has_source_files |= !source.is_empty(); - - match interpret( - Origin::Path(Path::new(path)), - &source, - &mut parser, - &mut evaluator, - ) { - Err(error) if interactive => error.report(), - error @ Err(_) => return error, - _ => (), - } - } + _ if !arg.starts_with('-') => source_paths.push(arg), "-h" | "--help" => show_usage = true, "-i" | "--interactive" => interactive = true, "--license" => show_license = true, @@ -67,7 +50,25 @@ pub fn main() -> Result<(), CliError> { Ok(()) })?; - if !interactive && !has_source_files { + for path in source_paths.iter() { + let mut file = File::open(path)?; + + source_buffer.clear(); + file.read_to_string(&mut source_buffer)?; + + match interpret( + Origin::Path(Path::new(path)), + &source_buffer, + &mut parser, + &mut evaluator, + ) { + Err(error) if interactive => error.report(), + error @ Err(_) => return error, + _ => (), + } + } + + if !interactive && source_paths.is_empty() { if show_usage { println!("{USAGE}"); return Ok(()); @@ -79,8 +80,8 @@ pub fn main() -> Result<(), CliError> { } } - if interactive || !has_source_files { - repl(&mut parser, &mut evaluator, !has_source_files)?; + if interactive || source_paths.is_empty() { + repl(&mut parser, &mut evaluator, source_paths.is_empty())?; } Ok(()) @@ -115,7 +116,7 @@ fn repl( if show_greeting { println!( - "{}\nRat {} CLI {}\nEnter Ctrl+D to exit.\nThis software is licensed under MPL-2.0 terms, visit https://www.mozilla.org/MPL/2.0/ for more information.\n", + "{}{NEWLINE}Rat {} CLI {}{NEWLINE}Enter Ctrl+D to exit.{NEWLINE}This software is licensed under MPL-2.0 terms, visit https://www.mozilla.org/MPL/2.0/ for more information.{NEWLINE}", REPL_GREET, rat::VERSION, VERSION, @@ -160,7 +161,17 @@ fn interpret( evaluator: &mut Evaluator, ) -> Result<(), CliError> { let program = parser.parse(origin, source)?; - evaluator.evaluate(program)?; + + evaluator.evaluate(program.into_iter()).map_err(|effect| { + evaluator.stack.iter().fold( + format!("unhandled effect: {effect:?}{NEWLINE}stack (top rightmost):"), + |mut acc, exp| { + let _ = write!(acc, " {exp:?}"); + acc + }, + ) + })?; + Ok(()) } @@ -588,3 +599,9 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. "###; + +#[cfg(windows)] +const NEWLINE: &str = "\r\n"; + +#[cfg(not(windows))] +const NEWLINE: &str = "\n"; diff --git a/rat/Cargo.toml b/rat/Cargo.toml index b5ca3af..153c3e9 100644 --- a/rat/Cargo.toml +++ b/rat/Cargo.toml @@ -8,11 +8,10 @@ keywords = ["rat", "rust", "programming-language", "concatenative", "concurrency license = "MPL-2.0" name = "rat" repository = "https://github.com/daddinuz/rat" -rust-version = "1.75" +rust-version = "1.80" version = "0.1.0" [dependencies] -crossbeam = { version = "0.8", features = ["crossbeam-channel"] } dirs = "5" pest = "2" pest_derive = "2" diff --git a/rat/src/boolean.rs b/rat/src/boolean.rs index a7e318c..aa5f33d 100644 --- a/rat/src/boolean.rs +++ b/rat/src/boolean.rs @@ -8,7 +8,7 @@ use std::fmt::{Debug, Display}; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not}; use crate::codegen; -use crate::effect::Effect; +use crate::error::RuntimeError; use crate::evaluate::Evaluate; use crate::evaluator::Evaluator; use crate::expression::Expression; @@ -30,11 +30,11 @@ impl From for bool { } } -impl Evaluate<&mut Evaluator> for Boolean { - type Output = Result<(), Effect>; +impl Evaluate for &mut Evaluator { + type Output = Result<(), RuntimeError>; - fn evaluate(self, evaluator: &mut Evaluator) -> Self::Output { - evaluator.stack.push(Expression::Boolean(self)); + fn evaluate(self, value: Boolean) -> Self::Output { + self.stack.push(Expression::Boolean(value)); Ok(()) } } diff --git a/rat/src/builtin.rs b/rat/src/builtin.rs index 0900d74..af9367f 100644 --- a/rat/src/builtin.rs +++ b/rat/src/builtin.rs @@ -6,22 +6,20 @@ use std::io::{self, Write}; -use crate::signal; - -use crate::effect::Effect; +use crate::error::RuntimeError; use crate::evaluate::Evaluate; use crate::evaluator::Evaluator; use crate::expression::Expression; use crate::boolean::Boolean; -use crate::channel::Channel; use crate::decimal::Decimal; use crate::integer::Integer; use crate::quote::Quote; use crate::string::String; +use crate::symbol::Symbol; -pub fn neg(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn neg(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &mut stack[..] { @@ -34,17 +32,17 @@ pub fn neg(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn incr(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn incr(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &mut stack[..] { @@ -57,17 +55,17 @@ pub fn incr(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn decr(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn decr(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &mut stack[..] { @@ -80,17 +78,17 @@ pub fn decr(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn add(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn add(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -105,17 +103,17 @@ pub fn add(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn sub(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn sub(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -130,17 +128,17 @@ pub fn sub(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn mul(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn mul(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -155,23 +153,23 @@ pub fn mul(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn div(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn div(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { [.., Expression::Integer(_), Expression::Integer(Integer::ZERO)] => { - stack.push(signal::divide_by_zero().into()); - Err(Effect::Raise) + stack.push(Symbol::divide_by_zero().into()); + Err(RuntimeError) } [.., Expression::Integer(ref mut lhs), Expression::Integer(rhs)] => { *lhs /= rhs; @@ -184,23 +182,23 @@ pub fn div(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn rem(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn rem(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { [.., Expression::Integer(_), Expression::Integer(Integer::ZERO)] => { - stack.push(signal::divide_by_zero().into()); - Err(Effect::Raise) + stack.push(Symbol::divide_by_zero().into()); + Err(RuntimeError) } [.., Expression::Integer(ref mut lhs), Expression::Integer(rhs)] => { *lhs %= rhs; @@ -213,79 +211,49 @@ pub fn rem(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn eq(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn eq(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { - [.., ref mut lhs @ Expression::Integer(_), Expression::Integer(r)] => match *lhs { - Expression::Integer(l) => { - *lhs = Expression::Boolean(Boolean(l == r)); - stack.pop(); - Ok(()) - } - _ => unreachable!(), - }, - [.., ref mut lhs @ Expression::Decimal(_), Expression::Decimal(r)] => match *lhs { - Expression::Decimal(l) => { - *lhs = Expression::Boolean(Boolean(l == r)); - stack.pop(); - Ok(()) - } - _ => unreachable!(), - }, - [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + [.., ref mut lhs, ref rhs] => { + *lhs = Expression::Boolean(Boolean(*lhs == *rhs)); + stack.pop(); + Ok(()) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn ne(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn ne(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { - [.., ref mut lhs @ Expression::Integer(_), Expression::Integer(r)] => match *lhs { - Expression::Integer(l) => { - *lhs = Expression::Boolean(Boolean(l != r)); - stack.pop(); - Ok(()) - } - _ => unreachable!(), - }, - [.., ref mut lhs @ Expression::Decimal(_), Expression::Decimal(r)] => match *lhs { - Expression::Decimal(l) => { - *lhs = Expression::Boolean(Boolean(l != r)); - stack.pop(); - Ok(()) - } - _ => unreachable!(), - }, - [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + [.., ref mut lhs, ref rhs] => { + *lhs = Expression::Boolean(Boolean(*lhs != *rhs)); + stack.pop(); + Ok(()) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn gt(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn gt(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -306,17 +274,17 @@ pub fn gt(evaluator: &mut Evaluator) -> Result<(), Effect> { _ => unreachable!(), }, [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn ge(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn ge(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -337,17 +305,17 @@ pub fn ge(evaluator: &mut Evaluator) -> Result<(), Effect> { _ => unreachable!(), }, [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn lt(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn lt(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -368,17 +336,17 @@ pub fn lt(evaluator: &mut Evaluator) -> Result<(), Effect> { _ => unreachable!(), }, [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn le(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn le(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -399,86 +367,86 @@ pub fn le(evaluator: &mut Evaluator) -> Result<(), Effect> { _ => unreachable!(), }, [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn positive(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn positive(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &stack[..] { [.., Expression::Integer(n)] => { - stack.push(Expression::Boolean(Boolean(n.is_positive()))); + *stack.last_mut().unwrap() = Expression::Boolean(Boolean(n.is_positive())); Ok(()) } [.., Expression::Decimal(n)] => { - stack.push(Expression::Boolean(Boolean(n.is_positive()))); + *stack.last_mut().unwrap() = Expression::Boolean(Boolean(n.is_positive())); Ok(()) } [.., _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn zero(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn zero(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &stack[..] { [.., Expression::Integer(n)] => { - stack.push(Expression::Boolean(Boolean(n.is_zero()))); + *stack.last_mut().unwrap() = Expression::Boolean(Boolean(n.is_zero())); Ok(()) } [.., Expression::Decimal(n)] => { - stack.push(Expression::Boolean(Boolean(n.is_zero()))); + *stack.last_mut().unwrap() = Expression::Boolean(Boolean(n.is_zero())); Ok(()) } [.., _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn negative(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn negative(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &stack[..] { [.., Expression::Integer(n)] => { - stack.push(Expression::Boolean(Boolean(n.is_negative()))); + *stack.last_mut().unwrap() = Expression::Boolean(Boolean(n.is_negative())); Ok(()) } [.., Expression::Decimal(n)] => { - stack.push(Expression::Boolean(Boolean(n.is_negative()))); + *stack.last_mut().unwrap() = Expression::Boolean(Boolean(n.is_negative())); Ok(()) } [.., _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn not(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn not(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &mut stack[..] { @@ -487,17 +455,17 @@ pub fn not(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn and(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn and(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -507,17 +475,17 @@ pub fn and(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn or(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn or(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -527,17 +495,17 @@ pub fn or(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn bitwise_not(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn bitwise_not(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &mut stack[..] { @@ -550,17 +518,17 @@ pub fn bitwise_not(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn bitwise_and(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn bitwise_and(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -575,17 +543,17 @@ pub fn bitwise_and(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn bitwise_xor(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn bitwise_xor(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -595,22 +563,22 @@ pub fn bitwise_xor(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., Expression::Boolean(ref mut lhs), Expression::Boolean(rhs)] => { - *lhs = Boolean(*lhs != rhs); + *lhs ^= rhs; stack.pop(); Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn bitwise_or(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn bitwise_or(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -625,17 +593,17 @@ pub fn bitwise_or(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn shl(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn shl(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -645,17 +613,17 @@ pub fn shl(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn shr(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn shr(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -665,17 +633,17 @@ pub fn shr(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn ushr(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn ushr(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -685,17 +653,17 @@ pub fn ushr(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn cat(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn cat(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &mut stack[..] { @@ -706,92 +674,106 @@ pub fn cat(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn quote(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn quote(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; let expression = stack.pop().ok_or_else(|| { - stack.push(signal::stack_underflow().into()); - Effect::Raise + stack.push(Symbol::stack_underflow().into()); + RuntimeError })?; stack.push(Expression::Quote(Quote::from([expression]))); Ok(()) } -pub fn unquote(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn unquote(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { match evaluator.stack.pop() { - Some(Expression::Quote(quote)) => { - let mut continuation = quote.into_iter(); - - match continuation.try_for_each(|e| evaluator.evaluate(e)) { - Err(Effect::Yield) => { - evaluator - .stack - .push(Expression::Quote(continuation.collect())); - Ok(()) - } - flow => flow, - } - } + Some(Expression::Quote(quote)) => evaluator.evaluate(quote.iter().cloned()), Some(expression) => { evaluator .stack - .extend_from_slice(&[expression, signal::type_error().into()]); - Err(Effect::Raise) + .extend_from_slice(&[expression, Symbol::type_error().into()]); + Err(RuntimeError) } _ => { - evaluator.stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + evaluator.stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn eval(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn eval(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack.pop() { Some(expression) => evaluator.evaluate(expression), _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn i(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn i(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &stack[..] { [.., Expression::Quote(_)] => unquote(evaluator), [.., _] => eval(evaluator), _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn x(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn x(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; let expression = stack.last().cloned().ok_or_else(|| { - stack.push(signal::stack_underflow().into()); - Effect::Raise + stack.push(Symbol::stack_underflow().into()); + RuntimeError })?; stack.push(expression); i(evaluator) } -pub fn dip(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn unary2(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { + let stack = &mut evaluator.stack; + let top = stack.len(); + + match stack[..] { + [.., _, ref mut input2, Expression::Quote(ref mut quote)] => { + let quote = std::mem::take(quote); + let input2 = std::mem::replace(input2, Expression::Integer(Integer::ZERO)); + + stack.truncate(top - 2); + evaluator.evaluate(quote.iter().cloned())?; + + evaluator.stack.push(input2); + evaluator.evaluate(quote.iter().cloned()) + } + [.., _, _, _] => { + stack.push(Symbol::type_error().into()); + Err(RuntimeError) + } + _ => { + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) + } + } +} + +pub fn dip(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { match &evaluator.stack[..] { [.., _, Expression::Quote(_)] => { let top = evaluator.stack.len(); @@ -801,17 +783,17 @@ pub fn dip(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - evaluator.stack.push(signal::type_error().into()); - Err(Effect::Raise) + evaluator.stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - evaluator.stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + evaluator.stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn r#if(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn r#if(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -820,7 +802,7 @@ pub fn r#if(evaluator: &mut Evaluator) -> Result<(), Effect> { let quote = std::mem::take(truthy); let top = stack.len(); stack.truncate(top - 2); - return evaluator.evaluate(quote); + return evaluator.evaluate(quote.iter().cloned()); } let top = stack.len(); @@ -828,17 +810,17 @@ pub fn r#if(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _, _] => { - evaluator.stack.push(signal::type_error().into()); - Err(Effect::Raise) + evaluator.stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - evaluator.stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + evaluator.stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn r#else(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn r#else(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -847,7 +829,7 @@ pub fn r#else(evaluator: &mut Evaluator) -> Result<(), Effect> { let quote = std::mem::take(falsy); let top = stack.len(); stack.truncate(top - 2); - return evaluator.evaluate(quote); + return evaluator.evaluate(quote.iter().cloned()); } let top = stack.len(); @@ -855,17 +837,17 @@ pub fn r#else(evaluator: &mut Evaluator) -> Result<(), Effect> { Ok(()) } [.., _, _] => { - evaluator.stack.push(signal::type_error().into()); - Err(Effect::Raise) + evaluator.stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - evaluator.stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + evaluator.stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn if_else(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn if_else(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match stack[..] { @@ -874,28 +856,24 @@ pub fn if_else(evaluator: &mut Evaluator) -> Result<(), Effect> { let quote = std::mem::take(if condition { truthy } else { falsy }); let top = stack.len(); stack.truncate(top - 3); - evaluator.evaluate(quote) + evaluator.evaluate(quote.iter().cloned()) } [.., _, _, _] => { - evaluator.stack.push(signal::type_error().into()); - Err(Effect::Raise) + evaluator.stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - evaluator.stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + evaluator.stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn r#yield(_: &mut Evaluator) -> Result<(), Effect> { - Err(Effect::Yield) +pub fn raise(_: &mut Evaluator) -> Result<(), RuntimeError> { + Err(RuntimeError) } -pub fn raise(_: &mut Evaluator) -> Result<(), Effect> { - Err(Effect::Raise) -} - -pub fn catch(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn r#try(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { match &evaluator.stack[..] { [.., Expression::Quote(_), Expression::Quote(_), _] => { let guarded = evaluator.stack.pop(); @@ -903,7 +881,7 @@ pub fn catch(evaluator: &mut Evaluator) -> Result<(), Effect> { let len = evaluator.stack.len(); match unquote(evaluator) { - Err(Effect::Raise) if evaluator.stack.last() == guarded.as_ref() => { + Err(RuntimeError) if evaluator.stack.last() == guarded.as_ref() => { evaluator.stack.truncate(len); evaluator.stack[len - 1] = guard; unquote(evaluator) @@ -912,311 +890,271 @@ pub fn catch(evaluator: &mut Evaluator) -> Result<(), Effect> { } } [.., _, _, _] => { - evaluator.stack.push(signal::type_error().into()); - Err(Effect::Raise) + evaluator.stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - evaluator.stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + evaluator.stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn first(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn first(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &stack[..] { [.., Expression::Quote(quote)] => { let expression = quote.first().cloned().ok_or_else(|| { - stack.push(signal::out_of_range().into()); - Effect::Raise + stack.push(Symbol::out_of_range().into()); + RuntimeError })?; - stack.push(expression); + *stack.last_mut().unwrap() = expression; Ok(()) } [.., _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn last(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn last(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &stack[..] { [.., Expression::Quote(quote)] => { let expression = quote.last().cloned().ok_or_else(|| { - stack.push(signal::out_of_range().into()); - Effect::Raise + stack.push(Symbol::out_of_range().into()); + RuntimeError })?; - stack.push(expression); + *stack.last_mut().unwrap() = expression; Ok(()) } [.., _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn head(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn prefix(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &stack[..] { [.., Expression::Quote(quote)] => { let quote_len = quote.len(); - - if quote_len < 2 { - stack.push(signal::out_of_range().into()); - return Err(Effect::Raise); - } - - stack.push(Expression::Quote( - quote[..quote_len - 1].iter().cloned().collect(), - )); + *stack.last_mut().unwrap() = Expression::Quote( + quote + .get(..quote_len - 1) + .unwrap_or_default() + .iter() + .cloned() + .collect(), + ); Ok(()) } [.., _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn tail(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn suffix(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &stack[..] { [.., Expression::Quote(quote)] => { - let quote_len = quote.len(); - - if quote_len < 2 { - stack.push(signal::out_of_range().into()); - return Err(Effect::Raise); - } - - stack.push(Expression::Quote(quote[1..].iter().cloned().collect())); - + *stack.last_mut().unwrap() = + Expression::Quote(quote.get(1..).unwrap_or_default().iter().cloned().collect()); Ok(()) } [.., _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn swap(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn at(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; - let top = stack.len(); - if top < 2 { - stack.push(signal::stack_underflow().into()); - return Err(Effect::Raise); - } + match stack[..] { + [.., Expression::Quote(ref quote), Expression::Integer(Integer(at))] => { + let quote_len = quote.len(); - stack.swap(top - 1, top - 2); - Ok(()) -} + if at.is_negative() || (at as usize) >= quote_len { + stack.push(Symbol::out_of_range().into()); + return Err(RuntimeError); + } -pub fn rollup(evaluator: &mut Evaluator) -> Result<(), Effect> { - let stack = &mut evaluator.stack; + let expression = quote[at as usize].clone(); - match &mut stack[..] { - [.., x, y, z] => { - std::mem::swap(y, z); - std::mem::swap(x, y); + stack.pop(); + *stack.last_mut().unwrap() = expression; Ok(()) } + [.., _, _] => { + stack.push(Symbol::type_error().into()); + Err(RuntimeError) + } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn rolldown(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn split(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; - match &mut stack[..] { - [.., x, y, z] => { - std::mem::swap(x, y); - std::mem::swap(y, z); + match stack[..] { + [.., Expression::Quote(ref mut quote), Expression::Integer(Integer(at))] => { + let quote_len = quote.len(); + + if at.is_negative() || (at as usize) >= quote_len { + stack.push(Symbol::out_of_range().into()); + return Err(RuntimeError); + } + + *stack.last_mut().unwrap() = quote.split(at as usize).into(); Ok(()) } + [.., _, _] => { + stack.push(Symbol::type_error().into()); + Err(RuntimeError) + } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn rotate(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn len(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; - match &mut stack[..] { - [.., x, _, z] => { - std::mem::swap(x, z); + match &stack[..] { + [.., Expression::Quote(quote)] => { + *stack.last_mut().unwrap() = Expression::Integer(Integer(quote.len() as _)); + Ok(()) + } + [.., Expression::String(string)] => { + *stack.last_mut().unwrap() = Expression::Integer(Integer(string.len() as _)); Ok(()) } + [.., _] => { + stack.push(Symbol::type_error().into()); + Err(RuntimeError) + } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn pop(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn swap(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; + let top = stack.len(); - if stack.pop().is_none() { - stack.push(signal::stack_underflow().into()); - return Err(Effect::Raise); + if top < 2 { + stack.push(Symbol::stack_underflow().into()); + return Err(RuntimeError); } + stack.swap(top - 1, top - 2); Ok(()) } -pub fn dup(evaluator: &mut Evaluator) -> Result<(), Effect> { - let stack = &mut evaluator.stack; - let expression = stack.last().cloned().ok_or_else(|| { - stack.push(signal::stack_underflow().into()); - Effect::Raise - })?; - - stack.push(expression); - Ok(()) -} - -pub fn send(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn rollup(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; - match stack[..] { - [.., Expression::Channel(ref channel), ref mut expression] => { - let expression = std::mem::replace(expression, Expression::Integer(Integer(0))); - - if channel.send(expression).is_err() { - stack.push(signal::io_error().into()); - return Err(Effect::Raise); - } - - stack.pop(); + match &mut stack[..] { + [.., x, y, z] => { + std::mem::swap(y, z); + std::mem::swap(x, y); Ok(()) } - [.., _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) - } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn receive(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn rolldown(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; - match stack[..] { - [.., Expression::Channel(ref channel)] => match channel.recv() { - Ok(expression) => { - stack.push(expression); - Ok(()) - } - Err(_) => { - stack.push(signal::io_error().into()); - Err(Effect::Raise) - } - }, - [.., _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + match &mut stack[..] { + [.., x, y, z] => { + std::mem::swap(x, y); + std::mem::swap(y, z); + Ok(()) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn produce(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn rotate(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; - match stack.pop() { - Some(expression) => { - if let Some(sender) = evaluator.channel.as_ref().map(|(sender, _)| sender) { - if sender.send(expression).is_err() { - stack.push(signal::io_error().into()); - return Err(Effect::Raise); - } - } - + match &mut stack[..] { + [.., x, _, z] => { + std::mem::swap(x, z); Ok(()) } - None => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + _ => { + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } -pub fn consume(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn pop(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; - if let Some(receiver) = evaluator.channel.as_ref().map(|(_, receiver)| receiver) { - if let Ok(expression) = receiver.recv() { - stack.push(expression); - return Ok(()); - } + if stack.pop().is_none() { + stack.push(Symbol::stack_underflow().into()); + return Err(RuntimeError); } - stack.push(signal::io_error().into()); - Err(Effect::Raise) + Ok(()) } -pub fn spawn(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn dup(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; + let expression = stack.last().cloned().ok_or_else(|| { + stack.push(Symbol::stack_underflow().into()); + RuntimeError + })?; - match &mut stack[..] { - [.., Expression::Quote(quote)] => { - let quote = std::mem::take(quote); - stack.pop().unwrap(); - - let channel = Channel::spawn(quote); - stack.push(Expression::Channel(channel)); - - Ok(()) - } - [.., _] => { - evaluator.stack.push(signal::type_error().into()); - Err(Effect::Raise) - } - _ => { - evaluator.stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) - } - } + stack.push(expression); + Ok(()) } -pub fn ask(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn ask(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &stack[..] { @@ -1224,153 +1162,235 @@ pub fn ask(evaluator: &mut Evaluator) -> Result<(), Effect> { let mut stdout_lock = io::stdout().lock(); write!(stdout_lock, "{prompt}").map_err(|_| { - stack.push(signal::io_error().into()); - Effect::Raise + stack.push(Symbol::io_error().into()); + RuntimeError })?; stdout_lock.flush().map_err(|_| { - stack.push(signal::io_error().into()); - Effect::Raise + stack.push(Symbol::io_error().into()); + RuntimeError })?; stack.pop(); } [.., _] => { - evaluator.stack.push(signal::type_error().into()); - return Err(Effect::Raise); + evaluator.stack.push(Symbol::type_error().into()); + return Err(RuntimeError); } _ => { - evaluator.stack.push(signal::stack_underflow().into()); - return Err(Effect::Raise); + evaluator.stack.push(Symbol::stack_underflow().into()); + return Err(RuntimeError); } } let mut buf = std::string::String::new(); io::stdin().read_line(&mut buf).map_err(|_| { - stack.push(signal::io_error().into()); - Effect::Raise + stack.push(Symbol::io_error().into()); + RuntimeError })?; stack.push(Expression::String(String::from_utf8(&buf))); Ok(()) } -pub fn say(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn say(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; let expression = stack.pop().ok_or_else(|| { - stack.push(signal::stack_underflow().into()); - Effect::Raise - })?; - - let mut stdout_lock = io::stdout().lock(); - - write!(stdout_lock, "{expression}").map_err(|_| { - stack.push(signal::io_error().into()); - Effect::Raise + stack.push(Symbol::stack_underflow().into()); + RuntimeError })?; - stdout_lock.flush().map_err(|_| { - stack.push(signal::io_error().into()); - Effect::Raise + writeln!(io::stdout(), "{expression}").map_err(|_| { + stack.push(Symbol::io_error().into()); + RuntimeError }) } -pub fn show(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn show(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; let mut stdout = io::stdout().lock(); write!(stdout, "#>>>").map_err(|_| { - stack.push(signal::io_error().into()); - Effect::Raise + stack.push(Symbol::io_error().into()); + RuntimeError })?; stack .iter() .try_for_each(|e| write!(stdout, " {e:?}")) .map_err(|_| { - stack.push(signal::io_error().into()); - Effect::Raise + stack.push(Symbol::io_error().into()); + RuntimeError })?; writeln!(stdout).map_err(|_| { - stack.push(signal::io_error().into()); - Effect::Raise + stack.push(Symbol::io_error().into()); + RuntimeError }) } -pub fn binrec(evaluator: &mut Evaluator) -> Result<(), Effect> { +pub fn linrec(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { let stack = &mut evaluator.stack; match &mut stack[..] { - [.., value, Expression::Quote(case), Expression::Quote(leave), Expression::Quote(left), Expression::Quote(right), Expression::Quote(merge)] => + [.., Expression::Quote(check), Expression::Quote(leave), Expression::Quote(shard), Expression::Quote(merge)] => { let merge = std::mem::take(merge); - let right = std::mem::take(right); - let left = std::mem::take(left); + let shard = std::mem::take(shard); let leave = std::mem::take(leave); - let case = std::mem::take(case); - let value = value.clone(); + let check = std::mem::take(check); let top = stack.len(); - stack.truncate(top - 5); + stack.truncate(top - 4); - binrec_aux(evaluator, value, &case, &leave, &left, &right, &merge) + linrec_aux(evaluator, &check, &leave, &shard, &merge) } - [.., _, _, _, _, _, _] => { - stack.push(signal::type_error().into()); - Err(Effect::Raise) + [.., _, _, _, _] => { + stack.push(Symbol::type_error().into()); + Err(RuntimeError) } _ => { - stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) + } + } +} + +fn linrec_aux( + evaluator: &mut Evaluator, + check: &Quote, + leave: &Quote, + shard: &Quote, + merge: &Quote, +) -> Result<(), RuntimeError> { + evaluator.evaluate(check.iter().cloned())?; + + match evaluator.stack.pop() { + Some(Expression::Boolean(Boolean(false))) => { + evaluator.evaluate(shard.iter().cloned())?; + linrec_aux(evaluator, check, leave, shard, merge)?; + evaluator.evaluate(merge.iter().cloned()) + } + Some(Expression::Boolean(Boolean(true))) => evaluator.evaluate(leave.iter().cloned()), + Some(expression) => { + evaluator + .stack + .extend_from_slice(&[expression, Symbol::type_error().into()]); + + Err(RuntimeError) + } + None => { + evaluator.stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) + } + } +} + +pub fn binrec(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { + let stack = &mut evaluator.stack; + + match &mut stack[..] { + [.., Expression::Quote(check), Expression::Quote(leave), Expression::Quote(shard), Expression::Quote(merge)] => + { + let merge = std::mem::take(merge); + let shard = std::mem::take(shard); + let leave = std::mem::take(leave); + let check = std::mem::take(check); + + let top = stack.len(); + stack.truncate(top - 4); + + binrec_aux(evaluator, &check, &leave, &shard, &merge) + } + [.., _, _, _, _] => { + stack.push(Symbol::type_error().into()); + Err(RuntimeError) + } + _ => { + stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } fn binrec_aux( evaluator: &mut Evaluator, - value: Expression, - case: &Quote, + check: &Quote, leave: &Quote, - left: &Quote, - right: &Quote, + shard: &Quote, merge: &Quote, -) -> Result<(), Effect> { - evaluator.evaluate(case.iter().cloned())?; - - match &mut evaluator.stack[..] { - [.., head @ Expression::Boolean(Boolean(false))] => { - *head = value.clone(); - evaluator.evaluate(left.iter().cloned())?; - let last = evaluator.stack.last().cloned().ok_or_else(|| { - evaluator.stack.push(signal::stack_underflow().into()); - Effect::Raise - })?; - binrec_aux(evaluator, last, case, leave, left, right, merge)?; +) -> Result<(), RuntimeError> { + evaluator.evaluate(check.iter().cloned())?; - evaluator.stack.push(value); - evaluator.evaluate(right.iter().cloned())?; - let last = evaluator.stack.last().cloned().ok_or_else(|| { - evaluator.stack.push(signal::stack_underflow().into()); - Effect::Raise - })?; - binrec_aux(evaluator, last, case, leave, left, right, merge)?; + match evaluator.stack.pop() { + Some(Expression::Boolean(Boolean(false))) => { + evaluator.evaluate(shard.iter().cloned())?; + + let expression = evaluator.stack.pop().unwrap(); + binrec_aux(evaluator, check, leave, shard, merge)?; + + evaluator.stack.push(expression); + binrec_aux(evaluator, check, leave, shard, merge)?; evaluator.evaluate(merge.iter().cloned()) } - [.., head @ Expression::Boolean(Boolean(true))] => { - *head = value; - evaluator.evaluate(leave.iter().cloned()) + Some(Expression::Boolean(Boolean(true))) => evaluator.evaluate(leave.iter().cloned()), + Some(expression) => { + evaluator + .stack + .extend_from_slice(&[expression, Symbol::type_error().into()]); + + Err(RuntimeError) } - [.., _] => { - evaluator.stack.push(signal::type_error().into()); - Err(Effect::Raise) + None => { + evaluator.stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } - _ => { - evaluator.stack.push(signal::stack_underflow().into()); - Err(Effect::Raise) + } +} + +pub fn bind(evaluator: &mut Evaluator) -> Result<(), RuntimeError> { + match evaluator.stack.pop() { + Some(Expression::Quote(quote)) => { + let mut top = evaluator.stack.len(); + let expression = quote + .iter() + .rev() + .map(|e| match e { + Expression::Symbol(_) => { + top -= 1; + evaluator.stack.get(top).cloned().ok_or(RuntimeError) + } + _ => Ok(e.clone()), + }) + .collect::, _>>() + .map(|mut q| { + q.reverse(); + Expression::Quote(Quote::from(q)) + }) + .inspect_err(|_| { + evaluator.stack.extend_from_slice(&[ + Expression::Quote(quote), + Symbol::stack_underflow().into(), + ]); + })?; + + evaluator.stack.truncate(top); + evaluator.stack.push(expression); + Ok(()) + } + Some(expression) => { + evaluator + .stack + .extend_from_slice(&[expression, Symbol::type_error().into()]); + + Err(RuntimeError) + } + None => { + evaluator.stack.push(Symbol::stack_underflow().into()); + Err(RuntimeError) } } } diff --git a/rat/src/channel.rs b/rat/src/channel.rs deleted file mode 100644 index 9241b7a..0000000 --- a/rat/src/channel.rs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -use crossbeam::channel::{self, Receiver, Sender}; - -use std::error::Error; -use std::fmt::{Debug, Display}; -use std::hash::Hash; -use std::sync::Arc; -use std::thread::{self, ThreadId}; - -use crate::effect::Effect; -use crate::evaluate::Evaluate; -use crate::evaluator::Evaluator; -use crate::expression::Expression; -use crate::quote::Quote; - -#[derive(Clone, PartialEq, Eq, Hash)] -pub struct Channel { - inner: Arc, -} - -impl Channel { - pub fn spawn(quote: Quote) -> Self { - let (sender1, receiver1) = channel::unbounded(); - let (sender2, receiver2) = channel::unbounded(); - - let handle = thread::spawn(move || { - let mut evaluator = Evaluator { - stack: Vec::new(), - channel: Some((sender2, receiver1)), - }; - evaluator.evaluate(quote) - }); - - Self { - inner: Arc::new(Inner { - sender: sender1, - receiver: receiver2, - identity: handle.thread().id(), - }), - } - } - - pub fn send(&self, expr: Expression) -> Result<(), impl Error> { - self.inner.sender.send(expr) - } - - pub fn recv(&self) -> Result { - self.inner.receiver.recv() - } -} - -impl Evaluate<&mut Evaluator> for Channel { - type Output = Result<(), Effect>; - - fn evaluate(self, evaluator: &mut Evaluator) -> Self::Output { - evaluator.stack.push(Expression::Channel(self)); - Ok(()) - } -} - -impl Display for Channel { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - Debug::fmt(self, f) - } -} - -impl Debug for Channel { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "↯{:?}", self.inner.identity) - } -} - -struct Inner { - sender: Sender, - receiver: Receiver, - identity: ThreadId, -} - -impl PartialEq for Inner { - fn eq(&self, other: &Self) -> bool { - self.identity == other.identity - } -} - -impl Eq for Inner {} - -impl Hash for Inner { - fn hash(&self, state: &mut H) { - self.identity.hash(state) - } -} diff --git a/rat/src/decimal.rs b/rat/src/decimal.rs index e81f261..3dfc724 100644 --- a/rat/src/decimal.rs +++ b/rat/src/decimal.rs @@ -13,7 +13,7 @@ use std::ops::{ }; use crate::codegen; -use crate::effect::Effect; +use crate::error::RuntimeError; use crate::evaluate::Evaluate; use crate::evaluator::Evaluator; use crate::expression::Expression; @@ -76,11 +76,11 @@ impl Decimal { } } -impl Evaluate<&mut Evaluator> for Decimal { - type Output = Result<(), Effect>; +impl Evaluate for &mut Evaluator { + type Output = Result<(), RuntimeError>; - fn evaluate(self, evaluator: &mut Evaluator) -> Self::Output { - evaluator.stack.push(Expression::Decimal(self)); + fn evaluate(self, value: Decimal) -> Self::Output { + self.stack.push(Expression::Decimal(value)); Ok(()) } } diff --git a/rat/src/vocabulary.rs b/rat/src/dictionary.rs similarity index 69% rename from rat/src/vocabulary.rs rename to rat/src/dictionary.rs index 0d799ff..85f48c3 100644 --- a/rat/src/vocabulary.rs +++ b/rat/src/dictionary.rs @@ -14,18 +14,18 @@ use crate::boolean::Boolean; use crate::builtin; use crate::decimal::Decimal; use crate::expression::Expression; -use crate::locution::Locution; +use crate::identifier::Identifier; use crate::verb::Verb; use crate::word::{OwnedWord, Word}; #[derive(Debug)] pub enum Definition { - Phrase { - phrase: Arc<[Expression]>, + Body { + body: Arc<[Expression]>, visibility: Visibility, }, - Vocabulary { - vocabulary: Arc, + Dictionary { + dictionary: Arc, visibility: Visibility, }, } @@ -34,8 +34,8 @@ impl Definition { pub fn is_intern(&self) -> bool { Visibility::Intern == match self { - Definition::Phrase { visibility, .. } => *visibility, - Definition::Vocabulary { visibility, .. } => *visibility, + Definition::Body { visibility, .. } => *visibility, + Definition::Dictionary { visibility, .. } => *visibility, } } @@ -51,11 +51,11 @@ pub enum Visibility { } #[derive(Debug, Default)] -pub struct Vocabulary { +pub struct Dictionary { definitions: HashMap, } -impl Vocabulary { +impl Dictionary { pub fn new() -> Self { Self { definitions: HashMap::new(), @@ -66,11 +66,11 @@ impl Vocabulary { Self { definitions: PRELUDE .into_iter() - .map(|(word, phrase)| { + .map(|(word, body)| { ( word.into(), - Definition::Phrase { - phrase: phrase.into(), + Definition::Body { + body: body.into(), visibility: Visibility::Intern, }, ) @@ -91,30 +91,30 @@ impl Vocabulary { self.definitions.get(word) } - pub fn lookup(&self, locution: &Locution) -> Option<&[Expression]> { - let mut words = locution.words(); - let mut vocabulary = self; + pub fn lookup(&self, identifier: &Identifier) -> Option<&[Expression]> { + let mut words = identifier.words(); + let mut dictionary = self; let mut is_first_word = true; while let Some(word) = words.next() { - let definition = vocabulary.get(word)?; + let definition = dictionary.get(word)?; if !is_first_word && definition.is_intern() { break; } match definition { - Definition::Phrase { phrase, .. } => { + Definition::Body { body, .. } => { if words.next().is_some() { break; } - return Some(phrase); + return Some(body); } - Definition::Vocabulary { - vocabulary: next_vocabulary, + Definition::Dictionary { + dictionary: next_dictionary, .. - } => vocabulary = next_vocabulary, + } => dictionary = next_dictionary, } is_first_word = false; @@ -145,17 +145,17 @@ static PRELUDE: [(&Word, &[Expression]); 64] = [ word_literal("decr"), &[Expression::Verb(Verb(builtin::decr))], ), - (word_literal("+"), &[Expression::Verb(Verb(builtin::add))]), - (word_literal("-"), &[Expression::Verb(Verb(builtin::sub))]), - (word_literal("*"), &[Expression::Verb(Verb(builtin::mul))]), - (word_literal("/"), &[Expression::Verb(Verb(builtin::div))]), - (word_literal("%"), &[Expression::Verb(Verb(builtin::rem))]), - (word_literal("="), &[Expression::Verb(Verb(builtin::eq))]), - (word_literal("<>"), &[Expression::Verb(Verb(builtin::ne))]), - (word_literal(">"), &[Expression::Verb(Verb(builtin::gt))]), - (word_literal(">="), &[Expression::Verb(Verb(builtin::ge))]), - (word_literal("<"), &[Expression::Verb(Verb(builtin::lt))]), - (word_literal("<="), &[Expression::Verb(Verb(builtin::le))]), + (word_literal("add"), &[Expression::Verb(Verb(builtin::add))]), + (word_literal("sub"), &[Expression::Verb(Verb(builtin::sub))]), + (word_literal("mul"), &[Expression::Verb(Verb(builtin::mul))]), + (word_literal("div"), &[Expression::Verb(Verb(builtin::div))]), + (word_literal("rem"), &[Expression::Verb(Verb(builtin::rem))]), + (word_literal("eq?"), &[Expression::Verb(Verb(builtin::eq))]), + (word_literal("ne?"), &[Expression::Verb(Verb(builtin::ne))]), + (word_literal("gt?"), &[Expression::Verb(Verb(builtin::gt))]), + (word_literal("ge?"), &[Expression::Verb(Verb(builtin::ge))]), + (word_literal("lt?"), &[Expression::Verb(Verb(builtin::lt))]), + (word_literal("le?"), &[Expression::Verb(Verb(builtin::le))]), ( word_literal("positive?"), &[Expression::Verb(Verb(builtin::positive))], @@ -174,7 +174,7 @@ static PRELUDE: [(&Word, &[Expression]); 64] = [ &[Expression::Decimal(Decimal::INFINITY)], ), ( - word_literal("-inf"), + word_literal("neg-inf"), &[Expression::Decimal(Decimal::NEG_INFINITY)], ), ( @@ -186,25 +186,25 @@ static PRELUDE: [(&Word, &[Expression]); 64] = [ (word_literal("and"), &[Expression::Verb(Verb(builtin::and))]), (word_literal("or"), &[Expression::Verb(Verb(builtin::or))]), ( - word_literal("~"), + word_literal("bit-not"), &[Expression::Verb(Verb(builtin::bitwise_not))], ), ( - word_literal("&"), + word_literal("bit-and"), &[Expression::Verb(Verb(builtin::bitwise_and))], ), ( - word_literal("^"), + word_literal("bit-xor"), &[Expression::Verb(Verb(builtin::bitwise_xor))], ), ( - word_literal("|"), + word_literal("bit-or"), &[Expression::Verb(Verb(builtin::bitwise_or))], ), - (word_literal("<<"), &[Expression::Verb(Verb(builtin::shl))]), - (word_literal(">>"), &[Expression::Verb(Verb(builtin::shr))]), + (word_literal("shl"), &[Expression::Verb(Verb(builtin::shl))]), + (word_literal("shr"), &[Expression::Verb(Verb(builtin::shr))]), ( - word_literal(">>>"), + word_literal("ushr"), &[Expression::Verb(Verb(builtin::ushr))], ), (word_literal("cat"), &[Expression::Verb(Verb(builtin::cat))]), @@ -223,26 +223,26 @@ static PRELUDE: [(&Word, &[Expression]); 64] = [ (word_literal("i"), &[Expression::Verb(Verb(builtin::i))]), (word_literal("x"), &[Expression::Verb(Verb(builtin::x))]), (word_literal("dip"), &[Expression::Verb(Verb(builtin::dip))]), + ( + word_literal("unary2"), + &[Expression::Verb(Verb(builtin::unary2))], + ), (word_literal("if"), &[Expression::Verb(Verb(builtin::r#if))]), ( word_literal("else"), &[Expression::Verb(Verb(builtin::r#else))], ), ( - word_literal("ifElse"), + word_literal("if-else"), &[Expression::Verb(Verb(builtin::if_else))], ), - ( - word_literal("yield"), - &[Expression::Verb(Verb(builtin::r#yield))], - ), ( word_literal("raise"), &[Expression::Verb(Verb(builtin::raise))], ), ( - word_literal("catch"), - &[Expression::Verb(Verb(builtin::catch))], + word_literal("try"), + &[Expression::Verb(Verb(builtin::r#try))], ), ( word_literal("first"), @@ -253,13 +253,19 @@ static PRELUDE: [(&Word, &[Expression]); 64] = [ &[Expression::Verb(Verb(builtin::last))], ), ( - word_literal("head"), - &[Expression::Verb(Verb(builtin::head))], + word_literal("prefix"), + &[Expression::Verb(Verb(builtin::prefix))], + ), + ( + word_literal("suffix"), + &[Expression::Verb(Verb(builtin::suffix))], ), + (word_literal("at"), &[Expression::Verb(Verb(builtin::at))]), ( - word_literal("tail"), - &[Expression::Verb(Verb(builtin::tail))], + word_literal("split"), + &[Expression::Verb(Verb(builtin::split))], ), + (word_literal("len"), &[Expression::Verb(Verb(builtin::len))]), ( word_literal("swap"), &[Expression::Verb(Verb(builtin::swap))], @@ -278,36 +284,24 @@ static PRELUDE: [(&Word, &[Expression]); 64] = [ ), (word_literal("pop"), &[Expression::Verb(Verb(builtin::pop))]), (word_literal("dup"), &[Expression::Verb(Verb(builtin::dup))]), - ( - word_literal("send"), - &[Expression::Verb(Verb(builtin::send))], - ), - ( - word_literal("receive"), - &[Expression::Verb(Verb(builtin::receive))], - ), - ( - word_literal("produce"), - &[Expression::Verb(Verb(builtin::produce))], - ), - ( - word_literal("consume"), - &[Expression::Verb(Verb(builtin::consume))], - ), - ( - word_literal("spawn"), - &[Expression::Verb(Verb(builtin::spawn))], - ), (word_literal("ask"), &[Expression::Verb(Verb(builtin::ask))]), (word_literal("say"), &[Expression::Verb(Verb(builtin::say))]), ( word_literal("show"), &[Expression::Verb(Verb(builtin::show))], ), + ( + word_literal("linrec"), + &[Expression::Verb(Verb(builtin::linrec))], + ), ( word_literal("binrec"), &[Expression::Verb(Verb(builtin::binrec))], ), + ( + word_literal("bind"), + &[Expression::Verb(Verb(builtin::bind))], + ), ]; const fn word_literal(literal: &str) -> &Word { diff --git a/rat/src/effect.rs b/rat/src/effect.rs index 5702df0..910ef06 100644 --- a/rat/src/effect.rs +++ b/rat/src/effect.rs @@ -6,6 +6,5 @@ #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Effect { - Yield, Raise, } diff --git a/rat/src/error.rs b/rat/src/error.rs new file mode 100644 index 0000000..de14ab4 --- /dev/null +++ b/rat/src/error.rs @@ -0,0 +1,8 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct RuntimeError; diff --git a/rat/src/evaluator.rs b/rat/src/evaluator.rs index bfe3644..bf38c82 100644 --- a/rat/src/evaluator.rs +++ b/rat/src/evaluator.rs @@ -4,47 +4,28 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use crossbeam::channel::{Receiver, Sender}; - -use crate::effect::Effect; +use crate::error::RuntimeError; use crate::evaluate::Evaluate; use crate::expression::Expression; +#[derive(Default)] pub struct Evaluator { pub stack: Vec, - pub channel: Option<(Sender, Receiver)>, -} - -impl Default for Evaluator { - fn default() -> Self { - Self::new() - } } impl Evaluator { - pub const fn new() -> Self { - Self { - stack: Vec::new(), - channel: None, - } + pub fn new() -> Self { + Default::default() } } -impl

Evaluate

for &mut Evaluator +impl Evaluate for &mut Evaluator where - P: IntoIterator, + I: Iterator, { - type Output = Result<(), Effect>; - - fn evaluate(self, program: P) -> Self::Output { - program.into_iter().try_for_each(|e| e.evaluate(self)) - } -} - -impl Evaluate for &mut Evaluator { - type Output = Result<(), Effect>; + type Output = Result<(), RuntimeError>; - fn evaluate(self, expression: Expression) -> Self::Output { - expression.evaluate(self) + fn evaluate(self, mut expressions: I) -> Self::Output { + expressions.try_for_each(|e| self.evaluate(e)) } } diff --git a/rat/src/expression.rs b/rat/src/expression.rs index c730a5d..76c365d 100644 --- a/rat/src/expression.rs +++ b/rat/src/expression.rs @@ -6,16 +6,14 @@ use std::fmt::{Debug, Display}; -use crate::effect::Effect; +use crate::error::RuntimeError; use crate::evaluate::Evaluate; use crate::evaluator::Evaluator; use crate::boolean::Boolean; -use crate::channel::Channel; use crate::decimal::Decimal; use crate::integer::Integer; use crate::quote::Quote; -use crate::signal::Signal; use crate::string::String; use crate::symbol::Symbol; use crate::verb::Verb; @@ -23,18 +21,16 @@ use crate::verb::Verb; #[derive(Clone, PartialEq, Eq, Hash)] pub enum Expression { Boolean(Boolean), - Channel(Channel), Decimal(Decimal), Integer(Integer), Quote(Quote), - Signal(Signal), String(String), Symbol(Symbol), Verb(Verb), } const _: [(); 16] = [(); std::mem::size_of::()]; -const _: [(); 1] = [(); std::mem::size_of::>()]; +const _: [(); 1] = [(); std::mem::size_of::>()]; impl From for Expression { #[inline] @@ -43,13 +39,6 @@ impl From for Expression { } } -impl From for Expression { - #[inline] - fn from(value: Channel) -> Self { - Self::Channel(value) - } -} - impl From for Expression { #[inline] fn from(value: Decimal) -> Self { @@ -71,13 +60,6 @@ impl From for Expression { } } -impl From for Expression { - #[inline] - fn from(value: Signal) -> Self { - Self::Signal(value) - } -} - impl From for Expression { #[inline] fn from(value: String) -> Self { @@ -99,20 +81,18 @@ impl From for Expression { } } -impl Evaluate<&mut Evaluator> for Expression { - type Output = Result<(), Effect>; - - fn evaluate(self, evaluator: &mut Evaluator) -> Self::Output { - match self { - Expression::Boolean(e) => e.evaluate(evaluator), - Expression::Channel(e) => e.evaluate(evaluator), - Expression::Decimal(e) => e.evaluate(evaluator), - Expression::Integer(e) => e.evaluate(evaluator), - Expression::Quote(e) => e.evaluate(evaluator), - Expression::Signal(e) => e.evaluate(evaluator), - Expression::String(e) => e.evaluate(evaluator), - Expression::Symbol(e) => e.evaluate(evaluator), - Expression::Verb(e) => e.evaluate(evaluator), +impl Evaluate for &mut Evaluator { + type Output = Result<(), RuntimeError>; + + fn evaluate(self, expression: Expression) -> Self::Output { + match expression { + Expression::Boolean(v) => self.evaluate(v), + Expression::Decimal(v) => self.evaluate(v), + Expression::Integer(v) => self.evaluate(v), + Expression::Quote(v) => self.evaluate(v), + Expression::String(v) => self.evaluate(v), + Expression::Symbol(v) => self.evaluate(v), + Expression::Verb(v) => self.evaluate(v), } } } @@ -120,15 +100,13 @@ impl Evaluate<&mut Evaluator> for Expression { impl Display for Expression { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Expression::Boolean(e) => Display::fmt(e, f), - Expression::Channel(e) => Display::fmt(e, f), - Expression::Decimal(e) => Display::fmt(e, f), - Expression::Integer(e) => Display::fmt(e, f), - Expression::Quote(e) => Display::fmt(e, f), - Expression::Signal(e) => Display::fmt(e, f), - Expression::String(e) => Display::fmt(e, f), - Expression::Symbol(e) => Display::fmt(e, f), - Expression::Verb(e) => Display::fmt(e, f), + Expression::Boolean(v) => Display::fmt(v, f), + Expression::Decimal(v) => Display::fmt(v, f), + Expression::Integer(v) => Display::fmt(v, f), + Expression::Quote(v) => Display::fmt(v, f), + Expression::String(v) => Display::fmt(v, f), + Expression::Symbol(v) => Display::fmt(v, f), + Expression::Verb(v) => Display::fmt(v, f), } } } @@ -136,15 +114,13 @@ impl Display for Expression { impl Debug for Expression { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Expression::Boolean(e) => Debug::fmt(e, f), - Expression::Channel(e) => Debug::fmt(e, f), - Expression::Decimal(e) => Debug::fmt(e, f), - Expression::Integer(e) => Debug::fmt(e, f), - Expression::Quote(e) => Debug::fmt(e, f), - Expression::Signal(e) => Debug::fmt(e, f), - Expression::String(e) => Debug::fmt(e, f), - Expression::Symbol(e) => Debug::fmt(e, f), - Expression::Verb(e) => Debug::fmt(e, f), + Expression::Boolean(v) => Debug::fmt(v, f), + Expression::Decimal(v) => Debug::fmt(v, f), + Expression::Integer(v) => Debug::fmt(v, f), + Expression::Quote(v) => Debug::fmt(v, f), + Expression::String(v) => Debug::fmt(v, f), + Expression::Symbol(v) => Debug::fmt(v, f), + Expression::Verb(v) => Debug::fmt(v, f), } } } diff --git a/rat/src/grammar.pest b/rat/src/grammar.pest index afb79c8..5f38251 100644 --- a/rat/src/grammar.pest +++ b/rat/src/grammar.pest @@ -5,99 +5,67 @@ */ Program = _{ - SOI ~ (Definition | Phrase)* ~ EOI + SOI ~ (Statement | Expression)* ~ EOI } -Definition = { - (Division | Colon) ~ Word ~ ((At ~ Locution) | (LeftArrow ~ Phrase)) ~ Semicolon +Statement = { + (Define | Import) } -Phrase = { - (Locution | Expression)+ +Define = { + // equal sign is optional for backward compatibility with forth + ":" ~ Export? ~ Word ~ "="? ~ Expression+ ~ ";" } -Word = @{ - _Word ~ &BREAK +Import = { + ":" ~ Export? ~ Word ~ "&" ~ Identifier ~ ";" +} + +Export = @{ + ("^" | "↑") } -_Word = @{ - // TODO: this must always be kept in sync with `Word` - ("!" | "%" | "&" | "*" | "+" | "-" | "/" | "<" | "=" | ">" | "?" | 'A'..'Z' | "^" | "_" | 'a'..'z' | "|" | "~")+ +Word = @{ + ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "-" | "_")* ~ ("!" | "?")? } -Locution = @{ - // TODO: this must always be kept in sync with `Locution` - _Word ~ ("\\" ~ _Word)* ~ &BREAK +Identifier = @{ + Word ~ ("/" ~ Word)* } Expression = { - (Boolean | Decimal | Integer | Quote | Signal | String | Symbol) + (Boolean | Decimal | Integer | Quote | String | Symbol) + | /* actually not part of expression */ + Identifier } Boolean = @{ - ("⊥" | "⊤") ~ &BREAK + ("⊥" | "⊤") } Decimal = @{ - ("+" | "-")? ~ "∞" | "0.NaN" | (((ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) | "0") ~ ((^"e" ~ ("+" | "-") ~ ASCII_DIGIT+) | "." ~ ASCII_DIGIT* ~ (^"e" ~ ("+" | "-") ~ ASCII_DIGIT+) | "." ~ ASCII_DIGIT+)) ~ &BREAK + ("+" | "-")? ~ ("∞" | "0.NaN" | (((ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) | "0") ~ ((^"e" ~ ("+" | "-") ~ ASCII_DIGIT+) | "." ~ ASCII_DIGIT* ~ (^"e" ~ ("+" | "-") ~ ASCII_DIGIT+) | "." ~ ASCII_DIGIT+))) } Integer = @{ - ("+" | "-")? ~ ((ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) | "0") ~ &BREAK + ("+" | "-")? ~ ((ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) | "0") } Quote = { - LeftSquareBracket ~ Phrase? ~ RightSquareBracket -} - -Signal = @{ - "$" ~ ('A'..'Z' | 'a'..'z')+ ~ &BREAK + "[" ~ Expression* ~ "]" } String = ${ - "\"" ~ StringUnicodeScalarValue* ~ "\"" ~ &BREAK + "\"" ~ UnicodeScalarValue* ~ "\"" } -StringUnicodeScalarValue = @{ +UnicodeScalarValue = @{ (!("\"" | "\\" | NEWLINE) ~ ANY | "\\" ~ ("\"" | "'" | "\\" | "n" | "r" | "t" | ("u{" ~ ASCII_HEX_DIGIT{1, 4} ~ "}"))) } Symbol = ${ - "'" ~ SymbolUnicodeScalarValue* ~ "'" ~ &BREAK -} - -SymbolUnicodeScalarValue = @{ - (!("'" | "\\" | NEWLINE) ~ ANY | "\\" ~ ("\"" | "'" | "\\" | "n" | "r" | "t" | ("u{" ~ ASCII_HEX_DIGIT{1, 4} ~ "}"))) -} - -At = @{ - "@" ~ &BREAK -} - -Colon = @{ - ":" ~ &BREAK -} - -Division = @{ - "÷" ~ &BREAK -} - -LeftArrow = @{ - "←" ~ &BREAK -} - -Semicolon = @{ - ";" ~ &BREAK -} - -LeftSquareBracket = @{ - "[" -} - -RightSquareBracket = @{ - "]" ~ &BREAK + "$" ~ (Identifier | String) } WHITESPACE = _{ NEWLINE | "\t" | " " } COMMENT = _{ "#" ~ (!NEWLINE ~ ANY)* } -BREAK = _{ WHITESPACE | "]" | EOI } diff --git a/rat/src/locution.rs b/rat/src/identifier.rs similarity index 56% rename from rat/src/locution.rs rename to rat/src/identifier.rs index ba417df..08c8048 100644 --- a/rat/src/locution.rs +++ b/rat/src/identifier.rs @@ -14,26 +14,26 @@ use crate::word::Word; #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] -pub struct Locution(str); +pub struct Identifier(str); -impl ToOwned for Locution { - type Owned = OwnedLocution; +impl ToOwned for Identifier { + type Owned = OwnedIdentifier; fn to_owned(&self) -> Self::Owned { let Self(inner) = self; - OwnedLocution { + OwnedIdentifier { inner: inner.into(), } } } -impl Locution { +impl Identifier { /// Whitespaces are not allowed in `literal`. // TODO: this must always be kept in sync with `grammar.pest` - pub const fn try_from_literal(literal: &str) -> Result<&Self, InvalidLocutionLiteral> { + pub const fn try_from_literal(literal: &str) -> Result<&Self, InvalidIdentifierLiteral> { let bytes = literal.as_bytes(); if bytes.is_empty() { - return Err(InvalidLocutionLiteral); + return Err(InvalidIdentifierLiteral); } let mut start = 0; @@ -41,9 +41,9 @@ impl Locution { while end < bytes.len() { let c = bytes[end]; - if b'\\' == c { + if b'/' == c { if !Word::is_valid(bytes, start, end) { - return Err(InvalidLocutionLiteral); + return Err(InvalidIdentifierLiteral); } start = end + 1; @@ -53,13 +53,13 @@ impl Locution { } if !Word::is_valid(bytes, start, end) { - return Err(InvalidLocutionLiteral); + return Err(InvalidIdentifierLiteral); } - let locution = literal as *const str as *const Self; - // Safety: `Locution` is a `repr(transparent)` wrapper around `str` + let identifier = literal as *const str as *const Self; + // Safety: `Identifier` is a `repr(transparent)` wrapper around `str` // have a look at: https://stackoverflow.com/a/72106272 - Ok(unsafe { &*locution }) + Ok(unsafe { &*identifier }) } #[inline] @@ -69,70 +69,89 @@ impl Locution { pub fn words(&self) -> impl Iterator { self.as_str() - .split('\\') + .split('/') .map(|s| Word::try_from_literal(s).unwrap()) } } -impl Display for Locution { +impl Display for Identifier { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.as_str()) } } -impl Debug for Locution { +impl Debug for Identifier { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.as_str()) } } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct OwnedLocution { +pub struct OwnedIdentifier { inner: Arc, } -impl From<&Locution> for OwnedLocution { - fn from(locution: &Locution) -> Self { - locution.to_owned() +impl From<&Identifier> for OwnedIdentifier { + fn from(identifier: &Identifier) -> Self { + identifier.to_owned() } } -impl Deref for OwnedLocution { - type Target = Locution; +impl Deref for OwnedIdentifier { + type Target = Identifier; fn deref(&self) -> &Self::Target { self.borrow() } } -impl Borrow for OwnedLocution { - fn borrow(&self) -> &Locution { - let locution = self.inner.as_ref() as *const str as *const Locution; - // Safety: `Locution` is a `repr(transparent)` wrapper around `str` +impl Borrow for OwnedIdentifier { + fn borrow(&self) -> &Identifier { + let identifier = self.inner.as_ref() as *const str as *const Identifier; + // Safety: `Identifier` is a `repr(transparent)` wrapper around `str` // have a look at: https://stackoverflow.com/a/72106272 - unsafe { &*locution } + unsafe { &*identifier } } } -impl Display for OwnedLocution { +impl Display for OwnedIdentifier { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.as_str()) } } -impl Debug for OwnedLocution { +impl Debug for OwnedIdentifier { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.as_str()) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InvalidLocutionLiteral; +pub struct InvalidIdentifierLiteral; -impl Display for InvalidLocutionLiteral { +impl Display for InvalidIdentifierLiteral { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Debug::fmt(self, f) } } -impl Error for InvalidLocutionLiteral {} +impl Error for InvalidIdentifierLiteral {} + +#[cfg(test)] +mod test { + use crate::word::Word; + + use super::Identifier; + + #[test] + fn identifier_words() { + let sut = Identifier::try_from_literal("a/very/long/identifier").unwrap(); + assert!(sut.words().eq([ + Word::try_from_literal("a").unwrap(), + Word::try_from_literal("very").unwrap(), + Word::try_from_literal("long").unwrap(), + Word::try_from_literal("identifier").unwrap() + ] + .into_iter())); + } +} diff --git a/rat/src/integer.rs b/rat/src/integer.rs index f245ca3..7f08d65 100644 --- a/rat/src/integer.rs +++ b/rat/src/integer.rs @@ -12,7 +12,7 @@ use std::ops::{ use crate::codegen; use crate::decimal::Decimal; -use crate::effect::Effect; +use crate::error::RuntimeError; use crate::evaluate::Evaluate; use crate::evaluator::Evaluator; use crate::expression::Expression; @@ -76,11 +76,11 @@ impl Integer { } } -impl Evaluate<&mut Evaluator> for Integer { - type Output = Result<(), Effect>; +impl Evaluate for &mut Evaluator { + type Output = Result<(), RuntimeError>; - fn evaluate(self, evaluator: &mut Evaluator) -> Self::Output { - evaluator.stack.push(Expression::Integer(self)); + fn evaluate(self, value: Integer) -> Self::Output { + self.stack.push(Expression::Integer(value)); Ok(()) } } diff --git a/rat/src/lib.rs b/rat/src/lib.rs index 7a6ae9d..94a1ea7 100644 --- a/rat/src/lib.rs +++ b/rat/src/lib.rs @@ -10,11 +10,9 @@ extern crate pest_derive; pub(crate) mod codegen; pub mod boolean; -pub mod channel; pub mod decimal; pub mod integer; pub mod quote; -pub mod signal; pub mod string; pub mod symbol; pub mod verb; @@ -22,12 +20,12 @@ pub mod verb; pub mod expression; pub mod builtin; -pub mod effect; +pub mod dictionary; +pub mod error; pub mod evaluate; pub mod evaluator; -pub mod locution; +pub mod identifier; pub mod parser; -pub mod vocabulary; pub mod word; use std::env; @@ -53,125 +51,130 @@ pub fn home_dir() -> &'static Path { #[cfg(test)] mod test { + use crate::boolean::Boolean; + use crate::builtin; use crate::evaluate::Evaluate; use crate::evaluator::Evaluator; use crate::expression::Expression; use crate::integer::Integer; + use crate::symbol::Symbol; use crate::verb::Verb; - use crate::{builtin, signal}; + + use std::path::Path; + use std::process::Command; #[test] fn it_works1() { let mut evaluator = Evaluator::default(); evaluator - .evaluate([ - Expression::Integer(Integer(64)), - Expression::Integer(Integer(22)), - Expression::Quote([Expression::Verb(Verb(builtin::sub))].into_iter().collect()), - Expression::Verb(Verb(builtin::unquote)), - ]) + .evaluate( + [ + Expression::Integer(Integer(64)), + Expression::Integer(Integer(22)), + Expression::Quote([Expression::Verb(Verb(builtin::sub))].into_iter().collect()), + Expression::Verb(Verb(builtin::unquote)), + ] + .into_iter(), + ) .unwrap(); - assert!(matches!( + assert_eq!( evaluator.stack.as_slice(), &[Expression::Integer(Integer(42))] - )); + ); } #[test] fn it_works2() { let mut evaluator = Evaluator::default(); evaluator - .evaluate([ - Expression::Integer(Integer(1)), - Expression::Quote( - [ - Expression::Integer(Integer(2)), - Expression::Integer(Integer(1)), - Expression::Verb(Verb(builtin::sub)), - Expression::Verb(Verb(builtin::r#yield)), - Expression::Verb(Verb(builtin::sub)), - ] - .into_iter() - .collect(), - ), - Expression::Verb(Verb(builtin::unquote)), - Expression::Verb(Verb(builtin::unquote)), - ]) + .evaluate( + [ + Expression::Quote( + [ + Expression::Integer(Integer(1)), + Expression::Verb(Verb(builtin::sub)), + ] + .into_iter() + .collect(), + ), + Expression::Quote([Expression::Integer(Integer(42))].into_iter().collect()), + Expression::Symbol(Symbol::stack_underflow()), + Expression::Verb(Verb(builtin::r#try)), + ] + .into_iter(), + ) .unwrap(); - assert!(matches!( + assert_eq!( evaluator.stack.as_slice(), - &[Expression::Integer(Integer(0))] - )); + &[Expression::Integer(Integer(42))] + ); } #[test] fn it_works3() { let mut evaluator = Evaluator::default(); evaluator - .evaluate([ - Expression::Quote( - [ - Expression::Integer(Integer(1)), - Expression::Verb(Verb(builtin::sub)), - ] - .into_iter() - .collect(), - ), - Expression::Quote([Expression::Integer(Integer(42))].into_iter().collect()), - Expression::Signal(signal::stack_underflow()), - Expression::Verb(Verb(builtin::catch)), - ]) + .evaluate( + [ + // if + Expression::Boolean(Boolean(true)), + Expression::Quote([Expression::Integer(42.into())].into_iter().collect()), + Expression::Verb(Verb(builtin::r#if)), + Expression::Boolean(Boolean(false)), + Expression::Quote([Expression::Integer(42.into())].into_iter().collect()), + Expression::Verb(Verb(builtin::r#if)), + // else + Expression::Boolean(Boolean(true)), + Expression::Quote([Expression::Integer(42.into())].into_iter().collect()), + Expression::Verb(Verb(builtin::r#else)), + Expression::Boolean(Boolean(false)), + Expression::Quote([Expression::Integer(42.into())].into_iter().collect()), + Expression::Verb(Verb(builtin::r#else)), + // if-else + Expression::Boolean(Boolean(true)), + Expression::Quote([Expression::Integer(42.into())].into_iter().collect()), + Expression::Quote([Expression::Integer(24.into())].into_iter().collect()), + Expression::Verb(Verb(builtin::if_else)), + Expression::Boolean(Boolean(false)), + Expression::Quote([Expression::Integer(24.into())].into_iter().collect()), + Expression::Quote([Expression::Integer(42.into())].into_iter().collect()), + Expression::Verb(Verb(builtin::if_else)), + ] + .into_iter(), + ) .unwrap(); - assert!(matches!( + assert_eq!( evaluator.stack.as_slice(), - &[Expression::Integer(Integer(42))] - )); + &[ + Expression::Integer(Integer(42)), + Expression::Integer(Integer(42)), + Expression::Integer(Integer(42)), + Expression::Integer(Integer(42)) + ] + ); } #[test] - fn it_works4() { - let mut evaluator = Evaluator::default(); - evaluator - .evaluate([ - Expression::Quote( - [ - Expression::Integer(Integer(8)), - Expression::Integer(Integer(12)), - Expression::Verb(Verb(builtin::add)), - Expression::Verb(Verb(builtin::produce)), - ] - .into_iter() - .collect(), - ), - Expression::Verb(Verb(builtin::spawn)), - Expression::Quote( - [ - Expression::Integer(Integer(10)), - Expression::Integer(Integer(12)), - Expression::Verb(Verb(builtin::add)), - Expression::Verb(Verb(builtin::produce)), - ] - .into_iter() - .collect(), - ), - Expression::Verb(Verb(builtin::spawn)), - Expression::Verb(Verb(builtin::receive)), - Expression::Verb(Verb(builtin::swap)), - Expression::Verb(Verb(builtin::pop)), - Expression::Verb(Verb(builtin::swap)), - Expression::Verb(Verb(builtin::receive)), - Expression::Verb(Verb(builtin::swap)), - Expression::Verb(Verb(builtin::pop)), - Expression::Verb(Verb(builtin::add)), - ]) + fn word_and_grammar_are_aligned() { + let grammar_path = Path::new(env!("CARGO_WORKSPACE_DIR")).join("rat/src/grammar.pest"); + let word_path = Path::new(env!("CARGO_WORKSPACE_DIR")).join("rat/src/word.rs"); + let output = Command::new("md5sum") + .arg(&grammar_path) + .arg(&word_path) + .output() .unwrap(); - assert!(matches!( - evaluator.stack.as_slice(), - &[Expression::Integer(Integer(42))] - )); + assert_eq!( + &output.stdout, + format!( + "c55d29c3dd9298a203dfc945d9c238cc {}\nab5aa40ceddd0bc08fbf445cd9fe054e {}\n", + grammar_path.display(), + word_path.display() + ) + .as_bytes() + ); } } diff --git a/rat/src/parser.rs b/rat/src/parser.rs index 5e80f10..9e1a8d7 100644 --- a/rat/src/parser.rs +++ b/rat/src/parser.rs @@ -17,26 +17,25 @@ use std::{env, fs}; use crate::boolean::Boolean; use crate::decimal::Decimal; +use crate::dictionary::{Definition, Dictionary, Visibility}; use crate::expression::Expression; +use crate::identifier::{Identifier, OwnedIdentifier}; use crate::integer::Integer; -use crate::locution::{Locution, OwnedLocution}; use crate::quote::Quote; -use crate::signal::Signal; use crate::string::String; use crate::symbol::Symbol; -use crate::vocabulary::{Definition, Visibility, Vocabulary}; use crate::word::{OwnedWord, Word}; #[derive(Debug, Default)] pub struct Parser { - vocabulary: Vocabulary, - cache: HashMap>, + dictionary: Dictionary, + cache: HashMap>, } -impl From for Parser { - fn from(vocabulary: Vocabulary) -> Self { +impl From for Parser { + fn from(dictionary: Dictionary) -> Self { Self { - vocabulary, + dictionary, cache: Default::default(), } } @@ -49,13 +48,13 @@ impl Parser { pub fn with_prelude() -> Self { Self { - vocabulary: Vocabulary::with_prelude(), + dictionary: Dictionary::with_prelude(), cache: Default::default(), } } - pub fn vocabulary(&self) -> &Vocabulary { - &self.vocabulary + pub fn dictionary(&self) -> &Dictionary { + &self.dictionary } pub fn parse(&mut self, origin: Origin, source: &str) -> Result, ParseError> { @@ -64,11 +63,8 @@ impl Parser { for pair in pairs { match pair.as_rule() { - Rule::Definition => parse_definition(self, origin, pair)?, - Rule::Phrase => { - let phrase = parse_phrase(self, origin, pair)?; - program.extend(phrase.into_iter()); - } + Rule::Statement => parse_statement(self, origin, pair)?, + Rule::Expression => parse_expressions(self, origin, pair, &mut program)?, Rule::EOI => break, rule => unreachable!("unexpected rule: `{rule:?}`"), } @@ -80,14 +76,14 @@ impl Parser { fn import( &mut self, word: &Word, - locution: &Locution, + identifier: &Identifier, visibility: Visibility, ) -> Result<(), ImportError> { - if let Some(vocabulary) = self.cache.get(locution) { - self.vocabulary.define( + if let Some(dictionary) = self.cache.get(identifier) { + self.dictionary.define( word.to_owned(), - Definition::Vocabulary { - vocabulary: vocabulary.clone(), + Definition::Dictionary { + dictionary: dictionary.clone(), visibility, }, ); @@ -95,21 +91,21 @@ impl Parser { return Ok(()); } - let mut words = locution.words(); + let mut words = identifier.words(); - let mut path = if locution.as_str().starts_with("rat\\") { + let mut path = if identifier.as_str().starts_with("rat/") { words.next(); crate::home_dir().join("lib") } else { env::current_dir() - .map_err(|error| ImportError::new(format!("`{}` {}", locution, error)))? + .map_err(|error| ImportError::new(format!("`{}` {}", identifier, error)))? }; for word in words { if !path.is_dir() { return Err(ImportError::new(format!( "`{}` {} is not a directory", - locution, + identifier, path.display() ))); } @@ -122,29 +118,29 @@ impl Parser { if !path.is_file() { return Err(ImportError::new(format!( "`{}` {} is not a regular file", - locution, + identifier, path.display() ))); } let source = fs::read_to_string(&path) - .map_err(|e| ImportError::new(format!("`{}` {} {}", locution, path.display(), e)))?; + .map_err(|e| ImportError::new(format!("`{}` {} {}", identifier, path.display(), e)))?; let mut parser = Parser::with_prelude(); parser .parse(Origin::Path(path.as_path()), &source) - .map_err(|e| ImportError::new(format!("`{}`\n{}", locution, e)))?; + .map_err(|e| ImportError::new(format!("`{}`\n{}", identifier, e)))?; - parser.vocabulary.retain(|_, d| d.is_extern()); + parser.dictionary.retain(|_, d| d.is_extern()); - let vocabulary = Arc::new(parser.vocabulary); + let dictionary = Arc::new(parser.dictionary); - self.cache.insert(locution.to_owned(), vocabulary.clone()); - self.vocabulary.define( + self.cache.insert(identifier.to_owned(), dictionary.clone()); + self.dictionary.define( word.to_owned(), - Definition::Vocabulary { - vocabulary, + Definition::Dictionary { + dictionary, visibility, }, ); @@ -189,16 +185,17 @@ impl FromStr for OwnedWord { } } -impl FromStr for OwnedLocution { +impl FromStr for OwnedIdentifier { type Err = ParseError; fn from_str(s: &str) -> Result { check_token_boundary(s)?; - let mut pairs = Grammar::parse(Rule::Locution, s).map_err(with_origin(Origin::Unknown))?; + let mut pairs = + Grammar::parse(Rule::Identifier, s).map_err(with_origin(Origin::Unknown))?; assert_eq!(pairs.len(), 1); - parse_locution(Origin::Unknown, pairs.next().unwrap()).map(ToOwned::to_owned) + parse_identifier(Origin::Unknown, pairs.next().unwrap()).map(ToOwned::to_owned) } } @@ -258,19 +255,6 @@ impl FromStr for Quote { } } -impl FromStr for Signal { - type Err = ParseError; - - fn from_str(s: &str) -> Result { - check_token_boundary(s)?; - - let mut pairs = Grammar::parse(Rule::Signal, s).map_err(with_origin(Origin::Unknown))?; - assert_eq!(pairs.len(), 1); - - parse_signal(pairs.next().unwrap()) - } -} - impl FromStr for String { type Err = ParseError; @@ -372,102 +356,102 @@ fn parse_error(origin: Origin, span: PestSpan, message: impl Display) -> ParseEr .into() } -fn parse_definition(parser: &mut Parser, origin: Origin, pair: PestPair) -> Result<(), ParseError> { - assert_eq!(pair.as_rule(), Rule::Definition); - let span = pair.as_span(); - let mut pairs = pair.into_inner(); +fn parse_statement(parser: &mut Parser, origin: Origin, pair: PestPair) -> Result<(), ParseError> { + assert_eq!(pair.as_rule(), Rule::Statement); + let pair = pair.into_inner().next().unwrap(); - let visibility = match pairs.next().unwrap().as_rule() { - Rule::Division => Visibility::Extern, - Rule::Colon => Visibility::Intern, + match pair.as_rule() { + Rule::Define => parse_define(parser, origin, pair), + Rule::Import => parse_import(parser, origin, pair), rule => unreachable!("unexpected rule: `{rule:?}`"), + } +} + +fn parse_define(parser: &mut Parser, origin: Origin, pair: PestPair) -> Result<(), ParseError> { + assert_eq!(pair.as_rule(), Rule::Define); + let mut pairs = pair.into_inner().peekable(); + + let visibility = match pairs.peek().unwrap().as_rule() { + Rule::Export => { + pairs.next().unwrap(); + Visibility::Extern + } + _ => Visibility::Intern, }; let word = pairs.next().map(|p| parse_word(origin, p)).unwrap()?; + let mut expressions = Vec::new(); + pairs.try_for_each(|p| parse_expressions(parser, origin, p, &mut expressions))?; + + parser.dictionary.define( + word.to_owned(), + Definition::Body { + body: expressions.into(), + visibility, + }, + ); - match pairs.next().unwrap().as_rule() { - Rule::At => { - let locution = parse_locution(origin, pairs.next().unwrap())?; + Ok(()) +} - parser - .import(word, locution, visibility) - .map_err(|e| parse_error(origin, span, e)) +fn parse_import(parser: &mut Parser, origin: Origin, pair: PestPair) -> Result<(), ParseError> { + assert_eq!(pair.as_rule(), Rule::Import); + let span = pair.as_span(); + let mut pairs = pair.into_inner().peekable(); + + let visibility = match pairs.peek().unwrap().as_rule() { + Rule::Export => { + pairs.next().unwrap(); + Visibility::Extern } - Rule::LeftArrow => { - let phrase = parse_phrase(parser, origin, pairs.next().unwrap())?; + _ => Visibility::Intern, + }; - parser.vocabulary.define( - word.to_owned(), - Definition::Phrase { - phrase: phrase.into(), - visibility, - }, - ); + let word = pairs.next().map(|p| parse_word(origin, p)).unwrap()?; + let identifier = parse_identifier(origin, pairs.next().unwrap())?; - Ok(()) - } - rule => unreachable!("unexpected rule: `{rule:?}`"), - } + parser + .import(word, identifier, visibility) + .map_err(|e| parse_error(origin, span, e)) } fn parse_word<'a>(origin: Origin, pair: PestPair<'a>) -> Result<&'a Word, ParseError> { - assert!(matches!(pair.as_rule(), Rule::Word | Rule::_Word)); + assert_eq!(pair.as_rule(), Rule::Word); Word::try_from_literal(pair.as_str()).map_err(|e| parse_error(origin, pair.as_span(), e)) } -fn parse_locution<'a>(origin: Origin, pair: PestPair<'a>) -> Result<&'a Locution, ParseError> { - assert_eq!(pair.as_rule(), Rule::Locution); - Locution::try_from_literal(pair.as_str()).map_err(|e| parse_error(origin, pair.as_span(), e)) +fn parse_identifier<'a>(origin: Origin, pair: PestPair<'a>) -> Result<&'a Identifier, ParseError> { + assert_eq!(pair.as_rule(), Rule::Identifier); + Identifier::try_from_literal(pair.as_str()).map_err(|e| parse_error(origin, pair.as_span(), e)) } -fn parse_expression( +fn parse_expressions( parser: &mut Parser, origin: Origin, pair: PestPair, -) -> Result { + buf: &mut Vec, +) -> Result<(), ParseError> { assert_eq!(pair.as_rule(), Rule::Expression); let pair = pair.into_inner().next().unwrap(); match pair.as_rule() { - Rule::Boolean => parse_boolean(origin, pair).map(Expression::Boolean), - Rule::Decimal => parse_decimal(origin, pair).map(Expression::Decimal), - Rule::Integer => parse_integer(origin, pair).map(Expression::Integer), - Rule::Quote => parse_quote(parser, origin, pair).map(Expression::Quote), - Rule::Signal => parse_signal(pair).map(Expression::Signal), - Rule::String => parse_string(origin, pair).map(Expression::String), - Rule::Symbol => parse_symbol(origin, pair).map(Expression::Symbol), - rule => unreachable!("unexpected rule: `{rule:?}`"), - } -} - -fn parse_phrase( - parser: &mut Parser, - origin: Origin, - pair: PestPair, -) -> Result, ParseError> { - assert_eq!(pair.as_rule(), Rule::Phrase); - let mut phrase = Vec::new(); - - for pair in pair.into_inner() { - match pair.as_rule() { - Rule::Locution => { - let span = pair.as_span(); - let locution = parse_locution(origin, pair)?; - parser - .vocabulary - .lookup(locution) - .map(|expressions| phrase.extend_from_slice(expressions)) - .ok_or_else(|| parse_error(origin, span, undefined_locution(locution)))?; - } - Rule::Expression => { - let expression = parse_expression(parser, origin, pair)?; - phrase.push(expression); - } - rule => unreachable!("unexpected rule: `{rule:?}`"), + Rule::Boolean => parse_boolean(origin, pair).map(|e| buf.push(Expression::Boolean(e))), + Rule::Decimal => parse_decimal(origin, pair).map(|e| buf.push(Expression::Decimal(e))), + Rule::Integer => parse_integer(origin, pair).map(|e| buf.push(Expression::Integer(e))), + Rule::Quote => parse_quote(parser, origin, pair).map(|e| buf.push(Expression::Quote(e))), + Rule::String => parse_string(origin, pair).map(|e| buf.push(Expression::String(e))), + Rule::Symbol => parse_symbol(origin, pair).map(|e| buf.push(Expression::Symbol(e))), + Rule::Identifier => { + let span = pair.as_span(); + let identifier = parse_identifier(origin, pair)?; + parser + .dictionary + .lookup(identifier) + .map(|expressions| buf.extend_from_slice(expressions)) + .ok_or_else(|| parse_error(origin, span, undefined_identifier(identifier))) } + rule => unreachable!("unexpected rule: `{rule:?}`"), } - - Ok(phrase) } fn parse_boolean(_: Origin, pair: PestPair) -> Result { @@ -503,26 +487,11 @@ fn parse_integer(origin: Origin, pair: PestPair) -> Result fn parse_quote(parser: &mut Parser, origin: Origin, pair: PestPair) -> Result { assert_eq!(pair.as_rule(), Rule::Quote); - let mut quote = Quote::new(); - - for pair in pair.into_inner() { - match pair.as_rule() { - Rule::Phrase => { - let phrase = parse_phrase(parser, origin, pair)?; - quote.extend(phrase.into_iter()); - } - Rule::LeftSquareBracket => (), - Rule::RightSquareBracket => break, - rule => unreachable!("unexpected rule: `{rule:?}`"), - } - } - - Ok(quote) -} + let mut expressions = Vec::new(); + pair.into_inner() + .try_for_each(|pair| parse_expressions(parser, origin, pair, &mut expressions))?; -fn parse_signal(pair: PestPair) -> Result { - assert_eq!(pair.as_rule(), Rule::Signal); - Ok(unsafe { Signal::new(&pair.as_str()[1..]) }) + Ok(expressions.into_iter().collect()) } fn parse_string(origin: Origin, pair: PestPair) -> Result { @@ -534,16 +503,19 @@ fn parse_string(origin: Origin, pair: PestPair) -> Result { fn parse_symbol(origin: Origin, pair: PestPair) -> Result { assert_eq!(pair.as_rule(), Rule::Symbol); - pair.into_inner() - .map(|p| parse_unicode_scalar_value(origin, p)) - .collect() + let pair = pair.into_inner().next().unwrap(); + match pair.as_rule() { + Rule::Identifier => Ok(pair.as_str().chars().collect()), + Rule::String => pair + .into_inner() + .map(|p| parse_unicode_scalar_value(origin, p)) + .collect(), + rule => unreachable!("unexpected rule: `{rule:?}`"), + } } fn parse_unicode_scalar_value(origin: Origin, pair: PestPair) -> Result { - assert!( - Rule::StringUnicodeScalarValue == pair.as_rule() - || Rule::SymbolUnicodeScalarValue == pair.as_rule() - ); + assert_eq!(pair.as_rule(), Rule::UnicodeScalarValue); match pair.as_str() { "\\n" => Ok('\n'), @@ -592,8 +564,8 @@ fn check_token_boundary(s: &str) -> Result<(), ParseError> { Ok(()) } -fn undefined_locution(locution: &Locution) -> StdString { - format!("undefined locution: `{locution}`") +fn undefined_identifier(identifier: &Identifier) -> StdString { + format!("undefined identifier: `{identifier}`") } #[cfg(test)] @@ -603,10 +575,9 @@ mod test { use crate::boolean::Boolean; use crate::decimal::Decimal; use crate::expression::Expression; + use crate::identifier::{Identifier, OwnedIdentifier}; use crate::integer::Integer; - use crate::locution::{Locution, OwnedLocution}; use crate::quote::Quote; - use crate::signal::{self, Signal}; use crate::string::String; use crate::symbol::Symbol; use crate::word::{OwnedWord, Word}; @@ -622,14 +593,14 @@ mod test { } #[test] - fn parse_locution() { - assert!(Locution::try_from_literal(" math\\abs").is_err()); - assert!(Locution::try_from_literal("math\\abs ").is_err()); - assert!(Locution::try_from_literal("math\\").is_err()); - assert!(Locution::try_from_literal("\\abs").is_err()); + fn parse_identifier() { + assert!(Identifier::try_from_literal(" math/abs").is_err()); + assert!(Identifier::try_from_literal("math/abs ").is_err()); + assert!(Identifier::try_from_literal("math/").is_err()); + assert!(Identifier::try_from_literal("/abs").is_err()); assert_eq!( - Locution::try_from_literal("math\\abs").unwrap(), - "math\\abs".parse::().unwrap().borrow() + Identifier::try_from_literal("math/abs").unwrap(), + "math/abs".parse::().unwrap().borrow() ); } @@ -660,10 +631,10 @@ mod test { #[test] fn parse_quote() { - assert!(" [⊥ ⊤ 42 3.14 $IoError 'hello' \"world\" []]" + assert!(" [⊥ ⊤ 42 3.14 $hello \"world\" []]" .parse::() .is_err()); - assert!("[⊥ ⊤ 42 3.14 $IoError 'hello' \"world\" []] " + assert!("[⊥ ⊤ 42 3.14 $hello \"world\" []] " .parse::() .is_err()); assert_eq!( @@ -672,29 +643,18 @@ mod test { Expression::Boolean(Boolean(true)), Expression::Integer(Integer(42)), Expression::Decimal(Decimal(3.14)), - Expression::Signal(unsafe { Signal::new("IoError") }), Expression::Symbol(Symbol::new("hello")), Expression::String(String::from_utf8("world")), Expression::Quote(Quote::default()), ] .into_iter() .collect::(), - "[⊥ ⊤ 42 3.14 $IoError 'hello' \"world\" []]" + "[⊥ ⊤ 42 3.14 $hello \"world\" []]" .parse::() .unwrap() ); } - #[test] - fn parse_signal() { - assert!(" $StackUnderflow".parse::().is_err()); - assert!("$StackUnderflow ".parse::().is_err()); - assert_eq!( - signal::stack_underflow(), - "$StackUnderflow".parse().unwrap() - ); - } - #[test] fn parse_string() { assert!(" \"hello\"".parse::().is_err()); @@ -714,14 +674,22 @@ mod test { #[test] fn parse_symbol() { - assert!(" 'hello'".parse::().is_err()); - assert!("'hello' ".parse::().is_err()); + assert!(" $hello".parse::().is_err()); + assert!("$hello ".parse::().is_err()); + assert!(" $\"hello\"".parse::().is_err()); + assert!("$\"hello\" ".parse::().is_err()); assert_eq!( Symbol::from_iter("hello".chars()), - "'hello'".parse().unwrap() + "$hello".parse().unwrap() ); - assert_eq!(Symbol::from_iter("a\"a".chars()), "'a\"a'".parse().unwrap()); - assert_eq!(Symbol::from_iter("a'a".chars()), "'a\\'a'".parse().unwrap()); + assert_eq!( + Symbol::from_iter("a\tstring".chars()), + "$\"a\tstring\"".parse().unwrap() + ); + assert_eq!( + Symbol::from_iter("a\tstring".chars()), + r#"$"a\tstring""#.parse().unwrap() + ); } } diff --git a/rat/src/quote.rs b/rat/src/quote.rs index 7a864ba..8319b62 100644 --- a/rat/src/quote.rs +++ b/rat/src/quote.rs @@ -8,7 +8,7 @@ use std::fmt::{Debug, Display}; use std::ops::Deref; use std::sync::Arc; -use crate::effect::Effect; +use crate::error::RuntimeError; use crate::evaluate::Evaluate; use crate::evaluator::Evaluator; use crate::expression::Expression; @@ -31,6 +31,14 @@ impl FromIterator for Quote { } } +impl From> for Quote { + fn from(value: Vec) -> Self { + Self { + inner: Some(Arc::new(value)), + } + } +} + impl From<[Expression; N]> for Quote { fn from(value: [Expression; N]) -> Self { Self::from_iter(value) @@ -68,6 +76,16 @@ impl Quote { .map(|v| v.remove(index)) } + pub fn split(&mut self, at: usize) -> Quote { + self.inner + .as_mut() + .map(Arc::make_mut) + .map(|v| Self { + inner: Some(Arc::new(v.split_off(at))), + }) + .unwrap() + } + pub fn as_slice(&self) -> &[Expression] { self.inner.as_deref().map(Deref::deref).unwrap_or(&[]) } @@ -83,11 +101,11 @@ impl Quote { } } -impl Evaluate<&mut Evaluator> for Quote { - type Output = Result<(), Effect>; +impl Evaluate for &mut Evaluator { + type Output = Result<(), RuntimeError>; - fn evaluate(self, evaluator: &mut Evaluator) -> Self::Output { - evaluator.stack.push(Expression::Quote(self)); + fn evaluate(self, value: Quote) -> Self::Output { + self.stack.push(Expression::Quote(value)); Ok(()) } } diff --git a/rat/src/signal.rs b/rat/src/signal.rs deleted file mode 100644 index 2c4bbfa..0000000 --- a/rat/src/signal.rs +++ /dev/null @@ -1,133 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -use std::error::Error; -use std::fmt::{Debug, Display}; - -use ustr::Ustr; - -use crate::effect::Effect; -use crate::evaluate::Evaluate; -use crate::evaluator::Evaluator; -use crate::expression::Expression; - -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Signal(Ustr); - -impl Signal { - // FIXME - /** - * SAFETY - * This rat does not validate input string so it can be called with invalid signal literals. - */ - pub(crate) unsafe fn new(literal: &str) -> Self { - Self(Ustr::from(literal)) - } - - pub fn try_trom_literal(literal: &str) -> Result { - if literal.len() > 1 - && literal - .strip_prefix('$') - .map(|s| s.bytes().all(|byte| byte.is_ascii_alphabetic())) - .unwrap_or(false) - { - return Ok(unsafe { Self::new(&literal[1..]) }); - } - - Err(InvalidSignalLiteral) - } -} - -pub fn divide_by_zero() -> Signal { - unsafe { Signal::new("DivideByZero") } -} - -pub fn stack_underflow() -> Signal { - unsafe { Signal::new("StackUnderflow") } -} - -pub fn out_of_range() -> Signal { - unsafe { Signal::new("OutOfRange") } -} - -pub fn type_error() -> Signal { - unsafe { Signal::new("TypeError") } -} - -pub fn io_error() -> Signal { - unsafe { Signal::new("IOError") } -} - -impl Evaluate<&mut Evaluator> for Signal { - type Output = Result<(), Effect>; - - fn evaluate(self, evaluator: &mut Evaluator) -> Self::Output { - evaluator.stack.push(Expression::Signal(self)); - Ok(()) - } -} - -impl Display for Signal { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Self(s) = self; - write!(f, "${s}") - } -} - -impl Debug for Signal { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - Display::fmt(self, f) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InvalidSignalLiteral; - -impl Display for InvalidSignalLiteral { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - Debug::fmt(self, f) - } -} - -impl Error for InvalidSignalLiteral {} - -#[cfg(test)] -mod test { - use std::collections::hash_map::DefaultHasher; - use std::hash::{BuildHasher, BuildHasherDefault}; - - use super::*; - - #[test] - fn identity() { - let sut1 = stack_underflow(); - let sut2 = stack_underflow(); - let sut3 = io_error(); - - assert_eq!(sut1, sut2); - assert_eq!(sut1, sut2); - - assert_ne!(sut1, sut3); - assert_ne!(sut1, sut3); - - assert_ne!(sut2, sut3); - assert_ne!(sut2, sut3); - - let hash_builder = BuildHasherDefault::::default(); - let h1 = hash_builder.hash_one(sut1); - let h2 = hash_builder.hash_one(sut2); - assert_eq!(h1, h2); - } - - #[test] - fn try_from_literal() { - assert_eq!(io_error(), Signal::try_trom_literal("$IOError").unwrap()); - assert!(Signal::try_trom_literal("$").is_err()); - assert!(Signal::try_trom_literal("$x!").is_err()); - assert!(Signal::try_trom_literal("$x42").is_err()); - assert!(Signal::try_trom_literal("IoError").is_err()); - } -} diff --git a/rat/src/string.rs b/rat/src/string.rs index 75008d3..b906754 100644 --- a/rat/src/string.rs +++ b/rat/src/string.rs @@ -8,7 +8,7 @@ use std::fmt::{Debug, Display}; use std::ops::Deref; use std::sync::Arc; -use crate::effect::Effect; +use crate::error::RuntimeError; use crate::evaluate::Evaluate; use crate::evaluator::Evaluator; use crate::expression::Expression; @@ -68,11 +68,11 @@ impl String { } } -impl Evaluate<&mut Evaluator> for String { - type Output = Result<(), Effect>; +impl Evaluate for &mut Evaluator { + type Output = Result<(), RuntimeError>; - fn evaluate(self, evaluator: &mut Evaluator) -> Self::Output { - evaluator.stack.push(Expression::String(self)); + fn evaluate(self, value: String) -> Self::Output { + self.stack.push(Expression::String(value)); Ok(()) } } diff --git a/rat/src/symbol.rs b/rat/src/symbol.rs index 982831f..16b94ce 100644 --- a/rat/src/symbol.rs +++ b/rat/src/symbol.rs @@ -6,14 +6,14 @@ use ustr::Ustr; -use crate::effect::Effect; +use crate::error::RuntimeError; use crate::evaluate::Evaluate; use crate::evaluator::Evaluator; use crate::expression::Expression; use std::fmt::{Debug, Display}; -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Symbol(Ustr); impl FromIterator for Symbol { @@ -27,17 +27,37 @@ impl Symbol { Symbol(Ustr::from(s)) } + pub fn divide_by_zero() -> Symbol { + Symbol::new("DivideByZero") + } + + pub fn stack_underflow() -> Symbol { + Symbol::new("StackUnderflow") + } + + pub fn out_of_range() -> Symbol { + Symbol::new("OutOfRange") + } + + pub fn type_error() -> Symbol { + Symbol::new("TypeError") + } + + pub fn io_error() -> Symbol { + Symbol::new("IOError") + } + #[inline] pub fn as_str(&self) -> &str { &self.0 } } -impl Evaluate<&mut Evaluator> for Symbol { - type Output = Result<(), Effect>; +impl Evaluate for &mut Evaluator { + type Output = Result<(), RuntimeError>; - fn evaluate(self, evaluator: &mut Evaluator) -> Self::Output { - evaluator.stack.push(Expression::Symbol(self)); + fn evaluate(self, value: Symbol) -> Self::Output { + self.stack.push(Expression::Symbol(value)); Ok(()) } } @@ -52,7 +72,7 @@ impl Display for Symbol { impl Debug for Symbol { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self(s) = self; - write!(f, "'{}'", s) + write!(f, "$\"{}\"", s) } } diff --git a/rat/src/verb.rs b/rat/src/verb.rs index c49d796..51497c1 100644 --- a/rat/src/verb.rs +++ b/rat/src/verb.rs @@ -6,19 +6,18 @@ use std::fmt::{Debug, Display}; -use crate::effect::Effect; +use crate::error::RuntimeError; use crate::evaluate::Evaluate; use crate::evaluator::Evaluator; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Verb(pub fn(&mut Evaluator) -> Result<(), Effect>); +pub struct Verb(pub fn(&mut Evaluator) -> Result<(), RuntimeError>); -impl Evaluate<&mut Evaluator> for Verb { - type Output = Result<(), Effect>; +impl Evaluate for &mut Evaluator { + type Output = Result<(), RuntimeError>; - fn evaluate(self, evaluator: &mut Evaluator) -> Self::Output { - let Self(verb) = self; - verb(evaluator) + fn evaluate(self, Verb(verb): Verb) -> Self::Output { + verb(self) } } diff --git a/rat/src/word.rs b/rat/src/word.rs index 1295739..87f447e 100644 --- a/rat/src/word.rs +++ b/rat/src/word.rs @@ -25,39 +25,34 @@ impl ToOwned for Word { impl Word { pub(crate) const fn is_valid(bytes: &[u8], start: usize, end: usize) -> bool { - if !(start < end && start < bytes.len() && end <= bytes.len()) { + if !(start < end + && start < bytes.len() + && end <= bytes.len() + && bytes[start].is_ascii_alphabetic()) + { return false; } - let mut i = start; + let mut i = start + 1; while i < end { let c = bytes[i]; - if !(b'!' == c - || b'%' == c - || b'&' == c - || b'*' == c - || b'+' == c - || b'-' == c - || b'/' == c - || b'<' == c - || b'=' == c - || b'>' == c - || b'?' == c - || (b'A' <= c && b'Z' >= c) - || b'^' == c - || b'_' == c - || (b'a' <= c && b'z' >= c) - || b'|' == c - || b'~' == c) - { - return false; + if !(c.is_ascii_alphanumeric() || c == b'-' || c == b'_') { + break; } i += 1; } - true + if i < end { + let c = bytes[i]; + + if c == b'!' || c == b'?' { + i += 1; + } + } + + end == i } /// Whitespaces are not allowed in `literal`.