diff --git a/Cargo.lock b/Cargo.lock index 6872ac7..b591dc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,9 +72,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "approx" @@ -98,7 +98,7 @@ dependencies = [ "objc2-foundation", "parking_lot", "percent-encoding", - "windows-sys 0.59.0", + "windows-sys 0.60.2", "wl-clipboard-rs", "x11rb", ] @@ -127,9 +127,9 @@ checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" dependencies = [ "serde_core", ] @@ -145,25 +145,26 @@ dependencies = [ [[package]] name = "boolmesh" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5e804fa333a4996316770fff780c7cd006371c58bc7824bd8798f17396eac2" +version = "0.1.9" +source = "git+https://github.com/IamTheCarl/boolmesh.git?branch=serde#76b3783817e1e9782e3efd814ca939c869f45a7f" dependencies = [ "glam 0.30.10", "rayon", + "serde", + "thiserror 2.0.18", ] [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytemuck" -version = "1.24.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" [[package]] name = "byteorder" @@ -173,9 +174,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.53" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "jobserver", @@ -212,9 +213,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.54" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" dependencies = [ "clap_builder", "clap_derive", @@ -222,9 +223,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.54" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" dependencies = [ "anstream", "anstyle", @@ -234,9 +235,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" dependencies = [ "heck", "proc-macro2", @@ -246,9 +247,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" [[package]] name = "cli" @@ -341,9 +342,9 @@ dependencies = [ [[package]] name = "crokey" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51360853ebbeb3df20c76c82aecf43d387a62860f1a59ba65ab51f00eea85aad" +checksum = "04a63daf06a168535c74ab97cdba3ed4fa5d4f32cb36e437dcceb83d66854b7c" dependencies = [ "crokey-proc_macros", "crossterm", @@ -354,9 +355,9 @@ dependencies = [ [[package]] name = "crokey-proc_macros" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf1a727caeb5ee5e0a0826a97f205a9cf84ee964b0b48239fef5214a00ae439" +checksum = "847f11a14855fc490bd5d059821895c53e77eeb3c2b73ee3dded7ce77c93b231" dependencies = [ "crossterm", "proc-macro2", @@ -669,9 +670,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixedbitset" @@ -741,6 +742,19 @@ dependencies = [ "wasip2", ] +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + [[package]] name = "git2" version = "0.20.4" @@ -851,6 +865,9 @@ name = "glam" version = "0.30.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" +dependencies = [ + "serde_core", +] [[package]] name = "hashable-map" @@ -890,9 +907,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -993,6 +1010,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "idna" version = "1.1.0" @@ -1028,6 +1051,8 @@ checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", + "serde", + "serde_core", ] [[package]] @@ -1057,6 +1082,7 @@ dependencies = [ "stack", "stl_io", "tempfile", + "thiserror 2.0.18", "tree-sitter", "tree-sitter-command-cad-model", "type-sitter", @@ -1110,7 +1136,7 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom", + "getrandom 0.3.4", "libc", ] @@ -1122,9 +1148,9 @@ checksum = "e90f66baf362a8a5ce2ca820290fbede0572a76fabf8408bc68c02f9ad1d03bf" [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "93f0862381daaec758576dcc22eb7bbf4d7efd67328553f3b45a412a51a3fb21" dependencies = [ "once_cell", "wasm-bindgen", @@ -1132,9 +1158,9 @@ dependencies = [ [[package]] name = "lazy-regex" -version = "3.5.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5c13b6857ade4c8ee05c3c3dc97d2ab5415d691213825b90d3211c425c1f907" +checksum = "6bae91019476d3ec7147de9aa291cadb6d870abf2f3015d2da73a90325ac1496" dependencies = [ "lazy-regex-proc_macros", "once_cell", @@ -1143,9 +1169,9 @@ dependencies = [ [[package]] name = "lazy-regex-proc_macros" -version = "3.5.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a95c68db5d41694cea563c86a4ba4dc02141c16ef64814108cb23def4d5438" +checksum = "4de9c1e1439d8b7b3061b2d209809f447ca33241733d9a3c01eabf2dc8d94358" dependencies = [ "proc-macro2", "quote", @@ -1159,6 +1185,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "levenshtein" version = "1.0.5" @@ -1167,9 +1199,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.180" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libgit2-sys" @@ -1300,9 +1332,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "minimad" @@ -1681,9 +1713,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -1721,9 +1753,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -1800,7 +1832,7 @@ dependencies = [ "strip-ansi-escapes", "strum", "strum_macros", - "thiserror 2.0.17", + "thiserror 2.0.18", "unicase", "unicode-segmentation", "unicode-width 0.2.2", @@ -1808,9 +1840,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -1820,9 +1852,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1831,9 +1863,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] name = "rustc-stable-hash" @@ -1871,9 +1903,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "safe_arch" @@ -2018,9 +2050,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "slice-group-by" @@ -2107,9 +2139,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.114" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -2129,12 +2161,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.24.0" +version = "3.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" dependencies = [ "fastrand", - "getrandom", + "getrandom 0.4.1", "once_cell", "rustix", "windows-sys 0.61.2", @@ -2152,7 +2184,7 @@ dependencies = [ "lazy-regex", "minimad", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", "unicode-width 0.1.14", ] @@ -2167,11 +2199,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -2187,9 +2219,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -2230,9 +2262,9 @@ dependencies = [ [[package]] name = "tree-sitter-language" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae62f7eae5eb549c71b76658648b72cc6111f2d87d24a1e31fa907f4943e3ce" +checksum = "009994f150cc0cd50ff54917d5bc8bffe8cad10ca10d81c34da2ec421ae61782" [[package]] name = "tree_magic_mini" @@ -2327,9 +2359,9 @@ checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" @@ -2349,6 +2381,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "units" version = "0.1.0" @@ -2450,11 +2488,20 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "1de241cdc66a9d91bd84f097039eb140cdc6eec47e0cdbaf9d932a1dd6c35866" dependencies = [ "cfg-if", "once_cell", @@ -2465,9 +2512,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "e12fdf6649048f2e3de6d7d5ff3ced779cdedee0e0baffd7dff5cdfa3abc8a52" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2475,9 +2522,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "0e63d1795c565ac3462334c1e396fd46dbf481c40f51f5072c310717bc4fb309" dependencies = [ "bumpalo", "proc-macro2", @@ -2488,13 +2535,47 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "e9f9cdac23a5ce71f6bf9f8824898a501e511892791ea2a0c6b8568c68b9cb53" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "wayland-backend" version = "0.3.12" @@ -2671,7 +2752,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -2689,14 +2779,31 @@ 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", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -2705,53 +2812,183 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[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_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "wl-clipboard-rs" @@ -2763,7 +3000,7 @@ dependencies = [ "log", "os_pipe", "rustix", - "thiserror 2.0.17", + "thiserror 2.0.18", "tree_magic_mini", "wayland-backend", "wayland-client", @@ -2879,6 +3116,6 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.14" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/interpreter/Cargo.toml b/interpreter/Cargo.toml index cbc904b..c6a0fe3 100644 --- a/interpreter/Cargo.toml +++ b/interpreter/Cargo.toml @@ -28,9 +28,11 @@ selen = "0.15.5" sha2 = "0.10.9" serde = { version = "1.0.228", features = ["derive"] } hex = "0.4.3" -boolmesh = { version = "0.1.7", features = ["rayon"] } +# boolmesh = { version = "0.1.7", features = ["rayon"] } +boolmesh = { git = "https://github.com/IamTheCarl/boolmesh.git", branch = "serde", features = ["rayon", "serde"] } stl_io = "0.10.0" itertools = "0.14.0" +thiserror = "2.0.18" [build-dependencies] type-sitter-gen = "0.8" diff --git a/interpreter/src/execution/errors.rs b/interpreter/src/execution/errors.rs index bef29b4..f44b461 100644 --- a/interpreter/src/execution/errors.rs +++ b/interpreter/src/execution/errors.rs @@ -20,14 +20,15 @@ use std::{any::Any, borrow::Cow, fmt::Display}; use ariadne::{Label, Report, ReportKind}; -use crate::compile::SourceReference; +use crate::{compile::SourceReference, StackTrace}; -pub type ExpressionResult = std::result::Result; +pub type ExecutionResult = std::result::Result; #[derive(Debug)] pub struct Error { pub ty: Box, pub trace: Vec, + pub failure_chain: Vec>, } impl Error { @@ -38,6 +39,8 @@ impl Error { builder.set_message("Failed to evaluate"); builder.add_label(Label::new(bottom).with_message(format!("{}", self.ty))); + builder.with_helps(self.failure_chain.iter()); + builder.finish() } } @@ -58,27 +61,61 @@ impl std::error::Error for Error {} /// A generic error that will just display a static message. #[derive(Debug, Eq, PartialEq)] -pub struct GenericFailure(pub Cow<'static, str>); +pub struct StrError(pub &'static str); -impl ErrorType for GenericFailure {} +impl std::error::Error for StrError {} -impl Display for GenericFailure { +impl Display for StrError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } -pub trait ErrorType: std::fmt::Debug + std::fmt::Display + Any + Send + Sync {} +/// A generic error that will just display a formatted message. +#[derive(Debug, Eq, PartialEq)] +pub struct StringError(pub String); + +impl std::error::Error for StringError {} + +impl Display for StringError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +pub trait ErrorType: std::error::Error + Send + Sync + Any { + fn as_any(&self) -> &dyn Any; +} + +impl ErrorType for E +where + E: std::error::Error + Send + Sync + Any, +{ + fn as_any(&self) -> &dyn Any { + self as &dyn Any + } +} pub trait Raise { - fn to_error<'s>(self, stack_trace: impl IntoIterator) -> Error; + fn to_error<'s>(self, stack_trace: impl IntoIterator>) -> Error; } impl Raise for E { - fn to_error<'s>(self, stack_trace: impl IntoIterator) -> Error { + fn to_error<'s>(self, stack_trace: impl IntoIterator>) -> Error { + let mut trace = Vec::new(); + let mut failure_chain = Vec::new(); + + for layer in stack_trace { + trace.push(layer.reference.clone()); + if let Some(message) = layer.failure_message.clone() { + failure_chain.push(message); + } + } + Error { ty: Box::new(self), - trace: stack_trace.into_iter().cloned().collect(), + trace, + failure_chain, } } } diff --git a/interpreter/src/execution/logging.rs b/interpreter/src/execution/logging.rs index b925b36..04a047c 100644 --- a/interpreter/src/execution/logging.rs +++ b/interpreter/src/execution/logging.rs @@ -57,8 +57,9 @@ impl RuntimeLog for Mutex> { #[derive(Debug, Clone)] pub struct StackTrace<'p> { - parent: Option<&'p StackTrace<'p>>, - reference: SourceReference, + pub parent: Option<&'p StackTrace<'p>>, + pub reference: SourceReference, + pub failure_message: Option>, } impl<'p> Display for StackTrace<'p> { @@ -76,6 +77,7 @@ impl StackTrace<'static> { Self { parent: None, reference, + failure_message: None, } } @@ -92,6 +94,7 @@ impl StackTrace<'static> { end_point: Point { row: 0, column: 0 }, }, }, + failure_message: Some("Bootstrap failed".into()), } } @@ -108,21 +111,35 @@ impl StackTrace<'static> { end_point: Point { row: 0, column: 0 }, }, }, + failure_message: Some("Test failed".into()), } } } impl<'p> StackTrace<'p> { - pub fn trace_scope(&'p self, reference: impl Into, code: F) -> R + pub fn trace_scope( + &'p self, + failure_message: Option>, + reference: impl Into, + code: F, + ) -> R where - F: FnOnce(StackTrace<'p>) -> R, + F: FnOnce(&StackTrace<'p>) -> R, { - let scope = Self { - parent: Some(self), - reference: reference.into(), - }; + let reference = reference.into(); - code(scope) + if self.reference != reference || self.failure_message != failure_message { + let scope = Self { + parent: Some(self), + reference, + failure_message, + }; + + code(&scope) + } else { + // This is not actually a new scope + code(self) + } } pub fn bottom(&self) -> &SourceReference { @@ -137,7 +154,7 @@ impl<'p> StackTrace<'p> { } impl<'p> IntoIterator for &'p StackTrace<'p> { - type Item = &'p SourceReference; + type Item = &'p StackTrace<'p>; type IntoIter = StackTraceIter<'p>; fn into_iter(self) -> Self::IntoIter { @@ -150,14 +167,14 @@ pub struct StackTraceIter<'p> { } impl<'p> Iterator for StackTraceIter<'p> { - type Item = &'p SourceReference; + type Item = &'p StackTrace<'p>; fn next(&mut self) -> Option { let next = self.current.take(); if let Some(next) = next { self.current = next.parent; } - next.map(|next| &next.reference) + next } } diff --git a/interpreter/src/execution/mod.rs b/interpreter/src/execution/mod.rs index ee8543f..6893345 100644 --- a/interpreter/src/execution/mod.rs +++ b/interpreter/src/execution/mod.rs @@ -17,6 +17,7 @@ */ use std::{ + borrow::Cow, cmp::Ordering, collections::HashMap, path::{Path, PathBuf}, @@ -27,7 +28,8 @@ use crate::{ build_function, compile::{self, AstNode, BinaryExpressionOperation, Expression, UnaryExpressionOperation}, execution::{ - errors::{GenericFailure, Raise}, + errors::{Raise, StrError, StringError}, + logging::StackTraceIter, stack::ScopeType, values::BuiltinCallableDatabase, }, @@ -47,7 +49,7 @@ mod logging; mod stack; mod standard_environment; pub mod values; -pub use errors::ExpressionResult; +pub use errors::ExecutionResult; use imstr::ImString; use logging::LocatedStr; pub use logging::{ExecutionFileCache, LogLevel, LogMessage, RuntimeLog, StackTrace}; @@ -55,6 +57,7 @@ pub use stack::StackScope; mod store; pub use store::Store; +use thiserror::Error; use values::{ closure::find_all_variable_accesses_in_closure_capture, dictionary::find_all_variable_accesses_in_dictionary_construction, Object, Value, ValueType, @@ -64,8 +67,8 @@ pub use standard_environment::build_prelude; pub fn find_all_variable_accesses_in_expression( expression: &Expression, - access_collector: &mut dyn FnMut(&AstNode) -> ExpressionResult<()>, -) -> ExpressionResult<()> { + access_collector: &mut dyn FnMut(&AstNode) -> ExecutionResult<()>, +) -> ExecutionResult<()> { match expression { Expression::BinaryExpression(ast_node) => { find_all_variable_accesses_in_expression(&ast_node.node.a.node, access_collector)?; @@ -218,21 +221,27 @@ pub struct ExecutionContext<'c> { } impl<'c> ExecutionContext<'c> { - pub fn trace_scope(&'c self, reference: impl Into, code: F) -> R + pub fn trace_scope( + &'c self, + failure_message: Option>, + reference: impl Into, + code: F, + ) -> R where F: FnOnce(&ExecutionContext<'_>) -> R, { - self.stack_trace.trace_scope(reference, move |stack_trace| { - let context = ExecutionContext { - stack_trace: &stack_trace, - ..*self - }; + self.stack_trace + .trace_scope(failure_message, reference, move |stack_trace| { + let context = ExecutionContext { + stack_trace, + ..*self + }; - code(&context) - }) + code(&context) + }) } - pub fn get_variable<'s, S: Into>>(&self, name: S) -> ExpressionResult<&Value> { + pub fn get_variable<'s, S: Into>>(&self, name: S) -> ExecutionResult<&Value> { self.stack.get_variable(self.stack_trace, [], name) } @@ -240,7 +249,7 @@ impl<'c> ExecutionContext<'c> { &self, local_variables: impl IntoIterator, name: S, - ) -> ExpressionResult<&Value> { + ) -> ExecutionResult<&Value> { self.stack .get_variable(self.stack_trace, local_variables, name) } @@ -250,7 +259,7 @@ impl<'c> ExecutionContext<'c> { mode: ScopeType, variables: HashMap, block: B, - ) -> ExpressionResult + ) -> ExecutionResult where B: FnOnce(&ExecutionContext) -> R, { @@ -267,12 +276,24 @@ impl<'c> ExecutionContext<'c> { } } +impl<'s> IntoIterator for &'s ExecutionContext<'_> { + type Item = &'s StackTrace<'s>; + + type IntoIter = StackTraceIter<'s>; + + fn into_iter(self) -> Self::IntoIter { + self.stack_trace.iter() + } +} + pub fn execute_expression( context: &ExecutionContext, expression: &compile::AstNode, -) -> ExpressionResult { - context.trace_scope(expression.reference.clone(), |context| { - match &expression.node { +) -> ExecutionResult { + context.trace_scope( + None, + expression.reference.clone(), + |context| match &expression.node { compile::Expression::BinaryExpression(ast_node) => { execute_binary_expression(context, ast_node) } @@ -291,7 +312,7 @@ pub fn execute_expression( compile::Expression::MemberAccess(ast_node) => { let base = execute_expression(context, &ast_node.node.base)?; - context.trace_scope(ast_node.node.member.reference.clone(), |context| { + context.trace_scope(None, ast_node.node.member.reference.clone(), |context| { base.get_attribute(context, &ast_node.node.member.node) }) } @@ -342,19 +363,18 @@ pub fn execute_expression( compile::Expression::ConstraintSet(constraint_set) => { ConstraintSet::from_ast(context, constraint_set).map(|set| set.into()) } - compile::Expression::Malformed(kind) => Err(GenericFailure( - format!("Malformed syntax, expected {kind}").into(), - ) - .to_error(context.stack_trace)), - } - }) + compile::Expression::Malformed(kind) => { + Err(StringError(format!("Malformed syntax, expected {kind}")).to_error(context)) + } + }, + ) } fn execute_unary_expression( context: &ExecutionContext, expression: &compile::AstNode>, -) -> ExpressionResult { - context.trace_scope(expression.reference.clone(), |context| { +) -> ExecutionResult { + context.trace_scope(None, expression.reference.clone(), |context| { let node = &expression.node; let value = execute_expression(context, &node.expression)?; match node.operation.node { @@ -368,7 +388,7 @@ fn execute_unary_expression( fn execute_function_call( context: &ExecutionContext, call: &compile::AstNode>, -) -> ExpressionResult { +) -> ExecutionResult { let to_call = execute_expression(context, &call.node.to_call)?; let argument = values::Dictionary::from_ast(context, &call.node.argument)?; @@ -382,7 +402,7 @@ fn execute_function_call( fn execute_method_call( context: &ExecutionContext, call: &compile::AstNode>, -) -> ExpressionResult { +) -> ExecutionResult { let self_dictionary = execute_expression(context, &call.node.self_dictionary)?; let to_call = self_dictionary .get_attribute(context, &call.node.to_call.node)? @@ -399,8 +419,8 @@ fn execute_method_call( fn execute_let_in( context: &ExecutionContext, expression: &compile::AstNode>, -) -> ExpressionResult { - context.trace_scope(expression.reference.clone(), |context| { +) -> ExecutionResult { + context.trace_scope(None, expression.reference.clone(), |context| { context.stack.scope_mut( context.stack_trace, ScopeType::Inherited, @@ -443,78 +463,72 @@ fn execute_let_in( fn execute_binary_expression( context: &ExecutionContext, expression: &compile::AstNode>, -) -> ExpressionResult { - context.trace_scope(expression.reference.clone(), |context| { - let node = &expression.node; - - let (result_a, result_b) = join( - || execute_expression(context, &node.a), - || execute_expression(context, &node.b), - ); +) -> ExecutionResult { + let node = &expression.node; - let value_a = result_a?; - let value_b = result_b?; + let (result_a, result_b) = join( + || execute_expression(context, &node.a), + || execute_expression(context, &node.b), + ); - match node.operation.node { - BinaryExpressionOperation::NotEq => Ok(values::Boolean( - !value_a - .clone() - .cmp(context, value_b.clone()) - .map(|ord| matches!(ord, Ordering::Equal)) - .or_else(|_| value_a.eq(context, value_b))?, - ) - .into()), - BinaryExpressionOperation::And => value_a.bit_and(context, value_b), - BinaryExpressionOperation::AndAnd => value_a.and(context, value_b), - BinaryExpressionOperation::Mul => value_a.multiply(context, value_b), - BinaryExpressionOperation::MulMul => value_a.exponent(context, value_b), - BinaryExpressionOperation::Add => value_a.addition(context, value_b), - BinaryExpressionOperation::Sub => value_a.subtraction(context, value_b), - BinaryExpressionOperation::Div => value_a.divide(context, value_b), - BinaryExpressionOperation::Lt => Ok(values::Boolean(matches!( - value_a.cmp(context, value_b)?, - Ordering::Less - )) - .into()), - BinaryExpressionOperation::LtLt => value_a.left_shift(context, value_b), - BinaryExpressionOperation::LtEq => Ok(values::Boolean(matches!( - value_a.cmp(context, value_b)?, - Ordering::Less | Ordering::Equal - )) - .into()), - BinaryExpressionOperation::EqEq => Ok(values::Boolean( - value_a - .clone() - .cmp(context, value_b.clone()) - .map(|ord| matches!(ord, Ordering::Equal)) - .or_else(|_| value_a.eq(context, value_b))?, - ) - .into()), - BinaryExpressionOperation::Gt => Ok(values::Boolean(matches!( - value_a.cmp(context, value_b)?, - Ordering::Greater - )) - .into()), - BinaryExpressionOperation::GtEq => Ok(values::Boolean(matches!( - value_a.cmp(context, value_b)?, - Ordering::Equal | Ordering::Greater - )) - .into()), - BinaryExpressionOperation::GtGt => value_a.right_shift(context, value_b), - BinaryExpressionOperation::BitXor => value_a.bit_xor(context, value_b), - BinaryExpressionOperation::Xor => value_a.xor(context, value_b), - BinaryExpressionOperation::Or => value_a.bit_or(context, value_b), - BinaryExpressionOperation::OrOr => value_a.or(context, value_b), + let value_a = result_a?; + let value_b = result_b?; + + match node.operation.node { + BinaryExpressionOperation::NotEq => Ok(values::Boolean( + !value_a + .clone() + .cmp(context, value_b.clone()) + .map(|ord| matches!(ord, Ordering::Equal)) + .or_else(|_| value_a.eq(context, value_b))?, + ) + .into()), + BinaryExpressionOperation::And => value_a.bit_and(context, value_b), + BinaryExpressionOperation::AndAnd => value_a.and(context, value_b), + BinaryExpressionOperation::Mul => value_a.multiply(context, value_b), + BinaryExpressionOperation::MulMul => value_a.exponent(context, value_b), + BinaryExpressionOperation::Add => value_a.addition(context, value_b), + BinaryExpressionOperation::Sub => value_a.subtraction(context, value_b), + BinaryExpressionOperation::Div => value_a.divide(context, value_b), + BinaryExpressionOperation::Lt => { + Ok(values::Boolean(matches!(value_a.cmp(context, value_b)?, Ordering::Less)).into()) } - }) + BinaryExpressionOperation::LtLt => value_a.left_shift(context, value_b), + BinaryExpressionOperation::LtEq => Ok(values::Boolean(matches!( + value_a.cmp(context, value_b)?, + Ordering::Less | Ordering::Equal + )) + .into()), + BinaryExpressionOperation::EqEq => Ok(values::Boolean( + value_a + .clone() + .cmp(context, value_b.clone()) + .map(|ord| matches!(ord, Ordering::Equal)) + .or_else(|_| value_a.eq(context, value_b))?, + ) + .into()), + BinaryExpressionOperation::Gt => { + Ok(values::Boolean(matches!(value_a.cmp(context, value_b)?, Ordering::Greater)).into()) + } + BinaryExpressionOperation::GtEq => Ok(values::Boolean(matches!( + value_a.cmp(context, value_b)?, + Ordering::Equal | Ordering::Greater + )) + .into()), + BinaryExpressionOperation::GtGt => value_a.right_shift(context, value_b), + BinaryExpressionOperation::BitXor => value_a.bit_xor(context, value_b), + BinaryExpressionOperation::Xor => value_a.xor(context, value_b), + BinaryExpressionOperation::Or => value_a.bit_or(context, value_b), + BinaryExpressionOperation::OrOr => value_a.or(context, value_b), + } } pub fn execute_if_expression( context: &ExecutionContext, expression: &compile::AstNode>, -) -> ExpressionResult { +) -> ExecutionResult { let condition = execute_expression(context, &expression.node.condition)? - .downcast::(context.stack_trace)? + .downcast::(context)? .0; let expression = if condition { @@ -527,7 +541,7 @@ pub fn execute_if_expression( } #[cfg(test)] -pub(crate) fn test_run(input: &str) -> ExpressionResult { +pub(crate) fn test_run(input: &str) -> ExecutionResult { let root = compile::full_compile(input); test_context([], |context| execute_expression(context, &root)) @@ -578,78 +592,83 @@ pub(crate) fn test_context_custom_database( f(&context) } -pub fn run_file(context: &ExecutionContext, file: impl Into) -> ExpressionResult { +#[derive(Debug, Error, Eq, PartialEq)] +pub enum FileRunError { + #[error("Absolute paths cannot be used for importing files")] + AbsolutePath, + + #[error("Import recursion depth has been exceeded")] + ImportDepth, + + #[error("Failed to get parent directory of file")] + NoParentDirectory, +} + +pub fn run_file(context: &ExecutionContext, file: impl Into) -> ExecutionResult { // TODO can/should we make the parser part of the execution context rather than build a new one // every time we load a file? let mut parser = new_parser(); - let file = file.into(); - if file.is_absolute() { - return Err( - GenericFailure("Absolute paths cannot be used for importing files".into()) - .to_error(context.stack_trace), - ); - } - - if context.import_limit == 0 { - return Err( - GenericFailure("Import recursion depth has been exceeded".into()) - .to_error(context.stack_trace), - ); - } - - let file = context.working_directory.join(file); - let parent_dir = file.parent().ok_or_else(|| { - GenericFailure("Failed to get parent directory of file: {error}".into()) - .to_error(context.stack_trace) - })?; - - let root = { - let mut files = context.file_cache.lock().map_err(|_error| { - GenericFailure("Failed to lock file cache".into()).to_error(context.stack_trace) - })?; - let file = Arc::new(file.clone()); - let input = match files.entry(file.clone()) { - std::collections::hash_map::Entry::Occupied(entry) => entry, - std::collections::hash_map::Entry::Vacant(vacency) => { - let input = std::fs::read_to_string(file.as_path()).map_err(|error| { - GenericFailure( - format!("Failed to read file {:?} from disk: {error}", file).into(), - ) - .to_error(context.stack_trace) - })?; - let input = ImString::from(input); + context.trace_scope( + Some(format!("Failed to import {file:?}").into()), + context.stack_trace.bottom().clone(), + |context| { + if file.is_absolute() { + return Err(FileRunError::AbsolutePath.to_error(context)); + } - vacency.insert_entry(Source::from(input)) + if context.import_limit == 0 { + return Err(FileRunError::ImportDepth.to_error(context)); } - }; - let input = input.get().text(); - let tree = parser.parse(input, None).map_err(|error| { - GenericFailure(format!("Failed to parse input: {error:?}").into()) - .to_error(context.stack_trace) - })?; + let file = context.working_directory.join(file); + let parent_dir = file + .parent() + .ok_or_else(|| FileRunError::NoParentDirectory.to_error(context))?; + + let root = { + let mut files = context + .file_cache + .lock() + .map_err(|_error| StrError("Failed to lock file cache").to_error(context))?; + let file = Arc::new(file.clone()); + let input = match files.entry(file.clone()) { + std::collections::hash_map::Entry::Occupied(entry) => entry, + std::collections::hash_map::Entry::Vacant(vacency) => { + let input = std::fs::read_to_string(file.as_path()) + .map_err(|error| error.to_error(context))?; + let input = ImString::from(input); + + vacency.insert_entry(Source::from(input)) + } + }; + let input = input.get().text(); - let root = crate::compile(&file, input, &tree).map_err(|error| { - GenericFailure(format!("Failed to compile: {error}").into()) - .to_error(context.stack_trace) - })?; + let tree = parser.parse(input, None).map_err(|error| { + StringError(format!("Failed to parse input: {error:?}")).to_error(context) + })?; - context - .log - .collect_syntax_errors(input, &tree, &file, root.reference.clone()); + let root = crate::compile(&file, input, &tree).map_err(|error| { + StringError(format!("Failed to compile: {error}")).to_error(context) + })?; - root - }; + context + .log + .collect_syntax_errors(input, &tree, &file, root.reference.clone()); - let context = ExecutionContext { - working_directory: parent_dir, - import_limit: context.import_limit - 1, - ..context.clone() - }; + root + }; + + let context = ExecutionContext { + working_directory: parent_dir, + import_limit: context.import_limit - 1, + ..context.clone() + }; - execute_expression(&context, &root) + execute_expression(&context, &root) + }, + ) } pub mod functions { @@ -779,6 +798,10 @@ mod test { #[test] fn infinite_recursion_import() { - test_run("std.import(path = \"./test_assets/infinite_recursion_import.ccm\")").unwrap_err(); + let error = test_run("std.import(path = \"./test_assets/infinite_recursion_import.ccm\")") + .unwrap_err(); + let error = error.ty.as_any(); + let error: &FileRunError = error.downcast_ref().unwrap(); + assert_eq!(*error, FileRunError::ImportDepth); } } diff --git a/interpreter/src/execution/stack.rs b/interpreter/src/execution/stack.rs index e517d0f..562c854 100644 --- a/interpreter/src/execution/stack.rs +++ b/interpreter/src/execution/stack.rs @@ -19,7 +19,7 @@ use crate::execution::logging::StackTrace; use super::{ - errors::{ErrorType, ExpressionResult, Raise}, + errors::{ExecutionResult, Raise}, logging::LocatedStr, values::Value, }; @@ -57,7 +57,7 @@ impl<'p> StackScope<'p> { mode: ScopeType, variables: HashMap, block: B, - ) -> ExpressionResult + ) -> ExecutionResult where B: FnOnce(&Self, &StackTrace) -> R, { @@ -79,7 +79,7 @@ impl<'p> StackScope<'p> { mode: ScopeType, variables: HashMap, block: B, - ) -> ExpressionResult + ) -> ExecutionResult where B: FnOnce(&mut Self, &StackTrace) -> R, { @@ -145,7 +145,7 @@ impl<'p> StackScope<'p> { stack_trace: &StackTrace, local_variables: impl IntoIterator, name: S, - ) -> ExpressionResult<&Value> { + ) -> ExecutionResult<&Value> { let name = name.into(); let mut scope_iterator = self.iter_visible_scopes(); @@ -172,7 +172,11 @@ impl<'p> StackScope<'p> { variable_name: ImString::from(name.string), suggestions: self.suggest_similar_names(local_variables, name.string), } - .to_error(stack_trace.iter().chain([&name.location]))) + .to_error(stack_trace.iter().chain([&StackTrace { + parent: None, + reference: name.location.clone(), + failure_message: None, + }]))) } } } @@ -204,7 +208,7 @@ struct NotInScopeError { suggestions: Vec, } -impl ErrorType for NotInScopeError {} +impl std::error::Error for NotInScopeError {} impl Display for NotInScopeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/interpreter/src/execution/store.rs b/interpreter/src/execution/store.rs index 0b760e4..1d659dc 100644 --- a/interpreter/src/execution/store.rs +++ b/interpreter/src/execution/store.rs @@ -25,7 +25,7 @@ use sha2::{Digest, Sha256}; use tempfile::{NamedTempFile, TempDir}; use crate::{ - execution::errors::{ErrorType, ExpressionResult, Raise}, + execution::errors::{ExecutionResult, Raise}, ExecutionContext, }; @@ -44,38 +44,42 @@ impl Store { context: &ExecutionContext, hashable: &impl std::hash::Hash, name: impl AsRef, - init: impl FnOnce(&mut NamedTempFile) -> ExpressionResult<()>, - ) -> ExpressionResult { - let store_path = self.generate_store_path(hashable, &name); - - if std::fs::exists(&store_path) - .map_err(|error| IoError(error).to_error(context.stack_trace))? - { - Ok(store_path) - } else { - // TODO should we be creating these in the project directory to increase the chances of - // them being on the same filesystem as the store? - let mut asset = PendingAsset { - store_path, - asset: NamedTempFile::new() - .map_err(|error| IoError(error).to_error(context.stack_trace))?, - }; - init(&mut asset.asset)?; - - let (mut file, temp_path) = asset - .asset - .keep() - .map_err(|error| IoError(error.error).to_error(context.stack_trace))?; - - // Make sure that file is flushed and closed. - file.flush() - .map_err(|error| IoError(error).to_error(context.stack_trace))?; - drop(file); - - self.move_path_into_store(context, &temp_path, &asset.store_path)?; - - Ok(asset.store_path) - } + init: impl FnOnce(&mut NamedTempFile) -> ExecutionResult<()>, + ) -> ExecutionResult { + let name = name.as_ref(); + + context.trace_scope( + Some(format!("Failed to fetch or create file {name} in store").into()), + context.stack_trace.bottom().clone(), + |context| { + let store_path = self.generate_store_path(hashable, name); + + if std::fs::exists(&store_path).map_err(|error| error.to_error(context))? { + Ok(store_path) + } else { + // TODO should we be creating these in the project directory to increase the chances of + // them being on the same filesystem as the store? + let mut asset = PendingAsset { + store_path, + asset: NamedTempFile::new().map_err(|error| error.to_error(context))?, + }; + init(&mut asset.asset)?; + + let (mut file, temp_path) = asset + .asset + .keep() + .map_err(|error| error.error.to_error(context))?; + + // Make sure that file is flushed and closed. + file.flush().map_err(|error| error.to_error(context))?; + drop(file); + + self.move_path_into_store(context, &temp_path, &asset.store_path)?; + + Ok(asset.store_path) + } + }, + ) } pub fn get_or_init_directory( @@ -83,28 +87,33 @@ impl Store { context: &ExecutionContext, hashable: &impl std::hash::Hash, name: impl AsRef, - init: impl FnOnce(&mut TempDir) -> ExpressionResult<()>, - ) -> ExpressionResult { - let store_path = self.generate_store_path(hashable, &name); - - if std::fs::exists(&store_path) - .map_err(|error| IoError(error).to_error(context.stack_trace))? - { - Ok(store_path) - } else { - // TODO should we be creating these in the project directory to increase the chances of - // them being on the same filesystem as the store? - let mut asset = PendingAsset { - store_path, - asset: TempDir::new() - .map_err(|error| IoError(error).to_error(context.stack_trace))?, - }; - init(&mut asset.asset)?; - let temp_path = asset.asset.keep(); - self.move_path_into_store(context, &temp_path, &asset.store_path)?; - - Ok(asset.store_path) - } + init: impl FnOnce(&mut TempDir) -> ExecutionResult<()>, + ) -> ExecutionResult { + let name = name.as_ref(); + + context.trace_scope( + Some(format!("Failed to fetch or create directory {name} in store").into()), + context.stack_trace.bottom().clone(), + |context| { + let store_path = self.generate_store_path(hashable, name); + + if std::fs::exists(&store_path).map_err(|error| error.to_error(context))? { + Ok(store_path) + } else { + // TODO should we be creating these in the project directory to increase the chances of + // them being on the same filesystem as the store? + let mut asset = PendingAsset { + store_path, + asset: TempDir::new().map_err(|error| error.to_error(context))?, + }; + init(&mut asset.asset)?; + let temp_path = asset.asset.keep(); + self.move_path_into_store(context, &temp_path, &asset.store_path)?; + + Ok(asset.store_path) + } + }, + ) } fn generate_store_path( @@ -129,7 +138,7 @@ impl Store { context: &ExecutionContext, temp_path: &Path, store_path: &Path, - ) -> ExpressionResult<()> { + ) -> ExecutionResult<()> { // Move the file into the store. match std::fs::rename(temp_path, store_path) { Ok(_) => Ok(store_path), @@ -157,7 +166,7 @@ impl Store { result } } - .map_err(|error| IoError(error).to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; Ok(()) } @@ -183,17 +192,6 @@ impl std::ops::DerefMut for PendingAsset { } } -#[derive(Debug)] -pub struct IoError(pub std::io::Error); - -impl ErrorType for IoError {} - -impl std::fmt::Display for IoError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - struct StoreHasher(Sha256); impl std::hash::Hasher for StoreHasher { diff --git a/interpreter/src/execution/values/boolean.rs b/interpreter/src/execution/values/boolean.rs index 2bb9fec..ba51a63 100644 --- a/interpreter/src/execution/values/boolean.rs +++ b/interpreter/src/execution/values/boolean.rs @@ -22,7 +22,7 @@ use crate::execution::{ ExecutionContext, }; -use super::{value_type::ValueType, ExpressionResult, Object, StaticTypeName, Value}; +use super::{value_type::ValueType, ExecutionResult, Object, StaticTypeName, Value}; use std::borrow::Cow; @@ -60,26 +60,26 @@ impl Object for Boolean { write!(f, "{}", self.0) } - fn eq(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn eq(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(self.0 == rhs.0) } - fn and(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn and(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(Self(self.0 && rhs.0).into()) } - fn or(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn or(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(Self(self.0 || rhs.0).into()) } - fn xor(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn xor(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(Self((self.0 && rhs.0) || (!self.0 && !rhs.0)).into()) } - fn unary_not(self, _context: &ExecutionContext) -> ExpressionResult { + fn unary_not(self, _context: &ExecutionContext) -> ExecutionResult { Ok(Self(!self.0).into()) } } diff --git a/interpreter/src/execution/values/closure.rs b/interpreter/src/execution/values/closure.rs index 2180a0d..24eefd5 100644 --- a/interpreter/src/execution/values/closure.rs +++ b/interpreter/src/execution/values/closure.rs @@ -25,7 +25,7 @@ use crate::{ compile::{AstNode, ClosureDefinition, Expression}, execute_expression, execution::{ - errors::{ExpressionResult, Raise}, + errors::{ExecutionResult, Raise}, find_all_variable_accesses_in_expression, logging::{LocatedStr, LogLevel, LogMessage}, stack::ScopeType, @@ -114,8 +114,8 @@ impl Display for Signature { pub fn find_all_variable_accesses_in_closure_capture( closure: &crate::compile::ClosureDefinition, - mut access_collector: &mut dyn FnMut(&AstNode) -> ExpressionResult<()>, -) -> ExpressionResult<()> { + mut access_collector: &mut dyn FnMut(&AstNode) -> ExecutionResult<()>, +) -> ExecutionResult<()> { let argument_names: Vec<&ImString> = { let mut argument_names = Vec::with_capacity(closure.argument_type.node.members.len()); @@ -163,18 +163,21 @@ impl UserClosure { pub fn from_ast( context: &ExecutionContext, source: &AstNode>, - ) -> ExpressionResult { - let argument_type = - context.trace_scope(source.node.argument_type.reference.clone(), |context| { + ) -> ExecutionResult { + let argument_type = context.trace_scope( + None, + source.node.argument_type.reference.clone(), + |context| { let argument_type = StructDefinition::new(context, &source.node.argument_type)?; Ok(argument_type) - })?; + }, + )?; let return_type = - context.trace_scope(source.node.return_type.reference.clone(), |context| { + context.trace_scope(None, source.node.return_type.reference.clone(), |context| { execute_expression(context, &source.node.return_type)? - .downcast::(context.stack_trace) + .downcast::(context) })?; let signature = Arc::new(Signature { @@ -244,12 +247,12 @@ impl Object for UserClosure { write!(f, "{}", self.get_type(context)) } - fn call(&self, context: &ExecutionContext, argument: Dictionary) -> ExpressionResult { + fn call(&self, context: &ExecutionContext, argument: Dictionary) -> ExecutionResult { self.data .signature .argument_type .check_other_qualifies(argument.struct_def()) - .map_err(|error| error.to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; let argument = self.data.signature.argument_type.fill_defaults(argument); @@ -266,7 +269,7 @@ impl Object for UserClosure { .signature .return_type .check_other_qualifies(&result.get_type(context)) - .map_err(|error| error.to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; Ok(result) })? @@ -280,7 +283,7 @@ impl StaticTypeName for UserClosure { } pub trait BuiltinCallable: Sync + Send { - fn call(&self, context: &ExecutionContext, argument: Dictionary) -> ExpressionResult; + fn call(&self, context: &ExecutionContext, argument: Dictionary) -> ExecutionResult; fn name(&self) -> &str; @@ -400,7 +403,7 @@ macro_rules! build_function_callable { ($name:literal ($context:ident: &ExecutionContext $(, $($arg:ident: $ty:path $(= $default:expr)?),+)?) -> $return_type:ty $code:block) => {{ struct BuiltFunction where - F: Fn(&$crate::execution::ExecutionContext, &$crate::execution::values::closure::Signature, $crate::values::Dictionary) -> $crate::execution::ExpressionResult<$crate::execution::values::Value> + F: Fn(&$crate::execution::ExecutionContext, &$crate::execution::values::closure::Signature, $crate::values::Dictionary) -> $crate::execution::ExecutionResult<$crate::execution::values::Value> { function: F, signature: std::sync::Arc<$crate::execution::values::closure::Signature>, @@ -409,13 +412,13 @@ macro_rules! build_function_callable { impl $crate::execution::values::closure::BuiltinCallable for BuiltFunction where - F: Fn(&$crate::execution::ExecutionContext, &$crate::execution::values::closure::Signature, $crate::values::Dictionary) -> $crate::execution::ExpressionResult<$crate::execution::values::Value> + Send + Sync, + F: Fn(&$crate::execution::ExecutionContext, &$crate::execution::values::closure::Signature, $crate::values::Dictionary) -> $crate::execution::ExecutionResult<$crate::execution::values::Value> + Send + Sync, { fn call( &self, context: &$crate::execution::ExecutionContext, argument: $crate::execution::values::Dictionary, - ) -> $crate::execution::errors::ExpressionResult<$crate::execution::values::Value> { + ) -> $crate::execution::errors::ExecutionResult<$crate::execution::values::Value> { (self.function)(context, &self.signature, argument) } @@ -433,20 +436,20 @@ macro_rules! build_function_callable { $context: &$crate::execution::ExecutionContext, signature: &$crate::execution::values::closure::Signature, argument: $crate::execution::values::Dictionary - | -> $crate::execution::ExpressionResult<$crate::execution::values::Value> { + | -> $crate::execution::ExecutionResult<$crate::execution::values::Value> { use $crate::execution::errors::Raise as _; signature .argument_type .check_other_qualifies(argument.struct_def()) - .map_err(|error| error.to_error($context.stack_trace.iter()))?; + .map_err(|error| error.to_error($context))?; // Argument is potentially unused if we take no arguments. let mut _argument = signature.argument_type.fill_defaults(argument); let _data = std::sync::Arc::make_mut(&mut _argument.data); $($(let $arg: $ty = _data.members.remove(stringify!($arg)) - .expect("Argument was not present after argument check.").downcast::<$ty>($context.stack_trace)?;)*)? + .expect("Argument was not present after argument check.").downcast::<$ty>($context)?;)*)? let result: $return_type = { $code? @@ -477,7 +480,7 @@ macro_rules! build_method_callable { ) => {{ struct BuiltFunction where - F: Fn(&$crate::execution::ExecutionContext, &$crate::execution::values::closure::Signature, $crate::values::Dictionary) -> $crate::execution::ExpressionResult<$crate::execution::values::Value> + F: Fn(&$crate::execution::ExecutionContext, &$crate::execution::values::closure::Signature, $crate::values::Dictionary) -> $crate::execution::ExecutionResult<$crate::execution::values::Value> { function: F, signature: std::sync::Arc<$crate::execution::values::closure::Signature>, @@ -486,13 +489,13 @@ macro_rules! build_method_callable { impl $crate::execution::values::closure::BuiltinCallable for BuiltFunction where - F: Fn(&$crate::execution::ExecutionContext, &$crate::execution::values::closure::Signature, $crate::values::Dictionary) -> $crate::execution::ExpressionResult<$crate::execution::values::Value> + Send + Sync, + F: Fn(&$crate::execution::ExecutionContext, &$crate::execution::values::closure::Signature, $crate::values::Dictionary) -> $crate::execution::ExecutionResult<$crate::execution::values::Value> + Send + Sync, { fn call( &self, context: &$crate::execution::ExecutionContext, argument: $crate::execution::values::Dictionary, - ) -> $crate::execution::errors::ExpressionResult<$crate::execution::values::Value> { + ) -> $crate::execution::errors::ExecutionResult<$crate::execution::values::Value> { (self.function)(context, &self.signature, argument) } @@ -510,7 +513,7 @@ macro_rules! build_method_callable { $context: &$crate::execution::ExecutionContext, signature: &$crate::execution::values::closure::Signature, argument: $crate::execution::values::Dictionary - | -> $crate::execution::ExpressionResult<$crate::execution::values::Value> { + | -> $crate::execution::ExecutionResult<$crate::execution::values::Value> { use $crate::execution::errors::Raise as _; let $this = $context.get_variable( @@ -518,19 +521,19 @@ macro_rules! build_method_callable { location: $context.stack_trace.bottom().clone(), string: "self", }, - )?.downcast_ref::<$this_type>($context.stack_trace)?.clone(); + )?.downcast_ref::<$this_type>($context)?.clone(); signature .argument_type .check_other_qualifies(argument.struct_def()) - .map_err(|error| error.to_error($context.stack_trace.iter()))?; + .map_err(|error| error.to_error($context))?; // Argument is potentially unused if we take no arguments. let mut _argument = signature.argument_type.fill_defaults(argument); let _data = std::sync::Arc::make_mut(&mut _argument.data); $($(let $arg: $ty = _data.members.remove(stringify!($arg)) - .expect("Argument was not present after argument check.").downcast::<$ty>($context.stack_trace)?;)*)? + .expect("Argument was not present after argument check.").downcast::<$ty>($context)?;)*)? let result: $return_type = { $code? @@ -600,7 +603,7 @@ impl Object for BuiltinFunction { context.database.get_callable(self.0).callable.scope_type() } - fn call(&self, context: &ExecutionContext, argument: Dictionary) -> ExpressionResult { + fn call(&self, context: &ExecutionContext, argument: Dictionary) -> ExecutionResult { context .database .get_callable(self.0) @@ -617,9 +620,12 @@ impl StaticTypeName for BuiltinFunction { #[cfg(test)] mod test { use super::*; - use crate::execution::{ - test_context_custom_database, test_run, - values::{self, SignedInteger, StructMember, UnsignedInteger}, + use crate::{ + execution::{ + test_context_custom_database, test_run, + values::{self, SignedInteger, StructMember, UnsignedInteger}, + }, + values::value_type::{MissmatchedField, TypeQualificationError}, }; use hashable_map::HashableMap; use pretty_assertions::assert_eq; @@ -660,18 +666,41 @@ mod test { #[test] fn call_closure_bad_args() { - test_run( + let error = test_run( "let my_function = (a: std.types.UInt) -> std.types.UInt: a + 2u; in my_function(a = 3i)", ) .unwrap_err(); + let error = error.ty.as_any(); + let error: &TypeQualificationError = error.downcast_ref().unwrap(); + assert_eq!( + *error, + TypeQualificationError::Fields { + failed_feilds: vec![MissmatchedField { + name: "a".into(), + error: TypeQualificationError::This { + expected: ValueType::UnsignedInteger, + got: ValueType::SignedInteger, + }, + },], + } + ); } #[test] fn call_closure_bad_result() { - test_run( + let error = test_run( "let my_function = (a: std.types.UInt) -> std.types.UInt: \"test\"; in my_function(a = 3u)", ) .unwrap_err(); + let error = error.ty.as_any(); + let error: &TypeQualificationError = error.downcast_ref().unwrap(); + assert_eq!( + *error, + TypeQualificationError::This { + expected: ValueType::UnsignedInteger, + got: ValueType::String + } + ); } #[test] @@ -936,7 +965,7 @@ mod test { this: Dictionary, to_add: UnsignedInteger ) -> UnsignedInteger { - let value: UnsignedInteger = this.get_attribute(context, "value")?.downcast(context.stack_trace)?; + let value: UnsignedInteger = this.get_attribute(context, "value")?.downcast(context)?; Ok(values::UnsignedInteger::from(value.0 + to_add.0)) } diff --git a/interpreter/src/execution/values/constraint_set.rs b/interpreter/src/execution/values/constraint_set.rs index ddb0b80..837017b 100644 --- a/interpreter/src/execution/values/constraint_set.rs +++ b/interpreter/src/execution/values/constraint_set.rs @@ -30,7 +30,7 @@ use crate::{ AstNode, }, execution::{ - errors::{ErrorType, ExpressionResult, GenericFailure, Raise}, + errors::{ExecutionResult, Raise, StrError, StringError}, logging::LocatedStr, }, values::{ @@ -50,15 +50,15 @@ use std::{ pub fn find_all_captured_variables_in_constraint_set( constraint_set: &AstConstraintSet, - access_collector: &mut dyn FnMut(&AstNode) -> ExpressionResult<()>, -) -> ExpressionResult<()> { + access_collector: &mut dyn FnMut(&AstNode) -> ExecutionResult<()>, +) -> ExecutionResult<()> { use crate::compile::constraint_set; fn search_expression( variables: &Vec>, expression: &constraint_set::ConstraintSetExpression, - access_collector: &mut dyn FnMut(&AstNode) -> ExpressionResult<()>, - ) -> ExpressionResult<()> { + access_collector: &mut dyn FnMut(&AstNode) -> ExecutionResult<()>, + ) -> ExecutionResult<()> { match expression { ConstraintSetExpression::Parenthesis(expression) => { search_expression(variables, &expression.node, access_collector) @@ -220,7 +220,7 @@ impl ConstraintSet { pub fn from_ast( context: &ExecutionContext, source: &AstNode>, - ) -> ExpressionResult { + ) -> ExecutionResult { let mut variables = HashSet::new(); let mut duplicate_variables = Vec::new(); for variable in source.node.variables.iter().map(|field| &field.node) { @@ -233,7 +233,7 @@ impl ConstraintSet { return Err(DuplicateVariablesError { variables: duplicate_variables, } - .to_error(context.stack_trace)); + .to_error(context)); } let mut captured_values = HashMap::new(); @@ -323,17 +323,13 @@ impl Object for ConstraintSet { Ok(()) } - fn get_attribute( - &self, - context: &ExecutionContext, - attribute: &str, - ) -> ExpressionResult { + fn get_attribute(&self, context: &ExecutionContext, attribute: &str) -> ExecutionResult { match attribute { "solve" => Ok(BuiltinFunction::new::().into()), _ => Err(MissingAttributeError { name: attribute.into(), } - .to_error(context.stack_trace)), + .to_error(context)), } } } @@ -356,7 +352,7 @@ impl ConstraintSet { &self, context: &ExecutionContext, provided: Dictionary, - ) -> ExpressionResult { + ) -> ExecutionResult { let value_provider = ValueProvider { provided: &provided, captured: &self.captured_values, @@ -387,11 +383,10 @@ impl ConstraintSet { Self::build_relation(&mut m, &self.source.relation, left, right); let solution = m .solve() - .map_err(|error| SolverError(error).to_error(context.stack_trace))?; + .map_err(|error| SolverError(error).to_error(context))?; let dimension = dimension.ok_or_else(|| { - GenericFailure("Could not determine dimension of constraint set".into()) - .to_error(context.stack_trace) + StrError("Could not determine dimension of constraint set").to_error(context) })?; let mut members = HashMap::new(); @@ -402,7 +397,7 @@ impl ConstraintSet { variable_name, Scalar { dimension, - value: Float::new(value).unwrap_not_nan(context.stack_trace)?, + value: Float::new(value).unwrap_not_nan(context)?, } .into(), ); @@ -440,7 +435,7 @@ impl ConstraintSet { variables: &mut HashMap, dimension: &mut Option, expression: &ConstraintSetExpression, - ) -> ExpressionResult { + ) -> ExecutionResult { match expression { ConstraintSetExpression::Parenthesis(ast_node) => Self::model_expression( context, @@ -456,12 +451,12 @@ impl ConstraintSet { value: ast_node.node.value, }; - context.trace_scope(ast_node.reference.clone(), |context| { + context.trace_scope(None, ast_node.reference.clone(), |context| { Self::build_scalar(context, dimension, value) }) } ConstraintSetExpression::Identifier(ast_node) => { - context.trace_scope(ast_node.reference.clone(), |context| { + context.trace_scope(None, ast_node.reference.clone(), |context| { let name = &ast_node.node; if let Some(value) = value_provider .get(name) @@ -471,14 +466,11 @@ impl ConstraintSet { Value::Scalar(scalar) => { Self::build_scalar(context, dimension, *scalar) } - value => Err(GenericFailure( - format!( - "{} types are not supported in constraint sets", - value.type_name() - ) - .into(), - ) - .to_error(context.stack_trace)), + value => Err(StringError(format!( + "{} types are not supported in constraint sets", + value.type_name() + )) + .to_error(context)), } } else { let variable = variables @@ -529,11 +521,9 @@ impl ConstraintSet { } } ConstraintSetExpression::MethodCall(ast_node) => { - context.trace_scope(ast_node.reference.clone(), |context| { - Err( - GenericFailure("Methods are not yet supported in constraint sets".into()) - .to_error(context.stack_trace), - ) + context.trace_scope(None, ast_node.reference.clone(), |context| { + Err(StrError("Methods are not yet supported in constraint sets") + .to_error(context)) }) } } @@ -543,13 +533,13 @@ impl ConstraintSet { context: &ExecutionContext, dimension: &mut Option, value: Scalar, - ) -> ExpressionResult { + ) -> ExecutionResult { if let Some(dimension) = dimension { if value.dimension != Dimension::zero() && *dimension != value.dimension { - return Err(GenericFailure( - "All measurements in constraint set must be of the same dimension".into(), + return Err(StrError( + "All measurements in constraint set must be of the same dimension", ) - .to_error(context.stack_trace)); + .to_error(context)); } } else if value.dimension != Dimension::zero() { *dimension = Some(value.dimension); @@ -570,7 +560,7 @@ struct DuplicateVariablesError { pub variables: Vec, } -impl ErrorType for DuplicateVariablesError {} +impl std::error::Error for DuplicateVariablesError {} impl std::fmt::Display for DuplicateVariablesError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -596,7 +586,7 @@ impl std::fmt::Display for DuplicateVariablesError { #[derive(Debug)] struct SolverError(selen::api::SolverError); -impl ErrorType for SolverError {} +impl std::error::Error for SolverError {} impl std::fmt::Display for SolverError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -614,17 +604,13 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { } impl BuiltinCallable for BuiltFunction { - fn call( - &self, - context: &ExecutionContext, - argument: Dictionary, - ) -> ExpressionResult { + fn call(&self, context: &ExecutionContext, argument: Dictionary) -> ExecutionResult { let this = context .get_variable(LocatedStr { location: context.stack_trace.bottom().clone(), string: "self", })? - .downcast_ref::(context.stack_trace)? + .downcast_ref::(context)? .clone(); let solution = this.solve(context, argument)?; diff --git a/interpreter/src/execution/values/dictionary.rs b/interpreter/src/execution/values/dictionary.rs index 1a8decc..22ef3fa 100644 --- a/interpreter/src/execution/values/dictionary.rs +++ b/interpreter/src/execution/values/dictionary.rs @@ -26,13 +26,14 @@ use crate::{ compile::{AstNode, DictionaryConstruction}, execute_expression, execution::{ - errors::{ErrorType, ExpressionResult, Raise as _}, + errors::{ExecutionResult, Raise as _}, find_all_variable_accesses_in_expression, stack::ScopeType, values::string::formatting::Style, ExecutionContext, }, values::StaticType, + StackTrace, }; use super::{ @@ -53,8 +54,8 @@ impl PartialEq for DictionaryData { pub fn find_all_variable_accesses_in_dictionary_construction( dictionary_construction: &crate::compile::DictionaryConstruction, - access_collector: &mut dyn FnMut(&AstNode) -> ExpressionResult<()>, -) -> ExpressionResult<()> { + access_collector: &mut dyn FnMut(&AstNode) -> ExecutionResult<()>, +) -> ExecutionResult<()> { for assignment in dictionary_construction.assignments.iter() { find_all_variable_accesses_in_expression( &assignment.node.assignment.node, @@ -99,18 +100,14 @@ impl Object for Dictionary { Ok(()) } - fn get_attribute( - &self, - context: &ExecutionContext, - attribute: &str, - ) -> ExpressionResult { + fn get_attribute(&self, context: &ExecutionContext, attribute: &str) -> ExecutionResult { if let Some(member) = self.data.members.get(attribute) { Ok(member.clone()) } else { Err(MissingAttributeError { name: attribute.into(), } - .to_error(context.stack_trace)) + .to_error(context)) } } } @@ -141,7 +138,7 @@ impl Dictionary { pub fn from_ast( context: &ExecutionContext, ast_node: &AstNode, - ) -> ExpressionResult { + ) -> ExecutionResult { let mut members = HashMap::with_capacity(ast_node.node.assignments.len()); context.stack.scope_mut( @@ -172,7 +169,11 @@ impl Dictionary { if members.insert(name.clone(), value.clone()).is_some() { // That's a duplicate member. return Err(DuplicateMemberError { name }.to_error( - context.stack_trace.iter().chain([&ast_node.reference]), + context.stack_trace.iter().chain([&StackTrace { + parent: None, + reference: ast_node.reference.clone(), + failure_message: None, + }]), )); } @@ -225,7 +226,7 @@ pub struct DuplicateMemberError { pub name: ImString, } -impl ErrorType for DuplicateMemberError {} +impl std::error::Error for DuplicateMemberError {} impl Display for DuplicateMemberError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -268,12 +269,18 @@ mod test { #[test] fn duplicate_entries() { // Two values of the same name is not allowed. - test_run("(void = (), void = ())").unwrap_err(); + let error = test_run("(void = (), void = ())").unwrap_err(); + let error = error.ty.as_any(); + let error: &DuplicateMemberError = error.downcast_ref().unwrap(); + assert_eq!(error.name, "void"); } #[test] fn non_existant_member() { - test_run("(void = ()).does_not_exist").unwrap_err(); + let error = test_run("(void = ()).does_not_exist").unwrap_err(); + let error = error.ty.as_any(); + let error: &MissingAttributeError = error.downcast_ref().unwrap(); + assert_eq!(error.name, "does_not_exist"); } #[test] diff --git a/interpreter/src/execution/values/file.rs b/interpreter/src/execution/values/file.rs index 771e5c7..39eceef 100644 --- a/interpreter/src/execution/values/file.rs +++ b/interpreter/src/execution/values/file.rs @@ -23,7 +23,6 @@ use imstr::ImString; use crate::{ build_method, execution::{ - errors::GenericFailure, logging::{LogLevel, LogMessage}, values::{ string::formatting::Style, BuiltinCallableDatabase, IString, Object, StaticTypeName, @@ -102,10 +101,12 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: File ) -> IString { - let content = std::fs::read_to_string(this.path.as_path()) - .map_err(|error| GenericFailure(format!("Failed to read file to string: {error:?}").into()).to_error(context.stack_trace))?; + context.trace_scope(Some("Failed to read file to string".into()), context.stack_trace.bottom().clone(), |context| { + let content = std::fs::read_to_string(this.path.as_path()) + .map_err(|error| error.to_error(context))?; - Ok(IString(ImString::from(content))) + Ok(IString(ImString::from(content))) + }) } ); } diff --git a/interpreter/src/execution/values/integer.rs b/interpreter/src/execution/values/integer.rs index b270c19..8a22731 100644 --- a/interpreter/src/execution/values/integer.rs +++ b/interpreter/src/execution/values/integer.rs @@ -30,7 +30,7 @@ use std::{ use crate::{ build_function, execution::{ - errors::{ExpressionResult, GenericFailure, Raise}, + errors::{ExecutionResult, Raise, StrError}, logging::{LogLevel, LogMessage, StackTrace}, values::{ closure::BuiltinCallableDatabase, integer::methods::MethodSet, @@ -117,103 +117,89 @@ where } } - fn bit_and(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn bit_and(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(Self(self.0 & rhs.0).into()) } - fn bit_or(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn bit_or(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(Self(self.0 | rhs.0).into()) } - fn bit_xor(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn bit_xor(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(Self(self.0 ^ rhs.0).into()) } - fn cmp(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn cmp(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(self.0.cmp(&rhs.0)) } - fn addition(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn addition(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(Self(self.0.checked_add(&rhs.0).ok_or_else(|| { - GenericFailure( - "Integer overflow: The computed value is too large to store in the integer".into(), - ) - .to_error(context.stack_trace) + StrError("Integer overflow: The computed value is too large to store in the integer") + .to_error(context) })?) .into()) } - fn subtraction(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn subtraction(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(Self(self.0.checked_sub(&rhs.0).ok_or_else(|| { - GenericFailure( - "Integer underflow: The computed value is too small to store in the integer".into(), - ) - .to_error(context.stack_trace) + StrError("Integer underflow: The computed value is too small to store in the integer") + .to_error(context) })?) .into()) } - fn multiply(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn multiply(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(Self(self.0.checked_mul(&rhs.0).ok_or_else(|| { - GenericFailure( - "Integer overflow: The computed value is too large to store in the integer".into(), - ) - .to_error(context.stack_trace) + StrError("Integer overflow: The computed value is too large to store in the integer") + .to_error(context) })?) .into()) } - fn divide(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn divide(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(Self( self.0 .checked_div(&rhs.0) - .ok_or_else(|| GenericFailure("The computed value is either too large to store in the integer or you attempted to divide by zero".into()).to_error(context.stack_trace))?, + .ok_or_else(|| StrError("The computed value is either too large to store in the integer or you attempted to divide by zero").to_error(context))?, ) .into()) } - fn exponent(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn exponent(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; // This failure can only happen on 32bit (or less) systems. let rhs = rhs.0.to_usize().ok_or_else(|| { - GenericFailure( - "Integer overflow: The requested exponent is larger than the host machine word size".into(), - ) - .to_error(context.stack_trace) + StrError("Integer overflow: The requested exponent is larger than the host machine word size") + .to_error(context) })?; Ok(Self(checked_pow(self.0, rhs).ok_or_else(|| { - GenericFailure( - "Integer overflow: The computed value is too large to store in the integer".into(), - ) - .to_error(context.stack_trace) + StrError("Integer overflow: The computed value is too large to store in the integer") + .to_error(context) })?) .into()) } - fn left_shift(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn left_shift(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(Self(self.0 << rhs.0).into()) } - fn right_shift(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn right_shift(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(Self(self.0 >> rhs.0).into()) } - fn unary_plus(self, _context: &ExecutionContext) -> ExpressionResult { + fn unary_plus(self, _context: &ExecutionContext) -> ExecutionResult { Ok(self.into()) } - fn unary_minus(self, context: &ExecutionContext) -> ExpressionResult { + fn unary_minus(self, context: &ExecutionContext) -> ExecutionResult { self.0.neg(context.stack_trace) } - fn unary_not(self, _context: &ExecutionContext) -> ExpressionResult { + fn unary_not(self, _context: &ExecutionContext) -> ExecutionResult { Ok(Self(!self.0).into()) } - fn get_attribute( - &self, - context: &ExecutionContext, - attribute: &str, - ) -> ExpressionResult { + fn get_attribute(&self, context: &ExecutionContext, attribute: &str) -> ExecutionResult { match attribute { "count_ones" => Ok(BuiltinFunction::new::< <::MethodSet as methods::MethodSet>::CountOnes, @@ -282,7 +268,7 @@ where _ => Err(MissingAttributeError { name: attribute.into(), } - .to_error(context.stack_trace)), + .to_error(context)), } } } @@ -326,7 +312,7 @@ trait IntOps: { type MethodSet: MethodSet + 'static; - fn neg(&self, stack_trace: &StackTrace) -> ExpressionResult; + fn neg(&self, stack_trace: &StackTrace) -> ExecutionResult; fn count_ones(&self) -> u64; fn count_zeros(&self) -> u64; @@ -360,7 +346,7 @@ trait IntOps: impl IntOps for i64 { type MethodSet = methods::SignedIntegerMethodSet; - fn neg(&self, _stack_trace: &StackTrace) -> ExpressionResult { + fn neg(&self, _stack_trace: &StackTrace) -> ExecutionResult { Ok(SignedInteger::from(-self).into()) } @@ -451,7 +437,7 @@ impl StaticType for i64 { impl IntOps for u64 { type MethodSet = methods::UnsignedIntegerMethodSet; - fn neg(&self, stack_trace: &StackTrace) -> ExpressionResult { + fn neg(&self, stack_trace: &StackTrace) -> ExecutionResult { Err(super::UnsupportedOperationError { type_name: UnsignedInteger::static_type_name(), operation_name: "negate", @@ -808,15 +794,15 @@ pub fn register_methods_and_functions(database: &mut BuiltinCallableDatabase) { let end = end.0; if inclusive && reverse && end == 0 { - return Err(GenericFailure("Range ending will be less than zero, which is not possible for an unsigned integer".into()).to_error(context.stack_trace)); + return Err(StrError("Range ending will be less than zero, which is not possible for an unsigned integer").to_error(context)); } if reverse { if start < end { - return Err(GenericFailure("Start cannot be less than end when iterating a reversed range".into()).to_error(context.stack_trace)); + return Err(StrError("Start cannot be less than end when iterating a reversed range").to_error(context)); } } else if start > end { - return Err(GenericFailure("Start cannot be greater than end when iterating a range".into()).to_error(context.stack_trace)); + return Err(StrError("Start cannot be greater than end when iterating a range").to_error(context)); } Ok(ValueIterator::new(Range { start, end, inclusive, reverse })) @@ -838,10 +824,10 @@ pub fn register_methods_and_functions(database: &mut BuiltinCallableDatabase) { if reverse { if start < end { - return Err(GenericFailure("Start cannot be less than end when iterating a reversed range".into()).to_error(context.stack_trace)); + return Err(StrError("Start cannot be less than end when iterating a reversed range").to_error(context)); } } else if start > end { - return Err(GenericFailure("Start cannot be greater than end when iterating a range".into()).to_error(context.stack_trace)); + return Err(StrError("Start cannot be greater than end when iterating a range").to_error(context)); } Ok(ValueIterator::new(Range { start, end, inclusive, reverse })) @@ -867,8 +853,8 @@ where { fn iterate( &self, - callback: impl FnOnce(&mut dyn Iterator) -> ExpressionResult, - ) -> ExpressionResult { + callback: impl FnOnce(&mut dyn Iterator) -> ExecutionResult, + ) -> ExecutionResult { // We had to implement a lot of this manually due to std::range::Step not being stable yet. let mut index = self.start; let end = match (self.inclusive, self.reverse) { @@ -907,7 +893,10 @@ where #[cfg(test)] mod test { - use crate::execution::{test_run, values::Boolean}; + use crate::{ + execution::{test_run, values::Boolean}, + values::UnsupportedOperationError, + }; use super::*; @@ -1155,7 +1144,10 @@ mod test { #[test] fn unsigned_unary_minus() { - test_run("-3u").unwrap_err(); + let error = test_run("-3u").unwrap_err(); + let error = error.ty.as_any(); + let error: &UnsupportedOperationError = error.downcast_ref().unwrap(); + assert_eq!(error.operation_name, "negate"); } #[test] diff --git a/interpreter/src/execution/values/iterators.rs b/interpreter/src/execution/values/iterators.rs index d4eb8ba..2b86576 100644 --- a/interpreter/src/execution/values/iterators.rs +++ b/interpreter/src/execution/values/iterators.rs @@ -31,7 +31,7 @@ use crate::{ MissingAttributeError, Object, StaticType, StaticTypeName, Style, UnsignedInteger, Value, ValueNone, ValueType, }, - ExecutionContext, ExpressionResult, + ExecutionContext, ExecutionResult, }; use itertools::Itertools; @@ -40,8 +40,8 @@ use itertools::Itertools; pub trait IterableObject { fn iterate( &self, - callback: impl FnOnce(&mut dyn Iterator) -> ExpressionResult, - ) -> ExpressionResult; + callback: impl FnOnce(&mut dyn Iterator) -> ExecutionResult, + ) -> ExecutionResult; } #[allow(clippy::enum_variant_names)] // They're struct names, not just enum varients. @@ -111,11 +111,7 @@ impl Object for ValueIterator { write!(f, ")") } - fn get_attribute( - &self, - context: &ExecutionContext, - attribute: &str, - ) -> ExpressionResult { + fn get_attribute(&self, context: &ExecutionContext, attribute: &str) -> ExecutionResult { match attribute { "chunks" => Ok(BuiltinFunction::new::().into()), "chunks_exact" => Ok(BuiltinFunction::new::().into()), @@ -151,7 +147,7 @@ impl Object for ValueIterator { _ => Err(MissingAttributeError { name: attribute.into(), } - .to_error(context.stack_trace)), + .to_error(context)), } } } @@ -173,8 +169,8 @@ impl StaticType for ValueIterator { type IterateCallback<'s, R> = Box< dyn FnOnce( &ExecutionContext, - &mut dyn Iterator>, - ) -> ExpressionResult + &mut dyn Iterator>, + ) -> ExecutionResult + 's, >; @@ -190,7 +186,7 @@ impl ValueIterator { &'s self, context: &ExecutionContext, callback: IterateCallback<'s, R>, - ) -> ExpressionResult { + ) -> ExecutionResult { self.source.iterate(move |iterator| { let mut stages = self.stages.iter(); let iterator = &mut iterator.map(Ok); @@ -237,16 +233,16 @@ impl IteratorStage { &self, context: &ExecutionContext, stage_iter: &mut dyn Iterator, - iterator: &mut dyn Iterator>, + iterator: &mut dyn Iterator>, callback: impl FnOnce( &ExecutionContext, - &mut dyn Iterator>, - ) -> ExpressionResult, - ) -> ExpressionResult { + &mut dyn Iterator>, + ) -> ExecutionResult, + ) -> ExecutionResult { let start_next_stage = move |iterator: &mut dyn Iterator< - Item = ExpressionResult, + Item = ExecutionResult, >| - -> ExpressionResult { + -> ExecutionResult { if let Some(next_stage) = stage_iter.next() { next_stage.process::(context, stage_iter, iterator, callback) } else { @@ -259,7 +255,7 @@ impl IteratorStage { IteratorStage::Chunks { size } => { let chunks = iterator.chunks(*size); let mut iterator = chunks.into_iter().map(|chunk| { - let buffer: Vec<_> = chunk.collect::>()?; + let buffer: Vec<_> = chunk.collect::>()?; let list = List::from_iter(context, buffer); Ok(list.into()) }); @@ -269,7 +265,7 @@ impl IteratorStage { IteratorStage::ChunksExact { size } => { let chunks = iterator.chunks(*size); let mut iterator = chunks.into_iter().filter_map(|chunk| { - let collection: ExpressionResult> = chunk.collect(); + let collection: ExecutionResult> = chunk.collect(); match collection { Ok(buffer) => { @@ -294,7 +290,7 @@ impl IteratorStage { }), ), IteratorStage::Cycle { count } => { - let data: Vec<_> = iterator.collect::>()?; + let data: Vec<_> = iterator.collect::>()?; let mut iterator = &mut std::iter::repeat_n(data.iter(), *count) .flatten() .cloned() @@ -345,7 +341,7 @@ impl IteratorStage { HashMap::from_iter([("c".into(), value.clone())]), ), ) - .and_then(|value| value.downcast::(context.stack_trace)); + .and_then(|value| value.downcast::(context)); match result { Ok(keep) => { @@ -396,14 +392,14 @@ impl IteratorStage { fn chain_iterator( context: &ExecutionContext, start_next_stage: impl FnOnce( - &mut dyn Iterator>, - ) -> ExpressionResult, - previous_iterator: &mut dyn Iterator>, - iterators: &mut dyn Iterator>, - ) -> ExpressionResult { + &mut dyn Iterator>, + ) -> ExecutionResult, + previous_iterator: &mut dyn Iterator>, + iterators: &mut dyn Iterator>, + ) -> ExecutionResult { if let Some(result) = iterators.next() { let value = result?; - let sub_iterator: ValueIterator = value.downcast(context.stack_trace)?; + let sub_iterator: ValueIterator = value.downcast(context)?; sub_iterator.iterate( context, @@ -425,7 +421,7 @@ impl IteratorStage { chain_iterator(context, start_next_stage, &mut [].into_iter(), iterator) } IteratorStage::Map { map } => { - let iterator = &mut iterator.map(|result| -> ExpressionResult { + let iterator = &mut iterator.map(|result| -> ExecutionResult { let value = result?; map.call( context, @@ -478,7 +474,7 @@ impl IteratorStage { HashMap::from_iter([("c".into(), value.clone())]), ), ) - .and_then(|value| value.downcast::(context.stack_trace))? + .and_then(|value| value.downcast::(context))? .0; if !should_skip { @@ -509,7 +505,7 @@ impl IteratorStage { HashMap::from_iter([("c".into(), value.clone())]), ), ) - .and_then(|value| value.downcast::(context.stack_trace)); + .and_then(|value| value.downcast::(context)); match result { Ok(should_continue) => { @@ -806,7 +802,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { HashMap::from_iter([("c".into(), value.clone())]), ), ) - .and_then(|value| value.downcast::(context.stack_trace))?; + .and_then(|value| value.downcast::(context))?; if !passes.0 { return Ok(Boolean(false)); @@ -838,7 +834,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { HashMap::from_iter([("c".into(), value.clone())]), ), ) - .and_then(|value| value.downcast::(context.stack_trace))?; + .and_then(|value| value.downcast::(context))?; if passes.0 { return Ok(Boolean(true)); @@ -856,7 +852,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { this: ValueIterator ) -> List { this.iterate(context, Box::new(|_context, iterator| { - let values = iterator.collect::>>()?; + let values = iterator.collect::>>()?; Ok(List::from_iter(context, values.into_iter())) })) } @@ -872,7 +868,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { for value in iterator { let value = value?; - let string: IString = value.downcast(context.stack_trace)?; + let string: IString = value.downcast(context)?; collected += string.0.as_str(); } diff --git a/interpreter/src/execution/values/list.rs b/interpreter/src/execution/values/list.rs index 51fe883..b85da32 100644 --- a/interpreter/src/execution/values/list.rs +++ b/interpreter/src/execution/values/list.rs @@ -21,7 +21,7 @@ use crate::{ compile::{AstNode, Expression}, execute_expression, execution::{ - errors::{Error, ErrorType, GenericFailure, Raise as _}, + errors::{Error, Raise as _, StrError}, values::{ closure::BuiltinCallableDatabase, string::formatting::Style, Boolean, BuiltinFunction, Dictionary, MissingAttributeError, StaticType, UnsignedInteger, ValueNone, @@ -31,7 +31,7 @@ use crate::{ values::iterators::{IterableObject, ValueIterator}, }; -use super::{value_type::ValueType, ExpressionResult, Object, StaticTypeName, Value}; +use super::{value_type::ValueType, ExecutionResult, Object, StaticTypeName, Value}; use std::{borrow::Cow, cmp::Ordering, collections::HashMap, sync::Arc}; @@ -51,8 +51,8 @@ impl List { pub fn from_ast( context: &ExecutionContext, ast_node: &AstNode>>, - ) -> ExpressionResult { - let values: ExpressionResult> = ast_node + ) -> ExecutionResult { + let values: ExecutionResult> = ast_node .node .par_iter() .map(|expression| execute_expression(context, expression)) @@ -87,8 +87,8 @@ impl List { &self, context: &ExecutionContext, operation_name: &'static str, - mut operation: impl FnMut(&Value) -> ExpressionResult, - ) -> ExpressionResult { + mut operation: impl FnMut(&Value) -> ExecutionResult, + ) -> ExecutionResult { let values: Vec = self .values .iter() @@ -101,7 +101,7 @@ impl List { }) }) .collect::>() - .map_err(|error| error.to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; Ok(Self::from_iter(context, values)) } @@ -110,8 +110,8 @@ impl List { &self, context: &ExecutionContext, operation_name: &'static str, - operation: impl FnMut(&Value) -> ExpressionResult, - ) -> ExpressionResult { + operation: impl FnMut(&Value) -> ExecutionResult, + ) -> ExecutionResult { self.map_raw(context, operation_name, operation) .map(|value| value.into()) } @@ -145,16 +145,12 @@ impl Object for List { Ok(()) } - fn eq(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn eq(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(self.values == rhs.values) } - fn get_attribute( - &self, - context: &ExecutionContext, - attribute: &str, - ) -> ExpressionResult { + fn get_attribute(&self, context: &ExecutionContext, attribute: &str) -> ExecutionResult { match attribute { "append" => Ok(BuiltinFunction::new::().into()), "slice" => Ok(BuiltinFunction::new::().into()), @@ -180,86 +176,86 @@ impl Object for List { _ => Err(MissingAttributeError { name: attribute.into(), } - .to_error(context.stack_trace)), + .to_error(context)), } } - fn and(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn and(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { self.map_operation(context, "and", move |value| { value.clone().and(context, rhs.clone()) }) } - fn or(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn or(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { self.map_operation(context, "or", move |value| { value.clone().or(context, rhs.clone()) }) } - fn xor(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn xor(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { self.map_operation(context, "xor", move |value| { value.clone().xor(context, rhs.clone()) }) } - fn bit_and(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn bit_and(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { self.map_operation(context, "bit and", move |value| { value.clone().bit_and(context, rhs.clone()) }) } - fn bit_or(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn bit_or(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { self.map_operation(context, "bit or", move |value| { value.clone().bit_or(context, rhs.clone()) }) } - fn bit_xor(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn bit_xor(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { self.map_operation(context, "bit xor", move |value| { value.clone().bit_xor(context, rhs.clone()) }) } - fn addition(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn addition(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { self.map_operation(context, "addition", move |value| { value.clone().addition(context, rhs.clone()) }) } - fn subtraction(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn subtraction(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { self.map_operation(context, "subtraction", move |value| { value.clone().subtraction(context, rhs.clone()) }) } - fn multiply(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn multiply(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { self.map_operation(context, "multiply", move |value| { value.clone().multiply(context, rhs.clone()) }) } - fn divide(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn divide(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { self.map_operation(context, "divide", move |value| { value.clone().divide(context, rhs.clone()) }) } - fn exponent(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn exponent(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { self.map_operation(context, "exponent", move |value| { value.clone().exponent(context, rhs.clone()) }) } - fn left_shift(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn left_shift(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { self.map_operation(context, "left shift", move |value| { value.clone().left_shift(context, rhs.clone()) }) } - fn right_shift(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn right_shift(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { self.map_operation(context, "right shift", move |value| { value.clone().right_shift(context, rhs.clone()) }) } - fn unary_plus(self, context: &ExecutionContext) -> ExpressionResult { + fn unary_plus(self, context: &ExecutionContext) -> ExecutionResult { self.map_operation(context, "unary plus", move |value| { value.clone().unary_plus(context) }) } - fn unary_minus(self, context: &ExecutionContext) -> ExpressionResult { + fn unary_minus(self, context: &ExecutionContext) -> ExecutionResult { self.map_operation(context, "unary minus", move |value| { value.clone().unary_minus(context) }) } - fn unary_not(self, context: &ExecutionContext) -> ExpressionResult { + fn unary_not(self, context: &ExecutionContext) -> ExecutionResult { self.map_operation(context, "unary not", move |value| { value.clone().unary_not(context) }) @@ -286,8 +282,8 @@ pub struct ListIterator { impl IterableObject for ListIterator { fn iterate( &self, - callback: impl FnOnce(&mut dyn Iterator) -> ExpressionResult, - ) -> ExpressionResult { + callback: impl FnOnce(&mut dyn Iterator) -> ExecutionResult, + ) -> ExecutionResult { let mut iter = self.list.values.iter().cloned(); callback(&mut iter) } @@ -301,8 +297,8 @@ pub struct ListReverseIterator { impl IterableObject for ListReverseIterator { fn iterate( &self, - callback: impl FnOnce(&mut dyn Iterator) -> ExpressionResult, - ) -> ExpressionResult { + callback: impl FnOnce(&mut dyn Iterator) -> ExecutionResult, + ) -> ExecutionResult { let mut iter = self.list.values.iter().rev().cloned(); callback(&mut iter) } @@ -315,7 +311,7 @@ struct OperationMappingError { pub error: Error, } -impl ErrorType for OperationMappingError {} +impl std::error::Error for OperationMappingError {} impl std::fmt::Display for OperationMappingError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -332,7 +328,7 @@ struct SortingError { pub errors: Vec, } -impl ErrorType for SortingError {} +impl std::error::Error for SortingError {} impl std::fmt::Display for SortingError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -424,7 +420,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { if let Some(slice) = slice { Ok(List::from_iter(context, slice.iter().cloned())) } else { - Err(GenericFailure("Slice out of range".into()).to_error(context.stack_trace)) + Err(StrError("Slice out of range").to_error(context)) } } ); @@ -440,7 +436,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { if let Some(slice) = slice { Ok(slice.clone()) } else { - Err(GenericFailure("Index out of range".into()).to_error(context.stack_trace)) + Err(StrError("Index out of range").to_error(context)) } } ); @@ -485,7 +481,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { "c".into(), value.clone() ) - ])))?.downcast::(context.stack_trace)?; + ])))?.downcast::(context)?; if retain.0 { values.push(value.clone()); @@ -517,7 +513,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { if errors.is_empty() { Ok(List::from_iter(context, values.into_iter())) } else { - Err(SortingError { errors }.to_error(context.stack_trace)) + Err(SortingError { errors }.to_error(context)) } } ); diff --git a/interpreter/src/execution/values/manifold_mesh.rs b/interpreter/src/execution/values/manifold_mesh.rs index ecd26ef..55ee132 100644 --- a/interpreter/src/execution/values/manifold_mesh.rs +++ b/interpreter/src/execution/values/manifold_mesh.rs @@ -26,10 +26,7 @@ use std::{ use crate::{ build_function, build_method, - execution::{ - errors::{ExpressionResult, GenericFailure, Raise}, - store::IoError, - }, + execution::errors::{ExecutionResult, Raise, StrError}, values::{ scalar::{Length, UnwrapNotNan}, vector::{Length3, Zero3}, @@ -82,7 +79,7 @@ impl Object for ManifoldMesh3D { ) } - fn addition(mut self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn addition(mut self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { let input = Self::unpack_arithmetic_input(context, 0.0, rhs)?; match input { ArethmeticInput::Vector(vector) => { @@ -92,16 +89,14 @@ impl Object for ManifoldMesh3D { Ok(self.into()) } ArethmeticInput::Manifold(rhs) => { - let manifold = - compute_boolean(&self.0, &rhs.0, OpType::Add).map_err(|message| { - GenericFailure(message.into()).to_error(context.stack_trace) - })?; + let manifold = compute_boolean(&self.0, &rhs.0, OpType::Add) + .map_err(|error| error.to_error(context))?; Ok(Self(Arc::new(manifold)).into()) } } } - fn subtraction(mut self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn subtraction(mut self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { let input = Self::unpack_arithmetic_input(context, 0.0, rhs)?; match input { ArethmeticInput::Vector(vector) => { @@ -111,66 +106,60 @@ impl Object for ManifoldMesh3D { Ok(self.into()) } ArethmeticInput::Manifold(rhs) => { - let manifold = - compute_boolean(&self.0, &rhs.0, OpType::Subtract).map_err(|message| { - GenericFailure(message.into()).to_error(context.stack_trace) - })?; + let manifold = compute_boolean(&self.0, &rhs.0, OpType::Subtract) + .map_err(|error| error.to_error(context))?; Ok(Self(Arc::new(manifold)).into()) } } } - fn multiply(mut self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let input = rhs.downcast::(context.stack_trace)?; + fn multiply(mut self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let input = rhs.downcast::(context)?; let vector = input.raw_value(); let manifold = Arc::make_mut(&mut self.0); manifold.scale(vector.x, vector.y, vector.z); Ok(self.into()) } - fn bit_or(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn bit_or(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; let manifold = compute_boolean(&self.0, &rhs.0, OpType::Add) - .map_err(|message| GenericFailure(message.into()).to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; Ok(Self(Arc::new(manifold)).into()) } - fn bit_xor(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn bit_xor(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; // To compute xor, get the intersectiona and then subtract it from the union of the two // shapes. let intersection = compute_boolean(&self.0, &rhs.0, OpType::Intersect) - .map_err(|message| GenericFailure(message.into()).to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; let union = compute_boolean(&self.0, &rhs.0, OpType::Add) - .map_err(|message| GenericFailure(message.into()).to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; let difference = compute_boolean(&union, &intersection, OpType::Subtract) - .map_err(|message| GenericFailure(message.into()).to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; Ok(Self(Arc::new(difference)).into()) } - fn bit_and(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn bit_and(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; let manifold = compute_boolean(&self.0, &rhs.0, OpType::Intersect) - .map_err(|message| GenericFailure(message.into()).to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; Ok(Self(Arc::new(manifold)).into()) } - fn get_attribute( - &self, - context: &ExecutionContext, - attribute: &str, - ) -> ExpressionResult { + fn get_attribute(&self, context: &ExecutionContext, attribute: &str) -> ExecutionResult { match attribute { "to_stl" => Ok(BuiltinFunction::new::().into()), _ => Err(MissingAttributeError { name: attribute.into(), } - .to_error(context.stack_trace)), + .to_error(context)), } } } @@ -185,7 +174,7 @@ impl ManifoldMesh3D { context: &ExecutionContext, default: RawFloat, input: Value, - ) -> ExpressionResult { + ) -> ExecutionResult { let value = match input { Value::Vector2(v) => { let raw = v.raw_value(); @@ -201,7 +190,7 @@ impl ManifoldMesh3D { expected: "Vector2 or Vector3 of lengths, or another ManifoldMesh3D".into(), got: value.get_type(context).name(), } - .to_error(context.stack_trace)), + .to_error(context)), }?; match value { @@ -213,7 +202,7 @@ impl ManifoldMesh3D { expected: "Vector2 or Vector3 of lengths, or another ManifoldMesh3D".into(), got: vector.get_type(context).name(), } - .to_error(context.stack_trace)) + .to_error(context)) } else { Ok(ArethmeticInput::Vector(vector)) } @@ -251,21 +240,20 @@ fn unpack_radius( context: &ExecutionContext, radius: Option, diameter: Option, -) -> ExpressionResult { +) -> ExecutionResult { match (radius, diameter) { (Some(radius), None) => Ok(radius.into()), (None, Some(diameter)) => { let diameter: RawFloat = diameter.into(); Ok(diameter / 2.0) } - (Some(_), Some(_)) => Err(GenericFailure( - "You must provide the radius or the diameter, not both".into(), - ) - .to_error(context.stack_trace)), - (None, None) => Err(GenericFailure( - "You must provide the radius or the diameter, neither were provided".into(), + (Some(_), Some(_)) => { + Err(StrError("You must provide the radius or the diameter, not both").to_error(context)) + } + (None, None) => Err(StrError( + "You must provide the radius or the diameter, neither were provided", ) - .to_error(context.stack_trace)), + .to_error(context)), } } @@ -283,7 +271,7 @@ pub fn register_methods_and_functions(database: &mut BuiltinCallableDatabase) { let radius = unpack_radius(context, radius, diameter)?; let manifold = generate_cone(apex.into(), center.into(), radius, divide.0 as usize) - .map_err(|error| GenericFailure(error.into()).to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; Ok(ManifoldMesh3D(Arc::new(manifold))) } ); @@ -296,7 +284,7 @@ pub fn register_methods_and_functions(database: &mut BuiltinCallableDatabase) { let size: Vector3 = size.into(); let size = size.raw_value(); - let mut manifold = generate_cube().map_err(|error| GenericFailure(error.into()).to_error(context.stack_trace))?; + let mut manifold = generate_cube().map_err(|error| error.to_error(context))?; manifold.scale(size.x, size.y, size.z); Ok(ManifoldMesh3D(Arc::new(manifold))) @@ -315,7 +303,7 @@ pub fn register_methods_and_functions(database: &mut BuiltinCallableDatabase) { let radius = unpack_radius(context, radius, diameter)?; let manifold = generate_cylinder(radius, height.into(), sectors.0 as usize, stacks.0 as usize) - .map_err(|error| GenericFailure(error.into()).to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; Ok(ManifoldMesh3D(Arc::new(manifold))) } ); @@ -330,7 +318,7 @@ pub fn register_methods_and_functions(database: &mut BuiltinCallableDatabase) { let scale = unpack_radius(context, radius, diameter)?; let mut manifold = generate_icosphere(subdivions.0 as u32) - .map_err(|error| GenericFailure(error.into()).to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; manifold.scale(scale, scale, scale); Ok(ManifoldMesh3D(Arc::new(manifold))) @@ -346,7 +334,7 @@ pub fn register_methods_and_functions(database: &mut BuiltinCallableDatabase) { sectors: UnsignedInteger ) -> ManifoldMesh3D { let manifold = generate_torus(major_radius.into(), minor_raidus.into(), rings.0 as usize, sectors.0 as usize) - .map_err(|error| GenericFailure(error.into()).to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; Ok(ManifoldMesh3D(Arc::new(manifold))) } ); @@ -362,7 +350,7 @@ pub fn register_methods_and_functions(database: &mut BuiltinCallableDatabase) { let scale = unpack_radius(context, radius, diameter)?; let mut manifold = generate_uv_sphere(sectors.0 as usize, stacks.0 as usize) - .map_err(|error| GenericFailure(error.into()).to_error(context.stack_trace))?; + .map_err(|error| error.to_error(context))?; manifold.scale(scale, scale, scale); Ok(ManifoldMesh3D(Arc::new(manifold))) @@ -381,84 +369,85 @@ pub fn register_methods_and_functions(database: &mut BuiltinCallableDatabase) { }.into(), ascii: Boolean = Boolean(false).into() ) -> File { - if !ascii.0 { - // Produce a binary STL - let mut mesh = Vec::new(); - - use stl_io::{Triangle, Vertex, write_stl}; + context.trace_scope(Some("Failed to write stl file".into()), context.stack_trace.bottom().clone(), |context| { + if !ascii.0 { + // Produce a binary STL + let mut mesh = Vec::new(); - for (normal, halfedge) in this.0.face_normals.iter().zip(this.0.hs.chunks(3)) { - let p0 = this.0.ps[halfedge[0].tail]; - let p1 = this.0.ps[halfedge[1].tail]; - let p2 = this.0.ps[halfedge[2].tail]; + use stl_io::{Triangle, Vertex, write_stl}; - let scale = 1.0 / *scale.value; + for (normal, halfedge) in this.0.face_normals.iter().zip(this.0.hs.chunks(3)) { + let p0 = this.0.ps[halfedge[0].tail]; + let p1 = this.0.ps[halfedge[1].tail]; + let p2 = this.0.ps[halfedge[2].tail]; - let triangle = Triangle { - normal: Vertex::new([(normal.x * scale) as f32, (normal.y * scale) as f32, (normal.z * scale) as f32]), - vertices: [Vertex::new([(p0.x * scale) as f32, (p0.y * scale) as f32, (p0.z * scale) as f32]), - Vertex::new([(p1.x * scale) as f32, (p1.y * scale) as f32, (p1.z * scale) as f32]), - Vertex::new([(p2.x * scale) as f32, (p2.y * scale) as f32, (p2.z * scale) as f32])] - }; + let scale = 1.0 / *scale.value; - mesh.push(triangle); - } + let triangle = Triangle { + normal: Vertex::new([(normal.x * scale) as f32, (normal.y * scale) as f32, (normal.z * scale) as f32]), + vertices: [Vertex::new([(p0.x * scale) as f32, (p0.y * scale) as f32, (p0.z * scale) as f32]), + Vertex::new([(p1.x * scale) as f32, (p1.y * scale) as f32, (p1.z * scale) as f32]), + Vertex::new([(p2.x * scale) as f32, (p2.y * scale) as f32, (p2.z * scale) as f32])] + }; - // This thing doesn't kick back IO errors, so we'll collect to an infaulable - // structure and then write that ourselves. - let mut serialized = Vec::new(); - write_stl(&mut serialized, mesh.iter()).map_err(|_| GenericFailure("Failed to serialize STL file".into()).to_error(context.stack_trace))?; + mesh.push(triangle); + } - let path = context.store.get_or_init_file(context, &(&this, &scale, "ascii"), format!("{}.stl", name.0), |file| { - file.write_all(&serialized).map_err(|error| IoError(error).to_error(context.stack_trace))?; + // This thing doesn't kick back IO errors, so we'll collect to an infaulable + // structure and then write that ourselves. + let mut serialized = Vec::new(); + write_stl(&mut serialized, mesh.iter()).map_err(|_| StrError("Failed to serialize STL file").to_error(context))?; - Ok(()) - })?; + let path = context.store.get_or_init_file(context, &(&this, &scale, "ascii"), format!("{}.stl", name.0), |file| { + file.write_all(&serialized).map_err(|error| error.to_error(context))?; - Ok(File { path: Arc::new(path) }) - } else { - let path = context.store.get_or_init_file(context, &(&this, &scale, "binary"), format!("{}.stl", name.0), |file| { - let mut file = BufWriter::new(file); - let scale = *Float::new(1.0 / *scale.value).unwrap_not_nan(context.stack_trace)?; + Ok(()) + })?; - // file.write_all(&serialized).map_err(|error| IoError(error).to_error(context.stack_trace))?; - let mut trampoline = || -> std::io::Result<()> { - writeln!(file, "solid {}", name.0)?; + Ok(File { path: Arc::new(path) }) + } else { + let path = context.store.get_or_init_file(context, &(&this, &scale, "binary"), format!("{}.stl", name.0), |file| { + let mut file = BufWriter::new(file); + let scale = *Float::new(1.0 / *scale.value).unwrap_not_nan(context)?; - for (normal, halfedge) in this.0.face_normals.iter().zip(this.0.hs.chunks(3)) { - let p0 = this.0.ps[halfedge[0].tail]; - let p1 = this.0.ps[halfedge[1].tail]; - let p2 = this.0.ps[halfedge[2].tail]; + let mut trampoline = || -> std::io::Result<()> { + writeln!(file, "solid {}", name.0)?; - writeln!(file, "\tfacet normal {} {} {}", normal.x, normal.y, normal.z)?; + for (normal, halfedge) in this.0.face_normals.iter().zip(this.0.hs.chunks(3)) { + let p0 = this.0.ps[halfedge[0].tail]; + let p1 = this.0.ps[halfedge[1].tail]; + let p2 = this.0.ps[halfedge[2].tail]; - { - writeln!(file, "\t\touter loop")?; + writeln!(file, "\tfacet normal {} {} {}", normal.x, normal.y, normal.z)?; { - writeln!(file, "\t\t\tvertex {} {} {}", p0.x * scale, p0.y * scale, p0.z * scale)?; - writeln!(file, "\t\t\tvertex {} {} {}", p1.x * scale, p1.y * scale, p1.z * scale)?; - writeln!(file, "\t\t\tvertex {} {} {}", p2.x * scale, p2.y * scale, p2.z * scale)?; + writeln!(file, "\t\touter loop")?; + + { + writeln!(file, "\t\t\tvertex {} {} {}", p0.x * scale, p0.y * scale, p0.z * scale)?; + writeln!(file, "\t\t\tvertex {} {} {}", p1.x * scale, p1.y * scale, p1.z * scale)?; + writeln!(file, "\t\t\tvertex {} {} {}", p2.x * scale, p2.y * scale, p2.z * scale)?; + } + + writeln!(file, "\t\tendloop")?; } - writeln!(file, "\t\tendloop")?; + writeln!(file, "\tendfacet")?; } - writeln!(file, "\tendfacet")?; - } - - writeln!(file, "endsolid {}", name.0)?; + writeln!(file, "endsolid {}", name.0)?; - Ok(()) - }; + Ok(()) + }; - trampoline().map_err(|error| IoError(error).to_error(context.stack_trace))?; + trampoline().map_err(|error| error.to_error(context))?; - Ok(()) - })?; + Ok(()) + })?; - Ok(File { path: Arc::new(path) }) - } + Ok(File { path: Arc::new(path) }) + } + }) } ); } diff --git a/interpreter/src/execution/values/mod.rs b/interpreter/src/execution/values/mod.rs index 2942fc0..7aacd16 100644 --- a/interpreter/src/execution/values/mod.rs +++ b/interpreter/src/execution/values/mod.rs @@ -23,11 +23,11 @@ use enum_downcast::{AsVariant, EnumDowncast, IntoVariant}; use unwrap_enum::EnumAs; use crate::{ - execution::{logging::StackTrace, stack::ScopeType, ExecutionContext}, + execution::{stack::ScopeType, ExecutionContext}, values::{iterators::ValueIterator, manifold_mesh::ManifoldMesh3D}, }; -use super::errors::{ErrorType, ExpressionResult, Raise as _}; +use super::errors::{ExecutionResult, Raise as _}; mod void; pub use void::ValueNone; @@ -130,7 +130,7 @@ struct UnsupportedOperationError { pub operation_name: &'static str, } -impl ErrorType for UnsupportedOperationError {} +impl std::error::Error for UnsupportedOperationError {} impl Display for UnsupportedOperationError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -145,14 +145,14 @@ impl Display for UnsupportedOperationError { impl UnsupportedOperationError { fn raise( object: &O, - stack_trace: &StackTrace, + context: &ExecutionContext, operation_name: &'static str, - ) -> ExpressionResult { + ) -> ExecutionResult { Err(Self { type_name: object.type_name(), operation_name, } - .to_error(stack_trace)) + .to_error(context)) } } @@ -161,7 +161,7 @@ struct MissingAttributeError { pub name: String, } -impl ErrorType for MissingAttributeError {} +impl std::error::Error for MissingAttributeError {} impl Display for MissingAttributeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -185,85 +185,81 @@ pub trait Object: StaticTypeName + Sized + Eq + PartialEq + Clone { Self::static_type_name() } - fn and(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "logical and") + fn and(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "logical and") } - fn or(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "logical or") + fn or(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "logical or") } - fn xor(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "logical xor") + fn xor(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "logical xor") } - fn bit_and(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "binary and") + fn bit_and(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "binary and") } - fn bit_or(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "binary or") + fn bit_or(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "binary or") } - fn bit_xor(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "binary xor") + fn bit_xor(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "binary xor") } - fn cmp(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "compare") + fn cmp(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "compare") } - fn eq(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { + fn eq(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { Ok(matches!(self.cmp(context, rhs)?, Ordering::Equal)) } - fn addition(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "addition") + fn addition(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "addition") } - fn subtraction(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "subtraction") + fn subtraction(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "subtraction") } - fn multiply(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "multiply") + fn multiply(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "multiply") } - fn divide(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "divide") + fn divide(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "divide") } - fn exponent(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "exponent") + fn exponent(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "exponent") } - fn left_shift(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "left shift") + fn left_shift(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "left shift") } - fn right_shift(self, context: &ExecutionContext, _rhs: Value) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "right shift") + fn right_shift(self, context: &ExecutionContext, _rhs: Value) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "right shift") } - fn get_attribute( - &self, - context: &ExecutionContext, - attribute: &str, - ) -> ExpressionResult { + fn get_attribute(&self, context: &ExecutionContext, attribute: &str) -> ExecutionResult { Err(MissingAttributeError { name: attribute.into(), } - .to_error(context.stack_trace)) + .to_error(context)) } fn call_scope_type(&self, _context: &ExecutionContext) -> ScopeType { ScopeType::Isolated } - fn call(&self, context: &ExecutionContext, _argument: Dictionary) -> ExpressionResult { - UnsupportedOperationError::raise(self, context.stack_trace, "call") + fn call(&self, context: &ExecutionContext, _argument: Dictionary) -> ExecutionResult { + UnsupportedOperationError::raise(self, context, "call") } - fn formula_call(&self, context: &ExecutionContext, _value: Value) -> ExpressionResult { - UnsupportedOperationError::raise(self, context.stack_trace, "inverse call") + fn formula_call(&self, context: &ExecutionContext, _value: Value) -> ExecutionResult { + UnsupportedOperationError::raise(self, context, "inverse call") } fn formula_inverse_call( &self, context: &ExecutionContext, _value: Value, - ) -> ExpressionResult { - UnsupportedOperationError::raise(self, context.stack_trace, "inverse call") + ) -> ExecutionResult { + UnsupportedOperationError::raise(self, context, "inverse call") } - fn unary_plus(self, context: &ExecutionContext) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "unary plus") + fn unary_plus(self, context: &ExecutionContext) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "unary plus") } - fn unary_minus(self, context: &ExecutionContext) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "unary minus") + fn unary_minus(self, context: &ExecutionContext) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "unary minus") } - fn unary_not(self, context: &ExecutionContext) -> ExpressionResult { - UnsupportedOperationError::raise(&self, context.stack_trace, "unary not") + fn unary_not(self, context: &ExecutionContext) -> ExecutionResult { + UnsupportedOperationError::raise(&self, context, "unary not") } // fn export( @@ -317,7 +313,7 @@ pub struct DowncastError { pub got: Cow<'static, str>, } -impl ErrorType for DowncastError {} +impl std::error::Error for DowncastError {} impl Display for DowncastError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -331,7 +327,7 @@ pub struct DowncastForBinaryOpError { pub got: Cow<'static, str>, } -impl ErrorType for DowncastForBinaryOpError {} +impl std::error::Error for DowncastForBinaryOpError {} impl Display for DowncastForBinaryOpError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -362,23 +358,23 @@ impl IntoVariant for Value { } impl Value { - pub fn downcast_for_binary_op_ref(&self, stack_trace: &StackTrace) -> ExpressionResult<&T> + pub fn downcast_for_binary_op_ref(&self, context: &ExecutionContext) -> ExecutionResult<&T> where T: StaticTypeName, Self: AsVariant, { - self.downcast_ref(stack_trace) + self.downcast_ref(context) } - pub fn downcast_for_binary_op(self, stack_trace: &StackTrace) -> ExpressionResult + pub fn downcast_for_binary_op(self, context: &ExecutionContext) -> ExecutionResult where T: StaticTypeName, Self: IntoVariant, { - self.downcast(stack_trace) + self.downcast(context) } - pub fn downcast_ref(&self, stack_trace: &StackTrace) -> ExpressionResult<&T> + pub fn downcast_ref(&self, context: &ExecutionContext) -> ExecutionResult<&T> where T: StaticTypeName, Self: AsVariant, @@ -390,11 +386,11 @@ impl Value { expected: T::static_type_name(), got: self.type_name(), } - .to_error(stack_trace)) + .to_error(context)) } } - pub fn downcast(self, stack_trace: &StackTrace) -> ExpressionResult + pub fn downcast(self, context: &ExecutionContext) -> ExecutionResult where T: StaticTypeName, Self: IntoVariant, @@ -405,18 +401,18 @@ impl Value { expected: T::static_type_name(), got: original.type_name(), } - .to_error(stack_trace)), + .to_error(context)), } } - pub fn downcast_optional(self, stack_trace: &StackTrace) -> ExpressionResult> + pub fn downcast_optional(self, context: &ExecutionContext) -> ExecutionResult> where T: StaticTypeName, Self: IntoVariant, { match self { Self::ValueNone(_) => Ok(None), - this => Ok(Some(this.downcast_for_binary_op::(stack_trace)?)), + this => Ok(Some(this.downcast_for_binary_op::(context)?)), } } } diff --git a/interpreter/src/execution/values/scalar.rs b/interpreter/src/execution/values/scalar.rs index be5540d..7765359 100644 --- a/interpreter/src/execution/values/scalar.rs +++ b/interpreter/src/execution/values/scalar.rs @@ -18,12 +18,13 @@ use std::{borrow::Cow, cmp::Ordering, f64::consts::PI}; use common_data_types::{Dimension, Float, FloatIsNan}; +use thiserror::Error; use crate::{ build_method, execution::{ - errors::{ExpressionResult, GenericFailure, Raise}, - logging::{LogLevel, LogMessage, StackTrace}, + errors::{ExecutionResult, Raise, StrError}, + logging::{LogLevel, LogMessage}, values::{ self, closure::BuiltinCallableDatabase, string::formatting::Style, Boolean, BuiltinFunction, MissingAttributeError, SignedInteger, StaticType, UnsignedInteger, @@ -37,21 +38,29 @@ use crate::{ use super::{value_type::ValueType, DowncastForBinaryOpError, Object, StaticTypeName, Value}; pub trait UnwrapNotNan: Sized { - fn unwrap_not_nan(self, stack_trace: &StackTrace) -> ExpressionResult; + fn unwrap_not_nan(self, context: &ExecutionContext) -> ExecutionResult; } impl UnwrapNotNan for std::result::Result { - fn unwrap_not_nan(self, stack_trace: &StackTrace) -> ExpressionResult { + fn unwrap_not_nan(self, context: &ExecutionContext) -> ExecutionResult { match self { Ok(number) => Ok(number), - Err(_float_is_nan) => Err(GenericFailure( - "Result of arithmetic operation is NaN".into(), - ) - .to_error(stack_trace)), + Err(_float_is_nan) => Err(ResultIsNan.to_error(context)), } } } +#[derive(Debug, Eq, PartialEq)] +pub struct ResultIsNan; + +impl std::error::Error for ResultIsNan {} + +impl std::fmt::Display for ResultIsNan { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Result of operation is NaN") + } +} + #[derive(Debug, Hash, Clone, Copy, Eq, PartialEq)] pub struct Scalar { pub dimension: Dimension, @@ -147,38 +156,36 @@ impl Object for Scalar { } } - fn addition(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs = self.unpack_same_dimension(context.stack_trace, rhs)?; + fn addition(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs = self.unpack_same_dimension(context, rhs)?; - let value = Float::new(*self.value + *rhs.value).unwrap_not_nan(context.stack_trace)?; + let value = Float::new(*self.value + *rhs.value).unwrap_not_nan(context)?; Ok(Self { value, ..self }.into()) } - fn subtraction(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs = self.unpack_same_dimension(context.stack_trace, rhs)?; + fn subtraction(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs = self.unpack_same_dimension(context, rhs)?; - let value = Float::new(*self.value - *rhs.value).unwrap_not_nan(context.stack_trace)?; + let value = Float::new(*self.value - *rhs.value).unwrap_not_nan(context)?; Ok(Self { value, ..self }.into()) } - fn multiply(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs = rhs.downcast_for_binary_op_ref::(context.stack_trace)?; - self.multiply_by_scalar(context.stack_trace, rhs) - .map(|rhs| rhs.into()) + fn multiply(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs = rhs.downcast_for_binary_op_ref::(context)?; + self.multiply_by_scalar(context, rhs).map(|rhs| rhs.into()) } - fn divide(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs = rhs.downcast_for_binary_op_ref::(context.stack_trace)?; - self.divide_by_measurement(context.stack_trace, rhs) + fn divide(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs = rhs.downcast_for_binary_op_ref::(context)?; + self.divide_by_measurement(context, rhs) .map(|rhs| rhs.into()) } - fn exponent(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs = rhs.downcast::(context.stack_trace)?; + fn exponent(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs = rhs.downcast::(context)?; if rhs.dimension == Dimension::zero() { Ok(Scalar { dimension: self.dimension * *rhs.value as i8, - value: Float::new(self.value.powf(*rhs.value)) - .unwrap_not_nan(context.stack_trace)?, + value: Float::new(self.value.powf(*rhs.value)).unwrap_not_nan(context)?, } .into()) } else { @@ -186,21 +193,21 @@ impl Object for Scalar { expected: ValueType::Scalar(Some(Dimension::zero())).name(), got: rhs.get_type(context).name(), } - .to_error(context.stack_trace)) + .to_error(context)) } } - fn unary_plus(self, _context: &ExecutionContext) -> ExpressionResult { + fn unary_plus(self, _context: &ExecutionContext) -> ExecutionResult { Ok(self.into()) } - fn unary_minus(self, _context: &ExecutionContext) -> ExpressionResult { + fn unary_minus(self, _context: &ExecutionContext) -> ExecutionResult { Ok(Self { value: -self.value, ..self } .into()) } - fn cmp(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs = rhs.downcast_for_binary_op_ref::(context.stack_trace)?; + fn cmp(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs = rhs.downcast_for_binary_op_ref::(context)?; if self.dimension == rhs.dimension { Ok(std::cmp::Ord::cmp(&self.value, &rhs.value)) } else { @@ -208,15 +215,11 @@ impl Object for Scalar { expected: self.type_name(), got: rhs.type_name(), } - .to_error(context.stack_trace)) + .to_error(context)) } } - fn get_attribute( - &self, - context: &ExecutionContext, - attribute: &str, - ) -> ExpressionResult { + fn get_attribute(&self, context: &ExecutionContext, attribute: &str) -> ExecutionResult { match attribute { "to_signed_integer" => Ok(BuiltinFunction::new::().into()), "to_unsigned_integer" => { @@ -259,14 +262,14 @@ impl Object for Scalar { _ => Err(MissingAttributeError { name: attribute.into(), } - .to_error(context.stack_trace)), + .to_error(context)), } } // fn export( // &self, // _log: &dyn RuntimeLog, - // _stack_trace: &StackTrace, + // _context: &ExecutionContext, // ) -> ExpressionResult { // Ok(SerializableValue::Scalar(self.clone())) // } @@ -285,8 +288,8 @@ impl StaticType for Scalar { } impl Scalar { - fn multiply_by_scalar(&self, stack_trace: &StackTrace, rhs: &Self) -> ExpressionResult { - let value = Float::new(*self.value * *rhs.value).unwrap_not_nan(stack_trace)?; + fn multiply_by_scalar(&self, context: &ExecutionContext, rhs: &Self) -> ExecutionResult { + let value = Float::new(*self.value * *rhs.value).unwrap_not_nan(context)?; let dimension = self.dimension + rhs.dimension; Ok(Self { dimension, value }) @@ -294,35 +297,36 @@ impl Scalar { fn divide_by_measurement( &self, - stack_trace: &StackTrace, + context: &ExecutionContext, rhs: &Self, - ) -> ExpressionResult { - let value = Float::new(*self.value / *rhs.value).unwrap_not_nan(stack_trace)?; + ) -> ExecutionResult { + let value = Float::new(*self.value / *rhs.value).unwrap_not_nan(context)?; let dimension = self.dimension - rhs.dimension; Ok(Self { dimension, value }) } - fn check_inverse_trig_compatible(&self, stack_trace: &StackTrace) -> ExpressionResult<()> { + fn check_inverse_trig_compatible(&self, context: &ExecutionContext) -> ExecutionResult<()> { if self.dimension.is_zero_dimension() { Ok(()) } else { - Err(GenericFailure("Inverse trigonometric functions can only be used with zero dimensional types (Angles, Ratios)".into()).to_error(stack_trace)) + Err(StrError("Inverse trigonometric functions can only be used with zero dimensional types (Angles, Ratios)").to_error(context)) } } - fn check_trig_compatible(&self, stack_trace: &StackTrace) -> ExpressionResult<()> { + fn check_trig_compatible(&self, context: &ExecutionContext) -> ExecutionResult<()> { if self.dimension.is_zero_dimension() && self.dimension.ratio_type_hint.is_angle() { Ok(()) } else { - Err( - GenericFailure("Trigonometric functions can only be used with angles".into()) - .to_error(stack_trace), - ) + Err(StrError("Trigonometric functions can only be used with angles").to_error(context)) } } - fn unpack_same_dimension(self, stack_trace: &StackTrace, rhs: Value) -> ExpressionResult { + fn unpack_same_dimension( + self, + context: &ExecutionContext, + rhs: Value, + ) -> ExecutionResult { if let Value::Scalar(rhs) = rhs { if self.dimension == rhs.dimension { Ok(rhs) @@ -331,14 +335,14 @@ impl Scalar { expected: self.type_name(), got: rhs.type_name(), } - .to_error(stack_trace)) + .to_error(context)) } } else { Err(DowncastForBinaryOpError { expected: self.type_name(), got: rhs.type_name(), } - .to_error(stack_trace)) + .to_error(context)) } } } @@ -382,6 +386,15 @@ mod methods { pub struct Tanh; } +#[derive(Debug, Error)] +pub enum ScalarToInteger { + #[error("Only zero dimensional scalars can be converted into an integer")] + NonZeroDimensionToInteger, + + #[error("Negative values cannot be converted to unsigned integers")] + NegativeToUnsignedInteger, +} + pub fn register_methods(database: &mut BuiltinCallableDatabase) { build_method!( database, @@ -392,8 +405,8 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { if this.dimension.is_zero_dimension() { Ok(values::SignedInteger::from(*this.value as i64)) } else { - Err(GenericFailure("Only zero dimensional scalars can be converted into an integer".into()) - .to_error(context.stack_trace)) + Err(ScalarToInteger::NonZeroDimensionToInteger + .to_error(context)) } } ); @@ -407,12 +420,12 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { if *this.value >= 0.0 { Ok(values::UnsignedInteger::from(*this.value as u64)) } else { - Err(GenericFailure("Negative values cannot be converted to signed integers".into()) - .to_error(context.stack_trace)) + Err(ScalarToInteger::NegativeToUnsignedInteger + .to_error(context)) } } else { - Err(GenericFailure("Only zero dimensional scalars can be converted into an integer".into()) - .to_error(context.stack_trace)) + Err(ScalarToInteger::NonZeroDimensionToInteger + .to_error(context)) } } ); @@ -424,7 +437,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { { Ok(Scalar { dimension: this.dimension, - value: Float::new(this.value.abs()).unwrap_not_nan(context.stack_trace)? + value: Float::new(this.value.abs()).unwrap_not_nan(context)? }) } ); @@ -451,7 +464,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { { Ok(Scalar { dimension: Dimension::zero(), - value: Float::new(this.value.copysign(*sign.value)).unwrap_not_nan(context.stack_trace)? + value: Float::new(this.value.copysign(*sign.value)).unwrap_not_nan(context)? }) } ); @@ -464,7 +477,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { { Ok(Scalar { dimension: Dimension::zero(), - value: Float::new(this.value.hypot(*other.value)).unwrap_not_nan(context.stack_trace)? + value: Float::new(this.value.hypot(*other.value)).unwrap_not_nan(context)? }) } ); @@ -503,7 +516,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { { Ok(Scalar { dimension: this.dimension / 3, - value: Float::new(this.value.cbrt()).unwrap_not_nan(context.stack_trace)? + value: Float::new(this.value.cbrt()).unwrap_not_nan(context)? }) } ); @@ -516,7 +529,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { { Ok(Scalar { dimension: this.dimension * *exp.value as i8, - value: Float::new(this.value.powf(*exp.value)).unwrap_not_nan(context.stack_trace)? + value: Float::new(this.value.powf(*exp.value)).unwrap_not_nan(context)? }) } ); @@ -528,7 +541,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { { Ok(Scalar { dimension: this.dimension / 2, - value: Float::new(this.value.sqrt()).unwrap_not_nan(context.stack_trace)? + value: Float::new(this.value.sqrt()).unwrap_not_nan(context)? }) } ); @@ -558,7 +571,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { { Ok(Scalar { dimension: -this.dimension, - value: Float::new(this.value.recip()).unwrap_not_nan(context.stack_trace)? + value: Float::new(this.value.recip()).unwrap_not_nan(context)? }) } ); @@ -569,13 +582,13 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { this: Scalar, unit: Scalar) -> Scalar { - let unit = this.unpack_same_dimension(context.stack_trace, unit.into())?; + let unit = this.unpack_same_dimension(context, unit.into())?; let value = this.value / unit.value; Ok(Scalar { dimension: this.dimension, - value: Float::new(value.round() * *unit.value).unwrap_not_nan(context.stack_trace)? + value: Float::new(value.round() * *unit.value).unwrap_not_nan(context)? }) } ); @@ -590,7 +603,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { Ok(Scalar { dimension: this.dimension, - value: Float::new(value.trunc() * *unit.value).unwrap_not_nan(context.stack_trace)? + value: Float::new(value.trunc() * *unit.value).unwrap_not_nan(context)? }) } ); @@ -605,7 +618,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { Ok(Scalar { dimension: this.dimension, - value: Float::new(value.fract() * *unit.value).unwrap_not_nan(context.stack_trace)? + value: Float::new(value.fract() * *unit.value).unwrap_not_nan(context)? }) } ); @@ -620,7 +633,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { Ok(Scalar { dimension: this.dimension, - value: Float::new(value.floor() * *unit.value).unwrap_not_nan(context.stack_trace)? + value: Float::new(value.floor() * *unit.value).unwrap_not_nan(context)? }) } ); @@ -635,7 +648,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { Ok(Scalar { dimension: this.dimension, - value: Float::new(value.ceil() * *unit.value).unwrap_not_nan(context.stack_trace)? + value: Float::new(value.ceil() * *unit.value).unwrap_not_nan(context)? }) } ); @@ -673,7 +686,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { { Ok(Scalar { dimension: Dimension::zero(), - value: Float::new(this.value.signum()).unwrap_not_nan(context.stack_trace)? + value: Float::new(this.value.signum()).unwrap_not_nan(context)? }) } ); @@ -683,11 +696,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: Scalar) -> Scalar { - this.check_inverse_trig_compatible(context.stack_trace)?; + this.check_inverse_trig_compatible(context)?; Ok(Scalar { dimension: Dimension::angle(), - value: Float::new((this.value * PI).acos()).unwrap_not_nan(context.stack_trace)? + value: Float::new((this.value * PI).acos()).unwrap_not_nan(context)? }) } ); @@ -697,11 +710,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: Scalar) -> Scalar { - this.check_inverse_trig_compatible(context.stack_trace)?; + this.check_inverse_trig_compatible(context)?; Ok(Scalar { dimension: Dimension::angle(), - value: Float::new((this.value).acosh() / PI).unwrap_not_nan(context.stack_trace)? + value: Float::new((this.value).acosh() / PI).unwrap_not_nan(context)? }) } ); @@ -711,11 +724,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: Scalar) -> Scalar { - this.check_trig_compatible(context.stack_trace)?; + this.check_trig_compatible(context)?; Ok(Scalar { dimension: Dimension::zero(), - value: Float::new((this.value * PI).cos()).unwrap_not_nan(context.stack_trace)? + value: Float::new((this.value * PI).cos()).unwrap_not_nan(context)? }) } ); @@ -725,11 +738,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: Scalar) -> Scalar { - this.check_trig_compatible(context.stack_trace)?; + this.check_trig_compatible(context)?; Ok(Scalar { dimension: Dimension::zero(), - value: Float::new((this.value * PI).cosh()).unwrap_not_nan(context.stack_trace)? + value: Float::new((this.value * PI).cosh()).unwrap_not_nan(context)? }) } ); @@ -739,11 +752,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: Scalar) -> Scalar { - this.check_inverse_trig_compatible(context.stack_trace)?; + this.check_inverse_trig_compatible(context)?; Ok(Scalar { dimension: Dimension::angle(), - value: Float::new((this.value * PI).asin()).unwrap_not_nan(context.stack_trace)? + value: Float::new((this.value * PI).asin()).unwrap_not_nan(context)? }) } ); @@ -753,11 +766,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: Scalar) -> Scalar { - this.check_inverse_trig_compatible(context.stack_trace)?; + this.check_inverse_trig_compatible(context)?; Ok(Scalar { dimension: Dimension::angle(), - value: Float::new((this.value).asinh() / PI).unwrap_not_nan(context.stack_trace)? + value: Float::new((this.value).asinh() / PI).unwrap_not_nan(context)? }) } ); @@ -767,11 +780,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: Scalar) -> Scalar { - this.check_trig_compatible(context.stack_trace)?; + this.check_trig_compatible(context)?; Ok(Scalar { dimension: Dimension::zero(), - value: Float::new((this.value * PI).sin()).unwrap_not_nan(context.stack_trace)? + value: Float::new((this.value * PI).sin()).unwrap_not_nan(context)? }) } ); @@ -781,11 +794,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: Scalar) -> Scalar { - this.check_trig_compatible(context.stack_trace)?; + this.check_trig_compatible(context)?; Ok(Scalar { dimension: Dimension::zero(), - value: Float::new((this.value * PI).sinh()).unwrap_not_nan(context.stack_trace)? + value: Float::new((this.value * PI).sinh()).unwrap_not_nan(context)? }) } ); @@ -795,7 +808,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: Scalar) -> Vector2 { - this.check_trig_compatible(context.stack_trace)?; + this.check_trig_compatible(context)?; let (sin, cos) = (this.value * PI).sin_cos(); Vector2::new(context, Dimension::zero(), [cos, sin]) @@ -807,11 +820,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: Scalar) -> Scalar { - this.check_inverse_trig_compatible(context.stack_trace)?; + this.check_inverse_trig_compatible(context)?; Ok(Scalar { dimension: Dimension::angle(), - value: Float::new((this.value * PI).atan()).unwrap_not_nan(context.stack_trace)? + value: Float::new((this.value * PI).atan()).unwrap_not_nan(context)? }) } ); @@ -821,11 +834,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: Scalar) -> Scalar { - this.check_inverse_trig_compatible(context.stack_trace)?; + this.check_inverse_trig_compatible(context)?; Ok(Scalar { dimension: Dimension::angle(), - value: Float::new((this.value).atanh() / PI).unwrap_not_nan(context.stack_trace)? + value: Float::new((this.value).atanh() / PI).unwrap_not_nan(context)? }) } ); @@ -835,11 +848,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: Scalar) -> Scalar { - this.check_trig_compatible(context.stack_trace)?; + this.check_trig_compatible(context)?; Ok(Scalar { dimension: Dimension::zero(), - value: Float::new((this.value * PI).tan()).unwrap_not_nan(context.stack_trace)? + value: Float::new((this.value * PI).tan()).unwrap_not_nan(context)? }) } ); @@ -849,11 +862,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: Scalar) -> Scalar { - this.check_trig_compatible(context.stack_trace)?; + this.check_trig_compatible(context)?; Ok(Scalar { dimension: Dimension::zero(), - value: Float::new((this.value * PI).tanh()).unwrap_not_nan(context.stack_trace)? + value: Float::new((this.value * PI).tanh()).unwrap_not_nan(context)? }) } ); @@ -1032,7 +1045,10 @@ mod test { let product = test_run("(100m / 1m)::to_signed_integer() == 100i").unwrap(); assert_eq!(product, Boolean(true).into()); - test_run("100m::to_signed_integer()").unwrap_err(); + let error = test_run("100m::to_signed_integer()").unwrap_err(); + let error = error.ty.as_any(); + let error: &ScalarToInteger = error.downcast_ref().unwrap(); + assert!(matches!(error, ScalarToInteger::NonZeroDimensionToInteger)); } #[test] @@ -1043,8 +1059,15 @@ mod test { let product = test_run("(100m / 1m)::to_unsigned_integer() == 100u").unwrap(); assert_eq!(product, Boolean(true).into()); - test_run("(-100)::to_unsigned_integer()").unwrap_err(); - test_run("100m::to_unsigned_integer()").unwrap_err(); + let error = test_run("(-100)::to_unsigned_integer()").unwrap_err(); + let error = error.ty.as_any(); + let error: &ScalarToInteger = error.downcast_ref().unwrap(); + assert!(matches!(error, ScalarToInteger::NegativeToUnsignedInteger)); + + let error = test_run("100m::to_unsigned_integer()").unwrap_err(); + let error = error.ty.as_any(); + let error: &ScalarToInteger = error.downcast_ref().unwrap(); + assert!(matches!(error, ScalarToInteger::NonZeroDimensionToInteger)); } #[test] diff --git a/interpreter/src/execution/values/string/formatting.rs b/interpreter/src/execution/values/string/formatting.rs index 36421d4..d3f199d 100644 --- a/interpreter/src/execution/values/string/formatting.rs +++ b/interpreter/src/execution/values/string/formatting.rs @@ -31,8 +31,8 @@ use nom::{ }; use crate::execution::{ - errors::{ExpressionResult, GenericFailure, Raise}, - logging::{LocatedStr, StackTrace}, + errors::{ExecutionResult, Raise, StringError}, + logging::LocatedStr, values::{Dictionary, Object, UnsignedInteger}, ExecutionContext, }; @@ -184,12 +184,12 @@ impl Format { context: &ExecutionContext, f: &mut dyn Write, arguments: Dictionary, - ) -> ExpressionResult<()> { + ) -> ExecutionResult<()> { fn get_precision( context: &ExecutionContext, precision: &Precision, arguments: &Dictionary, - ) -> ExpressionResult> { + ) -> ExecutionResult> { match precision { Precision::Default => Ok(None), Precision::Inline(precision) => Ok(Some(*precision)), @@ -202,69 +202,65 @@ impl Format { }) .ok() }) { - let precision = argument - .downcast_ref::(context.stack_trace)? - .0; + let precision = argument.downcast_ref::(context)?.0; if precision <= u8::MAX as u64 { Ok(Some(precision as u8)) } else { - Err(GenericFailure( - format!( - "Precision of {precision} is not in the valid range of 0 to {}", - u8::MAX - ) - .into(), - ) - .to_error(context.stack_trace)) + Err(StringError(format!( + "Precision of {precision} is not in the valid range of 0 to {}", + u8::MAX + )) + .to_error(context)) } } else { - Err( - GenericFailure(format!("Could not find argument `{name}`").into()) - .to_error(context.stack_trace), - ) + Err(StringError(format!("Could not find argument `{name}`")) + .to_error(context)) } } } } - for component in self.components.iter() { - match component { - Component::Litteral(text) => { - write!(f, "{}", text).unwrap_formatting_result(context.stack_trace)? - } - Component::Parameter(Parameter { - name, - style, - precision, - }) => { - let precision = get_precision(context, precision, &arguments)?; - - if let Some(argument) = arguments.get(name.as_str()).or_else(|| { - context - .get_variable(LocatedStr { - location: context.stack_trace.bottom().clone(), - string: name.as_str(), - }) - .ok() - }) { - argument - .format(context, f, *style, precision) - .map_err(|error| { - GenericFailure(format!("Error while formatting: {error:?}").into()) - .to_error(context.stack_trace) - })?; - } else { - return Err(GenericFailure( - format!("Could not find argument `{name}`").into(), - ) - .to_error(context.stack_trace)); + context.trace_scope( + Some("Failed to format string".into()), + context.stack_trace.bottom().clone(), + |context| { + for component in self.components.iter() { + match component { + Component::Litteral(text) => { + write!(f, "{}", text).map_err(|error| error.to_error(context))?; + } + Component::Parameter(Parameter { + name, + style, + precision, + }) => { + let precision = get_precision(context, precision, &arguments)?; + + if let Some(argument) = arguments.get(name.as_str()).or_else(|| { + context + .get_variable(LocatedStr { + location: context.stack_trace.bottom().clone(), + string: name.as_str(), + }) + .ok() + }) { + argument + .format(context, f, *style, precision) + .map_err(|error| error.to_error(context))?; + } else { + return Err(StringError(format!( + "Could not find argument `{name}`" + )) + .to_error(context)); + } + } } } - } - } - Ok(()) + Ok(()) + }, + ) } } @@ -275,22 +271,6 @@ fn number(input: &str) -> VResult<&str, u8> { .parse(input) } -pub trait UnwrapFormattingResult { - fn unwrap_formatting_result(self, stack_trace: &StackTrace) -> ExpressionResult; -} - -impl UnwrapFormattingResult for std::result::Result { - fn unwrap_formatting_result(self, stack_trace: &StackTrace) -> ExpressionResult { - match self { - Ok(result) => Ok(result), - Err(error) => { - Err(GenericFailure(format!("Failed to format: {error}",).into()) - .to_error(stack_trace)) - } - } - } -} - #[cfg(test)] mod test { use std::collections::HashMap; diff --git a/interpreter/src/execution/values/string/mod.rs b/interpreter/src/execution/values/string/mod.rs index a165ae6..98b0d53 100644 --- a/interpreter/src/execution/values/string/mod.rs +++ b/interpreter/src/execution/values/string/mod.rs @@ -23,7 +23,7 @@ use imstr::ImString; use crate::{ build_closure_type, build_method, execution::{ - errors::{GenericFailure, Raise}, + errors::{Raise, StringError}, logging::{LocatedStr, LogLevel, LogMessage}, stack::ScopeType, values::{ @@ -38,7 +38,7 @@ use crate::{ values::iterators::{IterableObject, ValueIterator}, }; -use super::{value_type::ValueType, ExpressionResult, Object, StaticTypeName, Value}; +use super::{value_type::ValueType, ExecutionResult, Object, StaticTypeName, Value}; use std::{borrow::Cow, collections::HashMap, sync::Arc}; @@ -78,16 +78,12 @@ impl Object for IString { write!(f, "{}", self.0) } - fn eq(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: &Self = rhs.downcast_for_binary_op_ref(context.stack_trace)?; + fn eq(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: &Self = rhs.downcast_for_binary_op_ref(context)?; Ok(self.0 == rhs.0) } - fn get_attribute( - &self, - context: &ExecutionContext, - attribute: &str, - ) -> ExpressionResult { + fn get_attribute(&self, context: &ExecutionContext, attribute: &str) -> ExecutionResult { match attribute { "format" => Ok(BuiltinFunction::new::().into()), @@ -117,7 +113,7 @@ impl Object for IString { _ => Err(MissingAttributeError { name: attribute.into(), } - .to_error(context.stack_trace)), + .to_error(context)), } } } @@ -151,8 +147,8 @@ pub struct CharIterator { impl IterableObject for CharIterator { fn iterate( &self, - callback: impl FnOnce(&mut dyn Iterator) -> ExpressionResult, - ) -> ExpressionResult { + callback: impl FnOnce(&mut dyn Iterator) -> ExecutionResult, + ) -> ExecutionResult { let mut iter = self .string .0 @@ -171,8 +167,8 @@ pub struct LineIterator { impl IterableObject for LineIterator { fn iterate( &self, - callback: impl FnOnce(&mut dyn Iterator) -> ExpressionResult, - ) -> ExpressionResult { + callback: impl FnOnce(&mut dyn Iterator) -> ExecutionResult, + ) -> ExecutionResult { let mut iterator = self .string .0 @@ -215,23 +211,26 @@ fn register_format_method(database: &mut BuiltinCallableDatabase) { } impl BuiltinCallable for BuiltFunction { - fn call( - &self, - context: &ExecutionContext, - argument: Dictionary, - ) -> ExpressionResult { + fn call(&self, context: &ExecutionContext, argument: Dictionary) -> ExecutionResult { let this = context .get_variable(LocatedStr { location: context.stack_trace.bottom().clone(), string: "self", })? - .downcast_ref::(context.stack_trace)? + .downcast_ref::(context)? .clone(); - let (excess, format) = Format::parse(&this.0).map_err(|error| { - GenericFailure(format!("Failed to parse formatting string: {error:?}").into()) - .to_error(context.stack_trace) - })?; + let (excess, format) = context.trace_scope( + Some("".into()), + context.stack_trace.bottom().clone(), + |_context| { + // The error message of this type is dependent on the lifetime of `this`, so we + // encode it to a string to make the lifetime static. + Format::parse(&this.0) + .map_err(|error| StringError(format!("{error:?}")).to_error(context)) + }, + )?; + assert!(excess.is_empty()); let mut output = String::new(); @@ -362,7 +361,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { "c".into(), IString(ImString::from(format!("{c}"))).into() ) - ])))?.downcast::(context.stack_trace)?; + ])))?.downcast::(context)?; if retain.0 { product.push(c); @@ -421,11 +420,13 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: IString ) -> Scalar { - let value = this.0.parse::() - .map_err(|error| GenericFailure(format!("Failed to parse scalar value: {error:?}").into()).to_error(context.stack_trace))?; - Ok(Scalar { - dimension: Dimension::zero(), - value, + context.trace_scope(Some("Failed to parse scalar value".into()), context.stack_trace.bottom().clone(), |context| { + let value = this.0.parse::() + .map_err(|error| error.to_error(context))?; + Ok(Scalar { + dimension: Dimension::zero(), + value, + }) }) } ); @@ -435,9 +436,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: IString ) -> UnsignedInteger { - let value = this.0.parse::() - .map_err(|error| GenericFailure(format!("Failed to parse unsigned integer: {error:?}").into()).to_error(context.stack_trace))?; - Ok(UnsignedInteger::from(value)) + context.trace_scope(Some("Failed to parse unsigned integer".into()), context.stack_trace.bottom().clone(), |context| { + let value = this.0.parse::() + .map_err(|error| error.to_error(context))?; + Ok(UnsignedInteger::from(value)) + }) } ); build_method!( @@ -446,9 +449,11 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { context: &ExecutionContext, this: IString ) -> SignedInteger { - let value = this.0.parse::() - .map_err(|error| GenericFailure(format!("Failed to parse signed integer: {error:?}").into()).to_error(context.stack_trace))?; - Ok(SignedInteger::from(value)) + context.trace_scope(Some("Failed to parse signed integer".into()), context.stack_trace.bottom().clone(), |context| { + let value = this.0.parse::() + .map_err(|error| error.to_error(context))?; + Ok(SignedInteger::from(value)) + }) } ); build_method!( diff --git a/interpreter/src/execution/values/value_type.rs b/interpreter/src/execution/values/value_type.rs index a0698a7..5a80d91 100644 --- a/interpreter/src/execution/values/value_type.rs +++ b/interpreter/src/execution/values/value_type.rs @@ -31,7 +31,7 @@ use crate::{ compile::{self, AstNode}, execute_expression, execution::{ - errors::{ErrorType, ExpressionResult, Raise}, + errors::{ExecutionResult, Raise}, logging::{LogLevel, LogMessage}, values::{ self, closure::BuiltinCallableDatabase, dictionary::DictionaryData, @@ -239,24 +239,20 @@ impl Object for ValueType { write!(f, "{}", self) } - fn bit_or(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: Self = rhs.downcast_for_binary_op(context.stack_trace)?; + fn bit_or(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: Self = rhs.downcast_for_binary_op(context)?; Ok(self.merge(rhs).into()) } - fn get_attribute( - &self, - context: &ExecutionContext, - attribute: &str, - ) -> ExpressionResult { + fn get_attribute(&self, context: &ExecutionContext, attribute: &str) -> ExecutionResult { match attribute { "qualify" => Ok(BuiltinFunction::new::().into()), "try_qualify" => Ok(BuiltinFunction::new::().into()), _ => Err(MissingAttributeError { name: attribute.into(), } - .to_error(context.stack_trace)), + .to_error(context)), } } } @@ -280,7 +276,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { this: ValueType, to_qualify: Value) -> ValueNone { - this.check_other_qualifies(&to_qualify.get_type(context)).map_err(|error| error.to_error(context.stack_trace))?; + this.check_other_qualifies(&to_qualify.get_type(context)).map_err(|error| error.to_error(context))?; Ok(values::ValueNone) } ); @@ -307,9 +303,8 @@ impl StructMember { fn new( context: &ExecutionContext, source: &AstNode, - ) -> ExpressionResult { - let ty = execute_expression(context, &source.node.ty)? - .downcast::(context.stack_trace)?; + ) -> ExecutionResult { + let ty = execute_expression(context, &source.node.ty)?.downcast::(context)?; let default = if let Some(default) = source.node.default.as_ref() { Some(execute_expression(context, default)?) } else { @@ -340,7 +335,7 @@ impl StructDefinition { pub fn new( context: &ExecutionContext, source: &AstNode, - ) -> ExpressionResult { + ) -> ExecutionResult { let mut members = HashMap::new(); for member in source.node.members.iter() { let name = member.node.name.node.clone(); @@ -518,7 +513,7 @@ impl TypeQualificationError { } } -impl ErrorType for TypeQualificationError {} +impl std::error::Error for TypeQualificationError {} impl Display for TypeQualificationError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -540,9 +535,16 @@ mod test { .check_other_qualifies(&ValueType::TypeNone) .unwrap(); - ValueType::TypeNone + let error = ValueType::TypeNone .check_other_qualifies(&ValueType::UnsignedInteger) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: ValueType::TypeNone, + got: ValueType::UnsignedInteger + } + ) } #[test] @@ -551,9 +553,16 @@ mod test { .check_other_qualifies(&ValueType::Boolean) .unwrap(); - ValueType::Boolean + let error = ValueType::Boolean .check_other_qualifies(&ValueType::TypeNone) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: ValueType::Boolean, + got: ValueType::TypeNone + } + ) } #[test] @@ -562,9 +571,16 @@ mod test { .check_other_qualifies(&ValueType::SignedInteger) .unwrap(); - ValueType::SignedInteger + let error = ValueType::SignedInteger .check_other_qualifies(&ValueType::TypeNone) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: ValueType::SignedInteger, + got: ValueType::TypeNone + } + ) } #[test] @@ -573,9 +589,16 @@ mod test { .check_other_qualifies(&ValueType::UnsignedInteger) .unwrap(); - ValueType::UnsignedInteger + let error = ValueType::UnsignedInteger .check_other_qualifies(&ValueType::TypeNone) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: ValueType::UnsignedInteger, + got: ValueType::TypeNone + } + ) } #[test] @@ -588,13 +611,27 @@ mod test { .check_other_qualifies(&ValueType::Scalar(Some(Dimension::length()))) .unwrap(); - ValueType::Scalar(Some(Dimension::length())) + let error = ValueType::Scalar(Some(Dimension::length())) .check_other_qualifies(&ValueType::Scalar(Some(Dimension::area()))) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: ValueType::Scalar(Some(Dimension::length())), + got: ValueType::Scalar(Some(Dimension::area())), + } + ); - ValueType::Scalar(Some(Dimension::length())) + let error = ValueType::Scalar(Some(Dimension::length())) .check_other_qualifies(&ValueType::TypeNone) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: ValueType::Scalar(Some(Dimension::length())), + got: ValueType::TypeNone + } + ); } #[test] @@ -607,13 +644,27 @@ mod test { .check_other_qualifies(&ValueType::Vector2(Some(Dimension::length()))) .unwrap(); - ValueType::Vector2(Some(Dimension::length())) + let error = ValueType::Vector2(Some(Dimension::length())) .check_other_qualifies(&ValueType::Vector2(Some(Dimension::area()))) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: ValueType::Vector2(Some(Dimension::length())), + got: ValueType::Vector2(Some(Dimension::area())), + } + ); - ValueType::Vector2(Some(Dimension::length())) + let error = ValueType::Vector2(Some(Dimension::length())) .check_other_qualifies(&ValueType::TypeNone) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: ValueType::Vector2(Some(Dimension::length())), + got: ValueType::TypeNone + } + ); } #[test] @@ -626,13 +677,27 @@ mod test { .check_other_qualifies(&ValueType::Vector3(Some(Dimension::length()))) .unwrap(); - ValueType::Vector3(Some(Dimension::length())) + let error = ValueType::Vector3(Some(Dimension::length())) .check_other_qualifies(&ValueType::Vector3(Some(Dimension::area()))) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: ValueType::Vector3(Some(Dimension::length())), + got: ValueType::Vector3(Some(Dimension::area())), + } + ); - ValueType::Vector3(Some(Dimension::length())) + let error = ValueType::Vector3(Some(Dimension::length())) .check_other_qualifies(&ValueType::TypeNone) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: ValueType::Vector3(Some(Dimension::length())), + got: ValueType::TypeNone + } + ); } #[test] @@ -645,13 +710,27 @@ mod test { .check_other_qualifies(&ValueType::Vector4(Some(Dimension::length()))) .unwrap(); - ValueType::Vector4(Some(Dimension::length())) + let error = ValueType::Vector4(Some(Dimension::length())) .check_other_qualifies(&ValueType::Vector4(Some(Dimension::area()))) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: ValueType::Vector4(Some(Dimension::length())), + got: ValueType::Vector4(Some(Dimension::area())), + } + ); - ValueType::Vector4(Some(Dimension::length())) + let error = ValueType::Vector4(Some(Dimension::length())) .check_other_qualifies(&ValueType::TypeNone) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: ValueType::Vector4(Some(Dimension::length())), + got: ValueType::TypeNone + } + ); } #[test] @@ -722,9 +801,22 @@ mod test { let dictionary = test_run("(a = std.consts.None, b = std.consts.None)").unwrap(); let dictionary = dictionary.as_dictionary().unwrap(); - structure + let error = structure .check_other_qualifies(&dictionary.get_type(context)) .unwrap_err(); + dbg!(&error); + assert_eq!( + error, + TypeQualificationError::Fields { + failed_feilds: vec![MissmatchedField { + name: "b".into(), + error: TypeQualificationError::This { + expected: ValueType::TypeNone, + got: ValueType::TypeNone + } + }] + } + ); }) } @@ -764,9 +856,16 @@ mod test { .check_other_qualifies(&ValueType::ValueType) .unwrap(); - ValueType::UnsignedInteger + let error = ValueType::ValueType .check_other_qualifies(&ValueType::TypeNone) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: ValueType::ValueType, + got: ValueType::TypeNone + } + ); } #[test] @@ -782,9 +881,16 @@ mod test { .check_other_qualifies(&ValueType::UnsignedInteger) .unwrap(); - value_type + let error = value_type .check_other_qualifies(&ValueType::SignedInteger) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: value_type.clone(), + got: ValueType::SignedInteger + } + ); } #[test] @@ -804,9 +910,16 @@ mod test { .check_other_qualifies(&ValueType::SignedInteger) .unwrap(); - value_type + let error = value_type .check_other_qualifies(&ValueType::Boolean) .unwrap_err(); + assert_eq!( + error, + TypeQualificationError::This { + expected: value_type.clone(), + got: ValueType::Boolean + } + ); } #[test] @@ -866,6 +979,15 @@ mod test { let result = test_run("std.types.Bool::qualify(to_qualify = true)").unwrap(); assert_eq!(result, values::ValueNone.into()); - test_run("std.types.Bool::qualify(to_qualify = 5u)").unwrap_err(); + let error = test_run("std.types.Bool::qualify(to_qualify = 5u)").unwrap_err(); + let error = error.ty.as_any(); + let error: &TypeQualificationError = error.downcast_ref().unwrap(); + assert_eq!( + *error, + TypeQualificationError::This { + expected: ValueType::Boolean, + got: ValueType::UnsignedInteger + } + ); } } diff --git a/interpreter/src/execution/values/vector.rs b/interpreter/src/execution/values/vector.rs index 8f13e63..9cfa12f 100644 --- a/interpreter/src/execution/values/vector.rs +++ b/interpreter/src/execution/values/vector.rs @@ -7,8 +7,7 @@ use crate::{ compile::{self, AstNode}, execute_expression, execution::{ - errors::{ExpressionResult, GenericFailure, Raise as _}, - logging::StackTrace, + errors::{ExecutionResult, Raise as _}, values::{ closure::BuiltinCallableDatabase, scalar::UnwrapNotNan, string::formatting::Style, BuiltinFunction, DowncastForBinaryOpError, MissingAttributeError, Object, Scalar, @@ -16,6 +15,7 @@ use crate::{ }, ExecutionContext, }, + values::scalar::ResultIsNan, }; use std::{ @@ -77,36 +77,36 @@ where Ok(()) } - fn addition(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs = self.unpack_same_dimension(context.stack_trace, rhs)?; + fn addition(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs = self.unpack_same_dimension(context, rhs)?; let value = self.value + rhs.value; Ok(Self::new_raw(context, self.dimension, value)?.into()) } - fn subtraction(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs = self.unpack_same_dimension(context.stack_trace, rhs)?; + fn subtraction(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs = self.unpack_same_dimension(context, rhs)?; let value = self.value - rhs.value; Ok(Self::new_raw(context, self.dimension, value)?.into()) } - fn multiply(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs = rhs.downcast_for_binary_op_ref::(context.stack_trace)?; + fn multiply(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs = rhs.downcast_for_binary_op_ref::(context)?; let value = self.value * *rhs.value; let dimension = self.dimension + rhs.dimension; Ok(Self::new_raw(context, dimension, value)?.into()) } - fn divide(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs = rhs.downcast_for_binary_op_ref::(context.stack_trace)?; + fn divide(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs = rhs.downcast_for_binary_op_ref::(context)?; let value = self.value / *rhs.value; let dimension = self.dimension - rhs.dimension; Ok(Self::new_raw(context, dimension, value)?.into()) } - fn unary_plus(self, _context: &ExecutionContext) -> ExpressionResult { + fn unary_plus(self, _context: &ExecutionContext) -> ExecutionResult { Ok(self.into()) } - fn unary_minus(self, _context: &ExecutionContext) -> ExpressionResult { + fn unary_minus(self, _context: &ExecutionContext) -> ExecutionResult { Ok(Self { value: -self.value, ..self @@ -114,16 +114,12 @@ where .into()) } - fn eq(self, context: &ExecutionContext, rhs: Value) -> ExpressionResult { - let rhs: Self = rhs.downcast_for_binary_op(context.stack_trace)?; + fn eq(self, context: &ExecutionContext, rhs: Value) -> ExecutionResult { + let rhs: Self = rhs.downcast_for_binary_op(context)?; Ok(self.dimension == rhs.dimension && self.value == rhs.value) } - fn get_attribute( - &self, - context: &ExecutionContext, - attribute: &str, - ) -> ExpressionResult { + fn get_attribute(&self, context: &ExecutionContext, attribute: &str) -> ExecutionResult { if let Some(value) = self .value .get_attribute(context, attribute, self.dimension)? @@ -174,7 +170,7 @@ where _ => Err(MissingAttributeError { name: attribute.into(), } - .to_error(context.stack_trace)), + .to_error(context)), } } } @@ -190,7 +186,7 @@ where context: &ExecutionContext, dimension: Dimension, value: I::BuildFrom, - ) -> ExpressionResult { + ) -> ExecutionResult { let value = I::build(value); Self::new_raw(context, dimension, value) @@ -199,7 +195,7 @@ where pub fn from_ast( context: &ExecutionContext, ast_node: &AstNode>, - ) -> ExpressionResult { + ) -> ExecutionResult { I::from_ast(context, ast_node) } @@ -207,14 +203,11 @@ where context: &ExecutionContext, dimension: Dimension, value: I, - ) -> ExpressionResult { + ) -> ExecutionResult { if !value.is_nan() { Ok(Self { dimension, value }) } else { - Err( - GenericFailure("Result of arithmetic operation is NaN".into()) - .to_error(context.stack_trace), - ) + Err(ResultIsNan.to_error(context)) } } @@ -226,8 +219,12 @@ where self.value } - fn unpack_same_dimension(self, stack_trace: &StackTrace, rhs: Value) -> ExpressionResult { - let rhs: Vector = rhs.downcast_for_binary_op(stack_trace)?; + fn unpack_same_dimension( + self, + context: &ExecutionContext, + rhs: Value, + ) -> ExecutionResult { + let rhs: Vector = rhs.downcast_for_binary_op(context)?; if self.dimension == rhs.dimension { Ok(rhs) @@ -236,7 +233,7 @@ where expected: self.type_name(), got: rhs.type_name(), } - .to_error(stack_trace)) + .to_error(context)) } } } @@ -354,7 +351,7 @@ mod methods { expected: this.type_name(), got: value.type_name(), } - .to_error(context.stack_trace)) + .to_error(context)) } } ); @@ -364,7 +361,7 @@ mod methods { context: &ExecutionContext, this: Vector) -> Scalar { - let value = common_data_types::Float::new(this.value.amax()).unwrap_not_nan(context.stack_trace)?; + let value = common_data_types::Float::new(this.value.amax()).unwrap_not_nan(context)?; Ok(Scalar { dimension: this.dimension, @@ -378,7 +375,7 @@ mod methods { context: &ExecutionContext, this: Vector) -> Scalar { - let value = common_data_types::Float::new(this.value.amin()).unwrap_not_nan(context.stack_trace)?; + let value = common_data_types::Float::new(this.value.amin()).unwrap_not_nan(context)?; Ok(Scalar { dimension: this.dimension, @@ -394,7 +391,7 @@ mod methods { rhs: Vector) -> Scalar { if this.dimension == rhs.dimension { - let value = common_data_types::Float::new(this.value.dot(&rhs.value)).unwrap_not_nan(context.stack_trace)?; + let value = common_data_types::Float::new(this.value.dot(&rhs.value)).unwrap_not_nan(context)?; Ok(Scalar { dimension: this.dimension, @@ -405,7 +402,7 @@ mod methods { expected: this.type_name(), got: rhs.type_name(), } - .to_error(context.stack_trace)) + .to_error(context)) } } ); @@ -415,7 +412,7 @@ mod methods { context: &ExecutionContext, this: Vector) -> Scalar { - let value = common_data_types::Float::new(this.value.norm()).unwrap_not_nan(context.stack_trace)?; + let value = common_data_types::Float::new(this.value.norm()).unwrap_not_nan(context)?; Ok(Scalar { dimension: this.dimension, @@ -440,7 +437,7 @@ mod methods { this: Vector, other: Vector) -> Scalar { - let value = common_data_types::Float::new(this.value.angle(&other.value)).unwrap_not_nan(context.stack_trace)?; + let value = common_data_types::Float::new(this.value.angle(&other.value)).unwrap_not_nan(context)?; Ok(Scalar { dimension: Dimension::angle(), @@ -460,20 +457,20 @@ mod methods { "c".into(), Scalar { dimension: this.dimension, - value: common_data_types::Float::new(c).unwrap_not_nan(context.stack_trace)? + value: common_data_types::Float::new(c).unwrap_not_nan(context)? }.into() ) - ])))).collect::>()?; + ])))).collect::>()?; - let result: ArrayVec<[Scalar; 4]> = operations.into_iter().map(|v| v.downcast::(context.stack_trace)).collect::>()?; + let result: ArrayVec<[Scalar; 4]> = operations.into_iter().map(|v| v.downcast::(context)).collect::>()?; // The smallest vector we support is 2, so this should never panic. let dimension = result[0].dimension; for component in result.iter() { if component.dimension != dimension { - return Err(GenericFailure("All components of a vector must match".into()) - .to_error(context.stack_trace)); + return Err(MissmatchedComponentDimensionsError + .to_error(context)); } } @@ -496,7 +493,7 @@ mod methods { "c".into(), Scalar { dimension: this.dimension, - value: common_data_types::Float::new(component).unwrap_not_nan(context.stack_trace)? + value: common_data_types::Float::new(component).unwrap_not_nan(context)? }.into() ), ( @@ -532,7 +529,7 @@ pub fn register_methods(database: &mut BuiltinCallableDatabase) { expected: this.type_name(), got: rhs.type_name(), } - .to_error(context.stack_trace)) + .to_error(context)) } } ); @@ -563,7 +560,7 @@ pub trait VectorInternalType: fn from_ast( context: &ExecutionContext, ast_node: &AstNode>, - ) -> ExpressionResult>; + ) -> ExecutionResult>; fn from_iterator(iterator: I) -> Self where I: IntoIterator; @@ -573,7 +570,7 @@ pub trait VectorInternalType: context: &ExecutionContext, attribute: &str, dimension: Dimension, - ) -> ExpressionResult>; + ) -> ExecutionResult>; fn abs(&self) -> Self; fn add_scalar(&self, value: Float) -> Self; @@ -601,10 +598,20 @@ where } } +#[derive(Debug, Eq, PartialEq)] +pub struct MissmatchedComponentDimensionsError; + +impl std::error::Error for MissmatchedComponentDimensionsError {} + +impl std::fmt::Display for MissmatchedComponentDimensionsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "All components of a vector must match") + } +} + macro_rules! get_component { ($context:ident, $ast_node:ident, $c:ident) => { - execute_expression($context, &$ast_node.node.$c)? - .downcast::($context.stack_trace)? + execute_expression($context, &$ast_node.node.$c)?.downcast::($context)? }; } @@ -624,7 +631,7 @@ impl VectorInternalType for nalgebra::Vector2 { fn from_ast( context: &ExecutionContext, ast_node: &AstNode>, - ) -> ExpressionResult> { + ) -> ExecutionResult> { let x = get_component!(context, ast_node, x); let y = get_component!(context, ast_node, y); @@ -634,10 +641,7 @@ impl VectorInternalType for nalgebra::Vector2 { value: Self::new(*x.value, *y.value), }) } else { - Err( - GenericFailure("All components of a vector must match".into()) - .to_error(context.stack_trace), - ) + Err(MissmatchedComponentDimensionsError.to_error(context)) } } @@ -653,21 +657,19 @@ impl VectorInternalType for nalgebra::Vector2 { context: &ExecutionContext, attribute: &str, dimension: Dimension, - ) -> ExpressionResult> { + ) -> ExecutionResult> { match attribute { "x" => Ok(Some( Scalar { dimension, - value: common_data_types::Float::new(self.x) - .unwrap_not_nan(context.stack_trace)?, + value: common_data_types::Float::new(self.x).unwrap_not_nan(context)?, } .into(), )), "y" => Ok(Some( Scalar { dimension, - value: common_data_types::Float::new(self.y) - .unwrap_not_nan(context.stack_trace)?, + value: common_data_types::Float::new(self.y).unwrap_not_nan(context)?, } .into(), )), @@ -748,7 +750,7 @@ impl VectorInternalType for nalgebra::Vector3 { fn from_ast( context: &ExecutionContext, ast_node: &AstNode>, - ) -> ExpressionResult> { + ) -> ExecutionResult> { let x = get_component!(context, ast_node, x); let y = get_component!(context, ast_node, y); let z = get_component!(context, ast_node, z); @@ -759,10 +761,7 @@ impl VectorInternalType for nalgebra::Vector3 { value: Self::new(*x.value, *y.value, *z.value), }) } else { - Err( - GenericFailure("All components of a vector must match".into()) - .to_error(context.stack_trace), - ) + Err(MissmatchedComponentDimensionsError.to_error(context)) } } @@ -778,29 +777,26 @@ impl VectorInternalType for nalgebra::Vector3 { context: &ExecutionContext, attribute: &str, dimension: Dimension, - ) -> ExpressionResult> { + ) -> ExecutionResult> { match attribute { "x" => Ok(Some( Scalar { dimension, - value: common_data_types::Float::new(self.x) - .unwrap_not_nan(context.stack_trace)?, + value: common_data_types::Float::new(self.x).unwrap_not_nan(context)?, } .into(), )), "y" => Ok(Some( Scalar { dimension, - value: common_data_types::Float::new(self.y) - .unwrap_not_nan(context.stack_trace)?, + value: common_data_types::Float::new(self.y).unwrap_not_nan(context)?, } .into(), )), "z" => Ok(Some( Scalar { dimension, - value: common_data_types::Float::new(self.z) - .unwrap_not_nan(context.stack_trace)?, + value: common_data_types::Float::new(self.z).unwrap_not_nan(context)?, } .into(), )), @@ -883,7 +879,7 @@ impl VectorInternalType for nalgebra::Vector4 { fn from_ast( context: &ExecutionContext, ast_node: &AstNode>, - ) -> ExpressionResult> { + ) -> ExecutionResult> { let x = get_component!(context, ast_node, x); let y = get_component!(context, ast_node, y); let z = get_component!(context, ast_node, z); @@ -895,10 +891,7 @@ impl VectorInternalType for nalgebra::Vector4 { value: Self::new(*x.value, *y.value, *z.value, *w.value), }) } else { - Err( - GenericFailure("All components of a vector must match".into()) - .to_error(context.stack_trace), - ) + Err(MissmatchedComponentDimensionsError.to_error(context)) } } @@ -914,37 +907,33 @@ impl VectorInternalType for nalgebra::Vector4 { context: &ExecutionContext, attribute: &str, dimension: Dimension, - ) -> ExpressionResult> { + ) -> ExecutionResult> { match attribute { "x" => Ok(Some( Scalar { dimension, - value: common_data_types::Float::new(self.x) - .unwrap_not_nan(context.stack_trace)?, + value: common_data_types::Float::new(self.x).unwrap_not_nan(context)?, } .into(), )), "y" => Ok(Some( Scalar { dimension, - value: common_data_types::Float::new(self.y) - .unwrap_not_nan(context.stack_trace)?, + value: common_data_types::Float::new(self.y).unwrap_not_nan(context)?, } .into(), )), "z" => Ok(Some( Scalar { dimension, - value: common_data_types::Float::new(self.z) - .unwrap_not_nan(context.stack_trace)?, + value: common_data_types::Float::new(self.z).unwrap_not_nan(context)?, } .into(), )), "w" => Ok(Some( Scalar { dimension, - value: common_data_types::Float::new(self.w) - .unwrap_not_nan(context.stack_trace)?, + value: common_data_types::Float::new(self.w).unwrap_not_nan(context)?, } .into(), )), @@ -1142,23 +1131,65 @@ mod test { #[test] fn missmatched_dimensions_vector2() { - test_run("<(1deg, 2m)>").unwrap_err(); - test_run("<(1m, 2deg)>").unwrap_err(); + let error = test_run("<(1deg, 2m)>").unwrap_err(); + let error = error.ty.as_any(); + error + .downcast_ref::() + .unwrap(); + + let error = test_run("<(1m, 2deg)>").unwrap_err(); + let error = error.ty.as_any(); + error + .downcast_ref::() + .unwrap(); } #[test] fn missmatched_dimensions_vector3() { - test_run("<(1deg, 2m, 3m)>").unwrap_err(); - test_run("<(1m, 2deg, 3m)>").unwrap_err(); - test_run("<(1m, 2m, 3deg)>").unwrap_err(); + let error = test_run("<(1deg, 2m, 3m)>").unwrap_err(); + let error = error.ty.as_any(); + error + .downcast_ref::() + .unwrap(); + + let error = test_run("<(1m, 2deg, 3m)>").unwrap_err(); + let error = error.ty.as_any(); + error + .downcast_ref::() + .unwrap(); + + let error = test_run("<(1m, 2m, 3deg)>").unwrap_err(); + let error = error.ty.as_any(); + error + .downcast_ref::() + .unwrap(); } #[test] fn missmatched_dimensions_vector4() { - test_run("<(1deg, 2m, 3m, 4m)>").unwrap_err(); - test_run("<(1m, 2deg, 3m, 4m)>").unwrap_err(); - test_run("<(1m, 2m, 3deg, 4m)>").unwrap_err(); - test_run("<(1m, 2m, 3m, 4deg)>").unwrap_err(); + let error = test_run("<(1deg, 2m, 3m, 4m)>").unwrap_err(); + let error = error.ty.as_any(); + error + .downcast_ref::() + .unwrap(); + + let error = test_run("<(1m, 2deg, 3m, 4m)>").unwrap_err(); + let error = error.ty.as_any(); + error + .downcast_ref::() + .unwrap(); + let error = test_run("<(1m, 2m, 3deg, 4m)>").unwrap_err(); + + let error = error.ty.as_any(); + error + .downcast_ref::() + .unwrap(); + + let error = test_run("<(1m, 2m, 3m, 4deg)>").unwrap_err(); + let error = error.ty.as_any(); + error + .downcast_ref::() + .unwrap(); } #[test] @@ -1509,17 +1540,23 @@ mod test { #[test] fn normalize_zero_vector2() { - test_run("<(0m, 0m)>::normalize()").unwrap_err(); + let error = test_run("<(0m, 0m)>::normalize()").unwrap_err(); + let error = error.ty.as_any(); + error.downcast_ref::().unwrap(); } #[test] fn normalize_zero_vector3() { - test_run("<(0m, 0m, 0m)>::normalize()").unwrap_err(); + let error = test_run("<(0m, 0m, 0m)>::normalize()").unwrap_err(); + let error = error.ty.as_any(); + error.downcast_ref::().unwrap(); } #[test] fn normalize_zero_vector4() { - test_run("<(0m, 0m, 0m, 0m)>::normalize()").unwrap_err(); + let error = test_run("<(0m, 0m, 0m, 0m)>::normalize()").unwrap_err(); + let error = error.ty.as_any(); + error.downcast_ref::().unwrap(); } #[test] @@ -1558,7 +1595,11 @@ mod test { let product = test_run("<(0m, 1m)>::apply(f = (c: std.scalar.Length) -> std.scalar.Area: c * 1m) == <(0 'm^2', 1 'm^2')>").unwrap(); assert_eq!(product, Boolean(true).into()); - test_run("<(0m, 1m)>::apply(f = (c: std.scalar.Length) -> std.scalar.Any: if c == 0m then 1m else 1 'm^2')").unwrap_err(); + let error = test_run("<(0m, 1m)>::apply(f = (c: std.scalar.Length) -> std.scalar.Any: if c == 0m then 1m else 1 'm^2')").unwrap_err(); + let error = error.ty.as_any(); + error + .downcast_ref::() + .unwrap(); } #[test] @@ -1569,7 +1610,11 @@ mod test { let product = test_run("<(0m, 1m, 2m)>::apply(f = (c: std.scalar.Length) -> std.scalar.Area: c * 1m) == <(0 'm^2', 1 'm^2', 2 'm^2')>").unwrap(); assert_eq!(product, Boolean(true).into()); - test_run("<(0m, 1m, 1m)>::apply(f = (c: std.scalar.Length) -> std.scalar.Any: if c == 0m then 1m else 1 'm^2')").unwrap_err(); + let error =test_run("<(0m, 1m, 1m)>::apply(f = (c: std.scalar.Length) -> std.scalar.Any: if c == 0m then 1m else 1 'm^2')").unwrap_err(); + let error = error.ty.as_any(); + error + .downcast_ref::() + .unwrap(); } #[test] @@ -1580,7 +1625,11 @@ mod test { let product = test_run("<(0m, 1m, 2m, 3m)>::apply(f = (c: std.scalar.Length) -> std.scalar.Area: c * 1m) == <(0 'm^2', 1 'm^2', 2 'm^2', 3 'm^2')>").unwrap(); assert_eq!(product, Boolean(true).into()); - test_run("<(0m, 1m, 1m, 1m)>::apply(f = (c: std.scalar.Length) -> std.scalar.Any: if c == 0m then 1m else 1 'm^2')").unwrap_err(); + let error = test_run("<(0m, 1m, 1m, 1m)>::apply(f = (c: std.scalar.Length) -> std.scalar.Any: if c == 0m then 1m else 1 'm^2')").unwrap_err(); + let error = error.ty.as_any(); + error + .downcast_ref::() + .unwrap(); } #[test] diff --git a/interpreter/src/execution/values/void.rs b/interpreter/src/execution/values/void.rs index c9f1cec..46e9a8e 100644 --- a/interpreter/src/execution/values/void.rs +++ b/interpreter/src/execution/values/void.rs @@ -63,7 +63,7 @@ impl Object for ValueNone { write!(f, "None") } - fn eq(self, _context: &ExecutionContext, rhs: Value) -> crate::ExpressionResult { + fn eq(self, _context: &ExecutionContext, rhs: Value) -> crate::ExecutionResult { Ok(matches!(rhs, Value::ValueNone(_))) } } diff --git a/interpreter/src/lib.rs b/interpreter/src/lib.rs index 8ca108b..9356ebc 100644 --- a/interpreter/src/lib.rs +++ b/interpreter/src/lib.rs @@ -22,7 +22,7 @@ pub mod execution; pub use compile::{compile, new_parser, Parser, SourceReference}; pub use execution::{ build_prelude, execute_expression, run_file, values, Error, ExecutionContext, - ExecutionFileCache, ExpressionResult, LogLevel, LogMessage, RuntimeLog, StackScope, StackTrace, + ExecutionFileCache, ExecutionResult, LogLevel, LogMessage, RuntimeLog, StackScope, StackTrace, Store, }; pub use imstr::ImString;