Begin rewrite with register-based bytecode
This commit is contained in:
parent
974310ffab
commit
7b055d79b5
211
Cargo.lock
generated
211
Cargo.lock
generated
@ -4,9 +4,9 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -23,15 +23,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.13"
|
||||
version = "0.6.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
|
||||
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
@ -43,32 +44,38 @@ checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.3"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.2"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.2"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@ -77,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.14"
|
||||
version = "4.5.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36"
|
||||
checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -87,9 +94,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.14"
|
||||
version = "4.5.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed"
|
||||
checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -117,9 +124,9 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
@ -152,9 +159,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "dust-lang"
|
||||
@ -182,15 +189,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.10.0"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.0"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
|
||||
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
@ -211,9 +218,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.12"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@ -233,10 +240,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
@ -246,9 +259,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
version = "0.2.158"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@ -258,30 +271,33 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.79"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -318,9 +334,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.9.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
@ -338,9 +354,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.3"
|
||||
version = "1.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@ -350,9 +366,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.6"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
|
||||
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@ -361,30 +377,30 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.17"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.203"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.203"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -393,11 +409,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.117"
|
||||
version = "1.0.128"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
||||
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
@ -410,9 +427,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.53"
|
||||
version = "2.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032"
|
||||
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -421,9 +438,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
@ -433,9 +450,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
@ -458,7 +475,7 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.4",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -478,17 +495,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.4",
|
||||
"windows_aarch64_msvc 0.52.4",
|
||||
"windows_i686_gnu 0.52.4",
|
||||
"windows_i686_msvc 0.52.4",
|
||||
"windows_x86_64_gnu 0.52.4",
|
||||
"windows_x86_64_gnullvm 0.52.4",
|
||||
"windows_x86_64_msvc 0.52.4",
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -499,9 +517,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
@ -511,9 +529,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@ -523,9 +541,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@ -535,9 +559,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@ -547,9 +571,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
@ -559,9 +583,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@ -571,6 +595,27 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.4"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
@ -4,9 +4,9 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{AnnotatedError, Identifier, Instruction, Span, Value};
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Clone)]
|
||||
pub struct Chunk {
|
||||
code: Vec<(u8, Span)>,
|
||||
code: Vec<(Instruction, Span)>,
|
||||
constants: Vec<Option<Value>>,
|
||||
identifiers: Vec<Local>,
|
||||
scope_depth: usize,
|
||||
@ -23,7 +23,7 @@ impl Chunk {
|
||||
}
|
||||
|
||||
pub fn with_data(
|
||||
code: Vec<(u8, Span)>,
|
||||
code: Vec<(Instruction, Span)>,
|
||||
constants: Vec<Value>,
|
||||
identifiers: Vec<Local>,
|
||||
) -> Self {
|
||||
@ -47,17 +47,21 @@ impl Chunk {
|
||||
self.scope_depth
|
||||
}
|
||||
|
||||
pub fn get_code(&self, offset: usize, position: Span) -> Result<&(u8, Span), ChunkError> {
|
||||
pub fn get_code(
|
||||
&self,
|
||||
offset: usize,
|
||||
position: Span,
|
||||
) -> Result<&(Instruction, Span), ChunkError> {
|
||||
self.code
|
||||
.get(offset)
|
||||
.ok_or(ChunkError::CodeIndexOfBounds { offset, position })
|
||||
}
|
||||
|
||||
pub fn push_code<T: Into<u8>>(&mut self, into_byte: T, position: Span) {
|
||||
self.code.push((into_byte.into(), position));
|
||||
pub fn push_code(&mut self, instruction: Instruction, position: Span) {
|
||||
self.code.push((instruction, position));
|
||||
}
|
||||
|
||||
pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> {
|
||||
pub fn get_constant(&self, index: u16, position: Span) -> Result<&Value, ChunkError> {
|
||||
self.constants
|
||||
.get(index as usize)
|
||||
.ok_or(ChunkError::ConstantIndexOutOfBounds { index, position })
|
||||
@ -68,23 +72,15 @@ impl Chunk {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn use_constant(&mut self, index: u8, position: Span) -> Result<Value, ChunkError> {
|
||||
let index = index as usize;
|
||||
|
||||
pub fn use_constant(&mut self, index: u16, position: Span) -> Result<Value, ChunkError> {
|
||||
self.constants
|
||||
.get_mut(index)
|
||||
.ok_or_else(|| ChunkError::ConstantIndexOutOfBounds {
|
||||
index: index as u8,
|
||||
position,
|
||||
})?
|
||||
.get_mut(index as usize)
|
||||
.ok_or_else(|| ChunkError::ConstantIndexOutOfBounds { index, position })?
|
||||
.take()
|
||||
.ok_or(ChunkError::ConstantAlreadyUsed {
|
||||
index: index as u8,
|
||||
position,
|
||||
})
|
||||
.ok_or(ChunkError::ConstantAlreadyUsed { index, position })
|
||||
}
|
||||
|
||||
pub fn push_constant(&mut self, value: Value, position: Span) -> Result<u8, ChunkError> {
|
||||
pub fn push_constant(&mut self, value: Value, position: Span) -> Result<u16, ChunkError> {
|
||||
let starting_length = self.constants.len();
|
||||
|
||||
if starting_length + 1 > (u8::MAX as usize) {
|
||||
@ -92,7 +88,7 @@ impl Chunk {
|
||||
} else {
|
||||
self.constants.push(Some(value));
|
||||
|
||||
Ok(starting_length as u8)
|
||||
Ok(starting_length as u16)
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,7 +98,7 @@ impl Chunk {
|
||||
.any(|local| &local.identifier == identifier)
|
||||
}
|
||||
|
||||
pub fn get_local(&self, index: u8, position: Span) -> Result<&Local, ChunkError> {
|
||||
pub fn get_local(&self, index: u16, position: Span) -> Result<&Local, ChunkError> {
|
||||
self.identifiers
|
||||
.get(index as usize)
|
||||
.ok_or(ChunkError::IdentifierIndexOutOfBounds { index, position })
|
||||
@ -120,14 +116,14 @@ impl Chunk {
|
||||
&self,
|
||||
identifier: &Identifier,
|
||||
position: Span,
|
||||
) -> Result<u8, ChunkError> {
|
||||
) -> Result<u16, ChunkError> {
|
||||
self.identifiers
|
||||
.iter()
|
||||
.rev()
|
||||
.enumerate()
|
||||
.find_map(|(index, local)| {
|
||||
if &local.identifier == identifier {
|
||||
Some(index as u8)
|
||||
Some(index as u16)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -142,7 +138,7 @@ impl Chunk {
|
||||
&mut self,
|
||||
identifier: Identifier,
|
||||
position: Span,
|
||||
) -> Result<u8, ChunkError> {
|
||||
) -> Result<u16, ChunkError> {
|
||||
let starting_length = self.identifiers.len();
|
||||
|
||||
if starting_length + 1 > (u8::MAX as usize) {
|
||||
@ -153,7 +149,7 @@ impl Chunk {
|
||||
depth: self.scope_depth,
|
||||
});
|
||||
|
||||
Ok(starting_length as u8)
|
||||
Ok(starting_length as u16)
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,32 +179,19 @@ impl Chunk {
|
||||
let mut output = String::new();
|
||||
|
||||
let name_length = name.len();
|
||||
let buffer_length = 32_usize.saturating_sub(name_length + 2);
|
||||
let buffer_length = 34_usize.saturating_sub(name_length + 2);
|
||||
let name_buffer = " ".repeat(buffer_length / 2);
|
||||
let name_line = format!("{name_buffer} {name} {name_buffer}\n");
|
||||
let name_line = format!("\n{name_buffer}{name}{name_buffer}\n");
|
||||
let name_underline = format!("{name_buffer}{}{name_buffer}\n", "-".repeat(name_length));
|
||||
|
||||
output.push_str(&name_line);
|
||||
output.push_str(&name_underline);
|
||||
output.push_str("\n Code \n");
|
||||
output.push_str("------ ------------ ------------\n");
|
||||
output.push_str("OFFSET POSITION INSTRUCTION\n");
|
||||
output.push_str("------ ------------ ------------\n");
|
||||
|
||||
let mut previous = None;
|
||||
|
||||
for (offset, (byte, position)) in self.code.iter().enumerate() {
|
||||
if let Some(
|
||||
Instruction::Constant
|
||||
| Instruction::DeclareVariable
|
||||
| Instruction::GetVariable
|
||||
| Instruction::SetVariable,
|
||||
) = previous
|
||||
{
|
||||
previous = None;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
let instruction = Instruction::from_byte(*byte).unwrap();
|
||||
for (offset, (instruction, position)) in self.code.iter().enumerate() {
|
||||
let display = format!(
|
||||
"{offset:4} {:12} {}\n",
|
||||
position.to_string(),
|
||||
@ -216,8 +199,6 @@ impl Chunk {
|
||||
);
|
||||
|
||||
output.push_str(&display);
|
||||
|
||||
previous = Some(instruction);
|
||||
}
|
||||
|
||||
output.push_str("\n Constants\n");
|
||||
@ -296,18 +277,18 @@ pub enum ChunkError {
|
||||
position: Span,
|
||||
},
|
||||
ConstantAlreadyUsed {
|
||||
index: u8,
|
||||
index: u16,
|
||||
position: Span,
|
||||
},
|
||||
ConstantOverflow {
|
||||
position: Span,
|
||||
},
|
||||
ConstantIndexOutOfBounds {
|
||||
index: u8,
|
||||
index: u16,
|
||||
position: Span,
|
||||
},
|
||||
IdentifierIndexOutOfBounds {
|
||||
index: u8,
|
||||
index: u16,
|
||||
position: Span,
|
||||
},
|
||||
IdentifierOverflow {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use annotate_snippets::{Level, Renderer, Snippet};
|
||||
|
||||
use crate::{vm::VmError, LexError, ParseError, Span};
|
||||
use crate::{LexError, ParseError, Span};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DustError<'src> {
|
||||
@ -12,10 +12,6 @@ pub enum DustError<'src> {
|
||||
error: ParseError,
|
||||
source: &'src str,
|
||||
},
|
||||
Runtime {
|
||||
error: VmError,
|
||||
source: &'src str,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'src> DustError<'src> {
|
||||
@ -24,20 +20,6 @@ impl<'src> DustError<'src> {
|
||||
let renderer = Renderer::styled();
|
||||
|
||||
match self {
|
||||
DustError::Runtime { error, source } => {
|
||||
let position = error.position();
|
||||
let label = format!("Runtime error: {}", error.description());
|
||||
let details = error
|
||||
.details()
|
||||
.unwrap_or_else(|| "While running this code".to_string());
|
||||
let message = Level::Error.title(&label).snippet(
|
||||
Snippet::source(source)
|
||||
.fold(true)
|
||||
.annotation(Level::Error.span(position.0..position.1).label(&details)),
|
||||
);
|
||||
|
||||
report.push_str(&renderer.render(message).to_string());
|
||||
}
|
||||
DustError::Parse { error, source } => {
|
||||
let position = error.position();
|
||||
let label = format!("Parse error: {}", error.description());
|
||||
|
@ -1,141 +1,214 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{Chunk, Span};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Instruction {
|
||||
Constant = 0,
|
||||
Return = 1,
|
||||
Pop = 2,
|
||||
|
||||
// Variables
|
||||
DeclareVariable = 3,
|
||||
GetVariable = 4,
|
||||
SetVariable = 5,
|
||||
|
||||
// Unary
|
||||
Negate = 6,
|
||||
Not = 7,
|
||||
|
||||
// Binary
|
||||
Add = 8,
|
||||
Subtract = 9,
|
||||
Multiply = 10,
|
||||
Divide = 11,
|
||||
Greater = 12,
|
||||
Less = 13,
|
||||
GreaterEqual = 14,
|
||||
LessEqual = 15,
|
||||
Equal = 16,
|
||||
NotEqual = 17,
|
||||
And = 18,
|
||||
Or = 19,
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Instruction {
|
||||
opcode: OpCode,
|
||||
to_register: u8,
|
||||
arguments: [u8; 2],
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
pub fn from_byte(byte: u8) -> Option<Self> {
|
||||
match byte {
|
||||
0 => Some(Instruction::Constant),
|
||||
1 => Some(Instruction::Return),
|
||||
2 => Some(Instruction::Pop),
|
||||
3 => Some(Instruction::DeclareVariable),
|
||||
4 => Some(Instruction::GetVariable),
|
||||
5 => Some(Instruction::SetVariable),
|
||||
6 => Some(Instruction::Negate),
|
||||
7 => Some(Instruction::Not),
|
||||
8 => Some(Instruction::Add),
|
||||
9 => Some(Instruction::Subtract),
|
||||
10 => Some(Instruction::Multiply),
|
||||
11 => Some(Instruction::Divide),
|
||||
12 => Some(Instruction::Greater),
|
||||
13 => Some(Instruction::Less),
|
||||
14 => Some(Instruction::GreaterEqual),
|
||||
15 => Some(Instruction::LessEqual),
|
||||
16 => Some(Instruction::Equal),
|
||||
17 => Some(Instruction::NotEqual),
|
||||
18 => Some(Instruction::And),
|
||||
19 => Some(Instruction::Or),
|
||||
_ => None,
|
||||
pub fn r#move(to_register: u8, from_register: u8) -> Instruction {
|
||||
Instruction {
|
||||
opcode: OpCode::Move,
|
||||
to_register,
|
||||
arguments: [from_register, 0],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(to_register: u8) -> Instruction {
|
||||
Instruction {
|
||||
opcode: OpCode::Close,
|
||||
to_register,
|
||||
arguments: [0, 0],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_constant(to_register: u8, constant_index: u16) -> Instruction {
|
||||
Instruction {
|
||||
opcode: OpCode::LoadConstant,
|
||||
to_register,
|
||||
arguments: constant_index.to_le_bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn declare_variable(to_register: u8, variable_index: u16) -> Instruction {
|
||||
Instruction {
|
||||
opcode: OpCode::DeclareVariable,
|
||||
to_register,
|
||||
arguments: variable_index.to_le_bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_variable(to_register: u8, variable_index: u16) -> Instruction {
|
||||
Instruction {
|
||||
opcode: OpCode::GetVariable,
|
||||
to_register,
|
||||
arguments: variable_index.to_le_bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_variable(from_register: u8, variable_index: u16) -> Instruction {
|
||||
Instruction {
|
||||
opcode: OpCode::SetVariable,
|
||||
to_register: from_register,
|
||||
arguments: variable_index.to_le_bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(to_register: u8, left_register: u8, right_register: u8) -> Instruction {
|
||||
Instruction {
|
||||
opcode: OpCode::Add,
|
||||
to_register,
|
||||
arguments: [left_register, right_register],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subtract(to_register: u8, left_register: u8, right_register: u8) -> Instruction {
|
||||
Instruction {
|
||||
opcode: OpCode::Subtract,
|
||||
to_register,
|
||||
arguments: [left_register, right_register],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn multiply(to_register: u8, left_register: u8, right_register: u8) -> Instruction {
|
||||
Instruction {
|
||||
opcode: OpCode::Multiply,
|
||||
to_register,
|
||||
arguments: [left_register, right_register],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn divide(to_register: u8, left_register: u8, right_register: u8) -> Instruction {
|
||||
Instruction {
|
||||
opcode: OpCode::Divide,
|
||||
to_register,
|
||||
arguments: [left_register, right_register],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn negate(to_register: u8, from_register: u8) -> Instruction {
|
||||
Instruction {
|
||||
opcode: OpCode::Negate,
|
||||
to_register,
|
||||
arguments: [from_register, 0],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#return() -> Instruction {
|
||||
Instruction {
|
||||
opcode: OpCode::Return,
|
||||
to_register: 0,
|
||||
arguments: [0, 0],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disassemble(&self, chunk: &Chunk, offset: usize) -> String {
|
||||
let dummy_position = Span(0, 0);
|
||||
|
||||
match self {
|
||||
Instruction::Constant => {
|
||||
let (argument, position) = *chunk.get_code(offset + 1, dummy_position).unwrap();
|
||||
let value_display = chunk
|
||||
.get_constant(argument, position)
|
||||
.map(|value| value.to_string())
|
||||
.unwrap_or_else(|error| format!("{error:?}"));
|
||||
|
||||
format!("CONSTANT {value_display}")
|
||||
match self.opcode {
|
||||
OpCode::Move => format!(
|
||||
"{:04} MOVE R{} R{}",
|
||||
offset, self.to_register, self.arguments[0]
|
||||
),
|
||||
OpCode::Close => {
|
||||
format!("{:04} CLOSE R{}", offset, self.to_register)
|
||||
}
|
||||
Instruction::Return => "RETURN".to_string(),
|
||||
Instruction::Pop => "POP".to_string(),
|
||||
OpCode::LoadConstant => {
|
||||
let constant_index = u16::from_le_bytes(self.arguments);
|
||||
let constant_display = match chunk.get_constant(constant_index, Span(0, 0)) {
|
||||
Ok(value) => value.to_string(),
|
||||
Err(error) => format!("{:?}", error),
|
||||
};
|
||||
|
||||
format!(
|
||||
"{:04} LOAD_CONSTANT R{} C{} {}",
|
||||
offset, self.to_register, constant_index, constant_display
|
||||
)
|
||||
}
|
||||
OpCode::DeclareVariable => {
|
||||
let variable_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]);
|
||||
|
||||
format!(
|
||||
"{:04} DECLARE_VARIABLE V{} R{}",
|
||||
offset, variable_index, self.to_register
|
||||
)
|
||||
}
|
||||
OpCode::GetVariable => {
|
||||
let variable_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]);
|
||||
|
||||
format!(
|
||||
"{:04} GET_VARIABLE R{} V{}",
|
||||
offset, self.to_register, variable_index
|
||||
)
|
||||
}
|
||||
OpCode::SetVariable => {
|
||||
let variable_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]);
|
||||
|
||||
format!(
|
||||
"{:04} SET_VARIABLE V{} R{}",
|
||||
offset, variable_index, self.to_register
|
||||
)
|
||||
}
|
||||
OpCode::Add => format!(
|
||||
"{:04} ADD R{} = R{} + R{}",
|
||||
offset, self.to_register, self.arguments[0], self.arguments[1]
|
||||
),
|
||||
OpCode::Subtract => format!(
|
||||
"{:04} SUBTRACT R{} = R{} - R{}",
|
||||
offset, self.to_register, self.arguments[0], self.arguments[1]
|
||||
),
|
||||
OpCode::Multiply => format!(
|
||||
"{:04} MULTIPLY R{} = R{} * R{}",
|
||||
offset, self.to_register, self.arguments[0], self.arguments[1]
|
||||
),
|
||||
OpCode::Divide => format!(
|
||||
"{:04} DIVIDE R{} = R{} / R{}",
|
||||
offset, self.to_register, self.arguments[0], self.arguments[1]
|
||||
),
|
||||
OpCode::Negate => format!(
|
||||
"{:04} NEGATE R{} = !R{}",
|
||||
offset, self.to_register, self.arguments[0]
|
||||
),
|
||||
OpCode::Return => format!("{:04} RETURN", offset),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum OpCode {
|
||||
// Stack manipulation
|
||||
Move,
|
||||
Close,
|
||||
|
||||
// Constants
|
||||
LoadConstant,
|
||||
|
||||
// Variables
|
||||
Instruction::DeclareVariable => {
|
||||
let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap();
|
||||
let identifier_display = chunk
|
||||
.get_identifier(*argument)
|
||||
.map(|identifier| identifier.to_string())
|
||||
.unwrap_or_else(|| "ERROR".to_string());
|
||||
DeclareVariable,
|
||||
GetVariable,
|
||||
SetVariable,
|
||||
|
||||
format!("DECLARE_VARIABLE {identifier_display}")
|
||||
}
|
||||
Instruction::GetVariable => {
|
||||
let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap();
|
||||
let identifier_display = chunk
|
||||
.get_identifier(*argument)
|
||||
.map(|identifier| identifier.to_string())
|
||||
.unwrap_or_else(|| "ERROR".to_string());
|
||||
// Binary operations
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
|
||||
format!("GET_VARIABLE {identifier_display}")
|
||||
}
|
||||
// Unary operations
|
||||
Negate,
|
||||
|
||||
Instruction::SetVariable => {
|
||||
let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap();
|
||||
let identifier_display = chunk
|
||||
.get_identifier(*argument)
|
||||
.map(|identifier| identifier.to_string())
|
||||
.unwrap_or_else(|| "ERROR".to_string());
|
||||
|
||||
format!("SET_VARIABLE {identifier_display}")
|
||||
}
|
||||
|
||||
// Unary
|
||||
Instruction::Negate => "NEGATE".to_string(),
|
||||
Instruction::Not => "NOT".to_string(),
|
||||
|
||||
// Binary
|
||||
Instruction::Add => "ADD".to_string(),
|
||||
Instruction::Subtract => "SUBTRACT".to_string(),
|
||||
Instruction::Multiply => "MULTIPLY".to_string(),
|
||||
Instruction::Divide => "DIVIDE".to_string(),
|
||||
Instruction::Greater => "GREATER".to_string(),
|
||||
Instruction::Less => "LESS".to_string(),
|
||||
Instruction::GreaterEqual => "GREATER_EQUAL".to_string(),
|
||||
Instruction::LessEqual => "LESS_EQUAL".to_string(),
|
||||
Instruction::Equal => "EQUAL".to_string(),
|
||||
Instruction::NotEqual => "NOT_EQUAL".to_string(),
|
||||
Instruction::And => "AND".to_string(),
|
||||
Instruction::Or => "OR".to_string(),
|
||||
}
|
||||
}
|
||||
// Control flow
|
||||
Return,
|
||||
}
|
||||
|
||||
impl Display for Instruction {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::mem::size_of;
|
||||
|
||||
impl From<Instruction> for u8 {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
instruction as u8
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn instruction_is_32_bits() {
|
||||
assert_eq!(size_of::<Instruction>(), 4);
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,18 @@
|
||||
//! The Dust programming language.
|
||||
//!
|
||||
//! To get started, you can use the `run` function to run a Dust program.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use dust_lang::{run, Value};
|
||||
//!
|
||||
//! let program = "
|
||||
//! let foo = 21
|
||||
//! let bar = 2
|
||||
//! foo * bar
|
||||
//! ";
|
||||
//!
|
||||
//! let the_answer = run(program).unwrap();
|
||||
//!
|
||||
//! assert_eq!(the_answer, Some(Value::integer(42)));
|
||||
//! ```
|
||||
pub mod chunk;
|
||||
pub mod constructor;
|
||||
pub mod dust_error;
|
||||
pub mod identifier;
|
||||
pub mod instruction;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod token;
|
||||
pub mod r#type;
|
||||
pub mod value;
|
||||
pub mod vm;
|
||||
mod chunk;
|
||||
mod constructor;
|
||||
mod dust_error;
|
||||
mod identifier;
|
||||
mod instruction;
|
||||
mod lexer;
|
||||
mod parser;
|
||||
mod token;
|
||||
mod r#type;
|
||||
mod value;
|
||||
|
||||
pub use chunk::{Chunk, ChunkError, Local};
|
||||
pub use constructor::{ConstructError, Constructor};
|
||||
use std::fmt::Display;
|
||||
|
||||
pub use chunk::{Chunk, ChunkError};
|
||||
pub use constructor::Constructor;
|
||||
pub use dust_error::{AnnotatedError, DustError};
|
||||
pub use identifier::Identifier;
|
||||
pub use instruction::Instruction;
|
||||
@ -36,18 +20,13 @@ pub use lexer::{lex, LexError, Lexer};
|
||||
pub use parser::{parse, ParseError, Parser};
|
||||
pub use r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
|
||||
pub use token::{Token, TokenKind, TokenOwned};
|
||||
pub use value::{Struct, Value, ValueError};
|
||||
pub use vm::{run, Vm};
|
||||
pub use value::{Enum, Function, Struct, Value};
|
||||
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Span(pub usize, pub usize);
|
||||
|
||||
impl Display for Span {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "({}, {})", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::{
|
||||
fmt::{self, Display, Formatter},
|
||||
mem,
|
||||
@ -24,12 +27,13 @@ pub fn parse(source: &str) -> Result<Chunk, DustError> {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Parser<'src> {
|
||||
lexer: Lexer<'src>,
|
||||
chunk: Chunk,
|
||||
previous_token: Token<'src>,
|
||||
previous_position: Span,
|
||||
lexer: Lexer<'src>,
|
||||
current_register: u8,
|
||||
current_token: Token<'src>,
|
||||
current_position: Span,
|
||||
previous_token: Token<'src>,
|
||||
previous_position: Span,
|
||||
}
|
||||
|
||||
impl<'src> Parser<'src> {
|
||||
@ -42,10 +46,11 @@ impl<'src> Parser<'src> {
|
||||
Parser {
|
||||
lexer,
|
||||
chunk: Chunk::new(),
|
||||
previous_token: Token::Eof,
|
||||
previous_position: Span(0, 0),
|
||||
current_register: 0,
|
||||
current_token,
|
||||
current_position,
|
||||
previous_token: Token::Eof,
|
||||
previous_position: Span(0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +58,20 @@ impl<'src> Parser<'src> {
|
||||
matches!(self.current_token, Token::Eof)
|
||||
}
|
||||
|
||||
fn increment_register(&mut self) -> Result<(), ParseError> {
|
||||
let current = self.current_register;
|
||||
|
||||
if current == u8::MAX {
|
||||
Err(ParseError::RegisterOverflow {
|
||||
position: self.current_position,
|
||||
})
|
||||
} else {
|
||||
self.current_register += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn advance(&mut self) -> Result<(), ParseError> {
|
||||
if self.is_eof() {
|
||||
return Ok(());
|
||||
@ -90,16 +109,19 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_byte<T: Into<u8>>(&mut self, into_byte: T, position: Span) {
|
||||
self.chunk.push_code(into_byte.into(), position);
|
||||
fn emit_instruction(&mut self, instruction: Instruction, position: Span) {
|
||||
self.chunk.push_code(instruction, position);
|
||||
}
|
||||
|
||||
fn emit_constant(&mut self, value: Value) -> Result<(), ParseError> {
|
||||
let position = self.previous_position;
|
||||
let constant_index = self.chunk.push_constant(value, position)?;
|
||||
|
||||
self.emit_byte(Instruction::Constant, position);
|
||||
self.emit_byte(constant_index, position);
|
||||
self.emit_instruction(
|
||||
Instruction::load_constant(self.current_register, constant_index),
|
||||
position,
|
||||
);
|
||||
self.increment_register()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -180,7 +202,9 @@ impl<'src> Parser<'src> {
|
||||
fn parse_unary(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||
let operator_position = self.previous_position;
|
||||
let byte = match self.previous_token.kind() {
|
||||
TokenKind::Minus => Instruction::Negate,
|
||||
TokenKind::Minus => {
|
||||
Instruction::negate(self.current_register, self.current_register - 1)
|
||||
}
|
||||
_ => {
|
||||
return Err(ParseError::ExpectedTokenMultiple {
|
||||
expected: vec![TokenKind::Minus],
|
||||
@ -191,7 +215,7 @@ impl<'src> Parser<'src> {
|
||||
};
|
||||
|
||||
self.parse_expression()?;
|
||||
self.emit_byte(byte, operator_position);
|
||||
self.emit_instruction(byte, operator_position);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -205,12 +229,18 @@ impl<'src> Parser<'src> {
|
||||
|
||||
self.parse(rule.precedence.increment())?;
|
||||
|
||||
let to_register = if self.current_register < 2 {
|
||||
self.current_register + 2
|
||||
} else {
|
||||
self.current_register
|
||||
};
|
||||
let left_register = to_register - 1;
|
||||
let right_register = to_register - 2;
|
||||
let byte = match operator {
|
||||
TokenKind::Plus => Instruction::Add,
|
||||
TokenKind::Minus => Instruction::Subtract,
|
||||
TokenKind::Star => Instruction::Multiply,
|
||||
TokenKind::Slash => Instruction::Divide,
|
||||
TokenKind::DoubleAmpersand => Instruction::And,
|
||||
TokenKind::Plus => Instruction::add(to_register, left_register, right_register),
|
||||
TokenKind::Minus => Instruction::subtract(to_register, left_register, right_register),
|
||||
TokenKind::Star => Instruction::multiply(to_register, left_register, right_register),
|
||||
TokenKind::Slash => Instruction::divide(to_register, left_register, right_register),
|
||||
_ => {
|
||||
return Err(ParseError::ExpectedTokenMultiple {
|
||||
expected: vec![
|
||||
@ -225,7 +255,7 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
};
|
||||
|
||||
self.emit_byte(byte, operator_position);
|
||||
self.emit_instruction(byte, operator_position);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -240,11 +270,17 @@ impl<'src> Parser<'src> {
|
||||
|
||||
if allow_assignment && self.allow(TokenKind::Equal)? {
|
||||
self.parse_expression()?;
|
||||
self.emit_byte(Instruction::SetVariable, self.previous_position);
|
||||
self.emit_byte(identifier_index, self.previous_position);
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::set_variable(self.current_register, identifier_index),
|
||||
self.previous_position,
|
||||
);
|
||||
self.increment_register()?;
|
||||
} else {
|
||||
self.emit_byte(Instruction::GetVariable, self.previous_position);
|
||||
self.emit_byte(identifier_index, self.previous_position);
|
||||
self.emit_instruction(
|
||||
Instruction::get_variable(self.current_register - 1, identifier_index),
|
||||
self.previous_position,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -254,7 +290,7 @@ impl<'src> Parser<'src> {
|
||||
&mut self,
|
||||
token: TokenOwned,
|
||||
position: Span,
|
||||
) -> Result<u8, ParseError> {
|
||||
) -> Result<u16, ParseError> {
|
||||
if let TokenOwned::Identifier(text) = token {
|
||||
let identifier = Identifier::new(text);
|
||||
|
||||
@ -291,8 +327,10 @@ impl<'src> Parser<'src> {
|
||||
.next_back()
|
||||
.map_or(false, |local| local.depth > self.chunk.scope_depth())
|
||||
{
|
||||
self.emit_byte(Instruction::Pop, self.current_position);
|
||||
self.chunk.pop_identifier();
|
||||
self.emit_instruction(
|
||||
Instruction::close(self.current_register),
|
||||
self.current_position,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -326,7 +364,7 @@ impl<'src> Parser<'src> {
|
||||
if is_expression_statement && !contains_block && !has_semicolon {
|
||||
let end = self.previous_position.1;
|
||||
|
||||
self.emit_byte(Instruction::Return, Span(start, end))
|
||||
self.emit_instruction(Instruction::r#return(), Span(start, end))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -353,8 +391,11 @@ impl<'src> Parser<'src> {
|
||||
|
||||
let identifier_index = self.chunk.declare_variable(identifier, position)?;
|
||||
|
||||
self.emit_byte(Instruction::DeclareVariable, position);
|
||||
self.emit_byte(identifier_index, position);
|
||||
self.emit_instruction(
|
||||
Instruction::set_variable(self.current_register, identifier_index),
|
||||
position,
|
||||
);
|
||||
self.increment_register()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -618,6 +659,9 @@ pub enum ParseError {
|
||||
identifier: Identifier,
|
||||
position: Span,
|
||||
},
|
||||
RegisterOverflow {
|
||||
position: Span,
|
||||
},
|
||||
|
||||
// Wrappers around foreign errors
|
||||
Chunk(ChunkError),
|
||||
@ -646,6 +690,7 @@ impl AnnotatedError for ParseError {
|
||||
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
|
||||
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
|
||||
Self::UndefinedVariable { .. } => "Undefined variable",
|
||||
Self::RegisterOverflow { .. } => "Register overflow",
|
||||
Self::Chunk { .. } => "Chunk error",
|
||||
Self::Lex(_) => "Lex error",
|
||||
Self::ParseIntError { .. } => "Failed to parse integer",
|
||||
@ -654,9 +699,7 @@ impl AnnotatedError for ParseError {
|
||||
|
||||
fn details(&self) -> Option<String> {
|
||||
match self {
|
||||
Self::ExpectedExpression { found, .. } => {
|
||||
Some(format!("Expected an expression, found \"{found}\""))
|
||||
}
|
||||
Self::ExpectedExpression { found, .. } => Some(format!("Found \"{found}\"")),
|
||||
Self::ExpectedToken {
|
||||
expected, found, ..
|
||||
} => Some(format!("Expected \"{expected}\", found \"{found}\"")),
|
||||
@ -664,11 +707,12 @@ impl AnnotatedError for ParseError {
|
||||
expected, found, ..
|
||||
} => Some(format!("Expected one of {expected:?}, found \"{found}\"")),
|
||||
Self::InvalidAssignmentTarget { found, .. } => {
|
||||
Some(format!("Invalid assignment target \"{found}\""))
|
||||
Some(format!("Invalid assignment target, found \"{found}\""))
|
||||
}
|
||||
Self::UndefinedVariable { identifier, .. } => {
|
||||
Some(format!("Undefined variable \"{identifier}\""))
|
||||
}
|
||||
Self::RegisterOverflow { .. } => None,
|
||||
Self::Chunk(error) => error.details(),
|
||||
Self::Lex(error) => Some(error.to_string()),
|
||||
Self::ParseIntError { error, .. } => Some(error.to_string()),
|
||||
@ -682,6 +726,7 @@ impl AnnotatedError for ParseError {
|
||||
Self::ExpectedTokenMultiple { position, .. } => *position,
|
||||
Self::InvalidAssignmentTarget { position, .. } => *position,
|
||||
Self::UndefinedVariable { position, .. } => *position,
|
||||
Self::RegisterOverflow { position } => *position,
|
||||
Self::Chunk(error) => error.position(),
|
||||
Self::Lex(error) => error.position(),
|
||||
Self::ParseIntError { position, .. } => *position,
|
||||
@ -694,283 +739,3 @@ impl From<LexError> for ParseError {
|
||||
Self::Lex(error)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::Local;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn block() {
|
||||
let source = "{ 42; 42 }";
|
||||
let test_chunk = parse(source);
|
||||
|
||||
assert_eq!(
|
||||
test_chunk,
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::Constant as u8, Span(2, 4)),
|
||||
(0, Span(2, 4)),
|
||||
(Instruction::Constant as u8, Span(6, 8)),
|
||||
(1, Span(6, 8)),
|
||||
(Instruction::Return as u8, Span(6, 8)),
|
||||
],
|
||||
vec![Value::integer(42), Value::integer(42)],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_variables() {
|
||||
let source = "let x = 42; let y = 42; x + y";
|
||||
let test_chunk = parse(source);
|
||||
|
||||
assert_eq!(
|
||||
test_chunk,
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::Constant as u8, Span(8, 10)),
|
||||
(0, Span(8, 10)),
|
||||
(Instruction::DeclareVariable as u8, Span(4, 5)),
|
||||
(0, Span(4, 5)),
|
||||
(Instruction::Constant as u8, Span(20, 22)),
|
||||
(1, Span(20, 22)),
|
||||
(Instruction::DeclareVariable as u8, Span(16, 17)),
|
||||
(1, Span(16, 17)),
|
||||
(Instruction::GetVariable as u8, Span(24, 25)),
|
||||
(0, Span(24, 25)),
|
||||
(Instruction::GetVariable as u8, Span(28, 29)),
|
||||
(1, Span(28, 29)),
|
||||
(Instruction::Add as u8, Span(26, 27)),
|
||||
(Instruction::Return as u8, Span(24, 29)),
|
||||
],
|
||||
vec![Value::integer(42), Value::integer(42)],
|
||||
vec![
|
||||
Local {
|
||||
identifier: Identifier::new("x"),
|
||||
depth: 0,
|
||||
},
|
||||
Local {
|
||||
identifier: Identifier::new("y"),
|
||||
depth: 0,
|
||||
},
|
||||
],
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn let_statement() {
|
||||
let source = "let x = 42;";
|
||||
let test_chunk = parse(source);
|
||||
|
||||
assert_eq!(
|
||||
test_chunk,
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::Constant as u8, Span(8, 10)),
|
||||
(0, Span(8, 10)),
|
||||
(Instruction::DeclareVariable as u8, Span(4, 5)),
|
||||
(0, Span(4, 5)),
|
||||
],
|
||||
vec![Value::integer(42)],
|
||||
vec![Local {
|
||||
identifier: Identifier::new("x"),
|
||||
depth: 0,
|
||||
}],
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string() {
|
||||
let source = "\"Hello, World!\"";
|
||||
let test_chunk = parse(source);
|
||||
|
||||
assert_eq!(
|
||||
test_chunk,
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::Constant as u8, Span(0, 15)),
|
||||
(0, Span(0, 15)),
|
||||
(Instruction::Return as u8, Span(0, 15)),
|
||||
],
|
||||
vec![Value::string("Hello, World!")],
|
||||
vec![],
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn integer() {
|
||||
let source = "42";
|
||||
let test_chunk = parse(source);
|
||||
|
||||
assert_eq!(
|
||||
test_chunk,
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::Constant as u8, Span(0, 2)),
|
||||
(0, Span(0, 2)),
|
||||
(Instruction::Return as u8, Span(0, 2)),
|
||||
],
|
||||
vec![Value::integer(42)],
|
||||
vec![],
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean() {
|
||||
let source = "true";
|
||||
let test_chunk = parse(source);
|
||||
|
||||
assert_eq!(
|
||||
test_chunk,
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::Constant as u8, Span(0, 4)),
|
||||
(0, Span(0, 4)),
|
||||
(Instruction::Return as u8, Span(0, 4)),
|
||||
],
|
||||
vec![Value::boolean(true)],
|
||||
vec![],
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grouping() {
|
||||
let source = "(42 + 42) * 2";
|
||||
let test_chunk = parse(source);
|
||||
|
||||
assert_eq!(
|
||||
test_chunk,
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::Constant as u8, Span(1, 3)),
|
||||
(0, Span(1, 3)),
|
||||
(Instruction::Constant as u8, Span(6, 8)),
|
||||
(1, Span(6, 8)),
|
||||
(Instruction::Add as u8, Span(4, 5)),
|
||||
(Instruction::Constant as u8, Span(12, 13)),
|
||||
(2, Span(12, 13)),
|
||||
(Instruction::Multiply as u8, Span(10, 11)),
|
||||
(Instruction::Return as u8, Span(0, 13)),
|
||||
],
|
||||
vec![Value::integer(42), Value::integer(42), Value::integer(2)],
|
||||
vec![],
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negation() {
|
||||
let source = "-(42)";
|
||||
let test_chunk = parse(source);
|
||||
|
||||
assert_eq!(
|
||||
test_chunk,
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::Constant as u8, Span(2, 4)),
|
||||
(0, Span(2, 4)),
|
||||
(Instruction::Negate as u8, Span(0, 1)),
|
||||
(Instruction::Return as u8, Span(0, 5)),
|
||||
],
|
||||
vec![Value::integer(42)],
|
||||
vec![],
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn addition() {
|
||||
let source = "42 + 42";
|
||||
let test_chunk = parse(source);
|
||||
|
||||
assert_eq!(
|
||||
test_chunk,
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::Constant as u8, Span(0, 2)),
|
||||
(0, Span(0, 2)),
|
||||
(Instruction::Constant as u8, Span(5, 7)),
|
||||
(1, Span(5, 7)),
|
||||
(Instruction::Add as u8, Span(3, 4)),
|
||||
(Instruction::Return as u8, Span(0, 7)),
|
||||
],
|
||||
vec![Value::integer(42), Value::integer(42)],
|
||||
vec![],
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subtraction() {
|
||||
let source = "42 - 42";
|
||||
let test_chunk = parse(source);
|
||||
|
||||
assert_eq!(
|
||||
test_chunk,
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::Constant as u8, Span(0, 2)),
|
||||
(0, Span(0, 2)),
|
||||
(Instruction::Constant as u8, Span(5, 7)),
|
||||
(1, Span(5, 7)),
|
||||
(Instruction::Subtract as u8, Span(3, 4)),
|
||||
(Instruction::Return as u8, Span(0, 7)),
|
||||
],
|
||||
vec![Value::integer(42), Value::integer(42)],
|
||||
vec![],
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiplication() {
|
||||
let source = "42 * 42";
|
||||
let test_chunk = parse(source);
|
||||
|
||||
assert_eq!(
|
||||
test_chunk,
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::Constant as u8, Span(0, 2)),
|
||||
(0, Span(0, 2)),
|
||||
(Instruction::Constant as u8, Span(5, 7)),
|
||||
(1, Span(5, 7)),
|
||||
(Instruction::Multiply as u8, Span(3, 4)),
|
||||
(Instruction::Return as u8, Span(0, 7)),
|
||||
],
|
||||
vec![Value::integer(42), Value::integer(42)],
|
||||
vec![],
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn division() {
|
||||
let source = "42 / 42";
|
||||
let test_chunk = parse(source);
|
||||
|
||||
assert_eq!(
|
||||
test_chunk,
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::Constant as u8, Span(0, 2)),
|
||||
(0, Span(0, 2)),
|
||||
(Instruction::Constant as u8, Span(5, 7)),
|
||||
(1, Span(5, 7)),
|
||||
(Instruction::Divide as u8, Span(3, 4)),
|
||||
(Instruction::Return as u8, Span(0, 7)),
|
||||
],
|
||||
vec![Value::integer(42), Value::integer(42)],
|
||||
vec![],
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
33
dust-lang/src/parser/tests.rs
Normal file
33
dust-lang/src/parser/tests.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn integer() {
|
||||
assert_eq!(
|
||||
parse("42"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0), Span(0, 2)),
|
||||
(Instruction::r#return(), Span(0, 2)),
|
||||
],
|
||||
vec![Value::integer(42),],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add() {
|
||||
assert_eq!(
|
||||
parse("1 + 2"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0), Span(0, 1)),
|
||||
(Instruction::load_constant(1, 1), Span(4, 5)),
|
||||
(Instruction::add(2, 1, 0), Span(2, 3)),
|
||||
(Instruction::r#return(), Span(0, 5)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2),],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
use crate::{parse, DustError, Value, Vm};
|
||||
|
||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||
let chunk = parse(source)?;
|
||||
|
||||
let mut vm = Vm::new(chunk);
|
||||
|
||||
vm.run()
|
||||
.map_err(|error| DustError::Runtime { error, source })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn add_variables() {
|
||||
let source = "let foo = 21; let bar = 21; foo + bar";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(42))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable() {
|
||||
let source = "let foo = 42; foo";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(42))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn integer() {
|
||||
let source = "42";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(42))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn addition() {
|
||||
let source = "21 + 21";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(42))));
|
||||
}
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
//! Token and TokenOwned types.
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Source code token.
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Token<'src> {
|
||||
// End of file
|
||||
Eof,
|
||||
@ -293,7 +291,7 @@ impl<'src> Display for Token<'src> {
|
||||
/// Owned version of `Token`, which owns all the strings.
|
||||
///
|
||||
/// This is used for errors.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum TokenOwned {
|
||||
Eof,
|
||||
|
||||
@ -412,7 +410,7 @@ impl Display for TokenOwned {
|
||||
}
|
||||
|
||||
/// Token representation that holds no data.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum TokenKind {
|
||||
Eof,
|
||||
|
||||
|
@ -1,465 +0,0 @@
|
||||
use crate::{
|
||||
dust_error::AnnotatedError, parse, Chunk, ChunkError, DustError, Identifier, Instruction, Span,
|
||||
Value, ValueError,
|
||||
};
|
||||
|
||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||
let chunk = parse(source)?;
|
||||
|
||||
let mut vm = Vm::new(chunk);
|
||||
|
||||
vm.run()
|
||||
.map_err(|error| DustError::Runtime { error, source })
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Vm {
|
||||
chunk: Chunk,
|
||||
ip: usize,
|
||||
stack: Vec<Value>,
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
const STACK_SIZE: usize = 256;
|
||||
|
||||
pub fn new(chunk: Chunk) -> Self {
|
||||
Self {
|
||||
chunk,
|
||||
ip: 0,
|
||||
stack: Vec::with_capacity(Self::STACK_SIZE),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<Option<Value>, VmError> {
|
||||
let mut current_position = Span(0, 0);
|
||||
|
||||
while let Ok((byte, position)) = self.read(current_position).copied() {
|
||||
current_position = position;
|
||||
|
||||
let instruction = Instruction::from_byte(byte)
|
||||
.ok_or_else(|| VmError::InvalidInstruction(byte, position))?;
|
||||
|
||||
log::trace!("Running instruction {instruction} at {position}");
|
||||
|
||||
match instruction {
|
||||
Instruction::Constant => {
|
||||
let (argument, _) = *self.read(position)?;
|
||||
let value = self.chunk.use_constant(argument, position)?;
|
||||
|
||||
log::trace!("Pushing constant {value}");
|
||||
|
||||
self.push(value, position)?;
|
||||
}
|
||||
Instruction::Return => {
|
||||
let value = self.pop(position)?;
|
||||
|
||||
log::trace!("Returning {value}");
|
||||
|
||||
return Ok(Some(value));
|
||||
}
|
||||
Instruction::Pop => {
|
||||
let value = self.pop(position)?;
|
||||
|
||||
log::trace!("Popping {value:?}");
|
||||
}
|
||||
|
||||
// Variables
|
||||
Instruction::DeclareVariable => {
|
||||
let (argument, _) = *self.read(position)?;
|
||||
let identifier = self
|
||||
.chunk
|
||||
.get_identifier(argument)
|
||||
.ok_or_else(|| VmError::UndeclaredVariable { position })?;
|
||||
let value = self.stack.remove(argument as usize);
|
||||
|
||||
log::trace!("Declaring {identifier} as value {value}",);
|
||||
|
||||
self.push(value, position)?;
|
||||
}
|
||||
Instruction::GetVariable => {
|
||||
let (argument, _) = *self.read(position)?;
|
||||
let identifier = self
|
||||
.chunk
|
||||
.get_identifier(argument)
|
||||
.ok_or_else(|| VmError::UndeclaredVariable { position })?;
|
||||
let value = self.stack.remove(argument as usize);
|
||||
|
||||
log::trace!("Getting {identifier} as value {value}",);
|
||||
|
||||
self.push(value, position)?;
|
||||
}
|
||||
Instruction::SetVariable => {
|
||||
let (argument, _) = *self.read(position)?;
|
||||
let identifier = self
|
||||
.chunk
|
||||
.get_identifier(argument)
|
||||
.ok_or_else(|| VmError::UndeclaredVariable { position })?
|
||||
.clone();
|
||||
|
||||
if !self.chunk.contains_identifier(&identifier) {
|
||||
return Err(VmError::UndefinedVariable {
|
||||
identifier: identifier.clone(),
|
||||
position,
|
||||
});
|
||||
}
|
||||
|
||||
let value = self.pop(position)?;
|
||||
|
||||
log::trace!("Setting {identifier} to {value}");
|
||||
|
||||
self.stack[argument as usize] = value;
|
||||
}
|
||||
|
||||
// Unary
|
||||
Instruction::Negate => {
|
||||
let negated = self
|
||||
.pop(position)?
|
||||
.negate()
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(negated, position)?;
|
||||
}
|
||||
Instruction::Not => {
|
||||
let not = self
|
||||
.pop(position)?
|
||||
.not()
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(not, position)?;
|
||||
}
|
||||
|
||||
// Binary
|
||||
Instruction::Add => {
|
||||
let right = self.pop(position)?;
|
||||
let left = self.pop(position)?;
|
||||
let sum = left
|
||||
.add(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(sum, position)?;
|
||||
}
|
||||
Instruction::Subtract => {
|
||||
let right = self.pop(position)?;
|
||||
let left = self.pop(position)?;
|
||||
let difference = left
|
||||
.subtract(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(difference, position)?;
|
||||
}
|
||||
Instruction::Multiply => {
|
||||
let right = self.pop(position)?;
|
||||
let left = self.pop(position)?;
|
||||
let product = left
|
||||
.multiply(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(product, position)?;
|
||||
}
|
||||
Instruction::Divide => {
|
||||
let right = self.pop(position)?;
|
||||
let left = self.pop(position)?;
|
||||
let quotient = left
|
||||
.divide(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(quotient, position)?;
|
||||
}
|
||||
Instruction::Greater => {
|
||||
let right = self.pop(position)?;
|
||||
let left = self.pop(position)?;
|
||||
let greater = left
|
||||
.greater_than(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(greater, position)?;
|
||||
}
|
||||
Instruction::Less => {
|
||||
let right = self.pop(position)?;
|
||||
let left = self.pop(position)?;
|
||||
let less = left
|
||||
.less_than(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(less, position)?;
|
||||
}
|
||||
Instruction::GreaterEqual => {
|
||||
let right = self.pop(position)?;
|
||||
let left = self.pop(position)?;
|
||||
let greater_equal = left
|
||||
.greater_than_or_equal(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(greater_equal, position)?;
|
||||
}
|
||||
Instruction::LessEqual => {
|
||||
let right = self.pop(position)?;
|
||||
let left = self.pop(position)?;
|
||||
let less_equal = left
|
||||
.less_than_or_equal(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(less_equal, position)?;
|
||||
}
|
||||
Instruction::Equal => {
|
||||
let right = self.pop(position)?;
|
||||
let left = self.pop(position)?;
|
||||
let equal = left
|
||||
.equal(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(equal, position)?;
|
||||
}
|
||||
Instruction::NotEqual => {
|
||||
let right = self.pop(position)?;
|
||||
let left = self.pop(position)?;
|
||||
let not_equal = left
|
||||
.not_equal(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(not_equal, position)?;
|
||||
}
|
||||
Instruction::And => {
|
||||
let right = self.pop(position)?;
|
||||
let left = self.pop(position)?;
|
||||
let and = left
|
||||
.and(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(and, position)?;
|
||||
}
|
||||
Instruction::Or => {
|
||||
let right = self.pop(position)?;
|
||||
let left = self.pop(position)?;
|
||||
let or = left
|
||||
.or(&right)
|
||||
.map_err(|error| VmError::Value { error, position })?;
|
||||
|
||||
self.push(or, position)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn push(&mut self, value: Value, position: Span) -> Result<(), VmError> {
|
||||
if self.stack.len() == Self::STACK_SIZE {
|
||||
Err(VmError::StackOverflow(position))
|
||||
} else {
|
||||
self.stack.push(value);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn pop(&mut self, position: Span) -> Result<Value, VmError> {
|
||||
if let Some(stacked) = self.stack.pop() {
|
||||
Ok(stacked)
|
||||
} else {
|
||||
Err(VmError::StackUnderflow(position))
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&mut self, position: Span) -> Result<&(u8, Span), VmError> {
|
||||
let current = self.chunk.get_code(self.ip, position)?;
|
||||
|
||||
self.ip += 1;
|
||||
|
||||
Ok(current)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum VmError {
|
||||
InvalidInstruction(u8, Span),
|
||||
StackOverflow(Span),
|
||||
StackUnderflow(Span),
|
||||
UndeclaredVariable {
|
||||
position: Span,
|
||||
},
|
||||
UndefinedVariable {
|
||||
identifier: Identifier,
|
||||
position: Span,
|
||||
},
|
||||
|
||||
// Wrappers for foreign errors
|
||||
Chunk(ChunkError),
|
||||
Value {
|
||||
error: ValueError,
|
||||
position: Span,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ChunkError> for VmError {
|
||||
fn from(v: ChunkError) -> Self {
|
||||
Self::Chunk(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl AnnotatedError for VmError {
|
||||
fn title() -> &'static str {
|
||||
"Runtime Error"
|
||||
}
|
||||
|
||||
fn description(&self) -> &'static str {
|
||||
match self {
|
||||
Self::InvalidInstruction(_, _) => "Invalid instruction",
|
||||
Self::StackOverflow(_) => "Stack overflow",
|
||||
Self::StackUnderflow(_) => "Stack underflow",
|
||||
Self::UndeclaredVariable { .. } => "Undeclared variable",
|
||||
Self::UndefinedVariable { .. } => "Undefined variable",
|
||||
Self::Chunk(_) => "Chunk error",
|
||||
Self::Value { .. } => "Value error",
|
||||
}
|
||||
}
|
||||
|
||||
fn details(&self) -> Option<String> {
|
||||
match self {
|
||||
Self::InvalidInstruction(byte, _) => Some(format!(
|
||||
"The byte {byte} does not correspond to a valid instruction"
|
||||
)),
|
||||
Self::StackOverflow(position) => Some(format!("Stack overflow at {position}")),
|
||||
Self::StackUnderflow(position) => Some(format!("Stack underflow at {position}")),
|
||||
Self::UndeclaredVariable { .. } => Some("Variable is not declared".to_string()),
|
||||
Self::UndefinedVariable { identifier, .. } => {
|
||||
Some(format!("{identifier} is not in scope"))
|
||||
}
|
||||
Self::Chunk(error) => error.details(),
|
||||
Self::Value { error, .. } => Some(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn position(&self) -> Span {
|
||||
match self {
|
||||
Self::InvalidInstruction(_, position) => *position,
|
||||
Self::StackUnderflow(position) => *position,
|
||||
Self::StackOverflow(position) => *position,
|
||||
Self::UndeclaredVariable { position } => *position,
|
||||
Self::UndefinedVariable { position, .. } => *position,
|
||||
Self::Chunk(error) => error.position(),
|
||||
Self::Value { position, .. } => *position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn negation() {
|
||||
let mut chunk = Chunk::new();
|
||||
let dummy_position = Span(0, 0);
|
||||
let constant = chunk
|
||||
.push_constant(Value::integer(42), dummy_position)
|
||||
.unwrap();
|
||||
|
||||
chunk.push_code(Instruction::Constant as u8, dummy_position);
|
||||
chunk.push_code(constant, dummy_position);
|
||||
chunk.push_code(Instruction::Negate as u8, dummy_position);
|
||||
chunk.push_code(Instruction::Return as u8, dummy_position);
|
||||
|
||||
let mut vm = Vm::new(chunk);
|
||||
let result = vm.run();
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(-42))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn addition() {
|
||||
let mut chunk = Chunk::new();
|
||||
let dummy_position = Span(0, 0);
|
||||
let left = chunk
|
||||
.push_constant(Value::integer(42), dummy_position)
|
||||
.unwrap();
|
||||
let right = chunk
|
||||
.push_constant(Value::integer(23), dummy_position)
|
||||
.unwrap();
|
||||
|
||||
chunk.push_code(Instruction::Constant, dummy_position);
|
||||
chunk.push_code(left, dummy_position);
|
||||
chunk.push_code(Instruction::Constant, dummy_position);
|
||||
chunk.push_code(right, dummy_position);
|
||||
chunk.push_code(Instruction::Add, dummy_position);
|
||||
chunk.push_code(Instruction::Return, dummy_position);
|
||||
|
||||
let mut vm = Vm::new(chunk);
|
||||
let result = vm.run();
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(65))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subtraction() {
|
||||
let mut chunk = Chunk::new();
|
||||
let dummy_position = Span(0, 0);
|
||||
let left = chunk
|
||||
.push_constant(Value::integer(42), dummy_position)
|
||||
.unwrap();
|
||||
let right = chunk
|
||||
.push_constant(Value::integer(23), dummy_position)
|
||||
.unwrap();
|
||||
|
||||
chunk.push_code(Instruction::Constant, dummy_position);
|
||||
chunk.push_code(left, dummy_position);
|
||||
chunk.push_code(Instruction::Constant, dummy_position);
|
||||
chunk.push_code(right, dummy_position);
|
||||
chunk.push_code(Instruction::Subtract, dummy_position);
|
||||
chunk.push_code(Instruction::Return, dummy_position);
|
||||
|
||||
let mut vm = Vm::new(chunk);
|
||||
let result = vm.run();
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(19))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiplication() {
|
||||
let mut chunk = Chunk::new();
|
||||
let dummy_position = Span(0, 0);
|
||||
let left = chunk
|
||||
.push_constant(Value::integer(42), dummy_position)
|
||||
.unwrap();
|
||||
let right = chunk
|
||||
.push_constant(Value::integer(23), dummy_position)
|
||||
.unwrap();
|
||||
|
||||
chunk.push_code(Instruction::Constant, dummy_position);
|
||||
chunk.push_code(left, dummy_position);
|
||||
chunk.push_code(Instruction::Constant, dummy_position);
|
||||
chunk.push_code(right, dummy_position);
|
||||
chunk.push_code(Instruction::Multiply, dummy_position);
|
||||
chunk.push_code(Instruction::Return, dummy_position);
|
||||
|
||||
let mut vm = Vm::new(chunk);
|
||||
let result = vm.run();
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(966))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
fn division() {
|
||||
let mut chunk = Chunk::new();
|
||||
let dummy_position = Span(0, 0);
|
||||
let left = chunk
|
||||
.push_constant(Value::integer(42), dummy_position)
|
||||
.unwrap();
|
||||
let right = chunk
|
||||
.push_constant(Value::integer(23), dummy_position)
|
||||
.unwrap();
|
||||
|
||||
chunk.push_code(Instruction::Constant, dummy_position);
|
||||
chunk.push_code(left, dummy_position);
|
||||
chunk.push_code(Instruction::Constant, dummy_position);
|
||||
chunk.push_code(right, dummy_position);
|
||||
chunk.push_code(Instruction::Divide, dummy_position);
|
||||
chunk.push_code(Instruction::Return, dummy_position);
|
||||
|
||||
let mut vm = Vm::new(chunk);
|
||||
let result = vm.run();
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(1))));
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn addition() {
|
||||
let source = "21 + 21";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(42))));
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn integer() {
|
||||
let source = "42";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(42))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float() {
|
||||
let source = "42.0";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::float(42.0))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string() {
|
||||
let source = "\"Hello, World!\"";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::string("Hello, World!"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean() {
|
||||
let source = "true";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::boolean(true))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn byte() {
|
||||
let source = "0x42";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::byte(0x42))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn character() {
|
||||
let source = "'a'";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::character('a'))));
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn add_variables() {
|
||||
let source = "let foo = 21; let bar = 21; foo + bar";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(42))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable() {
|
||||
let source = "let foo = 42; foo";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(42))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lots_of_variables() {
|
||||
env_logger::builder().is_test(true).try_init().unwrap();
|
||||
|
||||
let source = "
|
||||
let foo = 1;
|
||||
let bar = 2;
|
||||
let baz = 3;
|
||||
let qux = 4;
|
||||
let quux = 5;
|
||||
foo + bar + baz + qux + quux";
|
||||
let result = run(source);
|
||||
|
||||
assert_eq!(result, Ok(Some(Value::integer(15))));
|
||||
}
|
@ -1,78 +1,78 @@
|
||||
use std::{fs::read_to_string, io::Write};
|
||||
// use std::{fs::read_to_string, io::Write};
|
||||
|
||||
use clap::Parser;
|
||||
use colored::Colorize;
|
||||
use dust_lang::{parse, run};
|
||||
use log::Level;
|
||||
// use clap::Parser;
|
||||
// use colored::Colorize;
|
||||
// use dust_lang::{parse, run};
|
||||
// use log::Level;
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Cli {
|
||||
#[arg(short, long)]
|
||||
command: Option<String>,
|
||||
// #[derive(Parser)]
|
||||
// struct Cli {
|
||||
// #[arg(short, long)]
|
||||
// command: Option<String>,
|
||||
|
||||
#[arg(short, long)]
|
||||
parse: bool,
|
||||
// #[arg(short, long)]
|
||||
// parse: bool,
|
||||
|
||||
path: Option<String>,
|
||||
}
|
||||
// path: Option<String>,
|
||||
// }
|
||||
|
||||
fn main() {
|
||||
env_logger::builder()
|
||||
.parse_env("DUST_LOG")
|
||||
.format(|buf, record| {
|
||||
let level = match record.level() {
|
||||
Level::Error => "ERROR".red(),
|
||||
Level::Warn => "WARN".yellow(),
|
||||
Level::Info => "INFO".white(),
|
||||
Level::Debug => "DEBUG".blue(),
|
||||
Level::Trace => "TRACE".purple(),
|
||||
}
|
||||
.bold();
|
||||
let level_display = format!("[{level:^5}]").white().on_black();
|
||||
let module = record
|
||||
.module_path()
|
||||
.map(|path| path.split("::").last().unwrap_or("unknown"))
|
||||
.unwrap_or("unknown")
|
||||
.dimmed();
|
||||
// env_logger::builder()
|
||||
// .parse_env("DUST_LOG")
|
||||
// .format(|buf, record| {
|
||||
// let level = match record.level() {
|
||||
// Level::Error => "ERROR".red(),
|
||||
// Level::Warn => "WARN".yellow(),
|
||||
// Level::Info => "INFO".white(),
|
||||
// Level::Debug => "DEBUG".blue(),
|
||||
// Level::Trace => "TRACE".purple(),
|
||||
// }
|
||||
// .bold();
|
||||
// let level_display = format!("[{level:^5}]").white().on_black();
|
||||
// let module = record
|
||||
// .module_path()
|
||||
// .map(|path| path.split("::").last().unwrap_or("unknown"))
|
||||
// .unwrap_or("unknown")
|
||||
// .dimmed();
|
||||
|
||||
writeln!(buf, "{level_display} {module:^6} {}", record.args())
|
||||
})
|
||||
.init();
|
||||
// writeln!(buf, "{level_display} {module:^6} {}", record.args())
|
||||
// })
|
||||
// .init();
|
||||
|
||||
let args = Cli::parse();
|
||||
// let args = Cli::parse();
|
||||
|
||||
if let Some(command) = &args.command {
|
||||
if args.parse {
|
||||
parse_and_display_errors(command);
|
||||
} else {
|
||||
run_and_display_errors(command);
|
||||
}
|
||||
} else if let Some(path) = &args.path {
|
||||
let source = read_to_string(path).expect("Failed to read file");
|
||||
// if let Some(command) = &args.command {
|
||||
// if args.parse {
|
||||
// parse_and_display_errors(command);
|
||||
// } else {
|
||||
// run_and_display_errors(command);
|
||||
// }
|
||||
// } else if let Some(path) = &args.path {
|
||||
// let source = read_to_string(path).expect("Failed to read file");
|
||||
|
||||
if args.parse {
|
||||
parse_and_display_errors(&source);
|
||||
} else {
|
||||
run_and_display_errors(&source);
|
||||
}
|
||||
}
|
||||
// if args.parse {
|
||||
// parse_and_display_errors(&source);
|
||||
// } else {
|
||||
// run_and_display_errors(&source);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn parse_and_display_errors(source: &str) {
|
||||
match parse(source) {
|
||||
Ok(chunk) => println!("{}", chunk.disassemble("Dust CLI Input")),
|
||||
Err(error) => {
|
||||
eprintln!("{:?}", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
// fn parse_and_display_errors(source: &str) {
|
||||
// match parse(source) {
|
||||
// Ok(chunk) => println!("{}", chunk.disassemble("Dust CLI Input")),
|
||||
// Err(error) => {
|
||||
// eprintln!("{:?}", error);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
fn run_and_display_errors(source: &str) {
|
||||
match run(source) {
|
||||
Ok(Some(value)) => println!("{}", value),
|
||||
Ok(_) => {}
|
||||
Err(error) => {
|
||||
eprintln!("{}", error.report());
|
||||
}
|
||||
}
|
||||
}
|
||||
// fn run_and_display_errors(source: &str) {
|
||||
// match run(source) {
|
||||
// Ok(Some(value)) => println!("{}", value),
|
||||
// Ok(_) => {}
|
||||
// Err(error) => {
|
||||
// eprintln!("{}", error.report());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
Loading…
Reference in New Issue
Block a user