forked from AbleScript/ablescript
Compare commits
394 commits
Author | SHA1 | Date | |
---|---|---|---|
Erin | a21fe4de57 | ||
Erin | 7aad9b83f0 | ||
Erin | 1bcd5ce9b0 | ||
Erin | 89c4fe68d2 | ||
Erin | 1d91e68b05 | ||
Erin | acd7cad29e | ||
Erin | 4dfddc2cdb | ||
Erin | e936585112 | ||
Erin | 61fbd3ee28 | ||
Erin | 2690c65c21 | ||
Erin | fd4845ac8d | ||
Erin | 9803a393d4 | ||
Erin | a9fdff0663 | ||
Erin | d004955c4e | ||
Erin | e674052942 | ||
Erin | cbb7152890 | ||
Erin | 1781a66c3a | ||
Erin | 18943f184d | ||
Erin | 167a5077df | ||
Erin | 5c0e89e38e | ||
Erin | f2d3561508 | ||
Erin | 63af15cc94 | ||
Erin | 24c5ada994 | ||
Erin | 83b25b2b89 | ||
Erin | e883010580 | ||
Erin | 472b41cf16 | ||
Erin | 844ccd731a | ||
Erin | 49e36b0a21 | ||
Erin | 43c9293e4f | ||
Erin | 7140082ff6 | ||
Erin | 84f54f12d6 | ||
Erin | 2c7596c871 | ||
Erin | c0a2857122 | ||
Erin | bb3de6db14 | ||
Alex Bethel | 507f700c44 | ||
Alex Bethel | ea802fbdf9 | ||
Erin | 7ec0b0b39d | ||
Erin | 3d745e6082 | ||
Erin | e4114ed2e8 | ||
Erin | ed1b3bfb43 | ||
Erin | d97d940d23 | ||
Erin | bda92cc2f7 | ||
Erin | aa29a1fc86 | ||
Erin | 86dfc6d1f7 | ||
Erin | 28a5415207 | ||
Erin | 766eb799c9 | ||
Erin | edb03dec94 | ||
Erin | 7b5a9aed89 | ||
Erin | 947c2552b1 | ||
Erin | 6ad0ab5e29 | ||
Erin | fd97655e04 | ||
Erin | 1a61c66e10 | ||
Erin | ab76516624 | ||
Erin | b76e890fb7 | ||
Erin | 85226fbfbb | ||
able | e895ef775f | ||
Erin | 19744aa63a | ||
Erin | c6f4aaef24 | ||
Erin | 90f8137b0d | ||
Erin | 842c121901 | ||
Erin | c51a332f60 | ||
Erin | db3b56d798 | ||
Erin | 5ccc181f47 | ||
Erin | 95e790d8e8 | ||
Erin | 3916b20b59 | ||
Erin | 5aada3c09e | ||
Erin | 98b72b14e2 | ||
Erin | 60fb95cb13 | ||
Erin | ee6105dd91 | ||
Erin | f2340dd493 | ||
Erin | ea13c79fc4 | ||
Erin | 6dd32793bb | ||
Erin | 0eb2698242 | ||
Erin | eac86b5e0b | ||
Erin | ba019d3b7e | ||
Erin | c1bc0e8a96 | ||
Erin | 35caa085f8 | ||
Erin | de3b6dc048 | ||
Erin | cee4a80584 | ||
Erin | 60505735c9 | ||
Erin | 89326fd344 | ||
Alex Bethel | 19fa04f979 | ||
Erin | 45007e7d2b | ||
Erin | ec48508ba7 | ||
Erin | 0b4db65ca3 | ||
Erin | eae118a78e | ||
Erin | 3b829d7e55 | ||
Erin | b46c178692 | ||
Erin | 38fe23d2b5 | ||
Erin | db7fc4d592 | ||
Erin | d890d7cfff | ||
Erin | a3d2922f7e | ||
Erin | c1c8f17c8d | ||
Erin | 6f663839a7 | ||
Erin | 86e1391ea2 | ||
Erin | 5a5403a8df | ||
Erin | c567341775 | ||
Erin | 070f3b2f0e | ||
Erin | 9d6f5740b1 | ||
Erin | 4f56411375 | ||
Erin | 5f842dc8d9 | ||
Erin | 0ec54ecc51 | ||
Erin | 3f4cc58b87 | ||
Erin | b975bc52d0 | ||
Erin | 567665a314 | ||
Erin | a23dac3ce0 | ||
able | 81f713a5e2 | ||
Erin | cb6fcceb79 | ||
Erin | f55f9e0512 | ||
Erin | d1f18be279 | ||
Erin | 0615091b3c | ||
Erin | 43ae772894 | ||
Erin | edce9dcc91 | ||
Erin | ca4a9aba4c | ||
Erin | 0134448472 | ||
Erin | 43c51f2c96 | ||
Erin | 0d00aba6ce | ||
Erin | e79c98f2f6 | ||
Erin | 5d29c8cf87 | ||
Erin | 8c867c7899 | ||
Erin | 5b320259f6 | ||
Erin | a9a9cc71e1 | ||
Erin | 3338ec94c2 | ||
Erin | d634692f12 | ||
Erin | a1f6d1ae5f | ||
Erin | 06ec25e717 | ||
Erin | 405f88e394 | ||
Erin | 09617b2a89 | ||
Erin | 68e5e7cf41 | ||
Erin | 7cf9433462 | ||
Erin | 2191e126f0 | ||
Erin | 8e55872f10 | ||
Alex Bethel | 1b84093eab | ||
Alex Bethel | 912437f10c | ||
Erin | 88ef782cea | ||
Alex Bethel | 7a34105c3e | ||
Alex Bethel | 0451e71104 | ||
Erin | 4b5ad3c462 | ||
Alex Bethel | b6413f6219 | ||
Alex Bethel | 20b1408179 | ||
Erin | 573df02ba6 | ||
Erin | c1471bcdb7 | ||
Erin | 8302324d0f | ||
Erin | 6c836125b3 | ||
Alex Bethel | cf289a82d7 | ||
Alex Bethel | 57939a87b0 | ||
Erin | db95d2f718 | ||
Erin | 872a16578f | ||
Erin | a9d9c7aa27 | ||
Erin | 6430387119 | ||
Erin | c92cb7e818 | ||
Erin | 6d48dca547 | ||
Erin | 61ee6b3597 | ||
Erin | 3bfe600103 | ||
Alex Bethel | 0b703173f8 | ||
Erin | 3c98caf2d6 | ||
Alex Bethel | 3cdca1666a | ||
Erin | 20c6b4c7cf | ||
Alex Bethel | 3044ef91e3 | ||
Alex Bethel | ffcd0000c2 | ||
Erin | f5e4be4d67 | ||
Erin | 904d647031 | ||
Erin | 6889c3f3b2 | ||
Erin | 65bb29d401 | ||
Erin | 3a59501217 | ||
Alex Bethel | 6ee12a7c91 | ||
Alex Bethel | 2f31e7c3d5 | ||
Alex Bethel | 6cd2b8bd44 | ||
Alex Bethel | 711b8e1c03 | ||
Alex Bethel | 9727a9b577 | ||
Alex Bethel | 6849f7f296 | ||
Erin | 5d6ac150ab | ||
Alex Bethel | 5634a8ad2b | ||
Alex Bethel | 5db6eebdc0 | ||
Erin | ae1416e329 | ||
Alex Bethel | 706d39e860 | ||
Erin | bcd446c5d1 | ||
Erin | 0f198a2554 | ||
Erin | 3e8b80af2a | ||
6779d7c898 | |||
Erin | 610ba7fc40 | ||
Alex Bethel | 31fe3f53be | ||
Erin | cdf4c5a308 | ||
Erin | 4c6dd311a7 | ||
Alex Bethel | 6eb97c6032 | ||
Erin | e47e739e02 | ||
Erin | 71c5738a6e | ||
Alex Bethel | e8349bf0d9 | ||
Erin | a288db985d | ||
Erin | 4d1e85a295 | ||
Erin | 113554428f | ||
Erin | 82c09e5b1b | ||
Erin | 14e9eb5c6b | ||
Alex Bethel | 6f8a49b711 | ||
Erin | 82239be160 | ||
Erin | 92f2c62d59 | ||
Erin | 49ba66de07 | ||
Erin | 58f0645b54 | ||
Erin | 31296ec0b8 | ||
Erin | 7a275556c7 | ||
Erin | 975655a96e | ||
Erin | 2f5b954b7c | ||
Erin | 3ce85a11ee | ||
Erin | a8c603164c | ||
Erin | e30eef32b5 | ||
Erin | 1c8722ba99 | ||
Erin | c66616ee2c | ||
Erin | e8f08a90d5 | ||
Erin | 4c74e3bef3 | ||
Erin | 8b57cbe5a5 | ||
Erin | 2c36b2a5f7 | ||
Alex Bethel | 4c3d44dfd8 | ||
92134d68d5 | |||
Erin | 3297605bf2 | ||
b3b2d1a8ef | |||
Erin | c70e77f717 | ||
Erin | aa854a067b | ||
Alex Bethel | a2d240f471 | ||
Alex Bethel | fd9054d3d3 | ||
Erin | 3686b3b608 | ||
Erin | 83a4f67121 | ||
Erin | 3df3cbbf6d | ||
Erin | 0c000337d2 | ||
Erin | 25a3c14145 | ||
Erin | b451503a27 | ||
Erin | e3cfaaf32c | ||
Erin | 241f2261d1 | ||
Erin | eb91aa96fa | ||
Alex Bethel | 3cd1115f5d | ||
Alex Bethel | 0bca9ff79c | ||
Erin | a6f86b7e26 | ||
Erin | 603a244b78 | ||
Erin | 0d497bccdd | ||
Erin | b53cc1e768 | ||
Erin | bf2e73a5d1 | ||
Erin | e7bd81e12a | ||
Erin | ab1515cdb0 | ||
Erin | e847a76c6d | ||
Alex Bethel | 282a9a6a07 | ||
Alex Bethel | 0b0a8942de | ||
Alex Bethel | dcc0c0b00f | ||
Alex Bethel | 501d182fb4 | ||
Alex Bethel | 50f9bfa4c6 | ||
Alex Bethel | 821eeae322 | ||
Alex Bethel | d3242484fc | ||
Erin | 0c795741e8 | ||
Alex Bethel | 141220fb38 | ||
Alex Bethel | 3d9f847862 | ||
Alex Bethel | a225105f09 | ||
Alex Bethel | 8818536717 | ||
Alex Bethel | bc7427014d | ||
Alex Bethel | 1e8314b1a1 | ||
Alex Bethel | c1cc54be75 | ||
Alex Bethel | eee173060c | ||
Erin | 3edbca51d5 | ||
Alex Bethel | 430f0c2b90 | ||
Alex Bethel | 4011997e60 | ||
Alex Bethel | cdb4a22443 | ||
Alex Bethel | 098137082e | ||
Alex Bethel | 2e5799d9e6 | ||
Alex Bethel | 9965d97713 | ||
Alex Bethel | d1064802ec | ||
Alex Bethel | 8aa6fc502a | ||
Alex Bethel | 7bf3cc6f5d | ||
Alex Bethel | ae0955209c | ||
Alex Bethel | 66d4f4f82d | ||
Alex Bethel | 4b0aa1602b | ||
Alex Bethel | d80c138735 | ||
Alex Bethel | d59b75fdb3 | ||
Alex Bethel | 75938f27c0 | ||
c2de1f6411 | |||
aa72945010 | |||
26df745dee | |||
953128887b | |||
b526d5c575 | |||
712e6cef67 | |||
72e48d1b86 | |||
327466cddc | |||
Erin | 93289e02f1 | ||
Erin | 232595c1be | ||
Erin | f62bab110f | ||
227fa8e35b | |||
fa0d3968f5 | |||
3646c5e32e | |||
286a12b28e | |||
855383faac | |||
Alex Bethel | da9ff64486 | ||
Alex Bethel | 92e0a22e24 | ||
Alex Bethel | 976518a251 | ||
Erin | ff04c34cf6 | ||
Erin | 183c584e96 | ||
Erin | 3d7c89634e | ||
Erin | 3bcef28843 | ||
Erin | 711ad27e00 | ||
Alex Bethel | 41316f5696 | ||
Erin | 6f16d2594b | ||
Erin | 851f04c653 | ||
dd28ff985f | |||
77749bc32b | |||
Alex Bethel | 7d7c1e4a1b | ||
Alex Bethel | dc5ef63fef | ||
Alex Bethel | a0c1f93392 | ||
Alex Bethel | ce20611c07 | ||
Alex Bethel | d8f462e0b5 | ||
Alex Bethel | addda8fc87 | ||
Alex Bethel | 426aee6a2b | ||
Erin | 903b1a3809 | ||
Erin | 8bd61a4a65 | ||
Erin | 1f78cb0ee1 | ||
Erin | 902d8b914c | ||
Erin | f893e24aed | ||
Erin | 2084ebeed1 | ||
Erin | b7464f6ad3 | ||
Erin | b6bce72b94 | ||
Erin | 97f0271534 | ||
Erin | a5a048728c | ||
Erin | b04bd77672 | ||
Erin | 93065e7dc9 | ||
Erin | 6fd95f3cc2 | ||
Erin | afe6588c05 | ||
Erin | 2c15f3dc17 | ||
Alex Bethel | 4bda25c9f3 | ||
Alex Bethel | 6c2d5cc84a | ||
5d230431e0 | |||
Alex Bethel | 528de718dc | ||
Alex Bethel | ce02aebd91 | ||
Alex Bethel | 326d0511e7 | ||
Alex Bethel | 07195d4cf6 | ||
Alex Bethel | acfd81ead2 | ||
Alex Bethel | 344a11084e | ||
90773eea3d | |||
Alex Bethel | 099b9e23d6 | ||
Alex Bethel | 585bf2e19f | ||
7b21eaf48a | |||
d52c775ed1 | |||
e02edbbd16 | |||
18973333b0 | |||
Alex Bethel | c906366e3f | ||
ec81ead3ea | |||
669f379700 | |||
109c77eeb2 | |||
Able | c27837a807 | ||
84016d3dcd | |||
b625a71711 | |||
ea211fc3b0 | |||
eccc00ff81 | |||
0ad680cadd | |||
1b195dc955 | |||
7b5ae34bfd | |||
c0b7bd2f34 | |||
396650ae95 | |||
able | 87139c4448 | ||
able | 9b7367353d | ||
able | 57d86b9f92 | ||
able | bdf27c83da | ||
143ae613d8 | |||
Erin | 72cd540728 | ||
Erin | 798c1807db | ||
Erin | 30eedbc854 | ||
Erin | 29bf01935c | ||
e8a2b87c6e | |||
Erin | 8c6f415ac9 | ||
e080fc65e3 | |||
Erin | 051f6e781f | ||
Erin | ecce080378 | ||
Erin | 5a8dd5051f | ||
Erin | c90d242b0f | ||
Erin | 18a1343e11 | ||
Erin | 3794fd3c8f | ||
Erin | eef7ec16fa | ||
Erin | ca60f818eb | ||
Erin | 17a32a8df7 | ||
Erin | e45afeac5e | ||
b4d3f0c9b6 | |||
Erin | f3779deeb5 | ||
Erin | 3f9e6b72cc | ||
Erin | 39a8bf6a54 | ||
Erin | d2160a3a4a | ||
Erin | 3b8ce34c2b | ||
Erin | cc4ec803c4 | ||
19762e940a | |||
Erin | e020fc4267 | ||
Erin | 7e0c881130 | ||
Erin | 48dd930872 | ||
able | bc1eb42af4 | ||
83c8595a8f | |||
Erin | 1db4bd74cf | ||
Erin | 81ff84ad73 | ||
able | d13d72ad89 | ||
a26058587a | |||
Erin | 968a65b1fb | ||
able | 1abd802b58 | ||
4235106434 | |||
Erin | 0d35f7f0a5 |
521
Cargo.lock
generated
521
Cargo.lock
generated
|
@ -4,7 +4,7 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ablescript"
|
name = "ablescript"
|
||||||
version = "0.3.0"
|
version = "0.5.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"logos",
|
"logos",
|
||||||
"rand",
|
"rand",
|
||||||
|
@ -12,7 +12,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ablescript_cli"
|
name = "ablescript_cli"
|
||||||
version = "0.3.0"
|
version = "0.5.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ablescript",
|
"ablescript",
|
||||||
"clap",
|
"clap",
|
||||||
|
@ -20,27 +20,58 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "anstream"
|
||||||
version = "0.2.14"
|
version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"anstyle",
|
||||||
"libc",
|
"anstyle-parse",
|
||||||
"winapi",
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"utf8parse",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "anstyle"
|
||||||
version = "1.1.0"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "beef"
|
name = "beef"
|
||||||
version = "0.5.1"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bed554bd50246729a1ec158d08aa3235d1b69d94ad120ebe187e28894787e736"
|
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
|
@ -49,10 +80,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "bitflags"
|
||||||
version = "1.0.73"
|
version = "2.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
|
@ -62,30 +93,61 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "3.1.8"
|
version = "4.4.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c"
|
checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"clap_builder",
|
||||||
"bitflags",
|
"clap_derive",
|
||||||
"indexmap",
|
|
||||||
"os_str_bytes",
|
|
||||||
"strsim",
|
|
||||||
"termcolor",
|
|
||||||
"textwrap",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clipboard-win"
|
name = "clap_builder"
|
||||||
version = "4.4.1"
|
version = "4.4.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2f3e1238132dc01f081e1cbb9dace14e5ef4c3a51ee244bd982275fb514605db"
|
checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clipboard-win"
|
||||||
|
version = "4.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"error-code",
|
"error-code",
|
||||||
"str-buf",
|
"str-buf",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs-next"
|
name = "dirs-next"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -115,23 +177,12 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.2.8"
|
version = "0.3.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"errno-dragonfly",
|
|
||||||
"libc",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "errno-dragonfly"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
"libc",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -146,13 +197,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fd-lock"
|
name = "fd-lock"
|
||||||
version = "3.0.5"
|
version = "3.0.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46e245f4c8ec30c6415c56cb132c07e69e74f1942f6b4a4061da748b49f486ca"
|
checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -163,9 +214,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.6"
|
version = "0.2.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -173,71 +224,54 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "heck"
|
||||||
version = "0.11.2"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hermit-abi"
|
|
||||||
version = "0.1.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "indexmap"
|
|
||||||
version = "1.8.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"hashbrown",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "io-lifetimes"
|
|
||||||
version = "0.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9448015e586b611e5d322f6703812bbca2f1e709d5773ecd38ddb4e3bb649504"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.122"
|
version = "0.2.150"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259"
|
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "libredox"
|
||||||
version = "0.0.42"
|
version = "0.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7"
|
checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "log"
|
|
||||||
version = "0.4.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"bitflags 2.4.1",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "logos"
|
name = "linux-raw-sys"
|
||||||
version = "0.12.0"
|
version = "0.4.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "427e2abca5be13136da9afdbf874e6b34ad9001dd70f2b103b083a85daa7b345"
|
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "logos"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"logos-derive",
|
"logos-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "logos-derive"
|
name = "logos-codegen"
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56a7d287fd2ac3f75b11f19a1c8a874a7d55744bd91f7a1b3e7cf87d4343c36d"
|
checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"beef",
|
"beef",
|
||||||
"fnv",
|
"fnv",
|
||||||
|
@ -245,23 +279,22 @@ dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"regex-syntax",
|
"regex-syntax",
|
||||||
"syn",
|
"syn",
|
||||||
"utf8-ranges",
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "logos-derive"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e"
|
||||||
|
dependencies = [
|
||||||
|
"logos-codegen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.4.1"
|
version = "2.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memoffset"
|
|
||||||
version = "0.6.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nibble_vec"
|
name = "nibble_vec"
|
||||||
|
@ -274,46 +307,35 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.23.1"
|
version = "0.26.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
|
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 1.3.2",
|
||||||
"cc",
|
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"memoffset",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "os_str_bytes"
|
|
||||||
version = "6.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.16"
|
version = "0.2.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.37"
|
version = "1.0.70"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
|
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.18"
|
version = "1.0.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
@ -351,60 +373,59 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.6.3"
|
version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.13"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
|
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 1.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_users"
|
name = "redox_users"
|
||||||
version = "0.4.3"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"redox_syscall",
|
"libredox",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.25"
|
version = "0.6.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.34.2"
|
version = "0.38.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96619609a54d638872db136f56941d34e2a00bb0acf3fa783a90d6b96a093ba2"
|
checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.4.1",
|
||||||
"errno",
|
"errno",
|
||||||
"io-lifetimes",
|
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"winapi",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustyline"
|
name = "rustyline"
|
||||||
version = "9.1.2"
|
version = "11.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db7826789c0e25614b03e5a54a0717a86f9ff6e6e5247f92b369472869320039"
|
checksum = "5dfc8644681285d1fb67a467fb3021bfea306b99b4146b166a1fe3ada965eece"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 1.3.2",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"clipboard-win",
|
"clipboard-win",
|
||||||
"dirs-next",
|
"dirs-next",
|
||||||
|
@ -415,7 +436,6 @@ dependencies = [
|
||||||
"nix",
|
"nix",
|
||||||
"radix_trie",
|
"radix_trie",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
"smallvec",
|
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
|
@ -424,21 +444,21 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.8.0"
|
version = "1.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "str-buf"
|
name = "str-buf"
|
||||||
version = "1.0.5"
|
version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a"
|
checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
|
@ -448,44 +468,29 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.91"
|
version = "2.0.39"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
|
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"unicode-xid",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "termcolor"
|
|
||||||
version = "1.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "textwrap"
|
|
||||||
version = "0.15.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.30"
|
version = "1.0.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.30"
|
version = "1.0.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -493,40 +498,34 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-ident"
|
||||||
version = "1.9.0"
|
version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.9"
|
version = "0.1.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-xid"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utf8-ranges"
|
|
||||||
version = "1.0.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.2+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
|
@ -544,15 +543,6 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-util"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
|
||||||
dependencies = [
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -561,43 +551,132 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.30.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "030b7ff91626e57a05ca64a07c481973cbb2db774e4852c9c7ca342408c6a99a"
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_msvc",
|
"windows-targets 0.48.5",
|
||||||
"windows_i686_gnu",
|
|
||||||
"windows_i686_msvc",
|
|
||||||
"windows_x86_64_gnu",
|
|
||||||
"windows_x86_64_msvc",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows-sys"
|
||||||
version = "0.30.0"
|
version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca"
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.48.5",
|
||||||
|
"windows_aarch64_msvc 0.48.5",
|
||||||
|
"windows_i686_gnu 0.48.5",
|
||||||
|
"windows_i686_msvc 0.48.5",
|
||||||
|
"windows_x86_64_gnu 0.48.5",
|
||||||
|
"windows_x86_64_gnullvm 0.48.5",
|
||||||
|
"windows_x86_64_msvc 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.52.0",
|
||||||
|
"windows_aarch64_msvc 0.52.0",
|
||||||
|
"windows_i686_gnu 0.52.0",
|
||||||
|
"windows_i686_msvc 0.52.0",
|
||||||
|
"windows_x86_64_gnu 0.52.0",
|
||||||
|
"windows_x86_64_gnullvm 0.52.0",
|
||||||
|
"windows_x86_64_msvc 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.30.0"
|
version = "0.48.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8"
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.30.0"
|
version = "0.48.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6"
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.30.0"
|
version = "0.48.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a"
|
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.30.0"
|
version = "0.48.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1"
|
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ablescript"
|
name = "ablescript"
|
||||||
version = "0.3.0"
|
version = "0.5.4"
|
||||||
authors = ["able <abl3theabove@gmail.com>"]
|
authors = ["AbleScript Developers"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
description = "The best programming language"
|
description = "The best programming language"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
documentation = "https://ablecorp.us/able-script-the-book/"
|
documentation = "https://ablecorp.us/able-script-the-book/"
|
||||||
repository = "https://github.com/AbleCorp/able-script"
|
repository = "https://git.ablecorp.us/AbleScript/able-script"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
logos = "0.12"
|
logos = "0.13"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
//! just plain subroutines and they do not return any value,
|
//! just plain subroutines and they do not return any value,
|
||||||
//! so their calls are statements.
|
//! so their calls are statements.
|
||||||
|
|
||||||
use crate::{base_55::char2num, variables::Value};
|
use crate::{base_55::char2num, value::Value};
|
||||||
use std::{fmt::Debug, hash::Hash};
|
use std::{fmt::Debug, hash::Hash};
|
||||||
|
|
||||||
type Span = std::ops::Range<usize>;
|
type Span = std::ops::Range<usize>;
|
||||||
|
@ -110,8 +110,8 @@ pub enum Stmt {
|
||||||
Loop {
|
Loop {
|
||||||
body: Block,
|
body: Block,
|
||||||
},
|
},
|
||||||
Break,
|
Enough,
|
||||||
HopBack,
|
AndAgain,
|
||||||
|
|
||||||
Dim {
|
Dim {
|
||||||
ident: Spanned<String>,
|
ident: Spanned<String>,
|
||||||
|
@ -136,9 +136,13 @@ pub enum Stmt {
|
||||||
expr: Spanned<Expr>,
|
expr: Spanned<Expr>,
|
||||||
args: Vec<Spanned<Expr>>,
|
args: Vec<Spanned<Expr>>,
|
||||||
},
|
},
|
||||||
Print(Spanned<Expr>),
|
Print {
|
||||||
|
expr: Spanned<Expr>,
|
||||||
|
newline: bool,
|
||||||
|
},
|
||||||
Read(Assignable),
|
Read(Assignable),
|
||||||
Melo(Spanned<String>),
|
Melo(Spanned<String>),
|
||||||
|
Finally(Block),
|
||||||
Rlyeh,
|
Rlyeh,
|
||||||
Rickroll,
|
Rickroll,
|
||||||
}
|
}
|
||||||
|
@ -160,10 +164,11 @@ pub enum Expr {
|
||||||
index: Box<Spanned<Expr>>,
|
index: Box<Spanned<Expr>>,
|
||||||
},
|
},
|
||||||
Len(Box<Spanned<Expr>>),
|
Len(Box<Spanned<Expr>>),
|
||||||
|
Keys(Box<Spanned<Expr>>),
|
||||||
Variable(String),
|
Variable(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
pub enum Literal {
|
pub enum Literal {
|
||||||
Char(char),
|
Char(char),
|
||||||
Int(isize),
|
Int(isize),
|
||||||
|
@ -180,7 +185,7 @@ impl From<Literal> for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
pub enum BinOpKind {
|
pub enum BinOpKind {
|
||||||
Add,
|
Add,
|
||||||
Subtract,
|
Subtract,
|
||||||
|
|
|
@ -5,7 +5,7 @@ pub const fn char2num(c: char) -> isize {
|
||||||
'/' => 53,
|
'/' => 53,
|
||||||
'\\' => 54,
|
'\\' => 54,
|
||||||
'.' => 55,
|
'.' => 55,
|
||||||
'U' => -210,
|
'U' => -210, // Backwards compatibility
|
||||||
'A'..='Z' => -(c as isize) + 64,
|
'A'..='Z' => -(c as isize) + 64,
|
||||||
'a'..='z' => (c as isize) - 96,
|
'a'..='z' => (c as isize) - 96,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
|
|
|
@ -221,15 +221,7 @@ impl<'a, I: BootlegRead> Interpreter<'a, I> {
|
||||||
};
|
};
|
||||||
Some((*counter, index))
|
Some((*counter, index))
|
||||||
})
|
})
|
||||||
.find_map(
|
.find_map(|(counter, index)| (counter == 0).then_some(index))
|
||||||
|(counter, index)| {
|
|
||||||
if counter == 0 {
|
|
||||||
Some(index)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_matching_opening_bracket(&mut self, closing: usize) -> Option<usize> {
|
fn get_matching_opening_bracket(&mut self, closing: usize) -> Option<usize> {
|
||||||
|
@ -245,15 +237,7 @@ impl<'a, I: BootlegRead> Interpreter<'a, I> {
|
||||||
};
|
};
|
||||||
Some((*counter, index))
|
Some((*counter, index))
|
||||||
})
|
})
|
||||||
.find_map(
|
.find_map(|(counter, index)| (counter == 0).then_some(index))
|
||||||
|(counter, index)| {
|
|
||||||
if counter == 0 {
|
|
||||||
Some(index)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Number constants.
|
//! Number constants.
|
||||||
|
|
||||||
use crate::variables::{Value, Variable};
|
use crate::value::{Value, Variable};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub const ANSWER: isize = 42;
|
pub const ANSWER: isize = 42;
|
||||||
|
@ -30,11 +30,12 @@ pub fn ablescript_consts() -> HashMap<String, Variable> {
|
||||||
Str("1452251871514141792252515212116".to_owned()),
|
Str("1452251871514141792252515212116".to_owned()),
|
||||||
),
|
),
|
||||||
("OCTOTHORPE", Str("#".to_owned())), // It's an octothorpe
|
("OCTOTHORPE", Str("#".to_owned())), // It's an octothorpe
|
||||||
|
("AMOGUS", Str("ඞ".to_owned())), // Amogus
|
||||||
("ANSWER", Int(ANSWER)),
|
("ANSWER", Int(ANSWER)),
|
||||||
("nul", Nul),
|
("nul", Nul),
|
||||||
("always", Abool(crate::variables::Abool::Always)),
|
("always", Abool(crate::value::Abool::Always)),
|
||||||
("sometimes", Abool(crate::variables::Abool::Sometimes)),
|
("sometimes", Abool(crate::value::Abool::Sometimes)),
|
||||||
("never", Abool(crate::variables::Abool::Never)),
|
("never", Abool(crate::value::Abool::Never)),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(name, value)| (name.to_owned(), Variable::from_value(value)))
|
.map(|(name, value)| (name.to_owned(), Variable::from_value(value)))
|
||||||
|
|
|
@ -9,15 +9,36 @@ pub struct Error {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ErrorKind {
|
pub enum ErrorKind {
|
||||||
UnexpectedEof,
|
/// Parser expected token, but none was available
|
||||||
|
UnexpectedEoi,
|
||||||
|
|
||||||
|
/// Parser encountered unknown token
|
||||||
|
InvalidToken,
|
||||||
|
|
||||||
|
/// Parser expected certain token, but other one appeared
|
||||||
UnexpectedToken(Token),
|
UnexpectedToken(Token),
|
||||||
|
|
||||||
|
/// Attempted to assign to undefined variable
|
||||||
UnknownVariable(String),
|
UnknownVariable(String),
|
||||||
|
|
||||||
|
/// Attempted to access banned variable
|
||||||
MeloVariable(String),
|
MeloVariable(String),
|
||||||
TopLevelBreak,
|
|
||||||
BfInterpretError(InterpretError),
|
/// Breaking / re-starting loop outside loop
|
||||||
MismatchedArgumentError,
|
LoopOpOutsideLoop,
|
||||||
|
|
||||||
|
/// Rlyeh was executed but host interface's exit
|
||||||
|
/// doesn't exit the program
|
||||||
|
NonExitingRlyeh(i32),
|
||||||
|
|
||||||
|
/// Missing left-hand side expression in binary expression
|
||||||
MissingLhs,
|
MissingLhs,
|
||||||
IOError(io::Error),
|
|
||||||
|
/// Error when executing BF code
|
||||||
|
Brian(InterpretError),
|
||||||
|
|
||||||
|
/// IO Error
|
||||||
|
Io(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
@ -25,10 +46,10 @@ impl Error {
|
||||||
Self { kind, span }
|
Self { kind, span }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an UnexpectedEof error, where the EOF occurs at the
|
/// Create an UnexpectedEoi error, where the EOI occurs at the
|
||||||
/// given index in the file.
|
/// given index in the input.
|
||||||
pub fn unexpected_eof(index: usize) -> Self {
|
pub fn unexpected_eoi(index: usize) -> Self {
|
||||||
Self::new(ErrorKind::UnexpectedEof, index..index)
|
Self::new(ErrorKind::UnexpectedEoi, index..index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,26 +67,27 @@ impl std::error::Error for Error {}
|
||||||
impl Display for ErrorKind {
|
impl Display for ErrorKind {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ErrorKind::UnexpectedEof => write!(f, "unexpected end of file"),
|
ErrorKind::UnexpectedEoi => write!(f, "unexpected end of input"),
|
||||||
|
ErrorKind::InvalidToken => write!(f, "invalid token"),
|
||||||
ErrorKind::UnexpectedToken(Token::Melo) => write!(f, "unexpected marten"),
|
ErrorKind::UnexpectedToken(Token::Melo) => write!(f, "unexpected marten"),
|
||||||
ErrorKind::UnexpectedToken(token) => write!(f, "unexpected token {:?}", token),
|
ErrorKind::UnexpectedToken(token) => write!(f, "unexpected token {:?}", token),
|
||||||
ErrorKind::UnknownVariable(name) => write!(f, "unknown identifier \"{}\"", name),
|
ErrorKind::UnknownVariable(name) => write!(f, "unknown identifier \"{}\"", name),
|
||||||
ErrorKind::MeloVariable(name) => write!(f, "banned variable \"{}\"", name),
|
ErrorKind::MeloVariable(name) => write!(f, "banned variable \"{}\"", name),
|
||||||
ErrorKind::TopLevelBreak => write!(f, "can only `break` out of a loop"),
|
ErrorKind::LoopOpOutsideLoop => write!(
|
||||||
ErrorKind::BfInterpretError(err) => write!(f, "brainfuck error: {}", err),
|
f,
|
||||||
|
"unable to perform loop operation (enough or and enough) outside a loop"
|
||||||
|
),
|
||||||
|
&ErrorKind::NonExitingRlyeh(code) => write!(f, "program exited with code {code}"),
|
||||||
|
ErrorKind::Brian(err) => write!(f, "brainfuck error: {}", err),
|
||||||
// TODO: give concrete numbers here.
|
// TODO: give concrete numbers here.
|
||||||
ErrorKind::MismatchedArgumentError => write!(f, "wrong number of function arguments"),
|
|
||||||
ErrorKind::MissingLhs => write!(f, "missing expression before binary operation"),
|
ErrorKind::MissingLhs => write!(f, "missing expression before binary operation"),
|
||||||
ErrorKind::IOError(err) => write!(f, "I/O error: {}", err),
|
ErrorKind::Io(err) => write!(f, "I/O error: {}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
impl From<io::Error> for ErrorKind {
|
||||||
fn from(e: io::Error) -> Self {
|
fn from(e: io::Error) -> Self {
|
||||||
Self {
|
Self::Io(e)
|
||||||
kind: ErrorKind::IOError(e),
|
|
||||||
span: 0..0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
53
ablescript/src/host_interface.rs
Normal file
53
ablescript/src/host_interface.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
use crate::value::Variable;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// Host Environment Interface
|
||||||
|
pub trait HostInterface {
|
||||||
|
/// Initial variables for a stack frame
|
||||||
|
fn initial_vars(&mut self) -> HashMap<String, Variable>;
|
||||||
|
|
||||||
|
/// Print a string
|
||||||
|
fn print(&mut self, string: &str, new_line: bool) -> std::io::Result<()>;
|
||||||
|
|
||||||
|
/// Read a byte
|
||||||
|
fn read_byte(&mut self) -> std::io::Result<u8>;
|
||||||
|
|
||||||
|
/// This function should exit the program with specified code.
|
||||||
|
///
|
||||||
|
/// For cases where exit is not desired, just let the function return
|
||||||
|
/// and interpreter will terminate with an error.
|
||||||
|
fn exit(&mut self, code: i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Standard [HostInterface] implementation
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct Standard;
|
||||||
|
impl HostInterface for Standard {
|
||||||
|
fn initial_vars(&mut self) -> HashMap<String, Variable> {
|
||||||
|
HashMap::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print(&mut self, string: &str, new_line: bool) -> std::io::Result<()> {
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
let mut stdout = std::io::stdout();
|
||||||
|
stdout.write_all(string.as_bytes())?;
|
||||||
|
if new_line {
|
||||||
|
stdout.write_all(b"\n")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_byte(&mut self) -> std::io::Result<u8> {
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
let mut buf = [0];
|
||||||
|
std::io::stdin().read_exact(&mut buf)?;
|
||||||
|
Ok(buf[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit(&mut self, code: i32) {
|
||||||
|
std::process::exit(code);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,23 +9,22 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Assignable, AssignableKind, Expr, Spanned, Stmt},
|
ast::{Assignable, AssignableKind, Block, Expr, Spanned, Stmt},
|
||||||
consts::ablescript_consts,
|
consts::ablescript_consts,
|
||||||
error::{Error, ErrorKind},
|
error::{Error, ErrorKind},
|
||||||
variables::{Functio, Value, ValueRef, Variable},
|
host_interface::HostInterface,
|
||||||
|
value::{Functio, Value, ValueRef, Variable},
|
||||||
};
|
};
|
||||||
use rand::random;
|
use rand::random;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
io::{stdin, stdout, Read, Write},
|
|
||||||
mem::take,
|
mem::take,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
process::exit,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An environment for executing AbleScript code.
|
/// An environment for executing AbleScript code.
|
||||||
pub struct ExecEnv {
|
pub struct ExecEnv<H> {
|
||||||
/// The stack, ordered such that `stack[stack.len() - 1]` is the
|
/// The stack, ordered such that `stack[stack.len() - 1]` is the
|
||||||
/// top-most (newest) stack frame, and `stack[0]` is the
|
/// top-most (newest) stack frame, and `stack[0]` is the
|
||||||
/// bottom-most (oldest) stack frame.
|
/// bottom-most (oldest) stack frame.
|
||||||
|
@ -37,6 +36,12 @@ pub struct ExecEnv {
|
||||||
/// (via the `read` statement). We store each of those bits as
|
/// (via the `read` statement). We store each of those bits as
|
||||||
/// booleans to facilitate easy manipulation.
|
/// booleans to facilitate easy manipulation.
|
||||||
read_buf: VecDeque<bool>,
|
read_buf: VecDeque<bool>,
|
||||||
|
|
||||||
|
/// Interface to interact with the host interface
|
||||||
|
host_interface: H,
|
||||||
|
|
||||||
|
/// Vector of blocks to be executed at the end of the program
|
||||||
|
finalisers: Vec<Block>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A set of visible variable and function definitions in a single
|
/// A set of visible variable and function definitions in a single
|
||||||
|
@ -46,12 +51,12 @@ struct Scope {
|
||||||
variables: HashMap<String, Variable>,
|
variables: HashMap<String, Variable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ExecEnv {
|
impl<H> Default for ExecEnv<H>
|
||||||
|
where
|
||||||
|
H: Default + HostInterface,
|
||||||
|
{
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self::with_host_interface(H::default())
|
||||||
stack: vec![Default::default()],
|
|
||||||
read_buf: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,57 +73,80 @@ enum HaltStatus {
|
||||||
/// We ran out of statements to execute.
|
/// We ran out of statements to execute.
|
||||||
Finished,
|
Finished,
|
||||||
|
|
||||||
/// A `break` statement occurred at the given span, and was not
|
/// An `enough` statement occurred at the given span, and was not
|
||||||
/// caught by a `loop` statement up to this point.
|
/// caught by a `loop` statement up to this point.
|
||||||
Break(Range<usize>),
|
Enough(Range<usize>),
|
||||||
|
|
||||||
/// A `hopback` statement occurred at the given span, and was not
|
/// A `and again` statement occurred at the given span, and was not
|
||||||
/// caught by a `loop` statement up to this point.
|
/// caught by a `loop` statement up to this point.
|
||||||
Hopback(Range<usize>),
|
AndAgain(Range<usize>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The number of bits the `read` statement reads at once from
|
/// The number of bits the `read` statement reads at once from
|
||||||
/// standard input.
|
/// standard input.
|
||||||
pub const READ_BITS: u8 = 3;
|
pub const READ_BITS: u8 = 3;
|
||||||
|
|
||||||
impl ExecEnv {
|
impl<H: HostInterface> ExecEnv<H> {
|
||||||
/// Create a new Scope with no predefined variable definitions or
|
/// Create a new Scope with no predefined variable definitions or
|
||||||
/// other information.
|
/// other information.
|
||||||
pub fn new() -> Self {
|
pub fn with_host_interface(mut host_interface: H) -> Self {
|
||||||
Self::default()
|
Self {
|
||||||
|
stack: vec![
|
||||||
|
Default::default(),
|
||||||
|
Scope {
|
||||||
|
variables: host_interface.initial_vars(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
read_buf: Default::default(),
|
||||||
|
finalisers: vec![],
|
||||||
|
host_interface,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new Scope with predefined variables
|
/// Create a new Scope with predefined variables
|
||||||
pub fn new_with_vars<I>(vars: I) -> Self
|
pub fn new_with_vars<I>(mut host_interface: H, vars: I) -> Self
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = (String, Variable)>,
|
I: IntoIterator<Item = (String, Variable)>,
|
||||||
{
|
{
|
||||||
let scope = Scope {
|
|
||||||
variables: ablescript_consts().into_iter().chain(vars).collect(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
stack: vec![scope],
|
stack: vec![
|
||||||
|
Scope {
|
||||||
|
variables: ablescript_consts().into_iter().chain(vars).collect(),
|
||||||
|
},
|
||||||
|
Scope {
|
||||||
|
variables: host_interface.initial_vars(),
|
||||||
|
},
|
||||||
|
],
|
||||||
read_buf: Default::default(),
|
read_buf: Default::default(),
|
||||||
|
finalisers: vec![],
|
||||||
|
host_interface,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a set of Statements in the root stack frame. Return an
|
/// Execute a set of Statements in the root stack frame. Return an
|
||||||
/// error if one or more of the Stmts failed to evaluate, or if a
|
/// error if one or more of the Stmts failed to evaluate, or if a
|
||||||
/// `break` or `hopback` statement occurred at the top level.
|
/// `enough` or `and again` statement occurred at the top level.
|
||||||
pub fn eval_stmts(&mut self, stmts: &[Spanned<Stmt>]) -> Result<(), Error> {
|
pub fn eval_stmts(&mut self, stmts: &[Spanned<Stmt>]) -> Result<(), Error> {
|
||||||
match self.eval_stmts_hs(stmts, false)? {
|
match self.eval_stmts_hs(stmts, false)? {
|
||||||
HaltStatus::Finished => Ok(()),
|
HaltStatus::Finished => Ok(()),
|
||||||
HaltStatus::Break(span) | HaltStatus::Hopback(span) => Err(Error {
|
HaltStatus::Enough(span) | HaltStatus::AndAgain(span) => Err(Error {
|
||||||
// It's an error to issue a `break` outside of a
|
// It's an error to issue a `enough` outside of a
|
||||||
// `loop` statement.
|
// `loop` statement.
|
||||||
kind: ErrorKind::TopLevelBreak,
|
kind: ErrorKind::LoopOpOutsideLoop,
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
while !self.finalisers.is_empty() {
|
||||||
|
for block in std::mem::take(&mut self.finalisers) {
|
||||||
|
self.eval_stmts_hs(&block, true)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The same as `eval_stmts`, but report "break" and "hopback"
|
/// The same as `eval_stmts`, but report "enough" and "and again"
|
||||||
/// exit codes as normal conditions in a HaltStatus enum, and
|
/// exit codes as normal conditions in a HaltStatus enum, and
|
||||||
/// create a new stack frame if `stackframe` is true.
|
/// create a new stack frame if `stackframe` is true.
|
||||||
///
|
///
|
||||||
|
@ -195,18 +223,30 @@ impl ExecEnv {
|
||||||
.unwrap_or(Value::Nul)
|
.unwrap_or(Value::Nul)
|
||||||
}
|
}
|
||||||
Len(expr) => Value::Int(self.eval_expr(expr)?.length()),
|
Len(expr) => Value::Int(self.eval_expr(expr)?.length()),
|
||||||
|
Keys(expr) => Value::Cart(
|
||||||
|
self.eval_expr(expr)?
|
||||||
|
.into_cart()
|
||||||
|
.into_keys()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, k)| (Value::Int(i as isize + 1), ValueRef::new(k)))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
// TODO: not too happy with constructing an artificial
|
// TODO: not too happy with constructing an artificial
|
||||||
// Ident here.
|
// Ident here.
|
||||||
Variable(name) => self.get_var(&Spanned::new(name.to_owned(), expr.span.clone()))?,
|
Variable(name) => {
|
||||||
|
self.get_var_value(&Spanned::new(name.to_owned(), expr.span.clone()))?
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform the action indicated by a statement.
|
/// Perform the action indicated by a statement.
|
||||||
fn eval_stmt(&mut self, stmt: &Spanned<Stmt>) -> Result<HaltStatus, Error> {
|
fn eval_stmt(&mut self, stmt: &Spanned<Stmt>) -> Result<HaltStatus, Error> {
|
||||||
match &stmt.item {
|
match &stmt.item {
|
||||||
Stmt::Print(expr) => {
|
Stmt::Print { expr, newline } => {
|
||||||
println!("{}", self.eval_expr(expr)?);
|
let value = self.eval_expr(expr)?;
|
||||||
|
self.host_interface
|
||||||
|
.print(&value.to_string(), *newline)
|
||||||
|
.map_err(|e| Error::new(e.into(), stmt.span.clone()))?;
|
||||||
}
|
}
|
||||||
Stmt::Dim { ident, init } => {
|
Stmt::Dim { ident, init } => {
|
||||||
let init = match init {
|
let init = match init {
|
||||||
|
@ -254,44 +294,59 @@ impl ExecEnv {
|
||||||
}
|
}
|
||||||
Stmt::Call { expr, args } => {
|
Stmt::Call { expr, args } => {
|
||||||
let func = self.eval_expr(expr)?.into_functio();
|
let func = self.eval_expr(expr)?.into_functio();
|
||||||
|
return self.fn_call(func, args, &stmt.span);
|
||||||
self.fn_call(func, args, &stmt.span)?;
|
|
||||||
}
|
}
|
||||||
Stmt::Loop { body } => loop {
|
Stmt::Loop { body } => loop {
|
||||||
let res = self.eval_stmts_hs(body, true)?;
|
let res = self.eval_stmts_hs(body, true)?;
|
||||||
match res {
|
match res {
|
||||||
HaltStatus::Finished => {}
|
HaltStatus::Finished => (),
|
||||||
HaltStatus::Break(_) => break,
|
HaltStatus::Enough(_) => break,
|
||||||
HaltStatus::Hopback(_) => continue,
|
HaltStatus::AndAgain(_) => continue,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Stmt::Assign { assignable, value } => {
|
Stmt::Assign { assignable, value } => {
|
||||||
self.assign(assignable, self.eval_expr(value)?)?;
|
self.assign(assignable, self.eval_expr(value)?)?;
|
||||||
}
|
}
|
||||||
Stmt::Break => {
|
Stmt::Enough => {
|
||||||
return Ok(HaltStatus::Break(stmt.span.clone()));
|
return Ok(HaltStatus::Enough(stmt.span.clone()));
|
||||||
}
|
}
|
||||||
Stmt::HopBack => {
|
Stmt::AndAgain => {
|
||||||
return Ok(HaltStatus::Hopback(stmt.span.clone()));
|
return Ok(HaltStatus::AndAgain(stmt.span.clone()));
|
||||||
}
|
|
||||||
Stmt::Melo(ident) => {
|
|
||||||
self.get_var_mut(ident)?.melo = true;
|
|
||||||
}
|
}
|
||||||
|
Stmt::Melo(ident) => match self.get_var_mut(ident)? {
|
||||||
|
var @ Variable::Ref(_) => *var = Variable::Melo,
|
||||||
|
Variable::Melo => {
|
||||||
|
for s in &mut self.stack {
|
||||||
|
if s.variables.remove(&ident.item).is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Stmt::Finally(block) => self.finalisers.push(block.clone()),
|
||||||
Stmt::Rlyeh => {
|
Stmt::Rlyeh => {
|
||||||
// Maybe print a creepy error message or something
|
// Maybe print a creepy error message or something
|
||||||
// here at some point. ~~Alex
|
// here at some point. ~~Alex
|
||||||
exit(random());
|
let code = random();
|
||||||
|
self.host_interface.exit(code);
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::NonExitingRlyeh(code),
|
||||||
|
stmt.span.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Stmt::Rickroll => {
|
Stmt::Rickroll => {
|
||||||
stdout()
|
self.host_interface
|
||||||
.write_all(include_str!("rickroll").as_bytes())
|
.print(include_str!("rickroll"), false)
|
||||||
.expect("Failed to write to stdout");
|
.map_err(|e| Error::new(e.into(), stmt.span.clone()))?;
|
||||||
}
|
}
|
||||||
Stmt::Read(assignable) => {
|
Stmt::Read(assignable) => {
|
||||||
let mut value = 0;
|
let mut value = 0;
|
||||||
for _ in 0..READ_BITS {
|
for _ in 0..READ_BITS {
|
||||||
value <<= 1;
|
value <<= 1;
|
||||||
value += self.get_bit()? as isize;
|
value += self
|
||||||
|
.get_bit()
|
||||||
|
.map_err(|e| Error::new(e, stmt.span.clone()))?
|
||||||
|
as isize;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.assign(assignable, Value::Int(value))?;
|
self.assign(assignable, Value::Int(value))?;
|
||||||
|
@ -305,10 +360,10 @@ impl ExecEnv {
|
||||||
fn assign(&mut self, dest: &Assignable, value: Value) -> Result<(), Error> {
|
fn assign(&mut self, dest: &Assignable, value: Value) -> Result<(), Error> {
|
||||||
match dest.kind {
|
match dest.kind {
|
||||||
AssignableKind::Variable => {
|
AssignableKind::Variable => {
|
||||||
self.get_var_mut(&dest.ident)?.value.replace(value);
|
self.get_var_rc_mut(&dest.ident)?.replace(value);
|
||||||
}
|
}
|
||||||
AssignableKind::Index { ref indices } => {
|
AssignableKind::Index { ref indices } => {
|
||||||
let mut cell = self.get_var_rc(&dest.ident)?;
|
let mut cell = self.get_var_rc_mut(&dest.ident)?.clone();
|
||||||
for index in indices {
|
for index in indices {
|
||||||
let index = self.eval_expr(index)?;
|
let index = self.eval_expr(index)?;
|
||||||
|
|
||||||
|
@ -357,14 +412,15 @@ impl ExecEnv {
|
||||||
func: Functio,
|
func: Functio,
|
||||||
args: &[Spanned<Expr>],
|
args: &[Spanned<Expr>],
|
||||||
span: &Range<usize>,
|
span: &Range<usize>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<HaltStatus, Error> {
|
||||||
// Arguments that are ExprKind::Variable are pass by
|
// Arguments that are ExprKind::Variable are pass by
|
||||||
// reference; all other expressions are pass by value.
|
// reference; all other expressions are pass by value.
|
||||||
let args = args
|
let args = args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg| {
|
.map(|arg| {
|
||||||
if let Expr::Variable(name) = &arg.item {
|
if let Expr::Variable(name) = &arg.item {
|
||||||
self.get_var_rc(&Spanned::new(name.to_owned(), arg.span.clone()))
|
self.get_var_rc_mut(&Spanned::new(name.to_owned(), arg.span.clone()))
|
||||||
|
.cloned()
|
||||||
} else {
|
} else {
|
||||||
self.eval_expr(arg).map(ValueRef::new)
|
self.eval_expr(arg).map(ValueRef::new)
|
||||||
}
|
}
|
||||||
|
@ -379,7 +435,7 @@ impl ExecEnv {
|
||||||
func: Functio,
|
func: Functio,
|
||||||
args: &[ValueRef],
|
args: &[ValueRef],
|
||||||
span: &Range<usize>,
|
span: &Range<usize>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<HaltStatus, Error> {
|
||||||
match func {
|
match func {
|
||||||
Functio::Bf {
|
Functio::Bf {
|
||||||
instructions,
|
instructions,
|
||||||
|
@ -399,22 +455,21 @@ impl ExecEnv {
|
||||||
)
|
)
|
||||||
.interpret_with_output(&mut output)
|
.interpret_with_output(&mut output)
|
||||||
.map_err(|e| Error {
|
.map_err(|e| Error {
|
||||||
kind: ErrorKind::BfInterpretError(e),
|
kind: ErrorKind::Brian(e),
|
||||||
span: span.to_owned(),
|
span: span.to_owned(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
stdout()
|
match String::from_utf8(output) {
|
||||||
.write_all(&output)
|
Ok(string) => self.host_interface.print(&string, false),
|
||||||
.expect("Failed to write to stdout");
|
Err(e) => self
|
||||||
|
.host_interface
|
||||||
|
.print(&format!("{:?}", e.as_bytes()), true),
|
||||||
|
}
|
||||||
|
.map_err(|e| Error::new(e.into(), span.clone()))?;
|
||||||
|
|
||||||
|
Ok(HaltStatus::Finished)
|
||||||
}
|
}
|
||||||
Functio::Able { params, body } => {
|
Functio::Able { params, body } => {
|
||||||
if params.len() != args.len() {
|
|
||||||
return Err(Error {
|
|
||||||
kind: ErrorKind::MismatchedArgumentError,
|
|
||||||
span: span.to_owned(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.stack.push(Default::default());
|
self.stack.push(Default::default());
|
||||||
|
|
||||||
for (param, arg) in params.iter().zip(args.iter()) {
|
for (param, arg) in params.iter().zip(args.iter()) {
|
||||||
|
@ -424,40 +479,45 @@ impl ExecEnv {
|
||||||
let res = self.eval_stmts_hs(&body, false);
|
let res = self.eval_stmts_hs(&body, false);
|
||||||
|
|
||||||
self.stack.pop();
|
self.stack.pop();
|
||||||
res?;
|
res
|
||||||
|
}
|
||||||
|
Functio::Builtin(b) => {
|
||||||
|
b.call(args).map_err(|e| Error::new(e, span.clone()))?;
|
||||||
|
Ok(HaltStatus::Finished)
|
||||||
}
|
}
|
||||||
Functio::Builtin(b) => b.call(args).map_err(|e| Error::new(e, span.clone()))?,
|
|
||||||
Functio::Chain { functios, kind } => {
|
Functio::Chain { functios, kind } => {
|
||||||
|
use crate::value::functio::FunctioChainKind;
|
||||||
let (left_functio, right_functio) = *functios;
|
let (left_functio, right_functio) = *functios;
|
||||||
match kind {
|
Ok(
|
||||||
crate::variables::FunctioChainKind::Equal => {
|
match match kind {
|
||||||
let (l, r) = args.split_at(args.len() / 2);
|
FunctioChainKind::Equal => {
|
||||||
|
let (l, r) = args.split_at(args.len() / 2);
|
||||||
|
|
||||||
self.fn_call_with_values(left_functio, l, span)?;
|
(
|
||||||
self.fn_call_with_values(right_functio, r, span)?;
|
self.fn_call_with_values(left_functio, l, span)?,
|
||||||
}
|
self.fn_call_with_values(right_functio, r, span)?,
|
||||||
crate::variables::FunctioChainKind::ByArity => {
|
)
|
||||||
let (l, r) =
|
}
|
||||||
Self::deinterlace(args, (left_functio.arity(), right_functio.arity()));
|
FunctioChainKind::ByArity => {
|
||||||
|
let (l, r) = Self::deinterlace(
|
||||||
|
args,
|
||||||
|
(left_functio.arity(), right_functio.arity()),
|
||||||
|
);
|
||||||
|
|
||||||
self.fn_call_with_values(left_functio, &l, span)?;
|
(
|
||||||
self.fn_call_with_values(right_functio, &r, span)?;
|
self.fn_call_with_values(left_functio, &l, span)?,
|
||||||
}
|
self.fn_call_with_values(right_functio, &r, span)?,
|
||||||
};
|
)
|
||||||
}
|
}
|
||||||
Functio::Eval(code) => {
|
} {
|
||||||
if !args.is_empty() {
|
(s, HaltStatus::Finished) => s,
|
||||||
return Err(Error {
|
(HaltStatus::Finished, s) => s,
|
||||||
kind: ErrorKind::MismatchedArgumentError,
|
(_, r) => r,
|
||||||
span: span.to_owned(),
|
},
|
||||||
});
|
)
|
||||||
}
|
|
||||||
|
|
||||||
let stmts = crate::parser::parse(&code)?;
|
|
||||||
self.eval_stmts(&stmts)?;
|
|
||||||
}
|
}
|
||||||
|
Functio::Eval(code) => self.eval_stmts_hs(&crate::parser::parse(&code)?, false),
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinterlace(args: &[ValueRef], arities: (usize, usize)) -> (Vec<ValueRef>, Vec<ValueRef>) {
|
fn deinterlace(args: &[ValueRef], arities: (usize, usize)) -> (Vec<ValueRef>, Vec<ValueRef>) {
|
||||||
|
@ -473,18 +533,23 @@ impl ExecEnv {
|
||||||
.take(n_alternations)
|
.take(n_alternations)
|
||||||
.map(|chunk| ValueRef::clone(&chunk[0]))
|
.map(|chunk| ValueRef::clone(&chunk[0]))
|
||||||
.chain(
|
.chain(
|
||||||
args[2 * n_alternations..]
|
args.get(2 * n_alternations..)
|
||||||
.iter()
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.flatten()
|
||||||
.map(ValueRef::clone)
|
.map(ValueRef::clone)
|
||||||
.take(extra_l),
|
.take(extra_l),
|
||||||
)
|
)
|
||||||
.collect(),
|
.collect(),
|
||||||
args.chunks(2)
|
args.chunks(2)
|
||||||
.take(n_alternations)
|
.take(n_alternations)
|
||||||
.map(|chunk| ValueRef::clone(&chunk[1]))
|
.flat_map(|chunk| chunk.get(1))
|
||||||
|
.map(ValueRef::clone)
|
||||||
.chain(
|
.chain(
|
||||||
args[2 * n_alternations..]
|
args.get(2 * n_alternations..)
|
||||||
.iter()
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.flatten()
|
||||||
.map(ValueRef::clone)
|
.map(ValueRef::clone)
|
||||||
.take(extra_r),
|
.take(extra_r),
|
||||||
)
|
)
|
||||||
|
@ -494,15 +559,14 @@ impl ExecEnv {
|
||||||
|
|
||||||
/// Get a single bit from the bit buffer, or refill it from
|
/// Get a single bit from the bit buffer, or refill it from
|
||||||
/// standard input if it is empty.
|
/// standard input if it is empty.
|
||||||
fn get_bit(&mut self) -> Result<bool, Error> {
|
fn get_bit(&mut self) -> Result<bool, ErrorKind> {
|
||||||
const BITS_PER_BYTE: u8 = 8;
|
const BITS_PER_BYTE: u8 = 8;
|
||||||
|
|
||||||
if self.read_buf.is_empty() {
|
if self.read_buf.is_empty() {
|
||||||
let mut data = [0];
|
let byte = self.host_interface.read_byte()?;
|
||||||
stdin().read_exact(&mut data)?;
|
|
||||||
|
|
||||||
for n in (0..BITS_PER_BYTE).rev() {
|
for n in (0..BITS_PER_BYTE).rev() {
|
||||||
self.read_buf.push_back(((data[0] >> n) & 1) != 0);
|
self.read_buf.push_back(((byte >> n) & 1) != 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,7 +578,7 @@ impl ExecEnv {
|
||||||
|
|
||||||
/// Get the value of a variable. Throw an error if the variable is
|
/// Get the value of a variable. Throw an error if the variable is
|
||||||
/// inaccessible or banned.
|
/// inaccessible or banned.
|
||||||
fn get_var(&self, name: &Spanned<String>) -> Result<Value, Error> {
|
fn get_var_value(&self, name: &Spanned<String>) -> Result<Value, Error> {
|
||||||
// Search for the name in the stack from top to bottom.
|
// Search for the name in the stack from top to bottom.
|
||||||
match self
|
match self
|
||||||
.stack
|
.stack
|
||||||
|
@ -522,25 +586,16 @@ impl ExecEnv {
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|scope| scope.variables.get(&name.item))
|
.find_map(|scope| scope.variables.get(&name.item))
|
||||||
{
|
{
|
||||||
Some(var) => {
|
Some(Variable::Ref(r)) => Ok(r.borrow().clone()),
|
||||||
if !var.melo {
|
Some(Variable::Melo) => Err(Error {
|
||||||
Ok(var.value.borrow().clone())
|
kind: ErrorKind::MeloVariable(name.item.to_owned()),
|
||||||
} else {
|
|
||||||
Err(Error {
|
|
||||||
kind: ErrorKind::MeloVariable(name.item.to_owned()),
|
|
||||||
span: name.span.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Err(Error {
|
|
||||||
kind: ErrorKind::UnknownVariable(name.item.to_owned()),
|
|
||||||
span: name.span.clone(),
|
span: name.span.clone(),
|
||||||
}),
|
}),
|
||||||
|
None => Ok(Value::Undefined),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to a variable. Throw an error if the
|
/// Get a mutable reference to a variable.
|
||||||
/// variable is inaccessible or banned.
|
|
||||||
fn get_var_mut(&mut self, name: &Spanned<String>) -> Result<&mut Variable, Error> {
|
fn get_var_mut(&mut self, name: &Spanned<String>) -> Result<&mut Variable, Error> {
|
||||||
// This function has a lot of duplicated code with `get_var`,
|
// This function has a lot of duplicated code with `get_var`,
|
||||||
// which I feel like is a bad sign...
|
// which I feel like is a bad sign...
|
||||||
|
@ -550,16 +605,7 @@ impl ExecEnv {
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|scope| scope.variables.get_mut(&name.item))
|
.find_map(|scope| scope.variables.get_mut(&name.item))
|
||||||
{
|
{
|
||||||
Some(var) => {
|
Some(var) => Ok(var),
|
||||||
if !var.melo {
|
|
||||||
Ok(var)
|
|
||||||
} else {
|
|
||||||
Err(Error {
|
|
||||||
kind: ErrorKind::MeloVariable(name.item.to_owned()),
|
|
||||||
span: name.span.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Err(Error {
|
None => Err(Error {
|
||||||
kind: ErrorKind::UnknownVariable(name.item.to_owned()),
|
kind: ErrorKind::UnknownVariable(name.item.to_owned()),
|
||||||
span: name.span.clone(),
|
span: name.span.clone(),
|
||||||
|
@ -567,10 +613,16 @@ impl ExecEnv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an Rc'd pointer to the value of a variable. Throw an error
|
/// Get an reference to an Rc'd pointer to the value of a variable. Throw an error
|
||||||
/// if the variable is inaccessible or banned.
|
/// if the variable is inaccessible or banned.
|
||||||
fn get_var_rc(&mut self, name: &Spanned<String>) -> Result<ValueRef, Error> {
|
fn get_var_rc_mut(&mut self, name: &Spanned<String>) -> Result<&mut ValueRef, Error> {
|
||||||
Ok(self.get_var_mut(name)?.value.clone())
|
match self.get_var_mut(name)? {
|
||||||
|
Variable::Ref(r) => Ok(r),
|
||||||
|
Variable::Melo => Err(Error {
|
||||||
|
kind: ErrorKind::MeloVariable(name.item.to_owned()),
|
||||||
|
span: name.span.clone(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Declare a new variable, with the given initial value.
|
/// Declare a new variable, with the given initial value.
|
||||||
|
@ -585,19 +637,22 @@ impl ExecEnv {
|
||||||
.last()
|
.last()
|
||||||
.expect("Declaring variable on empty stack")
|
.expect("Declaring variable on empty stack")
|
||||||
.variables
|
.variables
|
||||||
.insert(name.to_owned(), Variable { melo: false, value });
|
.insert(name.to_owned(), Variable::Ref(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ast::{Expr, Literal};
|
use crate::{
|
||||||
|
ast::{Expr, Literal},
|
||||||
|
host_interface::Standard,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_expression_test() {
|
fn basic_expression_test() {
|
||||||
// Check that 2 + 2 = 4.
|
// Check that 2 + 2 = 4.
|
||||||
let env = ExecEnv::new();
|
let env = ExecEnv::<Standard>::default();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
env.eval_expr(&Spanned {
|
env.eval_expr(&Spanned {
|
||||||
item: Expr::BinOp {
|
item: Expr::BinOp {
|
||||||
|
@ -623,7 +678,7 @@ mod tests {
|
||||||
// The sum of an integer and an aboolean causes an aboolean
|
// The sum of an integer and an aboolean causes an aboolean
|
||||||
// coercion.
|
// coercion.
|
||||||
|
|
||||||
let env = ExecEnv::new();
|
let env = ExecEnv::<Standard>::default();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
env.eval_expr(&Spanned {
|
env.eval_expr(&Spanned {
|
||||||
item: Expr::BinOp {
|
item: Expr::BinOp {
|
||||||
|
@ -648,7 +703,7 @@ mod tests {
|
||||||
fn overflow_should_not_panic() {
|
fn overflow_should_not_panic() {
|
||||||
// Integer overflow should throw a recoverable error instead
|
// Integer overflow should throw a recoverable error instead
|
||||||
// of panicking.
|
// of panicking.
|
||||||
let env = ExecEnv::new();
|
let env = ExecEnv::<Standard>::default();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
env.eval_expr(&Spanned {
|
env.eval_expr(&Spanned {
|
||||||
item: Expr::BinOp {
|
item: Expr::BinOp {
|
||||||
|
@ -692,7 +747,7 @@ mod tests {
|
||||||
// From here on out, I'll use this function to parse and run
|
// From here on out, I'll use this function to parse and run
|
||||||
// expressions, because writing out abstract syntax trees by hand
|
// expressions, because writing out abstract syntax trees by hand
|
||||||
// takes forever and is error-prone.
|
// takes forever and is error-prone.
|
||||||
fn eval(env: &mut ExecEnv, src: &str) -> Result<Value, Error> {
|
fn eval(env: &mut ExecEnv<Standard>, src: &str) -> Result<Value, Error> {
|
||||||
// We can assume there won't be any syntax errors in the
|
// We can assume there won't be any syntax errors in the
|
||||||
// interpreter tests.
|
// interpreter tests.
|
||||||
let ast = crate::parser::parse(src).unwrap();
|
let ast = crate::parser::parse(src).unwrap();
|
||||||
|
@ -704,12 +759,12 @@ mod tests {
|
||||||
// Functions have no return values, so use some
|
// Functions have no return values, so use some
|
||||||
// pass-by-reference hacks to detect the correct
|
// pass-by-reference hacks to detect the correct
|
||||||
// functionality.
|
// functionality.
|
||||||
let mut env = ExecEnv::new();
|
let mut env = ExecEnv::<Standard>::default();
|
||||||
|
|
||||||
// Declaring and reading from a variable.
|
// Declaring and reading from a variable.
|
||||||
eval(&mut env, "dim foo 32; dim bar foo + 1;").unwrap();
|
eval(&mut env, "foo dim 32; bar dim foo + 1;").unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
env.get_var(&Spanned {
|
env.get_var_value(&Spanned {
|
||||||
item: "bar".to_owned(),
|
item: "bar".to_owned(),
|
||||||
span: 1..1,
|
span: 1..1,
|
||||||
})
|
})
|
||||||
|
@ -720,7 +775,7 @@ mod tests {
|
||||||
// Assigning an existing variable.
|
// Assigning an existing variable.
|
||||||
eval(&mut env, "/*hi*/ =: foo;").unwrap();
|
eval(&mut env, "/*hi*/ =: foo;").unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
env.get_var(&Spanned {
|
env.get_var_value(&Spanned {
|
||||||
item: "foo".to_owned(),
|
item: "foo".to_owned(),
|
||||||
span: 1..1,
|
span: 1..1,
|
||||||
})
|
})
|
||||||
|
@ -738,15 +793,15 @@ mod tests {
|
||||||
// Declaration and assignment of variables declared in an `if`
|
// Declaration and assignment of variables declared in an `if`
|
||||||
// statement should have no effect on those declared outside
|
// statement should have no effect on those declared outside
|
||||||
// of it.
|
// of it.
|
||||||
let mut env = ExecEnv::new();
|
let mut env = ExecEnv::<Standard>::default();
|
||||||
eval(
|
eval(
|
||||||
&mut env,
|
&mut env,
|
||||||
"dim foo 1; 2 =: foo; unless (never) { dim foo 3; 4 =: foo; }",
|
"foo dim 1; 2 =: foo; unless (never) { foo dim 3; 4 =: foo; }",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
env.get_var(&Spanned {
|
env.get_var_value(&Spanned {
|
||||||
item: "foo".to_owned(),
|
item: "foo".to_owned(),
|
||||||
span: 1..1,
|
span: 1..1,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,135 +1,60 @@
|
||||||
use logos::{Lexer, Logos};
|
use logos::{Lexer, Logos};
|
||||||
|
|
||||||
#[derive(Logos, Debug, PartialEq, Clone)]
|
#[derive(Logos, Debug, PartialEq, Eq, Clone)]
|
||||||
|
#[logos(skip r"[ \t\n\f]+")]
|
||||||
|
#[logos(skip r"owo .*")]
|
||||||
|
#[rustfmt::skip]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
// Symbols
|
// Symbols
|
||||||
#[token("(")]
|
#[token("(")] LeftParen,
|
||||||
LeftParen,
|
#[token(")")] RightParen,
|
||||||
|
#[token("[")] LeftBracket,
|
||||||
#[token(")")]
|
#[token("]")] RightBracket,
|
||||||
RightParen,
|
#[token("{")] LeftCurly,
|
||||||
|
#[token("}")] RightCurly,
|
||||||
#[token("[")]
|
#[token(";")] Semicolon,
|
||||||
LeftBracket,
|
#[token(",")] Comma,
|
||||||
|
|
||||||
#[token("]")]
|
|
||||||
RightBracket,
|
|
||||||
|
|
||||||
#[token("{")]
|
|
||||||
LeftCurly,
|
|
||||||
|
|
||||||
#[token("}")]
|
|
||||||
RightCurly,
|
|
||||||
|
|
||||||
#[token(";")]
|
|
||||||
Semicolon,
|
|
||||||
|
|
||||||
#[token(",")]
|
|
||||||
Comma,
|
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
#[token("+")]
|
#[token("+")] Plus,
|
||||||
Plus,
|
#[token("-")] Minus,
|
||||||
|
#[token("*")] Star,
|
||||||
#[token("-")]
|
#[token("/")] FwdSlash,
|
||||||
Minus,
|
#[token("=:")] Assign,
|
||||||
|
#[token("<=")] Arrow,
|
||||||
#[token("*")]
|
|
||||||
Star,
|
|
||||||
|
|
||||||
#[token("/")]
|
|
||||||
FwdSlash,
|
|
||||||
|
|
||||||
#[token("=:")]
|
|
||||||
Assign,
|
|
||||||
|
|
||||||
#[token("<=")]
|
|
||||||
Arrow,
|
|
||||||
|
|
||||||
// Logical operators
|
// Logical operators
|
||||||
#[token("<")]
|
#[token("<")] LessThan,
|
||||||
LessThan,
|
#[token(">")] GreaterThan,
|
||||||
|
#[token("=")] Equals,
|
||||||
#[token(">")]
|
#[token("ain't")] Aint,
|
||||||
GreaterThan,
|
|
||||||
|
|
||||||
#[token("=")]
|
|
||||||
Equals,
|
|
||||||
|
|
||||||
#[token("ain't")]
|
|
||||||
Aint,
|
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
#[token("functio")]
|
#[token("functio")] Functio,
|
||||||
Functio,
|
#[token("bff")] Bff,
|
||||||
|
#[token("dim")] Dim,
|
||||||
/// Brain fuck FFI
|
#[token("print")] Print,
|
||||||
#[token("bff")]
|
#[token("read")] Read,
|
||||||
Bff,
|
#[token("melo")] Melo,
|
||||||
|
#[token("T-Dark")] TDark,
|
||||||
/// Variable bro
|
|
||||||
#[token("dim")]
|
|
||||||
Dim,
|
|
||||||
|
|
||||||
/// Prints the preceding things
|
|
||||||
#[token("print")]
|
|
||||||
Print,
|
|
||||||
|
|
||||||
/// Read input into preceding variable
|
|
||||||
#[token("read")]
|
|
||||||
Read,
|
|
||||||
|
|
||||||
/// Ban the following variable from ever being used again
|
|
||||||
#[token("melo")]
|
|
||||||
Melo,
|
|
||||||
|
|
||||||
#[token("T-Dark")]
|
|
||||||
TDark,
|
|
||||||
|
|
||||||
// Control flow keywords
|
// Control flow keywords
|
||||||
#[token("unless")]
|
#[token("unless")] Unless,
|
||||||
Unless,
|
#[token("loop")] Loop,
|
||||||
|
#[token("enough")] Enough,
|
||||||
|
#[token("and again")] AndAgain,
|
||||||
|
#[token("finally")] Finally,
|
||||||
|
#[token("rlyeh")] Rlyeh,
|
||||||
|
|
||||||
#[token("loop")]
|
#[token("rickroll")] Rickroll,
|
||||||
Loop,
|
|
||||||
|
|
||||||
#[token("break")]
|
|
||||||
Break,
|
|
||||||
|
|
||||||
/// HopBack hops on the back of loop - like `continue`
|
|
||||||
#[token("hopback")]
|
|
||||||
HopBack,
|
|
||||||
|
|
||||||
/// Crash with random error (see discussion #17)
|
|
||||||
#[token("rlyeh")]
|
|
||||||
Rlyeh,
|
|
||||||
|
|
||||||
#[token("rickroll")]
|
|
||||||
Rickroll,
|
|
||||||
|
|
||||||
// Literals
|
// Literals
|
||||||
/// String
|
#[token("/*", get_string)] String(String),
|
||||||
#[token("/*", get_string)]
|
#[regex(r"-?[0-9]+", get_value)] Integer(isize),
|
||||||
String(String),
|
#[regex(r"\p{XID_Start}", get_value)] Char(char),
|
||||||
|
|
||||||
/// Integer
|
|
||||||
#[regex(r"-?[0-9]+", get_value)]
|
|
||||||
Integer(isize),
|
|
||||||
|
|
||||||
// A character (to be base-55 converted)
|
|
||||||
#[regex(r"\p{XID_Start}", get_value)]
|
|
||||||
Char(char),
|
|
||||||
|
|
||||||
/// An identifier
|
|
||||||
#[regex(r"\p{XID_Start}[\p{XID_Continue}]+", get_ident)]
|
#[regex(r"\p{XID_Start}[\p{XID_Continue}]+", get_ident)]
|
||||||
|
#[token("and ", |_| "and".to_owned())]
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
|
|
||||||
#[regex(r"owo .*")]
|
|
||||||
Comment,
|
|
||||||
|
|
||||||
#[regex(r"[ \t\n\f]+", logos::skip)]
|
|
||||||
#[error]
|
|
||||||
Error,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_value<T: std::str::FromStr>(lexer: &mut Lexer<Token>) -> Option<T> {
|
fn get_value<T: std::str::FromStr>(lexer: &mut Lexer<Token>) -> Option<T> {
|
||||||
|
@ -138,7 +63,31 @@ fn get_value<T: std::str::FromStr>(lexer: &mut Lexer<Token>) -> Option<T> {
|
||||||
|
|
||||||
fn get_string(lexer: &mut Lexer<Token>) -> Option<String> {
|
fn get_string(lexer: &mut Lexer<Token>) -> Option<String> {
|
||||||
lexer.bump(lexer.remainder().find("*/")?);
|
lexer.bump(lexer.remainder().find("*/")?);
|
||||||
let string = lexer.slice()[2..].to_owned();
|
|
||||||
|
let mut string = String::new();
|
||||||
|
let mut slice = &lexer.slice()[2..];
|
||||||
|
while let Some(escape_start) = slice.find('"') {
|
||||||
|
// Push predeceasing string
|
||||||
|
string.push_str(slice.get(..escape_start)?);
|
||||||
|
|
||||||
|
// Move slice behind escape start delimiter
|
||||||
|
slice = slice.get(escape_start + 1..)?;
|
||||||
|
|
||||||
|
// Get escape end delimiter position and parse string before it to
|
||||||
|
// a character from it's unicode value (base-12) and push it to string
|
||||||
|
let escape_end = slice.find('"')?;
|
||||||
|
string.push(
|
||||||
|
u32::from_str_radix(slice.get(..escape_end)?, 12)
|
||||||
|
.ok()
|
||||||
|
.and_then(char::from_u32)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Move slice behind escape end delimiter
|
||||||
|
slice = slice.get(escape_end + 1..)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push remaining string
|
||||||
|
string.push_str(slice);
|
||||||
lexer.bump(2);
|
lexer.bump(2);
|
||||||
|
|
||||||
Some(string)
|
Some(string)
|
||||||
|
@ -179,8 +128,17 @@ mod tests {
|
||||||
RightCurly,
|
RightCurly,
|
||||||
RightCurly,
|
RightCurly,
|
||||||
];
|
];
|
||||||
let lexer = Token::lexer(code);
|
|
||||||
let result: Vec<Token> = lexer.collect();
|
let result: Vec<_> = Token::lexer(code).collect::<Result<_, _>>().unwrap();
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn escapes() {
|
||||||
|
let code = r#"/*»"720B""722B""7195"«*/"#;
|
||||||
|
let expected = &[Token::String("»にゃぁ«".to_owned())];
|
||||||
|
|
||||||
|
let result: Vec<_> = Token::lexer(code).collect::<Result<_, _>>().unwrap();
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
#![doc = include_str!("../../README.md")]
|
//! The AbleScript language reference implementation. See
|
||||||
#![forbid(unsafe_code, clippy::unwrap_used)]
|
//! <https://git.ablecorp.us/AbleScript/able-script> for more
|
||||||
|
//! information.
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
#![cfg_attr(not(test), forbid(clippy::unwrap_used))]
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod host_interface;
|
||||||
pub mod interpret;
|
pub mod interpret;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod variables;
|
pub mod value;
|
||||||
|
|
||||||
mod base_55;
|
mod base_55;
|
||||||
mod brian;
|
mod brian;
|
||||||
|
|
|
@ -31,12 +31,12 @@ impl<'source> Parser<'source> {
|
||||||
let mut ast = vec![];
|
let mut ast = vec![];
|
||||||
while let Some(token) = self.lexer.next() {
|
while let Some(token) = self.lexer.next() {
|
||||||
match token {
|
match token {
|
||||||
// Ignore comments
|
|
||||||
Token::Comment => continue,
|
|
||||||
|
|
||||||
// T-Dark block (replace `lang` with `script`)
|
// T-Dark block (replace `lang` with `script`)
|
||||||
Token::TDark => ast.extend(self.tdark_flow()?),
|
Ok(Token::TDark) => ast.extend(self.tdark_flow()?),
|
||||||
token => ast.push(self.parse_stmt(token)?),
|
Ok(token) => ast.push(self.parse_stmt(token)?),
|
||||||
|
|
||||||
|
// Invalid token
|
||||||
|
Err(()) => return Err(Error::new(ErrorKind::InvalidToken, self.lexer.span())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(ast)
|
Ok(ast)
|
||||||
|
@ -46,15 +46,10 @@ impl<'source> Parser<'source> {
|
||||||
///
|
///
|
||||||
/// If EOF, return Error instead of None
|
/// If EOF, return Error instead of None
|
||||||
fn checked_next(&mut self) -> Result<Token, Error> {
|
fn checked_next(&mut self) -> Result<Token, Error> {
|
||||||
loop {
|
match self.lexer.next() {
|
||||||
match self
|
Some(Ok(t)) => Ok(t),
|
||||||
.lexer
|
Some(Err(())) => Err(Error::new(ErrorKind::InvalidToken, self.lexer.span())),
|
||||||
.next()
|
None => Err(Error::unexpected_eoi(self.lexer.span().start)),
|
||||||
.ok_or_else(|| Error::unexpected_eof(self.lexer.span().start))?
|
|
||||||
{
|
|
||||||
Token::Comment => (),
|
|
||||||
token => break Ok(token),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,40 +61,16 @@ impl<'source> Parser<'source> {
|
||||||
let start = self.lexer.span().start;
|
let start = self.lexer.span().start;
|
||||||
|
|
||||||
match token {
|
match token {
|
||||||
Token::Unless => Ok(Spanned::new(
|
Token::Unless => self.unless_flow(),
|
||||||
self.unless_flow()?,
|
Token::Functio => self.functio_flow(),
|
||||||
start..self.lexer.span().end,
|
Token::Bff => self.bff_flow(),
|
||||||
)),
|
Token::Melo => self.melo_flow(),
|
||||||
Token::Functio => Ok(Spanned::new(
|
Token::Loop => self.get_block().map(|body| Stmt::Loop { body }),
|
||||||
self.functio_flow()?,
|
Token::Enough => self.semicolon_terminated(Stmt::Enough),
|
||||||
start..self.lexer.span().end,
|
Token::AndAgain => self.semicolon_terminated(Stmt::AndAgain),
|
||||||
)),
|
Token::Finally => self.get_block().map(Stmt::Finally),
|
||||||
Token::Bff => Ok(Spanned::new(self.bff_flow()?, start..self.lexer.span().end)),
|
Token::Rlyeh => self.semicolon_terminated(Stmt::Rlyeh),
|
||||||
Token::Dim => Ok(Spanned::new(self.dim_flow()?, start..self.lexer.span().end)),
|
Token::Rickroll => self.semicolon_terminated(Stmt::Rickroll),
|
||||||
Token::Melo => Ok(Spanned::new(
|
|
||||||
self.melo_flow()?,
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
Token::Loop => Ok(Spanned::new(
|
|
||||||
self.loop_flow()?,
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
Token::Break => Ok(Spanned::new(
|
|
||||||
self.semicolon_terminated(Stmt::Break)?,
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
Token::HopBack => Ok(Spanned::new(
|
|
||||||
self.semicolon_terminated(Stmt::HopBack)?,
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
Token::Rlyeh => Ok(Spanned::new(
|
|
||||||
self.semicolon_terminated(Stmt::Rlyeh)?,
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
Token::Rickroll => Ok(Spanned::new(
|
|
||||||
self.semicolon_terminated(Stmt::Rickroll)?,
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
|
|
||||||
Token::Identifier(_)
|
Token::Identifier(_)
|
||||||
| Token::String(_)
|
| Token::String(_)
|
||||||
|
@ -107,16 +78,14 @@ impl<'source> Parser<'source> {
|
||||||
| Token::Char(_)
|
| Token::Char(_)
|
||||||
| Token::Aint
|
| Token::Aint
|
||||||
| Token::LeftBracket
|
| Token::LeftBracket
|
||||||
| Token::LeftParen => Ok(Spanned::new(
|
| Token::LeftParen => self.value_flow(token),
|
||||||
self.value_flow(token)?,
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
|
|
||||||
t => Err(Error {
|
t => Err(Error {
|
||||||
kind: ErrorKind::UnexpectedToken(t),
|
kind: ErrorKind::UnexpectedToken(t),
|
||||||
span: start..self.lexer.span().end,
|
span: start..self.lexer.span().end,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
.map(|stmt| Spanned::new(stmt, start..self.lexer.span().end))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Require statement to be semicolon terminated
|
/// Require statement to be semicolon terminated
|
||||||
|
@ -138,14 +107,9 @@ impl<'source> Parser<'source> {
|
||||||
/// Get an Identifier
|
/// Get an Identifier
|
||||||
fn get_ident(&mut self) -> Result<Spanned<String>, Error> {
|
fn get_ident(&mut self) -> Result<Spanned<String>, Error> {
|
||||||
match self.checked_next()? {
|
match self.checked_next()? {
|
||||||
Token::Identifier(ident) => Ok(Spanned::new(
|
Token::Identifier(ident) => {
|
||||||
if self.tdark {
|
Ok(Spanned::new(self.tdark_subst(ident), self.lexer.span()))
|
||||||
ident.replace("lang", "script")
|
}
|
||||||
} else {
|
|
||||||
ident
|
|
||||||
},
|
|
||||||
self.lexer.span(),
|
|
||||||
)),
|
|
||||||
t => Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())),
|
t => Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,50 +131,20 @@ impl<'source> Parser<'source> {
|
||||||
|
|
||||||
match token {
|
match token {
|
||||||
// Values
|
// Values
|
||||||
Token::Identifier(i) => Ok(Spanned::new(
|
Token::Identifier(i) => Ok(Expr::Variable(self.tdark_subst(i))),
|
||||||
Expr::Variable(if self.tdark {
|
Token::Integer(i) => Ok(Expr::Literal(Literal::Int(i))),
|
||||||
i.replace("lang", "script")
|
Token::String(s) => Ok(Expr::Literal(Literal::Str(self.tdark_subst(s)))),
|
||||||
} else {
|
Token::Char(c) => Ok(Expr::Literal(Literal::Char(c))),
|
||||||
i
|
|
||||||
}),
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
Token::Integer(i) => Ok(Spanned::new(
|
|
||||||
Expr::Literal(Literal::Int(i)),
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
Token::String(s) => Ok(Spanned::new(
|
|
||||||
Expr::Literal(Literal::Str(if self.tdark {
|
|
||||||
s.replace("lang", "script")
|
|
||||||
} else {
|
|
||||||
s
|
|
||||||
})),
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
Token::Char(c) => Ok(Spanned::new(
|
|
||||||
Expr::Literal(Literal::Char(c)),
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
|
|
||||||
Token::LeftBracket => match buf.take() {
|
Token::LeftBracket => match buf.take() {
|
||||||
Some(buf) => Ok(Spanned::new(
|
Some(buf) => self.index_flow(buf),
|
||||||
self.index_flow(buf)?,
|
None => self.cart_flow(),
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
None => Ok(Spanned::new(
|
|
||||||
self.cart_flow()?,
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Operations
|
// Operations
|
||||||
Token::Aint if buf.is_none() => Ok(Spanned::new(
|
Token::Aint if buf.is_none() => {
|
||||||
{
|
let next = self.checked_next()?;
|
||||||
let next = self.checked_next()?;
|
Ok(Expr::Aint(Box::new(self.parse_expr(next, buf)?)))
|
||||||
Expr::Aint(Box::new(self.parse_expr(next, buf)?))
|
}
|
||||||
},
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
|
|
||||||
Token::Plus
|
Token::Plus
|
||||||
| Token::Minus
|
| Token::Minus
|
||||||
|
@ -219,17 +153,15 @@ impl<'source> Parser<'source> {
|
||||||
| Token::Equals
|
| Token::Equals
|
||||||
| Token::LessThan
|
| Token::LessThan
|
||||||
| Token::GreaterThan
|
| Token::GreaterThan
|
||||||
| Token::Aint => Ok(Spanned::new(
|
| Token::Aint => self.binop_flow(
|
||||||
self.binop_flow(
|
BinOpKind::from_token(token).map_err(|e| Error::new(e, self.lexer.span()))?,
|
||||||
BinOpKind::from_token(token).map_err(|e| Error::new(e, self.lexer.span()))?,
|
buf,
|
||||||
buf,
|
),
|
||||||
)?,
|
|
||||||
start..self.lexer.span().end,
|
|
||||||
)),
|
|
||||||
|
|
||||||
Token::LeftParen => self.expr_flow(Token::RightParen),
|
Token::LeftParen => return self.expr_flow(Token::RightParen),
|
||||||
t => Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())),
|
t => Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())),
|
||||||
}
|
}
|
||||||
|
.map(|expr| Spanned::new(expr, start..self.lexer.span().end))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flow for creating carts
|
/// Flow for creating carts
|
||||||
|
@ -258,7 +190,7 @@ impl<'source> Parser<'source> {
|
||||||
cart.push((
|
cart.push((
|
||||||
value,
|
value,
|
||||||
buf.take().ok_or_else(|| {
|
buf.take().ok_or_else(|| {
|
||||||
Error::unexpected_eof(self.lexer.span().start)
|
Error::unexpected_eoi(self.lexer.span().start)
|
||||||
})?,
|
})?,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -268,7 +200,7 @@ impl<'source> Parser<'source> {
|
||||||
t => buf = Some(self.parse_expr(t, &mut buf)?),
|
t => buf = Some(self.parse_expr(t, &mut buf)?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ok_or_else(|| Error::unexpected_eof(self.lexer.span().start))?;
|
.ok_or_else(|| Error::unexpected_eoi(self.lexer.span().start))?;
|
||||||
|
|
||||||
cart.push((value, key));
|
cart.push((value, key));
|
||||||
}
|
}
|
||||||
|
@ -294,6 +226,10 @@ impl<'source> Parser<'source> {
|
||||||
}
|
}
|
||||||
None => break Expr::Len(Box::new(expr)),
|
None => break Expr::Len(Box::new(expr)),
|
||||||
},
|
},
|
||||||
|
Token::GreaterThan if buf.is_none() => {
|
||||||
|
self.require(Token::RightBracket)?;
|
||||||
|
break Expr::Keys(Box::new(expr));
|
||||||
|
}
|
||||||
token => buf = Some(self.parse_expr(token, &mut buf)?),
|
token => buf = Some(self.parse_expr(token, &mut buf)?),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -315,10 +251,7 @@ impl<'source> Parser<'source> {
|
||||||
.ok_or_else(|| Error::new(ErrorKind::MissingLhs, self.lexer.span()))?,
|
.ok_or_else(|| Error::new(ErrorKind::MissingLhs, self.lexer.span()))?,
|
||||||
),
|
),
|
||||||
rhs: {
|
rhs: {
|
||||||
let next = self
|
let next = self.checked_next()?;
|
||||||
.lexer
|
|
||||||
.next()
|
|
||||||
.ok_or_else(|| Error::unexpected_eof(self.lexer.span().start))?;
|
|
||||||
Box::new(self.parse_expr(next, &mut None)?)
|
Box::new(self.parse_expr(next, &mut None)?)
|
||||||
},
|
},
|
||||||
kind,
|
kind,
|
||||||
|
@ -368,14 +301,28 @@ impl<'source> Parser<'source> {
|
||||||
/// will parse it to function call or print statement.
|
/// will parse it to function call or print statement.
|
||||||
fn value_flow(&mut self, init: Token) -> Result<Stmt, Error> {
|
fn value_flow(&mut self, init: Token) -> Result<Stmt, Error> {
|
||||||
let mut buf = Some(self.parse_expr(init, &mut None)?);
|
let mut buf = Some(self.parse_expr(init, &mut None)?);
|
||||||
let r = loop {
|
Ok(loop {
|
||||||
match self.checked_next()? {
|
match self.checked_next()? {
|
||||||
// Print to stdout
|
// Print to stdout
|
||||||
Token::Print => {
|
Token::Print => {
|
||||||
let stmt = Stmt::Print(buf.take().ok_or_else(|| {
|
break Stmt::Print {
|
||||||
Error::new(ErrorKind::UnexpectedToken(Token::Print), self.lexer.span())
|
expr: buf.take().ok_or_else(|| {
|
||||||
})?);
|
Error::new(ErrorKind::UnexpectedToken(Token::Print), self.lexer.span())
|
||||||
break self.semicolon_terminated(stmt)?;
|
})?,
|
||||||
|
newline: match self.checked_next()? {
|
||||||
|
Token::Semicolon => true,
|
||||||
|
Token::Minus => {
|
||||||
|
self.require(Token::Semicolon)?;
|
||||||
|
false
|
||||||
|
}
|
||||||
|
token => {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::UnexpectedToken(token),
|
||||||
|
self.lexer.span(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functio call
|
// Functio call
|
||||||
|
@ -388,7 +335,32 @@ impl<'source> Parser<'source> {
|
||||||
})?)?;
|
})?)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variable Assignment
|
// Variable declaration
|
||||||
|
Token::Dim => {
|
||||||
|
return match buf.take() {
|
||||||
|
Some(Spanned {
|
||||||
|
item: Expr::Variable(ident),
|
||||||
|
span,
|
||||||
|
}) => Ok(Stmt::Dim {
|
||||||
|
ident: Spanned::new(ident, span),
|
||||||
|
init: {
|
||||||
|
let mut init = None;
|
||||||
|
loop {
|
||||||
|
match self.checked_next()? {
|
||||||
|
Token::Semicolon => break init,
|
||||||
|
token => init = Some(self.parse_expr(token, &mut init)?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
_ => Err(Error::new(
|
||||||
|
ErrorKind::UnexpectedToken(Token::Dim),
|
||||||
|
self.lexer.span(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable assignment
|
||||||
Token::Assign => {
|
Token::Assign => {
|
||||||
return match buf.take() {
|
return match buf.take() {
|
||||||
Some(expr) => self.assignment_flow(expr),
|
Some(expr) => self.assignment_flow(expr),
|
||||||
|
@ -414,9 +386,7 @@ impl<'source> Parser<'source> {
|
||||||
|
|
||||||
t => buf = Some(self.parse_expr(t, &mut buf)?),
|
t => buf = Some(self.parse_expr(t, &mut buf)?),
|
||||||
}
|
}
|
||||||
};
|
})
|
||||||
|
|
||||||
Ok(r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Unless flow
|
/// Parse Unless flow
|
||||||
|
@ -492,16 +462,10 @@ impl<'source> Parser<'source> {
|
||||||
|
|
||||||
let mut code: Vec<u8> = vec![];
|
let mut code: Vec<u8> = vec![];
|
||||||
loop {
|
loop {
|
||||||
match self.checked_next()? {
|
match self.lexer.next() {
|
||||||
Token::Plus
|
Some(Ok(Token::RightCurly)) => break,
|
||||||
| Token::Minus
|
Some(_) => code.push(self.lexer.slice().as_bytes()[0]),
|
||||||
| Token::Comma
|
None => return Err(Error::unexpected_eoi(self.lexer.span().start)),
|
||||||
| Token::LeftBracket
|
|
||||||
| Token::RightBracket
|
|
||||||
| Token::LessThan
|
|
||||||
| Token::GreaterThan => code.push(self.lexer.slice().as_bytes()[0]),
|
|
||||||
Token::RightCurly => break,
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,20 +509,6 @@ impl<'source> Parser<'source> {
|
||||||
Ok(Stmt::Call { expr, args })
|
Ok(Stmt::Call { expr, args })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse variable declaration
|
|
||||||
fn dim_flow(&mut self) -> Result<Stmt, Error> {
|
|
||||||
let ident = self.get_ident()?;
|
|
||||||
let mut init = None;
|
|
||||||
loop {
|
|
||||||
match self.checked_next()? {
|
|
||||||
Token::Semicolon => break,
|
|
||||||
t => init = Some(self.parse_expr(t, &mut init)?),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Stmt::Dim { ident, init })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse assignment to assignable
|
/// Parse assignment to assignable
|
||||||
fn assignment_flow(&mut self, value: Spanned<Expr>) -> Result<Stmt, Error> {
|
fn assignment_flow(&mut self, value: Spanned<Expr>) -> Result<Stmt, Error> {
|
||||||
let ident = self.get_ident()?;
|
let ident = self.get_ident()?;
|
||||||
|
@ -595,13 +545,37 @@ impl<'source> Parser<'source> {
|
||||||
self.semicolon_terminated(Stmt::Melo(ident))
|
self.semicolon_terminated(Stmt::Melo(ident))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse loop flow
|
/// Perform lang -> script substitution if in T-Dark block
|
||||||
///
|
fn tdark_subst(&self, mut string: String) -> String {
|
||||||
/// `loop` is an infinite loop, no condition, only body
|
if self.tdark {
|
||||||
fn loop_flow(&mut self) -> Result<Stmt, Error> {
|
if let Some(pos) = string.to_lowercase().find("lang") {
|
||||||
Ok(Stmt::Loop {
|
let range = pos..pos + 4;
|
||||||
body: self.get_block()?,
|
let mut count_upper = 0_u8;
|
||||||
})
|
string.replace_range(
|
||||||
|
range.clone(),
|
||||||
|
&(string[range]
|
||||||
|
.chars()
|
||||||
|
.zip("scri".chars())
|
||||||
|
.map(|(lc, sc)| {
|
||||||
|
if lc.is_uppercase() {
|
||||||
|
count_upper += 1;
|
||||||
|
sc.to_ascii_uppercase()
|
||||||
|
} else {
|
||||||
|
sc.to_ascii_lowercase()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<String>()
|
||||||
|
+ match count_upper {
|
||||||
|
0 | 1 => "pt",
|
||||||
|
2 if rand::random() => "Pt",
|
||||||
|
2 => "pT",
|
||||||
|
_ => "PT",
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -618,40 +592,43 @@ mod tests {
|
||||||
fn simple_math() {
|
fn simple_math() {
|
||||||
let code = "1 * (num + 3) / 666 print;";
|
let code = "1 * (num + 3) / 666 print;";
|
||||||
let expected = &[Spanned {
|
let expected = &[Spanned {
|
||||||
item: Stmt::Print(Spanned {
|
item: Stmt::Print {
|
||||||
item: Expr::BinOp {
|
expr: Spanned {
|
||||||
lhs: Box::new(Spanned {
|
item: Expr::BinOp {
|
||||||
item: Expr::BinOp {
|
lhs: Box::new(Spanned {
|
||||||
lhs: Box::new(Spanned {
|
item: Expr::BinOp {
|
||||||
item: Expr::Literal(Literal::Int(1)),
|
lhs: Box::new(Spanned {
|
||||||
span: 0..1,
|
item: Expr::Literal(Literal::Int(1)),
|
||||||
}),
|
span: 0..1,
|
||||||
rhs: Box::new(Spanned {
|
}),
|
||||||
item: Expr::BinOp {
|
rhs: Box::new(Spanned {
|
||||||
lhs: Box::new(Spanned {
|
item: Expr::BinOp {
|
||||||
item: Expr::Variable("num".to_owned()),
|
lhs: Box::new(Spanned {
|
||||||
span: 5..6,
|
item: Expr::Variable("num".to_owned()),
|
||||||
}),
|
span: 5..6,
|
||||||
rhs: Box::new(Spanned {
|
}),
|
||||||
item: Expr::Literal(Literal::Int(3)),
|
rhs: Box::new(Spanned {
|
||||||
span: 9..10,
|
item: Expr::Literal(Literal::Int(3)),
|
||||||
}),
|
span: 9..10,
|
||||||
kind: BinOpKind::Add,
|
}),
|
||||||
},
|
kind: BinOpKind::Add,
|
||||||
span: 5..10,
|
},
|
||||||
}),
|
span: 5..10,
|
||||||
kind: BinOpKind::Multiply,
|
}),
|
||||||
},
|
kind: BinOpKind::Multiply,
|
||||||
span: 0..11,
|
},
|
||||||
}),
|
span: 0..11,
|
||||||
rhs: Box::new(Spanned {
|
}),
|
||||||
item: Expr::Literal(Literal::Int(666)),
|
rhs: Box::new(Spanned {
|
||||||
span: 14..17,
|
item: Expr::Literal(Literal::Int(666)),
|
||||||
}),
|
span: 14..17,
|
||||||
kind: BinOpKind::Divide,
|
}),
|
||||||
|
kind: BinOpKind::Divide,
|
||||||
|
},
|
||||||
|
span: 0..17,
|
||||||
},
|
},
|
||||||
span: 0..17,
|
newline: true,
|
||||||
}),
|
},
|
||||||
span: 0..24,
|
span: 0..24,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
@ -661,16 +638,16 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn variable_declaration() {
|
fn variable_declaration() {
|
||||||
let code = "dim var 42;";
|
let code = "var dim 42;";
|
||||||
let expected = &[Spanned {
|
let expected = &[Spanned {
|
||||||
item: Stmt::Dim {
|
item: Stmt::Dim {
|
||||||
ident: Spanned {
|
ident: Spanned {
|
||||||
item: "var".to_owned(),
|
item: "var".to_owned(),
|
||||||
span: 4..5,
|
span: 0..3,
|
||||||
},
|
},
|
||||||
init: Some(Spanned {
|
init: Some(Spanned {
|
||||||
item: Expr::Literal(Literal::Int(42)),
|
item: Expr::Literal(Literal::Int(42)),
|
||||||
span: 8..10,
|
span: 4..6,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
span: 0..11,
|
span: 0..11,
|
||||||
|
@ -700,10 +677,13 @@ mod tests {
|
||||||
span: 8..21,
|
span: 8..21,
|
||||||
},
|
},
|
||||||
body: vec![Spanned {
|
body: vec![Spanned {
|
||||||
item: Stmt::Print(Spanned {
|
item: Stmt::Print {
|
||||||
item: Expr::Literal(Literal::Str("Buy Able products!".to_owned())),
|
expr: Spanned {
|
||||||
span: 25..47,
|
item: Expr::Literal(Literal::Str("Buy Able products!".to_owned())),
|
||||||
}),
|
span: 25..47,
|
||||||
|
},
|
||||||
|
newline: true,
|
||||||
|
},
|
||||||
span: 25..54,
|
span: 25..54,
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
|
@ -716,12 +696,12 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tdark() {
|
fn tdark() {
|
||||||
let code = "T-Dark { dim lang /*lang*/ + lang; }";
|
let code = "T-Dark { lang dim /*lang*/ + lang; }";
|
||||||
let expected = &[Spanned {
|
let expected = &[Spanned {
|
||||||
item: Stmt::Dim {
|
item: Stmt::Dim {
|
||||||
ident: Spanned {
|
ident: Spanned {
|
||||||
item: "script".to_owned(),
|
item: "script".to_owned(),
|
||||||
span: 13..17,
|
span: 9..15,
|
||||||
},
|
},
|
||||||
init: Some(Spanned {
|
init: Some(Spanned {
|
||||||
item: Expr::BinOp {
|
item: Expr::BinOp {
|
||||||
|
@ -749,41 +729,44 @@ mod tests {
|
||||||
fn cart_construction() {
|
fn cart_construction() {
|
||||||
let code = "[/*able*/ <= 1, /*script*/ <= 3 - 1] print;";
|
let code = "[/*able*/ <= 1, /*script*/ <= 3 - 1] print;";
|
||||||
let expected = &[Spanned {
|
let expected = &[Spanned {
|
||||||
item: Stmt::Print(Spanned {
|
item: Stmt::Print {
|
||||||
item: Expr::Cart(vec![
|
expr: Spanned {
|
||||||
(
|
item: Expr::Cart(vec![
|
||||||
Spanned {
|
(
|
||||||
item: Expr::Literal(Literal::Str("able".to_owned())),
|
Spanned {
|
||||||
span: 1..7,
|
item: Expr::Literal(Literal::Str("able".to_owned())),
|
||||||
},
|
span: 1..7,
|
||||||
Spanned {
|
|
||||||
item: Expr::Literal(Literal::Int(1)),
|
|
||||||
span: 11..12,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Spanned {
|
|
||||||
item: Expr::Literal(Literal::Str("script".to_owned())),
|
|
||||||
span: 14..22,
|
|
||||||
},
|
|
||||||
Spanned {
|
|
||||||
item: Expr::BinOp {
|
|
||||||
kind: BinOpKind::Subtract,
|
|
||||||
lhs: Box::new(Spanned {
|
|
||||||
item: Expr::Literal(Literal::Int(3)),
|
|
||||||
span: 26..27,
|
|
||||||
}),
|
|
||||||
rhs: Box::new(Spanned {
|
|
||||||
item: Expr::Literal(Literal::Int(1)),
|
|
||||||
span: 30..31,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
span: 26..31,
|
Spanned {
|
||||||
},
|
item: Expr::Literal(Literal::Int(1)),
|
||||||
),
|
span: 11..12,
|
||||||
]),
|
},
|
||||||
span: 0..32,
|
),
|
||||||
}),
|
(
|
||||||
|
Spanned {
|
||||||
|
item: Expr::Literal(Literal::Str("script".to_owned())),
|
||||||
|
span: 14..22,
|
||||||
|
},
|
||||||
|
Spanned {
|
||||||
|
item: Expr::BinOp {
|
||||||
|
kind: BinOpKind::Subtract,
|
||||||
|
lhs: Box::new(Spanned {
|
||||||
|
item: Expr::Literal(Literal::Int(3)),
|
||||||
|
span: 26..27,
|
||||||
|
}),
|
||||||
|
rhs: Box::new(Spanned {
|
||||||
|
item: Expr::Literal(Literal::Int(1)),
|
||||||
|
span: 30..31,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
span: 26..31,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
span: 0..32,
|
||||||
|
},
|
||||||
|
newline: true,
|
||||||
|
},
|
||||||
span: 0..39,
|
span: 0..39,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
@ -795,28 +778,31 @@ mod tests {
|
||||||
fn cart_index() {
|
fn cart_index() {
|
||||||
let code = "[/*able*/ <= /*ablecorp*/][/*ablecorp*/] print;";
|
let code = "[/*able*/ <= /*ablecorp*/][/*ablecorp*/] print;";
|
||||||
let expected = &[Spanned {
|
let expected = &[Spanned {
|
||||||
item: Stmt::Print(Spanned {
|
item: Stmt::Print {
|
||||||
item: Expr::Index {
|
expr: Spanned {
|
||||||
expr: Box::new(Spanned {
|
item: Expr::Index {
|
||||||
item: Expr::Cart(vec![(
|
expr: Box::new(Spanned {
|
||||||
Spanned {
|
item: Expr::Cart(vec![(
|
||||||
item: Expr::Literal(Literal::Str("able".to_owned())),
|
Spanned {
|
||||||
span: 1..7,
|
item: Expr::Literal(Literal::Str("able".to_owned())),
|
||||||
},
|
span: 1..7,
|
||||||
Spanned {
|
},
|
||||||
item: Expr::Literal(Literal::Str("ablecorp".to_owned())),
|
Spanned {
|
||||||
span: 11..21,
|
item: Expr::Literal(Literal::Str("ablecorp".to_owned())),
|
||||||
},
|
span: 11..21,
|
||||||
)]),
|
},
|
||||||
span: 0..22,
|
)]),
|
||||||
}),
|
span: 0..22,
|
||||||
index: Box::new(Spanned {
|
}),
|
||||||
item: Expr::Literal(Literal::Str("ablecorp".to_owned())),
|
index: Box::new(Spanned {
|
||||||
span: 23..33,
|
item: Expr::Literal(Literal::Str("ablecorp".to_owned())),
|
||||||
}),
|
span: 23..33,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
span: 0..34,
|
||||||
},
|
},
|
||||||
span: 0..34,
|
newline: true,
|
||||||
}),
|
},
|
||||||
span: 0..41,
|
span: 0..41,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
|
279
ablescript/src/value/coercions.rs
Normal file
279
ablescript/src/value/coercions.rs
Normal file
|
@ -0,0 +1,279 @@
|
||||||
|
use super::{functio::FunctioChainKind, Abool, Cart, Functio, Value, ValueRef};
|
||||||
|
use crate::{brian::INSTRUCTION_MAPPINGS, consts};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
/// Coerce a value to an integer.
|
||||||
|
pub fn into_isize(self) -> isize {
|
||||||
|
match self {
|
||||||
|
Value::Nul => consts::ANSWER,
|
||||||
|
Value::Undefined => rand::random(),
|
||||||
|
Value::Str(text) => text
|
||||||
|
.parse()
|
||||||
|
.unwrap_or_else(|_| text.chars().map(|cr| cr as isize).sum()),
|
||||||
|
Value::Int(i) => i,
|
||||||
|
Value::Abool(a) => a as _,
|
||||||
|
Value::Functio(f) => match f {
|
||||||
|
Functio::Bf {
|
||||||
|
instructions,
|
||||||
|
tape_len,
|
||||||
|
} => {
|
||||||
|
instructions.into_iter().map(|x| x as isize).sum::<isize>() * tape_len as isize
|
||||||
|
}
|
||||||
|
Functio::Able { params, body } => {
|
||||||
|
params
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.bytes().map(|x| x as isize).sum::<isize>())
|
||||||
|
.sum::<isize>()
|
||||||
|
+ body.len() as isize
|
||||||
|
}
|
||||||
|
Functio::Builtin(b) => (b.fn_addr() + b.arity) as _,
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let (lf, rf) = *functios;
|
||||||
|
Value::Functio(lf).into_isize()
|
||||||
|
+ Value::Functio(rf).into_isize()
|
||||||
|
* match kind {
|
||||||
|
FunctioChainKind::Equal => -1,
|
||||||
|
FunctioChainKind::ByArity => 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Functio::Eval(code) => code.bytes().map(|x| x as isize).sum(),
|
||||||
|
},
|
||||||
|
Value::Cart(c) => c
|
||||||
|
.into_iter()
|
||||||
|
.map(|(i, v)| i.into_isize() * v.borrow().clone().into_isize())
|
||||||
|
.sum(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Coerce a value to an aboolean.
|
||||||
|
pub fn into_abool(self) -> Abool {
|
||||||
|
match self {
|
||||||
|
Value::Nul => Abool::Never,
|
||||||
|
Value::Undefined => Abool::Sometimes,
|
||||||
|
Value::Str(s) => match s.to_lowercase().as_str() {
|
||||||
|
"never" | "no" | "🇳🇴" => Abool::Never,
|
||||||
|
"sometimes" => Abool::Sometimes,
|
||||||
|
"always" | "yes" => Abool::Always,
|
||||||
|
s => (!s.is_empty()).into(),
|
||||||
|
},
|
||||||
|
Value::Int(x) => match x.cmp(&0) {
|
||||||
|
std::cmp::Ordering::Less => Abool::Never,
|
||||||
|
std::cmp::Ordering::Equal => Abool::Sometimes,
|
||||||
|
std::cmp::Ordering::Greater => Abool::Always,
|
||||||
|
},
|
||||||
|
Value::Abool(a) => a,
|
||||||
|
Value::Functio(f) => match f {
|
||||||
|
Functio::Bf {
|
||||||
|
instructions,
|
||||||
|
tape_len,
|
||||||
|
} => Value::Int(
|
||||||
|
(instructions.iter().map(|x| *x as usize).sum::<usize>() * tape_len) as _,
|
||||||
|
)
|
||||||
|
.into_abool(),
|
||||||
|
Functio::Able { params, body } => {
|
||||||
|
let str_to_isize =
|
||||||
|
|x: String| -> isize { x.as_bytes().iter().map(|x| *x as isize).sum() };
|
||||||
|
|
||||||
|
let params: isize = params.into_iter().map(str_to_isize).sum();
|
||||||
|
let body: isize = body
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| format!("{:?}", x))
|
||||||
|
.map(str_to_isize)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
Value::Int((params + body) % 3 - 1).into_abool()
|
||||||
|
}
|
||||||
|
Functio::Builtin(b) => (b.fn_addr() % b.arity == 0).into(),
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let (lhs, rhs) = *functios;
|
||||||
|
match kind {
|
||||||
|
FunctioChainKind::Equal => {
|
||||||
|
Value::Abool(Value::Functio(lhs).into_abool())
|
||||||
|
+ Value::Abool(Value::Functio(rhs).into_abool())
|
||||||
|
}
|
||||||
|
FunctioChainKind::ByArity => {
|
||||||
|
Value::Abool(Value::Functio(lhs).into_abool())
|
||||||
|
* Value::Abool(Value::Functio(rhs).into_abool())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.into_abool()
|
||||||
|
}
|
||||||
|
Functio::Eval(code) => Value::Str(code).into_abool(),
|
||||||
|
},
|
||||||
|
Value::Cart(c) => {
|
||||||
|
if c.is_empty() {
|
||||||
|
Abool::Never
|
||||||
|
} else {
|
||||||
|
Abool::Always
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Coerce a value to a functio.
|
||||||
|
pub fn into_functio(self) -> Functio {
|
||||||
|
match self {
|
||||||
|
Value::Nul | Value::Undefined => Functio::Able {
|
||||||
|
body: vec![],
|
||||||
|
params: vec![],
|
||||||
|
},
|
||||||
|
Value::Str(s) => Functio::Eval(s),
|
||||||
|
Value::Int(i) => Functio::Bf {
|
||||||
|
instructions: {
|
||||||
|
std::iter::successors(Some(i as usize), |i| {
|
||||||
|
Some(i / INSTRUCTION_MAPPINGS.len())
|
||||||
|
})
|
||||||
|
.take_while(|&i| i != 0)
|
||||||
|
.map(|i| INSTRUCTION_MAPPINGS[i % INSTRUCTION_MAPPINGS.len()])
|
||||||
|
.collect()
|
||||||
|
},
|
||||||
|
tape_len: crate::brian::DEFAULT_TAPE_SIZE_LIMIT,
|
||||||
|
},
|
||||||
|
Value::Abool(a) => Functio::Eval(match a {
|
||||||
|
Abool::Never => "".to_owned(),
|
||||||
|
Abool::Sometimes => {
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
let mut str_chars: Vec<_> = "Buy Able Products!".chars().collect();
|
||||||
|
str_chars.shuffle(&mut rand::thread_rng());
|
||||||
|
|
||||||
|
format!(r#""{}"print;"#, str_chars.iter().collect::<String>())
|
||||||
|
}
|
||||||
|
Abool::Always => r#"loop{"Buy Able products!"print;}"#.to_owned(),
|
||||||
|
}),
|
||||||
|
Value::Functio(f) => f,
|
||||||
|
Value::Cart(c) => {
|
||||||
|
let kind = if let Some(114514) = c
|
||||||
|
.get(&Value::Str("1452251871514141792252515212116".to_owned()))
|
||||||
|
.map(|x| x.borrow().to_owned().into_isize())
|
||||||
|
{
|
||||||
|
FunctioChainKind::Equal
|
||||||
|
} else {
|
||||||
|
FunctioChainKind::ByArity
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut cart_vec = c.iter().collect::<Vec<_>>();
|
||||||
|
cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less));
|
||||||
|
|
||||||
|
cart_vec
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_, x)| x.borrow().to_owned().into_functio())
|
||||||
|
.reduce(|acc, x| Functio::Chain {
|
||||||
|
functios: Box::new((acc, x)),
|
||||||
|
kind,
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| Functio::Eval(r#""Buy Able Products!"print;"#.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Coerce a value into a cart.
|
||||||
|
pub fn into_cart(self) -> Cart {
|
||||||
|
match self {
|
||||||
|
Value::Nul => HashMap::new(),
|
||||||
|
Value::Undefined => [(Value::Undefined, ValueRef::new(Value::Undefined))]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
Value::Str(s) => s
|
||||||
|
.chars()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| {
|
||||||
|
(
|
||||||
|
Value::Int(i as isize + 1),
|
||||||
|
ValueRef::new(Value::Str(x.to_string())),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
Value::Int(i) => Value::Str(i.to_string()).into_cart(),
|
||||||
|
Value::Abool(a) => Value::Str(a.to_string()).into_cart(),
|
||||||
|
Value::Functio(f) => match f {
|
||||||
|
Functio::Able { params, body } => {
|
||||||
|
let params: Cart = params
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| (Value::Int(i as isize + 1), ValueRef::new(Value::Str(x))))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let body: Cart = body
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| {
|
||||||
|
(
|
||||||
|
Value::Int(i as isize + 1),
|
||||||
|
ValueRef::new(Value::Str(format!("{:?}", x))),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut cart = HashMap::new();
|
||||||
|
cart.insert(
|
||||||
|
Value::Str("params".to_owned()),
|
||||||
|
ValueRef::new(Value::Cart(params)),
|
||||||
|
);
|
||||||
|
|
||||||
|
cart.insert(
|
||||||
|
Value::Str("body".to_owned()),
|
||||||
|
ValueRef::new(Value::Cart(body)),
|
||||||
|
);
|
||||||
|
|
||||||
|
cart
|
||||||
|
}
|
||||||
|
Functio::Bf {
|
||||||
|
instructions,
|
||||||
|
tape_len,
|
||||||
|
} => {
|
||||||
|
let mut cart: Cart = instructions
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| {
|
||||||
|
(
|
||||||
|
Value::Int(i as isize + 1),
|
||||||
|
ValueRef::new(
|
||||||
|
char::from_u32(x as u32)
|
||||||
|
.map(|x| Value::Str(x.to_string()))
|
||||||
|
.unwrap_or(Value::Nul),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
cart.insert(
|
||||||
|
Value::Str("tapelen".to_owned()),
|
||||||
|
ValueRef::new(Value::Int(tape_len as _)),
|
||||||
|
);
|
||||||
|
cart
|
||||||
|
}
|
||||||
|
Functio::Builtin(b) => {
|
||||||
|
let mut cart = HashMap::new();
|
||||||
|
cart.insert(
|
||||||
|
Value::Str("addr".to_owned()),
|
||||||
|
ValueRef::new(Value::Cart(Value::Int(b.fn_addr() as _).into_cart())),
|
||||||
|
);
|
||||||
|
|
||||||
|
cart.insert(
|
||||||
|
Value::Str("arity".to_owned()),
|
||||||
|
ValueRef::new(Value::Int(b.arity as _)),
|
||||||
|
);
|
||||||
|
|
||||||
|
cart
|
||||||
|
}
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let (lhs, rhs) = *functios;
|
||||||
|
match kind {
|
||||||
|
FunctioChainKind::Equal => {
|
||||||
|
Value::Cart(Value::Functio(lhs).into_cart())
|
||||||
|
+ Value::Cart(Value::Functio(rhs).into_cart())
|
||||||
|
}
|
||||||
|
FunctioChainKind::ByArity => {
|
||||||
|
Value::Cart(Value::Functio(lhs).into_cart())
|
||||||
|
* Value::Cart(Value::Functio(rhs).into_cart())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.into_cart()
|
||||||
|
}
|
||||||
|
Functio::Eval(s) => Value::Str(s).into_cart(),
|
||||||
|
},
|
||||||
|
Value::Cart(c) => c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
113
ablescript/src/value/functio.rs
Normal file
113
ablescript/src/value/functio.rs
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
use super::ValueRef;
|
||||||
|
use crate::ast::Block;
|
||||||
|
use std::{hash::Hash, rc::Rc};
|
||||||
|
|
||||||
|
type BuiltinRc = Rc<dyn Fn(&[ValueRef]) -> Result<(), crate::error::ErrorKind>>;
|
||||||
|
|
||||||
|
/// AbleScript Function
|
||||||
|
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||||
|
pub enum Functio {
|
||||||
|
/// BF instructions and a length of the type
|
||||||
|
///
|
||||||
|
/// Takes input bytes as parameters
|
||||||
|
Bf {
|
||||||
|
instructions: Vec<u8>,
|
||||||
|
tape_len: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Regular AbleScript functio
|
||||||
|
///
|
||||||
|
/// Consists of parameter name mapping and AST
|
||||||
|
Able { params: Vec<String>, body: Block },
|
||||||
|
|
||||||
|
/// Builtin Rust functio
|
||||||
|
Builtin(BuiltinFunctio),
|
||||||
|
|
||||||
|
/// Chained functio pair
|
||||||
|
Chain {
|
||||||
|
functios: Box<(Functio, Functio)>,
|
||||||
|
kind: FunctioChainKind,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Code to be parsed and then executed in current scope
|
||||||
|
Eval(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Functio {
|
||||||
|
pub fn arity(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Functio::Bf {
|
||||||
|
instructions: _,
|
||||||
|
tape_len: _,
|
||||||
|
} => 0,
|
||||||
|
Functio::Able { params, body: _ } => params.len(),
|
||||||
|
Functio::Builtin(b) => b.arity,
|
||||||
|
Functio::Chain { functios, kind: _ } => functios.0.arity() + functios.1.arity(),
|
||||||
|
Functio::Eval(_) => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Built-in Rust functio
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BuiltinFunctio {
|
||||||
|
pub(super) function: BuiltinRc,
|
||||||
|
pub(super) arity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuiltinFunctio {
|
||||||
|
/// Wrap a Rust function into AbleScript's built-in functio
|
||||||
|
///
|
||||||
|
/// Arity used for functio chaining, recommend value for variadic
|
||||||
|
/// functions is the accepted minimum.
|
||||||
|
pub fn new<F>(f: F, arity: usize) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(&[ValueRef]) -> Result<(), crate::error::ErrorKind> + 'static,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
function: Rc::new(f),
|
||||||
|
arity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call(&self, args: &[ValueRef]) -> Result<(), crate::error::ErrorKind> {
|
||||||
|
(self.function)(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fn_addr(&self) -> usize {
|
||||||
|
Rc::as_ptr(&self.function) as *const () as _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for BuiltinFunctio {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("BuiltinFunctio")
|
||||||
|
.field("function", &"built-in")
|
||||||
|
.field("arity", &self.arity)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for BuiltinFunctio {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.fn_addr() == other.fn_addr() && self.arity == other.arity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for BuiltinFunctio {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.fn_addr().hash(state);
|
||||||
|
self.arity.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A method of distributting parameters across functio chain
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
|
||||||
|
pub enum FunctioChainKind {
|
||||||
|
/// All parameters are equally distributed
|
||||||
|
Equal,
|
||||||
|
|
||||||
|
/// Parameters are distributed to members of chain
|
||||||
|
/// by their arity
|
||||||
|
ByArity,
|
||||||
|
}
|
202
ablescript/src/value/mod.rs
Normal file
202
ablescript/src/value/mod.rs
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
pub mod functio;
|
||||||
|
|
||||||
|
mod coercions;
|
||||||
|
mod ops;
|
||||||
|
|
||||||
|
pub use functio::Functio;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
cell::{Ref, RefCell, RefMut},
|
||||||
|
collections::HashMap,
|
||||||
|
fmt::Display,
|
||||||
|
hash::Hash,
|
||||||
|
io::Write,
|
||||||
|
mem::discriminant,
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type Cart = HashMap<Value, ValueRef>;
|
||||||
|
|
||||||
|
/// AbleScript Value
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub enum Value {
|
||||||
|
#[default]
|
||||||
|
Nul,
|
||||||
|
Undefined,
|
||||||
|
Str(String),
|
||||||
|
Int(isize),
|
||||||
|
Abool(Abool),
|
||||||
|
Functio(Functio),
|
||||||
|
Cart(Cart),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Value {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
discriminant(self).hash(state);
|
||||||
|
match self {
|
||||||
|
Value::Nul | Value::Undefined => (),
|
||||||
|
Value::Str(v) => v.hash(state),
|
||||||
|
Value::Int(v) => v.hash(state),
|
||||||
|
Value::Abool(v) => v.to_string().hash(state),
|
||||||
|
Value::Functio(statements) => statements.hash(state),
|
||||||
|
Value::Cart(_) => self.to_string().hash(state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
/// Write an AbleScript value to a Brainfuck input stream by
|
||||||
|
/// coercing the value to an integer, then truncating that integer
|
||||||
|
/// to a single byte, then writing that byte. This should only be
|
||||||
|
/// called on `Write`rs that cannot fail, e.g., `Vec<u8>`, because
|
||||||
|
/// any IO errors will cause a panic.
|
||||||
|
pub fn bf_write(&self, stream: &mut impl Write) {
|
||||||
|
stream
|
||||||
|
.write_all(&[self.clone().into_isize() as u8])
|
||||||
|
.expect("Failed to write to Brainfuck input");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Three-state logic value
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
|
||||||
|
pub enum Abool {
|
||||||
|
Never = -1,
|
||||||
|
Sometimes = 0,
|
||||||
|
Always = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Abool {
|
||||||
|
pub fn to_bool(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Always => true,
|
||||||
|
Self::Sometimes if rand::random() => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Abool {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Abool::Never => write!(f, "never"),
|
||||||
|
Abool::Sometimes => write!(f, "sometimes"),
|
||||||
|
Abool::Always => write!(f, "always"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for Abool {
|
||||||
|
fn from(b: bool) -> Self {
|
||||||
|
if b {
|
||||||
|
Abool::Always
|
||||||
|
} else {
|
||||||
|
Abool::Never
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Value {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Value::Nul => write!(f, "nul"),
|
||||||
|
Value::Undefined => write!(f, "undefined"),
|
||||||
|
Value::Str(v) => write!(f, "{}", v),
|
||||||
|
Value::Int(v) => write!(f, "{}", v),
|
||||||
|
Value::Abool(v) => write!(f, "{}", v),
|
||||||
|
Value::Functio(v) => match v {
|
||||||
|
Functio::Bf {
|
||||||
|
instructions,
|
||||||
|
tape_len,
|
||||||
|
} => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"({}) {}",
|
||||||
|
tape_len,
|
||||||
|
String::from_utf8(instructions.to_owned())
|
||||||
|
.expect("Brainfuck functio source should be UTF-8")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Functio::Able { params, body } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"({}) -> {:?}",
|
||||||
|
params.join(", "),
|
||||||
|
// Maybe we should have a pretty-printer for
|
||||||
|
// statement blocks at some point?
|
||||||
|
body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Functio::Builtin(b) => write!(f, "builtin @ {}", b.fn_addr()),
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let (a, b) = *functios.clone();
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} {} {} ",
|
||||||
|
Value::Functio(a),
|
||||||
|
match kind {
|
||||||
|
functio::FunctioChainKind::Equal => '+',
|
||||||
|
functio::FunctioChainKind::ByArity => '*',
|
||||||
|
},
|
||||||
|
Value::Functio(b)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Functio::Eval(s) => write!(f, "{}", s),
|
||||||
|
},
|
||||||
|
Value::Cart(c) => {
|
||||||
|
write!(f, "[")?;
|
||||||
|
let mut cart_vec = c.iter().collect::<Vec<_>>();
|
||||||
|
cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less));
|
||||||
|
|
||||||
|
for (idx, (key, value)) in cart_vec.into_iter().enumerate() {
|
||||||
|
write!(f, "{}", if idx != 0 { ", " } else { "" },)?;
|
||||||
|
match &*value.borrow() {
|
||||||
|
x if std::ptr::eq(x, self) => write!(f, "<cycle>"),
|
||||||
|
x => write!(f, "{x}"),
|
||||||
|
}?;
|
||||||
|
write!(f, " <= {key}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runtime borrow-checked, counted reference to a [Value]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ValueRef(Rc<RefCell<Value>>);
|
||||||
|
|
||||||
|
impl ValueRef {
|
||||||
|
pub fn new(v: Value) -> Self {
|
||||||
|
Self(Rc::new(RefCell::new(v)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn borrow(&self) -> Ref<Value> {
|
||||||
|
self.0.borrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn borrow_mut(&self) -> RefMut<Value> {
|
||||||
|
self.0.borrow_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn replace(&self, v: Value) -> Value {
|
||||||
|
self.0.replace(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AbleScript variable either holding a reference
|
||||||
|
/// or being banned
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Variable {
|
||||||
|
/// Reference to a value
|
||||||
|
Ref(ValueRef),
|
||||||
|
|
||||||
|
/// Banned variable
|
||||||
|
Melo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Variable {
|
||||||
|
pub fn from_value(value: Value) -> Self {
|
||||||
|
Self::Ref(ValueRef::new(value))
|
||||||
|
}
|
||||||
|
}
|
455
ablescript/src/value/ops.rs
Normal file
455
ablescript/src/value/ops.rs
Normal file
|
@ -0,0 +1,455 @@
|
||||||
|
use super::{
|
||||||
|
functio::{BuiltinFunctio, FunctioChainKind},
|
||||||
|
Abool, Functio, Value, ValueRef,
|
||||||
|
};
|
||||||
|
use crate::consts;
|
||||||
|
use rand::Rng;
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
ops::{Add, Div, Mul, Not, Sub},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl Add for Value {
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
Value::Nul | Value::Undefined => match rhs {
|
||||||
|
Value::Nul => Value::Nul,
|
||||||
|
Value::Undefined => Value::Undefined,
|
||||||
|
Value::Str(_) => Value::Str(self.to_string()) + rhs,
|
||||||
|
Value::Int(_) => Value::Int(self.into_isize()) + rhs,
|
||||||
|
Value::Abool(_) => Value::Abool(self.into_abool()) + rhs,
|
||||||
|
Value::Functio(_) => Value::Functio(self.into_functio()) + rhs,
|
||||||
|
Value::Cart(_) => Value::Cart(self.into_cart()) + rhs,
|
||||||
|
},
|
||||||
|
Value::Str(s) => Value::Str(format!("{s}{rhs}")),
|
||||||
|
Value::Int(i) => Value::Int(i.wrapping_add(rhs.into_isize())),
|
||||||
|
Value::Abool(_) => {
|
||||||
|
Value::Abool(Value::Int(self.into_isize().max(rhs.into_isize())).into_abool())
|
||||||
|
}
|
||||||
|
Value::Functio(f) => Value::Functio(Functio::Chain {
|
||||||
|
functios: Box::new((f, rhs.into_functio())),
|
||||||
|
kind: FunctioChainKind::Equal,
|
||||||
|
}),
|
||||||
|
Value::Cart(c) => {
|
||||||
|
Value::Cart(c.into_iter().chain(rhs.into_cart().into_iter()).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Value {
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
Value::Nul | Value::Undefined => match rhs {
|
||||||
|
Value::Nul => Value::Nul,
|
||||||
|
Value::Undefined => Value::Undefined,
|
||||||
|
Value::Str(_) => Value::Str(self.to_string()) - rhs,
|
||||||
|
Value::Int(_) => Value::Int(self.into_isize()) - rhs,
|
||||||
|
Value::Abool(_) => Value::Abool(self.into_abool()) - rhs,
|
||||||
|
Value::Functio(_) => Value::Functio(self.into_functio()) - rhs,
|
||||||
|
Value::Cart(_) => Value::Cart(self.into_cart()) - rhs,
|
||||||
|
},
|
||||||
|
Value::Str(s) => Value::Str(s.replace(&rhs.to_string(), "")),
|
||||||
|
Value::Int(i) => Value::Int(i.wrapping_sub(rhs.into_isize())),
|
||||||
|
Value::Abool(_) => (self.clone() + rhs.clone()) * !(self * rhs),
|
||||||
|
Value::Functio(f) => Value::Functio(match f {
|
||||||
|
Functio::Bf {
|
||||||
|
instructions: lhs_ins,
|
||||||
|
tape_len: lhs_tl,
|
||||||
|
} => match rhs.into_functio() {
|
||||||
|
Functio::Bf {
|
||||||
|
instructions: rhs_ins,
|
||||||
|
tape_len: rhs_tl,
|
||||||
|
} => Functio::Bf {
|
||||||
|
instructions: lhs_ins
|
||||||
|
.into_iter()
|
||||||
|
.zip(rhs_ins.into_iter())
|
||||||
|
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
|
||||||
|
.collect(),
|
||||||
|
tape_len: lhs_tl - rhs_tl,
|
||||||
|
},
|
||||||
|
rhs => Functio::Bf {
|
||||||
|
instructions: lhs_ins
|
||||||
|
.into_iter()
|
||||||
|
.zip(Value::Functio(rhs).to_string().bytes())
|
||||||
|
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
|
||||||
|
.collect(),
|
||||||
|
tape_len: lhs_tl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Functio::Able {
|
||||||
|
params: lhs_params,
|
||||||
|
body: lhs_body,
|
||||||
|
} => match rhs.into_functio() {
|
||||||
|
Functio::Able {
|
||||||
|
params: rhs_params,
|
||||||
|
body: rhs_body,
|
||||||
|
} => Functio::Able {
|
||||||
|
params: lhs_params
|
||||||
|
.into_iter()
|
||||||
|
.zip(rhs_params.into_iter())
|
||||||
|
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
|
||||||
|
.collect(),
|
||||||
|
body: lhs_body
|
||||||
|
.into_iter()
|
||||||
|
.zip(rhs_body.into_iter())
|
||||||
|
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
rhs => Value::Int(
|
||||||
|
Value::Functio(Functio::Able {
|
||||||
|
params: lhs_params,
|
||||||
|
body: lhs_body,
|
||||||
|
})
|
||||||
|
.into_isize()
|
||||||
|
- Value::Functio(rhs).into_isize(),
|
||||||
|
)
|
||||||
|
.into_functio(),
|
||||||
|
},
|
||||||
|
Functio::Builtin(b) => {
|
||||||
|
let arity = b.arity;
|
||||||
|
let resulting_arity = arity.saturating_sub(rhs.into_isize() as usize);
|
||||||
|
|
||||||
|
Functio::Builtin(BuiltinFunctio::new(
|
||||||
|
move |args| {
|
||||||
|
b.call(
|
||||||
|
&args
|
||||||
|
.iter()
|
||||||
|
.take(resulting_arity)
|
||||||
|
.cloned()
|
||||||
|
.chain(std::iter::repeat_with(|| ValueRef::new(Value::Nul)))
|
||||||
|
.take(arity)
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
resulting_arity,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Functio::Chain { functios, .. } => {
|
||||||
|
let rhs = rhs.into_functio();
|
||||||
|
let (a, b) = *functios;
|
||||||
|
|
||||||
|
match (a == rhs, b == rhs) {
|
||||||
|
(_, true) => a,
|
||||||
|
(true, _) => b,
|
||||||
|
(_, _) => (Value::Functio(a) - Value::Functio(rhs)).into_functio(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Functio::Eval(lhs_code) => Functio::Eval(lhs_code.replace(
|
||||||
|
&match rhs.into_functio() {
|
||||||
|
Functio::Eval(code) => code,
|
||||||
|
rhs => Value::Functio(rhs).to_string(),
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
)),
|
||||||
|
}),
|
||||||
|
Value::Cart(c) => Value::Cart({
|
||||||
|
let rhs_cart = rhs.into_cart();
|
||||||
|
c.into_iter()
|
||||||
|
.filter(|(k, v)| rhs_cart.get(k) != Some(v))
|
||||||
|
.collect()
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul for Value {
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
Value::Nul | Value::Undefined => match rhs {
|
||||||
|
Value::Nul => Value::Nul,
|
||||||
|
Value::Undefined => Value::Undefined,
|
||||||
|
Value::Str(_) => Value::Str(self.to_string()) * rhs,
|
||||||
|
Value::Int(_) => Value::Int(self.into_isize()) * rhs,
|
||||||
|
Value::Abool(_) => Value::Abool(self.into_abool()) * rhs,
|
||||||
|
Value::Functio(_) => Value::Functio(self.into_functio()) * rhs,
|
||||||
|
Value::Cart(_) => Value::Cart(self.into_cart()) * rhs,
|
||||||
|
},
|
||||||
|
Value::Str(s) => Value::Str(s.repeat(rhs.into_isize() as usize)),
|
||||||
|
Value::Int(i) => Value::Int(i.wrapping_mul(rhs.into_isize())),
|
||||||
|
Value::Abool(_) => {
|
||||||
|
Value::Abool(Value::Int(self.into_isize().min(rhs.into_isize())).into_abool())
|
||||||
|
}
|
||||||
|
Value::Functio(f) => Value::Functio(Functio::Chain {
|
||||||
|
functios: Box::new((f, rhs.into_functio())),
|
||||||
|
kind: FunctioChainKind::ByArity,
|
||||||
|
}),
|
||||||
|
Value::Cart(c) => {
|
||||||
|
let rhsc = rhs.into_cart();
|
||||||
|
|
||||||
|
Value::Cart(
|
||||||
|
c.into_iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
if let Some(k) = rhsc.get(&k) {
|
||||||
|
(k.borrow().clone(), v)
|
||||||
|
} else {
|
||||||
|
(k, v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div for Value {
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn div(self, rhs: Self) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
Value::Nul | Value::Undefined => match rhs {
|
||||||
|
Value::Nul => Value::Nul,
|
||||||
|
Value::Undefined => Value::Undefined,
|
||||||
|
Value::Str(_) => Value::Str(self.to_string()) / rhs,
|
||||||
|
Value::Int(_) => Value::Int(self.into_isize()) / rhs,
|
||||||
|
Value::Abool(_) => Value::Abool(self.into_abool()) / rhs,
|
||||||
|
Value::Functio(_) => Value::Functio(self.into_functio()) / rhs,
|
||||||
|
Value::Cart(_) => Value::Cart(self.into_cart()) / rhs,
|
||||||
|
},
|
||||||
|
Value::Str(s) => Value::Cart(
|
||||||
|
s.split(&rhs.to_string())
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| {
|
||||||
|
(
|
||||||
|
Value::Int(i as isize + 1),
|
||||||
|
ValueRef::new(Value::Str(x.to_owned())),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
Value::Int(i) => Value::Int(i.wrapping_div(match rhs.into_isize() {
|
||||||
|
0 => consts::ANSWER,
|
||||||
|
x => x,
|
||||||
|
})),
|
||||||
|
Value::Abool(_) => !self + rhs,
|
||||||
|
Value::Functio(f) => Value::Functio(match f {
|
||||||
|
Functio::Bf {
|
||||||
|
instructions,
|
||||||
|
tape_len,
|
||||||
|
} => {
|
||||||
|
let fraction = 1.0 / rhs.into_isize() as f64;
|
||||||
|
let len = instructions.len();
|
||||||
|
Functio::Bf {
|
||||||
|
instructions: instructions
|
||||||
|
.into_iter()
|
||||||
|
.take((len as f64 * fraction) as usize)
|
||||||
|
.collect(),
|
||||||
|
tape_len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Functio::Able { params, body } => {
|
||||||
|
let fraction = 1.0 / rhs.into_isize() as f64;
|
||||||
|
let len = body.len();
|
||||||
|
Functio::Able {
|
||||||
|
params,
|
||||||
|
body: body
|
||||||
|
.into_iter()
|
||||||
|
.take((len as f64 * fraction) as usize)
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Functio::Builtin(b) => Functio::Builtin(BuiltinFunctio {
|
||||||
|
arity: b.arity + rhs.into_isize() as usize,
|
||||||
|
..b
|
||||||
|
}),
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let functios = *functios;
|
||||||
|
Functio::Chain {
|
||||||
|
functios: Box::new((
|
||||||
|
(Value::Functio(functios.0) / rhs.clone()).into_functio(),
|
||||||
|
(Value::Functio(functios.1) / rhs).into_functio(),
|
||||||
|
)),
|
||||||
|
kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Functio::Eval(s) => {
|
||||||
|
let fraction = 1.0 / rhs.into_isize() as f64;
|
||||||
|
let len = s.len();
|
||||||
|
Functio::Eval(s.chars().take((len as f64 * fraction) as usize).collect())
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Value::Cart(c) => {
|
||||||
|
let cart_len = match c.len() {
|
||||||
|
0 => return Value::Cart(HashMap::new()),
|
||||||
|
l => l,
|
||||||
|
};
|
||||||
|
|
||||||
|
let chunk_len = match rhs.into_isize() as usize {
|
||||||
|
0 => rand::thread_rng().gen_range(1..=cart_len),
|
||||||
|
l => l,
|
||||||
|
};
|
||||||
|
|
||||||
|
Value::Cart(
|
||||||
|
c.into_iter()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.chunks(cart_len / chunk_len + (cart_len % chunk_len != 0) as usize)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
(
|
||||||
|
Value::Int(k as isize + 1),
|
||||||
|
ValueRef::new(Value::Cart(v.iter().cloned().collect())),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Not for Value {
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn not(self) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
Value::Nul => Value::Undefined,
|
||||||
|
Value::Undefined => Value::Nul,
|
||||||
|
Value::Str(s) => Value::Str(s.chars().rev().collect()),
|
||||||
|
Value::Int(i) => Value::Int(i.swap_bytes()),
|
||||||
|
Value::Abool(a) => Value::Abool(match a {
|
||||||
|
Abool::Never => Abool::Always,
|
||||||
|
Abool::Sometimes => Abool::Sometimes,
|
||||||
|
Abool::Always => Abool::Never,
|
||||||
|
}),
|
||||||
|
Value::Functio(f) => Value::Functio(match f {
|
||||||
|
Functio::Bf {
|
||||||
|
mut instructions,
|
||||||
|
tape_len,
|
||||||
|
} => {
|
||||||
|
instructions.reverse();
|
||||||
|
|
||||||
|
Functio::Bf {
|
||||||
|
instructions,
|
||||||
|
tape_len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Functio::Able {
|
||||||
|
mut params,
|
||||||
|
mut body,
|
||||||
|
} => {
|
||||||
|
params.reverse();
|
||||||
|
body.reverse();
|
||||||
|
|
||||||
|
Functio::Able { params, body }
|
||||||
|
}
|
||||||
|
Functio::Builtin(b) => {
|
||||||
|
let arity = b.arity;
|
||||||
|
Functio::Builtin(BuiltinFunctio::new(
|
||||||
|
move |args| b.call(&args.iter().cloned().rev().collect::<Vec<_>>()),
|
||||||
|
arity,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let (a, b) = *functios;
|
||||||
|
Functio::Chain {
|
||||||
|
functios: Box::new((
|
||||||
|
(!Value::Functio(b)).into_functio(),
|
||||||
|
(!Value::Functio(a)).into_functio(),
|
||||||
|
)),
|
||||||
|
kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Functio::Eval(code) => Functio::Eval(code.chars().rev().collect()),
|
||||||
|
}),
|
||||||
|
Value::Cart(c) => Value::Cart(
|
||||||
|
c.into_iter()
|
||||||
|
.map(|(k, v)| (v.borrow().clone(), ValueRef::new(k)))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Value {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
let other = other.clone();
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Value::Nul => matches!(other, Value::Nul),
|
||||||
|
Value::Undefined => matches!(other, Value::Undefined),
|
||||||
|
Value::Str(s) => *s == other.to_string(),
|
||||||
|
Value::Int(i) => *i == other.into_isize(),
|
||||||
|
Value::Abool(a) => *a == other.into_abool(),
|
||||||
|
Value::Functio(f) => *f == other.into_functio(),
|
||||||
|
Value::Cart(c) => *c == other.into_cart(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Value {}
|
||||||
|
|
||||||
|
impl PartialOrd for Value {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
use std::cmp::Ordering::*;
|
||||||
|
let other = other.clone();
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Value::Nul if other == Value::Nul => Some(Equal),
|
||||||
|
Value::Undefined if other == Value::Undefined => Some(Equal),
|
||||||
|
Value::Str(s) => Some(s.cmp(&other.to_string())),
|
||||||
|
Value::Int(i) => Some(i.cmp(&other.into_isize())),
|
||||||
|
Value::Abool(a) => a.partial_cmp(&other.into_abool()),
|
||||||
|
Value::Functio(_) => self.clone().into_isize().partial_cmp(&other.into_isize()),
|
||||||
|
Value::Cart(c) => Some(c.len().cmp(&other.into_cart().len())),
|
||||||
|
Value::Nul | Value::Undefined => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
/// Get a length of a value
|
||||||
|
pub fn length(&self) -> isize {
|
||||||
|
match self {
|
||||||
|
Value::Nul => 0,
|
||||||
|
Value::Undefined => -1,
|
||||||
|
Value::Str(s) => s.len() as _,
|
||||||
|
Value::Int(i) => i.count_zeros() as _,
|
||||||
|
Value::Abool(a) => match a {
|
||||||
|
Abool::Never => -3,
|
||||||
|
Abool::Sometimes => {
|
||||||
|
if rand::thread_rng().gen() {
|
||||||
|
3
|
||||||
|
} else {
|
||||||
|
-3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Abool::Always => 3,
|
||||||
|
},
|
||||||
|
Value::Functio(f) => match f {
|
||||||
|
// Compares lengths of functions:
|
||||||
|
// BfFunctio - Sum of lengths of instructions and length of tape
|
||||||
|
// AbleFunctio - Sum of argument count and body length
|
||||||
|
// Eval - Length of input code
|
||||||
|
Functio::Bf {
|
||||||
|
instructions,
|
||||||
|
tape_len,
|
||||||
|
} => (instructions.len() + tape_len) as _,
|
||||||
|
Functio::Able { params, body } => (params.len() + format!("{:?}", body).len()) as _,
|
||||||
|
Functio::Builtin(b) => (std::mem::size_of_val(b.function.as_ref()) + b.arity) as _,
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let (lhs, rhs) = *functios.clone();
|
||||||
|
match kind {
|
||||||
|
FunctioChainKind::Equal => {
|
||||||
|
Value::Int(Value::Functio(lhs).into_isize())
|
||||||
|
+ Value::Int(Value::Functio(rhs).into_isize())
|
||||||
|
}
|
||||||
|
FunctioChainKind::ByArity => {
|
||||||
|
Value::Int(Value::Functio(lhs).into_isize())
|
||||||
|
* Value::Int(Value::Functio(rhs).into_isize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.into_isize()
|
||||||
|
}
|
||||||
|
Functio::Eval(s) => s.len() as _,
|
||||||
|
},
|
||||||
|
Value::Cart(c) => c.len() as _,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,989 +0,0 @@
|
||||||
use std::{
|
|
||||||
cell::{Ref, RefCell, RefMut},
|
|
||||||
collections::HashMap,
|
|
||||||
fmt::Display,
|
|
||||||
hash::Hash,
|
|
||||||
io::Write,
|
|
||||||
mem::discriminant,
|
|
||||||
ops,
|
|
||||||
rc::Rc,
|
|
||||||
vec,
|
|
||||||
};
|
|
||||||
|
|
||||||
use rand::Rng;
|
|
||||||
|
|
||||||
use crate::{ast::Block, brian::INSTRUCTION_MAPPINGS, consts};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
|
||||||
pub enum Abool {
|
|
||||||
Never = -1,
|
|
||||||
Sometimes = 0,
|
|
||||||
Always = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Abool {
|
|
||||||
pub fn to_bool(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Always => true,
|
|
||||||
Self::Sometimes if rand::random() => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Abool {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Abool::Never => write!(f, "never"),
|
|
||||||
Abool::Sometimes => write!(f, "sometimes"),
|
|
||||||
Abool::Always => write!(f, "always"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bool> for Abool {
|
|
||||||
fn from(b: bool) -> Self {
|
|
||||||
if b {
|
|
||||||
Abool::Always
|
|
||||||
} else {
|
|
||||||
Abool::Never
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
|
||||||
pub enum Functio {
|
|
||||||
Bf {
|
|
||||||
instructions: Vec<u8>,
|
|
||||||
tape_len: usize,
|
|
||||||
},
|
|
||||||
Able {
|
|
||||||
params: Vec<String>,
|
|
||||||
body: Block,
|
|
||||||
},
|
|
||||||
Builtin(BuiltinFunctio),
|
|
||||||
Chain {
|
|
||||||
functios: Box<(Functio, Functio)>,
|
|
||||||
kind: FunctioChainKind,
|
|
||||||
},
|
|
||||||
Eval(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Functio {
|
|
||||||
pub fn arity(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Functio::Bf {
|
|
||||||
instructions: _,
|
|
||||||
tape_len: _,
|
|
||||||
} => 0,
|
|
||||||
Functio::Able { params, body: _ } => params.len(),
|
|
||||||
Functio::Builtin(b) => b.arity,
|
|
||||||
Functio::Chain { functios, kind: _ } => functios.0.arity() + functios.1.arity(),
|
|
||||||
Functio::Eval(_) => 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct BuiltinFunctio {
|
|
||||||
function: Rc<dyn Fn(&[ValueRef]) -> Result<(), crate::error::ErrorKind>>,
|
|
||||||
arity: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BuiltinFunctio {
|
|
||||||
pub fn new<F>(f: F, arity: usize) -> Self
|
|
||||||
where
|
|
||||||
F: Fn(&[ValueRef]) -> Result<(), crate::error::ErrorKind> + 'static,
|
|
||||||
{
|
|
||||||
Self {
|
|
||||||
function: Rc::new(f),
|
|
||||||
arity,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call(&self, args: &[ValueRef]) -> Result<(), crate::error::ErrorKind> {
|
|
||||||
(self.function)(args)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fn_addr(&self) -> usize {
|
|
||||||
Rc::as_ptr(&self.function) as *const () as _
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for BuiltinFunctio {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("BuiltinFunctio")
|
|
||||||
.field("function", &"built-in")
|
|
||||||
.field("arity", &self.arity)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for BuiltinFunctio {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.fn_addr() == other.fn_addr() && self.arity == other.arity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for BuiltinFunctio {
|
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
self.fn_addr().hash(state);
|
|
||||||
self.arity.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone, Hash)]
|
|
||||||
pub enum FunctioChainKind {
|
|
||||||
Equal,
|
|
||||||
ByArity,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Cart = HashMap<Value, ValueRef>;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Value {
|
|
||||||
Nul,
|
|
||||||
Str(String),
|
|
||||||
Int(isize),
|
|
||||||
Abool(Abool),
|
|
||||||
Functio(Functio),
|
|
||||||
Cart(Cart),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Value {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Nul
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for Value {
|
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
discriminant(self).hash(state);
|
|
||||||
match self {
|
|
||||||
Value::Nul => (),
|
|
||||||
Value::Str(v) => v.hash(state),
|
|
||||||
Value::Int(v) => v.hash(state),
|
|
||||||
Value::Abool(v) => v.to_string().hash(state),
|
|
||||||
Value::Functio(statements) => statements.hash(state),
|
|
||||||
Value::Cart(_) => self.to_string().hash(state),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Value {
|
|
||||||
/// Write an AbleScript value to a Brainfuck input stream by
|
|
||||||
/// coercing the value to an integer, then truncating that integer
|
|
||||||
/// to a single byte, then writing that byte. This should only be
|
|
||||||
/// called on `Write`rs that cannot fail, e.g., `Vec<u8>`, because
|
|
||||||
/// any IO errors will cause a panic.
|
|
||||||
pub fn bf_write(&self, stream: &mut impl Write) {
|
|
||||||
stream
|
|
||||||
.write_all(&[self.clone().into_isize() as u8])
|
|
||||||
.expect("Failed to write to Brainfuck input");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Coerce a value to an integer.
|
|
||||||
pub fn into_isize(self) -> isize {
|
|
||||||
match self {
|
|
||||||
Value::Abool(a) => a as _,
|
|
||||||
Value::Functio(f) => match f {
|
|
||||||
Functio::Bf {
|
|
||||||
instructions,
|
|
||||||
tape_len,
|
|
||||||
} => {
|
|
||||||
instructions.into_iter().map(|x| x as isize).sum::<isize>() * tape_len as isize
|
|
||||||
}
|
|
||||||
Functio::Able { params, body } => {
|
|
||||||
params
|
|
||||||
.into_iter()
|
|
||||||
.map(|x| x.bytes().map(|x| x as isize).sum::<isize>())
|
|
||||||
.sum::<isize>()
|
|
||||||
+ body.len() as isize
|
|
||||||
}
|
|
||||||
Functio::Builtin(b) => (b.fn_addr() + b.arity) as _,
|
|
||||||
Functio::Chain { functios, kind } => {
|
|
||||||
let (lf, rf) = *functios;
|
|
||||||
Value::Functio(lf).into_isize()
|
|
||||||
+ Value::Functio(rf).into_isize()
|
|
||||||
* match kind {
|
|
||||||
FunctioChainKind::Equal => -1,
|
|
||||||
FunctioChainKind::ByArity => 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Functio::Eval(code) => code.bytes().map(|x| x as isize).sum(),
|
|
||||||
},
|
|
||||||
Value::Int(i) => i,
|
|
||||||
Value::Nul => consts::ANSWER,
|
|
||||||
Value::Str(text) => text.parse().unwrap_or(consts::ANSWER),
|
|
||||||
Value::Cart(c) => c
|
|
||||||
.into_iter()
|
|
||||||
.map(|(i, v)| i.into_isize() * v.borrow().clone().into_isize())
|
|
||||||
.sum(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Coerce a value to an aboolean.
|
|
||||||
pub fn into_abool(self) -> Abool {
|
|
||||||
match self {
|
|
||||||
Value::Nul => Abool::Never,
|
|
||||||
Value::Str(s) => match s.to_lowercase().as_str() {
|
|
||||||
"never" | "no" | "🇳🇴" => Abool::Never,
|
|
||||||
"sometimes" => Abool::Sometimes,
|
|
||||||
"always" | "yes" => Abool::Always,
|
|
||||||
s => (!s.is_empty()).into(),
|
|
||||||
},
|
|
||||||
Value::Int(x) => match x.cmp(&0) {
|
|
||||||
std::cmp::Ordering::Less => Abool::Never,
|
|
||||||
std::cmp::Ordering::Equal => Abool::Sometimes,
|
|
||||||
std::cmp::Ordering::Greater => Abool::Always,
|
|
||||||
},
|
|
||||||
Value::Abool(a) => a,
|
|
||||||
Value::Functio(f) => match f {
|
|
||||||
Functio::Bf {
|
|
||||||
instructions,
|
|
||||||
tape_len,
|
|
||||||
} => Value::Int(
|
|
||||||
(instructions.iter().map(|x| *x as usize).sum::<usize>() * tape_len) as _,
|
|
||||||
)
|
|
||||||
.into_abool(),
|
|
||||||
Functio::Able { params, body } => {
|
|
||||||
let str_to_isize =
|
|
||||||
|x: String| -> isize { x.as_bytes().iter().map(|x| *x as isize).sum() };
|
|
||||||
|
|
||||||
let params: isize = params.into_iter().map(str_to_isize).sum();
|
|
||||||
let body: isize = body
|
|
||||||
.into_iter()
|
|
||||||
.map(|x| format!("{:?}", x))
|
|
||||||
.map(str_to_isize)
|
|
||||||
.sum();
|
|
||||||
|
|
||||||
Value::Int((params + body) % 3 - 1).into_abool()
|
|
||||||
}
|
|
||||||
Functio::Builtin(b) => (b.fn_addr() % b.arity == 0).into(),
|
|
||||||
Functio::Chain { functios, kind } => {
|
|
||||||
let (lhs, rhs) = *functios;
|
|
||||||
match kind {
|
|
||||||
FunctioChainKind::Equal => {
|
|
||||||
Value::Abool(Value::Functio(lhs).into_abool())
|
|
||||||
+ Value::Abool(Value::Functio(rhs).into_abool())
|
|
||||||
}
|
|
||||||
FunctioChainKind::ByArity => {
|
|
||||||
Value::Abool(Value::Functio(lhs).into_abool())
|
|
||||||
* Value::Abool(Value::Functio(rhs).into_abool())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.into_abool()
|
|
||||||
}
|
|
||||||
Functio::Eval(code) => Value::Str(code).into_abool(),
|
|
||||||
},
|
|
||||||
Value::Cart(c) => {
|
|
||||||
if c.is_empty() {
|
|
||||||
Abool::Never
|
|
||||||
} else {
|
|
||||||
Abool::Always
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Coerce a value to a functio.
|
|
||||||
pub fn into_functio(self) -> Functio {
|
|
||||||
match self {
|
|
||||||
Value::Nul => Functio::Able {
|
|
||||||
body: vec![],
|
|
||||||
params: vec![],
|
|
||||||
},
|
|
||||||
Value::Str(s) => Functio::Eval(s),
|
|
||||||
Value::Int(i) => Functio::Bf {
|
|
||||||
instructions: {
|
|
||||||
std::iter::successors(Some(i as usize), |i| {
|
|
||||||
Some(i / INSTRUCTION_MAPPINGS.len())
|
|
||||||
})
|
|
||||||
.take_while(|&i| i != 0)
|
|
||||||
.map(|i| INSTRUCTION_MAPPINGS[i % INSTRUCTION_MAPPINGS.len()])
|
|
||||||
.collect()
|
|
||||||
},
|
|
||||||
tape_len: crate::brian::DEFAULT_TAPE_SIZE_LIMIT,
|
|
||||||
},
|
|
||||||
Value::Abool(a) => Functio::Eval(match a {
|
|
||||||
Abool::Never => "".to_owned(),
|
|
||||||
Abool::Sometimes => {
|
|
||||||
use rand::seq::SliceRandom;
|
|
||||||
let mut str_chars: Vec<_> = "Buy Able Products!".chars().collect();
|
|
||||||
str_chars.shuffle(&mut rand::thread_rng());
|
|
||||||
|
|
||||||
format!(r#""{}"print;"#, str_chars.iter().collect::<String>())
|
|
||||||
}
|
|
||||||
Abool::Always => r#"loop{"Buy Able products!"print;}"#.to_owned(),
|
|
||||||
}),
|
|
||||||
Value::Functio(f) => f,
|
|
||||||
Value::Cart(c) => {
|
|
||||||
let kind = if let Some(114514) = c
|
|
||||||
.get(&Value::Str("1452251871514141792252515212116".to_owned()))
|
|
||||||
.map(|x| x.borrow().to_owned().into_isize())
|
|
||||||
{
|
|
||||||
FunctioChainKind::Equal
|
|
||||||
} else {
|
|
||||||
FunctioChainKind::ByArity
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut cart_vec = c.iter().collect::<Vec<_>>();
|
|
||||||
cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less));
|
|
||||||
|
|
||||||
cart_vec
|
|
||||||
.into_iter()
|
|
||||||
.map(|(_, x)| x.borrow().to_owned().into_functio())
|
|
||||||
.reduce(|acc, x| Functio::Chain {
|
|
||||||
functios: Box::new((acc, x)),
|
|
||||||
kind,
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| Functio::Eval(r#""Buy Able Products!"print;"#.to_owned()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Coerce a value into a cart.
|
|
||||||
pub fn into_cart(self) -> Cart {
|
|
||||||
match self {
|
|
||||||
Value::Nul => HashMap::new(),
|
|
||||||
Value::Str(s) => s
|
|
||||||
.chars()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, x)| {
|
|
||||||
(
|
|
||||||
Value::Int(i as isize + 1),
|
|
||||||
ValueRef::new(Value::Str(x.to_string())),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
Value::Int(i) => Value::Str(i.to_string()).into_cart(),
|
|
||||||
Value::Abool(a) => Value::Str(a.to_string()).into_cart(),
|
|
||||||
Value::Functio(f) => match f {
|
|
||||||
Functio::Able { params, body } => {
|
|
||||||
let params: Cart = params
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, x)| (Value::Int(i as isize + 1), ValueRef::new(Value::Str(x))))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let body: Cart = body
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, x)| {
|
|
||||||
(
|
|
||||||
Value::Int(i as isize + 1),
|
|
||||||
ValueRef::new(Value::Str(format!("{:?}", x))),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut cart = HashMap::new();
|
|
||||||
cart.insert(
|
|
||||||
Value::Str("params".to_owned()),
|
|
||||||
ValueRef::new(Value::Cart(params)),
|
|
||||||
);
|
|
||||||
|
|
||||||
cart.insert(
|
|
||||||
Value::Str("body".to_owned()),
|
|
||||||
ValueRef::new(Value::Cart(body)),
|
|
||||||
);
|
|
||||||
|
|
||||||
cart
|
|
||||||
}
|
|
||||||
Functio::Bf {
|
|
||||||
instructions,
|
|
||||||
tape_len,
|
|
||||||
} => {
|
|
||||||
let mut cart: Cart = instructions
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, x)| {
|
|
||||||
(
|
|
||||||
Value::Int(i as isize + 1),
|
|
||||||
ValueRef::new(
|
|
||||||
char::from_u32(x as u32)
|
|
||||||
.map(|x| Value::Str(x.to_string()))
|
|
||||||
.unwrap_or(Value::Nul),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
cart.insert(
|
|
||||||
Value::Str("tapelen".to_owned()),
|
|
||||||
ValueRef::new(Value::Int(tape_len as _)),
|
|
||||||
);
|
|
||||||
cart
|
|
||||||
}
|
|
||||||
Functio::Builtin(b) => {
|
|
||||||
let mut cart = HashMap::new();
|
|
||||||
cart.insert(
|
|
||||||
Value::Str("addr".to_owned()),
|
|
||||||
ValueRef::new(Value::Cart(Value::Int(b.fn_addr() as _).into_cart())),
|
|
||||||
);
|
|
||||||
|
|
||||||
cart.insert(
|
|
||||||
Value::Str("arity".to_owned()),
|
|
||||||
ValueRef::new(Value::Int(b.arity as _)),
|
|
||||||
);
|
|
||||||
|
|
||||||
cart
|
|
||||||
}
|
|
||||||
Functio::Chain { functios, kind } => {
|
|
||||||
let (lhs, rhs) = *functios;
|
|
||||||
match kind {
|
|
||||||
FunctioChainKind::Equal => {
|
|
||||||
Value::Cart(Value::Functio(lhs).into_cart())
|
|
||||||
+ Value::Cart(Value::Functio(rhs).into_cart())
|
|
||||||
}
|
|
||||||
FunctioChainKind::ByArity => {
|
|
||||||
Value::Cart(Value::Functio(lhs).into_cart())
|
|
||||||
* Value::Cart(Value::Functio(rhs).into_cart())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.into_cart()
|
|
||||||
}
|
|
||||||
Functio::Eval(s) => Value::Str(s).into_cart(),
|
|
||||||
},
|
|
||||||
Value::Cart(c) => c,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a length of a value
|
|
||||||
pub fn length(&self) -> isize {
|
|
||||||
match self {
|
|
||||||
Value::Nul => 0,
|
|
||||||
Value::Str(s) => s.len() as _,
|
|
||||||
Value::Int(i) => i.count_zeros() as _,
|
|
||||||
Value::Abool(a) => match a {
|
|
||||||
Abool::Never => -3,
|
|
||||||
Abool::Sometimes => {
|
|
||||||
if rand::thread_rng().gen() {
|
|
||||||
3
|
|
||||||
} else {
|
|
||||||
-3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Abool::Always => 3,
|
|
||||||
},
|
|
||||||
Value::Functio(f) => match f {
|
|
||||||
// Compares lengths of functions:
|
|
||||||
// BfFunctio - Sum of lengths of instructions and length of tape
|
|
||||||
// AbleFunctio - Sum of argument count and body length
|
|
||||||
// Eval - Length of input code
|
|
||||||
Functio::Bf {
|
|
||||||
instructions,
|
|
||||||
tape_len,
|
|
||||||
} => (instructions.len() + tape_len) as _,
|
|
||||||
Functio::Able { params, body } => (params.len() + format!("{:?}", body).len()) as _,
|
|
||||||
Functio::Builtin(b) => (std::mem::size_of_val(b.function.as_ref()) + b.arity) as _,
|
|
||||||
Functio::Chain { functios, kind } => {
|
|
||||||
let (lhs, rhs) = *functios.clone();
|
|
||||||
match kind {
|
|
||||||
FunctioChainKind::Equal => {
|
|
||||||
Value::Int(Value::Functio(lhs).into_isize())
|
|
||||||
+ Value::Int(Value::Functio(rhs).into_isize())
|
|
||||||
}
|
|
||||||
FunctioChainKind::ByArity => {
|
|
||||||
Value::Int(Value::Functio(lhs).into_isize())
|
|
||||||
* Value::Int(Value::Functio(rhs).into_isize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.into_isize()
|
|
||||||
}
|
|
||||||
Functio::Eval(s) => s.len() as _,
|
|
||||||
},
|
|
||||||
Value::Cart(c) => c.len() as _,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::Add for Value {
|
|
||||||
type Output = Value;
|
|
||||||
|
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
|
||||||
match self {
|
|
||||||
Value::Nul => match rhs {
|
|
||||||
Value::Nul => Value::Nul,
|
|
||||||
Value::Str(_) => Value::Str(self.to_string()) + rhs,
|
|
||||||
Value::Int(_) => Value::Int(self.into_isize()) + rhs,
|
|
||||||
Value::Abool(_) => Value::Abool(self.into_abool()) + rhs,
|
|
||||||
Value::Functio(_) => Value::Functio(self.into_functio()) + rhs,
|
|
||||||
Value::Cart(_) => Value::Cart(self.into_cart()) + rhs,
|
|
||||||
},
|
|
||||||
Value::Str(s) => Value::Str(format!("{s}{rhs}")),
|
|
||||||
Value::Int(i) => Value::Int(i.wrapping_add(rhs.into_isize())),
|
|
||||||
Value::Abool(_) => {
|
|
||||||
Value::Abool(Value::Int(self.into_isize().max(rhs.into_isize())).into_abool())
|
|
||||||
}
|
|
||||||
Value::Functio(f) => Value::Functio(Functio::Chain {
|
|
||||||
functios: Box::new((f, rhs.into_functio())),
|
|
||||||
kind: FunctioChainKind::Equal,
|
|
||||||
}),
|
|
||||||
Value::Cart(c) => {
|
|
||||||
Value::Cart(c.into_iter().chain(rhs.into_cart().into_iter()).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::Sub for Value {
|
|
||||||
type Output = Value;
|
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
|
||||||
match self {
|
|
||||||
Value::Nul => match rhs {
|
|
||||||
Value::Nul => Value::Nul,
|
|
||||||
Value::Str(_) => Value::Str(self.to_string()) - rhs,
|
|
||||||
Value::Int(_) => Value::Int(self.into_isize()) - rhs,
|
|
||||||
Value::Abool(_) => Value::Abool(self.into_abool()) - rhs,
|
|
||||||
Value::Functio(_) => Value::Functio(self.into_functio()) - rhs,
|
|
||||||
Value::Cart(_) => Value::Cart(self.into_cart()) - rhs,
|
|
||||||
},
|
|
||||||
Value::Str(s) => Value::Str(s.replace(&rhs.to_string(), "")),
|
|
||||||
Value::Int(i) => Value::Int(i.wrapping_sub(rhs.into_isize())),
|
|
||||||
Value::Abool(_) => (self.clone() + rhs.clone()) * !(self * rhs),
|
|
||||||
Value::Functio(f) => Value::Functio(match f {
|
|
||||||
Functio::Bf {
|
|
||||||
instructions: lhs_ins,
|
|
||||||
tape_len: lhs_tl,
|
|
||||||
} => match rhs.into_functio() {
|
|
||||||
Functio::Bf {
|
|
||||||
instructions: rhs_ins,
|
|
||||||
tape_len: rhs_tl,
|
|
||||||
} => Functio::Bf {
|
|
||||||
instructions: lhs_ins
|
|
||||||
.into_iter()
|
|
||||||
.zip(rhs_ins.into_iter())
|
|
||||||
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
|
|
||||||
.collect(),
|
|
||||||
tape_len: lhs_tl - rhs_tl,
|
|
||||||
},
|
|
||||||
rhs => Functio::Bf {
|
|
||||||
instructions: lhs_ins
|
|
||||||
.into_iter()
|
|
||||||
.zip(Value::Functio(rhs).to_string().bytes())
|
|
||||||
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
|
|
||||||
.collect(),
|
|
||||||
tape_len: lhs_tl,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Functio::Able {
|
|
||||||
params: lhs_params,
|
|
||||||
body: lhs_body,
|
|
||||||
} => match rhs.into_functio() {
|
|
||||||
Functio::Able {
|
|
||||||
params: rhs_params,
|
|
||||||
body: rhs_body,
|
|
||||||
} => Functio::Able {
|
|
||||||
params: lhs_params
|
|
||||||
.into_iter()
|
|
||||||
.zip(rhs_params.into_iter())
|
|
||||||
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
|
|
||||||
.collect(),
|
|
||||||
body: lhs_body
|
|
||||||
.into_iter()
|
|
||||||
.zip(rhs_body.into_iter())
|
|
||||||
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
|
|
||||||
.collect(),
|
|
||||||
},
|
|
||||||
rhs => Value::Int(
|
|
||||||
Value::Functio(Functio::Able {
|
|
||||||
params: lhs_params,
|
|
||||||
body: lhs_body,
|
|
||||||
})
|
|
||||||
.into_isize()
|
|
||||||
- Value::Functio(rhs).into_isize(),
|
|
||||||
)
|
|
||||||
.into_functio(),
|
|
||||||
},
|
|
||||||
Functio::Builtin(b) => {
|
|
||||||
let arity = b.arity;
|
|
||||||
let resulting_arity = arity.saturating_sub(rhs.into_isize() as usize);
|
|
||||||
|
|
||||||
Functio::Builtin(BuiltinFunctio::new(
|
|
||||||
move |args| {
|
|
||||||
b.call(
|
|
||||||
&args
|
|
||||||
.iter()
|
|
||||||
.take(resulting_arity)
|
|
||||||
.cloned()
|
|
||||||
.chain(std::iter::repeat_with(|| ValueRef::new(Value::Nul)))
|
|
||||||
.take(arity)
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
resulting_arity,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Functio::Chain { functios, .. } => {
|
|
||||||
let rhs = rhs.into_functio();
|
|
||||||
let (a, b) = *functios;
|
|
||||||
|
|
||||||
match (a == rhs, b == rhs) {
|
|
||||||
(_, true) => a,
|
|
||||||
(true, _) => b,
|
|
||||||
(_, _) => (Value::Functio(a) - Value::Functio(rhs)).into_functio(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Functio::Eval(lhs_code) => Functio::Eval(lhs_code.replace(
|
|
||||||
&match rhs.into_functio() {
|
|
||||||
Functio::Eval(code) => code,
|
|
||||||
rhs => Value::Functio(rhs).to_string(),
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
)),
|
|
||||||
}),
|
|
||||||
Value::Cart(c) => Value::Cart({
|
|
||||||
let rhs_cart = rhs.into_cart();
|
|
||||||
c.into_iter()
|
|
||||||
.filter(|(k, v)| rhs_cart.get(k) != Some(v))
|
|
||||||
.collect()
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::Mul for Value {
|
|
||||||
type Output = Value;
|
|
||||||
|
|
||||||
fn mul(self, rhs: Self) -> Self::Output {
|
|
||||||
match self {
|
|
||||||
Value::Nul => match rhs {
|
|
||||||
Value::Nul => Value::Nul,
|
|
||||||
Value::Str(_) => Value::Str(self.to_string()) * rhs,
|
|
||||||
Value::Int(_) => Value::Int(self.into_isize()) * rhs,
|
|
||||||
Value::Abool(_) => Value::Abool(self.into_abool()) * rhs,
|
|
||||||
Value::Functio(_) => Value::Functio(self.into_functio()) * rhs,
|
|
||||||
Value::Cart(_) => Value::Cart(self.into_cart()) * rhs,
|
|
||||||
},
|
|
||||||
Value::Str(s) => Value::Str(s.repeat(rhs.into_isize() as usize)),
|
|
||||||
Value::Int(i) => Value::Int(i.wrapping_mul(rhs.into_isize())),
|
|
||||||
Value::Abool(_) => {
|
|
||||||
Value::Abool(Value::Int(self.into_isize().min(rhs.into_isize())).into_abool())
|
|
||||||
}
|
|
||||||
Value::Functio(f) => Value::Functio(Functio::Chain {
|
|
||||||
functios: Box::new((f, rhs.into_functio())),
|
|
||||||
kind: FunctioChainKind::ByArity,
|
|
||||||
}),
|
|
||||||
Value::Cart(c) => {
|
|
||||||
let rhsc = rhs.into_cart();
|
|
||||||
|
|
||||||
Value::Cart(
|
|
||||||
c.into_iter()
|
|
||||||
.map(|(k, v)| {
|
|
||||||
if let Some(k) = rhsc.get(&k) {
|
|
||||||
(k.borrow().clone(), v)
|
|
||||||
} else {
|
|
||||||
(k, v)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::Div for Value {
|
|
||||||
type Output = Value;
|
|
||||||
|
|
||||||
fn div(self, rhs: Self) -> Self::Output {
|
|
||||||
match self {
|
|
||||||
Value::Nul => match rhs {
|
|
||||||
Value::Nul => Value::Nul,
|
|
||||||
Value::Str(_) => Value::Str(self.to_string()) / rhs,
|
|
||||||
Value::Int(_) => Value::Int(self.into_isize()) / rhs,
|
|
||||||
Value::Abool(_) => Value::Abool(self.into_abool()) / rhs,
|
|
||||||
Value::Functio(_) => Value::Functio(self.into_functio()) / rhs,
|
|
||||||
Value::Cart(_) => Value::Cart(self.into_cart()) / rhs,
|
|
||||||
},
|
|
||||||
Value::Str(s) => Value::Cart(
|
|
||||||
s.split(&rhs.to_string())
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, x)| {
|
|
||||||
(
|
|
||||||
Value::Int(i as isize + 1),
|
|
||||||
ValueRef::new(Value::Str(x.to_owned())),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
Value::Int(i) => Value::Int(i.wrapping_div(match rhs.into_isize() {
|
|
||||||
0 => consts::ANSWER,
|
|
||||||
x => x,
|
|
||||||
})),
|
|
||||||
Value::Abool(_) => !self + rhs,
|
|
||||||
Value::Functio(f) => Value::Functio(match f {
|
|
||||||
Functio::Bf {
|
|
||||||
instructions,
|
|
||||||
tape_len,
|
|
||||||
} => {
|
|
||||||
let fraction = 1.0 / rhs.into_isize() as f64;
|
|
||||||
let len = instructions.len();
|
|
||||||
Functio::Bf {
|
|
||||||
instructions: instructions
|
|
||||||
.into_iter()
|
|
||||||
.take((len as f64 * fraction) as usize)
|
|
||||||
.collect(),
|
|
||||||
tape_len,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Functio::Able { params, body } => {
|
|
||||||
let fraction = 1.0 / rhs.into_isize() as f64;
|
|
||||||
let len = body.len();
|
|
||||||
Functio::Able {
|
|
||||||
params,
|
|
||||||
body: body
|
|
||||||
.into_iter()
|
|
||||||
.take((len as f64 * fraction) as usize)
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Functio::Builtin(b) => Functio::Builtin(BuiltinFunctio {
|
|
||||||
arity: b.arity + rhs.into_isize() as usize,
|
|
||||||
..b
|
|
||||||
}),
|
|
||||||
Functio::Chain { functios, kind } => {
|
|
||||||
let functios = *functios;
|
|
||||||
Functio::Chain {
|
|
||||||
functios: Box::new((
|
|
||||||
(Value::Functio(functios.0) / rhs.clone()).into_functio(),
|
|
||||||
(Value::Functio(functios.1) / rhs).into_functio(),
|
|
||||||
)),
|
|
||||||
kind,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Functio::Eval(s) => {
|
|
||||||
let fraction = 1.0 / rhs.into_isize() as f64;
|
|
||||||
let len = s.len();
|
|
||||||
Functio::Eval(s.chars().take((len as f64 * fraction) as usize).collect())
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Value::Cart(c) => {
|
|
||||||
let cart_len = c.len();
|
|
||||||
let chunk_len = rhs.into_isize() as usize;
|
|
||||||
|
|
||||||
Value::Cart(
|
|
||||||
c.into_iter()
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.chunks(cart_len / chunk_len + (cart_len % chunk_len != 0) as usize)
|
|
||||||
.enumerate()
|
|
||||||
.map(|(k, v)| {
|
|
||||||
(
|
|
||||||
Value::Int(k as isize + 1),
|
|
||||||
ValueRef::new(Value::Cart(v.iter().cloned().collect())),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::Not for Value {
|
|
||||||
type Output = Value;
|
|
||||||
|
|
||||||
fn not(self) -> Self::Output {
|
|
||||||
match self {
|
|
||||||
Value::Nul => Value::Nul,
|
|
||||||
Value::Str(s) => Value::Str(s.chars().rev().collect()),
|
|
||||||
Value::Int(i) => Value::Int(i.swap_bytes()),
|
|
||||||
Value::Abool(a) => Value::Abool(match a {
|
|
||||||
Abool::Never => Abool::Always,
|
|
||||||
Abool::Sometimes => Abool::Sometimes,
|
|
||||||
Abool::Always => Abool::Never,
|
|
||||||
}),
|
|
||||||
Value::Functio(f) => Value::Functio(match f {
|
|
||||||
Functio::Bf {
|
|
||||||
mut instructions,
|
|
||||||
tape_len,
|
|
||||||
} => {
|
|
||||||
instructions.reverse();
|
|
||||||
|
|
||||||
Functio::Bf {
|
|
||||||
instructions,
|
|
||||||
tape_len,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Functio::Able {
|
|
||||||
mut params,
|
|
||||||
mut body,
|
|
||||||
} => {
|
|
||||||
params.reverse();
|
|
||||||
body.reverse();
|
|
||||||
|
|
||||||
Functio::Able { params, body }
|
|
||||||
}
|
|
||||||
Functio::Builtin(b) => {
|
|
||||||
let arity = b.arity;
|
|
||||||
Functio::Builtin(BuiltinFunctio::new(
|
|
||||||
move |args| b.call(&args.iter().cloned().rev().collect::<Vec<_>>()),
|
|
||||||
arity,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Functio::Chain { functios, kind } => {
|
|
||||||
let (a, b) = *functios;
|
|
||||||
Functio::Chain {
|
|
||||||
functios: Box::new((
|
|
||||||
(!Value::Functio(b)).into_functio(),
|
|
||||||
(!Value::Functio(a)).into_functio(),
|
|
||||||
)),
|
|
||||||
kind,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Functio::Eval(code) => Functio::Eval(code.chars().rev().collect()),
|
|
||||||
}),
|
|
||||||
Value::Cart(c) => Value::Cart(
|
|
||||||
c.into_iter()
|
|
||||||
.map(|(k, v)| (v.borrow().clone(), ValueRef::new(k)))
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Value {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
let other = other.clone();
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Value::Nul => matches!(other, Value::Nul),
|
|
||||||
Value::Str(s) => *s == other.to_string(),
|
|
||||||
Value::Int(i) => *i == other.into_isize(),
|
|
||||||
Value::Abool(a) => *a == other.into_abool(),
|
|
||||||
Value::Functio(f) => *f == other.into_functio(),
|
|
||||||
Value::Cart(c) => *c == other.into_cart(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Value {}
|
|
||||||
|
|
||||||
impl PartialOrd for Value {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
use std::cmp::Ordering::*;
|
|
||||||
let other = other.clone();
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Value::Nul => {
|
|
||||||
if other == Value::Nul {
|
|
||||||
Some(Equal)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Str(s) => Some(s.cmp(&other.to_string())),
|
|
||||||
Value::Int(i) => Some(i.cmp(&other.into_isize())),
|
|
||||||
Value::Abool(a) => a.partial_cmp(&other.into_abool()),
|
|
||||||
Value::Functio(_) => self.clone().into_isize().partial_cmp(&other.into_isize()),
|
|
||||||
Value::Cart(c) => Some(c.len().cmp(&other.into_cart().len())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Value {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Value::Nul => write!(f, "nul"),
|
|
||||||
Value::Str(v) => write!(f, "{}", v),
|
|
||||||
Value::Int(v) => write!(f, "{}", v),
|
|
||||||
Value::Abool(v) => write!(f, "{}", v),
|
|
||||||
Value::Functio(v) => match v {
|
|
||||||
Functio::Bf {
|
|
||||||
instructions,
|
|
||||||
tape_len,
|
|
||||||
} => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"({}) {}",
|
|
||||||
tape_len,
|
|
||||||
String::from_utf8(instructions.to_owned())
|
|
||||||
.expect("Brainfuck functio source should be UTF-8")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Functio::Able { params, body } => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"({}) -> {:?}",
|
|
||||||
params.join(", "),
|
|
||||||
// Maybe we should have a pretty-printer for
|
|
||||||
// statement blocks at some point?
|
|
||||||
body,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Functio::Builtin(b) => write!(f, "builtin @ {}", b.fn_addr()),
|
|
||||||
Functio::Chain { functios, kind } => {
|
|
||||||
let (a, b) = *functios.clone();
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{} {} {} ",
|
|
||||||
Value::Functio(a),
|
|
||||||
match kind {
|
|
||||||
FunctioChainKind::Equal => '+',
|
|
||||||
FunctioChainKind::ByArity => '*',
|
|
||||||
},
|
|
||||||
Value::Functio(b)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Functio::Eval(s) => write!(f, "{}", s),
|
|
||||||
},
|
|
||||||
Value::Cart(c) => {
|
|
||||||
write!(f, "[")?;
|
|
||||||
let mut cart_vec = c.iter().collect::<Vec<_>>();
|
|
||||||
cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less));
|
|
||||||
|
|
||||||
for (idx, (key, value)) in cart_vec.into_iter().enumerate() {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}{} <= {}",
|
|
||||||
if idx != 0 { ", " } else { "" },
|
|
||||||
value.borrow(),
|
|
||||||
key
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct ValueRef(Rc<RefCell<Value>>);
|
|
||||||
|
|
||||||
impl ValueRef {
|
|
||||||
pub fn new(v: Value) -> Self {
|
|
||||||
Self(Rc::new(RefCell::new(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn borrow(&self) -> Ref<Value> {
|
|
||||||
self.0.borrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn borrow_mut(&self) -> RefMut<Value> {
|
|
||||||
self.0.borrow_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn replace(&self, v: Value) -> Value {
|
|
||||||
self.0.replace(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Variable {
|
|
||||||
pub melo: bool,
|
|
||||||
|
|
||||||
// Multiple Variables can reference the same underlying Value when
|
|
||||||
// pass-by-reference is used, therefore we use Rc here.
|
|
||||||
pub value: ValueRef,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Variable {
|
|
||||||
pub fn from_value(value: Value) -> Self {
|
|
||||||
Self {
|
|
||||||
melo: false,
|
|
||||||
value: ValueRef::new(value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +1,15 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ablescript_cli"
|
name = "ablescript_cli"
|
||||||
version = "0.3.0"
|
version = "0.5.4"
|
||||||
authors = ["able <abl3theabove@gmail.com>"]
|
authors = ["AbleScript Developers"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
description = "The best programming language"
|
description = "The best programming language"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
documentation = "https://ablecorp.us/able-script-the-book/"
|
documentation = "https://gblecorp.github.io/able-script-the-book"
|
||||||
repository = "https://github.com/AbleCorp/able-script"
|
repository = "https://git.ablecorp.us/AbleScript/able-script"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ablescript = { version = "0.3.0", path = "../ablescript" }
|
ablescript = { version = "0.5.3", path = "../ablescript" }
|
||||||
|
clap = { version = "4.2", features = ["derive"] }
|
||||||
clap = "3.1"
|
rustyline = "11.0"
|
||||||
rustyline = "9.1"
|
|
||||||
|
|
|
@ -1,52 +1,31 @@
|
||||||
#![forbid(unsafe_code, clippy::unwrap_used)]
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
mod repl;
|
mod repl;
|
||||||
|
|
||||||
use ablescript::interpret::ExecEnv;
|
use ablescript::{interpret::ExecEnv, parser::parse};
|
||||||
use ablescript::parser::parse;
|
use clap::Parser;
|
||||||
use clap::{Arg, Command};
|
use std::{path::PathBuf, process::exit};
|
||||||
use std::process::exit;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// variables::test(); // NOTE(Able): Add this as a test case
|
// variables::test(); // NOTE(Able): Add this as a test case
|
||||||
let matches = Command::new("AbleScript")
|
let args = Args::parse();
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
match args.file {
|
||||||
.author("Able <abl3theabove@gmail.com>")
|
|
||||||
.about("AbleScript interpreter")
|
|
||||||
.arg(
|
|
||||||
Arg::new("file")
|
|
||||||
.short('f')
|
|
||||||
.long("file")
|
|
||||||
.value_name("FILE")
|
|
||||||
.help("Set the path to interpret from")
|
|
||||||
.takes_value(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("debug")
|
|
||||||
.long("debug")
|
|
||||||
.help("Enable debug AST printing"),
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
let ast_print = matches.is_present("debug");
|
|
||||||
|
|
||||||
match matches.value_of("file") {
|
|
||||||
Some(file_path) => {
|
Some(file_path) => {
|
||||||
// Read file
|
// Read file
|
||||||
let source = match std::fs::read_to_string(file_path) {
|
let source = match std::fs::read_to_string(&file_path) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Failed to read file \"{}\": {}", file_path, e);
|
println!("Failed to read file \"{:?}\": {}", file_path, e);
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse & evaluate
|
// Parse & evaluate
|
||||||
if let Err(e) = parse(&source).and_then(|ast| {
|
if let Err(e) = parse(&source).and_then(|ast| {
|
||||||
if ast_print {
|
if args.debug {
|
||||||
println!("{:#?}", ast);
|
eprintln!("{:#?}", ast);
|
||||||
}
|
}
|
||||||
ExecEnv::new().eval_stmts(&ast)
|
ExecEnv::<ablescript::host_interface::Standard>::default().eval_stmts(&ast)
|
||||||
}) {
|
}) {
|
||||||
println!(
|
println!(
|
||||||
"Error `{:?}` occurred at span: {:?} = `{:?}`",
|
"Error `{:?}` occurred at span: {:?} = `{:?}`",
|
||||||
|
@ -58,7 +37,19 @@ fn main() {
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
println!("Hi [AbleScript {}]", env!("CARGO_PKG_VERSION"));
|
println!("Hi [AbleScript {}]", env!("CARGO_PKG_VERSION"));
|
||||||
repl::repl(ast_print);
|
repl::repl(args.debug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(name = "AbleScript", version, about)]
|
||||||
|
struct Args {
|
||||||
|
/// File to execute
|
||||||
|
#[arg(short, long)]
|
||||||
|
file: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Dump AST to console
|
||||||
|
#[arg(short, long)]
|
||||||
|
debug: bool,
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
use ablescript::interpret::ExecEnv;
|
use ablescript::{interpret::ExecEnv, parser::parse};
|
||||||
use ablescript::parser::parse;
|
use rustyline::DefaultEditor;
|
||||||
use rustyline::Editor;
|
|
||||||
|
|
||||||
pub fn repl(ast_print: bool) {
|
pub fn repl(ast_print: bool) {
|
||||||
let mut rl = Editor::<()>::new();
|
let mut rl = match DefaultEditor::new() {
|
||||||
let mut env = ExecEnv::new();
|
Ok(rl) => rl,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to create editor: {e}");
|
||||||
|
std::process::exit(-1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut env = ExecEnv::<ablescript::host_interface::Standard>::default();
|
||||||
|
|
||||||
// If this is `Some`, the user has previously entered an
|
// If this is `Some`, the user has previously entered an
|
||||||
// incomplete statement and is now completing it; otherwise, the
|
// incomplete statement and is now completing it; otherwise, the
|
||||||
|
@ -14,7 +20,7 @@ pub fn repl(ast_print: bool) {
|
||||||
match rl.readline(if partial.is_some() { ">> " } else { ":: " }) {
|
match rl.readline(if partial.is_some() { ">> " } else { ":: " }) {
|
||||||
Ok(readline) => {
|
Ok(readline) => {
|
||||||
let readline = readline.trim_end();
|
let readline = readline.trim_end();
|
||||||
rl.add_history_entry(readline);
|
let _ = rl.add_history_entry(readline);
|
||||||
|
|
||||||
let partial_data = match partial {
|
let partial_data = match partial {
|
||||||
Some(line) => line + readline,
|
Some(line) => line + readline,
|
||||||
|
@ -23,7 +29,7 @@ pub fn repl(ast_print: bool) {
|
||||||
|
|
||||||
partial = match parse(&partial_data).and_then(|ast| {
|
partial = match parse(&partial_data).and_then(|ast| {
|
||||||
if ast_print {
|
if ast_print {
|
||||||
println!("{:#?}", &ast);
|
eprintln!("{:#?}", &ast);
|
||||||
}
|
}
|
||||||
env.eval_stmts(&ast)
|
env.eval_stmts(&ast)
|
||||||
}) {
|
}) {
|
||||||
|
@ -31,7 +37,7 @@ pub fn repl(ast_print: bool) {
|
||||||
Err(ablescript::error::Error {
|
Err(ablescript::error::Error {
|
||||||
// Treat "Unexpected EOF" errors as "we need
|
// Treat "Unexpected EOF" errors as "we need
|
||||||
// more data".
|
// more data".
|
||||||
kind: ablescript::error::ErrorKind::UnexpectedEof,
|
kind: ablescript::error::ErrorKind::UnexpectedEoi,
|
||||||
..
|
..
|
||||||
}) => Some(partial_data),
|
}) => Some(partial_data),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
@ -25,20 +25,20 @@ owo arity_1(/*foo*/);
|
||||||
owo arity_2(/*foo*/, /*bar*/);
|
owo arity_2(/*foo*/, /*bar*/);
|
||||||
owo arity_3(/*foo*/, /*bar*/, /*baz*/);
|
owo arity_3(/*foo*/, /*bar*/, /*baz*/);
|
||||||
|
|
||||||
dim i1 arity_0 * arity_1;
|
i1 dim arity_0 * arity_1;
|
||||||
i1(/*second*/);
|
i1(/*second*/);
|
||||||
|
|
||||||
/*----*/ print;
|
/*----*/ print;
|
||||||
|
|
||||||
dim i2 arity_1 * arity_0;
|
i2 dim arity_1 * arity_0;
|
||||||
i2(/*first*/);
|
i2(/*first*/);
|
||||||
|
|
||||||
/*----*/ print;
|
/*----*/ print;
|
||||||
|
|
||||||
dim ifancy arity_3 * arity_3;
|
ifancy dim arity_3 * arity_3;
|
||||||
ifancy(/*left1*/, /*right1*/, /*left2*/, /*right2*/, /*left3*/, /*right3*/);
|
ifancy(/*left1*/, /*right1*/, /*left2*/, /*right2*/, /*left3*/, /*right3*/);
|
||||||
|
|
||||||
/*---*/ print;
|
/*---*/ print;
|
||||||
|
|
||||||
dim another arity_0 * arity_3;
|
another dim arity_0 * arity_3;
|
||||||
another(/*right1*/, /*right2*/, /*right3*/);
|
another(/*right1*/, /*right2*/, /*right3*/);
|
||||||
|
|
|
@ -2,7 +2,7 @@ functio helloable() {
|
||||||
/*Hello, Able!*/ print;
|
/*Hello, Able!*/ print;
|
||||||
}
|
}
|
||||||
|
|
||||||
dim cart [/*able*/ <= 42, helloable <= /*hello*/];
|
cart dim [/*able*/ <= 42, helloable <= /*hello*/];
|
||||||
|
|
||||||
cart[42] print;
|
cart[42] print;
|
||||||
cart[/*hello*/]();
|
cart[/*hello*/]();
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
dim hello /*world*/;
|
hello dim /*world*/;
|
||||||
hello print;
|
hello print;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
dim data;
|
data dim;
|
||||||
loop {
|
loop {
|
||||||
data read;
|
data read;
|
||||||
data print;
|
data print;
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
dim hi /*wonk*/;
|
hi dim /*wonk*/;
|
||||||
melo hi;
|
melo hi;
|
||||||
hi print; owo Should error out
|
hi print; owo Should error out
|
||||||
|
|
|
@ -2,13 +2,13 @@ owo Pass-by-reference test
|
||||||
|
|
||||||
owo Swap two variables.
|
owo Swap two variables.
|
||||||
functio swap(left, right) {
|
functio swap(left, right) {
|
||||||
dim tmp left;
|
tmp dim left;
|
||||||
right =: left;
|
right =: left;
|
||||||
tmp =: right;
|
tmp =: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
dim foo /*hello*/;
|
foo dim /*hello*/;
|
||||||
dim bar /*world*/;
|
bar dim /*world*/;
|
||||||
|
|
||||||
swap(foo, bar);
|
swap(foo, bar);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue