Begin rewrite with register-based bytecode

This commit is contained in:
Jeff 2024-09-11 23:07:20 -04:00
parent 974310ffab
commit 7b055d79b5
14 changed files with 542 additions and 1289 deletions

211
Cargo.lock generated
View File

@ -4,9 +4,9 @@ version = 3
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.1.2" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@ -23,15 +23,16 @@ dependencies = [
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.13" version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"anstyle-parse", "anstyle-parse",
"anstyle-query", "anstyle-query",
"anstyle-wincon", "anstyle-wincon",
"colorchoice", "colorchoice",
"is_terminal_polyfill",
"utf8parse", "utf8parse",
] ]
@ -43,32 +44,38 @@ checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]] [[package]]
name = "anstyle-parse" name = "anstyle-parse"
version = "0.2.3" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
dependencies = [ dependencies = [
"utf8parse", "utf8parse",
] ]
[[package]] [[package]]
name = "anstyle-query" name = "anstyle-query"
version = "1.0.2" version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
name = "anstyle-wincon" name = "anstyle-wincon"
version = "3.0.2" version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"windows-sys 0.52.0", "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]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -77,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.14" version = "4.5.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36" checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -87,9 +94,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.14" version = "4.5.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed" checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -117,9 +124,9 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.0" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]] [[package]]
name = "colored" name = "colored"
@ -152,9 +159,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.19" version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]] [[package]]
name = "dust-lang" name = "dust-lang"
@ -182,15 +189,15 @@ dependencies = [
[[package]] [[package]]
name = "either" name = "either"
version = "1.10.0" version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]] [[package]]
name = "env_filter" name = "env_filter"
version = "0.1.0" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
dependencies = [ dependencies = [
"log", "log",
"regex", "regex",
@ -211,9 +218,9 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.12" version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
@ -233,10 +240,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "itoa" name = "is_terminal_polyfill"
version = "1.0.10" version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "lazy_static" name = "lazy_static"
@ -246,9 +259,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.153" version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]] [[package]]
name = "log" name = "log"
@ -258,30 +271,33 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.1" version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.17" version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.79" version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.35" version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -318,9 +334,9 @@ dependencies = [
[[package]] [[package]]
name = "rayon" name = "rayon"
version = "1.9.0" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [ dependencies = [
"either", "either",
"rayon-core", "rayon-core",
@ -338,9 +354,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.10.3" version = "1.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -350,9 +366,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.4.6" version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -361,30 +377,30 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.2" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.17" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.203" version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.203" version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -393,11 +409,12 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.117" version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr",
"ryu", "ryu",
"serde", "serde",
] ]
@ -410,9 +427,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.53" version = "2.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -421,9 +438,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.12" version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
@ -433,9 +450,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "wasi" name = "wasi"
@ -458,7 +475,7 @@ 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 = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [ dependencies = [
"windows-targets 0.52.4", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@ -478,17 +495,18 @@ dependencies = [
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.4" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm 0.52.4", "windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.4", "windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.4", "windows_i686_gnu 0.52.6",
"windows_i686_msvc 0.52.4", "windows_i686_gnullvm",
"windows_x86_64_gnu 0.52.4", "windows_i686_msvc 0.52.6",
"windows_x86_64_gnullvm 0.52.4", "windows_x86_64_gnu 0.52.6",
"windows_x86_64_msvc 0.52.4", "windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
] ]
[[package]] [[package]]
@ -499,9 +517,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.52.4" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
@ -511,9 +529,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.4" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
@ -523,9 +541,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.4" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
@ -535,9 +559,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.4" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
@ -547,9 +571,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.4" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
@ -559,9 +583,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.4" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
@ -571,6 +595,27 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.4" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" 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",
]

View File

@ -4,9 +4,9 @@ use serde::{Deserialize, Serialize};
use crate::{AnnotatedError, Identifier, Instruction, Span, Value}; use crate::{AnnotatedError, Identifier, Instruction, Span, Value};
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone)]
pub struct Chunk { pub struct Chunk {
code: Vec<(u8, Span)>, code: Vec<(Instruction, Span)>,
constants: Vec<Option<Value>>, constants: Vec<Option<Value>>,
identifiers: Vec<Local>, identifiers: Vec<Local>,
scope_depth: usize, scope_depth: usize,
@ -23,7 +23,7 @@ impl Chunk {
} }
pub fn with_data( pub fn with_data(
code: Vec<(u8, Span)>, code: Vec<(Instruction, Span)>,
constants: Vec<Value>, constants: Vec<Value>,
identifiers: Vec<Local>, identifiers: Vec<Local>,
) -> Self { ) -> Self {
@ -47,17 +47,21 @@ impl Chunk {
self.scope_depth 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 self.code
.get(offset) .get(offset)
.ok_or(ChunkError::CodeIndexOfBounds { offset, position }) .ok_or(ChunkError::CodeIndexOfBounds { offset, position })
} }
pub fn push_code<T: Into<u8>>(&mut self, into_byte: T, position: Span) { pub fn push_code(&mut self, instruction: Instruction, position: Span) {
self.code.push((into_byte.into(), position)); 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 self.constants
.get(index as usize) .get(index as usize)
.ok_or(ChunkError::ConstantIndexOutOfBounds { index, position }) .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> { pub fn use_constant(&mut self, index: u16, position: Span) -> Result<Value, ChunkError> {
let index = index as usize;
self.constants self.constants
.get_mut(index) .get_mut(index as usize)
.ok_or_else(|| ChunkError::ConstantIndexOutOfBounds { .ok_or_else(|| ChunkError::ConstantIndexOutOfBounds { index, position })?
index: index as u8,
position,
})?
.take() .take()
.ok_or(ChunkError::ConstantAlreadyUsed { .ok_or(ChunkError::ConstantAlreadyUsed { index, position })
index: index as u8,
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(); let starting_length = self.constants.len();
if starting_length + 1 > (u8::MAX as usize) { if starting_length + 1 > (u8::MAX as usize) {
@ -92,7 +88,7 @@ impl Chunk {
} else { } else {
self.constants.push(Some(value)); 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) .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 self.identifiers
.get(index as usize) .get(index as usize)
.ok_or(ChunkError::IdentifierIndexOutOfBounds { index, position }) .ok_or(ChunkError::IdentifierIndexOutOfBounds { index, position })
@ -120,14 +116,14 @@ impl Chunk {
&self, &self,
identifier: &Identifier, identifier: &Identifier,
position: Span, position: Span,
) -> Result<u8, ChunkError> { ) -> Result<u16, ChunkError> {
self.identifiers self.identifiers
.iter() .iter()
.rev() .rev()
.enumerate() .enumerate()
.find_map(|(index, local)| { .find_map(|(index, local)| {
if &local.identifier == identifier { if &local.identifier == identifier {
Some(index as u8) Some(index as u16)
} else { } else {
None None
} }
@ -142,7 +138,7 @@ impl Chunk {
&mut self, &mut self,
identifier: Identifier, identifier: Identifier,
position: Span, position: Span,
) -> Result<u8, ChunkError> { ) -> Result<u16, ChunkError> {
let starting_length = self.identifiers.len(); let starting_length = self.identifiers.len();
if starting_length + 1 > (u8::MAX as usize) { if starting_length + 1 > (u8::MAX as usize) {
@ -153,7 +149,7 @@ impl Chunk {
depth: self.scope_depth, 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 mut output = String::new();
let name_length = name.len(); 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_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_line);
output.push_str(&name_underline);
output.push_str("\n Code \n"); output.push_str("\n Code \n");
output.push_str("------ ------------ ------------\n"); output.push_str("------ ------------ ------------\n");
output.push_str("OFFSET POSITION INSTRUCTION\n"); output.push_str("OFFSET POSITION INSTRUCTION\n");
output.push_str("------ ------------ ------------\n"); output.push_str("------ ------------ ------------\n");
let mut previous = None; for (offset, (instruction, position)) in self.code.iter().enumerate() {
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();
let display = format!( let display = format!(
"{offset:4} {:12} {}\n", "{offset:4} {:12} {}\n",
position.to_string(), position.to_string(),
@ -216,8 +199,6 @@ impl Chunk {
); );
output.push_str(&display); output.push_str(&display);
previous = Some(instruction);
} }
output.push_str("\n Constants\n"); output.push_str("\n Constants\n");
@ -296,18 +277,18 @@ pub enum ChunkError {
position: Span, position: Span,
}, },
ConstantAlreadyUsed { ConstantAlreadyUsed {
index: u8, index: u16,
position: Span, position: Span,
}, },
ConstantOverflow { ConstantOverflow {
position: Span, position: Span,
}, },
ConstantIndexOutOfBounds { ConstantIndexOutOfBounds {
index: u8, index: u16,
position: Span, position: Span,
}, },
IdentifierIndexOutOfBounds { IdentifierIndexOutOfBounds {
index: u8, index: u16,
position: Span, position: Span,
}, },
IdentifierOverflow { IdentifierOverflow {

View File

@ -1,6 +1,6 @@
use annotate_snippets::{Level, Renderer, Snippet}; use annotate_snippets::{Level, Renderer, Snippet};
use crate::{vm::VmError, LexError, ParseError, Span}; use crate::{LexError, ParseError, Span};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum DustError<'src> { pub enum DustError<'src> {
@ -12,10 +12,6 @@ pub enum DustError<'src> {
error: ParseError, error: ParseError,
source: &'src str, source: &'src str,
}, },
Runtime {
error: VmError,
source: &'src str,
},
} }
impl<'src> DustError<'src> { impl<'src> DustError<'src> {
@ -24,20 +20,6 @@ impl<'src> DustError<'src> {
let renderer = Renderer::styled(); let renderer = Renderer::styled();
match self { 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 } => { DustError::Parse { error, source } => {
let position = error.position(); let position = error.position();
let label = format!("Parse error: {}", error.description()); let label = format!("Parse error: {}", error.description());

View File

@ -1,141 +1,214 @@
use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use crate::{Chunk, Span}; use crate::{Chunk, Span};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Instruction { pub struct Instruction {
Constant = 0, opcode: OpCode,
Return = 1, to_register: u8,
Pop = 2, arguments: [u8; 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,
} }
impl Instruction { impl Instruction {
pub fn from_byte(byte: u8) -> Option<Self> { pub fn r#move(to_register: u8, from_register: u8) -> Instruction {
match byte { Instruction {
0 => Some(Instruction::Constant), opcode: OpCode::Move,
1 => Some(Instruction::Return), to_register,
2 => Some(Instruction::Pop), arguments: [from_register, 0],
3 => Some(Instruction::DeclareVariable), }
4 => Some(Instruction::GetVariable), }
5 => Some(Instruction::SetVariable),
6 => Some(Instruction::Negate), pub fn close(to_register: u8) -> Instruction {
7 => Some(Instruction::Not), Instruction {
8 => Some(Instruction::Add), opcode: OpCode::Close,
9 => Some(Instruction::Subtract), to_register,
10 => Some(Instruction::Multiply), arguments: [0, 0],
11 => Some(Instruction::Divide), }
12 => Some(Instruction::Greater), }
13 => Some(Instruction::Less),
14 => Some(Instruction::GreaterEqual), pub fn load_constant(to_register: u8, constant_index: u16) -> Instruction {
15 => Some(Instruction::LessEqual), Instruction {
16 => Some(Instruction::Equal), opcode: OpCode::LoadConstant,
17 => Some(Instruction::NotEqual), to_register,
18 => Some(Instruction::And), arguments: constant_index.to_le_bytes(),
19 => Some(Instruction::Or), }
_ => None, }
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 { pub fn disassemble(&self, chunk: &Chunk, offset: usize) -> String {
let dummy_position = Span(0, 0); match self.opcode {
OpCode::Move => format!(
match self { "{:04} MOVE R{} R{}",
Instruction::Constant => { offset, self.to_register, self.arguments[0]
let (argument, position) = *chunk.get_code(offset + 1, dummy_position).unwrap(); ),
let value_display = chunk OpCode::Close => {
.get_constant(argument, position) format!("{:04} CLOSE R{}", offset, self.to_register)
.map(|value| value.to_string())
.unwrap_or_else(|error| format!("{error:?}"));
format!("CONSTANT {value_display}")
} }
Instruction::Return => "RETURN".to_string(), OpCode::LoadConstant => {
Instruction::Pop => "POP".to_string(), 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 // Variables
Instruction::DeclareVariable => { DeclareVariable,
let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap(); GetVariable,
let identifier_display = chunk SetVariable,
.get_identifier(*argument)
.map(|identifier| identifier.to_string())
.unwrap_or_else(|| "ERROR".to_string());
format!("DECLARE_VARIABLE {identifier_display}") // Binary operations
} Add,
Instruction::GetVariable => { Subtract,
let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap(); Multiply,
let identifier_display = chunk Divide,
.get_identifier(*argument)
.map(|identifier| identifier.to_string())
.unwrap_or_else(|| "ERROR".to_string());
format!("GET_VARIABLE {identifier_display}") // Unary operations
} Negate,
Instruction::SetVariable => { // Control flow
let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap(); Return,
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(),
}
}
} }
impl Display for Instruction { #[cfg(test)]
fn fmt(&self, f: &mut Formatter) -> fmt::Result { mod tests {
write!(f, "{self:?}") use std::mem::size_of;
}
}
impl From<Instruction> for u8 { use super::*;
fn from(instruction: Instruction) -> Self {
instruction as u8 #[test]
fn instruction_is_32_bits() {
assert_eq!(size_of::<Instruction>(), 4);
} }
} }

View File

@ -1,34 +1,18 @@
//! The Dust programming language. mod chunk;
//! mod constructor;
//! To get started, you can use the `run` function to run a Dust program. mod dust_error;
//! mod identifier;
//! ```rust mod instruction;
//! use dust_lang::{run, Value}; mod lexer;
//! mod parser;
//! let program = " mod token;
//! let foo = 21 mod r#type;
//! let bar = 2 mod value;
//! 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;
pub use chunk::{Chunk, ChunkError, Local}; use std::fmt::Display;
pub use constructor::{ConstructError, Constructor};
pub use chunk::{Chunk, ChunkError};
pub use constructor::Constructor;
pub use dust_error::{AnnotatedError, DustError}; pub use dust_error::{AnnotatedError, DustError};
pub use identifier::Identifier; pub use identifier::Identifier;
pub use instruction::Instruction; pub use instruction::Instruction;
@ -36,18 +20,13 @@ pub use lexer::{lex, LexError, Lexer};
pub use parser::{parse, ParseError, Parser}; pub use parser::{parse, ParseError, Parser};
pub use r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict}; pub use r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
pub use token::{Token, TokenKind, TokenOwned}; pub use token::{Token, TokenKind, TokenOwned};
pub use value::{Struct, Value, ValueError}; pub use value::{Enum, Function, Struct, Value};
pub use vm::{run, Vm};
use std::fmt::{self, Display, Formatter}; #[derive(Clone, Copy, Debug, PartialEq)]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Span(pub usize, pub usize); pub struct Span(pub usize, pub usize);
impl Display for Span { 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) write!(f, "({}, {})", self.0, self.1)
} }
} }

View File

@ -1,3 +1,6 @@
#[cfg(test)]
mod tests;
use std::{ use std::{
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
mem, mem,
@ -24,12 +27,13 @@ pub fn parse(source: &str) -> Result<Chunk, DustError> {
#[derive(Debug)] #[derive(Debug)]
pub struct Parser<'src> { pub struct Parser<'src> {
lexer: Lexer<'src>,
chunk: Chunk, chunk: Chunk,
previous_token: Token<'src>, lexer: Lexer<'src>,
previous_position: Span, current_register: u8,
current_token: Token<'src>, current_token: Token<'src>,
current_position: Span, current_position: Span,
previous_token: Token<'src>,
previous_position: Span,
} }
impl<'src> Parser<'src> { impl<'src> Parser<'src> {
@ -42,10 +46,11 @@ impl<'src> Parser<'src> {
Parser { Parser {
lexer, lexer,
chunk: Chunk::new(), chunk: Chunk::new(),
previous_token: Token::Eof, current_register: 0,
previous_position: Span(0, 0),
current_token, current_token,
current_position, 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) 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> { fn advance(&mut self) -> Result<(), ParseError> {
if self.is_eof() { if self.is_eof() {
return Ok(()); return Ok(());
@ -90,16 +109,19 @@ impl<'src> Parser<'src> {
} }
} }
fn emit_byte<T: Into<u8>>(&mut self, into_byte: T, position: Span) { fn emit_instruction(&mut self, instruction: Instruction, position: Span) {
self.chunk.push_code(into_byte.into(), position); self.chunk.push_code(instruction, position);
} }
fn emit_constant(&mut self, value: Value) -> Result<(), ParseError> { fn emit_constant(&mut self, value: Value) -> Result<(), ParseError> {
let position = self.previous_position; let position = self.previous_position;
let constant_index = self.chunk.push_constant(value, position)?; let constant_index = self.chunk.push_constant(value, position)?;
self.emit_byte(Instruction::Constant, position); self.emit_instruction(
self.emit_byte(constant_index, position); Instruction::load_constant(self.current_register, constant_index),
position,
);
self.increment_register()?;
Ok(()) Ok(())
} }
@ -180,7 +202,9 @@ impl<'src> Parser<'src> {
fn parse_unary(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { fn parse_unary(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
let operator_position = self.previous_position; let operator_position = self.previous_position;
let byte = match self.previous_token.kind() { 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 { return Err(ParseError::ExpectedTokenMultiple {
expected: vec![TokenKind::Minus], expected: vec![TokenKind::Minus],
@ -191,7 +215,7 @@ impl<'src> Parser<'src> {
}; };
self.parse_expression()?; self.parse_expression()?;
self.emit_byte(byte, operator_position); self.emit_instruction(byte, operator_position);
Ok(()) Ok(())
} }
@ -205,12 +229,18 @@ impl<'src> Parser<'src> {
self.parse(rule.precedence.increment())?; 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 { let byte = match operator {
TokenKind::Plus => Instruction::Add, TokenKind::Plus => Instruction::add(to_register, left_register, right_register),
TokenKind::Minus => Instruction::Subtract, TokenKind::Minus => Instruction::subtract(to_register, left_register, right_register),
TokenKind::Star => Instruction::Multiply, TokenKind::Star => Instruction::multiply(to_register, left_register, right_register),
TokenKind::Slash => Instruction::Divide, TokenKind::Slash => Instruction::divide(to_register, left_register, right_register),
TokenKind::DoubleAmpersand => Instruction::And,
_ => { _ => {
return Err(ParseError::ExpectedTokenMultiple { return Err(ParseError::ExpectedTokenMultiple {
expected: vec![ expected: vec![
@ -225,7 +255,7 @@ impl<'src> Parser<'src> {
} }
}; };
self.emit_byte(byte, operator_position); self.emit_instruction(byte, operator_position);
Ok(()) Ok(())
} }
@ -240,11 +270,17 @@ impl<'src> Parser<'src> {
if allow_assignment && self.allow(TokenKind::Equal)? { if allow_assignment && self.allow(TokenKind::Equal)? {
self.parse_expression()?; 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 { } else {
self.emit_byte(Instruction::GetVariable, self.previous_position); self.emit_instruction(
self.emit_byte(identifier_index, self.previous_position); Instruction::get_variable(self.current_register - 1, identifier_index),
self.previous_position,
);
} }
Ok(()) Ok(())
@ -254,7 +290,7 @@ impl<'src> Parser<'src> {
&mut self, &mut self,
token: TokenOwned, token: TokenOwned,
position: Span, position: Span,
) -> Result<u8, ParseError> { ) -> Result<u16, ParseError> {
if let TokenOwned::Identifier(text) = token { if let TokenOwned::Identifier(text) = token {
let identifier = Identifier::new(text); let identifier = Identifier::new(text);
@ -291,8 +327,10 @@ impl<'src> Parser<'src> {
.next_back() .next_back()
.map_or(false, |local| local.depth > self.chunk.scope_depth()) .map_or(false, |local| local.depth > self.chunk.scope_depth())
{ {
self.emit_byte(Instruction::Pop, self.current_position); self.emit_instruction(
self.chunk.pop_identifier(); Instruction::close(self.current_register),
self.current_position,
);
} }
Ok(()) Ok(())
@ -326,7 +364,7 @@ impl<'src> Parser<'src> {
if is_expression_statement && !contains_block && !has_semicolon { if is_expression_statement && !contains_block && !has_semicolon {
let end = self.previous_position.1; let end = self.previous_position.1;
self.emit_byte(Instruction::Return, Span(start, end)) self.emit_instruction(Instruction::r#return(), Span(start, end))
} }
Ok(()) Ok(())
@ -353,8 +391,11 @@ impl<'src> Parser<'src> {
let identifier_index = self.chunk.declare_variable(identifier, position)?; let identifier_index = self.chunk.declare_variable(identifier, position)?;
self.emit_byte(Instruction::DeclareVariable, position); self.emit_instruction(
self.emit_byte(identifier_index, position); Instruction::set_variable(self.current_register, identifier_index),
position,
);
self.increment_register()?;
Ok(()) Ok(())
} }
@ -618,6 +659,9 @@ pub enum ParseError {
identifier: Identifier, identifier: Identifier,
position: Span, position: Span,
}, },
RegisterOverflow {
position: Span,
},
// Wrappers around foreign errors // Wrappers around foreign errors
Chunk(ChunkError), Chunk(ChunkError),
@ -646,6 +690,7 @@ impl AnnotatedError for ParseError {
Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens", Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens",
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target", Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
Self::UndefinedVariable { .. } => "Undefined variable", Self::UndefinedVariable { .. } => "Undefined variable",
Self::RegisterOverflow { .. } => "Register overflow",
Self::Chunk { .. } => "Chunk error", Self::Chunk { .. } => "Chunk error",
Self::Lex(_) => "Lex error", Self::Lex(_) => "Lex error",
Self::ParseIntError { .. } => "Failed to parse integer", Self::ParseIntError { .. } => "Failed to parse integer",
@ -654,9 +699,7 @@ impl AnnotatedError for ParseError {
fn details(&self) -> Option<String> { fn details(&self) -> Option<String> {
match self { match self {
Self::ExpectedExpression { found, .. } => { Self::ExpectedExpression { found, .. } => Some(format!("Found \"{found}\"")),
Some(format!("Expected an expression, found \"{found}\""))
}
Self::ExpectedToken { Self::ExpectedToken {
expected, found, .. expected, found, ..
} => Some(format!("Expected \"{expected}\", found \"{found}\"")), } => Some(format!("Expected \"{expected}\", found \"{found}\"")),
@ -664,11 +707,12 @@ impl AnnotatedError for ParseError {
expected, found, .. expected, found, ..
} => Some(format!("Expected one of {expected:?}, found \"{found}\"")), } => Some(format!("Expected one of {expected:?}, found \"{found}\"")),
Self::InvalidAssignmentTarget { found, .. } => { Self::InvalidAssignmentTarget { found, .. } => {
Some(format!("Invalid assignment target \"{found}\"")) Some(format!("Invalid assignment target, found \"{found}\""))
} }
Self::UndefinedVariable { identifier, .. } => { Self::UndefinedVariable { identifier, .. } => {
Some(format!("Undefined variable \"{identifier}\"")) Some(format!("Undefined variable \"{identifier}\""))
} }
Self::RegisterOverflow { .. } => None,
Self::Chunk(error) => error.details(), Self::Chunk(error) => error.details(),
Self::Lex(error) => Some(error.to_string()), Self::Lex(error) => Some(error.to_string()),
Self::ParseIntError { 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::ExpectedTokenMultiple { position, .. } => *position,
Self::InvalidAssignmentTarget { position, .. } => *position, Self::InvalidAssignmentTarget { position, .. } => *position,
Self::UndefinedVariable { position, .. } => *position, Self::UndefinedVariable { position, .. } => *position,
Self::RegisterOverflow { position } => *position,
Self::Chunk(error) => error.position(), Self::Chunk(error) => error.position(),
Self::Lex(error) => error.position(), Self::Lex(error) => error.position(),
Self::ParseIntError { position, .. } => *position, Self::ParseIntError { position, .. } => *position,
@ -694,283 +739,3 @@ impl From<LexError> for ParseError {
Self::Lex(error) 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![],
))
);
}
}

View 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![]
))
);
}

View File

@ -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))));
}
}

View File

@ -1,10 +1,8 @@
//! Token and TokenOwned types. //! Token and TokenOwned types.
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
/// Source code token. /// Source code token.
#[derive(Debug, Serialize, Deserialize, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Token<'src> { pub enum Token<'src> {
// End of file // End of file
Eof, Eof,
@ -293,7 +291,7 @@ impl<'src> Display for Token<'src> {
/// Owned version of `Token`, which owns all the strings. /// Owned version of `Token`, which owns all the strings.
/// ///
/// This is used for errors. /// This is used for errors.
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] #[derive(Debug, PartialEq, Clone)]
pub enum TokenOwned { pub enum TokenOwned {
Eof, Eof,
@ -412,7 +410,7 @@ impl Display for TokenOwned {
} }
/// Token representation that holds no data. /// Token representation that holds no data.
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] #[derive(Debug, PartialEq, Clone)]
pub enum TokenKind { pub enum TokenKind {
Eof, Eof,

View File

@ -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))));
}
}

View File

@ -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))));
}

View File

@ -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'))));
}

View File

@ -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))));
}

View File

@ -1,78 +1,78 @@
use std::{fs::read_to_string, io::Write}; // use std::{fs::read_to_string, io::Write};
use clap::Parser; // use clap::Parser;
use colored::Colorize; // use colored::Colorize;
use dust_lang::{parse, run}; // use dust_lang::{parse, run};
use log::Level; // use log::Level;
#[derive(Parser)] // #[derive(Parser)]
struct Cli { // struct Cli {
#[arg(short, long)] // #[arg(short, long)]
command: Option<String>, // command: Option<String>,
#[arg(short, long)] // #[arg(short, long)]
parse: bool, // parse: bool,
path: Option<String>, // path: Option<String>,
} // }
fn main() { fn main() {
env_logger::builder() // env_logger::builder()
.parse_env("DUST_LOG") // .parse_env("DUST_LOG")
.format(|buf, record| { // .format(|buf, record| {
let level = match record.level() { // let level = match record.level() {
Level::Error => "ERROR".red(), // Level::Error => "ERROR".red(),
Level::Warn => "WARN".yellow(), // Level::Warn => "WARN".yellow(),
Level::Info => "INFO".white(), // Level::Info => "INFO".white(),
Level::Debug => "DEBUG".blue(), // Level::Debug => "DEBUG".blue(),
Level::Trace => "TRACE".purple(), // Level::Trace => "TRACE".purple(),
} // }
.bold(); // .bold();
let level_display = format!("[{level:^5}]").white().on_black(); // let level_display = format!("[{level:^5}]").white().on_black();
let module = record // let module = record
.module_path() // .module_path()
.map(|path| path.split("::").last().unwrap_or("unknown")) // .map(|path| path.split("::").last().unwrap_or("unknown"))
.unwrap_or("unknown") // .unwrap_or("unknown")
.dimmed(); // .dimmed();
writeln!(buf, "{level_display} {module:^6} {}", record.args()) // writeln!(buf, "{level_display} {module:^6} {}", record.args())
}) // })
.init(); // .init();
let args = Cli::parse(); // let args = Cli::parse();
if let Some(command) = &args.command { // if let Some(command) = &args.command {
if args.parse { // if args.parse {
parse_and_display_errors(command); // parse_and_display_errors(command);
} else { // } else {
run_and_display_errors(command); // run_and_display_errors(command);
} // }
} else if let Some(path) = &args.path { // } else if let Some(path) = &args.path {
let source = read_to_string(path).expect("Failed to read file"); // let source = read_to_string(path).expect("Failed to read file");
if args.parse { // if args.parse {
parse_and_display_errors(&source); // parse_and_display_errors(&source);
} else { // } else {
run_and_display_errors(&source); // run_and_display_errors(&source);
} // }
} // }
} }
fn parse_and_display_errors(source: &str) { // fn parse_and_display_errors(source: &str) {
match parse(source) { // match parse(source) {
Ok(chunk) => println!("{}", chunk.disassemble("Dust CLI Input")), // Ok(chunk) => println!("{}", chunk.disassemble("Dust CLI Input")),
Err(error) => { // Err(error) => {
eprintln!("{:?}", error); // eprintln!("{:?}", error);
} // }
} // }
} // }
fn run_and_display_errors(source: &str) { // fn run_and_display_errors(source: &str) {
match run(source) { // match run(source) {
Ok(Some(value)) => println!("{}", value), // Ok(Some(value)) => println!("{}", value),
Ok(_) => {} // Ok(_) => {}
Err(error) => { // Err(error) => {
eprintln!("{}", error.report()); // eprintln!("{}", error.report());
} // }
} // }
} // }