updated a bunch of stuff

This commit is contained in:
Moritz Gmeiner 2024-09-01 19:16:30 +02:00
commit 67bb5fe8fd
24 changed files with 683 additions and 702 deletions

428
Cargo.lock generated
View file

@ -3,55 +3,83 @@
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.20"
name = "anstream"
version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [
"memchr",
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "autocfg"
version = "1.1.0"
name = "anstyle"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]]
name = "bitflags"
version = "1.3.2"
name = "anstyle-parse"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
dependencies = [
"utf8parse",
]
[[package]]
name = "cc"
version = "1.0.78"
name = "anstyle-query"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "clap"
version = "4.1.4"
version = "4.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
dependencies = [
"bitflags",
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"is-terminal",
"once_cell",
"strsim",
"termcolor",
]
[[package]]
name = "clap_derive"
version = "4.1.0"
version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
@ -59,147 +87,54 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.3.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
dependencies = [
"os_str_bytes",
]
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "colorchoice"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "either"
version = "1.8.1"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "errno"
version = "0.2.8"
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "heck"
version = "0.4.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.2.6"
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]]
name = "io-lifetimes"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "is-terminal"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys",
]
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.10.5"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "num-derive"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
[[package]]
name = "os_str_bytes"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "phf"
version = "0.11.1"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_macros",
"phf_shared",
@ -207,9 +142,9 @@ dependencies = [
[[package]]
name = "phf_generator"
version = "0.11.1"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
"phf_shared",
"rand",
@ -217,9 +152,9 @@ dependencies = [
[[package]]
name = "phf_macros"
version = "0.11.1"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
"phf_generator",
"phf_shared",
@ -230,51 +165,27 @@ dependencies = [
[[package]]
name = "phf_shared"
version = "0.11.1"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
dependencies = [
"siphasher",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.50"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
@ -294,23 +205,6 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "regex"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "rlox2"
version = "0.1.0"
@ -318,7 +212,6 @@ dependencies = [
"clap",
"rlox2-frontend",
"rlox2-interpreter",
"rlox2-vm",
]
[[package]]
@ -334,98 +227,56 @@ dependencies = [
name = "rlox2-interpreter"
version = "0.1.0"
dependencies = [
"glob",
"itertools",
"rlox2-frontend",
"rustc-hash",
"thiserror",
]
[[package]]
name = "rlox2-vm"
version = "0.1.0"
dependencies = [
"itertools",
"lazy_static",
"num-derive",
"num-traits",
"regex",
"rlox2-frontend",
"static_assertions",
"thiserror",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
[[package]]
name = "siphasher"
version = "0.3.10"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "strsim"
version = "0.10.0"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "1.0.107"
version = "2.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.38"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
@ -434,56 +285,35 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.6"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version_check"
version = "0.9.4"
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "windows-sys"
version = "0.42.0"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
@ -492,42 +322,48 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
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"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View file

@ -8,7 +8,7 @@ name = "rlox"
path = "src/main.rs"
[workspace]
members = ["frontend", "interpreter", "vm"]
members = ["frontend", "interpreter"] # , "vm"
[dependencies.rlox2-frontend]
path = "frontend"
@ -16,8 +16,8 @@ path = "frontend"
[dependencies.rlox2-interpreter]
path = "interpreter"
[dependencies.rlox2-vm]
path = "vm"
# [dependencies.rlox2-vm]
# path = "vm"
[dependencies]
clap = { version = "4.1.4", features = ["derive"] }
clap = { version = "4", features = ["derive"] }

View file

@ -6,6 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
phf = { version = "0.11.1", features = ["macros"] }
thiserror = "1.0.38"
itertools = "0.10.5"
phf = { version = "0.11", features = ["macros"] }
thiserror = "1"
itertools = "0.13"

View file

@ -129,6 +129,11 @@ impl Lexer {
// line comment
// advance until either source is empty or newline if found
while !self.source_is_empty() && self.advance() != '\n' {}
let comment: Box<str> =
self.source[self.start + 2..self.current].iter().collect();
self.push_token(TokenType::Comment(comment));
} else if self.consume('*') {
// block comment
@ -164,6 +169,12 @@ impl Lexer {
self.advance();
}
let comment: Box<str> = self.source[self.start + 2..self.current - 2]
.iter()
.collect();
self.push_token(TokenType::Comment(comment));
} else {
self.push_token(Slash)
}
@ -237,10 +248,13 @@ impl Lexer {
}
}
let string_literal = self.source[self.start + 1..self.current - 1].iter().collect();
let string_literal: Box<str> = self.source[self.start + 1..self.current - 1]
.iter()
.collect();
// Some(TokenType::String(Box::new(string_literal)))
self.tokens.push(Token::new_string(string_literal, self.code_pos));
self.tokens
.push(Token::new_string(string_literal, self.code_pos));
}
fn try_parse_number(&mut self) {
@ -290,8 +304,12 @@ impl Lexer {
}
fn try_parse_identifier(&mut self) {
let is_alpha_num_underscore =
|c: Option<char>| c.map_or(false, |c| matches!(c, '0'..='9' | 'A'..='Z' | '_' | 'a'..='z'));
let is_alpha_num_underscore = |c: Option<char>| {
c.map_or(
false,
|c| matches!(c, '0'..='9' | 'A'..='Z' | '_' | 'a'..='z'),
)
};
while is_alpha_num_underscore(self.peek()) {
self.advance();
@ -304,11 +322,12 @@ impl Lexer {
.cloned()
.unwrap_or(TokenType::Identifier(Box::new(lexeme))); */
if let Some(&token_type) = KEYWORDS.get(&lexeme) {
if let Some(token_type) = KEYWORDS.get(&lexeme) {
// Token::new(token_type, self.code_pos)
self.push_token(token_type);
self.push_token(token_type.clone());
} else {
self.tokens.push(Token::new_identifier(lexeme, self.code_pos));
self.tokens
.push(Token::new_identifier(lexeme, self.code_pos));
}
// Some(token_type)

View file

@ -4,13 +4,13 @@ use super::CodePos;
#[derive(Error, Debug)]
pub enum LexerError {
#[error("Unexpected character '{c}' at {code_pos}.")]
#[error("LexerError: Unexpected character '{c}' at {code_pos}.")]
UnexpectedCharacter { c: char, code_pos: CodePos },
#[error("Unterminated string literal starting at {code_pos}.")]
#[error("LexerError: Unterminated string literal starting at {code_pos}.")]
UnterminatedStringLiteral { code_pos: CodePos },
#[error("Unterminated block comment starting at {code_pos}.")]
#[error("LexerError: Unterminated block comment starting at {code_pos}.")]
UnterminatedBlockComment { code_pos: CodePos },
#[error("Invalid number literal {lexeme} at {code_pos}: {msg}")]
#[error("LexerError: Invalid number literal {lexeme} at {code_pos}: {msg}")]
InvalidNumberLiteral {
lexeme: String,
msg: String,

View file

@ -1,10 +1,9 @@
use std::fmt::{Debug, Display};
use std::mem::ManuallyDrop;
use super::CodePos;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
#[rustfmt::skip]
pub enum TokenType {
// Single-character tokens
@ -18,42 +17,20 @@ pub enum TokenType {
Less, LessEqual,
// Identifier and literals
Identifier, String, Number,
Identifier(Box<str>), String(Box<str>), Number(f64),
// Keywords
And, Break, Class, Else, False, Fun, For, If, Nil, Or,
Print, Return, Super, This, True, Var, While,
#[allow(dead_code, clippy::upper_case_acronyms)]
Comment(Box<str>),
#[allow(clippy::upper_case_acronyms)]
EOF
}
union TokenData {
none: (),
#[allow(clippy::box_collection)]
s: ManuallyDrop<Box<String>>,
num: f64,
}
impl TokenData {
fn none() -> Self {
TokenData { none: () }
}
fn string(s: String) -> Self {
let s = ManuallyDrop::new(Box::new(s));
TokenData { s }
}
fn num(num: f64) -> Self {
TokenData { num }
}
}
pub struct Token {
pub token_type: TokenType,
// pub lexeme: String,
data: TokenData,
pub code_pos: CodePos,
}
@ -61,95 +38,64 @@ impl Token {
pub fn new(token_type: TokenType, code_pos: CodePos) -> Self {
Token {
token_type,
// lexeme,
data: TokenData::none(),
code_pos,
}
}
pub fn new_string(s: String, code_pos: CodePos) -> Self {
pub fn new_string(s: impl Into<Box<str>>, code_pos: CodePos) -> Self {
Token {
token_type: TokenType::String,
data: TokenData::string(s),
token_type: TokenType::String(s.into()),
code_pos,
}
}
pub fn new_identifier(name: String, code_pos: CodePos) -> Self {
pub fn new_identifier(name: impl Into<Box<str>>, code_pos: CodePos) -> Self {
Token {
token_type: TokenType::Identifier,
data: TokenData::string(name),
token_type: TokenType::Identifier(name.into()),
code_pos,
}
}
pub fn new_number(num: f64, code_pos: CodePos) -> Self {
Token {
token_type: TokenType::Number,
data: TokenData::num(num),
token_type: TokenType::Number(num),
code_pos,
}
}
pub fn string_data(self) -> String {
assert!(self.token_type == TokenType::String || self.token_type == TokenType::Identifier);
pub fn take(&mut self) -> Self {
let code_pos = self.code_pos;
// std::mem::take(&mut self.data.s)
unsafe {
let mut me = self;
use TokenType::*;
let s = std::mem::take(&mut me.data.s);
let token_type = match &mut self.token_type {
String(s) => String(std::mem::take(s)),
Identifier(name) => Identifier(std::mem::take(name)),
other => other.clone(),
};
*ManuallyDrop::into_inner(s)
Token {
token_type,
code_pos,
}
}
pub fn num_data(self) -> f64 {
assert_eq!(self.token_type, TokenType::Number);
unsafe { self.data.num }
}
}
impl Debug for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// write!(f, "<{:?}>", self.token_type)
match self.token_type {
TokenType::Number => unsafe { write!(f, "<{:?}({})>", self.token_type, self.data.num) },
TokenType::String => unsafe { write!(f, "<{:?}({})>", self.token_type, self.data.s.as_ref()) },
TokenType::Identifier => unsafe { write!(f, "<{:?}({})>", self.token_type, self.data.s.as_ref()) },
_ => write!(f, "<{:?}>", self.token_type),
}
write!(f, "<{:?} {:?}> ", self.token_type, self.code_pos)
}
}
impl Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// write!(f, "<{:?}>", self.token_type)
match self.token_type {
TokenType::Number => unsafe { write!(f, "<{:?}({})>", self.token_type, self.data.num) },
TokenType::String => unsafe { write!(f, "<{:?}({})>", self.token_type, self.data.s.as_ref()) },
TokenType::Identifier => unsafe { write!(f, "<{:?}({})>", self.token_type, self.data.s.as_ref()) },
_ => write!(f, "<{:?}>", self.token_type),
use TokenType::*;
match &self.token_type {
String(s) => write!(f, "<String {}>", s),
Identifier(name) => write!(f, "<Identifier {}>", name),
Number(x) => write!(f, "<Number {}>", x),
tt => write!(f, "<{:?}>", tt),
}
}
}
/* impl Clone for Token {
fn clone(&self) -> Self {
let code_pos = self.code_pos;
match self.token_type {
TokenType::Number => Token::new_number(self.num_data(), code_pos),
TokenType::String => unsafe { Token::new_string(self.data.s.as_ref().clone(), code_pos) },
TokenType::Identifier => unsafe { Token::new_identifier(self.data.s.as_ref().clone(), code_pos) },
token_type => Token::new(token_type, code_pos),
}
}
} */
impl Drop for Token {
fn drop(&mut self) {
if self.token_type == TokenType::String {}
}
}

View file

@ -1,5 +1,3 @@
use std::vec::IntoIter;
use crate::lexer::{Token, TokenType};
use crate::parser::expr::BinaryOp;
@ -18,31 +16,39 @@ pub fn parse_tokens(tokens: Vec<Token>) -> Result<Vec<Stmt>, Vec<ParserError>> {
// takes care of token iteration
struct TokenIter {
token_iter: IntoIter<Token>,
tokens: Vec<Token>,
peek_token: Option<Token>,
pos: usize,
}
impl TokenIter {
pub fn new(tokens: Vec<Token>) -> Self {
TokenIter {
token_iter: tokens.into_iter(),
peek_token: None,
TokenIter { tokens, pos: 0 }
}
fn skip_comments(&mut self) {
while !self.is_empty() && matches![self.tokens[self.pos].token_type, TokenType::Comment(_)]
{
self.pos += 1; // skip comment token
}
}
fn peek(&mut self) -> Option<&Token> {
// if peek_token is empty: fill with next token from token_iter
if self.peek_token.is_none() && self.token_iter.len() != 0 {
self.peek_token = self.token_iter.next();
if self.is_empty() {
return None;
}
self.peek_token.as_ref()
self.skip_comments();
let token = &self.tokens[self.pos];
assert!(!matches!(token.token_type, TokenType::Comment(_)));
Some(token)
}
fn is_empty(&self) -> bool {
// peek_token is None and there are no more tokens to take from token_iter
self.peek_token.is_none() && self.token_iter.len() == 0
self.pos == self.tokens.len()
}
}
@ -50,8 +56,16 @@ impl Iterator for TokenIter {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
// return the peeked token if any, or else next token from token_iter
self.peek_token.take().or_else(|| self.token_iter.next())
self.skip_comments();
self.pos += 1;
assert!(!matches!(
self.tokens[self.pos - 1].token_type,
TokenType::Comment(_)
));
Some(self.tokens[self.pos - 1].take())
}
}
@ -101,7 +115,11 @@ impl Parser {
}
}
assert_eq!(me.next_token().token_type, TokenType::EOF);
assert_eq!(
me.next_token().token_type,
TokenType::EOF,
"last token wasn't EOF"
);
if !me.parse_errors.is_empty() {
Err(me.parse_errors)
@ -122,10 +140,10 @@ impl Parser {
self.is_in_function = false;
self.is_in_init = false;
let peek_token = self.peek_token();
let tt = &self.peek_token().token_type;
// if we match a synchronisation point: return
match peek_token.token_type {
match tt {
TokenType::Class
| TokenType::Fun
| TokenType::Var
@ -177,22 +195,12 @@ impl Parser {
let code_pos = self.peek_token().code_pos;
assert_eq!(self.next_token().token_type, TokenType::Return);
let expr = match self.peek_token().token_type {
TokenType::Semicolon => {
if !self.is_in_init {
Expr::nil()
} else {
Expr::Variable {
name: "this".to_owned(),
}
}
}
_ => {
if !self.is_in_init {
self.expression()?
} else {
return Err(ParserError::ReturnInInit { code_pos });
}
let expr = match (self.is_in_init, &self.peek_token().token_type) {
(false, TokenType::Semicolon) => Expr::nil(),
(true, TokenType::Semicolon) => Expr::variable("this"),
(false, _) => self.expression()?,
(true, _) => {
return Err(ParserError::ReturnInInit { code_pos });
}
};
@ -208,14 +216,18 @@ impl Parser {
fn if_statement(&mut self) -> ParserResult<Stmt> {
assert_eq!(self.next_token().token_type, TokenType::If);
self.consume_token(TokenType::LeftParen, |token| ParserError::MissingParenAfterIf {
code_pos: token.code_pos,
self.consume_token(TokenType::LeftParen, |token| {
ParserError::MissingParenAfterIf {
code_pos: token.code_pos,
}
})?;
let condition = self.expression()?;
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
code_pos: token.code_pos,
self.consume_token(TokenType::RightParen, |token| {
ParserError::MissingRightParen {
code_pos: token.code_pos,
}
})?;
let then_branch = self.statement()?;
@ -235,14 +247,18 @@ impl Parser {
fn while_statement(&mut self) -> ParserResult<Stmt> {
assert_eq!(self.next_token().token_type, TokenType::While);
self.consume_token(TokenType::LeftParen, |token| ParserError::MissingParenAfterWhile {
code_pos: token.code_pos,
self.consume_token(TokenType::LeftParen, |token| {
ParserError::MissingParenAfterWhile {
code_pos: token.code_pos,
}
})?;
let condition = self.expression()?;
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
code_pos: token.code_pos,
self.consume_token(TokenType::RightParen, |token| {
ParserError::MissingRightParen {
code_pos: token.code_pos,
}
})?;
let is_in_loop = std::mem::replace(&mut self.is_in_loop, true);
@ -257,8 +273,10 @@ impl Parser {
fn for_statement(&mut self) -> ParserResult<Stmt> {
assert_eq!(self.next_token().token_type, TokenType::For);
self.consume_token(TokenType::LeftParen, |token| ParserError::MissingParenAfterFor {
code_pos: token.code_pos,
self.consume_token(TokenType::LeftParen, |token| {
ParserError::MissingParenAfterFor {
code_pos: token.code_pos,
}
})?;
let initializer = match self.peek_token().token_type {
@ -282,8 +300,10 @@ impl Parser {
_ => Some(self.expression()?),
};
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
code_pos: token.code_pos,
self.consume_token(TokenType::RightParen, |token| {
ParserError::MissingRightParen {
code_pos: token.code_pos,
}
})?;
let is_in_loop = std::mem::replace(&mut self.is_in_loop, true);
@ -326,7 +346,7 @@ impl Parser {
let token = self.next_token();
let name = match token.token_type {
TokenType::Identifier => token.string_data(),
TokenType::Identifier(s) => s,
_ => return Err(ParserError::ExpectedVarName { token }),
};
@ -354,13 +374,17 @@ impl Parser {
let superclass_name = self.identifier("Expected superclass")?;
Some(Expr::Variable { name: superclass_name })
Some(Expr::Variable {
name: superclass_name,
})
} else {
None
};
self.consume_token(TokenType::LeftBrace, |token| ParserError::MissingClassBody {
code_pos: token.code_pos,
self.consume_token(TokenType::LeftBrace, |token| {
ParserError::MissingClassBody {
code_pos: token.code_pos,
}
})?;
let is_in_loop = std::mem::replace(&mut self.is_in_loop, false);
@ -381,7 +405,7 @@ impl Parser {
})?;
let is_in_init = self.is_in_init;
if method_name == "init" {
if &*method_name == "init" {
self.is_in_init = true;
}
@ -398,7 +422,7 @@ impl Parser {
self.is_in_class = is_in_class;
self.is_in_function = is_in_function;
let class = Expr::class(&name, methods, superclass);
let class = Expr::class(name.clone(), methods, superclass);
Ok(Stmt::var_decl(name, class))
}
@ -451,7 +475,7 @@ impl Parser {
Ok(Expr::function(name, param_names, body))
}
fn collect_params(&mut self) -> ParserResult<Vec<String>> {
fn collect_params(&mut self) -> ParserResult<Vec<Box<str>>> {
assert_eq!(self.next_token().token_type, TokenType::LeftParen);
if self.peek_token().token_type == TokenType::RightParen {
@ -470,8 +494,10 @@ impl Parser {
param_names.push(self.identifier("Expected parameter name")?);
}
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
code_pos: token.code_pos,
self.consume_token(TokenType::RightParen, |token| {
ParserError::MissingRightParen {
code_pos: token.code_pos,
}
})?;
Ok(param_names)
@ -532,7 +558,11 @@ impl Parser {
Expr::Variable { name } => Ok(Expr::assignment(Expr::Variable { name }, value)),
Expr::Get { target, name } => {
let value = Box::new(value);
Ok(Expr::Set { target, name, value })
Ok(Expr::Set {
target,
name,
value,
})
}
_ => Err(ParserError::InvalidAssignment { expr, code_pos }),
}
@ -720,8 +750,10 @@ impl Parser {
args.push(self.expression()?);
}
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
code_pos: token.code_pos,
self.consume_token(TokenType::RightParen, |token| {
ParserError::MissingRightParen {
code_pos: token.code_pos,
}
})?;
Ok(args)
@ -736,52 +768,53 @@ impl Parser {
match token.token_type {
TokenType::Fun => Ok(self.fun_params_and_body("<lambda>")?),
TokenType::Number => Ok(Expr::number(token.num_data())),
TokenType::String => Ok(Expr::string(token.string_data())),
TokenType::Number(x) => Ok(Expr::number(x)),
TokenType::String(s) => Ok(Expr::string(s)),
TokenType::False => Ok(Expr::bool(false)),
TokenType::True => Ok(Expr::bool(true)),
TokenType::Nil => Ok(Expr::nil()),
TokenType::This => Ok(Expr::This),
TokenType::Super => {
self.consume_token(TokenType::Dot, |token| ParserError::MissingMethodAfterSuper {
code_pos: token.code_pos,
self.consume_token(TokenType::Dot, |token| {
ParserError::MissingMethodAfterSuper {
code_pos: token.code_pos,
}
})?;
let method = self.identifier("Expected method name after super")?;
let super_var = Box::new(Expr::Variable {
name: "super".to_owned(),
});
let this_var = Box::new(Expr::This);
Ok(Expr::Super {
super_var,
this_var,
method,
})
let super_var = Expr::variable("super");
let this_var = Expr::This;
Ok(Expr::super_(super_var, this_var, method))
}
TokenType::LeftParen => {
let expr = self.expression()?;
self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen {
code_pos: token.code_pos,
self.consume_token(TokenType::RightParen, |token| {
ParserError::MissingRightParen {
code_pos: token.code_pos,
}
})?;
Ok(Expr::grouping(expr))
}
TokenType::Identifier => Ok(Expr::Variable {
name: token.string_data(),
}),
TokenType::Identifier(name) => Ok(Expr::Variable { name }),
_ => Err(ParserError::ExpectedPrimary { token }),
}
}
fn semicolon(&mut self) -> ParserResult<()> {
self.consume_token(TokenType::Semicolon, |token| ParserError::MissingSemicolon {
code_pos: token.code_pos,
self.consume_token(TokenType::Semicolon, |token| {
ParserError::MissingSemicolon {
code_pos: token.code_pos,
}
})
}
fn identifier(&mut self, msg: &str) -> ParserResult<String> {
fn identifier(&mut self, msg: &str) -> ParserResult<Box<str>> {
match self.peek_token().token_type {
TokenType::Identifier => Ok(self.next_token().string_data()),
TokenType::Identifier(_) => match self.next_token().token_type {
TokenType::Identifier(s) => Ok(s),
_ => unreachable!(),
},
_ => Err(ParserError::MissingIdentifier {
msg: msg.to_owned(),
code_pos: self.peek_token().code_pos,
@ -813,7 +846,7 @@ impl Parser {
fn consume_token<F>(&mut self, token_type: TokenType, err_fn: F) -> ParserResult<()>
where
F: Fn(Token) -> ParserError,
F: Fn(&Token) -> ParserError,
{
/* let token = self.next_token();
@ -829,8 +862,8 @@ impl Parser {
Ok(())
}
// call err_fn with dummy token so we don't have to eat the EOF token
TokenType::EOF => Err(err_fn(Token::new(TokenType::EOF, self.peek_token().code_pos))),
_ => Err(err_fn(self.next_token())),
TokenType::EOF => Err(err_fn(self.peek_token())),
_ => Err(err_fn(&self.next_token())),
}
}
}

View file

@ -6,44 +6,44 @@ use super::Expr;
#[derive(Error, Debug)]
pub enum ParserError {
#[error("Token stream ended unexpectedly.")]
#[error("ParserError: Token stream ended unexpectedly.")]
TokenStreamEnded,
#[error("Expected a primary expression, but found a {token} token instead at {0}.", token.code_pos)]
#[error("ParserError: Expected a primary expression, but found a {token} token instead at {0}.", token.code_pos)]
ExpectedPrimary { token: Token },
#[error("Missing semicolon at {code_pos}")]
#[error("ParserError: Missing semicolon at {code_pos}")]
MissingSemicolon { code_pos: CodePos },
#[error("Expected variable name at {0}, got {token} instead", token.code_pos)]
#[error("ParserError: Expected variable name at {0}, got {token} instead", token.code_pos)]
ExpectedVarName { token: Token },
#[error("Can't assign to {expr} at {code_pos}")]
#[error("ParserError: Can't assign to {expr} at {code_pos}")]
InvalidAssignment { expr: Expr, code_pos: CodePos },
#[error("Missing closing curly brace at {code_pos}")]
#[error("ParserError: Missing closing curly brace at {code_pos}")]
MissingRightBrace { code_pos: CodePos },
#[error("Missing closing parenthesis at {code_pos}")]
#[error("ParserError: Missing closing parenthesis at {code_pos}")]
MissingRightParen { code_pos: CodePos },
#[error("Missing parenthesis after if at {code_pos}")]
#[error("ParserError: Missing parenthesis after if at {code_pos}")]
MissingParenAfterIf { code_pos: CodePos },
#[error("Missing parenthesis after while at {code_pos}")]
#[error("ParserError: Missing parenthesis after while at {code_pos}")]
MissingParenAfterWhile { code_pos: CodePos },
#[error("Missing parenthesis after for at {code_pos}")]
#[error("ParserError: Missing parenthesis after for at {code_pos}")]
MissingParenAfterFor { code_pos: CodePos },
#[error("Call at {code_pos} has too many arguments")]
#[error("ParserError: Call at {code_pos} has too many arguments")]
TooManyArguments { code_pos: CodePos },
#[error("{msg} at {code_pos}")]
#[error("ParserError: {msg} at {code_pos}")]
MissingIdentifier { msg: String, code_pos: CodePos },
#[error("Missing arguments to function declaration at {code_pos}")]
#[error("ParserError: Missing arguments to function declaration at {code_pos}")]
MissingFunctionArgs { code_pos: CodePos },
#[error("Missing body to function declaration at {code_pos}")]
#[error("ParserError: Missing body to function declaration at {code_pos}")]
MissingFunctionBody { code_pos: CodePos },
#[error("Function declaration at {code_pos} has too many parameters")]
#[error("ParserError: Function declaration at {code_pos} has too many parameters")]
TooManyParams { code_pos: CodePos },
#[error("Return statement outside of function definition")]
#[error("ParserError: Return statement outside of function definition")]
ReturnOutsideFunction { code_pos: CodePos },
#[error("Break statement outside of loop")]
#[error("ParserError: Break statement outside of loop")]
InvalidBreak { code_pos: CodePos },
#[error("Missing class body at {code_pos}")]
#[error("ParserError: Missing class body at {code_pos}")]
MissingClassBody { code_pos: CodePos },
#[error("Return statement in init")]
#[error("ParserError: Return statement in init")]
ReturnInInit { code_pos: CodePos },
#[error("Missing method name after super")]
#[error("ParserError: Missing method name after super")]
MissingMethodAfterSuper { code_pos: CodePos },
}

View file

@ -1,5 +1,4 @@
use std::fmt::Display;
use std::rc::Rc;
use itertools::Itertools;
@ -28,14 +27,14 @@ pub enum Expr {
expr: Box<Expr>,
},
Variable {
name: String,
name: Box<str>,
},
LocalVariable {
name: String,
name: Box<str>,
level: usize,
},
GlobalVariable {
name: String,
name: Box<str>,
},
Assignment {
target: Box<Expr>,
@ -47,37 +46,37 @@ pub enum Expr {
},
Get {
target: Box<Expr>,
name: String,
name: Box<str>,
},
Set {
target: Box<Expr>,
name: String,
name: Box<str>,
value: Box<Expr>,
},
Function {
name: String,
param_names: Vec<String>,
closure_vars: Vec<(String, usize)>,
name: Box<str>,
param_names: Vec<Box<str>>,
closure_vars: Vec<(Box<str>, usize)>,
body: Box<Stmt>,
},
Class {
name: Box<str>,
superclass: Option<Box<Expr>>,
name: String,
methods: Box<Vec<Expr>>,
},
This,
Super {
super_var: Box<Expr>,
this_var: Box<Expr>,
method: String,
method: Box<str>,
},
}
impl Expr {
pub fn string(s: impl Into<String>) -> Self {
pub fn string(s: impl Into<Box<str>>) -> Self {
let s = s.into();
Expr::Literal {
literal: Literal::String(Rc::new(s)),
literal: Literal::String(s),
}
}
@ -94,7 +93,9 @@ impl Expr {
}
pub fn nil() -> Self {
Expr::Literal { literal: Literal::Nil }
Expr::Literal {
literal: Literal::Nil,
}
}
pub fn unary(op: UnaryOp, expr: Expr) -> Self {
@ -119,6 +120,21 @@ impl Expr {
Expr::Grouping { expr }
}
pub fn variable(name: impl Into<Box<str>>) -> Self {
let name = name.into();
Expr::Variable { name }
}
pub fn local_variable(name: impl Into<Box<str>>, level: usize) -> Self {
let name = name.into();
Expr::LocalVariable { name, level }
}
pub fn global_variable(name: impl Into<Box<str>>) -> Self {
let name = name.into();
Expr::GlobalVariable { name }
}
pub fn assignment(target: Expr, value: Expr) -> Self {
let target = Box::new(target);
let value = Box::new(value);
@ -130,15 +146,14 @@ impl Expr {
Expr::Call { callee, args }
}
pub fn get(target: Expr, name: impl Into<String>) -> Self {
pub fn get(target: Expr, name: impl Into<Box<str>>) -> Self {
let target = Box::new(target);
let name = name.into();
Expr::Get { target, name }
}
pub fn function(name: String, param_names: Vec<String>, body: Stmt) -> Self {
// let name = Box::new(name);
// let param_names = Box::new(param_names);
pub fn function(name: impl Into<Box<str>>, param_names: Vec<Box<str>>, body: Stmt) -> Self {
let name = name.into();
#[allow(clippy::box_default)]
// let closure_vars = Box::new(Vec::new());
let closure_vars = Vec::new();
@ -151,16 +166,33 @@ impl Expr {
}
}
pub fn class(name: impl Into<String>, methods: Vec<Expr>, superclass: Option<Expr>) -> Self {
pub fn class(name: impl Into<Box<str>>, methods: Vec<Expr>, superclass: Option<Expr>) -> Self {
let superclass = superclass.map(Box::new);
let name = name.into();
let methods = Box::new(methods);
Expr::Class {
superclass,
name,
superclass,
methods,
}
}
pub fn super_(
super_var: impl Into<Box<Expr>>,
this_var: impl Into<Box<Expr>>,
method: impl Into<Box<str>>,
) -> Self {
let super_var = super_var.into();
let this_var = this_var.into();
let method = method.into();
Expr::Super {
super_var,
this_var,
method,
}
}
}
impl Display for Expr {
@ -174,12 +206,20 @@ impl Display for Expr {
}
Expr::Grouping { expr } => write!(f, "(group {expr})"),
Expr::Variable { name } => write!(f, "(var {name})"),
Expr::LocalVariable { name, level } => write!(f, "(var {name} local({level}))"),
Expr::GlobalVariable { name } => write!(f, "(var {name} global)"),
Expr::Assignment { target, value } => write!(f, "{target} = {value}"),
Expr::Call { callee, args } => write!(f, "({callee} {})", args.iter().map(|arg| arg.to_string()).join(" ")),
Expr::LocalVariable { name, level } => write!(f, "(local {name} {level})"),
Expr::GlobalVariable { name } => write!(f, "(global {name})"),
Expr::Assignment { target, value } => write!(f, "(set {target} {value})"),
Expr::Call { callee, args } => write!(
f,
"({callee} {})",
args.iter().map(|arg| arg.to_string()).join(" ")
),
Expr::Get { target, name } => write!(f, "(get {name} {target})"),
Expr::Set { target, name, value } => write!(f, "(set {name} {target} {value})"),
Expr::Set {
target,
name,
value,
} => write!(f, "(set {name} {target} {value})"),
Expr::Function {
name,
param_names,
@ -189,9 +229,13 @@ impl Display for Expr {
if !closure_vars.is_empty() {
let closure_fmt = closure_vars.iter().map(|(name, _level)| name).join(", ");
write!(f, "fun [{closure_fmt}] {name}({}) => {body}", param_names.join(", "))
write!(
f,
"(fun [{closure_fmt}] {name} ({}) {body})",
param_names.join(", ")
)
} else {
write!(f, "fun {name}({}) => {body}", param_names.join(", "))
write!(f, "(fun {name} ({}) {body})", param_names.join(", "))
}
}
Expr::Class {
@ -200,14 +244,14 @@ impl Display for Expr {
methods,
} => {
if let Some(superclass) = superclass {
writeln!(f, "class {name} < {superclass} {{")?;
writeln!(f, "(class {name} < {superclass} ")?;
} else {
writeln!(f, "class {name} {{")?;
writeln!(f, "(class {name} ")?;
}
for method in methods.iter() {
writeln!(f, "{method}")?;
}
write!(f, "}}")
write!(f, ")")
}
Expr::This => write!(f, "this"),
Expr::Super {
@ -223,7 +267,7 @@ impl Display for Expr {
#[derive(Debug, Clone)]
pub enum Literal {
String(Rc<String>),
String(Box<str>),
Number(f64),
Bool(bool),
Nil,

View file

@ -1,5 +1,5 @@
use itertools::Itertools;
pub fn indent(s: String) -> String {
s.split('\n').map(|line| format!("\t{line}")).join("\n")
s.split('\n').map(|line| format!(" {line}")).join("\n")
}

View file

@ -18,7 +18,7 @@ pub enum Stmt {
body: Box<Stmt>,
},
VarDecl {
name: String,
name: Box<str>,
initializer: Box<Expr>,
},
Block {
@ -62,19 +62,23 @@ impl Stmt {
Stmt::While { condition, body }
}
pub fn var_decl(name: impl Into<String>, initializer: impl Into<Box<Expr>>) -> Self {
pub fn var_decl(name: impl Into<Box<str>>, initializer: impl Into<Box<Expr>>) -> Self {
let name = name.into();
let initializer = initializer.into();
Stmt::VarDecl { name, initializer }
}
pub fn expr_stmt(expr: impl Into<Box<Expr>>) -> Self {
let expr = expr.into();
Stmt::ExprStmt { expr }
}
pub fn return_stmt(expr: impl Into<Box<Expr>>) -> Self {
let expr = expr.into();
Stmt::Return { expr }
}
}
@ -82,7 +86,7 @@ impl Stmt {
impl Display for Stmt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Stmt::Print { expr } => write!(f, "(print {expr});"),
Stmt::Print { expr } => write!(f, "(print {expr})"),
Stmt::IfStmt {
condition,
then_branch,
@ -110,18 +114,18 @@ impl Display for Stmt {
}
Ok(())
}
Stmt::VarDecl { name, initializer } => write!(f, "var {name} = {initializer};"),
Stmt::VarDecl { name, initializer } => write!(f, "(decl {name} {initializer})"),
Stmt::Block { statements } => {
writeln!(f, "{{")?;
writeln!(f, "(block")?;
for statement in statements {
writeln!(f, "{}", indent(statement.to_string()))?;
}
write!(f, "}}")
write!(f, ")")
}
Stmt::ExprStmt { expr } => write!(f, "{expr};"),
Stmt::Break => write!(f, "break;"),
Stmt::Break => write!(f, "(break)"),
Stmt::Return { expr } => {
write!(f, "return {expr};")
write!(f, "(return {expr})")
}
}
}

View file

@ -10,7 +10,7 @@ use crate::{LoxFunction, LoxReference, Value};
pub struct LoxClass {
superclass: Option<Rc<LoxClass>>,
name: String,
name: Box<str>,
methods: FxHashMap<String, Value>,
}
@ -18,7 +18,7 @@ pub struct LoxClass {
/// Representation of a class in Lox. Always behind an Rc to ensure uniqueness. Should never be handled raw.
impl LoxClass {
pub fn new(
name: impl Into<String>,
name: impl Into<Box<str>>,
methods: FxHashMap<String, Value>,
superclass: Option<Rc<LoxClass>>,
) -> Rc<Self> {
@ -33,10 +33,7 @@ impl LoxClass {
let mut body = init.body().clone();
if let Stmt::Block { ref mut statements } = body {
statements.push(Stmt::return_stmt(Expr::LocalVariable {
name: "this".to_owned(),
level: 1,
}));
statements.push(Stmt::return_stmt(Expr::local_variable("this", 1)));
} else {
panic!("Body of init method of class {name} wasn't a block");
}
@ -130,7 +127,12 @@ impl LoxClass {
impl Display for LoxClass {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<class {} at {:#X}>", self.name, (self as *const LoxClass) as usize)
write!(
f,
"<class {} at {:#X}>",
self.name,
(self as *const LoxClass) as usize
)
}
}

View file

@ -8,12 +8,11 @@ use crate::error::RuntimeError;
use super::value::HeapedValue;
use super::{Runtime, Value};
pub type Scope = FxHashMap<String, HeapedValue>;
pub type Scope = FxHashMap<Box<str>, HeapedValue>;
#[derive(Debug)]
pub struct Environment<'a> {
scope_stack: Vec<Scope>,
scope_top: usize,
runtime: &'a mut Runtime,
@ -24,7 +23,6 @@ impl<'a> Environment<'a> {
pub fn new(runtime: &'a mut Runtime) -> Self {
Environment {
scope_stack: Vec::new(),
scope_top: 0,
runtime,
@ -32,6 +30,10 @@ impl<'a> Environment<'a> {
}
}
pub fn runtime(&mut self) -> &mut Runtime {
self.runtime
}
pub fn push_arg(&mut self, arg: Value) {
self.arg_stack.push(arg);
}
@ -44,16 +46,16 @@ impl<'a> Environment<'a> {
std::mem::take(&mut self.arg_stack)
}
pub fn globals(&self) -> &FxHashMap<String, Value> {
pub fn globals(&self) -> &FxHashMap<Box<str>, Value> {
self.runtime.globals()
}
pub fn scopes(&self) -> &[Scope] {
&self.scope_stack[..self.scope_top]
&self.scope_stack
}
pub fn scopes_mut(&mut self) -> &mut [Scope] {
&mut self.scope_stack[..self.scope_top]
&mut self.scope_stack
}
pub fn has_scope(&self) -> bool {
@ -61,30 +63,24 @@ impl<'a> Environment<'a> {
}
pub fn current_scope(&self) -> &Scope {
&self.scope_stack[self.scope_top - 1]
self.scope_stack.last().unwrap()
}
pub fn current_scope_mut(&mut self) -> &mut Scope {
&mut self.scope_stack[self.scope_top - 1]
self.scope_stack.last_mut().unwrap()
}
pub fn enter_scope(&mut self) {
// self.current_frame_mut().enter_scope();
// self.local_scopes.push(Scope::new());
if self.scope_top == self.scope_stack.len() {
self.scope_stack.push(Scope::default());
} else {
self.scope_stack[self.scope_top].clear();
}
self.scope_top += 1;
self.scope_stack.push(Scope::default());
}
pub fn exit_scope(&mut self) {
// self.current_frame_mut().exit_scope();
// self.local_scopes.pop().expect("Tried to pop global scope");
self.scope_top -= 1;
self.scope_stack.pop();
}
pub fn insert_closure(&mut self, closure: Scope) {
@ -108,14 +104,17 @@ impl<'a> Environment<'a> {
}
}
Err(RuntimeError::NameNotDefined { name: name.to_owned() })
Err(RuntimeError::NameNotDefined {
name: name.to_owned(),
})
// self.runtime.get_global(name)
}
pub fn define(&mut self, name: impl Into<String>, value: Value) {
pub fn define(&mut self, name: impl Into<Box<str>>, value: Value) {
if self.has_scope() {
let name = name.into();
self.current_scope_mut().insert(name, HeapedValue::new(value));
self.current_scope_mut()
.insert(name, HeapedValue::new(value));
} else {
self.runtime.define_global(name, value)
}
@ -133,7 +132,9 @@ impl<'a> Environment<'a> {
}
}
Err(RuntimeError::NameNotDefined { name: name.to_owned() })
Err(RuntimeError::NameNotDefined {
name: name.to_owned(),
})
// self.runtime.assign_global(name, value)
}
@ -145,12 +146,12 @@ impl<'a> Environment<'a> {
self.frames.pop().expect("Tried to pop global frame");
} */
pub fn collect_closure(&self, closure_vars: &[(String, usize)]) -> Scope {
pub fn collect_closure(&self, closure_vars: &[(Box<str>, usize)]) -> Scope {
let mut closure_scope = Scope::default();
for (name, level) in closure_vars {
// special injected variables
if name == "this" || name == "super" {
if &**name == "this" || &**name == "super" {
continue;
}
@ -181,7 +182,7 @@ impl Display for Environment<'_> {
write!(f, "\nScope {level}:")?;
for (name, value) in scope.iter() {
write!(f, "\n\t{name} = {value}")?;
write!(f, "\n\t{name} = {}", value.as_ref())?;
}
}

View file

@ -10,35 +10,43 @@ use crate::{LoxClass, ResolverError};
#[derive(Error, Debug)]
pub enum RuntimeError {
#[error("Unary operator {op} had invalid argument {arg}")]
#[error("RuntimeError: Unary operator {op} had invalid argument {arg}")]
UnaryOpInvalidArgument { op: UnaryOp, arg: Value },
#[error("Binary operator {op} had invalid arguments {left} and {right}")]
BinaryOpInvalidArguments { left: Value, op: BinaryOp, right: Value },
#[error("Division by zero")]
#[error("RuntimeError: Binary operator {op} had invalid arguments {left} and {right}")]
BinaryOpInvalidArguments {
left: Value,
op: BinaryOp,
right: Value,
},
#[error("RuntimeError: Division by zero")]
DivisionByZero,
#[error("Local {name} is not defined")]
#[error("RuntimeError: Local {name} is not defined")]
NameNotDefined { name: String },
#[error("Global {name} is not defined")]
#[error("RuntimeError: Global {name} is not defined")]
GlobalNotDefined { name: String },
#[error("{callee} is not callable")]
#[error("RuntimeError: {callee} is not callable")]
NotCallable { callee: Value },
#[error("{name}() takes {arity} args, but {given} were given.")]
WrongArity { name: String, arity: usize, given: usize },
#[error("Extern function call to {name} failed: {msg}")]
#[error("RuntimeError: {name}() takes {arity} args, but {given} were given.")]
WrongArity {
name: String,
arity: usize,
given: usize,
},
#[error("RuntimeError: Extern function call to {name} failed: {msg}")]
ExtFunCallFailed { name: String, msg: String },
#[error("Uncaught break statement")]
#[error("RuntimeError: Uncaught break statement")]
Break,
#[error("Uncaught return statement")]
#[error("RuntimeError: Uncaught return statement")]
Return { value: Value },
#[error("Exit with exit code {exit_code}")]
#[error("RuntimeError: Exit with exit code {exit_code}")]
Exit { exit_code: i32 },
#[error("Only objects have attributes")]
#[error("RuntimeError: Only objects have attributes")]
InvalidGetTarget,
#[error("Only objects have attributes")]
#[error("RuntimeError: Only objects have attributes")]
InvalidSetTarget,
#[error("Class {0} has no property {name}", class.name())]
UndefinedAttribute { class: Rc<LoxClass>, name: String },
#[error("{value} is not a valid superclass")]
#[error("RuntimeError: Class {0} has no property {name}", class.name())]
UndefinedAttribute { class: Rc<LoxClass>, name: Box<str> },
#[error("RuntimeError: {value} is not a valid superclass")]
InvalidSuperclass { value: Value },
}
@ -57,7 +65,7 @@ pub enum LoxError {
Exit { exit_code: i32 },
}
fn format_multiple_errors(errs: &Vec<impl std::error::Error>) -> String {
fn format_multiple_errors(errs: &[impl std::error::Error]) -> String {
let msg = if errs.len() == 1 {
errs[0].to_string()
} else {
@ -81,7 +89,9 @@ impl From<Vec<ParserError>> for LoxError {
impl From<ResolverError> for LoxError {
fn from(resolver_err: ResolverError) -> Self {
LoxError::ResolverError { inner: resolver_err }
LoxError::ResolverError {
inner: resolver_err,
}
}
}

View file

@ -11,14 +11,19 @@ use super::{Runtime, Value};
#[derive(Debug, Clone)]
pub struct LoxFunction {
name: String,
name: Box<str>,
closure: Scope,
param_names: Vec<String>,
param_names: Vec<Box<str>>,
body: Box<Stmt>,
}
impl LoxFunction {
pub fn new(name: impl Into<String>, closure: Scope, param_names: Vec<String>, body: Stmt) -> Rc<Self> {
pub fn new(
name: impl Into<Box<str>>,
closure: Scope,
param_names: Vec<Box<str>>,
body: Stmt,
) -> Rc<Self> {
let name = name.into();
let body = Box::new(body);
let fun = LoxFunction {
@ -38,7 +43,7 @@ impl LoxFunction {
&self.closure
}
pub fn param_names(&self) -> &[String] {
pub fn param_names(&self) -> &[Box<str>] {
&self.param_names
}
@ -50,7 +55,7 @@ impl LoxFunction {
self.param_names().len()
}
pub fn inject_closed_var(&mut self, name: impl Into<String>, value: Value) {
pub fn inject_closed_var(&mut self, name: impl Into<Box<str>>, value: Value) {
let name = name.into();
let heaped_value = HeapedValue::new(value);
self.closure.insert(name, heaped_value);
@ -88,7 +93,11 @@ impl LoxExternFunction {
pub fn new(name: impl Into<String>, arity: usize, closure: ExternFunClosure) -> Self {
let name = name.into();
LoxExternFunction { name, arity, closure }
LoxExternFunction {
name,
arity,
closure,
}
}
pub fn name(&self) -> &str {

View file

@ -31,7 +31,7 @@ trait Eval {
impl Eval for Literal {
fn eval(&self, _env: &mut Environment) -> EvalResult<Value> {
match self {
Literal::String(s) => Ok(Value::String(Rc::clone(s))),
Literal::String(s) => Ok(Value::String(s.clone())),
Literal::Number(num) => Ok(Value::Number(*num)),
Literal::Bool(b) => Ok(Value::Bool(*b)),
Literal::Nil => Ok(Value::Nil),
@ -83,10 +83,10 @@ impl Eval for Expr {
},
(String(left), String(right)) => match op {
BinaryOp::Add => {
let mut s = std::string::String::with_capacity(left.capacity() + right.capacity());
s += &left;
s += &right;
Ok(String(Rc::new(s)))
let s: Box<str> =
itertools::chain(left.as_ref().chars(), right.chars()).collect();
Ok(Value::string(s))
}
BinaryOp::Less => Ok(Bool(left < right)),
BinaryOp::LessEqual => Ok(Bool(left <= right)),
@ -99,14 +99,18 @@ impl Eval for Expr {
right: String(right),
}),
},
(left, right) => Err(RuntimeError::BinaryOpInvalidArguments { left, op, right }),
(left, right) => {
Err(RuntimeError::BinaryOpInvalidArguments { left, op, right })
}
}
}
Expr::Logical { left, op, right } => {
let left = left.eval(env)?;
// shortcircuit
if *op == LogicalOp::Or && left.is_truthy() || *op == LogicalOp::And && !left.is_truthy() {
if *op == LogicalOp::Or && left.is_truthy()
|| *op == LogicalOp::And && !left.is_truthy()
{
return Ok(left);
}
@ -121,7 +125,9 @@ impl Eval for Expr {
let value = value.eval(env)?;
match target.as_ref() {
Expr::LocalVariable { name, level } => env.assign(name, value.clone(), *level)?,
Expr::LocalVariable { name, level } => {
env.assign(name, value.clone(), *level)?
}
Expr::GlobalVariable { name } => env.assign_global(name, value.clone())?,
_ => panic!("Invalid assigment target {target}"),
}
@ -159,12 +165,16 @@ impl Eval for Expr {
Err(RuntimeError::InvalidGetTarget)
}
}
Expr::Set { target, name, value } => {
Expr::Set {
target,
name,
value,
} => {
let target = target.eval(env)?;
if let Value::Object(mut object) = target {
let value = value.eval(env)?;
object.set(name, value);
object.set(name.clone(), value);
Ok(Value::Nil)
} else {
Err(RuntimeError::InvalidSetTarget)
@ -176,7 +186,7 @@ impl Eval for Expr {
closure_vars,
body,
} => Ok(Value::function(LoxFunction::new(
name,
name.clone(),
env.collect_closure(closure_vars),
param_names.clone(),
body.as_ref().clone(),
@ -208,7 +218,11 @@ impl Eval for Expr {
env.exit_scope();
Ok(Value::class(LoxClass::new(name, methods, superclass)))
Ok(Value::class(LoxClass::new(
name.clone(),
methods,
superclass,
)))
}
Expr::This => panic!("Unresolved this"),
Expr::Super {
@ -223,7 +237,9 @@ impl Eval for Expr {
Err(RuntimeError::InvalidGetTarget)
}
}
(super_val, this_val) => panic!("super evaluated to {super_val} and this evaluated to {this_val}"),
(super_val, this_val) => {
panic!("super evaluated to {super_val} and this evaluated to {this_val}")
}
},
}
}
@ -233,11 +249,10 @@ impl Eval for Stmt {
fn eval(&self, env: &mut Environment) -> EvalResult<Value> {
match self {
Stmt::Print { expr } => {
match expr.eval(env)? {
// special case: when printing a string, drop the surrounding ""
Value::String(s) => println!("{s}"),
val => println!("{val}"),
}
use std::io::Write;
let val = expr.eval(env)?;
writeln!(env.runtime(), "{val}").unwrap();
}
Stmt::IfStmt {
condition,
@ -262,7 +277,7 @@ impl Eval for Stmt {
}
Stmt::VarDecl { name, initializer } => {
let initializer = initializer.eval(env)?;
env.define(name, initializer);
env.define(name.clone(), initializer);
}
Stmt::Block { statements } => {
env.enter_scope();
@ -310,7 +325,7 @@ pub fn call_fun(fun: Rc<LoxFunction>, env: &mut Environment) -> EvalResult<Value
env.insert_closure(fun.closure().clone());
for (name, value) in std::iter::zip(fun.param_names(), args) {
env.define(name, value);
env.define(name.clone(), value);
}
let ret_val = match fun.body().eval(env) {

View file

@ -47,7 +47,7 @@ fn clock() -> LoxExternFunction {
fn print_globals() -> LoxExternFunction {
let closure: ExternFunClosure = |args, env| {
assert_eq!(args.len(), 0);
let mut globals: Vec<(&String, &Value)> = env.globals().iter().collect();
let mut globals: Vec<(&Box<str>, &Value)> = env.globals().iter().collect();
globals.sort_by_key(|&(name, _value)| name);

View file

@ -12,7 +12,7 @@ use crate::{LoxClass, Value};
struct LoxObject {
class: Rc<LoxClass>,
attrs: FxHashMap<String, Value>,
attrs: FxHashMap<Box<str>, Value>,
}
impl LoxObject {
@ -35,7 +35,7 @@ impl LoxObject {
self.class.get_method(name, this)
}
fn set(&mut self, name: impl Into<String>, value: Value) {
fn set(&mut self, name: impl Into<Box<str>>, value: Value) {
let name = name.into();
self.attrs.insert(name, value);
}
@ -75,7 +75,7 @@ impl LoxReference {
}
}
pub fn set(&mut self, name: impl Into<String>, value: Value) {
pub fn set(&mut self, name: impl Into<Box<str>>, value: Value) {
unsafe {
let ptr = self.inner.get();
let object = &mut *ptr;
@ -99,7 +99,12 @@ impl Display for LoxReference {
unsafe {
let ptr = self.inner.get();
let object = &*ptr;
write!(f, "<{} object at {:#X}>", object.class().name(), ptr as usize)
write!(
f,
"<{} object at {:#X}>",
object.class().name(),
ptr as usize
)
}
}
}

View file

@ -6,7 +6,7 @@ use crate::{LoxError, ResolverError};
/*====================================================================================================================*/
type ResolverScope = FxHashMap<String, ResolveStatus>;
type ResolverScope = FxHashMap<Box<str>, ResolveStatus>;
type ResolverFrame = Vec<ResolverScope>;
/*====================================================================================================================*/
@ -35,11 +35,11 @@ struct Resolver {
// local_scopes: Vec<ResolverScope>,
frames: Vec<ResolverFrame>,
closure_vars: FxHashMap<String, usize>,
closure_vars: FxHashMap<Box<str>, usize>,
}
impl Resolver {
fn new(global_names: impl Iterator<Item = String>) -> Self {
fn new(global_names: impl Iterator<Item = Box<str>>) -> Self {
let mut global_scope = ResolverScope::default();
for name in global_names {
@ -76,11 +76,14 @@ impl Resolver {
}
fn pop_frame(&mut self) {
self.frames.pop().expect("Tried to pop non-existant ResolverFrame");
self.frames
.pop()
.expect("Tried to pop non-existant ResolverFrame");
}
fn declare(&mut self, name: &str) {
let name = name.to_owned();
fn declare(&mut self, name: impl Into<Box<str>>) {
let name = name.into();
if let Some(local_scope) = self.local_scopes_mut().last_mut() {
local_scope.insert(name, ResolveStatus::Declared);
} else {
@ -88,8 +91,9 @@ impl Resolver {
}
}
fn define(&mut self, name: &str) {
let name = name.to_owned();
fn define(&mut self, name: impl Into<Box<str>>) {
let name = name.into();
if let Some(local_scope) = self.local_scopes_mut().last_mut() {
local_scope.insert(name, ResolveStatus::Defined);
} else {
@ -106,38 +110,36 @@ impl Resolver {
}
fn resolve_var(&mut self, name: &str) -> Result<Expr, ResolverError> {
let name = name.to_owned();
let mut level = 0;
for scope in self.local_scopes().iter().rev() {
if scope.contains_key(&name) {
return Ok(Expr::LocalVariable { name, level });
if scope.contains_key(name) {
return Ok(Expr::local_variable(name, level));
}
level += 1;
}
for frame in self.frames.iter().rev().skip(1) {
for scope in frame.iter().rev() {
if scope.contains_key(&name) {
if !self.closure_vars.contains_key(&name) {
if scope.contains_key(name) {
if !self.closure_vars.contains_key(name) {
// the level at which the closed-over variable will be collected from
// from the perspective of the parameter/closure scope i.e. the outmost of the function
self.closure_vars
.insert(name.clone(), level - self.local_scopes().len());
.insert(name.into(), level - self.local_scopes().len());
}
return Ok(Expr::LocalVariable {
name,
// distance from actual variable refernce to parameter/closure scope
level: self.local_scopes().len() - 1,
});
// distance from actual variable refernce to parameter/closure scope
let level = self.local_scopes().len() - 1;
return Ok(Expr::local_variable(name, level));
}
level += 1;
}
}
if self.global_scope.contains_key(&name) {
return Ok(Expr::GlobalVariable { name });
if self.global_scope.contains_key(name) {
return Ok(Expr::global_variable(name));
}
if name == "this" {
@ -145,6 +147,7 @@ impl Resolver {
} else if name == "super" {
Err(ResolverError::SuperOutsideMethod)
} else {
let name = name.into();
Err(ResolverError::UnresolvableVariable { name })
}
}
@ -170,9 +173,9 @@ impl Resolver {
Ok(())
}
Stmt::VarDecl { name, initializer } => {
self.declare(name);
self.declare(name.clone());
self.resolve_expr(initializer)?;
self.define(name);
self.define(name.clone());
Ok(())
}
Stmt::Block { statements } => {
@ -244,7 +247,11 @@ impl Resolver {
self.resolve_expr(target)?;
Ok(())
}
Expr::Set { target, name: _, value } => {
Expr::Set {
target,
name: _,
value,
} => {
self.resolve_expr(target)?;
self.resolve_expr(value)?;
Ok(())
@ -259,12 +266,12 @@ impl Resolver {
self.push_frame();
self.declare(name);
self.define(name);
self.declare(name.clone());
self.define(name.clone());
for param_name in param_names.iter() {
self.declare(param_name);
self.define(param_name);
self.declare(param_name.clone());
self.define(param_name.clone());
}
self.resolve_stmt(body)?;
@ -284,13 +291,13 @@ impl Resolver {
name,
methods,
} => {
self.declare(name);
self.declare(name.clone());
if let Some(superclass) = superclass {
self.resolve_expr(superclass)?;
}
self.define(name);
self.define(name.clone());
// this is the scope "this" is defined in
self.enter_scope();

View file

@ -3,9 +3,9 @@ use thiserror::Error;
#[derive(Error, Debug)]
pub enum ResolverError {
#[error("Can't read variable {name} in its own initializer.")]
VarInOwnInitializer { name: String },
VarInOwnInitializer { name: Box<str> },
#[error("Variable {name} not defined")]
UnresolvableVariable { name: String },
UnresolvableVariable { name: Box<str> },
#[error("Return outside of function definition")]
ReturnOutsideFunction,
#[error("this outside of method")]

View file

@ -24,9 +24,12 @@ pub fn run_repl(runtime: &mut Runtime) {
std::process::exit(66);
});
let num_open_braces = (input_buf.matches('{').count() as i64) - (input_buf.matches('}').count() as i64);
let num_open_parens = (input_buf.matches('(').count() as i64) - (input_buf.matches(')').count() as i64);
let num_open_brackets = (input_buf.matches('[').count() as i64) - (input_buf.matches(']').count() as i64);
let num_open_braces =
(input_buf.matches('{').count() as i64) - (input_buf.matches('}').count() as i64);
let num_open_parens =
(input_buf.matches('(').count() as i64) - (input_buf.matches(')').count() as i64);
let num_open_brackets =
(input_buf.matches('[').count() as i64) - (input_buf.matches(']').count() as i64);
// all braces/parens/brackets closed => break
if num_open_braces == 0 && num_open_parens == 0 && num_open_brackets == 0 {
@ -47,12 +50,12 @@ pub fn run_repl(runtime: &mut Runtime) {
std::io::stdout().flush().unwrap();
}
let input_buf = input_buf.trim();
if input_buf.is_empty() || input_buf == "exit" || input_buf == "quit" {
if input_buf.is_empty() || input_buf == "exit\n" || input_buf == "quit\n" {
std::process::exit(0);
}
let input_buf = input_buf.trim();
match run(input_buf, runtime) {
Ok(()) => {}
Err(LoxError::Exit { exit_code }) => std::process::exit(exit_code),
@ -64,20 +67,25 @@ pub fn run_repl(runtime: &mut Runtime) {
pub fn run(source: &str, runtime: &mut Runtime) -> Result<(), LoxError> {
let tokens: Vec<Token> = scan_tokens(source)?;
let token_str = itertools::Itertools::join(&mut tokens.iter().map(|token| token.to_string()), " ");
if runtime.debug() {
let token_str =
itertools::Itertools::join(&mut tokens.iter().map(|token| token.to_string()), " ");
println!("{token_str}");
println!("{token_str}");
}
let statements = parse_tokens(tokens)?;
/* for statement in statements.iter() {
println!("{statement}");
} */
for mut statement in statements {
if runtime.debug() {
println!("{statement}");
}
resolve(&mut statement, runtime)?;
// println!("{statement}");
if runtime.debug() {
println!("{statement}");
}
execute(statement, runtime)?;
}

View file

@ -1,5 +1,7 @@
use std::cell::RefCell;
use std::fmt::Display;
use std::io::{stdin, stdout, Read, Write};
use std::rc::Rc;
use rustc_hash::FxHashMap;
@ -9,19 +11,24 @@ use super::lox_std::init_std;
use super::Value;
pub struct Runtime {
globals: FxHashMap<String, Value>,
globals: FxHashMap<Box<str>, Value>,
in_stream: Box<dyn std::io::BufRead>,
out_stream: Box<dyn std::io::Write>,
in_stream: Rc<RefCell<dyn std::io::Read>>,
out_stream: Rc<RefCell<dyn std::io::Write>>,
debug: bool,
}
impl Runtime {
pub fn new(in_stream: Box<dyn std::io::Read>, out_stream: Box<dyn std::io::Write>) -> Self {
let in_stream = Box::new(std::io::BufReader::new(in_stream));
pub fn new(
in_stream: Rc<RefCell<dyn std::io::Read>>,
out_stream: Rc<RefCell<dyn std::io::Write>>,
) -> Self {
let mut runtime = Runtime {
globals: FxHashMap::default(),
in_stream,
out_stream,
debug: false,
};
init_std(&mut runtime);
@ -29,7 +36,24 @@ impl Runtime {
runtime
}
pub fn globals(&self) -> &FxHashMap<String, Value> {
pub fn set_debug(&mut self, debug: bool) {
self.debug = debug;
}
pub fn debug(&self) -> bool {
self.debug
}
pub fn in_stream(&mut self) -> std::cell::RefMut<dyn std::io::Read> {
self.in_stream.as_ref().borrow_mut()
}
pub fn out_stream(&mut self) -> std::cell::RefMut<dyn std::io::Write> {
// &mut self.out_stream
self.out_stream.as_ref().borrow_mut()
}
pub fn globals(&self) -> &FxHashMap<Box<str>, Value> {
&self.globals
}
@ -37,10 +61,12 @@ impl Runtime {
self.globals
.get(name)
.cloned()
.ok_or_else(|| RuntimeError::GlobalNotDefined { name: name.to_owned() })
.ok_or_else(|| RuntimeError::GlobalNotDefined {
name: name.to_owned(),
})
}
pub fn define_global(&mut self, name: impl Into<String>, value: Value) {
pub fn define_global(&mut self, name: impl Into<Box<str>>, value: Value) {
let name = name.into();
self.globals.insert(name, value);
}
@ -50,20 +76,30 @@ impl Runtime {
*old_value = value;
Ok(())
} else {
Err(RuntimeError::GlobalNotDefined { name: name.to_owned() })
Err(RuntimeError::GlobalNotDefined {
name: name.to_owned(),
})
}
}
}
impl Default for Runtime {
fn default() -> Self {
Runtime::new(Box::new(std::io::BufReader::new(stdin())), Box::new(stdout()))
// let buf_reader = std::io::BufReader::new(stdin());
// let buf_reader_boxed = Box::new(&mut buf_reader);
let in_stream = Rc::new(RefCell::new(stdin()));
let out_stream = Rc::new(RefCell::new(stdout()));
Runtime::new(in_stream, out_stream)
}
}
impl std::fmt::Debug for Runtime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Runtime").field("globals", &self.globals).finish()
f.debug_struct("Runtime")
.field("globals", &self.globals)
.finish()
}
}
@ -81,22 +117,22 @@ impl Display for Runtime {
impl Drop for Runtime {
fn drop(&mut self) {
self.out_stream.flush().unwrap();
self.out_stream().flush().unwrap();
}
}
impl Read for Runtime {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.in_stream.read(buf)
self.in_stream().read(buf)
}
}
impl Write for Runtime {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.out_stream.write(buf)
self.out_stream().write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
self.out_stream.flush()
self.out_stream().flush()
}
}

View file

@ -13,11 +13,12 @@ pub enum Value {
Class(Rc<LoxClass>),
Function(Rc<LoxFunction>),
ExternFunction(Rc<LoxExternFunction>),
String(Rc<String>),
String(Box<str>),
Number(f64),
Bool(bool),
Nil,
}
impl Value {
pub fn class(class: Rc<LoxClass>) -> Self {
Value::Class(class)
@ -33,8 +34,8 @@ impl Value {
Value::ExternFunction(fun)
}
pub fn string(s: impl Into<String>) -> Self {
let s = Rc::new(s.into());
pub fn string(s: impl Into<Box<str>>) -> Self {
let s = s.into();
Value::String(s)
}
@ -50,7 +51,7 @@ impl Display for Value {
Value::Class(class) => write!(f, "{class}"),
Value::Function(fun) => write!(f, "{fun}"),
Value::ExternFunction(ext_fun) => write!(f, "{ext_fun}"),
Value::String(s) => write!(f, "\"{s}\""),
Value::String(s) => write!(f, "{s}"),
Value::Number(num) => write!(f, "{num}"),
Value::Bool(b) => write!(f, "{b}"),
Value::Nil => write!(f, "nil"),
@ -60,7 +61,7 @@ impl Display for Value {
/*====================================================================================================================*/
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct HeapedValue {
inner: Rc<UnsafeCell<Value>>,
}
@ -88,14 +89,11 @@ impl HeapedValue {
}
}
impl Debug for HeapedValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HeapValue").field("inner", &self.get_clone()).finish()
}
}
impl Display for HeapedValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.get_clone())
impl AsRef<Value> for HeapedValue {
fn as_ref(&self) -> &Value {
unsafe {
let ptr = self.inner.get();
&*ptr
}
}
}

View file

@ -10,6 +10,9 @@ struct CliArgs {
#[arg(long, action = ArgAction::SetTrue)]
vm: bool,
#[arg(long, action=ArgAction::SetTrue)]
debug: bool,
}
/*
@ -22,7 +25,8 @@ fn main() {
let cli_args = CliArgs::parse();
if cli_args.vm {
use rlox2_vm::LoxError;
unimplemented!()
/* use rlox2_vm::LoxError;
let mut vm = rlox2_vm::VM::default();
@ -35,7 +39,9 @@ fn main() {
if let Err(err) = rlox2_vm::run(&source, &mut vm) {
eprintln!("{err}");
match err {
LoxError::LexerError { .. } | LoxError::CompileError { .. } => std::process::exit(65),
LoxError::LexerError { .. } | LoxError::CompileError { .. } => {
std::process::exit(65)
}
LoxError::RuntimeError { .. } => std::process::exit(70),
LoxError::Exit { exit_code } => std::process::exit(exit_code),
}
@ -46,12 +52,14 @@ fn main() {
}
}
rlox2_vm::run_repl(&mut vm);
rlox2_vm::run_repl(&mut vm); */
} else {
use rlox2_interpreter::LoxError;
let mut runtime = rlox2_interpreter::Runtime::default();
runtime.set_debug(cli_args.debug);
if let Some(file_name) = cli_args.file_name {
let source = std::fs::read_to_string(&file_name).unwrap_or_else(|err| {
eprintln!("Reading script file {file_name} failed: {err}");
@ -61,9 +69,9 @@ fn main() {
if let Err(err) = rlox2_interpreter::run(&source, &mut runtime) {
eprintln!("{err}");
match err {
LoxError::LexerError { .. } | LoxError::ParserError { .. } | LoxError::ResolverError { .. } => {
std::process::exit(65)
}
LoxError::LexerError { .. }
| LoxError::ParserError { .. }
| LoxError::ResolverError { .. } => std::process::exit(65),
LoxError::RuntimeError { .. } => std::process::exit(70),
LoxError::Exit { exit_code } => std::process::exit(exit_code),
}